org.mule.transport.tcp.issues.LingerExperimentMule2067TestCase.java Source code

Java tutorial

Introduction

Here is the source code for org.mule.transport.tcp.issues.LingerExperimentMule2067TestCase.java

Source

/*
 * $Id$
 * --------------------------------------------------------------------------------------
 * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
 *
 * The software in this package is published under the terms of the CPAL v1.0
 * license, a copy of which has been included with this distribution in the
 * LICENSE.txt file.
 */

package org.mule.transport.tcp.issues;

import org.mule.tck.junit4.AbstractMuleTestCase;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Test;

/**
 * Are the "address already in use" errors coming from lingering sockets?
 *
 * We see "address already in use" errors when trying to re-use sockets very quickly,
 * but the tests below don't give much information, except that:
 * - data needs to be sent
 * - explicitly setting or disabling the SO_LINGER value has little effect
 */
public class LingerExperimentMule2067TestCase extends AbstractMuleTestCase {

    private static final int NO_LINGER = -1;
    private static final int HARD_CLOSE = 0;
    private static final int NO_WAIT = -1;
    private static final int PORT = 65432;

    private Log logger = LogFactory.getLog(getClass());

    @Test
    public void testInoffensive() throws IOException {
        // this shows it's not simple open/close that causes a problem
        openCloseServer(1000, PORT); // ok
        openCloseClientServer(1000, PORT, NO_LINGER, NO_LINGER); // ok
    }

    @Test
    public void testThisShowsTheProblem() throws IOException {
        // this shows a problem with repeated open/close with a client/server pair
        repeatOpenCloseClientServer(10, 10, PORT, 1000); // ok
        repeatOpenCloseClientServer(10, 10, PORT, 100); // ok
        repeatOpenCloseClientServer(10, 10, PORT, 10); // ok
        repeatOpenCloseClientServer(10, 10, PORT, 1); // ok
        repeatOpenCloseClientServer(10, 10, PORT, 0); // intermittent
        repeatOpenCloseClientServer(10, 10, PORT, NO_WAIT); // intermittent
    }

    @Test
    public void testWithClientLinger() throws IOException {
        // this shows it's not simple client linger time, or the later tests would always fail
        repeatOpenCloseClientServer(10, 10, PORT, NO_WAIT, NO_LINGER); // intermittent, as above
        repeatOpenCloseClientServer(10, 10, PORT, 100, 1); // ok
        repeatOpenCloseClientServer(10, 10, PORT, 10, 1); // intermittent
        repeatOpenCloseClientServer(10, 10, PORT, 100, 2); // intermittent
        repeatOpenCloseClientServer(10, 10, PORT, 100, 30); // intermittent
        // hard close on client doesn't help
        repeatOpenCloseClientServer(10, 10, PORT, 10, HARD_CLOSE); // intermittent
        repeatOpenCloseClientServer(10, 10, PORT, NO_WAIT, HARD_CLOSE); // intermittent
    }

    @Test
    public void testWithServerLinger() throws IOException {
        // server linger seems to improve things(?!), but still have problems
        repeatOpenCloseClientServer(10, 10, PORT, 10, NO_LINGER, 1); // ok
        repeatOpenCloseClientServer(10, 10, PORT, 10, NO_LINGER, 1); // ok
        repeatOpenCloseClientServer(10, 10, PORT, 10, NO_LINGER, 2); // ok
        repeatOpenCloseClientServer(10, 10, PORT, 10, NO_LINGER, 30); // ok
        repeatOpenCloseClientServer(10, 10, PORT, NO_WAIT, NO_LINGER, 1); // intermittent
    }

    @Test
    public void testHardClose() throws IOException {
        // this gives (very?) occasional "already in use" and also a "connection reset by peer"
        // at the client, due to server closing so quickly
        repeatOpenCloseClientServer(10, 10, PORT, NO_WAIT, HARD_CLOSE, HARD_CLOSE); // intermittent
    }

    protected void openCloseServer(int numberOfSockets, int port) throws IOException {
        for (int i = 0; i < numberOfSockets; i++) {
            ServerSocket socket = new ServerSocket(port);
            socket.close();
        }
    }

    protected void repeatOpenCloseClientServer(int numberOfRepeats, int numberOfConnections, int port, long pause)
            throws IOException {
        repeatOpenCloseClientServer(numberOfRepeats, numberOfConnections, port, pause, NO_LINGER);
    }

    protected void repeatOpenCloseClientServer(int numberOfRepeats, int numberOfConnections, int port, long pause,
            int clientLinger) throws IOException {
        repeatOpenCloseClientServer(numberOfRepeats, numberOfConnections, port, pause, clientLinger, NO_LINGER);
    }

    protected void repeatOpenCloseClientServer(int numberOfRepeats, int numberOfConnections, int port, long pause,
            int clientLinger, int serverLinger) throws IOException {
        logger.info("Repeating openCloseClientServer with pauses of " + pause + " ms and lingers of " + clientLinger
                + "/" + serverLinger + " s (client/server)");
        for (int i = 0; i < numberOfRepeats; i++) {
            if (0 != i && pause != NO_WAIT) {
                try {
                    synchronized (this) {
                        if (pause > 0) {
                            this.wait(pause);
                        }
                    }
                } catch (InterruptedException e) {
                    // ignore
                }
            }
            openCloseClientServer(numberOfConnections, port, clientLinger, serverLinger);
        }
    }

    protected void openCloseClientServer(int numberOfConnections, int port, int clientLinger, int serverLinger)
            throws IOException {
        Server server = new Server(port, serverLinger);
        try {
            new Thread(server).start();
            for (int i = 0; i < numberOfConnections; i++) {
                logger.debug("opening socket " + i);
                Socket client = new Socket("localhost", port);
                if (NO_LINGER != clientLinger) {
                    client.setSoLinger(true, clientLinger);
                }
                client.close();
            }
        } finally {
            server.close();
        }
    }

    protected static class Server implements Runnable {

        private Log logger = LogFactory.getLog(getClass());
        private ServerSocket server;
        private int linger;

        public Server(int port, int linger) throws IOException {
            this.linger = linger;
            server = new ServerSocket();
            server.bind(new InetSocketAddress("localhost", port));
        }

        public void run() {
            try {
                while (true) {
                    Socket socket = server.accept();
                    if (NO_LINGER != linger) {
                        socket.setSoLinger(true, linger);
                    }
                    socket.close();
                }
            } catch (Exception e) {
                logger.debug("Expected - dirty closedown: " + e);
            }
        }

        public void close() throws IOException {
            server.close();
            server = null;
        }
    }

}