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      * ".&lt;protocol&gt;.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.&lt;protocol&gt;.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    }