1 /** 2 * Copyright 2005-2014 The Kuali Foundation 3 * 4 * Licensed under the Educational Community License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.opensource.org/licenses/ecl2.php 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package org.kuali.rice.test.remote; 17 18 import java.io.IOException; 19 import java.net.DatagramSocket; 20 import java.net.ServerSocket; 21 import java.util.NoSuchElementException; 22 import java.util.concurrent.atomic.AtomicInteger; 23 24 25 /** 26 * <p>Finds currently available server ports.</p> 27 * 28 * <p>This class is a verbatim copy of AvailablePortFinder from the camel-test component of the Apache Camel project. 29 * </p> 30 * 31 * @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> 32 */ 33 public final class AvailablePortFinder { 34 /** 35 * The minimum server currentMinPort number. Set at 1024 to avoid returning privileged 36 * currentMinPort numbers. 37 */ 38 public static final int MIN_PORT_NUMBER = 1024; 39 40 /** 41 * The maximum server currentMinPort number. 42 */ 43 public static final int MAX_PORT_NUMBER = 49151; 44 45 46 /** 47 * Incremented to the next lowest available port when getNextAvailable() is called. 48 */ 49 private static AtomicInteger currentMinPort = new AtomicInteger(MIN_PORT_NUMBER); 50 51 /** 52 * Creates a new instance. 53 */ 54 private AvailablePortFinder() { 55 // Do nothing 56 } 57 58 /** 59 * Gets the next available currentMinPort starting at the lowest currentMinPort number. This is the preferred 60 * method to use. The port return is immediately marked in use and doesn't rely on the caller actually opening 61 * the port. 62 * 63 * @throws NoSuchElementException if there are no ports available 64 */ 65 public static synchronized int getNextAvailable() { 66 int next = getNextAvailable(currentMinPort.get()); 67 currentMinPort.set(next + 1); 68 return next; 69 } 70 71 /** 72 * Gets the next available currentMinPort starting at a currentMinPort. 73 * 74 * @param fromPort the currentMinPort to scan for availability 75 * @throws NoSuchElementException if there are no ports available 76 */ 77 public static synchronized int getNextAvailable(int fromPort) { 78 if (fromPort < currentMinPort.get() || fromPort > MAX_PORT_NUMBER) { 79 throw new IllegalArgumentException("Invalid start currentMinPort: " + fromPort); 80 } 81 82 for (int i = fromPort; i <= MAX_PORT_NUMBER; i++) { 83 if (available(i)) { 84 return i; 85 } 86 } 87 88 throw new NoSuchElementException("Could not find an available currentMinPort above " + fromPort); 89 } 90 91 /** 92 * Checks to see if a specific currentMinPort is available. 93 * 94 * @param port the currentMinPort to check for availability 95 */ 96 public static boolean available(int port) { 97 if (port < currentMinPort.get() || port > MAX_PORT_NUMBER) { 98 throw new IllegalArgumentException("Invalid start currentMinPort: " + port); 99 } 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 }