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