1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 *
19 */
20 package org.apache.mina.util;
21
22 import java.io.IOException;
23 import java.net.DatagramSocket;
24 import java.net.ServerSocket;
25 import java.util.NoSuchElementException;
26 import java.util.Set;
27 import java.util.TreeSet;
28
29 /**
30 * Finds currently available server ports.
31 *
32 * @author <a href="http://mina.apache.org">Apache MINA Project</a>
33 * @see <a href="http://www.iana.org/assignments/port-numbers">IANA.org</a>
34 */
35 public class AvailablePortFinder {
36 /**
37 * The minimum number of server port number.
38 */
39 public static final int MIN_PORT_NUMBER = 1;
40
41 /**
42 * The maximum number of server port number.
43 */
44 public static final int MAX_PORT_NUMBER = 49151;
45
46 /**
47 * Creates a new instance.
48 */
49 private AvailablePortFinder() {
50 // Do nothing
51 }
52
53 /**
54 * Returns the {@link Set} of currently available port numbers
55 * ({@link Integer}). This method is identical to
56 * <code>getAvailablePorts(MIN_PORT_NUMBER, MAX_PORT_NUMBER)</code>.
57 *
58 * WARNING: this can take a very long time.
59 */
60 public static Set<Integer> getAvailablePorts() {
61 return getAvailablePorts(MIN_PORT_NUMBER, MAX_PORT_NUMBER);
62 }
63
64 /**
65 * Gets an available port, selected by the system.
66 *
67 * @throws NoSuchElementException if there are no ports available
68 */
69 public static int getNextAvailable() {
70 ServerSocket serverSocket = null;
71
72 try {
73 // Here, we simply return an available port found by the system
74 serverSocket = new ServerSocket(0);
75 int port = serverSocket.getLocalPort();
76
77 // Don't forget to close the socket...
78 serverSocket.close();
79
80 return port;
81 } catch (IOException ioe) {
82 throw new NoSuchElementException(ioe.getMessage());
83 }
84 }
85
86 /**
87 * Gets the next available port starting at a port.
88 *
89 * @param fromPort the port to scan for availability
90 * @throws NoSuchElementException if there are no ports available
91 */
92 public static int getNextAvailable(int fromPort) {
93 if (fromPort < MIN_PORT_NUMBER || fromPort > MAX_PORT_NUMBER) {
94 throw new IllegalArgumentException("Invalid start port: " + fromPort);
95 }
96
97 for (int i = fromPort; i <= MAX_PORT_NUMBER; i++) {
98 if (available(i)) {
99 return i;
100 }
101 }
102
103 throw new NoSuchElementException("Could not find an available port " + "above " + fromPort);
104 }
105
106 /**
107 * Checks to see if a specific port is available.
108 *
109 * @param port the port to check for availability
110 */
111 public static boolean available(int port) {
112 if (port < MIN_PORT_NUMBER || port > MAX_PORT_NUMBER) {
113 throw new IllegalArgumentException("Invalid start port: " + port);
114 }
115
116 ServerSocket ss = null;
117 DatagramSocket ds = null;
118
119 try {
120 ss = new ServerSocket(port);
121 ss.setReuseAddress(true);
122 ds = new DatagramSocket(port);
123 ds.setReuseAddress(true);
124 return true;
125 } catch (IOException e) {
126 // Do nothing
127 } finally {
128 if (ds != null) {
129 ds.close();
130 }
131
132 if (ss != null) {
133 try {
134 ss.close();
135 } catch (IOException e) {
136 /* should not be thrown */
137 }
138 }
139 }
140
141 return false;
142 }
143
144 /**
145 * Returns the {@link Set} of currently avaliable port numbers ({@link Integer})
146 * between the specified port range.
147 *
148 * @throws IllegalArgumentException if port range is not between
149 * {@link #MIN_PORT_NUMBER} and {@link #MAX_PORT_NUMBER} or
150 * <code>fromPort</code> if greater than <code>toPort</code>.
151 */
152 public static Set<Integer> getAvailablePorts(int fromPort, int toPort) {
153 if (fromPort < MIN_PORT_NUMBER || toPort > MAX_PORT_NUMBER || fromPort > toPort) {
154 throw new IllegalArgumentException("Invalid port range: " + fromPort + " ~ " + toPort);
155 }
156
157 Set<Integer> result = new TreeSet<Integer>();
158
159 for (int i = fromPort; i <= toPort; i++) {
160 ServerSocket s = null;
161
162 try {
163 s = new ServerSocket(i);
164 result.add(new Integer(i));
165 } catch (IOException e) {
166 // Do nothing
167 } finally {
168 if (s != null) {
169 try {
170 s.close();
171 } catch (IOException e) {
172 /* should not be thrown */
173 }
174 }
175 }
176 }
177
178 return result;
179 }
180 }