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