View Javadoc

1   /*
2    *   @(#) $Id: CumulativeProtocolDecoder.java 210062 2005-07-11 03:52:38Z 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: 210062 $, $Date: 2005-07-11 12:52:38 +0900 $
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                 boolean decoded = doDecode( session, buf, out );
105                 if( decoded )
106                 {
107                     if( buf.position() == oldPos )
108                     {
109                         throw new IllegalStateException(
110                                 "doDecode() can't return true when buffer is not consumed." );
111                     }
112                     
113                     if( !buf.hasRemaining() )
114                     {
115                         break;
116                     }
117                 }
118                 else
119                 {
120                     break;
121                 }
122             }
123         }
124         finally
125         {
126             buf.compact();
127         }
128     }
129     
130     /***
131      * Implement this method to consume the specified cumulative buffer and
132      * decode its content into message(s). 
133      *  
134      * @param in the cumulative buffer
135      * @return <tt>true</tt> if and only if there's more to decode in the buffer
136      *         and you want to have <tt>doDecode</tt> method invoked again.
137      *         Return <tt>false</tt> if remaining data is not enough to decode,
138      *         then this method will be invoked again when more data is cumulated.
139      * @throws ProtocolViolationException if cannot decode <tt>in</tt>.
140      */
141     protected abstract boolean doDecode( ProtocolSession session, ByteBuffer in,
142                                          ProtocolDecoderOutput out ) throws ProtocolViolationException;
143 }