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