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