1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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 }