/* * Copyright 2002-2016 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 * * http://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.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. * @author Gunnar Hillert * @author Gary Russell * @since 2.2 */ 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 (Exception e) { } } 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 (Exception e) { } } 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; } }