View Javadoc

1   /**
2    * Copyright 2005-2011 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 }