org.springframework.integration.test.util.SocketUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.integration.test.util.SocketUtils.java

Source

/*
 * Copyright 2002-2019 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.integration.test.util;

import java.io.IOException;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javax.net.ServerSocketFactory;

import org.springframework.util.Assert;

/**
 * Contains several socket-specific utility methods. For example, you may have test cases
 * that require an open port. Rather than hard-coding the relevant port, it will be better
 * to use methods from this utility class to automatically select an open port, therefore
 * improving the portability of your test-cases across systems.
 *
 * @deprecated - it's generally better to set the server port to 0; let the operating
 * system choose a port; wait until the server starts (see TestingUtilities in the ip
 * module for an example), then set the port on the client factory.
 *
 * @author Gunnar Hillert
 * @author Gary Russell
 * @since 2.2
 */
@Deprecated
public final class SocketUtils {

    public static final int DEFAULT_PORT_RANGE_MIN = 10000;

    public static final int DEFAULT_PORT_RANGE_MAX = 60000;

    /**
     * The constructor is intentionally public. In several test cases you may have
     * the need to use the methods of this class multiple times from within your
     * Spring Application Context XML file using SpEL. Of course you can do:
     * <pre class="code">
     * {@code
     * ...port="#{T(org.springframework.integration.test.util.SocketUtils).findAvailableServerSocket(12000)}"
     * }
     * </pre>
     * But unfortunately, you would need to repeat the package for each usage.
     * This will be acceptable for single use, but if you need to invoke the
     * methods numerous time, you may instead want to do this:
     * <pre class="code">
     * {@code
     * <bean id="tcpIpUtils" class="org.springframework.integration.test.util.SocketUtils" />
     *
     * ...port="#{tcpIpUtils.findAvailableServerSocket(12000)}"
     * }
     * </pre>
     */
    private SocketUtils() {
    }

    /**
     * Determines a free available server socket (port) using the 'seed' value as
     * the starting port. The utility methods will probe for 200 sockets but will
     * return as soon an open port is found.
     * @param seed The starting port, which must not be negative.
     * @return An available port number
     * @throws IllegalStateException when no open port was found.
     */
    public static int findAvailableServerSocket(int seed) {
        final List<Integer> openPorts = findAvailableServerSockets(seed, 1);
        return openPorts.get(0);
    }

    /**
     * Determines a free available server socket (port) using the 'seed' value as
     * the starting port. The utility methods will probe for 200 sockets but will
     * return as soon an open port is found.
     * @param seed                   The starting port, which must not be negative.
     * @param numberOfRequestedPorts How many open ports shall be retrieved?
     * @return A list containing the requested number of open ports
     * @throws IllegalStateException when no open port was found.
     */
    public static List<Integer> findAvailableServerSockets(int seed, int numberOfRequestedPorts) {

        Assert.isTrue(seed >= 0, "'seed' must not be negative");
        Assert.isTrue(numberOfRequestedPorts > 0, "'numberOfRequestedPorts' must not be negative");

        final List<Integer> openPorts = new ArrayList<Integer>(numberOfRequestedPorts);

        for (int i = seed; i < seed + 200; i = i == 0 ? i : i + 1) {
            try {
                ServerSocket sock = ServerSocketFactory.getDefault().createServerSocket(i, 1,
                        InetAddress.getByName("localhost"));
                sock.close();
                openPorts.add(i == 0 ? sock.getLocalPort() : i);

                if (openPorts.size() == numberOfRequestedPorts) {
                    return openPorts;
                }

            } catch (@SuppressWarnings("unused") IOException e) {
                // empty
            }
        }

        throw new IllegalStateException(
                String.format("Cannot find a free server socket (%s requested)", numberOfRequestedPorts));
    }

    /**
     * Determines a free available server socket (port) using an automatically
     * chosen start seed port.
     * @return An available port number
     * @throws IllegalStateException when no open port was found.
     */
    public static int findAvailableServerSocket() {
        int seed = getRandomSeedPort();
        return findAvailableServerSocket(seed);
    }

    /**
     * Determines a free available Udp socket (port) using the 'seed' value as
     * the starting port. The utility methods will probe for 200 sockets but will
     * return as soon an open port is found.
     * @param seed The starting port, which must not be negative.
     * @return An available port number
     * @throws IllegalStateException when no open port was found.
     */
    public static int findAvailableUdpSocket(int seed) {
        final List<Integer> openPorts = findAvailableUdpSockets(seed, 1);
        return openPorts.get(0);
    }

    /**
     * Determines free available udp socket(s) (port) using the 'seed' value as
     * the starting port. The utility methods will probe for 200 sockets but will
     * return as soon an open port is found.
     * @param seed                   The starting port, which must not be negative.
     * @param numberOfRequestedPorts How many open ports shall be retrieved?
     * @return A list containing the requested number of open ports
     * @throws IllegalStateException when no open port was found.
     */
    public static List<Integer> findAvailableUdpSockets(int seed, int numberOfRequestedPorts) {

        Assert.isTrue(seed >= 0, "'seed' must not be negative");
        Assert.isTrue(numberOfRequestedPorts > 0, "'numberOfRequestedPorts' must not be negative");

        final List<Integer> openPorts = new ArrayList<Integer>(numberOfRequestedPorts);

        for (int i = seed; i < seed + 200; i++) {
            try {
                DatagramSocket sock = new DatagramSocket(i, InetAddress.getByName("localhost"));
                sock.close();
                Thread.sleep(100);

                openPorts.add(i);

                if (openPorts.size() == numberOfRequestedPorts) {
                    return openPorts;
                }

            } catch (@SuppressWarnings("unused") IOException e) {
                // empty
            } catch (@SuppressWarnings("unused") InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }

        throw new IllegalStateException(
                String.format("Cannot find a free server socket (%s requested)", numberOfRequestedPorts));
    }

    /**
     * Determines a free available Udp socket using an automatically
     * chosen start seed port.
     * @return An available port number
     * @throws IllegalStateException when no open port was found.
     */
    public static int findAvailableUdpSocket() {
        int seed = getRandomSeedPort();
        return findAvailableUdpSocket(seed);
    }

    /**
     * Determines a random seed port number within the port range
     * {@value #DEFAULT_PORT_RANGE_MIN} and {@value #DEFAULT_PORT_RANGE_MAX}.
     * @return A number with the the specified range
     */
    public static int getRandomSeedPort() {
        return new Random().nextInt(DEFAULT_PORT_RANGE_MAX - DEFAULT_PORT_RANGE_MIN + 1) + DEFAULT_PORT_RANGE_MIN;
    }

}