001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020package org.apache.mina.util; 021 022import java.io.IOException; 023import java.net.DatagramSocket; 024import java.net.ServerSocket; 025import java.util.NoSuchElementException; 026import java.util.Set; 027import java.util.TreeSet; 028 029/** 030 * Finds currently available server ports. 031 * 032 * @author <a href="http://mina.apache.org">Apache MINA Project</a> 033 * @see <a href="http://www.iana.org/assignments/port-numbers">IANA.org</a> 034 */ 035public class AvailablePortFinder { 036 /** 037 * The minimum number of server port number. 038 */ 039 public static final int MIN_PORT_NUMBER = 1; 040 041 /** 042 * The maximum number of server port number. 043 */ 044 public static final int MAX_PORT_NUMBER = 49151; 045 046 /** 047 * Creates a new instance. 048 */ 049 private AvailablePortFinder() { 050 // Do nothing 051 } 052 053 /** 054 * @return the {@link Set} of currently available port numbers 055 * ({@link Integer}). This method is identical to 056 * <code>getAvailablePorts(MIN_PORT_NUMBER, MAX_PORT_NUMBER)</code>. 057 * 058 * WARNING: this can take a very long time. 059 */ 060 public static Set<Integer> getAvailablePorts() { 061 return getAvailablePorts(MIN_PORT_NUMBER, MAX_PORT_NUMBER); 062 } 063 064 /** 065 * @return an available port, selected by the system. 066 * 067 * @throws NoSuchElementException if there are no ports available 068 */ 069 public static int getNextAvailable() { 070 ServerSocket serverSocket = null; 071 072 try { 073 // Here, we simply return an available port found by the system 074 serverSocket = new ServerSocket(0); 075 int port = serverSocket.getLocalPort(); 076 077 // Don't forget to close the socket... 078 serverSocket.close(); 079 080 return port; 081 } catch (IOException ioe) { 082 throw new NoSuchElementException(ioe.getMessage()); 083 } 084 } 085 086 /** 087 * @return the next available port starting at a port. 088 * 089 * @param fromPort the port to scan for availability 090 * @throws NoSuchElementException if there are no ports available 091 */ 092 public static int getNextAvailable(int fromPort) { 093 if (fromPort < MIN_PORT_NUMBER || fromPort > MAX_PORT_NUMBER) { 094 throw new IllegalArgumentException("Invalid start port: " + fromPort); 095 } 096 097 for (int i = fromPort; i <= MAX_PORT_NUMBER; i++) { 098 if (available(i)) { 099 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}