    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 12:19:00 +0900 (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 = acceptor.getManagedServiceAddresses()
    117                 .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 }