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()).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 }