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