blob: 5ab892064ceefb78983589ce18257389d2a1b3e4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2003, 2005 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - Initial API and implementation
*******************************************************************************/
package org.eclipse.wst.server.core.util;
import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import org.eclipse.wst.server.core.internal.Trace;
/**
* A utility class for socket-related function. It's main purposes are to find
* unused ports, check whether a port is in use, and check whether a given
* address is a local(host) address.
*
* @since 1.0
*/
public class SocketUtil {
private static final Random rand = new Random(System.currentTimeMillis());
protected static final Object lock = new Object();
private static List localHostCache;
private static List addressCache;
/**
* Static utility class - cannot create an instance.
*/
private SocketUtil() {
// cannot create
}
/**
* Finds an unused port between the given from and to values.
*
* @param low lowest possible port number
* @param high highest possible port number
* @return an usused port number, or <code>-1</code> if no used ports could be found
*/
public static int findUnusedPort(int low, int high) {
if (high < low)
return -1;
for (int i = 0; i < 10; i++) {
int port = getRandomPort(low, high);
if (!isPortInUse(port))
return port;
}
return -1;
}
/**
* Return a random port number in the given range.
*
* @param low lowest possible port number
* @param high highest possible port number
* @return a random port number in the given range
*/
private static int getRandomPort(int low, int high) {
return rand.nextInt(high - low) + low;
}
/**
* Checks to see if the given port number is being used.
* Returns <code>true</code> if the given port is in use, and <code>false</code>
* otherwise. Retries every 500ms for "count" tries.
*
* @param port the port number to check
* @param count the number of times to retry
* @return boolean <code>true</code> if the port is in use, and
* <code>false</code> otherwise
*/
public static boolean isPortInUse(int port, int count) {
boolean inUse = isPortInUse(port);
while (inUse && count > 0) {
try {
Thread.sleep(500);
} catch (Exception e) {
// ignore
}
inUse = isPortInUse(port);
count --;
}
return inUse;
}
/**
* Checks to see if the given port number is being used.
* Returns <code>true</code> if the given port is in use, and <code>false</code>
* otherwise.
*
* @param port the port number to check
* @return boolean <code>true</code> if the port is in use, and
* <code>false</code> otherwise
*/
public static boolean isPortInUse(int port) {
ServerSocket s = null;
try {
s = new ServerSocket(port);
} catch (SocketException e) {
return true;
} catch (IOException e) {
return true;
} catch (Exception e) {
return true;
} finally {
if (s != null) {
try {
s.close();
} catch (Exception e) {
// ignore
}
}
}
return false;
}
/**
* Checks if the given host (name, fully qualified name, or IP address) is
* referring to the local machine.
* <p>
* The first time this method is called (or the first call after each time
* the network configuration has changed, e.g. by the user switching from a
* wired connection to wireless) a background process is used to cache the
* network information. On most machines the network information will be found
* quickly and the results of this call will be returned immediately.
* </p><p>
* On machines where the network configuration of the machine is bad or the
* network has problems, this first method call will take at most 250ms, but
* the results may be incorrect (incomplete).
* </p><p>
* All subsequent calls (until the network configuration changes) will
* return very quickly. If the background process is still running it will
* continue to fill the cache and each subsequent call to this method may be
* more correct.
* </p>
*
* @param host a hostname or IP address
* @return <code>true</code> if the given host is localhost, and
* <code>false</code> otherwise
*/
public static boolean isLocalhost(String host) {
if (host == null)
return false;
if ("localhost".equals(host) || "127.0.0.1".equals(host))
return true;
// check if cache is ok
try {
// get network interfaces
final List currentAddresses = new ArrayList();
currentAddresses.add(InetAddress.getLocalHost());
Enumeration nis = NetworkInterface.getNetworkInterfaces();
while (nis.hasMoreElements()) {
NetworkInterface inter = (NetworkInterface) nis.nextElement();
Enumeration ias = inter.getInetAddresses();
while (ias.hasMoreElements())
currentAddresses.add(ias.nextElement());
}
// check if cache is empty or old and refill it if necessary
if (addressCache == null || !addressCache.containsAll(currentAddresses) || !currentAddresses.containsAll(addressCache)) {
addressCache = currentAddresses;
final List addressList = new ArrayList(currentAddresses.size() * 3);
Iterator iter = currentAddresses.iterator();
while (iter.hasNext()) {
InetAddress addr = (InetAddress) iter.next();
String a = addr.getHostAddress();
if (a != null && !addressList.contains(a))
addressList.add(a);
}
synchronized (lock) {
localHostCache = addressList;
}
Thread cacheThread = new Thread("Caching localhost information") {
public void run() {
Iterator iter2 = currentAddresses.iterator();
while (iter2.hasNext()) {
InetAddress addr = (InetAddress) iter2.next();
String hostname = addr.getHostName();
String hostname2 = addr.getCanonicalHostName();
synchronized (lock) {
if (hostname != null && !addressList.contains(hostname))
addressList.add(hostname);
if (hostname2 != null && !addressList.contains(hostname2))
addressList.add(hostname2);
}
}
}
};
cacheThread.setDaemon(true);
cacheThread.setPriority(Thread.NORM_PRIORITY - 1);
cacheThread.start();
cacheThread.join(250);
}
} catch (Exception e) {
// ignore
Trace.trace(Trace.WARNING, "Localhost caching failure", e);
}
if (localHostCache == null)
return false;
synchronized (lock) {
Iterator iterator = localHostCache.iterator();
while (iterator.hasNext()) {
if (host.equals(iterator.next()))
return true;
}
}
return false;
}
}