Source for java.net.URLConnection

   1: /* URLConnection.java -- Abstract superclass for reading from URL's
   2:    Copyright (C) 1998, 2002, 2003, 2004, 2006 Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   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)
   9: any later version.
  10: 
  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.
  15: 
  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., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  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
  24: combination.
  25: 
  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. */
  37: 
  38: 
  39: package java.net;
  40: 
  41: import java.io.IOException;
  42: import java.io.InputStream;
  43: import java.io.OutputStream;
  44: import java.security.AllPermission;
  45: import java.security.Permission;
  46: import java.text.ParsePosition;
  47: import java.text.SimpleDateFormat;
  48: import java.util.Collections;
  49: import java.util.Date;
  50: import java.util.Hashtable;
  51: import java.util.Locale;
  52: import java.util.Map;
  53: import java.util.StringTokenizer;
  54: import gnu.gcj.io.MimeTypes;
  55: 
  56: 
  57: /**
  58:  * Written using on-line Java Platform 1.2 API Specification, as well
  59:  * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998).
  60:  * Status:  One guessContentTypeFrom... methods not implemented.
  61:  *    getContent method assumes content type from response; see comment there.
  62:  */
  63: /**
  64:  * This class models a connection that retrieves the information pointed
  65:  * to by a URL object.  This is typically a connection to a remote node
  66:  * on the network, but could be a simple disk read.
  67:  * <p>
  68:  * A URLConnection object is normally created by calling the openConnection()
  69:  * method of a URL object.  This method is somewhat misnamed because it does
  70:  * not actually open the connection.  Instead, it return an unconnected
  71:  * instance of this object.  The caller then has the opportunity to set
  72:  * various connection options prior to calling the actual connect() method.
  73:  * <p>
  74:  * After the connection has been opened, there are a number of methods in
  75:  * this class that access various attributes of the data, typically
  76:  * represented by headers sent in advance of the actual data itself.
  77:  * <p>
  78:  * Also of note are the getInputStream and getContent() methods which allow
  79:  * the caller to retrieve the actual data from the connection.  Note that
  80:  * for some types of connections, writing is also allowed.  The setDoOutput()
  81:  * method must be called prior to connecing in order to enable this, then
  82:  * the getOutputStream method called after the connection in order to
  83:  * obtain a stream to write the output to.
  84:  * <p>
  85:  * The getContent() method is of particular note.  This method returns an
  86:  * Object that encapsulates the data returned.  There is no way do determine
  87:  * the type of object that will be returned in advance.  This is determined
  88:  * by the actual content handlers as described in the description of that
  89:  * method.
  90:  *
  91:  * @author Aaron M. Renn (arenn@urbanophile.com)
  92:  * @author Warren Levy (warrenl@cygnus.com)
  93:  */
  94: public abstract class URLConnection
  95: {
  96:   /**
  97:    * This is an object that maps filenames to MIME types.  The interface
  98:    * to do this is implemented by this class, so just create an empty
  99:    * instance and store it here.
 100:    */
 101:   private static FileNameMap fileNameMap;
 102: 
 103:   /**
 104:    * This is the ContentHandlerFactory set by the caller, if any
 105:    */
 106:   private static ContentHandlerFactory factory;
 107: 
 108:   /**
 109:    * This is the default value that will be used to determine whether or
 110:    * not user interaction should be allowed.
 111:    */
 112:   private static boolean defaultAllowUserInteraction;
 113: 
 114:   /**
 115:    * This is the default flag indicating whether or not to use caches to
 116:    * store the data returned from a server
 117:    */
 118:   private static boolean defaultUseCaches = true;
 119: 
 120:   private static ContentHandlerFactory defaultFactory
 121:     = new gnu.java.net.DefaultContentHandlerFactory();
 122: 
 123:   /**
 124:    * This variable determines whether or not interaction is allowed with
 125:    * the user.  For example, to prompt for a username and password.
 126:    */
 127:   protected boolean allowUserInteraction;
 128: 
 129:   /**
 130:    * Indicates whether or not a connection has been established to the
 131:    * destination specified in the URL
 132:    */
 133:   protected boolean connected;
 134: 
 135:   /**
 136:    * Indicates whether or not input can be read from this URL
 137:    */
 138:   protected boolean doInput = true;
 139: 
 140:   /**
 141:    * Indicates whether or not output can be sent to this URL
 142:    */
 143:   protected boolean doOutput;
 144: 
 145:   /**
 146:    * If this flag is set, the protocol is allowed to cache data whenever
 147:    * it can (caching is not guaranteed). If it is not set, the protocol
 148:    * must a get a fresh copy of the data.
 149:    * <p>
 150:    * This field is set by the setUseCaches method and returned by the
 151:    * getUseCaches method.
 152:    *
 153:    * Its default value is that determined by the last invocation of
 154:    * setDefaultUseCaches
 155:    */
 156:   protected boolean useCaches;
 157: 
 158:   /**
 159:    * If this value is non-zero, then the connection will only attempt to
 160:    * fetch the document pointed to by the URL if the document has been
 161:    * modified more recently than the date set in this variable.  That date
 162:    * should be specified as the number of seconds since 1/1/1970 GMT.
 163:    */
 164:   protected long ifModifiedSince;
 165: 
 166:   /**
 167:    * This is the URL associated with this connection
 168:    */
 169:   protected URL url;
 170: 
 171:   private static Hashtable handlers = new Hashtable();
 172:   private static SimpleDateFormat[] dateFormats;
 173:   private static boolean dateformats_initialized;
 174: 
 175:   /* Cached ParsePosition, used when parsing dates. */
 176:   private ParsePosition position;
 177: 
 178:   /**
 179:    * Creates a URL connection to a given URL. A real connection is not made.
 180:    * Use #connect to do this.
 181:    *
 182:    * @param url The Object to create the URL connection to
 183:    *
 184:    * @see URLConnection#connect()
 185:    */
 186:   protected URLConnection(URL url)
 187:   {
 188:     // Set up all our instance variables
 189:     this.url = url;
 190:     allowUserInteraction = defaultAllowUserInteraction;
 191:     useCaches = defaultUseCaches;
 192:   }
 193: 
 194:   /**
 195:    * Establishes the actual connection to the URL associated with this
 196:    * connection object
 197:    *
 198:    * @exception IOException if an error occurs
 199:    */
 200:   public abstract void connect() throws IOException;
 201: 
 202:   /**
 203:    * Returns the URL object associated with this connection
 204:    *
 205:    * @return The URL for this connection.
 206:    */
 207:   public URL getURL()
 208:   {
 209:     return url;
 210:   }
 211: 
 212:   /**
 213:    * Returns the value of the content-length header field or -1 if the value
 214:    * is not known or not present.
 215:    *
 216:    * @return The content-length field
 217:    */
 218:   public int getContentLength()
 219:   {
 220:     return getHeaderFieldInt("content-length", -1);
 221:   }
 222: 
 223:   /**
 224:    * Returns the the content-type of the data pointed to by the URL.  This
 225:    * method first tries looking for a content-type header.  If that is not
 226:    * present, it attempts to use the file name to determine the content's
 227:    * MIME type.  If that is unsuccessful, the method returns null.  The caller
 228:    * may then still attempt to determine the MIME type by a call to
 229:    * guessContentTypeFromStream()
 230:    *
 231:    * @return The content MIME type
 232:    */
 233:   public String getContentType()
 234:   {
 235:     return getHeaderField("content-type");
 236:   }
 237: 
 238:   /**
 239:    * Returns the value of the content-encoding field or null if it is not
 240:    * known or not present.
 241:    *
 242:    * @return The content-encoding field
 243:    */
 244:   public String getContentEncoding()
 245:   {
 246:     return getHeaderField("content-encoding");
 247:   }
 248: 
 249:   /**
 250:    * Returns the value of the expires header or 0 if not known or present.
 251:    * If populated, the return value is number of seconds since midnight
 252:    * on 1/1/1970 GMT.
 253:    *
 254:    * @return The expiration time.
 255:    */
 256:   public long getExpiration()
 257:   {
 258:     return getHeaderFieldDate("expires", 0L);
 259:   }
 260: 
 261:   /**
 262:    * Returns the date of the document pointed to by the URL as reported in
 263:    * the date field of the header or 0 if the value is not present or not
 264:    * known. If populated, the return value is number of seconds since
 265:    * midnight on 1/1/1970 GMT.
 266:    *
 267:    * @return The document date
 268:    */
 269:   public long getDate()
 270:   {
 271:     return getHeaderFieldDate("date", 0L);
 272:   }
 273: 
 274:   /**
 275:    * Returns the value of the last-modified header field or 0 if not known known
 276:    * or not present.  If populated, the return value is the number of seconds
 277:    * since midnight on 1/1/1970.
 278:    *
 279:    * @return The last modified time
 280:    */
 281:   public long getLastModified()
 282:   {
 283:     return getHeaderFieldDate("last-modified", 0L);
 284:   }
 285: 
 286:   /**
 287:    * Return a String representing the header value at the specified index.
 288:    * This allows the caller to walk the list of header fields.  The analogous
 289:    * getHeaderFieldKey(int) method allows access to the corresponding key
 290:    * for this header field
 291:    *
 292:    * @param index The index into the header field list to retrieve the value for
 293:    *
 294:    * @return The header value or null if index is past the end of the headers
 295:    */
 296:   public String getHeaderField(int index)
 297:   {
 298:     // Subclasses for specific protocols override this.
 299:     return null;
 300:   }
 301: 
 302:   /**
 303:    * Returns a String representing the value of the header field having
 304:    * the named key.  Returns null if the header field does not exist.
 305:    *
 306:    * @param name The key of the header field
 307:    *
 308:    * @return The value of the header field as a String
 309:    */
 310:   public String getHeaderField(String name)
 311:   {
 312:     // Subclasses for specific protocols override this.
 313:     return null;
 314:   }
 315: 
 316:   /**
 317:    * Returns a map of all sent header fields
 318:    *
 319:    * @return all header fields
 320:    *
 321:    * @since 1.4
 322:    */
 323:   public Map getHeaderFields()
 324:   {
 325:     // Subclasses for specific protocols override this.
 326:     return Collections.EMPTY_MAP;
 327:   }
 328: 
 329:   /**
 330:    * Returns the value of the named header field as an int.  If the field
 331:    * is not present or cannot be parsed as an integer, the default value
 332:    * will be returned.
 333:    *
 334:    * @param name The header field key to lookup
 335:    * @param defaultValue The defaule value if the header field is not found
 336:    * or can't be parsed.
 337:    *
 338:    * @return The value of the header field or the default value if the field
 339:    * is missing or malformed
 340:    */
 341:   public int getHeaderFieldInt(String name, int defaultValue)
 342:   {
 343:     String value = getHeaderField(name);
 344: 
 345:     if (value == null)
 346:       return defaultValue;
 347: 
 348:     try
 349:       {
 350:     return Integer.parseInt(value);
 351:       }
 352:     catch (NumberFormatException e)
 353:       {
 354:     return defaultValue;
 355:       }
 356:   }
 357: 
 358:   /**
 359:    * Returns the value of the named header field as a date.  This date will
 360:    * be the number of seconds since midnight 1/1/1970 GMT or the default
 361:    * value if the field is not present or cannot be converted to a date.
 362:    *
 363:    * @param name The name of the header field
 364:    * @param defaultValue The default date if the header field is not found
 365:    * or can't be converted.
 366:    *
 367:    * @return Returns the date value of the header filed or the default value
 368:    * if the field is missing or malformed
 369:    */
 370:   public long getHeaderFieldDate(String name, long defaultValue)
 371:   {
 372:     if (! dateformats_initialized)
 373:       initializeDateFormats();
 374: 
 375:     if (position == null)
 376:       position = new ParsePosition(0);
 377: 
 378:     long result = defaultValue;
 379:     String str = getHeaderField(name);
 380: 
 381:     if (str != null)
 382:       {
 383:     for (int i = 0; i < dateFormats.length; i++)
 384:       {
 385:         SimpleDateFormat df = dateFormats[i];
 386:         position.setIndex(0);
 387:         position.setErrorIndex(0);
 388:         Date date = df.parse(str, position);
 389:         if (date != null)
 390:           return date.getTime();
 391:       }
 392:       }
 393: 
 394:     return result;
 395:   }
 396: 
 397:   /**
 398:    * Returns a String representing the header key at the specified index.
 399:    * This allows the caller to walk the list of header fields.  The analogous
 400:    * getHeaderField(int) method allows access to the corresponding value for
 401:    * this tag.
 402:    *
 403:    * @param index The index into the header field list to retrieve the key for.
 404:    *
 405:    * @return The header field key or null if index is past the end
 406:    * of the headers.
 407:    */
 408:   public String getHeaderFieldKey(int index)
 409:   {
 410:     // Subclasses for specific protocols override this.
 411:     return null;
 412:   }
 413: 
 414:   /**
 415:    * This method returns the content of the document pointed to by the
 416:    * URL as an Object.  The type of object depends on the MIME type of
 417:    * the object and particular content hander loaded.  Most text type
 418:    * content handlers will return a subclass of
 419:    * <code>InputStream</code>.  Images usually return a class that
 420:    * implements <code>ImageProducer</code>.  There is not guarantee
 421:    * what type of object will be returned, however.
 422:    *
 423:    * <p>This class first determines the MIME type of the content, then
 424:    * creates a ContentHandler object to process the input.  If the
 425:    * <code>ContentHandlerFactory</code> is set, then that object is
 426:    * called to load a content handler, otherwise a class called
 427:    * gnu.java.net.content.&lt;content_type&gt; is tried.  If this
 428:    * handler does not exist, the method will simple return the
 429:    * <code>InputStream</code> returned by
 430:    * <code>getInputStream()</code>.  Note that the default
 431:    * implementation of <code>getInputStream()</code> throws a
 432:    * <code>UnknownServiceException</code> so subclasses are encouraged
 433:    * to override this method.</p>
 434:    *
 435:    * @return the content
 436:    *
 437:    * @exception IOException If an error with the connection occurs.
 438:    * @exception UnknownServiceException If the protocol does not support the
 439:    * content type at all.
 440:    */
 441:   public Object getContent() throws IOException
 442:   {
 443:     if (!connected)
 444:       connect();
 445: 
 446:     // FIXME: Doc indicates that other criteria should be applied as
 447:     // heuristics to determine the true content type, e.g. see 
 448:     // guessContentTypeFromName() and guessContentTypeFromStream methods
 449:     // as well as FileNameMap class & fileNameMap field & get/set methods.
 450:     String type = getContentType();
 451:     ContentHandler ch = getContentHandler(type);
 452: 
 453:     if (ch != null)
 454:       return ch.getContent(this);
 455: 
 456:     return getInputStream();
 457:   }
 458: 
 459:   /**
 460:    * Retrieves the content of this URLConnection
 461:    *
 462:    * @param classes The allowed classes for the content
 463:    *
 464:    * @return the content
 465:    *
 466:    * @exception IOException If an error occurs
 467:    * @exception UnknownServiceException If the protocol does not support the
 468:    * content type
 469:    */
 470:   public Object getContent(Class[] classes) throws IOException
 471:   {
 472:     // FIXME: implement this
 473:     return getContent();
 474:   }
 475: 
 476:   /**
 477:    * This method returns a <code>Permission</code> object representing the
 478:    * permissions required to access this URL.  This method returns
 479:    * <code>java.security.AllPermission</code> by default.  Subclasses should
 480:    * override it to return a more specific permission.  For example, an
 481:    * HTTP URL should return an instance of <code>SocketPermission</code>
 482:    * for the appropriate host and port.
 483:    * <p>
 484:    * Note that because of items such as HTTP redirects, the permission
 485:    * object returned might be different before and after connecting.
 486:    *
 487:    * @return A Permission object
 488:    *
 489:    * @exception IOException If the computation of the permission requires
 490:    * network or file I/O and an exception occurs while computing it
 491:    */
 492:   public Permission getPermission() throws IOException
 493:   {
 494:     // Subclasses may override this.
 495:     return new AllPermission();
 496:   }
 497: 
 498:   /**
 499:    * Returns an InputStream for this connection.  As this default
 500:    * implementation returns null, subclasses should override this method
 501:    *
 502:    * @return An InputStream for this connection
 503:    *
 504:    * @exception IOException If an error occurs
 505:    * @exception UnknownServiceException If the protocol does not support input
 506:    */
 507:   public InputStream getInputStream() throws IOException
 508:   {
 509:     // Subclasses for specific protocols override this.
 510:     throw new UnknownServiceException("Protocol " + url.getProtocol()
 511:                                       + " does not support input.");
 512:   }
 513: 
 514:   /**
 515:    * Returns an OutputStream for this connection.  As this default
 516:    * implementation returns null, subclasses should override this method
 517:    *
 518:    * @return An OutputStream for this connection
 519:    *
 520:    * @exception IOException If an error occurs
 521:    * @exception UnknownServiceException If the protocol does not support output
 522:    */
 523:   public OutputStream getOutputStream() throws IOException
 524:   {
 525:     // Subclasses for specific protocols override this.
 526:     throw new UnknownServiceException("Protocol " + url.getProtocol()
 527:                                       + " does not support output.");
 528:   }
 529: 
 530:   /**
 531:    * The methods prints the value of this object as a String by calling the
 532:    * toString() method of its associated URL.  Overrides Object.toString()
 533:    *
 534:    * @return A String representation of this object
 535:    */
 536:   public String toString()
 537:   {
 538:     return this.getClass().getName() + ":" + url.toString();
 539:   }
 540: 
 541:   /**
 542:    * Sets the value of a flag indicating whether or not input is going
 543:    * to be done for this connection.  This default to true unless the
 544:    * doOutput flag is set to false, in which case this defaults to false.
 545:    *
 546:    * @param input <code>true</code> if input is to be done,
 547:    * <code>false</code> otherwise
 548:    *
 549:    * @exception IllegalStateException If already connected
 550:    */
 551:   public void setDoInput(boolean input)
 552:   {
 553:     if (connected)
 554:       throw new IllegalStateException("Already connected");
 555: 
 556:     doInput = input;
 557:   }
 558: 
 559:   /**
 560:    * Returns the value of a flag indicating whether or not input is going
 561:    * to be done for this connection.  This default to true unless the
 562:    * doOutput flag is set to false, in which case this defaults to false.
 563:    *
 564:    * @return true if input is to be done, false otherwise
 565:    */
 566:   public boolean getDoInput()
 567:   {
 568:     return doInput;
 569:   }
 570: 
 571:   /**
 572:    * Sets a boolean flag indicating whether or not output will be done
 573:    * on this connection.  The default value is false, so this method can
 574:    * be used to override the default
 575:    *
 576:    * @param output ture if output is to be done, false otherwise
 577:    *
 578:    * @exception IllegalStateException If already connected
 579:    */
 580:   public void setDoOutput(boolean output)
 581:   {
 582:     if (connected)
 583:       throw new IllegalStateException("Already connected");
 584: 
 585:     doOutput = output;
 586:   }
 587: 
 588:   /**
 589:    * Returns a boolean flag indicating whether or not output will be done
 590:    * on this connection.  This defaults to false.
 591:    *
 592:    * @return true if output is to be done, false otherwise
 593:    */
 594:   public boolean getDoOutput()
 595:   {
 596:     return doOutput;
 597:   }
 598: 
 599:   /**
 600:    * Sets a boolean flag indicating whether or not user interaction is
 601:    * allowed for this connection.  (For example, in order to prompt for
 602:    * username and password info.
 603:    *
 604:    * @param allow true if user interaction should be allowed, false otherwise.
 605:    *
 606:    * @exception IllegalStateException If already connected
 607:    */
 608:   public void setAllowUserInteraction(boolean allow)
 609:   {
 610:     allowUserInteraction = allow;
 611:   }
 612: 
 613:   /**
 614:    * Returns a boolean flag indicating whether or not user interaction is
 615:    * allowed for this connection.  (For example, in order to prompt for
 616:    * username and password info.
 617:    *
 618:    * @return true if user interaction is allowed, false otherwise
 619:    */
 620:   public boolean getAllowUserInteraction()
 621:   {
 622:     return allowUserInteraction;
 623:   }
 624: 
 625:   /**
 626:    * Sets the default flag for whether or not interaction with a user
 627:    * is allowed.  This will be used for all connections unless overridden
 628:    *
 629:    * @param allow true to allow user interaction, false otherwise
 630:    */
 631:   public static void setDefaultAllowUserInteraction(boolean allow)
 632:   {
 633:     defaultAllowUserInteraction = allow;
 634:   }
 635: 
 636:   /**
 637:    * Returns the default flag for whether or not interaction with a user
 638:    * is allowed.  This will be used for all connections unless overridden
 639:    *
 640:    * @return true if user interaction is allowed, false otherwise
 641:    */
 642:   public static boolean getDefaultAllowUserInteraction()
 643:   {
 644:     return defaultAllowUserInteraction;
 645:   }
 646: 
 647:   /**
 648:    * Sets a boolean flag indicating whether or not caching will be used
 649:    * (if possible) to store data downloaded via the connection.
 650:    *
 651:    * @param usecaches The new value
 652:    *
 653:    * @exception IllegalStateException If already connected
 654:    */
 655:   public void setUseCaches(boolean usecaches)
 656:   {
 657:     if (connected)
 658:       throw new IllegalStateException("Already connected");
 659: 
 660:     useCaches = usecaches;
 661:   }
 662: 
 663:   /**
 664:    * Returns a boolean flag indicating whether or not caching will be used
 665:    * (if possible) to store data downloaded via the connection.
 666:    *
 667:    * @return true if caching should be used if possible, false otherwise
 668:    */
 669:   public boolean getUseCaches()
 670:   {
 671:     return useCaches;
 672:   }
 673: 
 674:   /**
 675:    * Sets the ifModified since instance variable.  If this value is non
 676:    * zero and the underlying protocol supports it, the actual document will
 677:    * not be fetched unless it has been modified since this time.  The value
 678:    * passed should  be 0 if this feature is to be disabled or the time expressed
 679:    * as the number of seconds since midnight 1/1/1970 GMT otherwise.
 680:    *
 681:    * @param ifmodifiedsince The new value in milliseconds
 682:    * since January 1, 1970 GMT
 683:    *
 684:    * @exception IllegalStateException If already connected
 685:    */
 686:   public void setIfModifiedSince(long ifmodifiedsince)
 687:   {
 688:     if (connected)
 689:       throw new IllegalStateException("Already connected");
 690: 
 691:     ifModifiedSince = ifmodifiedsince;
 692:   }
 693: 
 694:   /**
 695:    * Returns the ifModified since instance variable.  If this value is non
 696:    * zero and the underlying protocol supports it, the actual document will
 697:    * not be fetched unless it has been modified since this time.  The value
 698:    * returned will be 0 if this feature is disabled or the time expressed
 699:    * as the number of seconds since midnight 1/1/1970 GMT otherwise
 700:    *
 701:    * @return The ifModifiedSince value
 702:    */
 703:   public long getIfModifiedSince()
 704:   {
 705:     return ifModifiedSince;
 706:   }
 707: 
 708:   /**
 709:    * Returns the default value used to determine whether or not caching
 710:    * of documents will be done when possible.
 711:    *
 712:    * @return true if caches will be used, false otherwise
 713:    */
 714:   public boolean getDefaultUseCaches()
 715:   {
 716:     return defaultUseCaches;
 717:   }
 718: 
 719:   /**
 720:    * Sets the default value used to determine whether or not caching
 721:    * of documents will be done when possible.
 722:    *
 723:    * @param use true to use caches if possible by default, false otherwise
 724:    */
 725:   public void setDefaultUseCaches(boolean use)
 726:   {
 727:     defaultUseCaches = use;
 728:   }
 729: 
 730:   /**
 731:    * Sets the value of the named request property
 732:    *
 733:    * @param key The name of the property
 734:    * @param value The value of the property
 735:    *
 736:    * @exception IllegalStateException If already connected
 737:    * @exception NullPointerException If key is null
 738:    *
 739:    * @see URLConnection#getRequestProperty(String key)
 740:    * @see URLConnection#addRequestProperty(String key, String value)
 741:    *
 742:    * @since 1.4
 743:    */
 744:   public void setRequestProperty(String key, String value)
 745:   {
 746:     if (connected)
 747:       throw new IllegalStateException("Already connected");
 748: 
 749:     if (key == null)
 750:       throw new NullPointerException("key is null");
 751: 
 752:     // Do nothing unless overridden by subclasses that support setting
 753:     // header fields in the request.
 754:   }
 755: 
 756:   /**
 757:    * Adds a new request property by a key/value pair.
 758:    * This method does not overwrite existing properties with the same key.
 759:    *
 760:    * @param key Key of the property to add
 761:    * @param value Value of the Property to add
 762:    *
 763:    * @exception IllegalStateException If already connected
 764:    * @exception NullPointerException If key is null
 765:    *
 766:    * @see URLConnection#getRequestProperty(String key)
 767:    * @see URLConnection#setRequestProperty(String key, String value)
 768:    *
 769:    * @since 1.4
 770:    */
 771:   public void addRequestProperty(String key, String value)
 772:   {
 773:     if (connected)
 774:       throw new IllegalStateException("Already connected");
 775: 
 776:     if (key == null)
 777:       throw new NullPointerException("key is null");
 778: 
 779:     // Do nothing unless overridden by subclasses that support adding
 780:     // header fields in the request.
 781:   }
 782: 
 783:   /**
 784:    * Returns the value of the named request property.
 785:    *
 786:    * @param key The name of the property
 787:    *
 788:    * @return Value of the property
 789:    *
 790:    * @exception IllegalStateException If already connected
 791:    *
 792:    * @see URLConnection#setRequestProperty(String key, String value)
 793:    * @see URLConnection#addRequestProperty(String key, String value)
 794:    */
 795:   public String getRequestProperty(String key)
 796:   {
 797:     if (connected)
 798:       throw new IllegalStateException("Already connected");
 799: 
 800:     // Overridden by subclasses that support reading header fields from the
 801:     // request.
 802:     return null;
 803:   }
 804: 
 805:   /**
 806:    * Returns an unmodifiable Map containing the request properties.
 807:    *
 808:    * @return The map of properties
 809:    *
 810:    * @exception IllegalStateException If already connected
 811:    *
 812:    * @since 1.4
 813:    */
 814:   public Map getRequestProperties()
 815:   {
 816:     if (connected)
 817:       throw new IllegalStateException("Already connected");
 818: 
 819:     // Overridden by subclasses that support reading header fields from the
 820:     // request.
 821:     return Collections.EMPTY_MAP;
 822:   }
 823: 
 824:   /**
 825:    * Sets the default value of a request property.  This will be used
 826:    * for all connections unless the value of the property is manually
 827:    * overridden.
 828:    *
 829:    * @param key The request property name the default is being set for
 830:    * @param value The value to set the default to
 831:    *
 832:    * @deprecated 1.3 The method setRequestProperty should be used instead.
 833:    * This method does nothing now.
 834:    *
 835:    * @see URLConnection#setRequestProperty(String key, String value)
 836:    */
 837:   public static void setDefaultRequestProperty(String key, String value)
 838:   {
 839:     // This method does nothing since JDK 1.3.
 840:   }
 841: 
 842:   /**
 843:    * Returns the default value of a request property.  This will be used
 844:    * for all connections unless the value of the property is manually
 845:    * overridden.
 846:    *
 847:    * @param key The request property to return the default value of
 848:    *
 849:    * @return The value of the default property or null if not available
 850:    *
 851:    * @deprecated 1.3 The method getRequestProperty should be used instead.
 852:    * This method does nothing now.
 853:    *
 854:    * @see URLConnection#getRequestProperty(String key)
 855:    */
 856:   public static String getDefaultRequestProperty(String key)
 857:   {
 858:     // This method does nothing since JDK 1.3.
 859:     return null;
 860:   }
 861: 
 862:   /**
 863:    * Sets the ContentHandlerFactory for an application.  This can be called
 864:    * once and only once.  If it is called again, then an Error is thrown.
 865:    * Unlike for other set factory methods, this one does not do a security
 866:    * check prior to setting the factory.
 867:    *
 868:    * @param factory The ContentHandlerFactory for this application
 869:    *
 870:    * @exception Error If the factory has already been defined
 871:    * @exception SecurityException If a security manager exists and its
 872:    * checkSetFactory method doesn't allow the operation
 873:    */
 874:   public static synchronized void setContentHandlerFactory(ContentHandlerFactory factory)
 875:   {
 876:     if (URLConnection.factory != null)
 877:       throw new Error("ContentHandlerFactory already set");
 878: 
 879:     // Throw an exception if an extant security mgr precludes
 880:     // setting the factory.
 881:     SecurityManager s = System.getSecurityManager();
 882:     if (s != null)
 883:       s.checkSetFactory();
 884: 
 885:     URLConnection.factory = factory;
 886:   }
 887: 
 888:   /**
 889:    * Returns the MIME type of a file based on the name of the file.  This
 890:    * works by searching for the file's extension in a list of file extensions
 891:    * and returning the MIME type associated with it.  If no type is found,
 892:    * then a MIME type of "application/octet-stream" will be returned.
 893:    *
 894:    * @param filename The filename to determine the MIME type for
 895:    *
 896:    * @return The MIME type String
 897:    *
 898:    * @specnote public since JDK 1.4
 899:    */
 900:   public static String guessContentTypeFromName(String filename)
 901:   {
 902:     return getFileNameMap().getContentTypeFor(filename.toLowerCase());
 903:   }
 904: 
 905:   /**
 906:    * Returns the MIME type of a stream based on the first few characters
 907:    * at the beginning of the stream.  This routine can be used to determine
 908:    * the MIME type if a server is believed to be returning an incorrect
 909:    * MIME type.  This method returns "application/octet-stream" if it
 910:    * cannot determine the MIME type.
 911:    * <p>
 912:    * NOTE: Overriding MIME types sent from the server can be obnoxious
 913:    * to user's.  See Internet Exploder 4 if you don't believe me.
 914:    *
 915:    * @param is The InputStream to determine the MIME type from
 916:    *
 917:    * @return The MIME type
 918:    *
 919:    * @exception IOException If an error occurs
 920:    */
 921:   public static String guessContentTypeFromStream(InputStream is)
 922:     throws IOException
 923:   {
 924:     is.mark(1024);
 925:     // FIXME: Implement this. Use system mimetype informations (like "file").
 926:     is.reset();
 927:     return "application/octet-stream";
 928:   }
 929: 
 930:   /**
 931:    * This method returns the <code>FileNameMap</code> object being used
 932:    * to decode MIME types by file extension.
 933:    *
 934:    * @return The <code>FileNameMap</code>.
 935:    *
 936:    * @since 1.2
 937:    */
 938:   public static synchronized FileNameMap getFileNameMap()
 939:   {
 940:     // Delayed initialization.
 941:     if (fileNameMap == null)
 942:       fileNameMap = new MimeTypeMapper();
 943: 
 944:     return fileNameMap;
 945:   }
 946: 
 947:   /**
 948:    * This method sets the <code>FileNameMap</code> object being used
 949:    * to decode MIME types by file extension.
 950:    *
 951:    * @param map The <code>FileNameMap</code>.
 952:    *
 953:    * @exception SecurityException If a security manager exists and its
 954:    * checkSetFactory method doesn't allow the operation
 955:    *
 956:    * @since 1.2
 957:    */
 958:   public static synchronized void setFileNameMap(FileNameMap map)
 959:   {
 960:     // Throw an exception if an extant security manager precludes
 961:     // setting the factory.
 962:     SecurityManager s = System.getSecurityManager();
 963:     if (s != null)
 964:       s.checkSetFactory();
 965: 
 966:     fileNameMap = map;
 967:   }
 968: 
 969:   private ContentHandler getContentHandler(String contentType)
 970:   {
 971:     // No content type so just handle it as the default.
 972:     if (contentType == null || contentType.equals(""))
 973:       return null;
 974: 
 975:     ContentHandler handler = null;
 976: 
 977:     // See if a handler has been cached for this content type.
 978:     // For efficiency, if a content type has been searched for but not
 979:     // found, it will be in the hash table but as the contentType String
 980:     // instead of a ContentHandler.
 981:     {
 982:       Object cachedHandler;
 983:       if ((cachedHandler = handlers.get(contentType)) != null)
 984:     {
 985:       if (cachedHandler instanceof ContentHandler)
 986:         return (ContentHandler)cachedHandler;
 987:       else
 988:         return null;
 989:     }
 990:     }
 991: 
 992:     // If a non-default factory has been set, use it.
 993:     if (factory != null)
 994:       handler = factory.createContentHandler(contentType);
 995: 
 996:     // Now try default factory. Using this factory to instantiate built-in
 997:     // content handlers is preferable  
 998:     if (handler == null)
 999:       handler = defaultFactory.createContentHandler(contentType);
1000: 
1001:     // User-set factory has not returned a handler. Use the default search 
1002:     // algorithm.
1003:     if (handler == null)
1004:       {
1005:     // Get the list of packages to check and append our default handler
1006:     // to it, along with the JDK specified default as a last resort.
1007:     // Except in very unusual environments the JDK specified one shouldn't
1008:     // ever be needed (or available).
1009:     String propVal = System.getProperty("java.content.handler.pkgs");
1010:     propVal = (propVal == null) ? "" : (propVal + "|");
1011:     propVal = propVal + "gnu.java.net.content|sun.net.www.content";
1012: 
1013:     // Replace the '/' character in the content type with '.' and
1014:     // all other non-alphabetic, non-numeric characters with '_'.
1015:     char[] cArray = contentType.toCharArray();
1016:     for (int i = 0; i < cArray.length; i++)
1017:       {
1018:         if (cArray[i] == '/')
1019:           cArray[i] = '.';
1020:         else if (! ((cArray[i] >= 'A' && cArray[i] <= 'Z') || 
1021:             (cArray[i] >= 'a' && cArray[i] <= 'z') ||
1022:             (cArray[i] >= '0' && cArray[i] <= '9')))
1023:           cArray[i] = '_';
1024:       }
1025:     String contentClass = new String(cArray);
1026: 
1027:     // See if a class of this content type exists in any of the packages.
1028:     StringTokenizer pkgPrefix = new StringTokenizer(propVal, "|");
1029:     do
1030:       {
1031:         String facName = pkgPrefix.nextToken() + "." + contentClass;
1032:         try
1033:           {
1034:         handler =
1035:           (ContentHandler) Class.forName(facName).newInstance();
1036:           }
1037:         catch (Exception e)
1038:           {
1039:         // Can't instantiate; handler still null, go on to next element.
1040:           }
1041:       } while ((handler == null ||
1042:             ! (handler instanceof ContentHandler)) &&
1043:            pkgPrefix.hasMoreTokens());
1044:       }
1045: 
1046:     // Update the hashtable with the new content handler.
1047:     if (handler instanceof ContentHandler)
1048:       {
1049:     handlers.put(contentType, handler);
1050:     return handler;
1051:       }
1052: 
1053:     // For efficiency on subsequent searches, put a dummy entry in the hash
1054:     // table for content types that don't have a non-default ContentHandler.
1055:     handlers.put(contentType, contentType);
1056:     return null;
1057:   }
1058:   
1059:   // We don't put these in a static initializer, because it creates problems
1060:   // with initializer co-dependency: SimpleDateFormat's constructors eventually 
1061:   // depend on URLConnection (via the java.text.*Symbols classes).
1062:   private static synchronized void initializeDateFormats()
1063:   {
1064:     if (dateformats_initialized)
1065:       return;
1066: 
1067:     Locale locale = new Locale("En", "Us", "Unix");
1068:     dateFormats = new SimpleDateFormat[3];
1069:     dateFormats[0] =
1070:       new SimpleDateFormat("EEE, dd MMM yyyy hh:mm:ss 'GMT'", locale);
1071:     dateFormats[1] =
1072:       new SimpleDateFormat("EEEE, dd-MMM-yy hh:mm:ss 'GMT'", locale);
1073:     dateFormats[2] = new SimpleDateFormat("EEE MMM d hh:mm:ss yyyy", locale);
1074:     dateformats_initialized = true;
1075:   }
1076: }