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

Java tutorial

Introduction

Here is the source code for org.mule.transport.tcp.issues.ReuseExperimentMule2067TestCase.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.BindException;
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;

/**
 * Can we avoid the "address already in use" errors by using SO_REUSEADDR?
 *
 * Typical results are
<pre>
 [07-24 19:32:49] INFO  ReuseExperimentMule2067TestCase [main]: Measuring average run length for 100 repeats without reuse and a pause of 100 ms
 [07-24 19:33:49] INFO  ReuseExperimentMule2067TestCase [main]: Average run length: 57.3 +/- 33.15131973240282
 [07-24 19:33:49] INFO  ReuseExperimentMule2067TestCase [main]: Measuring average run length for 100 repeats with reuse and a pause of 100 ms
 [07-24 19:35:32] INFO  ReuseExperimentMule2067TestCase [main]: Average run length: 100.0 +/- 0.0
 [07-24 19:35:32] INFO  ReuseExperimentMule2067TestCase [main]: Measuring average run length for 100 repeats without reuse and a pause of 10 ms
 [07-24 19:35:48] INFO  ReuseExperimentMule2067TestCase [main]: Average run length: 96.8 +/- 7.332121111929359
 [07-24 19:35:48] INFO  ReuseExperimentMule2067TestCase [main]: Measuring average run length for 100 repeats with reuse and a pause of 10 ms
 [07-24 19:36:04] INFO  ReuseExperimentMule2067TestCase [main]: Average run length: 100.0 +/- 0.0
 [07-24 19:36:04] INFO  ReuseExperimentMule2067TestCase [main]: Measuring average run length for 100 repeats without reuse and a pause of 1 ms
 [07-24 19:36:10] INFO  ReuseExperimentMule2067TestCase [main]: Average run length: 75.8 +/- 37.690317058894586
 [07-24 19:36:10] INFO  ReuseExperimentMule2067TestCase [main]: Measuring average run length for 100 repeats with reuse and a pause of 1 ms
 [07-24 19:36:18] INFO  ReuseExperimentMule2067TestCase [main]: Average run length: 100.0 +/- 0.0
</pre>
 * which suggest that enabling address re-use could help with the issue.
 *
 * Note that if a single socket (ie a single port number) is reused for all tests we often
 * zeroes eveywhere (even with waits of 2sec and similar between iterations/tests).  This
 * suggests that once the error occurs, the socket enters a long-lived "broken" state.
 *
 * All this is by AC on linux, dual CPU, Java 1.4 - I suspect results will vary like crazy
 * in different contexts.
 */
public class ReuseExperimentMule2067TestCase extends AbstractMuleTestCase {

    private static final int NO_WAIT = -1;
    private static final int PORT = 65432;
    private static boolean NO_REUSE = false;
    private static boolean REUSE = true;

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

    @Test
    public void testReuse() throws IOException {
        repeatOpenCloseClientServer(1000, 10, PORT, 1, REUSE, false); // fails, but less often?
        repeatOpenCloseClientServer(100, 10, PORT, 1, NO_REUSE, false); // intermittent
    }

    @Test
    public void testMeasureImprovement() throws IOException {
        measureMeanRunLength(10, 100, 10, PORT, 100, NO_REUSE);
        measureMeanRunLength(10, 100, 10, PORT + 10, 100, REUSE);
        measureMeanRunLength(10, 100, 10, PORT + 20, 10, NO_REUSE);
        measureMeanRunLength(10, 100, 10, PORT + 30, 10, REUSE);
        measureMeanRunLength(10, 100, 10, PORT + 40, 1, NO_REUSE);
        measureMeanRunLength(10, 100, 10, PORT + 50, 1, REUSE);
    }

    protected void measureMeanRunLength(int sampleSize, int numberOfRepeats, int numberOfConnections, int port,
            long pause, boolean reuse) throws IOException {
        logger.info("Measuring average run length for " + numberOfRepeats + " repeats "
                + (reuse ? "with" : "without") + " reuse and a pause of " + pause + " ms");
        int totalLength = 0;
        long totalLengthSquared = 0;
        for (int i = 0; i < sampleSize; ++i) {
            int length = repeatOpenCloseClientServer(numberOfRepeats, numberOfConnections, port + i, pause, reuse,
                    true);
            totalLength += length;
            totalLengthSquared += length * length;
        }
        double mean = totalLength / (double) sampleSize;
        double sd = Math.sqrt(totalLengthSquared / (double) sampleSize - mean * mean);
        logger.info("Average run length: " + mean + " +/- " + sd);
    }

    protected int repeatOpenCloseClientServer(int numberOfRepeats, int numberOfConnections, int port, long pause,
            boolean reuse, boolean noFail) throws IOException {
        String message = "Repeating openCloseClientServer with pauses of " + pause + " ms "
                + (reuse ? "with" : "without") + " reuse";
        if (noFail) {
            logger.debug(message);
        } else {
            logger.info(message);
        }
        for (int i = 0; i < numberOfRepeats; i++) {
            if (0 != i) {
                pause(pause);
            }
            try {
                openCloseClientServer(numberOfConnections, port, reuse);
            } catch (BindException e) {
                if (noFail && e.getMessage().indexOf("Address already in use") > -1) {
                    return i;
                }
                throw e;
            }
        }
        return numberOfRepeats;
    }

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

    protected void pause(long pause) {
        if (pause != NO_WAIT) {
            try {
                synchronized (this) {
                    if (pause > 0) {
                        this.wait(pause);
                    }
                }
            } catch (InterruptedException e) {
                // ignore
            }
        }
    }

    protected static class Server implements Runnable {

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

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

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

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

}