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