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 = 1; 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())
104                     .setReuseAddress(reuseAddress);
105         } else if (acceptor instanceof SocketAcceptor) {
106             ((SocketAcceptor) acceptor).setReuseAddress(reuseAddress);
107         }
108     }
109 
110     @After
111     public void tearDown() {
112         try {
113             acceptor.dispose();
114         } catch (Exception e) {
115             // ignore
116         }
117 
118         acceptor.setDefaultLocalAddress(null);
119     }
120 
121     @Test
122     public void testAnonymousBind() throws Exception {
123         acceptor.setHandler(new IoHandlerAdapter());
124         acceptor.setDefaultLocalAddress(null);
125         acceptor.bind();
126         assertNotNull(acceptor.getLocalAddress());
127         acceptor.unbind(acceptor.getLocalAddress());
128         assertNull(acceptor.getLocalAddress());
129         acceptor.setDefaultLocalAddress(createSocketAddress(0));
130         acceptor.bind();
131         assertNotNull(acceptor.getLocalAddress());
132         assertTrue(getPort(acceptor.getLocalAddress()) != 0);
133         acceptor.unbind(acceptor.getLocalAddress());
134     }
135 
136     @Test
137     public void testDuplicateBind() throws IOException {
138         bind(false);
139 
140         try {
141             acceptor.bind();
142             fail("Exception is not thrown");
143         } catch (Exception e) {
144             // Signifies a successfull test case execution
145             assertTrue(true);
146         }
147     }
148 
149     @Test
150     public void testDuplicateUnbind() throws IOException {
151         bind(false);
152 
153         // this should succeed
154         acceptor.unbind();
155 
156         // this shouldn't fail
157         acceptor.unbind();
158     }
159 
160     @Test
161     public void testManyTimes() throws IOException {
162         bind(true);
163 
164         for (int i = 0; i < 1024; i++) {
165             acceptor.unbind();
166             acceptor.bind();
167         }
168     }
169 
170     @Test
171     public void testUnbindDisconnectsClients() throws Exception {
172         bind(true);
173         IoConnector connector = newConnector();
174         IoSession[] sessions = new IoSession[5];
175         connector.setHandler(new IoHandlerAdapter());
176         for (int i = 0; i < sessions.length; i++) {
177             ConnectFuture future = connector.connect(createSocketAddress(port));
178             future.awaitUninterruptibly();
179             sessions[i] = future.getSession();
180             assertTrue(sessions[i].isConnected());
181             assertTrue(sessions[i].write(IoBuffer.allocate(1)).awaitUninterruptibly().isWritten());
182         }
183 
184         // Wait for the server side sessions to be created.
185         Thread.sleep(500);
186 
187         Collection<IoSession> managedSessions = acceptor.getManagedSessions().values();
188         assertEquals(5, managedSessions.size());
189 
190         acceptor.unbind();
191 
192         // Wait for the client side sessions to close.
193         Thread.sleep(500);
194 
195         assertEquals(0, managedSessions.size());
196         for (IoSession element : managedSessions) {
197             assertFalse(element.isConnected());
198         }
199     }
200 
201     @Test
202     public void testUnbindResume() throws Exception {
203         bind(true);
204         IoConnector connector = newConnector();
205         IoSession session = null;
206         connector.setHandler(new IoHandlerAdapter());
207         
208         ConnectFuture future = connector.connect(createSocketAddress(port));
209         future.awaitUninterruptibly();
210         session = future.getSession();
211         assertTrue(session.isConnected());
212         assertTrue(session.write(IoBuffer.allocate(1)).awaitUninterruptibly().isWritten());
213 
214         // Wait for the server side session to be created.
215         Thread.sleep(500);
216 
217         Collection<IoSession> managedSession = acceptor.getManagedSessions().values();
218         assertEquals(1, managedSession.size());
219 
220         acceptor.unbind();
221 
222         // Wait for the client side sessions to close.
223         Thread.sleep(500);
224 
225         assertEquals(0, managedSession.size());
226         for (IoSession element : managedSession) {
227             assertFalse(element.isConnected());
228         }
229         
230         // Rebind
231         bind(true);
232         
233         // Check again the connection
234         future = connector.connect(createSocketAddress(port));
235         future.awaitUninterruptibly();
236         session = future.getSession();
237         assertTrue(session.isConnected());
238         assertTrue(session.write(IoBuffer.allocate(1)).awaitUninterruptibly().isWritten());
239 
240         // Wait for the server side session to be created.
241         Thread.sleep(500);
242 
243         managedSession = acceptor.getManagedSessions().values();
244         assertEquals(1, managedSession.size());
245     }
246 
247     @Test
248     @Ignore
249     public void testRegressively() throws IOException {
250         setReuseAddress(true);
251 
252         SocketAddress addr = createSocketAddress(port);
253         EchoProtocolHandler handler = new EchoProtocolHandler();
254         acceptor.setDefaultLocalAddress(addr);
255         acceptor.setHandler(handler);
256         for (int i = 0; i < 1048576; i++) {
257             acceptor.bind();
258             acceptor.unbind();
259         }
260         bind(false);
261     }
262 
263     private static class EchoProtocolHandler extends IoHandlerAdapter {
264         private static final Logger LOG = LoggerFactory
265                 .getLogger(EchoProtocolHandler.class);
266 
267         /**
268          * Default constructor
269          */
270         public EchoProtocolHandler() {
271             super();
272         }
273         
274         @Override
275         public void sessionCreated(IoSession session) {
276             if (session.getConfig() instanceof SocketSessionConfig) {
277                 ((SocketSessionConfig) session.getConfig())
278                         .setReceiveBufferSize(2048);
279             }
280 
281             session.getConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);
282         }
283 
284         @Override
285         public void sessionIdle(IoSession session, IdleStatus status) {
286             LOG.info("*** IDLE #" + session.getIdleCount(IdleStatus.BOTH_IDLE)
287                     + " ***");
288         }
289 
290         @Override
291         public void exceptionCaught(IoSession session, Throwable cause) {
292             //cause.printStackTrace();
293             session.close(true);
294         }
295 
296         @Override
297         public void messageReceived(IoSession session, Object message)
298                 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 = IoBuffer.allocate(rb.remaining());
306             wb.put(rb);
307             wb.flip();
308             session.write(wb);
309         }
310     }
311 }