View Javadoc

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.transport.socket.nio;
21  
22  import static org.junit.Assert.assertEquals;
23  import static org.junit.Assert.assertNotSame;
24  import static org.junit.Assert.assertTrue;
25  
26  import java.net.InetSocketAddress;
27  
28  import org.apache.mina.core.buffer.IoBuffer;
29  import org.apache.mina.core.future.ConnectFuture;
30  import org.apache.mina.core.future.WriteFuture;
31  import org.apache.mina.core.service.IoHandlerAdapter;
32  import org.apache.mina.core.session.ExpiringSessionRecycler;
33  import org.apache.mina.core.session.IdleStatus;
34  import org.apache.mina.core.session.IoSession;
35  import org.apache.mina.util.AvailablePortFinder;
36  import org.junit.After;
37  import org.junit.Before;
38  import org.junit.Test;
39  
40  /**
41   * Tests if datagram sessions are recycled properly.
42   *
43   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
44   */
45  public class DatagramRecyclerTest {
46      private NioDatagramAcceptor acceptor;
47      private NioDatagramConnector connector;
48  
49      public DatagramRecyclerTest() {
50          // Do nothing
51      }
52  
53      @Before
54      public void setUp() throws Exception {
55          acceptor = new NioDatagramAcceptor();
56          connector = new NioDatagramConnector();
57      }
58  
59      @After
60      public void tearDown() throws Exception {
61          acceptor.dispose();
62          connector.dispose();
63      }
64  
65      @Test
66      public void testDatagramRecycler() throws Exception {
67          int port = AvailablePortFinder.getNextAvailable(1024);
68          ExpiringSessionRecycler recycler = new ExpiringSessionRecycler(1, 1);
69  
70          MockHandler acceptorHandler = new MockHandler();
71          MockHandler connectorHandler = new MockHandler();
72  
73          acceptor.setHandler(acceptorHandler);
74          acceptor.setSessionRecycler(recycler);
75          acceptor.bind(new InetSocketAddress(port));
76  
77          try {
78              connector.setHandler(connectorHandler);
79              ConnectFuture future = connector.connect(new InetSocketAddress(
80                      "localhost", port));
81              future.awaitUninterruptibly();
82  
83              // Write whatever to trigger the acceptor.
84              future.getSession().write(IoBuffer.allocate(1))
85                      .awaitUninterruptibly();
86  
87              // Close the client-side connection.
88              // This doesn't mean that the acceptor-side connection is also closed.
89              // The life cycle of the acceptor-side connection is managed by the recycler.
90              future.getSession().close(true);
91              future.getSession().getCloseFuture().awaitUninterruptibly();
92              assertTrue(future.getSession().getCloseFuture().isClosed());
93  
94              // Wait until the acceptor-side connection is closed.
95              while (acceptorHandler.session == null) {
96                  Thread.yield();
97              }
98              acceptorHandler.session.getCloseFuture().awaitUninterruptibly(3000);
99  
100             // Is it closed?
101             assertTrue(acceptorHandler.session.getCloseFuture()
102                     .isClosed());
103 
104             Thread.sleep(1000);
105 
106             assertEquals("CROPSECL", connectorHandler.result.toString());
107             assertEquals("CROPRECL", acceptorHandler.result.toString());
108         } finally {
109             acceptor.unbind();
110         }
111     }
112     
113     @Test
114     public void testCloseRequest() throws Exception {
115         int port = AvailablePortFinder.getNextAvailable(1024);
116         ExpiringSessionRecycler recycler = new ExpiringSessionRecycler(10, 1);
117 
118         MockHandler acceptorHandler = new MockHandler();
119         MockHandler connectorHandler = new MockHandler();
120 
121         acceptor.getSessionConfig().setIdleTime(IdleStatus.READER_IDLE, 1);
122         acceptor.setHandler(acceptorHandler);
123         acceptor.setSessionRecycler(recycler);
124         acceptor.bind(new InetSocketAddress(port));
125 
126         try {
127             connector.setHandler(connectorHandler);
128             ConnectFuture future = connector.connect(new InetSocketAddress(
129                     "localhost", port));
130             future.awaitUninterruptibly();
131             
132             // Write whatever to trigger the acceptor.
133             future.getSession().write(IoBuffer.allocate(1)).awaitUninterruptibly();
134 
135             // Make sure the connection is closed before recycler closes it.
136             while (acceptorHandler.session == null) {
137                 Thread.yield();
138             }
139             acceptorHandler.session.close(true);
140             assertTrue(
141                     acceptorHandler.session.getCloseFuture().awaitUninterruptibly(3000));
142             
143             IoSession oldSession = acceptorHandler.session;
144 
145             // Wait until all events are processed and clear the state.
146             long startTime = System.currentTimeMillis();
147             while (acceptorHandler.result.length() < 8) {
148                 Thread.yield();
149                 if (System.currentTimeMillis() - startTime > 5000) {
150                     throw new Exception();
151                 }
152             }
153             acceptorHandler.result.setLength(0);
154             acceptorHandler.session = null;
155             
156             // Write whatever to trigger the acceptor again.
157             WriteFuture wf = future.getSession().write(
158                     IoBuffer.allocate(1)).awaitUninterruptibly();
159             assertTrue(wf.isWritten());
160             
161             // Make sure the connection is closed before recycler closes it.
162             while (acceptorHandler.session == null) {
163                 Thread.yield();
164             }
165             acceptorHandler.session.close(true);
166             assertTrue(
167                     acceptorHandler.session.getCloseFuture().awaitUninterruptibly(3000));
168 
169             future.getSession().close(true).awaitUninterruptibly();
170             
171             assertNotSame(oldSession, acceptorHandler.session);
172         } finally {
173             acceptor.unbind();
174         }
175     }
176 
177     private class MockHandler extends IoHandlerAdapter {
178         public volatile IoSession session;
179         public final StringBuffer result = new StringBuffer();
180 
181         /**
182          * Default constructor
183          */
184         public MockHandler() {
185             super();
186         }
187         
188         @Override
189         public void exceptionCaught(IoSession session, Throwable cause)
190                 throws Exception {
191             this.session = session;
192             result.append("CA");
193         }
194 
195         @Override
196         public void messageReceived(IoSession session, Object message)
197                 throws Exception {
198             this.session = session;
199             result.append("RE");
200         }
201 
202         @Override
203         public void messageSent(IoSession session, Object message)
204                 throws Exception {
205             this.session = session;
206             result.append("SE");
207         }
208 
209         @Override
210         public void sessionClosed(IoSession session) throws Exception {
211             this.session = session;
212             result.append("CL");
213         }
214 
215         @Override
216         public void sessionCreated(IoSession session) throws Exception {
217             this.session = session;
218             result.append("CR");
219         }
220 
221         @Override
222         public void sessionIdle(IoSession session, IdleStatus status)
223                 throws Exception {
224             this.session = session;
225             result.append("ID");
226         }
227 
228         @Override
229         public void sessionOpened(IoSession session) throws Exception {
230             this.session = session;
231             result.append("OP");
232         }
233     }
234 }