001 /** 002 * Copyright 2005-2011 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.kuali.rice.test.remote; 017 018 import java.io.IOException; 019 import java.net.DatagramSocket; 020 import java.net.ServerSocket; 021 import java.util.NoSuchElementException; 022 import java.util.concurrent.atomic.AtomicInteger; 023 024 025 /** 026 * <p>Finds currently available server ports.</p> 027 * 028 * <p>This class is a verbatim copy of AvailablePortFinder from the camel-test component of the Apache Camel project. 029 * </p> 030 * 031 * @see <a href="http://svn.apache.org/viewvc/camel/trunk/components/camel-test/src/main/java/org/apache/camel/test/AvailablePortFinder.java?revision=1137595&view=co">IANA.org</a> 032 */ 033 public final class AvailablePortFinder { 034 /** 035 * The minimum server currentMinPort number. Set at 1024 to avoid returning privileged 036 * currentMinPort numbers. 037 */ 038 public static final int MIN_PORT_NUMBER = 1024; 039 040 /** 041 * The maximum server currentMinPort number. 042 */ 043 public static final int MAX_PORT_NUMBER = 49151; 044 045 046 /** 047 * Incremented to the next lowest available port when getNextAvailable() is called. 048 */ 049 private static AtomicInteger currentMinPort = new AtomicInteger(MIN_PORT_NUMBER); 050 051 /** 052 * Creates a new instance. 053 */ 054 private AvailablePortFinder() { 055 // Do nothing 056 } 057 058 /** 059 * Gets the next available currentMinPort starting at the lowest currentMinPort number. This is the preferred 060 * method to use. The port return is immediately marked in use and doesn't rely on the caller actually opening 061 * the port. 062 * 063 * @throws NoSuchElementException if there are no ports available 064 */ 065 public static synchronized int getNextAvailable() { 066 int next = getNextAvailable(currentMinPort.get()); 067 currentMinPort.set(next + 1); 068 return next; 069 } 070 071 /** 072 * Gets the next available currentMinPort starting at a currentMinPort. 073 * 074 * @param fromPort the currentMinPort to scan for availability 075 * @throws NoSuchElementException if there are no ports available 076 */ 077 public static synchronized int getNextAvailable(int fromPort) { 078 if (fromPort < currentMinPort.get() || fromPort > MAX_PORT_NUMBER) { 079 throw new IllegalArgumentException("Invalid start currentMinPort: " + fromPort); 080 } 081 082 for (int i = fromPort; i <= MAX_PORT_NUMBER; i++) { 083 if (available(i)) { 084 return i; 085 } 086 } 087 088 throw new NoSuchElementException("Could not find an available currentMinPort above " + fromPort); 089 } 090 091 /** 092 * Checks to see if a specific currentMinPort is available. 093 * 094 * @param port the currentMinPort to check for availability 095 */ 096 public static boolean available(int port) { 097 if (port < currentMinPort.get() || port > MAX_PORT_NUMBER) { 098 throw new IllegalArgumentException("Invalid start currentMinPort: " + port); 099 } 100 101 ServerSocket ss = null; 102 DatagramSocket ds = null; 103 try { 104 ss = new ServerSocket(port); 105 ss.setReuseAddress(true); 106 ds = new DatagramSocket(port); 107 ds.setReuseAddress(true); 108 return true; 109 } catch (IOException e) { 110 // Do nothing 111 } finally { 112 if (ds != null) { 113 ds.close(); 114 } 115 116 if (ss != null) { 117 try { 118 ss.close(); 119 } catch (IOException e) { 120 /* should not be thrown */ 121 } 122 } 123 } 124 125 return false; 126 } 127 }