Source code: edu/mit/media/hive/support/CellAddress.java
1 // $Id: CellAddress.java,v 2.1 1999/10/22 19:50:03 tucker Exp $
2 // Hive. Copyright (c) 1998-1999, The Massachusetts Institute of Technology.
3 // All rights reserved. Distributed with no warranty, without even the
4 // implied warranty of merchantability or fitness for a particular purpose.
5 // For more details see COPYING.GPL, the GNU General Public License.
6
7
8 package edu.mit.media.hive.support;
9 import java.net.*;
10 import java.io.Serializable;
11 import java.rmi.Naming;
12
13 import edu.mit.media.hive.Global;
14 import edu.mit.media.hive.cell.RemoteCell;
15
16
17 /** This immutable class represents the URL of a Hive Cell. The
18 *protocol portion of the url is defined to be "hive", and will not
19 *allow the construction of urls with a different
20 *protocol. CellAddress follows the CISS (Common Internet Scheme
21 *Standard) Spec, although currently the file and ref portions of the
22 *URL are not used by the Hive system.
23 *
24 * WARNING: Java deviates from it's spec (at least on windows) in the
25 * DNS lookup behavior. Doing a
26 * InetAdderss.getByName(host).getHostName() does NOT return the fully
27 * qualified hostname as it should. Rather, some wierd caching
28 * behavior is causing the original argument to be returned. To work
29 * around this, we do a lookup to an IP Address and then do a lookup
30 * from that. This requires one more DNS lookup and is hence lower
31 * performance.
32
33 * Also, hostnames are internally represented as they are returned by
34 * the DNS Cell, but for the purposes of equal and hashCode, the
35 * hostname is converted to lowercase, because some DNS servers return
36 * inconsistent case information.
37
38 * ANOTHER WARNING: Because this class is specified in the RemoteCell
39 * interface that is absolutely core to Hive, any changes in the
40 * SerialVersionID of this class will cause an incompatibility with
41 * any existing Hive cells. So think very carefully before performing
42 * even 'minor' modifications to this class.
43
44 * */
45 public class CellAddress implements Serializable {
46
47 /** This is the default port that is assumed if one is not
48 * specified, or if -1 is used as an argument.
49 * */
50 public static final int DEFAULT_PORT = Global.defaultPort;
51
52 /** The fixed string representing the protocol used in Hive cells.
53 *
54 */
55 public static final String PROTOCOL = "hive";
56 public static final String RMI_NAME = "server";
57
58 /** The port of the URL.
59 *
60 */
61 private int port;
62
63 /** The Host of the URL. Canonicalized at construction.
64 *
65 */
66 private String host;
67
68 /** The file of the URL.
69 *
70 */
71 private String file; // Currently ignored.
72
73 /** The Reference or Anchor of the URL.
74 *
75 */
76 private String ref; // Currently ignored.
77
78 /** The Hashcode of the URL. Created at construction.
79 *
80 */
81 private int hashCode;
82
83 /** This rather grotesque method does all the parsing of the
84 * string necessary to construct an URL. Anything that is
85 * unparsable, including URLs with a protocol other than "hive"
86 * will be rejected with a MalformedHiveURLException. The
87 * constructor does a lot of work up front, including
88 * canonicalizing the hostname (which requires two DNS lookups,
89 * and calculating the hashcode.) Hosts that are not in DNS will
90 * throw a MalformedHiveURLException.
91 *
92 * @param cellAddress String to be parsed into a CellAddress */
93 public CellAddress(String cellAddress) throws MalformedHiveURLException {
94 int hostIndex = 0; // First char of the host if present.
95 boolean portPresent = true; // Is there a port specified.
96 int portIndex; // The delimeter between host and port.
97 boolean filePresent = true; // Is there a file present?
98 int fileIndex; // First char of the file if present.
99 boolean refPresent = true; // Is there an ref specified.
100 int refIndex; // The delimeter between file and ref.
101
102 if (cellAddress == null)
103 throw new MalformedHiveURLException();
104
105 if (cellAddress.indexOf("://") >= 0) {
106 if (cellAddress.indexOf(PROTOCOL + "://") != 0) {
107 throw new MalformedHiveURLException();
108 }
109 else {
110 hostIndex = 7;
111 }
112 }
113 else /* Require the hostname to be prepended with hive://. This prevents strings like
114 "--nojoin" and "nowhere" from causing a DNS lookup, which locks hive if
115 the network is down. such things as hive://ivy:9999 still work though.
116 */
117 throw new MalformedHiveURLException();
118
119 try {
120 fileIndex = cellAddress.indexOf('/', hostIndex);
121 refIndex = cellAddress.indexOf('#', hostIndex) + 1;
122 }
123 catch (StringIndexOutOfBoundsException e) {
124 throw new MalformedHiveURLException();
125 // There is no URL after the protocol which is not valid.
126 }
127 if (refIndex == 0) {
128 refIndex = cellAddress.length();
129 refPresent = false;
130 }
131 if ((fileIndex == -1) || ((refPresent) && (refIndex < fileIndex))) {
132 fileIndex = refIndex;
133 filePresent = false;
134 }
135 if (refIndex == 0) {
136 refPresent = false;
137 refIndex = fileIndex + 1;
138 }
139 portIndex = cellAddress.indexOf(':', hostIndex) + 1;
140 if ((portIndex == 0) || (portIndex > fileIndex) || (portIndex > refIndex)) {
141 portIndex = fileIndex;
142 portPresent = false;
143 }
144
145 /*
146 System.out.println("hostIndex: " + hostIndex);
147 if (portPresent)
148 System.out.println("portPresent");
149 else
150 System.out.println("!portPresent");
151
152 System.out.println("portIndex: " + portIndex);
153 if (filePresent)
154 System.out.println("filePresent");
155 else
156 System.out.println("!filePresent");
157 System.out.println("fileIndex: " + fileIndex);
158 if (refPresent)
159 System.out.println("refPresent");
160 else
161 System.out.println("!refPresent");
162 System.out.println("refIndex: " + refIndex);
163 */
164
165 try {
166 if (portPresent) {
167 host = cellAddress.substring(hostIndex, portIndex - 1);
168 int endPort;
169 if ((!filePresent) && (!refPresent)) {
170 port = Integer.parseInt(cellAddress.substring(portIndex));
171 }
172 else if (fileIndex < refIndex)
173 port = Integer.parseInt(cellAddress.substring(portIndex,
174 fileIndex));
175 else
176 port = Integer.parseInt(cellAddress.
177 substring(portIndex, refIndex - 1));
178 }
179 else {
180 host = cellAddress.substring(hostIndex, portIndex);
181 port = DEFAULT_PORT;
182 }
183 if (filePresent) {
184 if (refPresent) {
185 file = cellAddress.substring(fileIndex, refIndex - 1);
186 ref = cellAddress.substring(refIndex);
187 }
188 else {
189 file = cellAddress.substring(fileIndex, refIndex);
190 ref = "";
191 }
192 }
193 else {
194 file = "";
195 if (refPresent) {
196 ref = cellAddress.substring(refIndex);
197 }
198 else {
199 ref = "";
200 }
201 }
202 }
203 catch (NumberFormatException e) {
204 throw new MalformedHiveURLException(); // Bad port specification.
205 }
206 catch(StringIndexOutOfBoundsException e) {
207 throw new MalformedHiveURLException(); // Missing port specification
208 }
209
210 try {
211 String address = InetAddress.getByName(host).getHostAddress();
212 host = InetAddress.getByName(address).getHostName();
213 // Canonicalize hostname. Requires two DNS lookups.
214 }
215 catch (UnknownHostException e) {
216 throw new MalformedHiveURLException("Bad hostname");
217 }
218 if (port == -1) {
219 port = DEFAULT_PORT; // This matches the URL spec.
220 }
221 else if (port < -1) {
222 throw new MalformedHiveURLException(); // No negative ports other than -1 allowed.
223 }
224 calculateHashCode();
225 }
226
227 /** This Constructor allows the creation of a CellAddress with individual arguments rather than a parseable string.
228 *
229 * @param port
230 * @param file
231 * @param ref
232 * @param host
233 */
234 public CellAddress(String host, int port, String file, String ref) throws MalformedHiveURLException {
235 if ((host == null) || (host.equals(""))) {
236 throw new MalformedHiveURLException();
237 }
238 try {
239 String address = InetAddress.getByName(host).getHostAddress();
240 this.host = InetAddress.getByName(address).getHostName();
241 }
242 catch (UnknownHostException e) {
243 throw new MalformedHiveURLException("Bad hostname");
244 }
245 ;
246 if (port == -1) {
247 port = DEFAULT_PORT; // This matches the URL spec.
248 }
249 else if (port < -1) {
250 throw new MalformedHiveURLException();
251 }
252 this.port = port;
253 if (file == null) {
254 this.file = "";
255 }
256 else {
257 this.file = file;
258 }
259 if (ref == null) {
260 this.ref = "";
261 }
262 else {
263 this.ref = ref;
264 }
265 calculateHashCode();
266 }
267
268 /** A convenience method for the most common types of CellAddress
269 *
270 * @param port
271 * @param host
272 */
273 public CellAddress(String host, int port) throws MalformedHiveURLException {
274 this(host, port, null, null);
275 }
276
277 /** Returns a cell address for the local machine and the specified port.
278 */
279
280 public static CellAddress createLocalCellAddress(int port) {
281 String host;
282
283 try {
284 host = InetAddress.getByName(InetAddress.getLocalHost().getHostAddress()).getHostName();
285 }
286 catch (UnknownHostException e) {
287 Debug.error("Unable to lookup own address.");
288 return null;
289 }
290 try {
291 return new CellAddress(host, port);
292 }
293 catch (MalformedHiveURLException e) {
294 Debug.error("Somehow cannot create a CellAddress for the local machine.");
295 return null;
296 }
297 }
298
299 /** Returns a cell address for the local machine and the default port.
300 */
301 public static CellAddress createLocalCellAddress() {
302 return createLocalCellAddress(DEFAULT_PORT);
303 }
304
305 /** This method calculates the hashcode. All the fields must be valid before this
306 * method is called.
307 */
308 private void calculateHashCode() {
309 hashCode = getProtocol().hashCode() ^ getHost().toLowerCase().hashCode() ^ getPort() ^ file.hashCode() ^ ref.hashCode();
310 }
311
312 /** Return the host of the CellAddress
313 *
314 */
315 public String getHost() {
316 return host;
317 }
318
319 /** return the port number of the CellAdress
320 *
321 */
322 public int getPort() {
323 return port;
324 }
325
326 /** Return the file of the cell address. May return the empty string.
327 *
328 */
329 public String getFile() {
330 return file;
331 }
332
333 /** Return the reference of the CellAdress. May return the empty string.
334 *
335 */
336 public String getRef() {
337 return ref;
338 }
339
340 /** Returns the protocol of the URL. (Always "hive")
341 *
342 */
343 public String getProtocol() {
344 return PROTOCOL;
345 }
346
347 /** Returns true if the argument refers to the same cell as the
348 * one defined.
349 *
350 * @param obj */
351 public boolean equals(Object obj) {
352 if (obj instanceof CellAddress) {
353 CellAddress ca = (CellAddress) obj;
354 return (host.equalsIgnoreCase(ca.getHost()) &&
355 (port == ca.getPort()) &&
356 (file.equals(ca.getFile())) &&
357 (ref.equals(ca.getRef())));
358 }
359 return false;
360 }
361
362 /** Returns a hashcode for the CellAddress.
363 * */
364 public int hashCode() {
365 return hashCode;
366 }
367
368 /** Returns the string representation of the CellAddress.
369 *
370 */
371 public String toString() {
372 StringBuffer sb = new StringBuffer();
373 sb.append(PROTOCOL);
374 sb.append("://");
375 sb.append(host);
376 if (port != DEFAULT_PORT) {
377 sb.append(":");
378 sb.append(port);
379 }
380 if (!file.equals("")) {
381 sb.append(file);
382 }
383 if (!ref.equals("")) {
384 sb.append("#");
385 sb.append(ref);
386 }
387 return sb.toString();
388 }
389
390 /** Returns true if the address is determined to specify the
391 * local machine.
392 *
393 */
394 public boolean isLocal() {
395 try {
396 return InetAddress.getByName(host).equals(InetAddress.getLocalHost());
397 }
398 catch (UnknownHostException e) {
399 return false;
400 }
401 }
402
403 /**
404 * Find the Cell on the specified remote host.
405 * @param address The host:port address
406 * @return the RemoteCell, or else null if not found.
407 */
408 public RemoteCell getRemoteCell() throws CellConnectException {
409 String name = "//" + host + ":" + port + "/" + RMI_NAME;
410 try {
411 RemoteCell rs = (RemoteCell)Naming.lookup( name );
412 return rs;
413 }
414 catch( Exception error ) {
415 // Debug.warning( "Couldn't find server at " + name );
416 throw new CellConnectException( error );
417 }
418 }
419
420 /** Private testing method for test cases.
421 *
422 * @param str
423 */
424 private static void test(String str) {
425 System.out.println("Attempting to parse: " + str);
426 try{
427 CellAddress ca = new CellAddress(str);
428 System.out.println("Protocol: " + ca.getProtocol());
429 System.out.println("Host: " + ca.getHost());
430 System.out.println("Port: " + ca.getPort());
431 System.out.println("File: " + ca.getFile());
432 System.out.println("Ref: " + ca.getRef());
433 System.out.println(ca.toString());
434 System.out.println();
435 }
436 catch(MalformedHiveURLException e) {
437 System.out.println("Unparseable: " + str);
438 e.printStackTrace();
439 }
440 }
441
442 /** Test cases to check the complicated parsing method.
443 *
444 * @param args
445 */
446 public static final void main(String[] args) {
447 try {
448 System.out.println(InetAddress.getByName("18.244.1.37").getHostName());
449 }
450 catch(Throwable e) {
451 }
452 CellAddress.test("hive://oroup:23235/");
453 CellAddress.test("hive://oroup:23237");
454 CellAddress.test("oroup:23237/");
455 CellAddress.test("oroup");
456 CellAddress.test("oroup/");
457 CellAddress.test("oroup/sds#dfsdf");
458 CellAddress.test("hive://oroup.mit.edu:23235/34:53#21:24");
459 CellAddress.test("hive://oroup:23/sdfdf#sdfdf");
460 CellAddress.test("hi://oroup:23235/");
461 CellAddress.test("hive://oroup:232435");
462 CellAddress.test("hive://oroup:232435#sdffd");
463 CellAddress.test("4334:232435#sd/ffd");
464 }
465
466 }