View Javadoc
1   /*
2    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
3    * The software in this package is published under the terms of the CPAL v1.0
4    * license, a copy of which has been included with this distribution in the
5    * LICENSE.txt file.
6    */
7   package org.mule.tck.junit4.rule;
8   
9   import java.io.IOException;
10  import java.net.ServerSocket;
11  import java.util.HashSet;
12  import java.util.Set;
13  
14  import org.apache.commons.logging.Log;
15  import org.apache.commons.logging.LogFactory;
16  
17  /**
18   * Finds available port numbers in a specified range.
19   */
20  public class FreePortFinder
21  {
22  
23      protected final Log logger = LogFactory.getLog(getClass());
24  
25      protected final int minPortNumber;
26      protected final int maxPortNumber;
27  
28      private Set<Integer> selectedPorts = new HashSet<Integer>();
29  
30      public FreePortFinder(int minPortNumber, int maxPortNumber)
31      {
32          this.minPortNumber = minPortNumber;
33          this.maxPortNumber = maxPortNumber;
34      }
35  
36      public synchronized Integer find()
37      {
38          for (int port = minPortNumber; port < maxPortNumber; port++)
39          {
40              if (selectedPorts.contains(port))
41              {
42                  continue;
43              }
44  
45              if (isPortFree(port))
46              {
47                  if (logger.isDebugEnabled())
48                  {
49                      logger.debug("Found free port: " + port);
50                  }
51  
52                  selectedPorts.add(port);
53                  return port;
54              }
55          }
56  
57          throw new IllegalStateException("Unable to find an available port");
58      }
59  
60      /**
61       * Indicates that the port is free from the point of view of the caller.
62       * <p/>
63       * Checks that the port was released, if it was not, then it would be
64       * marked as in use, so no other client receives the same port again.
65       *
66       * @param port the port number to release.
67       */
68      public synchronized void releasePort(int port)
69      {
70          if (isPortFree(port))
71          {
72              selectedPorts.remove(port);
73          }
74          else
75          {
76              if (logger.isInfoEnabled())
77              {
78                  logger.info(String.format("Port %d was is not correctly released", port));
79              }
80          }
81      }
82  
83      /**
84       * Check and log is a given port is available
85       *
86       * @param port the port number to check
87       * @return true if the port is available, false otherwise
88       */
89      public boolean isPortFree(int port)
90      {
91          boolean portIsFree = true;
92  
93          ServerSocket server = null;
94          try
95          {
96              server = new ServerSocket(port);
97          }
98          catch (IOException e)
99          {
100             portIsFree = false;
101         }
102         finally
103         {
104             if (server != null)
105             {
106                 try
107                 {
108                     server.close();
109                 }
110                 catch (IOException e)
111                 {
112                     // ignore
113                 }
114             }
115         }
116 
117         return portIsFree;
118     }
119 }