1 /* URLConnection.java -- Abstract superclass for reading from URL's
2 Copyright (C) 1998, 2002 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.OutputStream;
44 import java.security.Permission;
45 import java.security.AllPermission;
46 import java.text.ParsePosition;
47 import java.text.SimpleDateFormat;
48 import java.util.Date;
49 import java.util.Locale;
50 import java.util.Hashtable;
52 import java.util.StringTokenizer;
53 import gnu.gcj.io.MimeTypes;
56 * Written using on-line Java Platform 1.2 API Specification, as well
57 * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998).
58 * Status: One guessContentTypeFrom... methods not implemented.
59 * getContent method assumes content type from response; see comment there.
63 * This class models a connection that retrieves the information pointed
64 * to by a URL object. This is typically a connection to a remote node
65 * on the network, but could be a simple disk read.
67 * A URLConnection object is normally created by calling the openConnection()
68 * method of a URL object. This method is somewhat misnamed because it does
69 * not actually open the connection. Instead, it return an unconnected
70 * instance of this object. The caller then has the opportunity to set
71 * various connection options prior to calling the actual connect() method.
73 * After the connection has been opened, there are a number of methods in
74 * this class that access various attributes of the data, typically
75 * represented by headers sent in advance of the actual data itself.
77 * Also of note are the getInputStream and getContent() methods which allow
78 * the caller to retrieve the actual data from the connection. Note that
79 * for some types of connections, writing is also allowed. The setDoOutput()
80 * method must be called prior to connecing in order to enable this, then
81 * the getOutputStream method called after the connection in order to
82 * obtain a stream to write the output to.
84 * The getContent() method is of particular note. This method returns an
85 * Object that encapsulates the data returned. There is no way do determine
86 * the type of object that will be returned in advance. This is determined
87 * by the actual content handlers as described in the description of that
90 * @author Aaron M. Renn <arenn@urbanophile.com>
91 * @author Warren Levy <warrenl@cygnus.com>
93 public abstract class URLConnection
96 * This is an object that maps filenames to MIME types. The interface
97 * to do this is implemented by this class, so just create an empty
98 * instance and store it here.
100 private static FileNameMap fileNameMap;
103 * This is the ContentHandlerFactory set by the caller, if any
105 private static ContentHandlerFactory factory;
108 * This is the default value that will be used to determine whether or
109 * not user interaction should be allowed.
111 private static boolean defaultAllowUserInteraction = false;
114 * This is the default flag indicating whether or not to use caches to
115 * store the data returned from a server
117 private static boolean defaultUseCaches = true;
120 * This variable determines whether or not interaction is allowed with
121 * the user. For example, to prompt for a username and password.
123 protected boolean allowUserInteraction;
126 * Indicates whether or not a connection has been established to the
127 * destination specified in the URL
129 protected boolean connected = false;
132 * Indicates whether or not input can be read from this URL
134 protected boolean doInput = true;
137 * Indicates whether or not output can be sent to this URL
139 protected boolean doOutput = false;
142 * If this flag is set, the protocol is allowed to cache data whenever
143 * it can (caching is not guaranteed). If it is not set, the protocol
144 * must a get a fresh copy of the data.
146 * This field is set by the setUseCaches method and returned by the
147 * getUseCaches method.
149 * Its default value is that determined by the last invocation of
150 * setDefaultUseCaches
152 protected boolean useCaches;
155 * If this value is non-zero, then the connection will only attempt to
156 * fetch the document pointed to by the URL if the document has been
157 * modified more recently than the date set in this variable. That date
158 * should be specified as the number of seconds since 1/1/1970 GMT.
160 protected long ifModifiedSince = 0L;
163 * This is the URL associated with this connection
167 private static ContentHandler contentHandler;
168 private static Hashtable handlers = new Hashtable();
169 private static Locale locale;
170 private static SimpleDateFormat dateFormat1, dateFormat2, dateFormat3;
171 private static boolean dateformats_initialized = false;
174 * Creates a URL connection to a given URL. A real connection is not made.
175 * Use #connect to do this.
177 * @param url The Object to create the URL connection to
179 * @see URLConnection#connect()
181 protected URLConnection(URL url)
183 // Set up all our instance variables
185 allowUserInteraction = defaultAllowUserInteraction;
186 useCaches = defaultUseCaches;
190 * Establishes the actual connection to the URL associated with this
193 public abstract void connect() throws IOException;
196 * Returns the URL object associated with this connection
198 * @return The URL for this connection.
206 * Returns the value of the content-length header field or -1 if the value
207 * is not known or not present.
209 * @return The content-length field
211 public int getContentLength()
213 return getHeaderFieldInt("content-length", -1);
217 * Returns the the content-type of the data pointed to by the URL. This
218 * method first tries looking for a content-type header. If that is not
219 * present, it attempts to use the file name to determine the content's
220 * MIME type. If that is unsuccessful, the method returns null. The caller
221 * may then still attempt to determine the MIME type by a call to
222 * guessContentTypeFromStream()
224 * @return The content MIME type
226 public String getContentType()
228 return getHeaderField("content-type");
232 * Returns the value of the content-encoding field or null if it is not
233 * known or not present.
235 * @return The content-encoding field
237 public String getContentEncoding()
239 return getHeaderField("content-encoding");
243 * Returns the value of the expires header or 0 if not known or present.
244 * If populated, the return value is number of seconds since midnight
247 * @return The expiration time.
249 public long getExpiration()
251 return getHeaderFieldDate("expires", 0L);
255 * Returns the date of the document pointed to by the URL as reported in
256 * the date field of the header or 0 if the value is not present or not
257 * known. If populated, the return value is number of seconds since
258 * midnight on 1/1/1970 GMT.
260 * @return The document date
262 public long getDate()
264 return getHeaderFieldDate("date", 0L);
268 * Returns the value of the last-modified header field or 0 if not known known
269 * or not present. If populated, the return value is the number of seconds
270 * since midnight on 1/1/1970.
272 * @return The last modified time
274 public long getLastModified()
276 return getHeaderFieldDate("last-modified", 0L);
280 * Return a String representing the header value at the specified index.
281 * This allows the caller to walk the list of header fields. The analogous
282 * getHeaderFieldKey(int) method allows access to the corresponding key
283 * for this header field
285 * @param index The index into the header field list to retrieve the value for
287 * @return The header value or null if index is past the end of the headers
289 public String getHeaderField(int index)
291 // Subclasses for specific protocols override this.
296 * Returns a String representing the value of the header field having
297 * the named key. Returns null if the header field does not exist.
299 * @param The key of the header field
301 * @return The value of the header field as a String
303 public String getHeaderField(String name)
305 // Subclasses for specific protocols override this.
310 * Returns a map of all sent header fields
314 public Map getHeaderFields()
316 // Subclasses for specific protocols override this.
321 * Returns the value of the named header field as an int. If the field
322 * is not present or cannot be parsed as an integer, the default value
325 * @param name The name of the header field
326 * @param val The default value
328 * @return The value of the header field or the default value if the field
329 * is missing or malformed
331 public int getHeaderFieldInt(String name, int val)
333 String str = getHeaderField(name);
337 val = Integer.parseInt(str);
339 catch (NumberFormatException e)
341 ; // Do nothing; val is the default.
347 * Returns the value of the named header field as a date. This date will
348 * be the number of seconds since midnight 1/1/1970 GMT or the default
349 * value if the field is not present or cannot be converted to a date.
351 * @param name The name of the header field
352 * @param val The dafault date
354 * @return Returns the date value of the header filed or the default value
355 * if the field is missing or malformed
357 public long getHeaderFieldDate(String name, long val)
359 if (! dateformats_initialized)
360 initializeDateFormats();
361 String str = getHeaderField(name);
365 if ((date = dateFormat1.parse(str, new ParsePosition(0))) != null)
366 val = date.getTime();
367 else if ((date = dateFormat2.parse(str, new ParsePosition(0))) != null)
368 val = date.getTime();
369 else if ((date = dateFormat3.parse(str, new ParsePosition(0))) != null)
370 val = date.getTime();
376 * Returns a String representing the header key at the specified index.
377 * This allows the caller to walk the list of header fields. The analogous
378 * getHeaderField(int) method allows access to the corresponding value for
381 * @param index The index into the header field list to retrieve the key for.
383 * @return The header field key or null if index is past the end
386 public String getHeaderFieldKey(int index)
388 // Subclasses for specific protocols override this.
393 * This method returns the content of the document pointed to by the URL
394 * as an Object. The type of object depends on the MIME type of the
395 * object and particular content hander loaded. Most text type content
396 * handlers will return a subclass of InputStream. Images usually return
397 * a class that implements ImageProducer. There is not guarantee what
398 * type of object will be returned, however.
400 * This class first determines the MIME type of the content, then creates
401 * a ContentHandler object to process the input. If the ContentHandlerFactory
402 * is set, then that object is called to load a content handler, otherwise
403 * a class called gnu.java.net.content.<content_type> is tried.
404 * The default class will also be used if the content handler factory returns
405 * a null content handler.
407 * @exception IOException If an error occurs
408 * @exception UnknownServiceException If the protocol does not support the
411 public Object getContent() throws IOException
413 // FIXME: Doc indicates that other criteria should be applied as
414 // heuristics to determine the true content type, e.g. see
415 // guessContentTypeFromName() and guessContentTypeFromStream methods
416 // as well as FileNameMap class & fileNameMap field & get/set methods.
417 String cType = getContentType();
418 contentHandler = setContentHandler(cType);
419 if (contentHandler == null)
420 return getInputStream();
422 return contentHandler.getContent(this);
426 * Retrieves the content of this URLConnection
428 * @exception IOException If an error occurs
429 * @exception UnknownServiceException If the protocol does not support the
432 public Object getContent(Class[] classes) throws IOException
434 // FIXME: implement this
435 return getContent ();
439 * This method returns a <code>Permission</code> object representing the
440 * permissions required to access this URL. This method returns
441 * <code>java.security.AllPermission</code> by default. Subclasses should
442 * override it to return a more specific permission. For example, an
443 * HTTP URL should return an instance of <code>SocketPermission</code>
444 * for the appropriate host and port.
446 * Note that because of items such as HTTP redirects, the permission
447 * object returned might be different before and after connecting.
449 * @return A Permission object
451 * @exception IOException If the computation of the permission requires
452 * network or file I/O and an exception occurs while computing it
454 public Permission getPermission() throws IOException
456 // Subclasses may override this.
457 return new java.security.AllPermission();
461 * Returns an InputStream for this connection. As this default
462 * implementation returns null, subclasses should override this method
464 * @return An InputStream for this connection
466 * @exception IOException If an error occurs
467 * @exception UnknownServiceException If the protocol does not support input
469 public InputStream getInputStream() throws IOException
471 // Subclasses for specific protocols override this.
472 throw new UnknownServiceException("Protocol " + url.getProtocol() +
473 " does not support input.");
477 * Returns an OutputStream for this connection. As this default
478 * implementation returns null, subclasses should override this method
480 * @return An OutputStream for this connection
482 * @exception IOException If an error occurs
483 * @exception UnknownServiceException If the protocol does not support output
485 public OutputStream getOutputStream() throws IOException
487 // Subclasses for specific protocols override this.
488 throw new UnknownServiceException("Protocol " + url.getProtocol() +
489 " does not support output.");
493 * The methods prints the value of this object as a String by calling the
494 * toString() method of its associated URL. Overrides Object.toString()
496 * @return A String representation of this object
498 public String toString()
500 return this.getClass().getName() + ":" + url.toString();
504 * Returns the value of a flag indicating whether or not input is going
505 * to be done for this connection. This default to true unless the
506 * doOutput flag is set to false, in which case this defaults to false.
508 * @param doinput The new value of the doInput field
510 * @exception IllegalStateException If already connected
512 public void setDoInput(boolean input)
515 throw new IllegalStateException ("Already connected");
521 * Returns the value of a flag indicating whether or not input is going
522 * to be done for this connection. This default to true unless the
523 * doOutput flag is set to false, in which case this defaults to false.
525 * @return true if input is to be done, false otherwise
527 public boolean getDoInput()
533 * Returns a boolean flag indicating whether or not output will be done
534 * on this connection. The default value is false, so this method can
535 * be used to override the default
537 * @param output ture if output is to be done, false otherwise
539 * @exception IllegalStateException If already connected
541 public void setDoOutput(boolean output)
544 throw new IllegalStateException ("Already connected");
550 * Returns a boolean flag indicating whether or not output will be done
551 * on this connection. This defaults to false.
553 * @return true if output is to be done, false otherwise
555 public boolean getDoOutput()
561 * Sets a boolean flag indicating whether or not user interaction is
562 * allowed for this connection. (For example, in order to prompt for
563 * username and password info.
565 * @param allow true if user interaction should be allowed, false otherwise.
567 * @exception IllegalStateException If already connected
569 public void setAllowUserInteraction(boolean allow)
571 allowUserInteraction = allow;
575 * Returns a boolean flag indicating whether or not user interaction is
576 * allowed for this connection. (For example, in order to prompt for
577 * username and password info.
579 * @return true if user interaction is allowed, false otherwise
581 public boolean getAllowUserInteraction()
583 return allowUserInteraction;
587 * Sets the default flag for whether or not interaction with a user
588 * is allowed. This will be used for all connections unless overridden
590 * @param allow true to allow user interaction, false otherwise
592 public static void setDefaultAllowUserInteraction(boolean allow)
594 defaultAllowUserInteraction = allow;
598 * Returns the default flag for whether or not interaction with a user
599 * is allowed. This will be used for all connections unless overridden
601 * @return true if user interaction is allowed, false otherwise
603 public static boolean getDefaultAllowUserInteraction()
605 return defaultAllowUserInteraction;
609 * Sets a boolean flag indicating whether or not caching will be used
610 * (if possible) to store data downloaded via the connection.
612 * @param usecaches The new value
614 * @exception IllegalStateException If already connected
616 public void setUseCaches(boolean usecaches)
619 throw new IllegalStateException ("Already connected");
621 useCaches = usecaches;
625 * Returns a boolean flag indicating whether or not caching will be used
626 * (if possible) to store data downloaded via the connection.
628 * @return true if caching should be used if possible, false otherwise
630 public boolean getUseCaches()
636 * Sets the ifModified since instance variable. If this value is non
637 * zero and the underlying protocol supports it, the actual document will
638 * not be fetched unless it has been modified since this time. The value
639 * passed should be 0 if this feature is to be disabled or the time expressed
640 * as the number of seconds since midnight 1/1/1970 GMT otherwise.
642 * @param ifmodifiedsince The new value in milliseconds
643 * since January 1, 1970 GMT
645 * @exception IllegalStateException If already connected
647 public void setIfModifiedSince(long ifmodifiedsince)
650 throw new IllegalStateException ("Already connected");
652 ifModifiedSince = ifmodifiedsince;
656 * Returns the ifModified since instance variable. If this value is non
657 * zero and the underlying protocol supports it, the actual document will
658 * not be fetched unless it has been modified since this time. The value
659 * returned will be 0 if this feature is disabled or the time expressed
660 * as the number of seconds since midnight 1/1/1970 GMT otherwise
662 * @return The ifModifiedSince value
664 public long getIfModifiedSince()
666 return ifModifiedSince;
670 * Returns the default value of the useCaches field
672 public boolean getDefaultUseCaches()
674 return defaultUseCaches;
678 * Sets the default value used to determine whether or not caching
679 * of documents will be done when possible.
681 * @param use true to use caches if possible by default, false otherwise
683 public void setDefaultUseCaches(boolean defaultusecaches)
685 defaultUseCaches = defaultusecaches;
689 * Returns the default value used to determine whether or not caching
690 * of documents will be done when possible.
692 * @param key Key of the property to set
693 * @param value Value of the Property to set
695 * @exception IllegalStateException If already connected
696 * @exception NullPointerException If key is null
698 * @see URLConnection#getRequestProperty(String key)
699 * @see URLConnection#addRequestProperty(String key, String value)
701 public void setRequestProperty(String key, String value)
704 throw new IllegalStateException ("Already connected");
706 // Do nothing unless overridden by subclasses that support setting
707 // header fields in the request.
711 * Sets the value of the named request property
713 * @param key Key of the property to add
714 * @param value Value of the Property to add
716 * @exception IllegalStateException If already connected
717 * @exception NullPointerException If key is null
719 * @see URLConnection#getRequestProperty(String key)
720 * @see URLConnection#setRequestProperty(String key, String value)
724 public void addRequestProperty(String key, String value)
727 throw new IllegalStateException ("Already connected");
729 if (getRequestProperty (key) == null)
731 setRequestProperty (key, value);
736 * Returns the value of the named request property.
738 * @param key The name of the property
740 * @return Value of the property
742 * @exception IllegalStateException If already connected
744 * @see URLConnection#setRequestProperty(String key, String value)
745 * @see URLConnection#addRequestProperty(String key, String value)
747 public String getRequestProperty(String key)
750 throw new IllegalStateException ("Already connected");
752 // Overridden by subclasses that support reading header fields from the
758 * Returns an unmodifiable Map containing the request properties.
760 * @return The map of properties
762 * @exception IllegalStateException If already connected
766 public Map getRequestProperties()
768 // Overridden by subclasses that support reading header fields from the
774 * Sets the default value of a request property. This will be used
775 * for all connections unless the value of the property is manually
778 * @param key The request property name the default is being set for
779 * @param value The value to set the default to
781 * @deprecated 1.3 The method setRequestProperty should be used instead
783 * @see URLConnectionr#setRequestProperty(String key, String value)
785 public static void setDefaultRequestProperty(String key, String value)
787 // Do nothing unless overridden by subclasses that support setting
788 // default request properties.
792 * Returns the default value of a request property. This will be used
793 * for all connections unless the value of the property is manually
796 * @param key The request property to return the default value of
798 * @return The value of the default property or null if not available
800 * @deprecated 1.3 The method getRequestProperty should be used instead
802 * @see URLConnection#getRequestProperty(String key)
804 public static String getDefaultRequestProperty(String key)
806 // Overridden by subclasses that support default request properties.
811 * Set's the ContentHandlerFactory for an application. This can be called
812 * once and only once. If it is called again, then an Error is thrown.
813 * Unlike for other set factory methods, this one does not do a security
814 * check prior to setting the factory.
816 * @param factory The ContentHandlerFactory for this application
818 * @exception Error If the factory has already been defined
819 * @exception SecurityException If a security manager exists and its
820 * checkSetFactory method doesn't allow the operation
822 public static synchronized void setContentHandlerFactory
823 (ContentHandlerFactory fac)
826 throw new Error("ContentHandlerFactory already set");
828 // Throw an exception if an extant security mgr precludes
829 // setting the factory.
830 SecurityManager s = System.getSecurityManager();
838 * Returns the MIME type of a file based on the name of the file. This
839 * works by searching for the file's extension in a list of file extensions
840 * and returning the MIME type associated with it. If no type is found,
841 * then a MIME type of "application/octet-stream" will be returned.
843 * @param filename The filename to determine the MIME type for
845 * @return The MIME type String
847 * @specnote public since JDK 1.4
849 public static String guessContentTypeFromName(String filename)
851 int dot = filename.lastIndexOf (".");
855 if (dot == filename.length())
856 return ("application/octet-stream");
858 filename = filename.substring (dot + 1);
861 String type = MimeTypes.getMimeTypeFromExtension (filename);
864 return("application/octet-stream");
870 * Returns the MIME type of a stream based on the first few characters
871 * at the beginning of the stream. This routine can be used to determine
872 * the MIME type if a server is believed to be returning an incorrect
873 * MIME type. This method returns "application/octet-stream" if it
874 * cannot determine the MIME type.
876 * NOTE: Overriding MIME types sent from the server can be obnoxious
877 * to user's. See Internet Exploder 4 if you don't believe me.
879 * @param is The InputStream to determine the MIME type from
881 * @return The MIME type
883 * @exception IOException If an error occurs
885 public static String guessContentTypeFromStream(InputStream is)
889 // FIXME: Implement this. Use system mimetype informations (like "file").
895 * This method returns the <code>FileNameMap</code> object being used
896 * to decode MIME types by file extension.
898 * @return The <code>FileNameMap</code>.
902 public static FileNameMap getFileNameMap()
908 * This method set the <code>FileNameMap</code> object being used
909 * to decode MIME types by file extension.
911 * @param map The <code>FileNameMap</code>.
913 * @exception SecurityException If a security manager exists and its
914 * checkSetFactory method doesn't allow the operation
918 public static void setFileNameMap(FileNameMap map)
920 // Throw an exception if an extant security mgr precludes
921 // setting the factory.
922 SecurityManager s = System.getSecurityManager();
929 private ContentHandler setContentHandler(String contentType)
931 ContentHandler handler;
933 // No content type so just handle it as the default.
934 if (contentType == null || contentType == "")
937 // See if a handler has been cached for this content type.
938 // For efficiency, if a content type has been searched for but not
939 // found, it will be in the hash table but as the contentType String
940 // instead of a ContentHandler.
941 if ((handler = (ContentHandler) handlers.get(contentType)) != null)
942 if (handler instanceof ContentHandler)
947 // If a non-default factory has been set, use it to find the content type.
949 handler = factory.createContentHandler(contentType);
951 // Non-default factory may have returned null or a factory wasn't set.
952 // Use the default search algorithm to find a handler for this content type.
955 // Get the list of packages to check and append our default handler
956 // to it, along with the JDK specified default as a last resort.
957 // Except in very unusual environments the JDK specified one shouldn't
958 // ever be needed (or available).
959 String propVal = System.getProperty("java.content.handler.pkgs");
960 propVal = (propVal == null) ? "" : (propVal + "|");
961 propVal = propVal + "gnu.gcj.content|sun.net.www.content";
963 // Replace the '/' character in the content type with '.' and
964 // all other non-alphabetic, non-numeric characters with '_'.
965 StringTokenizer pkgPrefix = new StringTokenizer(propVal, "|");
966 char[] cArray = contentType.toCharArray();
967 for (int i = 0; i < cArray.length; i++)
969 if (cArray[i] == '/')
971 else if (! ((cArray[i] >= 'A' && cArray[i] <= 'Z') ||
972 (cArray[i] >= 'a' && cArray[i] <= 'z') ||
973 (cArray[i] >= '0' && cArray[i] <= '9')))
976 String contentClass = new String(cArray);
978 // See if a class of this content type exists in any of the packages.
981 String facName = pkgPrefix.nextToken() + "." + contentClass;
985 (ContentHandler) Class.forName(facName).newInstance();
989 // Can't instantiate; handler still null, go on to next element.
991 } while ((handler == null ||
992 ! (handler instanceof ContentHandler)) &&
993 pkgPrefix.hasMoreTokens());
996 // Update the hashtable with the new content handler.
997 if (handler != null && handler instanceof ContentHandler)
999 handlers.put(contentType, handler);
1003 // For efficiency on subsequent searches, put a dummy entry in the hash
1004 // table for content types that don't have a non-default ContentHandler.
1005 handlers.put(contentType, contentType);
1009 // We don't put these in a static initializer, because it creates problems
1010 // with initializer co-dependency: SimpleDateFormat's constructors eventually
1011 // depend on URLConnection (via the java.text.*Symbols classes).
1012 private synchronized void initializeDateFormats()
1014 if (dateformats_initialized)
1016 locale = new Locale("En", "Us", "Unix");
1017 dateFormat1 = new SimpleDateFormat("EEE, dd MMM yyyy hh:mm:ss 'GMT'",
1019 dateFormat2 = new SimpleDateFormat("EEEE, dd-MMM-yy hh:mm:ss 'GMT'",
1021 dateFormat3 = new SimpleDateFormat("EEE MMM d hh:mm:ss yyyy", locale);
1022 dateformats_initialized = true;
1024 } // class URLConnection