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