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