1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  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,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.mina.filter.codec;
21  
22  import java.net.SocketAddress;
23  import java.util.ArrayList;
24  import java.util.List;
25  
26  import org.apache.mina.core.buffer.IoBuffer;
27  import org.apache.mina.core.service.DefaultTransportMetadata;
28  import org.apache.mina.core.session.IoSession;
29  import org.apache.mina.core.session.IoSessionConfig;
30  import org.junit.After;
31  import org.junit.Before;
32  import org.junit.Test;
33  import static org.junit.Assert.assertFalse;
34  import static org.junit.Assert.assertEquals;
35  import static org.junit.Assert.fail;
36  import static org.junit.Assert.assertTrue;
37  
38  
39  /**
40   * Tests {@link CumulativeProtocolDecoder}.
41   *
42   * @author The Apache MINA Project (dev@mina.apache.org)
43   */
44  public class CumulativeProtocolDecoderTest {
45      private final ProtocolCodecSession session = new ProtocolCodecSession();
46  
47      private IoBuffer buf;
48      private IntegerDecoder decoder;
49  
50      @Before
51      public void setUp() throws Exception {
52          buf = IoBuffer.allocate(16);
53          decoder = new IntegerDecoder();
54          session.setTransportMetadata(
55                  new DefaultTransportMetadata(
56                          "mina", "dummy", false, true, SocketAddress.class,
57                          IoSessionConfig.class, IoBuffer.class));
58      }
59  
60      @After
61      public void tearDown() throws Exception {
62          decoder.dispose(session);
63      }
64  
65      @Test
66      public void testCumulation() throws Exception {
67          buf.put((byte) 0);
68          buf.flip();
69  
70          decoder.decode(session, buf, session.getDecoderOutput());
71          assertEquals(0, session.getDecoderOutputQueue().size());
72          assertEquals(buf.limit(), buf.position());
73  
74          buf.clear();
75          buf.put((byte) 0);
76          buf.put((byte) 0);
77          buf.put((byte) 1);
78          buf.flip();
79  
80          decoder.decode(session, buf, session.getDecoderOutput());
81          assertEquals(1, session.getDecoderOutputQueue().size());
82          assertEquals(new Integer(1), session.getDecoderOutputQueue().poll());
83          assertEquals(buf.limit(), buf.position());
84      }
85  
86      @Test
87      public void testRepeatitiveDecode() throws Exception {
88          for (int i = 0; i < 4; i++) {
89              buf.putInt(i);
90          }
91          buf.flip();
92  
93          decoder.decode(session, buf, session.getDecoderOutput());
94          assertEquals(4, session.getDecoderOutputQueue().size());
95          assertEquals(buf.limit(), buf.position());
96  
97          List<Object> expected = new ArrayList<Object>();
98          
99          for (int i = 0; i < 4; i++) {
100             expected.add(new Integer(i));
101         }
102         
103         assertEquals(expected, session.getDecoderOutputQueue());
104     }
105 
106     @Test
107     public void testWrongImplementationDetection() throws Exception {
108         try {
109             new WrongDecoder().decode(session, buf, session.getDecoderOutput());
110             fail();
111         } catch (IllegalStateException e) {
112             // OK
113         }
114     }
115     
116     @Test
117     public void testBufferDerivation() throws Exception {
118         decoder = new DuplicatingIntegerDecoder();
119         
120         buf.putInt(1);
121         
122         // Put some extra byte to make the decoder create an internal buffer.
123         buf.put((byte) 0);
124         buf.flip();
125 
126         decoder.decode(session, buf, session.getDecoderOutput());
127         assertEquals(1, session.getDecoderOutputQueue().size());
128         assertEquals(1, session.getDecoderOutputQueue().poll());
129         assertEquals(buf.limit(), buf.position());
130 
131         // Keep appending to the internal buffer.
132         // DuplicatingIntegerDecoder will keep duplicating the internal
133         // buffer to disable auto-expansion, and CumulativeProtocolDecoder
134         // should detect that user derived its internal buffer.
135         // Consequently, CumulativeProtocolDecoder will perform 
136         // reallocation to avoid putting incoming data into
137         // the internal buffer with auto-expansion disabled.
138         for (int i = 2; i < 10; i ++) {
139             buf.clear();
140             buf.putInt(i);
141             // Put some extra byte to make the decoder keep the internal buffer.
142             buf.put((byte) 0);
143             buf.flip();
144             buf.position(1);
145     
146             decoder.decode(session, buf, session.getDecoderOutput());
147             assertEquals(1, session.getDecoderOutputQueue().size());
148             assertEquals(i, session.getDecoderOutputQueue().poll());
149             assertEquals(buf.limit(), buf.position());
150         }
151     }
152 
153     private static class IntegerDecoder extends CumulativeProtocolDecoder {
154         /**
155          * Default constructor
156          */
157         public IntegerDecoder() {
158             super();
159         }
160         
161         @Override
162         protected boolean doDecode(IoSession session, IoBuffer in,
163                 ProtocolDecoderOutput out) throws Exception {
164             assertTrue(in.hasRemaining());
165             
166             if (in.remaining() < 4) {
167                 return false;
168             }
169 
170             out.write(new Integer(in.getInt()));
171             return true;
172         }
173 
174         public void dispose() throws Exception {
175             // Do nothing
176         }
177     }
178     
179     private static class WrongDecoder extends CumulativeProtocolDecoder {
180         /**
181          * Default constructor
182          */
183         public WrongDecoder() {
184             super();
185         }
186         
187         @Override
188         protected boolean doDecode(IoSession session, IoBuffer in,
189                 ProtocolDecoderOutput out) throws Exception {
190             return true;
191         }
192 
193         public void dispose() throws Exception {
194             // Do nothing
195         }
196     }
197 
198     private static class DuplicatingIntegerDecoder extends IntegerDecoder {
199         /**
200          * Default constructor
201          */
202         public DuplicatingIntegerDecoder() {
203             super();
204         }
205         
206         @Override
207         protected boolean doDecode(IoSession session, IoBuffer in,
208                 ProtocolDecoderOutput out) throws Exception {
209             in.duplicate(); // Will disable auto-expansion.
210             assertFalse(in.isAutoExpand());
211             return super.doDecode(session, in, out);
212         }
213 
214         public void dispose() throws Exception {
215             // Do nothing
216         }
217     }
218 }