/* URLConnection.java -- Abstract superclass for reading from URL's
- Copyright (C) 1998, 2002 Free Software Foundation, Inc.
+ Copyright (C) 1998, 2002, 2003, 2004 Free Software Foundation, Inc.
This file is part of GNU Classpath.
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
-
+
GNU Classpath is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
package java.net;
-import java.io.InputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.io.OutputStream;
-import java.security.Permission;
import java.security.AllPermission;
+import java.security.Permission;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
+import java.util.Collections;
import java.util.Date;
-import java.util.Locale;
import java.util.Hashtable;
+import java.util.Locale;
import java.util.Map;
import java.util.StringTokenizer;
import gnu.gcj.io.MimeTypes;
+
/**
* Written using on-line Java Platform 1.2 API Specification, as well
* as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998).
* Status: One guessContentTypeFrom... methods not implemented.
* getContent method assumes content type from response; see comment there.
*/
-
/**
* This class models a connection that retrieves the information pointed
* to by a URL object. This is typically a connection to a remote node
* by the actual content handlers as described in the description of that
* method.
*
- * @author Aaron M. Renn <arenn@urbanophile.com>
- * @author Warren Levy <warrenl@cygnus.com>
+ * @author Aaron M. Renn (arenn@urbanophile.com)
+ * @author Warren Levy (warrenl@cygnus.com)
*/
public abstract class URLConnection
{
* instance and store it here.
*/
private static FileNameMap fileNameMap;
-
+
/**
* This is the ContentHandlerFactory set by the caller, if any
*/
private static ContentHandlerFactory factory;
-
+
/**
* This is the default value that will be used to determine whether or
* not user interaction should be allowed.
*/
- private static boolean defaultAllowUserInteraction = false;
-
+ private static boolean defaultAllowUserInteraction;
+
/**
* This is the default flag indicating whether or not to use caches to
* store the data returned from a server
*/
private static boolean defaultUseCaches = true;
+ private static ContentHandlerFactory defaultFactory
+ = new gnu.java.net.DefaultContentHandlerFactory();
+
/**
* This variable determines whether or not interaction is allowed with
* the user. For example, to prompt for a username and password.
* Indicates whether or not a connection has been established to the
* destination specified in the URL
*/
- protected boolean connected = false;
-
+ protected boolean connected;
+
/**
* Indicates whether or not input can be read from this URL
*/
protected boolean doInput = true;
-
+
/**
* Indicates whether or not output can be sent to this URL
*/
- protected boolean doOutput = false;
-
+ protected boolean doOutput;
+
/**
* If this flag is set, the protocol is allowed to cache data whenever
* it can (caching is not guaranteed). If it is not set, the protocol
* modified more recently than the date set in this variable. That date
* should be specified as the number of seconds since 1/1/1970 GMT.
*/
- protected long ifModifiedSince = 0L;
+ protected long ifModifiedSince;
/**
* This is the URL associated with this connection
*/
protected URL url;
- private static ContentHandler contentHandler;
private static Hashtable handlers = new Hashtable();
- private static Locale locale;
- private static SimpleDateFormat dateFormat1, dateFormat2, dateFormat3;
- private static boolean dateformats_initialized = false;
+ private static SimpleDateFormat[] dateFormats;
+ private static boolean dateformats_initialized;
+
+ /* Cached ParsePosition, used when parsing dates. */
+ private ParsePosition position;
/**
* Creates a URL connection to a given URL. A real connection is not made.
/**
* Establishes the actual connection to the URL associated with this
* connection object
+ *
+ * @exception IOException if an error occurs
*/
public abstract void connect() throws IOException;
/**
* Returns the value of the content-encoding field or null if it is not
* known or not present.
- *
+ *
* @return The content-encoding field
*/
public String getContentEncoding()
* Returns a String representing the value of the header field having
* the named key. Returns null if the header field does not exist.
*
- * @param The key of the header field
+ * @param name The key of the header field
*
* @return The value of the header field as a String
*/
/**
* Returns a map of all sent header fields
- *
+ *
+ * @return all header fields
+ *
* @since 1.4
*/
public Map getHeaderFields()
{
// Subclasses for specific protocols override this.
- return null;
+ return Collections.EMPTY_MAP;
}
/**
* is not present or cannot be parsed as an integer, the default value
* will be returned.
*
- * @param name The name of the header field
- * @param val The default value
+ * @param name The header field key to lookup
+ * @param defaultValue The defaule value if the header field is not found
+ * or can't be parsed.
*
* @return The value of the header field or the default value if the field
* is missing or malformed
*/
- public int getHeaderFieldInt(String name, int val)
+ public int getHeaderFieldInt(String name, int defaultValue)
{
- String str = getHeaderField(name);
+ String value = getHeaderField(name);
+
+ if (value == null)
+ return defaultValue;
+
try
{
- if (str != null)
- val = Integer.parseInt(str);
+ return Integer.parseInt(value);
}
catch (NumberFormatException e)
{
- ; // Do nothing; val is the default.
+ return defaultValue;
}
- return val;
}
/**
* value if the field is not present or cannot be converted to a date.
*
* @param name The name of the header field
- * @param val The dafault date
+ * @param defaultValue The default date if the header field is not found
+ * or can't be converted.
*
* @return Returns the date value of the header filed or the default value
* if the field is missing or malformed
*/
- public long getHeaderFieldDate(String name, long val)
+ public long getHeaderFieldDate(String name, long defaultValue)
{
if (! dateformats_initialized)
initializeDateFormats();
+
+ if (position == null)
+ position = new ParsePosition(0);
+
+ long result = defaultValue;
String str = getHeaderField(name);
+
if (str != null)
{
- Date date;
- if ((date = dateFormat1.parse(str, new ParsePosition(0))) != null)
- val = date.getTime();
- else if ((date = dateFormat2.parse(str, new ParsePosition(0))) != null)
- val = date.getTime();
- else if ((date = dateFormat3.parse(str, new ParsePosition(0))) != null)
- val = date.getTime();
+ for (int i = 0; i < dateFormats.length; i++)
+ {
+ SimpleDateFormat df = dateFormats[i];
+ position.setIndex(0);
+ position.setErrorIndex(0);
+ Date date = df.parse(str, position);
+ if (date != null)
+ return date.getTime();
+ }
}
- return val;
+
+ return result;
}
/**
* getHeaderField(int) method allows access to the corresponding value for
* this tag.
*
- * @param index The index into the header field list to retrieve the key for.
+ * @param index The index into the header field list to retrieve the key for.
*
* @return The header field key or null if index is past the end
* of the headers.
}
/**
- * This method returns the content of the document pointed to by the URL
- * as an Object. The type of object depends on the MIME type of the
- * object and particular content hander loaded. Most text type content
- * handlers will return a subclass of InputStream. Images usually return
- * a class that implements ImageProducer. There is not guarantee what
- * type of object will be returned, however.
- * <p>
- * This class first determines the MIME type of the content, then creates
- * a ContentHandler object to process the input. If the ContentHandlerFactory
- * is set, then that object is called to load a content handler, otherwise
- * a class called gnu.java.net.content.<content_type> is tried.
- * The default class will also be used if the content handler factory returns
- * a null content handler.
- *
- * @exception IOException If an error occurs
+ * This method returns the content of the document pointed to by the
+ * URL as an Object. The type of object depends on the MIME type of
+ * the object and particular content hander loaded. Most text type
+ * content handlers will return a subclass of
+ * <code>InputStream</code>. Images usually return a class that
+ * implements <code>ImageProducer</code>. There is not guarantee
+ * what type of object will be returned, however.
+ *
+ * <p>This class first determines the MIME type of the content, then
+ * creates a ContentHandler object to process the input. If the
+ * <code>ContentHandlerFactory</code> is set, then that object is
+ * called to load a content handler, otherwise a class called
+ * gnu.java.net.content.<content_type> is tried. If this
+ * handler does not exist, the method will simple return the
+ * <code>InputStream</code> returned by
+ * <code>getInputStream()</code>. Note that the default
+ * implementation of <code>getInputStream()</code> throws a
+ * <code>UnknownServiceException</code> so subclasses are encouraged
+ * to override this method.</p>
+ *
+ * @exception IOException If an error with the connection occurs.
* @exception UnknownServiceException If the protocol does not support the
- * content type
+ * content type at all.
*/
public Object getContent() throws IOException
{
+ if (!connected)
+ connect();
+
// FIXME: Doc indicates that other criteria should be applied as
// heuristics to determine the true content type, e.g. see
// guessContentTypeFromName() and guessContentTypeFromStream methods
// as well as FileNameMap class & fileNameMap field & get/set methods.
- String cType = getContentType();
- contentHandler = setContentHandler(cType);
- if (contentHandler == null)
- return getInputStream();
+ String type = getContentType();
+ ContentHandler ch = getContentHandler(type);
+
+ if (ch != null)
+ return ch.getContent(this);
- return contentHandler.getContent(this);
+ return getInputStream();
}
/**
* Retrieves the content of this URLConnection
*
+ * @param classes The allowed classes for the content
+ *
* @exception IOException If an error occurs
* @exception UnknownServiceException If the protocol does not support the
* content type
public Object getContent(Class[] classes) throws IOException
{
// FIXME: implement this
- return getContent ();
+ return getContent();
}
/**
public Permission getPermission() throws IOException
{
// Subclasses may override this.
- return new java.security.AllPermission();
+ return new AllPermission();
}
/**
public InputStream getInputStream() throws IOException
{
// Subclasses for specific protocols override this.
- throw new UnknownServiceException("Protocol " + url.getProtocol() +
- " does not support input.");
+ throw new UnknownServiceException("Protocol " + url.getProtocol()
+ + " does not support input.");
}
/**
public OutputStream getOutputStream() throws IOException
{
// Subclasses for specific protocols override this.
- throw new UnknownServiceException("Protocol " + url.getProtocol() +
- " does not support output.");
+ throw new UnknownServiceException("Protocol " + url.getProtocol()
+ + " does not support output.");
}
/**
* The methods prints the value of this object as a String by calling the
* toString() method of its associated URL. Overrides Object.toString()
- *
+ *
* @return A String representation of this object
*/
public String toString()
* Returns the value of a flag indicating whether or not input is going
* to be done for this connection. This default to true unless the
* doOutput flag is set to false, in which case this defaults to false.
- *
- * @param doinput The new value of the doInput field
+ *
+ * @param input <code>true</code> if input is to be done,
+ * <code>false</code> otherwise
*
* @exception IllegalStateException If already connected
*/
public void setDoInput(boolean input)
{
if (connected)
- throw new IllegalStateException ("Already connected");
+ throw new IllegalStateException("Already connected");
doInput = input;
}
public void setDoOutput(boolean output)
{
if (connected)
- throw new IllegalStateException ("Already connected");
+ throw new IllegalStateException("Already connected");
doOutput = output;
}
public void setUseCaches(boolean usecaches)
{
if (connected)
- throw new IllegalStateException ("Already connected");
+ throw new IllegalStateException("Already connected");
useCaches = usecaches;
}
public void setIfModifiedSince(long ifmodifiedsince)
{
if (connected)
- throw new IllegalStateException ("Already connected");
+ throw new IllegalStateException("Already connected");
ifModifiedSince = ifmodifiedsince;
}
}
/**
- * Returns the default value of the useCaches field
+ * Returns the default value used to determine whether or not caching
+ * of documents will be done when possible.
+ *
+ * @return true if caches will be used, false otherwise
*/
public boolean getDefaultUseCaches()
{
*
* @param use true to use caches if possible by default, false otherwise
*/
- public void setDefaultUseCaches(boolean defaultusecaches)
+ public void setDefaultUseCaches(boolean use)
{
- defaultUseCaches = defaultusecaches;
+ defaultUseCaches = use;
}
/**
- * Returns the default value used to determine whether or not caching
- * of documents will be done when possible.
+ * Sets the value of the named request property
*
- * @param key Key of the property to set
- * @param value Value of the Property to set
+ * @param key The name of the property
+ * @param value The value of the property
*
* @exception IllegalStateException If already connected
* @exception NullPointerException If key is null
*
* @see URLConnection#getRequestProperty(String key)
* @see URLConnection#addRequestProperty(String key, String value)
+ *
+ * @since 1.4
*/
public void setRequestProperty(String key, String value)
{
if (connected)
- throw new IllegalStateException ("Already connected");
+ throw new IllegalStateException("Already connected");
+
+ if (key == null)
+ throw new NullPointerException("key is null");
// Do nothing unless overridden by subclasses that support setting
// header fields in the request.
}
/**
- * Sets the value of the named request property
+ * Adds a new request property by a key/value pair.
+ * This method does not overwrite existing properties with the same key.
*
* @param key Key of the property to add
* @param value Value of the Property to add
*
* @exception IllegalStateException If already connected
* @exception NullPointerException If key is null
- *
+ *
* @see URLConnection#getRequestProperty(String key)
* @see URLConnection#setRequestProperty(String key, String value)
- *
+ *
* @since 1.4
*/
public void addRequestProperty(String key, String value)
{
if (connected)
- throw new IllegalStateException ("Already connected");
+ throw new IllegalStateException("Already connected");
- if (getRequestProperty (key) == null)
- {
- setRequestProperty (key, value);
- }
+ if (key == null)
+ throw new NullPointerException("key is null");
+
+ // Do nothing unless overridden by subclasses that support adding
+ // header fields in the request.
}
/**
public String getRequestProperty(String key)
{
if (connected)
- throw new IllegalStateException ("Already connected");
+ throw new IllegalStateException("Already connected");
// Overridden by subclasses that support reading header fields from the
// request.
*/
public Map getRequestProperties()
{
+ if (connected)
+ throw new IllegalStateException("Already connected");
+
// Overridden by subclasses that support reading header fields from the
// request.
- return null;
+ return Collections.EMPTY_MAP;
}
/**
* @param key The request property name the default is being set for
* @param value The value to set the default to
*
- * @deprecated 1.3 The method setRequestProperty should be used instead
+ * @deprecated 1.3 The method setRequestProperty should be used instead.
+ * This method does nothing now.
*
- * @see URLConnectionr#setRequestProperty(String key, String value)
+ * @see URLConnection#setRequestProperty(String key, String value)
*/
public static void setDefaultRequestProperty(String key, String value)
{
- // Do nothing unless overridden by subclasses that support setting
- // default request properties.
+ // This method does nothing since JDK 1.3.
}
/**
* @param key The request property to return the default value of
*
* @return The value of the default property or null if not available
- *
- * @deprecated 1.3 The method getRequestProperty should be used instead
+ *
+ * @deprecated 1.3 The method getRequestProperty should be used instead.
+ * This method does nothing now.
*
* @see URLConnection#getRequestProperty(String key)
*/
public static String getDefaultRequestProperty(String key)
{
- // Overridden by subclasses that support default request properties.
+ // This method does nothing since JDK 1.3.
return null;
}
* @exception SecurityException If a security manager exists and its
* checkSetFactory method doesn't allow the operation
*/
- public static synchronized void setContentHandlerFactory
- (ContentHandlerFactory fac)
+ public static synchronized void setContentHandlerFactory(ContentHandlerFactory factory)
{
- if (factory != null)
+ if (URLConnection.factory != null)
throw new Error("ContentHandlerFactory already set");
// Throw an exception if an extant security mgr precludes
if (s != null)
s.checkSetFactory();
- factory = fac;
+ URLConnection.factory = factory;
}
/**
*/
public static String guessContentTypeFromName(String filename)
{
- int dot = filename.lastIndexOf (".");
+ int dot = filename.lastIndexOf(".");
if (dot != -1)
{
if (dot == filename.length())
- return ("application/octet-stream");
+ return "application/octet-stream";
else
- filename = filename.substring (dot + 1);
+ filename = filename.substring(dot + 1);
}
- String type = MimeTypes.getMimeTypeFromExtension (filename);
+ String type = MimeTypes.getMimeTypeFromExtension(filename);
if (type == null)
- return("application/octet-stream");
+ return"application/octet-stream";
return type;
}
* Returns the MIME type of a stream based on the first few characters
* at the beginning of the stream. This routine can be used to determine
* the MIME type if a server is believed to be returning an incorrect
- * MIME type. This method returns "application/octet-stream" if it
+ * MIME type. This method returns "application/octet-stream" if it
* cannot determine the MIME type.
* <p>
* NOTE: Overriding MIME types sent from the server can be obnoxious
*
* @exception SecurityException If a security manager exists and its
* checkSetFactory method doesn't allow the operation
- *
+ *
* @since 1.2
*/
public static void setFileNameMap(FileNameMap map)
{
- // Throw an exception if an extant security mgr precludes
+ // Throw an exception if an extant security manager precludes
// setting the factory.
SecurityManager s = System.getSecurityManager();
if (s != null)
fileNameMap = map;
}
- private ContentHandler setContentHandler(String contentType)
+ private ContentHandler getContentHandler(String contentType)
{
- ContentHandler handler;
-
// No content type so just handle it as the default.
- if (contentType == null || contentType == "")
+ if (contentType == null || contentType.equals(""))
return null;
+ ContentHandler handler;
+
// See if a handler has been cached for this content type.
// For efficiency, if a content type has been searched for but not
// found, it will be in the hash table but as the contentType String
else
return null;
- // If a non-default factory has been set, use it to find the content type.
+ // If a non-default factory has been set, use it.
if (factory != null)
handler = factory.createContentHandler(contentType);
- // Non-default factory may have returned null or a factory wasn't set.
- // Use the default search algorithm to find a handler for this content type.
+ // Now try default factory. Using this factory to instantiate built-in
+ // content handlers is preferable
+ if (handler == null)
+ handler = defaultFactory.createContentHandler(contentType);
+
+ // User-set factory has not returned a handler. Use the default search
+ // algorithm.
if (handler == null)
{
// Get the list of packages to check and append our default handler
// ever be needed (or available).
String propVal = System.getProperty("java.content.handler.pkgs");
propVal = (propVal == null) ? "" : (propVal + "|");
- propVal = propVal + "gnu.gcj.content|sun.net.www.content";
+ propVal = propVal + "gnu.java.net.content|sun.net.www.content";
// Replace the '/' character in the content type with '.' and
// all other non-alphabetic, non-numeric characters with '_'.
- StringTokenizer pkgPrefix = new StringTokenizer(propVal, "|");
char[] cArray = contentType.toCharArray();
for (int i = 0; i < cArray.length; i++)
{
String contentClass = new String(cArray);
// See if a class of this content type exists in any of the packages.
+ StringTokenizer pkgPrefix = new StringTokenizer(propVal, "|");
do
{
String facName = pkgPrefix.nextToken() + "." + contentClass;
}
// Update the hashtable with the new content handler.
- if (handler != null && handler instanceof ContentHandler)
+ if (handler instanceof ContentHandler)
{
handlers.put(contentType, handler);
return handler;
// We don't put these in a static initializer, because it creates problems
// with initializer co-dependency: SimpleDateFormat's constructors eventually
// depend on URLConnection (via the java.text.*Symbols classes).
- private synchronized void initializeDateFormats()
+ private static synchronized void initializeDateFormats()
{
if (dateformats_initialized)
return;
- locale = new Locale("En", "Us", "Unix");
- dateFormat1 = new SimpleDateFormat("EEE, dd MMM yyyy hh:mm:ss 'GMT'",
- locale);
- dateFormat2 = new SimpleDateFormat("EEEE, dd-MMM-yy hh:mm:ss 'GMT'",
- locale);
- dateFormat3 = new SimpleDateFormat("EEE MMM d hh:mm:ss yyyy", locale);
+
+ Locale locale = new Locale("En", "Us", "Unix");
+ dateFormats = new SimpleDateFormat[3];
+ dateFormats[0] =
+ new SimpleDateFormat("EEE, dd MMM yyyy hh:mm:ss 'GMT'", locale);
+ dateFormats[1] =
+ new SimpleDateFormat("EEEE, dd-MMM-yy hh:mm:ss 'GMT'", locale);
+ dateFormats[2] = new SimpleDateFormat("EEE MMM d hh:mm:ss yyyy", locale);
dateformats_initialized = true;
}
}