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.reqres;
21  
22  import java.util.NoSuchElementException;
23  import java.util.concurrent.Executors;
24  import java.util.concurrent.ScheduledExecutorService;
25  
26  import junit.framework.TestCase;
27  
28  import org.apache.mina.core.filterchain.IoFilterChain;
29  import org.apache.mina.core.filterchain.IoFilter.NextFilter;
30  import org.apache.mina.core.session.DummySession;
31  import org.apache.mina.core.session.IoSession;
32  import org.apache.mina.core.write.DefaultWriteRequest;
33  import org.apache.mina.core.write.WriteRequest;
34  import org.easymock.AbstractMatcher;
35  import org.easymock.MockControl;
36  import org.junit.After;
37  import org.junit.Assert;
38  import org.junit.Before;
39  import org.junit.Test;
40  
41  /**
42   * Tests {@link RequestResponseFilter}.
43   *
44   * @author The Apache MINA Project (dev@mina.apache.org)
45   * @version $Rev: 671827 $, $Date: 2008-06-26 10:49:48 +0200 (Thu, 26 Jun 2008) $
46   */
47  public class RequestResponseFilterTest extends TestCase {
48  
49      private ScheduledExecutorService scheduler;
50  
51      private RequestResponseFilter filter;
52  
53      private IoSession session;
54  
55      private IoFilterChain chain;
56  
57      private NextFilter nextFilter;
58  
59      private MockControl nextFilterControl;
60  
61      private final WriteRequestMatcher matcher = new WriteRequestMatcher();
62  
63      @Override
64      @Before
65      public void setUp() throws Exception {
66          scheduler = Executors.newScheduledThreadPool(1);
67          filter = new RequestResponseFilter(new MessageInspector(), scheduler);
68  
69          // Set up mock objects.
70          session = new DummySession();
71          chain = session.getFilterChain();
72          nextFilterControl = MockControl.createControl(NextFilter.class);
73          nextFilter = (NextFilter) nextFilterControl.getMock();
74  
75          // Initialize the filter.
76          filter.onPreAdd(chain, "reqres", nextFilter);
77          filter.onPostAdd(chain, "reqres", nextFilter);
78          Assert.assertFalse(session.getAttributeKeys().isEmpty());
79      }
80  
81      @Override
82      @After
83      public void tearDown() throws Exception {
84          // Destroy the filter.
85          filter.onPreRemove(chain, "reqres", nextFilter);
86          filter.onPostRemove(chain, "reqres", nextFilter);
87          filter.destroy();
88          filter = null;
89          scheduler.shutdown();
90      }
91  
92      @Test
93      public void testWholeResponse() throws Exception {
94          Request req = new Request(1, new Object(), Long.MAX_VALUE);
95          Response res = new Response(req, new Message(1, ResponseType.WHOLE),
96                  ResponseType.WHOLE);
97          WriteRequest rwr = new DefaultWriteRequest(req);
98  
99          // Record
100         nextFilter.filterWrite(session, new DefaultWriteRequest(req
101                 .getMessage()));
102         nextFilterControl.setMatcher(matcher);
103         nextFilter.messageSent(session, rwr);
104         nextFilter.messageReceived(session, res);
105 
106         // Replay
107         nextFilterControl.replay();
108         filter.filterWrite(nextFilter, session, rwr);
109         filter.messageSent(nextFilter, session, matcher.getLastWriteRequest());
110         filter.messageReceived(nextFilter, session, res.getMessage());
111         filter.messageReceived(nextFilter, session, res.getMessage()); // Ignored
112 
113         // Verify
114         nextFilterControl.verify();
115         Assert.assertEquals(res, req.awaitResponse());
116         assertNoSuchElementException(req);
117     }
118 
119     private void assertNoSuchElementException(Request req)
120             throws InterruptedException {
121         // Make sure if an exception is thrown if a user waits one more time.
122         try {
123             req.awaitResponse();
124             Assert.fail();
125         } catch (NoSuchElementException e) {
126             // OK
127         }
128     }
129 
130     @Test
131     public void testPartialResponse() throws Exception {
132         Request req = new Request(1, new Object(), Long.MAX_VALUE);
133         Response res1 = new Response(req, new Message(1, ResponseType.PARTIAL),
134                 ResponseType.PARTIAL);
135         Response res2 = new Response(req, new Message(1,
136                 ResponseType.PARTIAL_LAST), ResponseType.PARTIAL_LAST);
137         WriteRequest rwr = new DefaultWriteRequest(req);
138 
139         // Record
140         nextFilter.filterWrite(session, new DefaultWriteRequest(req
141                 .getMessage()));
142         nextFilterControl.setMatcher(matcher);
143         nextFilter.messageSent(session, rwr);
144         nextFilter.messageReceived(session, res1);
145         nextFilter.messageReceived(session, res2);
146 
147         // Replay
148         nextFilterControl.replay();
149         filter.filterWrite(nextFilter, session, rwr);
150         filter.messageSent(nextFilter, session, matcher.getLastWriteRequest());
151         filter.messageReceived(nextFilter, session, res1.getMessage());
152         filter.messageReceived(nextFilter, session, res2.getMessage());
153         filter.messageReceived(nextFilter, session, res1.getMessage()); // Ignored
154         filter.messageReceived(nextFilter, session, res2.getMessage()); // Ignored
155 
156         // Verify
157         nextFilterControl.verify();
158         Assert.assertEquals(res1, req.awaitResponse());
159         Assert.assertEquals(res2, req.awaitResponse());
160         assertNoSuchElementException(req);
161     }
162 
163     @Test
164     public void testWholeResponseTimeout() throws Exception {
165         Request req = new Request(1, new Object(), 10); // 10ms timeout
166         Response res = new Response(req, new Message(1, ResponseType.WHOLE),
167                 ResponseType.WHOLE);
168         WriteRequest rwr = new DefaultWriteRequest(req);
169 
170         // Record
171         nextFilter.filterWrite(session, new DefaultWriteRequest(req
172                 .getMessage()));
173         nextFilterControl.setMatcher(matcher);
174         nextFilter.messageSent(session, rwr);
175         nextFilter.exceptionCaught(session, new RequestTimeoutException(req));
176         nextFilterControl.setMatcher(new ExceptionMatcher());
177 
178         // Replay
179         nextFilterControl.replay();
180         filter.filterWrite(nextFilter, session, rwr);
181         filter.messageSent(nextFilter, session, matcher.getLastWriteRequest());
182         Thread.sleep(300); // Wait until the request times out.
183         filter.messageReceived(nextFilter, session, res.getMessage()); // Ignored
184 
185         // Verify
186         nextFilterControl.verify();
187         assertRequestTimeoutException(req);
188         assertNoSuchElementException(req);
189     }
190 
191     private void assertRequestTimeoutException(Request req)
192             throws InterruptedException {
193         try {
194             req.awaitResponse();
195             Assert.fail();
196         } catch (RequestTimeoutException e) {
197             // OK
198         }
199     }
200 
201     @Test
202     public void testPartialResponseTimeout() throws Exception {
203         Request req = new Request(1, new Object(), 10); // 10ms timeout
204         Response res1 = new Response(req, new Message(1, ResponseType.PARTIAL),
205                 ResponseType.PARTIAL);
206         Response res2 = new Response(req, new Message(1,
207                 ResponseType.PARTIAL_LAST), ResponseType.PARTIAL_LAST);
208         WriteRequest rwr = new DefaultWriteRequest(req);
209 
210         // Record
211         nextFilter.filterWrite(session, new DefaultWriteRequest(req
212                 .getMessage()));
213         nextFilterControl.setMatcher(matcher);
214         nextFilter.messageSent(session, rwr);
215         nextFilter.messageReceived(session, res1);
216         nextFilter.exceptionCaught(session, new RequestTimeoutException(req));
217         nextFilterControl.setMatcher(new ExceptionMatcher());
218 
219         // Replay
220         nextFilterControl.replay();
221         filter.filterWrite(nextFilter, session, rwr);
222         filter.messageSent(nextFilter, session, matcher.getLastWriteRequest());
223         filter.messageReceived(nextFilter, session, res1.getMessage());
224         Thread.sleep(300); // Wait until the request times out.
225         filter.messageReceived(nextFilter, session, res2.getMessage()); // Ignored
226         filter.messageReceived(nextFilter, session, res1.getMessage()); // Ignored
227 
228         // Verify
229         nextFilterControl.verify();
230         Assert.assertEquals(res1, req.awaitResponse());
231         assertRequestTimeoutException(req);
232         assertNoSuchElementException(req);
233     }
234 
235     @Test
236     public void testTimeoutByDisconnection() throws Exception {
237         // We run a test case that doesn't raise a timeout to make sure
238         // the timeout is not raised again by disconnection.
239         testWholeResponse();
240         nextFilterControl.reset();
241 
242         Request req1 = new Request(1, new Object(), Long.MAX_VALUE);
243         Request req2 = new Request(2, new Object(), Long.MAX_VALUE);
244         WriteRequest rwr1 = new DefaultWriteRequest(req1);
245         WriteRequest rwr2 = new DefaultWriteRequest(req2);
246 
247         // Record
248         nextFilter.filterWrite(session, new DefaultWriteRequest(req1
249                 .getMessage()));
250         nextFilterControl.setMatcher(matcher);
251         nextFilter.messageSent(session, rwr1);
252         nextFilter.filterWrite(session, new DefaultWriteRequest(req2
253                 .getMessage()));
254         nextFilter.messageSent(session, rwr2);
255         nextFilter.exceptionCaught(session, new RequestTimeoutException(req1));
256         nextFilterControl.setMatcher(new ExceptionMatcher());
257         nextFilter.exceptionCaught(session, new RequestTimeoutException(req2));
258         nextFilter.sessionClosed(session);
259 
260         // Replay
261         nextFilterControl.replay();
262         filter.filterWrite(nextFilter, session, rwr1);
263         filter.messageSent(nextFilter, session, matcher.getLastWriteRequest());
264         filter.filterWrite(nextFilter, session, rwr2);
265         filter.messageSent(nextFilter, session, matcher.getLastWriteRequest());
266         filter.sessionClosed(nextFilter, session);
267 
268         // Verify
269         nextFilterControl.verify();
270         assertRequestTimeoutException(req1);
271         assertRequestTimeoutException(req2);
272     }
273 
274     private static class Message {
275         private final int id;
276 
277         private final ResponseType type;
278 
279         private Message(int id, ResponseType type) {
280             this.id = id;
281             this.type = type;
282         }
283 
284         public int getId() {
285             return id;
286         }
287 
288         public ResponseType getType() {
289             return type;
290         }
291     }
292 
293     private static class MessageInspector implements ResponseInspector {
294 
295         public Object getRequestId(Object message) {
296             if (!(message instanceof Message)) {
297                 return null;
298             }
299 
300             return ((Message) message).getId();
301         }
302 
303         public ResponseType getResponseType(Object message) {
304             if (!(message instanceof Message)) {
305                 return null;
306             }
307 
308             return ((Message) message).getType();
309         }
310     }
311 
312     private static class WriteRequestMatcher extends AbstractMatcher {
313         private WriteRequest lastWriteRequest;
314 
315         public WriteRequest getLastWriteRequest() {
316             return lastWriteRequest;
317         }
318 
319         @Override
320         protected boolean argumentMatches(Object expected, Object actual) {
321             if (actual instanceof WriteRequest
322                     && expected instanceof WriteRequest) {
323                 boolean answer = ((WriteRequest) expected).getMessage().equals(
324                         ((WriteRequest) actual).getMessage());
325                 lastWriteRequest = (WriteRequest) actual;
326                 return answer;
327             }
328             return super.argumentMatches(expected, actual);
329         }
330     }
331 
332     private static class ExceptionMatcher extends AbstractMatcher {
333         @Override
334         protected boolean argumentMatches(Object expected, Object actual) {
335             if (actual instanceof RequestTimeoutException
336                     && expected instanceof RequestTimeoutException) {
337                 return ((RequestTimeoutException) expected)
338                         .getRequest()
339                         .equals(((RequestTimeoutException) actual).getRequest());
340             }
341             return super.argumentMatches(expected, actual);
342         }
343     }
344 }