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