1 package org.jgroups.util;
2 
3 import org.jgroups.Global;
4 
5 import java.net.*;
6 import java.rmi.server.UID;
7 import java.util.ArrayList;
8 import java.util.List;
9 import java.util.StringTokenizer;
10 
11 /**
12  * Manages resources such as multicast addresses and multicast ports, and TCP
13  * ports. This class is mainly used for running unit tests in parallel (TestNG)
14  * and preventing clusters intended to be separate from joining each other.
15  *
16  * @author Bela Ban
17  */
18 public class ResourceManager {
19 	private static final IpAddressRep rep;
20 	private static short mcast_port;
21 	private static short tcp_port;
22     private static SocketFactory socket_factory=new DefaultSocketFactory();
23 
24 	static {
25 
26         StackType type=Util.getIpStackType();
27 
28         String tmp_addr = System.getProperty(Global.INITIAL_MCAST_ADDR,
29                                              type == StackType.IPv6? "ff0e::9:9:9" : "230.1.1.1");
30         mcast_port = Short.valueOf(System.getProperty(Global.INITIAL_MCAST_PORT, "7000"));
31 		tcp_port = Short.valueOf(System.getProperty(Global.INITIAL_TCP_PORT, "10000"));
32 		try {
33 			InetAddress tmp = InetAddress.getByName(tmp_addr);
34 			if (!tmp.isMulticastAddress())
35 				throw new IllegalArgumentException("initial multicast address "	+ tmp_addr + " is not a valid multicast address");
36 
37 			if (tmp instanceof Inet4Address)
38 				rep = new IPv4AddressRep(tmp_addr);
39 			else
40 				rep = new IPv6AddressRep(tmp_addr);
41 
42 		} catch (UnknownHostException e) {
43 			throw new RuntimeException("initial multicast address " + tmp_addr + " is incorrect", e);
44 		}
45 	}
46 
ResourceManager()47 	private ResourceManager() {
48 	}
49 
50 	/**
51 	 * Returns the next available multicast address, e.g. "228.1.2.3". This
52 	 * class is a JVM singleton
53 	 *
54 	 * @return
55 	 */
getNextMulticastAddress()56 	public static String getNextMulticastAddress() {
57 		return rep.nextAddress();
58 	}
59 
getNextMulticastPort(InetAddress bind_addr)60 	public static synchronized short getNextMulticastPort(InetAddress bind_addr) throws Exception {
61 		short port = mcast_port;
62 		try {
63 			DatagramSocket sock = Util.createDatagramSocket(socket_factory, "jgroups.temp.resourcemgr.mcast_sock", bind_addr, port);
64 			port = (short) sock.getLocalPort();
65 			socket_factory.close(sock);
66 			return port;
67 		} finally {
68 			mcast_port = (short) (port + 1);
69 		}
70 	}
71 
getNextTcpPorts(InetAddress bind_addr, int num_requested_ports)72 	public static synchronized List<Short> getNextTcpPorts(InetAddress bind_addr, int num_requested_ports) throws Exception {
73 		short port = tcp_port++;
74 		List<Short> retval = new ArrayList<Short>(num_requested_ports);
75 
76 		for (int i = 0; i < num_requested_ports; i++) {
77 			ServerSocket sock = Util.createServerSocket(socket_factory, "jgroups.temp.resourcemgr.srv_sock", bind_addr, port);
78 			port = (short) sock.getLocalPort();
79 			retval.add(port);
80 			tcp_port = ++port;
81 			socket_factory.close(sock);
82 		}
83 		return retval;
84 	}
85 
getUniqueClusterName(String base_name)86 	public static String getUniqueClusterName(String base_name) {
87 		return base_name != null ? base_name + "-" + new UID().toString()
88 				: new UID().toString();
89 	}
90 
getUniqueClusterName()91 	public static String getUniqueClusterName() {
92 		return getUniqueClusterName(null);
93 	}
94 
main(String[] args)95 	public static void main(String[] args) throws Exception {
96 		List<Short> ports = getNextTcpPorts(InetAddress.getByName("192.168.1.5"), 15);
97 		System.out.println("ports = " + ports);
98 
99 		ports = getNextTcpPorts(InetAddress.getByName("192.168.1.5"), 5);
100 		System.out.println("ports = " + ports);
101 
102 	}
103 
104 	/*
105 	 * Interface for IpAddress representations
106 	 */
107 	public interface IpAddressRep {
nextAddress()108 		public String nextAddress();
109 	}
110 
111 	/** Representation of an IPv4 address */
112 	static class IPv4AddressRep implements IpAddressRep {
113 		short a = 225, b = MIN, c = MIN, d = MIN;
114 
115 		private static final short MIN = 1, MAX = 250;
116 		private static final char DOT = '.';
117 
IPv4AddressRep(String initial_addr)118 		IPv4AddressRep(String initial_addr) {
119 			StringTokenizer tok = new StringTokenizer(initial_addr, ".", false);
120 			a = Short.valueOf(tok.nextToken());
121 			b = Short.valueOf(tok.nextToken());
122 			c = Short.valueOf(tok.nextToken());
123 			d = Short.valueOf(tok.nextToken());
124 		}
125 
nextAddress()126 		public synchronized String nextAddress() {
127 			StringBuilder sb = new StringBuilder();
128 			sb.append(a).append(DOT).append(b).append(DOT).append(c)
129 					.append(DOT).append(d);
130 			increment();
131 			return sb.toString();
132 		}
133 
increment()134 		private void increment() {
135 			d++;
136 			if (d > MAX) {
137 				d = MIN;
138 				c++;
139 				if (c > MAX) {
140 					c = MIN;
141 					b++;
142 					if (b > MAX) {
143 						b = MIN;
144 						a++;
145 						if (a > MAX)
146 							a = 225;
147 					}
148 				}
149 			}
150 		}
151 	}
152 
153 	/** Representation of an IPv6 address */
154 	static class IPv6AddressRep implements IpAddressRep {
155 
156 		byte[] bv = null;
157 		InetAddress address = null;
158 
159 		private static boolean carry = false;
160 
IPv6AddressRep(String initial_addr)161 		IPv6AddressRep(String initial_addr) {
162 
163 			try {
164 				address = InetAddress.getByName(initial_addr);
165 			} catch (UnknownHostException e) {
166 				//
167 				throw new RuntimeException ("Multicast address " + initial_addr + " has incorrect format", e) ;
168 			} catch (SecurityException e) {
169 				//
170 				throw new RuntimeException ("Security violation in accessing multicast address " + initial_addr, e) ;
171 			}
172 
173 			// get the byte representation
174 			bv = address.getAddress();
175 		}
176 
nextAddress()177 		public synchronized String nextAddress() {
178 			// build the existing address, then create the next one
179 			try {
180 				address = InetAddress.getByAddress(bv);
181 			} catch (UnknownHostException e) {
182 				//
183 				throw new RuntimeException ("Multicast address has incorrect length", e) ;
184 			}
185 			increment();
186 
187 			// strings are returned as hostname/IP address, so remove the hostname prefix
188 			// before returning the string
189 			String addressWithHostname = address.toString();
190 
191 			return addressWithHostname.substring(addressWithHostname.indexOf('/')+1) ;
192 		}
193 
increment()194 		private void increment() {
195 
196 			// process hex digits from right to left
197 			for (int i = bv.length - 1; i >= 0; i--) {
198 
199 				// increment the ith byte
200 				bv[i] =incrementHexValue(bv[i]);
201 
202 				// if carry, increment (i-1)th byte
203 				if (carry) {
204 					carry = false;
205 					continue;
206 				}
207 				// otherwise, we are done
208 				return ;
209 			}
210 			// if we reach here, incrementing no longer possible
211 			throw new RuntimeException ("Cannot increment multicast address ") ;
212 		}
213 
214 		// increments a byte in hex and notes if carry req'd
215 		// these bytes contain 2's complement representations
216 		// of hex values "00" through "ff"
incrementHexValue(byte b)217 		private static byte incrementHexValue(byte b) {
218 
219 			// "00" to "7e"
220 			if (b >= 0 && b < 127)
221 				return (byte) (b + (byte) 1);
222 			// "7f"
223 			else if (b == 127) {
224 				return (byte) -128;
225 			}
226 			// "80" to "fe"
227 			else if (b >= -128 && b < -1) {
228 				return (byte) (b + (byte) 1);
229 			}
230 			// "ff"
231 			else if (b == -1) {
232 				carry = true;
233 				return (byte) 0;
234 			}
235 			return 0;
236 		}
237 	}
238 }
239