View Javadoc

1   /*
2    *  XNap Commons
3    *
4    *  Copyright (C) 2005  Steffen Pingel
5    *
6    *  This library is free software; you can redistribute it and/or
7    *  modify it under the terms of the GNU Lesser General Public
8    *  License as published by the Free Software Foundation; either
9    *  version 2.1 of the License, or (at your option) any later version.
10   *
11   *  This library is distributed in the hope that it will be useful,
12   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   *  Lesser General Public License for more details.
15   *
16   *  You should have received a copy of the GNU Lesser General Public
17   *  License along with this library; if not, write to the Free Software
18   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19   */
20  package org.xnap.commons.util;
21  
22  import java.io.IOException;
23  import java.net.ServerSocket;
24  import java.util.ArrayList;
25  import java.util.Collections;
26  import java.util.Iterator;
27  import java.util.LinkedHashSet;
28  import java.util.List;
29  import java.util.Random;
30  import java.util.Set;
31  import java.util.StringTokenizer;
32  import org.xnap.commons.settings.PortRangeValidator;
33  
34  /***
35   * Provides a data container for port ranges. All methods make sure
36   * that only valid ports are added.
37   *
38   * @see org.xnap.commons.settings.PortRangeValidator 
39   */
40  public class PortRange {
41  
42      public static final int MIN_PORT = 1;
43      public static final int MAX_PORT = 65535;
44  
45  	/***
46  	 * The set of valid ports.
47  	 */
48      private Set<Integer> list = new LinkedHashSet<Integer>();
49      
50      public PortRange(int first, int last) 
51      {
52  		add(first, last);
53      }
54  
55      public PortRange(String range)
56      {
57  		add(range);
58      }
59  
60      public PortRange()
61      {
62      }
63  
64      /***
65       * Adds all ports that are <code>first</code> <= port <= <code>last</code>.
66       */
67      public void add(int first, int last)
68      {
69  		if (first < MIN_PORT || first > MAX_PORT) {
70  			throw new IllegalArgumentException();
71  		}
72  		if (last < MIN_PORT || last > MAX_PORT) {
73  			throw new IllegalArgumentException();
74  		}
75  		if (first > last) {
76  			throw new IllegalArgumentException("first must be smaller than or equal to last (" + first + ">" + last + ")");
77  		}
78  
79  		for (int i = first; i <= last; i++) {
80  			list.add(i);
81  		}
82      }
83  
84      /***
85       * Adds <code>port</code> to the range.
86       */
87      public void add(int port)
88      {
89  		if (port < MIN_PORT || port > MAX_PORT) {
90  			throw new IllegalArgumentException();
91  		}
92  		
93  		list.add(new Integer(port));
94      }
95  
96      /***
97       * Adds a range as a string.
98       *
99       * @see PortRangeValidator
100      */
101     public void add(String range)
102     {
103 		StringTokenizer t = new StringTokenizer(range, ";");
104 		while (t.hasMoreTokens()) {
105 			String ports = t.nextToken().trim();
106 
107 			if (ports.length() == 0) {
108 				continue;
109 			}
110 
111 			try {
112 				if (ports.indexOf("-") != -1) {
113 					StringTokenizer u = new StringTokenizer(ports, "-");
114 					if (u.countTokens() == 2) {
115 						add(Integer.parseInt(u.nextToken()),
116 							Integer.parseInt(u.nextToken()));
117 					}
118 				}
119 				else {
120 					add(Integer.parseInt(ports));
121 				}
122 			}
123 			catch (NumberFormatException e) {
124 			}
125 		}
126     }
127 
128     /***
129      * Tries to bind random ports from the range and returns the 
130      * {@link ServerSocket} object if successful. 
131      *
132      * @return null, if no port could be bound
133      */
134     public ServerSocket bindRandom()
135     {
136 		// bind to next free port
137 		for (Iterator<Integer> it = randomIterator(); it.hasNext();) {
138 			try {
139 				return new ServerSocket(it.next());
140 			} 
141 			catch (IOException e) {
142 			}
143 		}
144 
145 		return null;
146     }
147 
148     /***
149      * Returns true, if port is in the range.
150      */
151     public boolean contains(int port)
152     {
153 		return list.contains(new Integer(port));
154     }
155 
156     /***
157      * Returns a random port from the range.
158      *
159      * @param defaultPort a default port
160      * @return default, if range is empty
161      */
162     public int getRandom(int defaultPort)
163     {
164 		if (size() > 0) {
165 			List<Integer> copy = new ArrayList<Integer>(list);
166 			int index = (new Random()).nextInt(copy.size());
167 			return copy.get(index);
168 		}
169 	
170 		return defaultPort;
171     }
172     
173     /***
174      * Returns an iterator over all contained ports.
175      */
176     public Iterator<Integer> iterator()
177     {
178 		return list.iterator();
179     }
180 
181     /***
182      * Returns an iterator over a shuffeled instance.
183      */
184     public Iterator<Integer> randomIterator()
185     {
186 		List<Integer> copy = new ArrayList<Integer>(list);
187 		Collections.shuffle(copy);
188 		return copy.iterator();
189     }
190 
191     /***
192      * Returns the number of ports.
193      */
194     public int size()
195     {
196 		return list.size();
197     }
198 
199     /***
200      * Returns an ordered string representation.
201      */
202     public String toString()
203     {
204 		StringBuilder sb = new StringBuilder();
205 
206 		List<Integer> copy = new ArrayList<Integer>(list);
207 		Collections.sort(copy);
208 
209 		int start = 0;
210 		int last = -1;
211 		for (Iterator<Integer> it = copy.iterator(); it.hasNext();) {
212 			int current = it.next();
213 			if (last == -1) {
214 				start = current;
215 			}
216 			else if (current - last > 1) {
217 				sb.append(start);
218 				if (start != last) {
219 					sb.append("-");
220 					sb.append(last);
221 				}
222 				sb.append(";");
223 				start = current;
224 			}
225 			last = current;
226 		}
227 
228 		if (last != -1) {
229 			sb.append(start);
230 			if (start != last) {
231 				sb.append("-");
232 				sb.append(last);
233 			}
234 			sb.append(";");
235 		}
236 
237 		// return with trailing ';' removed
238 		return (sb.length() > 0) ? sb.substring(0, sb.length() - 1) : "";
239     }
240 
241 }
242 
243