1 /* URL.java -- Uniform Resource Locator Class
2 Copyright (C) 1998, 1999, 2000, 2002, 2003 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
41 import java.io.InputStream;
42 import java.io.IOException;
43 import java.io.Serializable;
44 import java.io.ObjectInputStream;
45 import java.io.ObjectOutputStream;
46 import java.util.Hashtable;
47 import java.util.StringTokenizer;
51 * Written using on-line Java Platform 1.2 API Specification, as well
52 * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998).
53 * Status: Believed complete and correct.
57 * This final class represents an Internet Uniform Resource Locator (URL).
58 * For details on the syntax of URL's and what they can be used for,
59 * refer to RFC 1738, available from <a
60 * href="http://ds.internic.net/rfcs/rfc1738.txt">http://ds.internic.net/rfcs/rfc1738.txt</a>
62 * There are a great many protocols supported by URL's such as "http",
63 * "ftp", and "file". This object can handle any arbitrary URL for which
64 * a URLStreamHandler object can be written. Default protocol handlers
65 * are provided for the "http" and "ftp" protocols. Additional protocols
66 * handler implementations may be provided in the future. In any case,
67 * an application or applet can install its own protocol handlers that
68 * can be "chained" with other protocol hanlders in the system to extend
69 * the base functionality provided with this class. (Note, however, that
70 * unsigned applets cannot access properties by default or install their
71 * own protocol handlers).
73 * This chaining is done via the system property java.protocol.handler.pkgs
74 * If this property is set, it is assumed to be a "|" separated list of
75 * package names in which to attempt locating protocol handlers. The
76 * protocol handler is searched for by appending the string
77 * ".<protocol>.Handler" to each packed in the list until a hander is found.
78 * If a protocol handler is not found in this list of packages, or if the
79 * property does not exist, then the default protocol handler of
80 * "gnu.java.net.<protocol>.Handler" is tried. If this is
81 * unsuccessful, a MalformedURLException is thrown.
83 * All of the constructor methods of URL attempt to load a protocol
84 * handler and so any needed protocol handlers must be installed when
85 * the URL is constructed.
87 * Here is an example of how URL searches for protocol handlers. Assume
88 * the value of java.protocol.handler.pkgs is "com.foo|com.bar" and the
89 * URL is "news://comp.lang.java.programmer". URL would looking the
90 * following places for protocol handlers:
92 * com.foo.news.Handler
93 * com.bar.news.Handler
94 * gnu.java.net.news.Handler
96 * If the protocol handler is not found in any of those locations, a
97 * MalformedURLException would be thrown.
99 * Please note that a protocol handler must be a subclass of
102 * @author Aaron M. Renn <arenn@urbanophile.com>
103 * @author Warren Levy <warrenl@cygnus.com>
105 * @see URLStreamHandler
107 public final class URL implements Serializable
110 * The name of the protocol for this URL.
111 * The protocol is always stored in lower case.
113 private String protocol;
116 * The "authority" portion of the URL.
118 private String authority;
121 * The hostname or IP address of this protocol.
122 * This includes a possible user. For example <code>joe@some.host.net</code>.
127 * The port number of this protocol or -1 if the port number used is
128 * the default for this protocol.
130 private int port = -1; // Initialize for constructor using context.
133 * The "file" portion of the URL. It is defined as <code>path[?query]</code>.
138 * The anchor portion of the URL.
143 * This is the hashCode for this URL
145 private int hashCode = 0;
148 * The protocol handler in use for this URL
150 transient URLStreamHandler ph;
153 * This a table where we cache protocol handlers to avoid the overhead
154 * of looking them up each time.
156 private static Hashtable handlers = new Hashtable();
159 * If an application installs its own protocol handler factory, this is
160 * where we keep track of it.
162 private static URLStreamHandlerFactory factory;
164 private static final long serialVersionUID = -7627629688361524110L;
167 * Constructs a URL and loads a protocol handler for the values passed as
170 * @param protocol The protocol for this URL ("http", "ftp", etc)
171 * @param host The hostname or IP address to connect to
172 * @param port The port number to use, or -1 to use the protocol's
174 * @param file The "file" portion of the URL.
176 * @exception MalformedURLException If a protocol handler cannot be loaded or
177 * a parse error occurs.
179 public URL(String protocol, String host, int port, String file)
180 throws MalformedURLException
182 this(protocol, host, port, file, null);
186 * Constructs a URL and loads a protocol handler for the values passed in
187 * as arugments. Uses the default port for the protocol.
189 * @param protocol The protocol for this URL ("http", "ftp", etc)
190 * @param host The hostname or IP address for this URL
191 * @param file The "file" portion of this URL.
193 * @exception MalformedURLException If a protocol handler cannot be loaded or
194 * a parse error occurs.
196 public URL(String protocol, String host, String file)
197 throws MalformedURLException
199 this(protocol, host, -1, file, null);
204 * This method initializes a new instance of <code>URL</code> with the
205 * specified protocol, host, port, and file. Additionally, this method
206 * allows the caller to specify a protocol handler to use instead of
207 * the default. If this handler is specified, the caller must have
208 * the "specifyStreamHandler" permission (see <code>NetPermission</code>)
209 * or a <code>SecurityException</code> will be thrown.
211 * @param protocol The protocol for this URL ("http", "ftp", etc)
212 * @param host The hostname or IP address to connect to
213 * @param port The port number to use, or -1 to use the protocol's default
215 * @param file The "file" portion of the URL.
216 * @param handler The protocol handler to use with this URL.
218 * @exception MalformedURLException If no protocol handler can be loaded
219 * for the specified protocol.
220 * @exception SecurityException If the <code>SecurityManager</code> exists
221 * and does not allow the caller to specify its own protocol handler.
225 public URL(String protocol, String host, int port, String file,
227 throws MalformedURLException
229 if (protocol == null)
230 throw new MalformedURLException("null protocol");
231 this.protocol = protocol.toLowerCase();
235 SecurityManager s = System.getSecurityManager();
237 s.checkPermission (new NetPermission ("specifyStreamHandler"));
242 this.ph = getURLStreamHandler(protocol);
245 throw new MalformedURLException (
246 "Protocol handler not found: " + protocol);
250 this.authority = null;
252 int hashAt = file.indexOf('#');
260 this.file = file.substring(0, hashAt);
261 this.ref = file.substring(hashAt + 1);
263 hashCode = hashCode(); // Used for serialization.
267 * Initializes a URL from a complete string specification such as
268 * "http://www.urbanophile.com/arenn/". First the protocol name is parsed
269 * out of the string. Then a handler is located for that protocol and
270 * the parseURL() method of that protocol handler is used to parse the
273 * @param spec The complete String representation of a URL
275 * @exception MalformedURLException If a protocol handler cannot be found
276 * or the URL cannot be parsed
278 public URL(String spec) throws MalformedURLException
280 this((URL) null, spec, (URLStreamHandler) null);
284 * This method parses a String representation of a URL within the
285 * context of an existing URL. Principally this means that any
286 * fields not present the URL are inheritied from the context URL.
287 * This allows relative URL's to be easily constructed. If the
288 * context argument is null, then a complete URL must be specified
289 * in the URL string. If the protocol parsed out of the URL is
290 * different from the context URL's protocol, then then URL String
291 * is also expected to be a complete URL.
293 * @param context The context on which to parse the specification
294 * @param spec The string to parse an URL
296 * @exception MalformedURLException If a protocol handler cannot be found
297 * for the URL cannot be parsed
299 public URL(URL context, String spec) throws MalformedURLException
301 this(context, spec, (URLStreamHandler) null);
305 * Creates an URL from given arguments
306 * This method parses a String representation of a URL within the
307 * context of an existing URL. Principally this means that any fields
308 * not present the URL are inheritied from the context URL. This allows
309 * relative URL's to be easily constructed. If the context argument is
310 * null, then a complete URL must be specified in the URL string.
311 * If the protocol parsed out of the URL is different
312 * from the context URL's protocol, then then URL String is also
313 * expected to be a complete URL.
315 * Additionally, this method allows the caller to specify a protocol handler
316 * to use instead of the default. If this handler is specified, the caller
317 * must have the "specifyStreamHandler" permission
318 * (see <code>NetPermission</code>) or a <code>SecurityException</code>
321 * @param context The context in which to parse the specification
322 * @param spec The string to parse as an URL
323 * @param handler The stream handler for the URL
325 * @exception MalformedURLException If a protocol handler cannot be found
326 * or the URL cannot be parsed
327 * @exception SecurityException If the <code>SecurityManager</code> exists
328 * and does not allow the caller to specify its own protocol handler.
332 public URL(URL context, String spec, URLStreamHandler ph)
333 throws MalformedURLException
335 /* A protocol is defined by the doc as the substring before a ':'
336 * as long as the ':' occurs before any '/'.
338 * If context is null, then spec must be an absolute URL.
340 * The relative URL need not specify all the components of a URL.
341 * If the protocol, host name, or port number is missing, the value
342 * is inherited from the context. A bare file component is appended
343 * to the context's file. The optional anchor is not inherited.
346 // If this is an absolute URL, then ignore context completely.
347 // An absolute URL must have chars prior to "://" but cannot have a colon
348 // right after the "://". The second colon is for an optional port value
349 // and implies that the host from the context is used if available.
351 if ((colon = spec.indexOf("://", 1)) > 0 &&
352 ! spec.regionMatches(colon, "://:", 0, 4))
356 if ((colon = spec.indexOf(':')) > 0 &&
357 (colon < (slash = spec.indexOf('/')) || slash < 0))
359 // Protocol specified in spec string.
360 protocol = spec.substring(0, colon).toLowerCase();
361 if (context != null && context.protocol.equals(protocol))
363 // The 1.2 doc specifically says these are copied to the new URL.
367 authority = context.authority;
370 else if (context != null)
372 // Protocol NOT specified in spec string.
373 // Use context fields (except ref) as a foundation for relative URLs.
375 protocol = context.protocol;
379 authority = context.authority;
381 else // Protocol NOT specified in spec. and no context available.
383 MalformedURLException("Absolute URL required with null context");
387 SecurityManager s = System.getSecurityManager ();
389 s.checkPermission (new NetPermission ("specifyStreamHandler"));
394 this.ph = getURLStreamHandler(protocol);
397 throw new MalformedURLException("Protocol handler not found: "
400 // JDK 1.2 doc for parseURL specifically states that any '#' ref
401 // is to be excluded by passing the 'limit' as the indexOf the '#'
402 // if one exists, otherwise pass the end of the string.
403 int hashAt = spec.indexOf('#', colon + 1);
404 this.ph.parseURL(this, spec, colon + 1,
405 hashAt < 0 ? spec.length() : hashAt);
407 ref = spec.substring(hashAt + 1);
409 hashCode = hashCode(); // Used for serialization.
413 * Test another URL for equality with this one. This will be true only if
414 * the argument is non-null and all of the fields in the URL's match
415 * exactly (ie, protocol, host, port, file, and ref). Overrides
416 * Object.equals(), implemented by calling the equals method of the handler.
418 * @param url The URL to compare with
420 * @return true if the URL is equal, false otherwise
422 public boolean equals (Object obj)
424 if (obj == null || ! (obj instanceof URL))
427 return ph.equals (this, (URL) obj);
431 * Returns the contents of this URL as an object by first opening a
432 * connection, then calling the getContent() method against the connection
434 * @return A content object for this URL
435 * @exception IOException If opening the connection or getting the
440 public final Object getContent() throws IOException
442 return openConnection().getContent();
446 * Gets the contents of this URL
448 * @exception IOException If an error occurs
450 public final Object getContent (Class[] classes) throws IOException
452 // FIXME: implement this
457 * Returns the file portion of the URL.
458 * Defined as <code>path[?query]</code>.
459 * Returns the empty string if there is no file portion.
461 public String getFile()
463 return file == null ? "" : file;
467 * Returns the path of the URL. This is the part of the file before any '?'
472 public String getPath()
474 int quest = (file == null) ? -1 : file.indexOf('?');
475 return quest < 0 ? getFile() : file.substring(0, quest);
479 * Returns the authority of the URL
483 public String getAuthority()
489 * Returns the host of the URL
491 public String getHost()
493 int at = (host == null) ? -1 : host.indexOf('@');
494 return at < 0 ? host : host.substring(at + 1, host.length());
498 * Returns the port number of this URL or -1 if the default port number is
501 * @return The port number
503 * @see #getDefaultPort()
511 * Returns the default port of the URL. If the StreamHandler for the URL
512 * protocol does not define a default port it returns -1.
514 public int getDefaultPort()
516 return ph.getDefaultPort();
520 * Returns the protocol of the URL
522 public String getProtocol()
528 * Returns the ref (sometimes called the "# reference" or "anchor") portion
533 public String getRef()
539 * Returns the user information of the URL. This is the part of the host
540 * name before the '@'.
542 * @return the user at a particular host or null when no user defined.
544 public String getUserInfo ()
546 int at = (host == null) ? -1 : host.indexOf('@');
547 return at < 0 ? null : host.substring(0, at);
551 * Returns the query of the URL. This is the part of the file before the
554 * @return the query part of the file, or null when there is no query part.
556 public String getQuery ()
558 int quest = (file == null) ? -1 : file.indexOf('?');
559 return quest < 0 ? null : file.substring(quest + 1, file.length());
563 * Returns a hashcode computed by the URLStreamHandler of this URL
565 public int hashCode()
568 return hashCode; // Use cached value if available.
570 return ph.hashCode (this);
574 * Returns a URLConnection object that represents a connection to the remote
575 * object referred to by the URL. The URLConnection is created by calling the
576 * openConnection() method of the protocol handler
578 * @return A URLConnection for this URL
579 * @exception IOException If an error occurs
581 public URLConnection openConnection() throws IOException
583 return ph.openConnection(this);
587 * Opens a connection to this URL and returns an InputStream for reading
588 * from that connection
590 * @exception IOException If an error occurs
592 public final InputStream openStream() throws IOException
594 return openConnection().getInputStream();
598 * Tests whether or not another URL refers to the same "file" as this one.
599 * This will be true if and only if the passed object is not null, is a
600 * URL, and matches all fields but the ref (ie, protocol, host, port,
603 * @param url The URL object to test with
605 * @return true if URL matches this URL's file, false otherwise
607 public boolean sameFile(URL other)
609 return ph.sameFile(this, other);
613 * Sets the specified fields of the URL. This is not a public method so
614 * that only URLStreamHandlers can modify URL fields. This might be called
615 * by the <code>parseURL()</code> method in that class. URLs are otherwise
618 * @param protocol The protocol name for this URL
619 * @param host The hostname or IP address for this URL
620 * @param port The port number of this URL
621 * @param file The "file" portion of this URL.
622 * @param ref The anchor portion of this URL.
624 protected void set(String protocol, String host, int port, String file,
627 // TBD: Theoretically, a poorly written StreamHandler could pass an
628 // invalid protocol. It will cause the handler to be set to null
629 // thus overriding a valid handler. Callers of this method should
631 this.ph = getURLStreamHandler(protocol);
632 this.protocol = protocol.toLowerCase();
633 this.authority = null;
638 hashCode = hashCode(); // Used for serialization.
642 * Sets the specified fields of the URL. This is not a public method so
643 * that only URLStreamHandlers can modify URL fields. URLs are otherwise
648 protected void set(String protocol, String host, int port,
649 String authority, String userInfo,
650 String path, String query, String ref)
652 // TBD: Theoretically, a poorly written StreamHandler could pass an
653 // invalid protocol. It will cause the handler to be set to null
654 // thus overriding a valid handler. Callers of this method should
656 this.ph = getURLStreamHandler(protocol);
657 this.protocol = protocol.toLowerCase();
658 if (userInfo == null)
661 this.host = userInfo + "@" + host;
666 this.file = path + "?" + query;
668 hashCode = hashCode(); // Used for serialization.
672 * Sets the URLStreamHandlerFactory for this class. This factory is
673 * responsible for returning the appropriate protocol handler for
676 * @param fac The URLStreamHandlerFactory class to use
678 * @exception Error If the factory is alread set.
679 * @exception SecurityException If a security manager exists and its
680 * checkSetFactory method doesn't allow the operation
682 public static synchronized void
683 setURLStreamHandlerFactory(URLStreamHandlerFactory fac)
686 throw new Error("URLStreamHandlerFactory already set");
688 // Throw an exception if an extant security mgr precludes
689 // setting the factory.
690 SecurityManager s = System.getSecurityManager();
697 * Returns a String representing this URL. The String returned is
698 * created by calling the protocol handler's toExternalForm() method.
700 * @return A string for this URL
702 public String toExternalForm()
704 // Identical to toString().
705 return ph.toExternalForm(this);
709 * Returns a String representing this URL. Identical to toExternalForm().
710 * The value returned is created by the protocol handler's
711 * toExternalForm method. Overrides Object.toString()
713 * @return A string for this URL
715 public String toString()
717 // Identical to toExternalForm().
718 return ph.toExternalForm(this);
721 private static synchronized URLStreamHandler
722 getURLStreamHandler (String protocol)
726 // See if a handler has been cached for this protocol.
727 if ((ph = (URLStreamHandler) handlers.get(protocol)) != null)
730 // If a non-default factory has been set, use it to find the protocol.
732 ph = factory.createURLStreamHandler(protocol);
733 else if (protocol.equals ("core"))
735 ph = new gnu.gcj.protocol.core.Handler ();
737 else if (protocol.equals ("file"))
739 // This is an interesting case. It's tempting to think that we
740 // could call Class.forName ("gnu.gcj.protocol.file.Handler") to
741 // get the appropriate class. Unfortunately, if we do that the
742 // program will never terminate, because getURLStreamHandler is
743 // eventually called by Class.forName.
745 // Treating "file" as a special case is the minimum that will
746 // fix this problem. If other protocols are required in a
747 // statically linked application they will need to be handled in
748 // the same way as "file".
749 ph = new gnu.gcj.protocol.file.Handler ();
752 // Non-default factory may have returned null or a factory wasn't set.
753 // Use the default search algorithm to find a handler for this protocol.
756 // Get the list of packages to check and append our default handler
757 // to it, along with the JDK specified default as a last resort.
758 // Except in very unusual environments the JDK specified one shouldn't
759 // ever be needed (or available).
760 String propVal = System.getProperty("java.protocol.handler.pkgs");
761 propVal = (propVal == null) ? "" : (propVal + "|");
762 propVal = propVal + "gnu.gcj.protocol|sun.net.www.protocol";
764 StringTokenizer pkgPrefix = new StringTokenizer(propVal, "|");
767 String facName = pkgPrefix.nextToken() + "." + protocol +
771 ph = (URLStreamHandler) Class.forName(facName).newInstance();
775 // Can't instantiate; handler still null, go on to next element.
777 } while ((ph == null ||
778 ! (ph instanceof URLStreamHandler)) &&
779 pkgPrefix.hasMoreTokens());
782 // Update the hashtable with the new protocol handler.
784 if (ph instanceof URLStreamHandler)
785 handlers.put(protocol, ph);
792 private void readObject(ObjectInputStream ois)
793 throws IOException, ClassNotFoundException
795 ois.defaultReadObject();
796 this.ph = getURLStreamHandler(protocol);
798 throw new IOException("Handler for protocol " + protocol + " not found");
801 private void writeObject(ObjectOutputStream oos) throws IOException
803 oos.defaultWriteObject();