Frames | No Frames |
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.<content_type> 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: }