1 // URLConnection.java - Superclass of all communications links between
2 // an application and a URL.
4 /* Copyright (C) 1999, 2000 Free Software Foundation
6 This file is part of libgcj.
8 This software is copyrighted work licensed under the terms of the
9 Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
15 import java
.text
.ParsePosition
;
16 import java
.text
.SimpleDateFormat
;
17 import java
.util
.Date
;
18 import java
.util
.Locale
;
19 import java
.util
.Hashtable
;
21 import java
.util
.StringTokenizer
;
22 import java
.security
.Permission
;
23 import java
.security
.AllPermission
;
24 import gnu
.gcj
.io
.MimeTypes
;
27 * @author Warren Levy <warrenl@cygnus.com>
28 * @date March 5, 1999.
32 * Written using on-line Java Platform 1.2 API Specification, as well
33 * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998).
34 * Status: One guessContentTypeFrom... methods not implemented.
35 * getContent method assumes content type from response; see comment there.
38 public abstract class URLConnection
41 protected boolean doInput
= true;
42 protected boolean doOutput
= false;
43 protected boolean allowUserInteraction
;
44 protected boolean useCaches
;
45 protected long ifModifiedSince
= 0L;
46 protected boolean connected
= false;
47 private static boolean defaultAllowUserInteraction
= false;
48 private static boolean defaultUseCaches
= true;
49 private static FileNameMap fileNameMap
; // Set by the URLConnection subclass.
50 private static ContentHandlerFactory factory
;
51 private static ContentHandler contentHandler
;
52 private static Hashtable handlers
= new Hashtable();
53 private static Locale locale
;
54 private static SimpleDateFormat dateFormat1
, dateFormat2
, dateFormat3
;
55 private static boolean dateformats_initialized
= false;
58 * Creates a URL connection to a given URL. A real connection is not made.
59 * Use #connect to do this.
61 * @param url The Object to create the URL connection to
63 * @see URLConnection:connect
65 protected URLConnection(URL url
)
68 allowUserInteraction
= defaultAllowUserInteraction
;
69 useCaches
= defaultUseCaches
;
73 * Creates a real connection to the object references by the URL given
76 * @exception IOException If an error occurs
78 public abstract void connect() throws IOException
;
81 * Returns ths URL to the object.
89 * Returns the value of the content-length header field
91 public int getContentLength()
93 return getHeaderFieldInt("content-length", -1);
97 * Returns the value of the content-type header field
99 public String
getContentType()
101 return getHeaderField("content-type");
105 * Returns the value of the content-encoding header field
107 public String
getContentEncoding()
109 return getHeaderField("content-encoding");
113 * Returns the value of the expires header field
115 public long getExpiration()
117 return getHeaderFieldDate("expiration", 0L);
121 * Returns the value of the date header field
123 public long getDate()
125 return getHeaderFieldDate("date", 0L);
129 * Returns the value of the last-modified header field
131 public long getLastModified()
133 return getHeaderFieldDate("last-modified", 0L);
137 * Returns the value of the n-th header field
139 * @param num The number of the header field
141 public String
getHeaderField(int num
)
143 // Subclasses for specific protocols override this.
148 * Returns the value of the header filed specified by name
150 * @param name The name of the header field
152 public String
getHeaderField(String name
)
154 // Subclasses for specific protocols override this.
159 * Returns a map of all sent header fields
163 public Map
getHeaderFields()
165 // Subclasses for specific protocols override this.
170 * Returns the value of the header filed name as int.
172 * @param name The name of the header field
173 * @param val The default value
175 * @return Returns the value of the header filed or the default value
176 * if the field is missing or malformed
178 public int getHeaderFieldInt(String name
, int val
)
180 String str
= getHeaderField(name
);
184 val
= Integer
.parseInt(str
);
186 catch (NumberFormatException e
)
188 ; // Do nothing; val is the default.
194 * Returns the value of a header field parsed as date. The result is then
195 * number of milliseconds since January 1st, 1970 GMT.
197 * @param name The name of the header field
198 * @param val The dafault date
200 * @return Returns the date value of the header filed or the default value
201 * if the field is missing or malformed
203 public long getHeaderFieldDate(String name
, long val
)
205 if (! dateformats_initialized
)
206 initializeDateFormats();
207 String str
= getHeaderField(name
);
211 if ((date
= dateFormat1
.parse(str
, new ParsePosition(0))) != null)
212 val
= date
.getTime();
213 else if ((date
= dateFormat2
.parse(str
, new ParsePosition(0))) != null)
214 val
= date
.getTime();
215 else if ((date
= dateFormat3
.parse(str
, new ParsePosition(0))) != null)
216 val
= date
.getTime();
222 * Returns the key of the n-th header field
224 * @param num The number of the header field
226 public String
getHeaderFieldKey(int num
)
228 // Subclasses for specific protocols override this.
233 * Retrieves the content of this URLConnection
235 * @exception IOException If an error occurs
236 * @exception UnknownServiceException If the protocol does not support the
239 public Object
getContent() throws IOException
241 // FIXME: Doc indicates that other criteria should be applied as
242 // heuristics to determine the true content type, e.g. see
243 // guessContentTypeFromName() and guessContentTypeFromStream methods
244 // as well as FileNameMap class & fileNameMap field & get/set methods.
245 String cType
= getContentType();
246 contentHandler
= setContentHandler(cType
);
247 if (contentHandler
== null)
248 return getInputStream();
250 return contentHandler
.getContent(this);
254 * Retrieves the content of this URLConnection
256 * @exception IOException If an error occurs
257 * @exception UnknownServiceException If the protocol does not support the
260 public Object
getContent(Class
[] classes
) throws IOException
262 // FIXME: implement this
263 return getContent ();
267 * Returns a permission object representing the permission necessary to make
268 * the connection represented by this object. This method returns null if no
269 * permission is required to make the connection.
271 * @exception IOException If the computation of the permission requires
272 * network or file I/O and an exception occurs while computing it
274 public Permission
getPermission() throws IOException
276 // Subclasses may override this.
277 return new java
.security
.AllPermission();
281 * Returns the input stream of the URL connection
283 * @exception IOException If an error occurs
284 * @exception UnknownServiceException If the protocol does not support input
286 public InputStream
getInputStream() throws IOException
288 // Subclasses for specific protocols override this.
289 throw new UnknownServiceException("Protocol " + url
.getProtocol() +
290 " does not support input.");
294 * Returns the output stream of the URL connection
296 * @exception IOException If an error occurs
297 * @exception UnknownServiceException If the protocol does not support output
299 public OutputStream
getOutputStream() throws IOException
301 // Subclasses for specific protocols override this.
302 throw new UnknownServiceException("Protocol " + url
.getProtocol() +
303 " does not support output.");
307 * Returns a string representation of the URL connection object
309 public String
toString()
311 return this.getClass().getName() + ":" + url
.toString();
315 * Sets tha value of the doInput field.
317 * @param doinput The new value of the doInput field
319 * @exception IllegalStateException If already connected
321 public void setDoInput(boolean doinput
)
324 throw new IllegalStateException ("Already connected");
330 * Returns the current value of the doInput field
332 public boolean getDoInput()
338 * Sets the value of the doOutput field
340 * @param dooutput The new value of the doOutput field
342 * @exception IllegalStateException If already connected
344 public void setDoOutput(boolean dooutput
)
347 throw new IllegalStateException ("Already connected");
353 * Returns the current value of the doOutput field
355 public boolean getDoOutput()
361 * Sets a new value to the allowUserInteraction field
363 * @param allowed The new value
365 * @exception IllegalStateException If already connected
367 public void setAllowUserInteraction(boolean allowed
)
370 throw new IllegalStateException ("Already connected");
372 allowUserInteraction
= allowed
;
376 * Returns the current value of the allowUserInteraction field
378 public boolean getAllowUserInteraction()
380 return allowUserInteraction
;
384 * Sets the default value if the allowUserInteraction field
386 * @param allowed The new default value
388 public static void setDefaultAllowUserInteraction(boolean allowed
)
390 defaultAllowUserInteraction
= allowed
;
394 * Returns the default value of the allowUserInteraction field
396 public static boolean getDefaultAllowUserInteraction()
398 return defaultAllowUserInteraction
;
402 * Sets a new value to the useCaches field
404 * @param usecaches The new value
406 * @exception IllegalStateException If already connected
408 public void setUseCaches(boolean usecaches
)
411 throw new IllegalStateException ("Already connected");
413 useCaches
= usecaches
;
417 * The current value of the useCaches field
419 public boolean getUseCaches()
425 * Sets the value of the ifModifiedSince field
427 * @param ifmodifiedsince The new value in milliseconds
428 * since January 1, 1970 GMT
430 * @exception IllegalStateException If already connected
432 public void setIfModifiedSince(long ifmodifiedsince
)
435 throw new IllegalStateException ("Already connected");
437 ifModifiedSince
= ifmodifiedsince
;
441 * Returns the current value of the ifModifiedSince field
443 public long getIfModifiedSince()
445 return ifModifiedSince
;
449 * Returns the default value of the useCaches field
451 public boolean getDefaultUseCaches()
453 return defaultUseCaches
;
457 * Sets the default value of the useCaches field
459 * @param defaultusecaches The new default value
461 public void setDefaultUseCaches(boolean defaultusecaches
)
463 defaultUseCaches
= defaultusecaches
;
467 * Sets a property specified by key to value.
469 * @param key Key of the property to set
470 * @param value Value of the Property to set
472 * @exception IllegalStateException If already connected
473 * @exception NullPointerException If key is null
475 * @see URLConnection:getRequestProperty(String key)
476 * @see URLConnection:addRequestProperty(String key, String value)
478 public void setRequestProperty(String key
, String value
)
481 throw new IllegalStateException ("Already connected");
483 // Do nothing unless overridden by subclasses that support setting
484 // header fields in the request.
488 * Sets a property specified by key to value. If the property key already
489 * is assigned to a value it does nothing.
491 * @param key Key of the property to add
492 * @param value Value of the Property to add
494 * @exception IllegalStateException If already connected
495 * @exception NullPointerException If key is null
497 * @see URLConnection:getRequestProperty(String key)
498 * @see URLConnection:setRequestProperty(String key, String value)
502 public void addRequestProperty(String key
, String value
)
505 throw new IllegalStateException ("Already connected");
507 if (getRequestProperty (key
) == null)
509 setRequestProperty (key
, value
);
514 * Returns a property value specified by key.
516 * @param key Key of the property to return
518 * @exception IllegalStateException If already connected
520 * @see URLConnection:setRequestProperty(String key, String value)
521 * @see URLConnection:addRequestProperty(String key, String value)
523 * @return Value of the property.
525 public String
getRequestProperty(String key
)
528 throw new IllegalStateException ("Already connected");
530 // Overridden by subclasses that support reading header fields from the
536 * Returns a map that contains all properties of the request
538 * @exception IllegalStateException If already connected
540 * @return The map of properties
542 public Map
getRequestProperties()
544 // Overridden by subclasses that support reading header fields from the
550 * Defines a default request property
552 * @param key The key of the property
553 * @param value The value of the property
555 * @deprecated 1.3 The method setRequestProperty should be used instead
557 * @see URLConnection:setRequestProperty
559 public static void setDefaultRequestProperty(String key
, String value
)
561 // Do nothing unless overridden by subclasses that support setting
562 // default request properties.
566 * Returns the value of a default request property
568 * @param key The key of the default property
570 * @return The value of the default property or null if not available
572 * @deprecated 1.3 The method getRequestProperty should be used instead
574 * @see URLConnection:getRequestProperty
576 public static String
getDefaultRequestProperty(String key
)
578 // Overridden by subclasses that support default request properties.
583 * Sets a ContentHandlerFactory
585 * @param fac The ContentHandlerFactory
587 * @exception Error If the factory has already been defined
588 * @exception SecurityException If a security manager exists and its
589 * checkSetFactory method doesn't allow the operation
591 public static void setContentHandlerFactory(ContentHandlerFactory fac
)
594 throw new Error("ContentHandlerFactory already set");
596 // Throw an exception if an extant security mgr precludes
597 // setting the factory.
598 SecurityManager s
= System
.getSecurityManager();
605 * Tries to determine the content type of an object, based on the
606 * specified file name
608 * @param fname The filename to guess the content type from
610 * @specnote public since JDK 1.4
612 public static String
guessContentTypeFromName(String fname
)
614 int dot
= fname
.lastIndexOf (".");
618 if (dot
== fname
.length())
619 return ("application/octet-stream");
621 fname
= fname
.substring (dot
+ 1);
624 String type
= MimeTypes
.getMimeTypeFromExtension (fname
);
627 return("application/octet-stream");
633 * Tries to guess the content type of an object, based on the characters
634 * at the beginning of then input stream
636 * @param is The input stream to guess from
638 * @exception IOException If an error occurs
640 public static String
guessContentTypeFromStream(InputStream is
)
644 // FIXME: Implement this. Use system mimetype informations (like "file").
650 * Returns a filename map (a mimetable)
654 public static FileNameMap
getFileNameMap()
662 * @param map The new FileNameMap
664 * @exception SecurityException If a security manager exists and its
665 * checkSetFactory method doesn't allow the operation
669 public static void setFileNameMap(FileNameMap map
)
671 // Throw an exception if an extant security mgr precludes
672 // setting the factory.
673 SecurityManager s
= System
.getSecurityManager();
680 private ContentHandler
setContentHandler(String contentType
)
682 ContentHandler handler
;
684 // No content type so just handle it as the default.
685 if (contentType
== null || contentType
== "")
688 // See if a handler has been cached for this content type.
689 // For efficiency, if a content type has been searched for but not
690 // found, it will be in the hash table but as the contentType String
691 // instead of a ContentHandler.
692 if ((handler
= (ContentHandler
) handlers
.get(contentType
)) != null)
693 if (handler
instanceof ContentHandler
)
698 // If a non-default factory has been set, use it to find the content type.
700 handler
= factory
.createContentHandler(contentType
);
702 // Non-default factory may have returned null or a factory wasn't set.
703 // Use the default search algorithm to find a handler for this content type.
706 // Get the list of packages to check and append our default handler
707 // to it, along with the JDK specified default as a last resort.
708 // Except in very unusual environments the JDK specified one shouldn't
709 // ever be needed (or available).
710 String propVal
= System
.getProperty("java.content.handler.pkgs");
711 propVal
= (propVal
== null) ?
"" : (propVal
+ "|");
712 propVal
= propVal
+ "gnu.gcj.content|sun.net.www.content";
714 // Replace the '/' character in the content type with '.' and
715 // all other non-alphabetic, non-numeric characters with '_'.
716 StringTokenizer pkgPrefix
= new StringTokenizer(propVal
, "|");
717 char[] cArray
= contentType
.toCharArray();
718 for (int i
= 0; i
< cArray
.length
; i
++)
720 if (cArray
[i
] == '/')
722 else if (! ((cArray
[i
] >= 'A' && cArray
[i
] <= 'Z') ||
723 (cArray
[i
] >= 'a' && cArray
[i
] <= 'z') ||
724 (cArray
[i
] >= '0' && cArray
[i
] <= '9')))
727 String contentClass
= new String(cArray
);
729 // See if a class of this content type exists in any of the packages.
732 String facName
= pkgPrefix
.nextToken() + "." + contentClass
;
736 (ContentHandler
) Class
.forName(facName
).newInstance();
740 // Can't instantiate; handler still null, go on to next element.
742 } while ((handler
== null ||
743 ! (handler
instanceof ContentHandler
)) &&
744 pkgPrefix
.hasMoreTokens());
747 // Update the hashtable with the new content handler.
748 if (handler
!= null && handler
instanceof ContentHandler
)
750 handlers
.put(contentType
, handler
);
754 // For efficiency on subsequent searches, put a dummy entry in the hash
755 // table for content types that don't have a non-default ContentHandler.
756 handlers
.put(contentType
, contentType
);
760 // We don't put these in a static initializer, because it creates problems
761 // with initializer co-dependency: SimpleDateFormat's constructors eventually
762 // depend on URLConnection (via the java.text.*Symbols classes).
763 private synchronized void initializeDateFormats()
765 if (dateformats_initialized
)
767 locale
= new Locale("En", "Us", "Unix");
768 dateFormat1
= new SimpleDateFormat("EEE, dd MMM yyyy hh:mm:ss 'GMT'",
770 dateFormat2
= new SimpleDateFormat("EEEE, dd-MMM-yy hh:mm:ss 'GMT'",
772 dateFormat3
= new SimpleDateFormat("EEE MMM d hh:mm:ss yyyy", locale
);
773 dateformats_initialized
= true;