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