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;
21  
22  import static org.junit.Assert.assertEquals;
23  import static org.junit.Assert.assertFalse;
24  import static org.junit.Assert.assertNotNull;
25  import static org.junit.Assert.assertNull;
26  import static org.junit.Assert.assertTrue;
27  import static org.junit.Assert.fail;
28  
29  import java.io.IOException;
30  import java.net.SocketAddress;
31  import java.util.Collection;
32  
33  import org.apache.mina.core.buffer.IoBuffer;
34  import org.apache.mina.core.future.ConnectFuture;
35  import org.apache.mina.core.service.IoAcceptor;
36  import org.apache.mina.core.service.IoConnector;
37  import org.apache.mina.core.service.IoHandlerAdapter;
38  import org.apache.mina.core.session.IdleStatus;
39  import org.apache.mina.core.session.IoSession;
40  import org.apache.mina.transport.socket.DatagramAcceptor;
41  import org.apache.mina.transport.socket.DatagramSessionConfig;
42  import org.apache.mina.transport.socket.SocketAcceptor;
43  import org.apache.mina.transport.socket.SocketSessionConfig;
44  import org.apache.mina.util.AvailablePortFinder;
45  import org.junit.After;
46  import org.junit.Assert;
47  import org.junit.Ignore;
48  import org.junit.Test;
49  import org.slf4j.Logger;
50  import org.slf4j.LoggerFactory;
51  
52  /**
53   * Tests {@link IoAcceptor} resource leakage by repeating bind and unbind.
54   *
55   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
56   */
57  public abstract class AbstractBindTest {
58      protected final IoAcceptor acceptor;
59  
60      protected int port;
61  
62      public AbstractBindTest(IoAcceptor acceptor) {
63          this.acceptor = acceptor;
64      }
65  
66      protected abstract SocketAddress createSocketAddress(int port);
67  
68      protected abstract int getPort(SocketAddress address);
69  
70      protected abstract IoConnector newConnector();
71  
72      protected void bind(boolean reuseAddress) throws IOException {
73          acceptor.setHandler(new EchoProtocolHandler());
74  
75          setReuseAddress(reuseAddress);
76  
77          // Find an available test port and bind to it.
78          boolean socketBound = false;
79  
80          // Let's start from port #1 to detect possible resource leak
81          // because test will fail in port 1-1023 if user run this test
82          // as a normal user.
83          port = AvailablePortFinder.getNextAvailable();
84          socketBound = false;
85          try {
86              acceptor.setDefaultLocalAddress(createSocketAddress(port));
87              acceptor.bind();
88              socketBound = true;
89          } catch (IOException e) {
90              //System.out.println(e.getMessage());
91          }
92  
93          // If there is no port available, test fails.
94          if (!socketBound) {
95              throw new IOException("Cannot bind any test port.");
96          }
97  
98          //System.out.println( "Using port " + port + " for testing." );
99      }
100 
101     private void setReuseAddress(boolean reuseAddress) {
102         if (acceptor instanceof DatagramAcceptor) {
103             ((DatagramSessionConfig) acceptor.getSessionConfig()).setReuseAddress(reuseAddress);
104         } else if (acceptor instanceof SocketAcceptor) {
105             ((SocketAcceptor) acceptor).setReuseAddress(reuseAddress);
106         }
107     }
108 
109     @After
110     public void tearDown() {
111         try {
112             acceptor.dispose();
113         } catch (Exception e) {
114             // ignore
115         }
116 
117         acceptor.setDefaultLocalAddress(null);
118     }
119 
120     @Test
121     public void testAnonymousBind() throws Exception {
122         acceptor.setHandler(new IoHandlerAdapter());
123         acceptor.setDefaultLocalAddress(null);
124         acceptor.bind();
125         assertNotNull(acceptor.getLocalAddress());
126         acceptor.unbind(acceptor.getLocalAddress());
127         assertNull(acceptor.getLocalAddress());
128         acceptor.setDefaultLocalAddress(createSocketAddress(0));
129         acceptor.bind();
130         assertNotNull(acceptor.getLocalAddress());
131         assertTrue(getPort(acceptor.getLocalAddress()) != 0);
132         acceptor.unbind(acceptor.getLocalAddress());
133     }
134 
135     @Test
136     public void testDuplicateBind() throws IOException {
137         bind(false);
138 
139         try {
140             acceptor.bind();
141             fail("Exception is not thrown");
142         } catch (Exception e) {
143             // Signifies a successfull test case execution
144             assertTrue(true);
145         }
146     }
147 
148     @Test
149     public void testDuplicateUnbind() throws IOException {
150         bind(false);
151 
152         // this should succeed
153         acceptor.unbind();
154 
155         // this shouldn't fail
156         acceptor.unbind();
157     }
158 
159     @Test
160     public void testManyTimes() throws IOException, InterruptedException {
161         bind(true);
162 
163 		for (int i = 0; i < 1024; i++) {
164 			Assert.assertTrue("Bound addresses is empty", acceptor.getLocalAddresses().size() > 0);
165 			acceptor.unbind();
166 			Thread.sleep(1);
167 			Assert.assertTrue("Bound addresses is not empty", acceptor.getLocalAddresses().size() == 0);
168 			acceptor.bind();
169 		}
170 
171         acceptor.unbind();
172     }
173 
174     @Test
175     public void testUnbindDisconnectsClients() throws Exception {
176         bind(true);
177         IoConnector connector = newConnector();
178         IoSession[] sessions = new IoSession[5];
179         connector.setHandler(new IoHandlerAdapter());
180         for (int i = 0; i < sessions.length; i++) {
181             ConnectFuture future = connector.connect(createSocketAddress(port));
182             future.awaitUninterruptibly();
183             sessions[i] = future.getSession();
184             assertTrue(sessions[i].isConnected());
185             assertTrue(sessions[i].write(IoBuffer.allocate(1)).awaitUninterruptibly().isWritten());
186         }
187 
188         // Wait for the server side sessions to be created.
189         Thread.sleep(500);
190 
191         Collection<IoSession> managedSessions = acceptor.getManagedSessions().values();
192         assertEquals(5, managedSessions.size());
193 
194         acceptor.unbind();
195 
196         // Wait for the client side sessions to close.
197         Thread.sleep(500);
198 
199         assertEquals(0, managedSessions.size());
200         for (IoSession element : managedSessions) {
201             assertFalse(element.isConnected());
202         }
203     }
204 
205     @Test
206     public void testUnbindResume() throws Exception {
207         bind(true);
208         IoConnector connector = newConnector();
209         IoSession session = null;
210         connector.setHandler(new IoHandlerAdapter());
211 
212         ConnectFuture future = connector.connect(createSocketAddress(port));
213         future.awaitUninterruptibly();
214         session = future.getSession();
215         assertTrue(session.isConnected());
216         assertTrue(session.write(IoBuffer.allocate(1)).awaitUninterruptibly().isWritten());
217 
218         // Wait for the server side session to be created.
219         Thread.sleep(500);
220 
221         Collection<IoSession> managedSession = acceptor.getManagedSessions().values();
222         assertEquals(1, managedSession.size());
223 
224         acceptor.unbind();
225 
226         // Wait for the client side sessions to close.
227         Thread.sleep(500);
228 
229         assertEquals(0, managedSession.size());
230         for (IoSession element : managedSession) {
231             assertFalse(element.isConnected());
232         }
233 
234         // Rebind
235         bind(true);
236 
237         // Check again the connection
238         future = connector.connect(createSocketAddress(port));
239         future.awaitUninterruptibly();
240         session = future.getSession();
241         assertTrue(session.isConnected());
242         assertTrue(session.write(IoBuffer.allocate(1)).awaitUninterruptibly().isWritten());
243 
244         // Wait for the server side session to be created.
245         Thread.sleep(500);
246 
247         managedSession = acceptor.getManagedSessions().values();
248         assertEquals(1, managedSession.size());
249     }
250 
251     @Test
252     @Ignore
253     public void testRegressively() throws IOException {
254         setReuseAddress(true);
255 
256         SocketAddress addr = createSocketAddress(port);
257         EchoProtocolHandler handler = new EchoProtocolHandler();
258         acceptor.setDefaultLocalAddress(addr);
259         acceptor.setHandler(handler);
260         for (int i = 0; i < 1048576; i++) {
261             acceptor.bind();
262             acceptor.unbind();
263         }
264         bind(false);
265     }
266 
267     private static class EchoProtocolHandler extends IoHandlerAdapter {
268         private static final Logger LOG = LoggerFactory.getLogger(EchoProtocolHandler.class);
269 
270         /**
271          * Default constructor
272          */
273         public EchoProtocolHandler() {
274             super();
275         }
276 
277         @Override
278         public void sessionCreated(IoSession session) {
279             if (session.getConfig() instanceof SocketSessionConfig) {
280                 ((SocketSessionConfig) session.getConfig()).setReceiveBufferSize(2048);
281             }
282 
283             session.getConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);
284         }
285 
286         @Override
287         public void sessionIdle(IoSession session, IdleStatus status) {
288             LOG.info("*** IDLE #" + session.getIdleCount(IdleStatus.BOTH_IDLE) + " ***");
289         }
290 
291         @Override
292         public void exceptionCaught(IoSession session, Throwable cause) {
293             //cause.printStackTrace();
294             session.closeNow();
295         }
296 
297         @Override
298         public void messageReceived(IoSession session, Object message) throws Exception {
299             if (!(message instanceof IoBuffer)) {
300                 return;
301             }
302 
303             IoBuffer rb = (IoBuffer) message;
304             // Write the received data back to remote peer
305             IoBuffer wb = rb.duplicate();
306             session.write(wb);
307         }
308     }
309 }