View Javadoc

1   /*
2    *   @(#) $Id: CumulativeProtocolDecoder.java 161880 2005-04-19 12:32:13Z trustin $
3    *
4    *   Copyright 2004 The Apache Software Foundation
5    *
6    *   Licensed under the Apache License, Version 2.0 (the "License");
7    *   you may not use this file except in compliance with the License.
8    *   You may obtain a copy of the License at
9    *
10   *       http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *   Unless required by applicable law or agreed to in writing, software
13   *   distributed under the License is distributed on an "AS IS" BASIS,
14   *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   *   See the License for the specific language governing permissions and
16   *   limitations under the License.
17   *
18   */
19  package org.apache.mina.protocol.codec;
20  
21  import org.apache.mina.common.ByteBuffer;
22  import org.apache.mina.protocol.ProtocolDecoder;
23  import org.apache.mina.protocol.ProtocolDecoderOutput;
24  import org.apache.mina.protocol.ProtocolSession;
25  import org.apache.mina.protocol.ProtocolViolationException;
26  
27  /***
28   * A {@link ProtocolDecoder} that cumulates the content of received
29   * buffers to a <em>cumulative buffer</em> to help users implement decoders.
30   * <p>
31   * If the received {@link ByteBuffer} is only a part of a message.
32   * decoders should cumulate received buffers to make a message complete or
33   * to postpone decoding until more buffers arrive.
34   * <p>
35   * Here is an example decoder that decodes a list of integers:
36   * <pre>
37   * public class IntegerDecoder extends CumulativeProtocolDecoder {
38   * 
39   *     public IntegerDecoder() {
40   *         super(4);
41   *     }
42   * 
43   *     protected boolean doDecode(ProtocolSession session, ByteBuffer in,
44   *                                ProtocolDecoderOutput out) throws ProtocolViolationException {
45   *         if (in.remaining() < 4) {
46   *             return false; // Cumulate remainder to decode later.
47   *         }
48   *         
49   *         out.write(new Integer(in.getInt()));
50   * 
51   *         // Decoded one integer; CumulativeProtocolDecoder will call me again,
52   *         // so I can decode as many integers as possible.
53   *         return true;
54   *     }
55   * }
56   * </pre>
57   * 
58   * @author Trustin Lee (trustin@apache.org)
59   * @version $Rev: 161880 $, $Date: 2005-04-19 21:32:13 +0900 (?, 19  4? 2005) $
60   */
61  public abstract class CumulativeProtocolDecoder implements ProtocolDecoder {
62      
63      /*** Cumulation buffer */
64      private ByteBuffer buf;
65      
66      /***
67       * Creates a new instance with the specified default capacity of
68       * cumulative buffer.  Please note that the capacity increases
69       * automatically.
70       */
71      protected CumulativeProtocolDecoder( int defaultCapacity )
72      {
73          buf = ByteBuffer.allocate( defaultCapacity );
74          buf.setAutoExpand( true );
75      }
76      
77      /***
78       * Cumulates content of <tt>in</tt> into internal buffer and forwards
79       * decoding request to {@link #doDecode(ProtocolSession, ByteBuffer, ProtocolDecoderOutput)}.
80       * <tt>doDecode()</tt> is invoked repeatedly until it returns <tt>false</tt>
81       * and the cumulative buffer is compacted after decoding ends.
82       * 
83       * @throws IllegalStateException if your <tt>doDecode()</tt> returned
84       *                               <tt>true</tt> not consuming the cumulative buffer.
85       */
86      public void decode( ProtocolSession session, ByteBuffer in,
87                          ProtocolDecoderOutput out ) throws ProtocolViolationException
88      {
89          if( session.getTransportType().isStateless() )
90          {
91              throw new IllegalStateException(
92                      "This decoder doesn't work for stateless transport types." );
93          }
94  
95          ByteBuffer buf = this.buf;
96          buf.put( in );
97          buf.flip();
98  
99          try
100         {
101             for( ;; )
102             {
103                 int oldPos = buf.position();
104                 if( !doDecode( session, buf, out ) )
105                 {
106                     break;
107                 }
108                 
109                 if( buf.position() == oldPos )
110                 {
111                     throw new IllegalStateException(
112                             "doDecode() can't return true when buffer is not consumed." );
113                 }
114             }
115         }
116         finally
117         {
118             buf.compact();
119         }
120     }
121     
122     /***
123      * Implement this method to consume the specified cumulative buffer and
124      * decode its content into message(s). 
125      *  
126      * @param in the cumulative buffer
127      * @return <tt>true</tt> if and only if there's more to decode in the buffer
128      *         and you want to have <tt>doDecode</tt> method invoked again.
129      *         Return <tt>false</tt> if remaining data is not enough to decode,
130      *         then this method will be invoked again when more data is cumulated.
131      * @throws ProtocolViolationException if cannot decode <tt>in</tt>.
132      */
133     protected abstract boolean doDecode( ProtocolSession session, ByteBuffer in,
134                                          ProtocolDecoderOutput out ) throws ProtocolViolationException;
135 }