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 java.io.IOException;
23  import java.net.SocketAddress;
24  import java.util.Collection;
25  import java.util.Date;
26  
27  import junit.framework.Assert;
28  import junit.framework.TestCase;
29  
30  import org.apache.mina.core.buffer.IoBuffer;
31  import org.apache.mina.core.future.ConnectFuture;
32  import org.apache.mina.core.service.IoAcceptor;
33  import org.apache.mina.core.service.IoConnector;
34  import org.apache.mina.core.service.IoHandlerAdapter;
35  import org.apache.mina.core.session.IdleStatus;
36  import org.apache.mina.core.session.IoSession;
37  import org.apache.mina.transport.socket.DatagramAcceptor;
38  import org.apache.mina.transport.socket.DatagramSessionConfig;
39  import org.apache.mina.transport.socket.SocketAcceptor;
40  import org.apache.mina.transport.socket.SocketSessionConfig;
41  import org.slf4j.Logger;
42  import org.slf4j.LoggerFactory;
43  
44  /**
45   * Tests {@link IoAcceptor} resource leakage by repeating bind and unbind.
46   *
47   * @author The Apache MINA Project (dev@mina.apache.org)
48   * @version $Rev: 671827 $, $Date: 2008-06-26 10:49:48 +0200 (jeu, 26 jun 2008) $
49   */
50  public abstract class AbstractBindTest extends TestCase {
51      protected final IoAcceptor acceptor;
52  
53      protected int port;
54  
55      public AbstractBindTest(IoAcceptor acceptor) {
56          this.acceptor = acceptor;
57      }
58  
59      protected abstract SocketAddress createSocketAddress(int port);
60  
61      protected abstract int getPort(SocketAddress address);
62  
63      protected abstract IoConnector newConnector();
64  
65      protected void bind(boolean reuseAddress) throws IOException {
66          acceptor.setHandler(new EchoProtocolHandler());
67  
68          setReuseAddress(reuseAddress);
69  
70          // Find an available test port and bind to it.
71          boolean socketBound = false;
72  
73          // Let's start from port #1 to detect possible resource leak
74          // because test will fail in port 1-1023 if user run this test
75          // as a normal user.
76          for (port = 1; port <= 65535; port++) {
77              socketBound = false;
78              try {
79                  acceptor.setDefaultLocalAddress(createSocketAddress(port));
80                  acceptor.bind();
81                  socketBound = true;
82                  break;
83              } catch (IOException e) {
84                  //System.out.println(e.getMessage());
85              }
86          }
87  
88          // If there is no port available, test fails.
89          if (!socketBound) {
90              throw new IOException("Cannot bind any test port.");
91          }
92  
93          //System.out.println( "Using port " + port + " for testing." );
94      }
95  
96      private void setReuseAddress(boolean reuseAddress) {
97          if (acceptor instanceof DatagramAcceptor) {
98              ((DatagramSessionConfig) acceptor.getSessionConfig())
99                      .setReuseAddress(reuseAddress);
100         } else if (acceptor instanceof SocketAcceptor) {
101             ((SocketAcceptor) acceptor).setReuseAddress(reuseAddress);
102         }
103     }
104 
105     @Override
106     public void tearDown() {
107         try {
108             acceptor.dispose();
109         } catch (Exception e) {
110             // ignore
111         }
112 
113         acceptor.setDefaultLocalAddress(null);
114     }
115 
116     public void testAnonymousBind() throws Exception {
117         acceptor.setHandler(new IoHandlerAdapter());
118         acceptor.setDefaultLocalAddress(null);
119         acceptor.bind();
120         Assert.assertNotNull(acceptor.getLocalAddress());
121         acceptor.unbind(acceptor.getLocalAddress());
122         Assert.assertNull(acceptor.getLocalAddress());
123         acceptor.setDefaultLocalAddress(createSocketAddress(0));
124         acceptor.bind();
125         Assert.assertNotNull(acceptor.getLocalAddress());
126         Assert.assertTrue(getPort(acceptor.getLocalAddress()) != 0);
127         acceptor.unbind(acceptor.getLocalAddress());
128     }
129 
130     public void testDuplicateBind() throws IOException {
131         bind(false);
132 
133         try {
134             acceptor.bind();
135             Assert.fail("Exception is not thrown");
136         } catch (Exception e) {
137         }
138     }
139 
140     public void testDuplicateUnbind() throws IOException {
141         bind(false);
142 
143         // this should succeed
144         acceptor.unbind();
145 
146         // this shouldn't fail
147         acceptor.unbind();
148     }
149 
150     public void testManyTimes() throws IOException {
151         bind(true);
152 
153         for (int i = 0; i < 1024; i++) {
154             acceptor.unbind();
155             acceptor.bind();
156         }
157     }
158 
159     public void testUnbindDisconnectsClients() throws Exception {
160         bind(true);
161         IoConnector connector = newConnector();
162         IoSession[] sessions = new IoSession[5];
163         connector.setHandler(new IoHandlerAdapter());
164         for (int i = 0; i < sessions.length; i++) {
165             ConnectFuture future = connector.connect(createSocketAddress(port));
166             future.awaitUninterruptibly();
167             sessions[i] = future.getSession();
168             Assert.assertTrue(sessions[i].isConnected());
169             Assert.assertTrue(sessions[i].write(IoBuffer.allocate(1)).awaitUninterruptibly().isWritten());
170         }
171 
172         // Wait for the server side sessions to be created.
173         Thread.sleep(500);
174 
175         Collection<IoSession> managedSessions = acceptor.getManagedSessions().values();
176         Assert.assertEquals(5, managedSessions.size());
177 
178         acceptor.unbind();
179 
180         // Wait for the client side sessions to close.
181         Thread.sleep(500);
182 
183         Assert.assertEquals(0, managedSessions.size());
184         for (IoSession element : managedSessions) {
185             Assert.assertFalse(element.isConnected());
186         }
187     }
188 
189     public void _testRegressively() throws IOException {
190         setReuseAddress(true);
191 
192         SocketAddress addr = createSocketAddress(port);
193         EchoProtocolHandler handler = new EchoProtocolHandler();
194         acceptor.setDefaultLocalAddress(addr);
195         acceptor.setHandler(handler);
196         for (int i = 0; i < 1048576; i++) {
197             acceptor.bind();
198             acceptor.unbind();
199             if (i % 100 == 0) {
200                 System.out.println(i + " (" + new Date() + ")");
201             }
202         }
203         bind(false);
204     }
205 
206     private static class EchoProtocolHandler extends IoHandlerAdapter {
207         private static final Logger log = LoggerFactory
208                 .getLogger(EchoProtocolHandler.class);
209 
210         @Override
211         public void sessionCreated(IoSession session) {
212             if (session.getConfig() instanceof SocketSessionConfig) {
213                 ((SocketSessionConfig) session.getConfig())
214                         .setReceiveBufferSize(2048);
215             }
216 
217             session.getConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);
218         }
219 
220         @Override
221         public void sessionIdle(IoSession session, IdleStatus status) {
222             log.info("*** IDLE #" + session.getIdleCount(IdleStatus.BOTH_IDLE)
223                     + " ***");
224         }
225 
226         @Override
227         public void exceptionCaught(IoSession session, Throwable cause) {
228             cause.printStackTrace();
229             session.close();
230         }
231 
232         @Override
233         public void messageReceived(IoSession session, Object message)
234                 throws Exception {
235             if (!(message instanceof IoBuffer)) {
236                 return;
237             }
238 
239             IoBuffer rb = (IoBuffer) message;
240             // Write the received data back to remote peer
241             IoBuffer wb = IoBuffer.allocate(rb.remaining());
242             wb.put(rb);
243             wb.flip();
244             session.write(wb);
245         }
246     }
247 }