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: 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 }