Frames | No Frames |
1: /* URL.java -- Uniform Resource Locator Class 2: Copyright (C) 1998, 1999, 2000, 2002, 2003, 2004, 2005 3: Free Software Foundation, Inc. 4: 5: This file is part of GNU Classpath. 6: 7: GNU Classpath is free software; you can redistribute it and/or modify 8: it under the terms of the GNU General Public License as published by 9: the Free Software Foundation; either version 2, or (at your option) 10: any later version. 11: 12: GNU Classpath is distributed in the hope that it will be useful, but 13: WITHOUT ANY WARRANTY; without even the implied warranty of 14: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15: General Public License for more details. 16: 17: You should have received a copy of the GNU General Public License 18: along with GNU Classpath; see the file COPYING. If not, write to the 19: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 20: 02110-1301 USA. 21: 22: Linking this library statically or dynamically with other modules is 23: making a combined work based on this library. Thus, the terms and 24: conditions of the GNU General Public License cover the whole 25: combination. 26: 27: As a special exception, the copyright holders of this library give you 28: permission to link this library with independent modules to produce an 29: executable, regardless of the license terms of these independent 30: modules, and to copy and distribute the resulting executable under 31: terms of your choice, provided that you also meet, for each linked 32: independent module, the terms and conditions of the license of that 33: module. An independent module is a module which is not derived from 34: or based on this library. If you modify this library, you may extend 35: this exception to your version of the library, but you are not 36: obligated to do so. If you do not wish to do so, delete this 37: exception statement from your version. */ 38: 39: package java.net; 40: 41: import gnu.java.net.URLParseError; 42: 43: import java.io.IOException; 44: import java.io.InputStream; 45: import java.io.ObjectInputStream; 46: import java.io.ObjectOutputStream; 47: import java.io.Serializable; 48: import java.security.AccessController; 49: import java.security.PrivilegedAction; 50: import java.util.HashMap; 51: import java.util.StringTokenizer; 52: 53: 54: /* 55: * Written using on-line Java Platform 1.2 API Specification, as well 56: * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). 57: * Status: Believed complete and correct. 58: */ 59: 60: /** 61: * This final class represents an Internet Uniform Resource Locator (URL). 62: * For details on the syntax of URL's and what they can be used for, 63: * refer to RFC 1738, available from <a 64: * href="http://ds.internic.net/rfcs/rfc1738.txt"> 65: * http://ds.internic.net/rfcs/rfc1738.txt</a> 66: * <p> 67: * There are a great many protocols supported by URL's such as "http", 68: * "ftp", and "file". This object can handle any arbitrary URL for which 69: * a URLStreamHandler object can be written. Default protocol handlers 70: * are provided for the "http" and "ftp" protocols. Additional protocols 71: * handler implementations may be provided in the future. In any case, 72: * an application or applet can install its own protocol handlers that 73: * can be "chained" with other protocol hanlders in the system to extend 74: * the base functionality provided with this class. (Note, however, that 75: * unsigned applets cannot access properties by default or install their 76: * own protocol handlers). 77: * <p> 78: * This chaining is done via the system property java.protocol.handler.pkgs 79: * If this property is set, it is assumed to be a "|" separated list of 80: * package names in which to attempt locating protocol handlers. The 81: * protocol handler is searched for by appending the string 82: * ".<protocol>.Handler" to each packed in the list until a hander is 83: * found. If a protocol handler is not found in this list of packages, or if 84: * the property does not exist, then the default protocol handler of 85: * "gnu.java.net.<protocol>.Handler" is tried. If this is 86: * unsuccessful, a MalformedURLException is thrown. 87: * <p> 88: * All of the constructor methods of URL attempt to load a protocol 89: * handler and so any needed protocol handlers must be installed when 90: * the URL is constructed. 91: * <p> 92: * Here is an example of how URL searches for protocol handlers. Assume 93: * the value of java.protocol.handler.pkgs is "com.foo|com.bar" and the 94: * URL is "news://comp.lang.java.programmer". URL would looking the 95: * following places for protocol handlers: 96: * <p><pre> 97: * com.foo.news.Handler 98: * com.bar.news.Handler 99: * gnu.java.net.news.Handler 100: * </pre><p> 101: * If the protocol handler is not found in any of those locations, a 102: * MalformedURLException would be thrown. 103: * <p> 104: * Please note that a protocol handler must be a subclass of 105: * URLStreamHandler. 106: * <p> 107: * Normally, this class caches protocol handlers. Once it finds a handler 108: * for a particular protocol, it never tries to look up a new handler 109: * again. However, if the system property 110: * gnu.java.net.nocache_protocol_handlers is set, then this 111: * caching behavior is disabled. This property is specific to this 112: * implementation. Sun's JDK may or may not do protocol caching, but it 113: * almost certainly does not examine this property. 114: * <p> 115: * Please also note that an application can install its own factory for 116: * loading protocol handlers (see setURLStreamHandlerFactory). If this is 117: * done, then the above information is superseded and the behavior of this 118: * class in loading protocol handlers is dependent on that factory. 119: * 120: * @author Aaron M. Renn (arenn@urbanophile.com) 121: * @author Warren Levy (warrenl@cygnus.com) 122: * 123: * @see URLStreamHandler 124: */ 125: public final class URL implements Serializable 126: { 127: private static final String DEFAULT_SEARCH_PATH = 128: "gnu.java.net.protocol|gnu.inet"; 129: 130: // Cached System ClassLoader 131: private static ClassLoader systemClassLoader; 132: 133: /** 134: * The name of the protocol for this URL. 135: * The protocol is always stored in lower case. 136: */ 137: private String protocol; 138: 139: /** 140: * The "authority" portion of the URL. 141: */ 142: private String authority; 143: 144: /** 145: * The hostname or IP address of this protocol. 146: * This includes a possible user. For example <code>joe@some.host.net</code>. 147: */ 148: private String host; 149: 150: /** 151: * The user information necessary to establish the connection. 152: */ 153: private String userInfo; 154: 155: /** 156: * The port number of this protocol or -1 if the port number used is 157: * the default for this protocol. 158: */ 159: private int port = -1; // Initialize for constructor using context. 160: 161: /** 162: * The "file" portion of the URL. It is defined as <code>path[?query]</code>. 163: */ 164: private String file; 165: 166: /** 167: * The anchor portion of the URL. 168: */ 169: private String ref; 170: 171: /** 172: * This is the hashCode for this URL 173: */ 174: private int hashCode; 175: 176: /** 177: * The protocol handler in use for this URL 178: */ 179: transient URLStreamHandler ph; 180: 181: /** 182: * If an application installs its own protocol handler factory, this is 183: * where we keep track of it. 184: */ 185: private static URLStreamHandlerFactory factory; 186: private static final long serialVersionUID = -7627629688361524110L; 187: 188: /** 189: * This a table where we cache protocol handlers to avoid the overhead 190: * of looking them up each time. 191: */ 192: private static HashMap ph_cache = new HashMap(); 193: 194: /** 195: * Whether or not to cache protocol handlers. 196: */ 197: private static boolean cache_handlers; 198: 199: static 200: { 201: String s = System.getProperty("gnu.java.net.nocache_protocol_handlers"); 202: 203: if (s == null) 204: cache_handlers = true; 205: else 206: cache_handlers = false; 207: } 208: 209: /** 210: * Constructs a URL and loads a protocol handler for the values passed as 211: * arguments. 212: * 213: * @param protocol The protocol for this URL ("http", "ftp", etc) 214: * @param host The hostname or IP address to connect to 215: * @param port The port number to use, or -1 to use the protocol's 216: * default port 217: * @param file The "file" portion of the URL. 218: * 219: * @exception MalformedURLException If a protocol handler cannot be loaded or 220: * a parse error occurs. 221: */ 222: public URL(String protocol, String host, int port, String file) 223: throws MalformedURLException 224: { 225: this(protocol, host, port, file, null); 226: } 227: 228: /** 229: * Constructs a URL and loads a protocol handler for the values passed in 230: * as arugments. Uses the default port for the protocol. 231: * 232: * @param protocol The protocol for this URL ("http", "ftp", etc) 233: * @param host The hostname or IP address for this URL 234: * @param file The "file" portion of this URL. 235: * 236: * @exception MalformedURLException If a protocol handler cannot be loaded or 237: * a parse error occurs. 238: */ 239: public URL(String protocol, String host, String file) 240: throws MalformedURLException 241: { 242: this(protocol, host, -1, file, null); 243: } 244: 245: /** 246: * This method initializes a new instance of <code>URL</code> with the 247: * specified protocol, host, port, and file. Additionally, this method 248: * allows the caller to specify a protocol handler to use instead of 249: * the default. If this handler is specified, the caller must have 250: * the "specifyStreamHandler" permission (see <code>NetPermission</code>) 251: * or a <code>SecurityException</code> will be thrown. 252: * 253: * @param protocol The protocol for this URL ("http", "ftp", etc) 254: * @param host The hostname or IP address to connect to 255: * @param port The port number to use, or -1 to use the protocol's default 256: * port 257: * @param file The "file" portion of the URL. 258: * @param ph The protocol handler to use with this URL. 259: * 260: * @exception MalformedURLException If no protocol handler can be loaded 261: * for the specified protocol. 262: * @exception SecurityException If the <code>SecurityManager</code> exists 263: * and does not allow the caller to specify its own protocol handler. 264: * 265: * @since 1.2 266: */ 267: public URL(String protocol, String host, int port, String file, 268: URLStreamHandler ph) throws MalformedURLException 269: { 270: if (protocol == null) 271: throw new MalformedURLException("null protocol"); 272: protocol = protocol.toLowerCase(); 273: this.protocol = protocol; 274: 275: if (ph != null) 276: { 277: SecurityManager s = System.getSecurityManager(); 278: if (s != null) 279: s.checkPermission(new NetPermission("specifyStreamHandler")); 280: 281: this.ph = ph; 282: } 283: else 284: this.ph = getURLStreamHandler(protocol); 285: 286: if (this.ph == null) 287: throw new MalformedURLException("Protocol handler not found: " 288: + protocol); 289: 290: this.host = host; 291: this.port = port; 292: this.authority = (host != null) ? host : ""; 293: if (port >= 0 && host != null) 294: this.authority += ":" + port; 295: 296: int hashAt = file.indexOf('#'); 297: if (hashAt < 0) 298: { 299: this.file = file; 300: this.ref = null; 301: } 302: else 303: { 304: this.file = file.substring(0, hashAt); 305: this.ref = file.substring(hashAt + 1); 306: } 307: hashCode = hashCode(); // Used for serialization. 308: } 309: 310: /** 311: * Initializes a URL from a complete string specification such as 312: * "http://www.urbanophile.com/arenn/". First the protocol name is parsed 313: * out of the string. Then a handler is located for that protocol and 314: * the parseURL() method of that protocol handler is used to parse the 315: * remaining fields. 316: * 317: * @param spec The complete String representation of a URL 318: * 319: * @exception MalformedURLException If a protocol handler cannot be found 320: * or the URL cannot be parsed 321: */ 322: public URL(String spec) throws MalformedURLException 323: { 324: this((URL) null, spec != null ? spec : "", (URLStreamHandler) null); 325: } 326: 327: /** 328: * This method parses a String representation of a URL within the 329: * context of an existing URL. Principally this means that any 330: * fields not present the URL are inheritied from the context URL. 331: * This allows relative URL's to be easily constructed. If the 332: * context argument is null, then a complete URL must be specified 333: * in the URL string. If the protocol parsed out of the URL is 334: * different from the context URL's protocol, then then URL String 335: * is also expected to be a complete URL. 336: * 337: * @param context The context on which to parse the specification 338: * @param spec The string to parse an URL 339: * 340: * @exception MalformedURLException If a protocol handler cannot be found 341: * for the URL cannot be parsed 342: */ 343: public URL(URL context, String spec) throws MalformedURLException 344: { 345: this(context, spec, (URLStreamHandler) null); 346: } 347: 348: /** 349: * Creates an URL from given arguments 350: * This method parses a String representation of a URL within the 351: * context of an existing URL. Principally this means that any fields 352: * not present the URL are inheritied from the context URL. This allows 353: * relative URL's to be easily constructed. If the context argument is 354: * null, then a complete URL must be specified in the URL string. 355: * If the protocol parsed out of the URL is different 356: * from the context URL's protocol, then then URL String is also 357: * expected to be a complete URL. 358: * <p> 359: * Additionally, this method allows the caller to specify a protocol handler 360: * to use instead of the default. If this handler is specified, the caller 361: * must have the "specifyStreamHandler" permission 362: * (see <code>NetPermission</code>) or a <code>SecurityException</code> 363: * will be thrown. 364: * 365: * @param context The context in which to parse the specification 366: * @param spec The string to parse as an URL 367: * @param ph The stream handler for the URL 368: * 369: * @exception MalformedURLException If a protocol handler cannot be found 370: * or the URL cannot be parsed 371: * @exception SecurityException If the <code>SecurityManager</code> exists 372: * and does not allow the caller to specify its own protocol handler. 373: * 374: * @since 1.2 375: */ 376: public URL(URL context, String spec, URLStreamHandler ph) 377: throws MalformedURLException 378: { 379: /* A protocol is defined by the doc as the substring before a ':' 380: * as long as the ':' occurs before any '/'. 381: * 382: * If context is null, then spec must be an absolute URL. 383: * 384: * The relative URL need not specify all the components of a URL. 385: * If the protocol, host name, or port number is missing, the value 386: * is inherited from the context. A bare file component is appended 387: * to the context's file. The optional anchor is not inherited. 388: */ 389: 390: // If this is an absolute URL, then ignore context completely. 391: // An absolute URL must have chars prior to "://" but cannot have a colon 392: // right after the "://". The second colon is for an optional port value 393: // and implies that the host from the context is used if available. 394: int colon; 395: int slash = spec.indexOf('/'); 396: if ((colon = spec.indexOf("://", 1)) > 0 397: && ((colon < slash || slash < 0)) 398: && ! spec.regionMatches(colon, "://:", 0, 4)) 399: context = null; 400: 401: if ((colon = spec.indexOf(':')) > 0 402: && (colon < slash || slash < 0)) 403: { 404: // Protocol specified in spec string. 405: protocol = spec.substring(0, colon).toLowerCase(); 406: if (context != null && context.protocol.equals(protocol)) 407: { 408: // The 1.2 doc specifically says these are copied to the new URL. 409: host = context.host; 410: port = context.port; 411: userInfo = context.userInfo; 412: authority = context.authority; 413: } 414: } 415: else if (context != null) 416: { 417: // Protocol NOT specified in spec string. 418: // Use context fields (except ref) as a foundation for relative URLs. 419: colon = -1; 420: protocol = context.protocol; 421: host = context.host; 422: port = context.port; 423: userInfo = context.userInfo; 424: if (spec.indexOf(":/", 1) < 0) 425: { 426: file = context.file; 427: if (file == null || file.length() == 0) 428: file = "/"; 429: } 430: authority = context.authority; 431: } 432: else // Protocol NOT specified in spec. and no context available. 433: throw new MalformedURLException("Absolute URL required with null" 434: + " context: " + spec); 435: 436: protocol = protocol.trim(); 437: 438: if (ph != null) 439: { 440: SecurityManager s = System.getSecurityManager(); 441: if (s != null) 442: s.checkPermission(new NetPermission("specifyStreamHandler")); 443: 444: this.ph = ph; 445: } 446: else 447: this.ph = getURLStreamHandler(protocol); 448: 449: if (this.ph == null) 450: throw new MalformedURLException("Protocol handler not found: " 451: + protocol); 452: 453: // JDK 1.2 doc for parseURL specifically states that any '#' ref 454: // is to be excluded by passing the 'limit' as the indexOf the '#' 455: // if one exists, otherwise pass the end of the string. 456: int hashAt = spec.indexOf('#', colon + 1); 457: 458: try 459: { 460: this.ph.parseURL(this, spec, colon + 1, 461: hashAt < 0 ? spec.length() : hashAt); 462: } 463: catch (URLParseError e) 464: { 465: throw new MalformedURLException(e.getMessage()); 466: } 467: 468: if (hashAt >= 0) 469: ref = spec.substring(hashAt + 1); 470: 471: hashCode = hashCode(); // Used for serialization. 472: } 473: 474: /** 475: * Test another URL for equality with this one. This will be true only if 476: * the argument is non-null and all of the fields in the URL's match 477: * exactly (ie, protocol, host, port, file, and ref). Overrides 478: * Object.equals(), implemented by calling the equals method of the handler. 479: * 480: * @param obj The URL to compare with 481: * 482: * @return true if the URL is equal, false otherwise 483: */ 484: public boolean equals(Object obj) 485: { 486: if (! (obj instanceof URL)) 487: return false; 488: 489: return ph.equals(this, (URL) obj); 490: } 491: 492: /** 493: * Returns the contents of this URL as an object by first opening a 494: * connection, then calling the getContent() method against the connection 495: * 496: * @return A content object for this URL 497: * @exception IOException If opening the connection or getting the 498: * content fails. 499: * 500: * @since 1.3 501: */ 502: public Object getContent() throws IOException 503: { 504: return openConnection().getContent(); 505: } 506: 507: /** 508: * Gets the contents of this URL 509: * 510: * @param classes The allow classes for the content object. 511: * 512: * @return a context object for this URL. 513: * 514: * @exception IOException If an error occurs 515: */ 516: public Object getContent(Class[] classes) throws IOException 517: { 518: // FIXME: implement this 519: return getContent(); 520: } 521: 522: /** 523: * Returns the file portion of the URL. 524: * Defined as <code>path[?query]</code>. 525: * Returns the empty string if there is no file portion. 526: * 527: * @return The filename specified in this URL, or an empty string if empty. 528: */ 529: public String getFile() 530: { 531: return file == null ? "" : file; 532: } 533: 534: /** 535: * Returns the path of the URL. This is the part of the file before any '?' 536: * character. 537: * 538: * @return The path specified in this URL, or null if empty. 539: * 540: * @since 1.3 541: */ 542: public String getPath() 543: { 544: // The spec says we need to return an empty string, but some 545: // applications depends on receiving null when the path is empty. 546: if (file == null) 547: return null; 548: int quest = file.indexOf('?'); 549: return quest < 0 ? getFile() : file.substring(0, quest); 550: } 551: 552: /** 553: * Returns the authority of the URL 554: * 555: * @return The authority specified in this URL. 556: * 557: * @since 1.3 558: */ 559: public String getAuthority() 560: { 561: return authority; 562: } 563: 564: /** 565: * Returns the host of the URL 566: * 567: * @return The host specified in this URL. 568: */ 569: public String getHost() 570: { 571: int at = (host == null) ? -1 : host.indexOf('@'); 572: return at < 0 ? host : host.substring(at + 1, host.length()); 573: } 574: 575: /** 576: * Returns the port number of this URL or -1 if the default port number is 577: * being used. 578: * 579: * @return The port number 580: * 581: * @see #getDefaultPort() 582: */ 583: public int getPort() 584: { 585: return port; 586: } 587: 588: /** 589: * Returns the default port of the URL. If the StreamHandler for the URL 590: * protocol does not define a default port it returns -1. 591: * 592: * @return The default port of the current protocol. 593: */ 594: public int getDefaultPort() 595: { 596: return ph.getDefaultPort(); 597: } 598: 599: /** 600: * Returns the protocol of the URL 601: * 602: * @return The specified protocol. 603: */ 604: public String getProtocol() 605: { 606: return protocol; 607: } 608: 609: /** 610: * Returns the ref (sometimes called the "# reference" or "anchor") portion 611: * of the URL. 612: * 613: * @return The ref 614: */ 615: public String getRef() 616: { 617: return ref; 618: } 619: 620: /** 621: * Returns the user information of the URL. This is the part of the host 622: * name before the '@'. 623: * 624: * @return the user at a particular host or null when no user defined. 625: */ 626: public String getUserInfo() 627: { 628: if (userInfo != null) 629: return userInfo; 630: int at = (host == null) ? -1 : host.indexOf('@'); 631: return at < 0 ? null : host.substring(0, at); 632: } 633: 634: /** 635: * Returns the query of the URL. This is the part of the file before the 636: * '?'. 637: * 638: * @return the query part of the file, or null when there is no query part. 639: */ 640: public String getQuery() 641: { 642: int quest = (file == null) ? -1 : file.indexOf('?'); 643: return quest < 0 ? null : file.substring(quest + 1, file.length()); 644: } 645: 646: /** 647: * Returns a hashcode computed by the URLStreamHandler of this URL 648: * 649: * @return The hashcode for this URL. 650: */ 651: public int hashCode() 652: { 653: if (hashCode != 0) 654: return hashCode; // Use cached value if available. 655: else 656: return ph.hashCode(this); 657: } 658: 659: /** 660: * Returns a URLConnection object that represents a connection to the remote 661: * object referred to by the URL. The URLConnection is created by calling the 662: * openConnection() method of the protocol handler 663: * 664: * @return A URLConnection for this URL 665: * 666: * @exception IOException If an error occurs 667: */ 668: public URLConnection openConnection() throws IOException 669: { 670: return ph.openConnection(this); 671: } 672: 673: /** 674: * Opens a connection to this URL and returns an InputStream for reading 675: * from that connection 676: * 677: * @return An <code>InputStream</code> for this URL. 678: * 679: * @exception IOException If an error occurs 680: */ 681: public InputStream openStream() throws IOException 682: { 683: return openConnection().getInputStream(); 684: } 685: 686: /** 687: * Tests whether or not another URL refers to the same "file" as this one. 688: * This will be true if and only if the passed object is not null, is a 689: * URL, and matches all fields but the ref (ie, protocol, host, port, 690: * and file); 691: * 692: * @param url The URL object to test with 693: * 694: * @return true if URL matches this URL's file, false otherwise 695: */ 696: public boolean sameFile(URL url) 697: { 698: return ph.sameFile(this, url); 699: } 700: 701: /** 702: * Sets the specified fields of the URL. This is not a public method so 703: * that only URLStreamHandlers can modify URL fields. This might be called 704: * by the <code>parseURL()</code> method in that class. URLs are otherwise 705: * constant. If the given protocol does not exist, it will keep the previously 706: * set protocol. 707: * 708: * @param protocol The protocol name for this URL 709: * @param host The hostname or IP address for this URL 710: * @param port The port number of this URL 711: * @param file The "file" portion of this URL. 712: * @param ref The anchor portion of this URL. 713: */ 714: protected void set(String protocol, String host, int port, String file, 715: String ref) 716: { 717: URLStreamHandler protocolHandler = null; 718: protocol = protocol.toLowerCase(); 719: if (! this.protocol.equals(protocol)) 720: protocolHandler = getURLStreamHandler(protocol); 721: 722: // It is an hidden feature of the JDK. If the protocol does not exist, 723: // we keep the previously initialized protocol. 724: if (protocolHandler != null) 725: { 726: this.ph = protocolHandler; 727: this.protocol = protocol; 728: } 729: this.authority = ""; 730: this.port = port; 731: this.host = host; 732: this.file = file; 733: this.ref = ref; 734: 735: if (host != null) 736: this.authority += host; 737: if (port >= 0) 738: this.authority += ":" + port; 739: 740: hashCode = hashCode(); // Used for serialization. 741: } 742: 743: /** 744: * Sets the specified fields of the URL. This is not a public method so 745: * that only URLStreamHandlers can modify URL fields. URLs are otherwise 746: * constant. If the given protocol does not exist, it will keep the previously 747: * set protocol. 748: * 749: * @param protocol The protocol name for this URL. 750: * @param host The hostname or IP address for this URL. 751: * @param port The port number of this URL. 752: * @param authority The authority of this URL. 753: * @param userInfo The user and password (if needed) of this URL. 754: * @param path The "path" portion of this URL. 755: * @param query The query of this URL. 756: * @param ref The anchor portion of this URL. 757: * 758: * @since 1.3 759: */ 760: protected void set(String protocol, String host, int port, String authority, 761: String userInfo, String path, String query, String ref) 762: { 763: URLStreamHandler protocolHandler = null; 764: protocol = protocol.toLowerCase(); 765: if (! this.protocol.equals(protocol)) 766: protocolHandler = getURLStreamHandler(protocol); 767: 768: // It is an hidden feature of the JDK. If the protocol does not exist, 769: // we keep the previously initialized protocol. 770: if (protocolHandler != null) 771: { 772: this.ph = protocolHandler; 773: this.protocol = protocol; 774: } 775: this.host = host; 776: this.userInfo = userInfo; 777: this.port = port; 778: this.authority = authority; 779: if (query == null) 780: this.file = path; 781: else 782: this.file = path + "?" + query; 783: this.ref = ref; 784: hashCode = hashCode(); // Used for serialization. 785: } 786: 787: /** 788: * Sets the URLStreamHandlerFactory for this class. This factory is 789: * responsible for returning the appropriate protocol handler for 790: * a given URL. 791: * 792: * @param fac The URLStreamHandlerFactory class to use 793: * 794: * @exception Error If the factory is alread set. 795: * @exception SecurityException If a security manager exists and its 796: * checkSetFactory method doesn't allow the operation 797: */ 798: public static synchronized void setURLStreamHandlerFactory(URLStreamHandlerFactory fac) 799: { 800: if (factory != null) 801: throw new Error("URLStreamHandlerFactory already set"); 802: 803: // Throw an exception if an extant security mgr precludes 804: // setting the factory. 805: SecurityManager s = System.getSecurityManager(); 806: if (s != null) 807: s.checkSetFactory(); 808: factory = fac; 809: } 810: 811: /** 812: * Returns a String representing this URL. The String returned is 813: * created by calling the protocol handler's toExternalForm() method. 814: * 815: * @return A string for this URL 816: */ 817: public String toExternalForm() 818: { 819: // Identical to toString(). 820: return ph.toExternalForm(this); 821: } 822: 823: /** 824: * Returns a String representing this URL. Identical to toExternalForm(). 825: * The value returned is created by the protocol handler's 826: * toExternalForm method. Overrides Object.toString() 827: * 828: * @return A string for this URL 829: */ 830: public String toString() 831: { 832: // Identical to toExternalForm(). 833: return ph.toExternalForm(this); 834: } 835: 836: /** 837: * This internal method is used in two different constructors to load 838: * a protocol handler for this URL. 839: * 840: * @param protocol The protocol to load a handler for 841: * 842: * @return A URLStreamHandler for this protocol, or null when not found. 843: */ 844: private static synchronized URLStreamHandler getURLStreamHandler(String protocol) 845: { 846: URLStreamHandler ph = null; 847: 848: // First, see if a protocol handler is in our cache. 849: if (cache_handlers) 850: { 851: if ((ph = (URLStreamHandler) ph_cache.get(protocol)) != null) 852: return ph; 853: } 854: 855: // If a non-default factory has been set, use it to find the protocol. 856: if (factory != null) 857: { 858: ph = factory.createURLStreamHandler(protocol); 859: } 860: else if (protocol.equals("core")) 861: { 862: ph = new gnu.java.net.protocol.core.Handler(); 863: } 864: else if (protocol.equals("file")) 865: { 866: // This is an interesting case. It's tempting to think that we 867: // could call Class.forName ("gnu.java.net.protocol.file.Handler") to 868: // get the appropriate class. Unfortunately, if we do that the 869: // program will never terminate, because getURLStreamHandler is 870: // eventually called by Class.forName. 871: // 872: // Treating "file" as a special case is the minimum that will 873: // fix this problem. If other protocols are required in a 874: // statically linked application they will need to be handled in 875: // the same way as "file". 876: ph = new gnu.java.net.protocol.file.Handler(); 877: } 878: 879: // Non-default factory may have returned null or a factory wasn't set. 880: // Use the default search algorithm to find a handler for this protocol. 881: if (ph == null) 882: { 883: // Get the list of packages to check and append our default handler 884: // to it, along with the JDK specified default as a last resort. 885: // Except in very unusual environments the JDK specified one shouldn't 886: // ever be needed (or available). 887: String ph_search_path = 888: System.getProperty("java.protocol.handler.pkgs"); 889: 890: // Tack our default package on at the ends. 891: if (ph_search_path != null) 892: ph_search_path += "|" + DEFAULT_SEARCH_PATH; 893: else 894: ph_search_path = DEFAULT_SEARCH_PATH; 895: 896: // Finally loop through our search path looking for a match. 897: StringTokenizer pkgPrefix = new StringTokenizer(ph_search_path, "|"); 898: 899: // Cache the systemClassLoader 900: if (systemClassLoader == null) 901: { 902: systemClassLoader = (ClassLoader) AccessController.doPrivileged 903: (new PrivilegedAction() { 904: public Object run() 905: { 906: return ClassLoader.getSystemClassLoader(); 907: } 908: }); 909: } 910: 911: do 912: { 913: try 914: { 915: // Try to get a class from the system/application 916: // classloader, initialize it, make an instance 917: // and try to cast it to a URLStreamHandler. 918: String clsName = 919: (pkgPrefix.nextToken() + "." + protocol + ".Handler"); 920: Class c = Class.forName(clsName, true, systemClassLoader); 921: ph = (URLStreamHandler) c.newInstance(); 922: } 923: catch (ThreadDeath death) 924: { 925: throw death; 926: } 927: catch (Throwable t) 928: { 929: // Ignored. 930: } 931: } 932: while (ph == null && pkgPrefix.hasMoreTokens()); 933: } 934: 935: // Update the hashtable with the new protocol handler. 936: if (ph != null && cache_handlers) 937: ph_cache.put(protocol, ph); 938: else 939: ph = null; 940: 941: return ph; 942: } 943: 944: private void readObject(ObjectInputStream ois) 945: throws IOException, ClassNotFoundException 946: { 947: ois.defaultReadObject(); 948: this.ph = getURLStreamHandler(protocol); 949: if (this.ph == null) 950: throw new IOException("Handler for protocol " + protocol + " not found"); 951: } 952: 953: private void writeObject(ObjectOutputStream oos) throws IOException 954: { 955: oos.defaultWriteObject(); 956: } 957: 958: /** 959: * Returns the equivalent <code>URI</code> object for this <code>URL</code>. 960: * This is the same as calling <code>new URI(this.toString())</code>. 961: * RFC2396-compliant URLs are guaranteed a successful conversion to 962: * a <code>URI</code> instance. However, there are some values which 963: * form valid URLs, but which do not also form RFC2396-compliant URIs. 964: * 965: * @throws URISyntaxException if this URL is not RFC2396-compliant, 966: * and thus can not be successfully converted to a URI. 967: */ 968: public URI toURI() 969: throws URISyntaxException 970: { 971: return new URI(toString()); 972: } 973: 974: }