Source for java.net.URLConnection

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