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.junit.After;
45  import org.junit.Ignore;
46  import org.junit.Test;
47  import org.slf4j.Logger;
48  import org.slf4j.LoggerFactory;
49  
50  /**
51   * Tests {@link IoAcceptor} resource leakage by repeating bind and unbind.
52   *
53   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
54   */
55  public abstract class AbstractBindTest {
56      protected final IoAcceptor acceptor;
57  
58      protected int port;
59  
60      public AbstractBindTest(IoAcceptor acceptor) {
61          this.acceptor = acceptor;
62      }
63  
64      protected abstract SocketAddress createSocketAddress(int port);
65  
66      protected abstract int getPort(SocketAddress address);
67  
68      protected abstract IoConnector newConnector();
69  
70      protected void bind(boolean reuseAddress) throws IOException {
71          acceptor.setHandler(new EchoProtocolHandler());
72  
73          setReuseAddress(reuseAddress);
74  
75          // Find an available test port and bind to it.
76          boolean socketBound = false;
77  
78          // Let's start from port #1 to detect possible resource leak
79          // because test will fail in port 1-1023 if user run this test
80          // as a normal user.
81          for (port = 1024; port <= 65535; port++) {
82              socketBound = false;
83              try {
84                  acceptor.setDefaultLocalAddress(createSocketAddress(port));
85                  acceptor.bind();
86                  socketBound = true;
87                  break;
88              } catch (IOException e) {
89                  //System.out.println(e.getMessage());
90              }
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 {
161         bind(true);
162 
163         for (int i = 0; i < 1024; i++) {
164             acceptor.unbind();
165             acceptor.bind();
166         }
167     }
168 
169     @Test
170     public void testUnbindDisconnectsClients() throws Exception {
171         bind(true);
172         IoConnector connector = newConnector();
173         IoSession[] sessions = new IoSession[5];
174         connector.setHandler(new IoHandlerAdapter());
175         for (int i = 0; i < sessions.length; i++) {
176             ConnectFuture future = connector.connect(createSocketAddress(port));
177             future.awaitUninterruptibly();
178             sessions[i] = future.getSession();
179             assertTrue(sessions[i].isConnected());
180             assertTrue(sessions[i].write(IoBuffer.allocate(1)).awaitUninterruptibly().isWritten());
181         }
182 
183         // Wait for the server side sessions to be created.
184         Thread.sleep(500);
185 
186         Collection<IoSession> managedSessions = acceptor.getManagedSessions().values();
187         assertEquals(5, managedSessions.size());
188 
189         acceptor.unbind();
190 
191         // Wait for the client side sessions to close.
192         Thread.sleep(500);
193 
194         assertEquals(0, managedSessions.size());
195         for (IoSession element : managedSessions) {
196             assertFalse(element.isConnected());
197         }
198     }
199 
200     @Test
201     public void testUnbindResume() throws Exception {
202         bind(true);
203         IoConnector connector = newConnector();
204         IoSession session = null;
205         connector.setHandler(new IoHandlerAdapter());
206 
207         ConnectFuture future = connector.connect(createSocketAddress(port));
208         future.awaitUninterruptibly();
209         session = future.getSession();
210         assertTrue(session.isConnected());
211         assertTrue(session.write(IoBuffer.allocate(1)).awaitUninterruptibly().isWritten());
212 
213         // Wait for the server side session to be created.
214         Thread.sleep(500);
215 
216         Collection<IoSession> managedSession = acceptor.getManagedSessions().values();
217         assertEquals(1, managedSession.size());
218 
219         acceptor.unbind();
220 
221         // Wait for the client side sessions to close.
222         Thread.sleep(500);
223 
224         assertEquals(0, managedSession.size());
225         for (IoSession element : managedSession) {
226             assertFalse(element.isConnected());
227         }
228 
229         // Rebind
230         bind(true);
231 
232         // Check again the connection
233         future = connector.connect(createSocketAddress(port));
234         future.awaitUninterruptibly();
235         session = future.getSession();
236         assertTrue(session.isConnected());
237         assertTrue(session.write(IoBuffer.allocate(1)).awaitUninterruptibly().isWritten());
238 
239         // Wait for the server side session to be created.
240         Thread.sleep(500);
241 
242         managedSession = acceptor.getManagedSessions().values();
243         assertEquals(1, managedSession.size());
244     }
245 
246     @Test
247     @Ignore
248     public void testRegressively() throws IOException {
249         setReuseAddress(true);
250 
251         SocketAddress addr = createSocketAddress(port);
252         EchoProtocolHandler handler = new EchoProtocolHandler();
253         acceptor.setDefaultLocalAddress(addr);
254         acceptor.setHandler(handler);
255         for (int i = 0; i < 1048576; i++) {
256             acceptor.bind();
257             acceptor.unbind();
258         }
259         bind(false);
260     }
261 
262     private static class EchoProtocolHandler extends IoHandlerAdapter {
263         private static final Logger LOG = LoggerFactory.getLogger(EchoProtocolHandler.class);
264 
265         /**
266          * Default constructor
267          */
268         public EchoProtocolHandler() {
269             super();
270         }
271 
272         @Override
273         public void sessionCreated(IoSession session) {
274             if (session.getConfig() instanceof SocketSessionConfig) {
275                 ((SocketSessionConfig) session.getConfig()).setReceiveBufferSize(2048);
276             }
277 
278             session.getConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);
279         }
280 
281         @Override
282         public void sessionIdle(IoSession session, IdleStatus status) {
283             LOG.info("*** IDLE #" + session.getIdleCount(IdleStatus.BOTH_IDLE) + " ***");
284         }
285 
286         @Override
287         public void exceptionCaught(IoSession session, Throwable cause) {
288             //cause.printStackTrace();
289             session.close(true);
290         }
291 
292         @Override
293         public void messageReceived(IoSession session, Object message) throws Exception {
294             if (!(message instanceof IoBuffer)) {
295                 return;
296             }
297 
298             IoBuffer rb = (IoBuffer) message;
299             // Write the received data back to remote peer
300             IoBuffer wb = IoBuffer.allocate(rb.remaining());
301             wb.put(rb);
302             wb.flip();
303             session.write(wb);
304         }
305     }
306 }