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.Date;
25  
26  import junit.framework.Assert;
27  import junit.framework.TestCase;
28  
29  import org.apache.mina.common.ByteBuffer;
30  import org.apache.mina.common.IdleStatus;
31  import org.apache.mina.common.IoAcceptor;
32  import org.apache.mina.common.IoHandlerAdapter;
33  import org.apache.mina.common.IoSession;
34  import org.apache.mina.transport.socket.nio.DatagramAcceptor;
35  import org.apache.mina.transport.socket.nio.DatagramSessionConfig;
36  import org.apache.mina.transport.socket.nio.SocketAcceptor;
37  import org.apache.mina.transport.socket.nio.SocketAcceptorConfig;
38  import org.apache.mina.transport.socket.nio.SocketSessionConfig;
39  import org.slf4j.Logger;
40  import org.slf4j.LoggerFactory;
41  
42  /**
43   * Tests {@link IoAcceptor} resource leakage by repeating bind and unbind.
44   * 
45   * @author The Apache Directory Project (mina-dev@directory.apache.org)
46   * @version $Rev: 555855 $, $Date: 2007-07-13 05:19:00 +0200 (Fri, 13 Jul 2007) $ 
47   */
48  public abstract class AbstractBindTest extends TestCase {
49      protected final IoAcceptor acceptor;
50  
51      protected int port;
52  
53      public AbstractBindTest(IoAcceptor acceptor) {
54          this.acceptor = acceptor;
55      }
56  
57      protected abstract SocketAddress createSocketAddress(int port);
58  
59      protected abstract int getPort(SocketAddress address);
60  
61      protected void bind(boolean reuseAddress) throws IOException {
62          setReuseAddress(reuseAddress);
63  
64          // Find an availble test port and bind to it.
65          boolean socketBound = false;
66  
67          // Let's start from port #1 to detect possible resource leak
68          // because test will fail in port 1-1023 if user run this test
69          // as a normal user.
70          for (port = 1; port <= 65535; port++) {
71              socketBound = false;
72              try {
73                  acceptor.bind(createSocketAddress(port),
74                          new EchoProtocolHandler());
75                  socketBound = true;
76                  break;
77              } catch (IOException e) {
78              }
79          }
80  
81          // If there is no port available, test fails.
82          if (!socketBound) {
83              throw new IOException("Cannot bind any test port.");
84          }
85  
86          //System.out.println( "Using port " + port + " for testing." );
87      }
88  
89      private void setReuseAddress(boolean reuseAddress) {
90          if (acceptor instanceof DatagramAcceptor) {
91              ((DatagramSessionConfig) acceptor.getDefaultConfig()
92                      .getSessionConfig()).setReuseAddress(reuseAddress);
93          } else if (acceptor instanceof SocketAcceptor) {
94              ((SocketAcceptorConfig) acceptor.getDefaultConfig())
95                      .setReuseAddress(reuseAddress);
96          }
97      }
98  
99      public void tearDown() {
100         try {
101             acceptor.unbindAll();
102         } catch (Exception e) {
103             // ignore
104         }
105     }
106 
107     public void testAnonymousBind() throws Exception {
108         acceptor.bind(null, new IoHandlerAdapter());
109         Assert.assertEquals(1, acceptor.getManagedServiceAddresses().size());
110         acceptor.unbindAll();
111         Thread.sleep(500);
112         Assert.assertEquals(0, acceptor.getManagedServiceAddresses().size());
113 
114         acceptor.bind(createSocketAddress(0), new IoHandlerAdapter());
115         Assert.assertEquals(1, acceptor.getManagedServiceAddresses().size());
116         SocketAddress address = (SocketAddress) acceptor
117                 .getManagedServiceAddresses().iterator().next();
118         Assert.assertTrue(getPort(address) != 0);
119         acceptor.unbind(address);
120     }
121 
122     public void testDuplicateBind() throws IOException {
123         bind(false);
124 
125         try {
126             acceptor.bind(createSocketAddress(port), new EchoProtocolHandler());
127             Assert.fail("IOException is not thrown");
128         } catch (IOException e) {
129         }
130     }
131 
132     public void testDuplicateUnbind() throws IOException {
133         bind(false);
134 
135         // this should succeed
136         acceptor.unbind(createSocketAddress(port));
137 
138         try {
139             // this should fail
140             acceptor.unbind(createSocketAddress(port));
141             Assert.fail("Exception is not thrown");
142         } catch (Exception e) {
143         }
144     }
145 
146     public void testManyTimes() throws IOException {
147         bind(true);
148 
149         SocketAddress addr = createSocketAddress(port);
150         EchoProtocolHandler handler = new EchoProtocolHandler();
151         for (int i = 0; i < 1024; i++) {
152             acceptor.unbind(addr);
153             acceptor.bind(addr, handler);
154         }
155     }
156 
157     public void _testRegressively() throws IOException {
158         setReuseAddress(true);
159 
160         SocketAddress addr = createSocketAddress(port);
161         EchoProtocolHandler handler = new EchoProtocolHandler();
162         for (int i = 0; i < 1048576; i++) {
163             acceptor.bind(addr, handler);
164             testDuplicateBind();
165             testDuplicateUnbind();
166             if (i % 100 == 0) {
167                 System.out.println(i + " (" + new Date() + ")");
168             }
169         }
170         bind(false);
171     }
172 
173     private static class EchoProtocolHandler extends IoHandlerAdapter {
174         private static final Logger log = LoggerFactory
175                 .getLogger(EchoProtocolHandler.class);
176 
177         public void sessionCreated(IoSession session) {
178             if (session.getConfig() instanceof SocketSessionConfig) {
179                 ((SocketSessionConfig) session.getConfig())
180                         .setReceiveBufferSize(2048);
181             }
182 
183             session.setIdleTime(IdleStatus.BOTH_IDLE, 10);
184         }
185 
186         public void sessionIdle(IoSession session, IdleStatus status) {
187             log.info("*** IDLE #" + session.getIdleCount(IdleStatus.BOTH_IDLE)
188                     + " ***");
189         }
190 
191         public void exceptionCaught(IoSession session, Throwable cause) {
192             cause.printStackTrace();
193             session.close();
194         }
195 
196         public void messageReceived(IoSession session, Object message)
197                 throws Exception {
198             if (!(message instanceof ByteBuffer)) {
199                 return;
200             }
201 
202             ByteBuffer rb = (ByteBuffer) message;
203             // Write the received data back to remote peer
204             ByteBuffer wb = ByteBuffer.allocate(rb.remaining());
205             wb.put(rb);
206             wb.flip();
207             session.write(wb);
208         }
209     }
210 }