/*
* Copyright 2002-2011 the original author or authors, or Red-Black IT Ltd, as appropriate.
*
* 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 com.redblackit.web.server;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Random;
import org.apache.log4j.Logger;
import org.springframework.util.Assert;
/**
* Utility class that finds free BSD ports for use in testing scenario's.
*
* Based on the Spring test class of the same name, but allowing supplied ports
* to be checked 9and so used) first
*/
public abstract class HostNetUtils {
public static final int MIN_SAFE_PORT = 1024;
public static final int MAX_PORT = 65535;
private static final Random random = new Random();
private static final Logger logger = Logger.getLogger("web.server");
/**
* Returns the number of a free port in the default range.
*/
public static int getFreePort() {
return getFreePort(0, 0, 0);
}
/**
* Returns the number of a free port in the default range, starting from
* supplied value
*
* @param startPort
* port value to try first
*/
public static int getFreePort(int startPort) {
return getFreePort(0, 0, startPort);
}
/**
* Returns the number of a free port in the given range, starting from
* supplied value. Note that the supplied value need not be between the min
* and max values, but will be ignored if <= 0.
*
* @param minPort
* minimum port value (default MIN_SAFE_PORT)
* @param maxPort
* maximum port value (default MAX_PORT)
* @param startPort
* port value to try first
*/
public static int getFreePort(int minPort, int maxPort, int startPort) {
if (minPort <= 0)
{
logger.info("defaulting minPort:" + minPort + "->" + MIN_SAFE_PORT);
minPort = MIN_SAFE_PORT;
}
if (maxPort <= 0 || maxPort > MAX_PORT)
{
logger.info("defaulting maxPort:" + maxPort + "->" + MAX_PORT);
maxPort = MAX_PORT;
}
Assert.state(minPort < maxPort, "after defaulting, minPort=" + minPort + " >= maxPort=" + maxPort);
if (startPort > 0 && isPortAvailable(startPort)) {
logger.info("Using startPort=" + startPort);
return startPort;
}
int candidatePort;
int portRange = maxPort - minPort;
int searchCounter = 0;
do {
if (++searchCounter > portRange) {
throw new IllegalStateException(String.format(
"There were no ports available in the range %d to %d",
minPort, maxPort));
}
candidatePort = getRandomPort(minPort, portRange);
logger.info("Trying candidatePort=" + candidatePort);
} while (!isPortAvailable(candidatePort));
logger.info("Using candidatePort=" + candidatePort);
return candidatePort;
}
/**
* Get local hostname
*
* @return hostname
*/
public static String getLocalHostname() {
String localhost;
try {
localhost = InetAddress.getLocalHost().getHostName();
logger.debug("localhost=" + localhost);
} catch (UnknownHostException ex) {
localhost = "localhost";
logger.warn("couldn't get localhost name, defaulting to localhost");
}
logger.debug("resulting localhost=" + localhost);
return localhost;
}
/**
* Main providing facility to run from command line
*
* @param args
*/
public void main(String[] args) {
final String msgpfx = HostNetUtils.getLocalHostname() + "-" + HostNetUtils.class.getName();
logger.info(msgpfx + " " + Arrays.toString(args));
try {
final int startPort = (args.length > 0 ? Integer.valueOf(args[0])
: -1);
final int minPort = (args.length > 1 ? Integer.valueOf(args[1])
: MIN_SAFE_PORT);
final int maxPort = (args.length > 2 ? Integer.valueOf(args[2])
: MAX_PORT);
final int freePort = getFreePort(minPort, maxPort, startPort);
logger.info(msgpfx + ":freePort=" + freePort);
} catch (Throwable t) {
logger.fatal(msgpfx + " usage:java " + msgpfx
+ " [startPort [minPort [maxPort]]]", t);
}
}
/**
* Get a rendom port value
*
* @param minPort
* @param portRange
* @return
*/
private static int getRandomPort(int minPort, int portRange) {
return minPort + random.nextInt(portRange);
}
/**
* Check if port is available
*
* @param port
* @return
*/
private static boolean isPortAvailable(int port) {
ServerSocket serverSocket;
try {
serverSocket = new ServerSocket();
} catch (IOException ex) {
throw new IllegalStateException("Unable to create ServerSocket.",
ex);
}
try {
InetSocketAddress sa = new InetSocketAddress(port);
serverSocket.bind(sa);
return true;
} catch (IOException ex) {
return false;
} finally {
try {
serverSocket.close();
} catch (IOException ex) {
// ignore
}
}
}
}