| /******************************************************************************* |
| * Copyright (c) 2003, 2007 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.net.UnknownHostException; |
| import java.util.Enumeration; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.Random; |
| import java.util.Set; |
| |
| 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 Set<String> localHostCache; |
| private static Set<String> notLocalHostCache = new HashSet<String>(); |
| private static Map<String, CacheThread> threadMap = new HashMap<String, CacheThread>(); |
| |
| private static Set<InetAddress> addressCache; |
| |
| static class CacheThread extends Thread { |
| private Set<InetAddress> currentAddresses; |
| private Set<String> addressList; |
| private String host; |
| private Set<String> nonAddressList; |
| private Map threadMap2; |
| |
| public CacheThread(String host, Set<InetAddress> currentAddresses, Set<String> addressList, Set<String> nonAddressList, Map threadMap2) { |
| super("Caching localhost information"); |
| this.host = host; |
| this.currentAddresses = currentAddresses; |
| this.addressList = addressList; |
| this.nonAddressList = nonAddressList; |
| this.threadMap2 = threadMap2; |
| } |
| |
| public void run() { |
| if (currentAddresses != null) { |
| 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); |
| } |
| } |
| } |
| |
| try { |
| InetAddress[] addrs = InetAddress.getAllByName(host); |
| int length = addrs.length; |
| for (int j = 0; j < length; j++) { |
| InetAddress addr = addrs[0]; |
| String hostname = addr.getHostName(); |
| String hostname2 = addr.getCanonicalHostName(); |
| synchronized (lock) { |
| if (addr.isLoopbackAddress()) { |
| if (hostname != null && !addressList.contains(hostname)) |
| addressList.add(hostname); |
| if (hostname2 != null && !addressList.contains(hostname2)) |
| addressList.add(hostname2); |
| } else { |
| if (hostname != null && !nonAddressList.contains(hostname)) |
| nonAddressList.add(hostname); |
| if (hostname2 != null && !nonAddressList.contains(hostname2)) |
| nonAddressList.add(hostname2); |
| } |
| } |
| } |
| } catch (UnknownHostException e) { |
| synchronized (lock) { |
| if (host != null && !nonAddressList.contains(host)) |
| nonAddressList.add(host); |
| } |
| } |
| synchronized (lock) { |
| threadMap2.remove(host); |
| } |
| } |
| } |
| |
| /** |
| * Static utility class - cannot create an instance. |
| */ |
| private SocketUtil() { |
| // cannot create |
| } |
| |
| /** |
| * Finds an unused local port between the given from and to values. |
| * |
| * @param low lowest possible port number |
| * @param high highest possible port number |
| * @return an unused port number, or <code>-1</code> if no used ports could be found |
| */ |
| public static int findUnusedPort(int low, int high) { |
| return findUnusedPort(null, low, high); |
| } |
| |
| /** |
| * Finds an unused local port between the given from and to values. |
| * |
| * @param address a local InetAddress |
| * @param low lowest possible port number |
| * @param high highest possible port number |
| * @return an unused port number, or <code>-1</code> if no used ports could be found |
| * @since 1.1 |
| */ |
| public static int findUnusedPort(InetAddress address, int low, int high) { |
| if (high < low) |
| return -1; |
| |
| for (int i = 0; i < 10; i++) { |
| int port = getRandomPort(low, high); |
| if (!isPortInUse(address, port)) |
| return port; |
| } |
| return -1; |
| } |
| |
| /** |
| * Return a random local 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 local 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) { |
| return isPortInUse(null, port, count); |
| } |
| |
| /** |
| * Checks to see if the given local 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 address a local InetAddress |
| * @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 |
| * @since 1.1 |
| */ |
| public static boolean isPortInUse(InetAddress address, int port, int count) { |
| boolean inUse = isPortInUse(address, port); |
| while (inUse && count > 0) { |
| try { |
| Thread.sleep(500); |
| } catch (Exception e) { |
| // ignore |
| } |
| inUse = isPortInUse(address, port); |
| count --; |
| } |
| |
| return inUse; |
| } |
| |
| /** |
| * Checks to see if the given local 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) { |
| return isPortInUse(null, port); |
| } |
| |
| /** |
| * Checks to see if the given local port number is being used. |
| * Returns <code>true</code> if the given port is in use, and <code>false</code> |
| * otherwise. |
| * |
| * @param address a local InetAddress |
| * @param port the port number to check |
| * @return boolean <code>true</code> if the port is in use, and |
| * <code>false</code> otherwise |
| * @since 1.1 |
| */ |
| public static boolean isPortInUse(InetAddress address, int port) { |
| ServerSocket s = null; |
| try { |
| s = new ServerSocket(port, 0, address); |
| } 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, the first call to this method will always return after |
| * 250ms, even if the caching is not complete. At that point it may return |
| * "false negative" results. (i.e. the method will return <code>false</code> |
| * even though it may later determine that the host address is a local host) |
| * </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/complete. |
| * </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(final String host) { |
| if (host == null || host.equals("")) |
| return false; |
| |
| if ("localhost".equals(host) || "127.0.0.1".equals(host) || "::1".equals(host)) |
| return true; |
| |
| // check simple cases |
| try { |
| InetAddress localHostaddr = InetAddress.getLocalHost(); |
| if (localHostaddr.getHostName().equals(host) |
| || host.equals(localHostaddr.getCanonicalHostName()) |
| || localHostaddr.getHostAddress().equals(host)) |
| return true; |
| } catch (Exception e) { |
| Trace.trace(Trace.WARNING, "Localhost caching failure", e); |
| } |
| |
| // check for current thread and wait if necessary |
| boolean currentThread = false; |
| try { |
| Thread t = null; |
| synchronized (lock) { |
| t = threadMap.get(host); |
| } |
| if (t != null && t.isAlive()) { |
| currentThread = true; |
| t.join(30); |
| } |
| } catch (Exception e) { |
| Trace.trace(Trace.WARNING, "Localhost caching failure", e); |
| } |
| |
| // check if cache is still ok |
| boolean refreshedCache = false; |
| try { |
| // get network interfaces |
| final Set<InetAddress> currentAddresses = new HashSet<InetAddress>(); |
| currentAddresses.add(InetAddress.getLocalHost()); |
| Enumeration nis = NetworkInterface.getNetworkInterfaces(); |
| while (nis.hasMoreElements()) { |
| NetworkInterface inter = (NetworkInterface) nis.nextElement(); |
| Enumeration<InetAddress> 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)) { |
| CacheThread cacheThread = null; |
| refreshedCache = true; |
| |
| synchronized (lock) { |
| addressCache = currentAddresses; |
| notLocalHostCache = new HashSet<String>(); |
| localHostCache = new HashSet<String>(currentAddresses.size() * 3); |
| |
| Iterator iter = currentAddresses.iterator(); |
| while (iter.hasNext()) { |
| InetAddress addr = (InetAddress) iter.next(); |
| String a = addr.getHostAddress(); |
| if (a != null && !localHostCache.contains(a)) |
| localHostCache.add(a); |
| } |
| |
| cacheThread = new CacheThread(host, currentAddresses, localHostCache, notLocalHostCache, threadMap); |
| threadMap.put(host, cacheThread); |
| cacheThread.setDaemon(true); |
| cacheThread.setPriority(Thread.NORM_PRIORITY - 1); |
| cacheThread.start(); |
| } |
| cacheThread.join(200); |
| } |
| } catch (Exception e) { |
| Trace.trace(Trace.WARNING, "Localhost caching failure", e); |
| } |
| |
| synchronized (lock) { |
| if (localHostCache.contains(host)) |
| return true; |
| if (notLocalHostCache.contains(host)) |
| return false; |
| } |
| |
| // if the cache hasn't been cleared, maybe we still need to lookup the host |
| if (!refreshedCache && !currentThread) { |
| try { |
| CacheThread cacheThread = null; |
| synchronized (lock) { |
| cacheThread = new CacheThread(host, null, localHostCache, notLocalHostCache, threadMap); |
| threadMap.put(host, cacheThread); |
| cacheThread.setDaemon(true); |
| cacheThread.setPriority(Thread.NORM_PRIORITY - 1); |
| cacheThread.start(); |
| } |
| cacheThread.join(75); |
| |
| synchronized (lock) { |
| if (localHostCache.contains(host)) |
| return true; |
| } |
| } catch (Exception e) { |
| Trace.trace(Trace.WARNING, "Could not find localhost", e); |
| } |
| } |
| |
| return false; |
| } |
| } |