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