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