View Javadoc
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       * @return 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       * @return 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       * @return 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      * @return <tt>true</tt> if the port is available
111      */
112     public static boolean available(int port) {
113         if (port < MIN_PORT_NUMBER || port > MAX_PORT_NUMBER) {
114             throw new IllegalArgumentException("Invalid start port: " + port);
115         }
116 
117         ServerSocket ss = null;
118         DatagramSocket ds = null;
119 
120         try {
121             ss = new ServerSocket(port);
122             ss.setReuseAddress(true);
123             ds = new DatagramSocket(port);
124             ds.setReuseAddress(true);
125             return true;
126         } catch (IOException e) {
127             // Do nothing
128         } finally {
129             if (ds != null) {
130                 ds.close();
131             }
132 
133             if (ss != null) {
134                 try {
135                     ss.close();
136                 } catch (IOException e) {
137                     /* should not be thrown */
138                 }
139             }
140         }
141 
142         return false;
143     }
144 
145     /**
146      * @param fromPort The port we start from
147      * @param toPort The posrt we stop at
148      * @return the {@link Set} of currently avalaible port numbers ({@link Integer})
149      * between the specified port range.
150      *
151      * @throws IllegalArgumentException if port range is not between
152      * {@link #MIN_PORT_NUMBER} and {@link #MAX_PORT_NUMBER} or
153      * <code>fromPort</code> if greater than <code>toPort</code>.
154      */
155     public static Set<Integer> getAvailablePorts(int fromPort, int toPort) {
156         if (fromPort < MIN_PORT_NUMBER || toPort > MAX_PORT_NUMBER || fromPort > toPort) {
157             throw new IllegalArgumentException("Invalid port range: " + fromPort + " ~ " + toPort);
158         }
159 
160         Set<Integer> result = new TreeSet<Integer>();
161 
162         for (int i = fromPort; i <= toPort; i++) {
163             ServerSocket s = null;
164 
165             try {
166                 s = new ServerSocket(i);
167                 result.add(Integer.valueOf(i));
168             } catch (IOException e) {
169                 // Do nothing
170             } finally {
171                 if (s != null) {
172                     try {
173                         s.close();
174                     } catch (IOException e) {
175                         /* should not be thrown */
176                     }
177                 }
178             }
179         }
180 
181         return result;
182     }
183 }