0001: /*
0002: * Copyright 1995-2006 Sun Microsystems, Inc. All Rights Reserved.
0003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004: *
0005: * This code is free software; you can redistribute it and/or modify it
0006: * under the terms of the GNU General Public License version 2 only, as
0007: * published by the Free Software Foundation. Sun designates this
0008: * particular file as subject to the "Classpath" exception as provided
0009: * by Sun in the LICENSE file that accompanied this code.
0010: *
0011: * This code is distributed in the hope that it will be useful, but WITHOUT
0012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014: * version 2 for more details (a copy is included in the LICENSE file that
0015: * accompanied this code).
0016: *
0017: * You should have received a copy of the GNU General Public License version
0018: * 2 along with this work; if not, write to the Free Software Foundation,
0019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020: *
0021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022: * CA 95054 USA or visit www.sun.com if you need additional information or
0023: * have any questions.
0024: */
0025:
0026: package java.net;
0027:
0028: import java.io.IOException;
0029: import java.io.InputStream;
0030: import java.io.OutputStream;
0031: import java.util.Hashtable;
0032: import java.util.Date;
0033: import java.util.StringTokenizer;
0034: import java.util.Collections;
0035: import java.util.Map;
0036: import java.util.List;
0037: import java.security.Permission;
0038: import java.security.AccessController;
0039: import sun.security.util.SecurityConstants;
0040: import sun.net.www.MessageHeader;
0041:
0042: /**
0043: * The abstract class <code>URLConnection</code> is the superclass
0044: * of all classes that represent a communications link between the
0045: * application and a URL. Instances of this class can be used both to
0046: * read from and to write to the resource referenced by the URL. In
0047: * general, creating a connection to a URL is a multistep process:
0048: * <p>
0049: * <center><table border=2 summary="Describes the process of creating a connection to a URL: openConnection() and connect() over time.">
0050: * <tr><th><code>openConnection()</code></th>
0051: * <th><code>connect()</code></th></tr>
0052: * <tr><td>Manipulate parameters that affect the connection to the remote
0053: * resource.</td>
0054: * <td>Interact with the resource; query header fields and
0055: * contents.</td></tr>
0056: * </table>
0057: * ---------------------------->
0058: * <br>time</center>
0059: *
0060: * <ol>
0061: * <li>The connection object is created by invoking the
0062: * <code>openConnection</code> method on a URL.
0063: * <li>The setup parameters and general request properties are manipulated.
0064: * <li>The actual connection to the remote object is made, using the
0065: * <code>connect</code> method.
0066: * <li>The remote object becomes available. The header fields and the contents
0067: * of the remote object can be accessed.
0068: * </ol>
0069: * <p>
0070: * The setup parameters are modified using the following methods:
0071: * <ul>
0072: * <li><code>setAllowUserInteraction</code>
0073: * <li><code>setDoInput</code>
0074: * <li><code>setDoOutput</code>
0075: * <li><code>setIfModifiedSince</code>
0076: * <li><code>setUseCaches</code>
0077: * </ul>
0078: * <p>
0079: * and the general request properties are modified using the method:
0080: * <ul>
0081: * <li><code>setRequestProperty</code>
0082: * </ul>
0083: * <p>
0084: * Default values for the <code>AllowUserInteraction</code> and
0085: * <code>UseCaches</code> parameters can be set using the methods
0086: * <code>setDefaultAllowUserInteraction</code> and
0087: * <code>setDefaultUseCaches</code>.
0088: * <p>
0089: * Each of the above <code>set</code> methods has a corresponding
0090: * <code>get</code> method to retrieve the value of the parameter or
0091: * general request property. The specific parameters and general
0092: * request properties that are applicable are protocol specific.
0093: * <p>
0094: * The following methods are used to access the header fields and
0095: * the contents after the connection is made to the remote object:
0096: * <ul>
0097: * <li><code>getContent</code>
0098: * <li><code>getHeaderField</code>
0099: * <li><code>getInputStream</code>
0100: * <li><code>getOutputStream</code>
0101: * </ul>
0102: * <p>
0103: * Certain header fields are accessed frequently. The methods:
0104: * <ul>
0105: * <li><code>getContentEncoding</code>
0106: * <li><code>getContentLength</code>
0107: * <li><code>getContentType</code>
0108: * <li><code>getDate</code>
0109: * <li><code>getExpiration</code>
0110: * <li><code>getLastModifed</code>
0111: * </ul>
0112: * <p>
0113: * provide convenient access to these fields. The
0114: * <code>getContentType</code> method is used by the
0115: * <code>getContent</code> method to determine the type of the remote
0116: * object; subclasses may find it convenient to override the
0117: * <code>getContentType</code> method.
0118: * <p>
0119: * In the common case, all of the pre-connection parameters and
0120: * general request properties can be ignored: the pre-connection
0121: * parameters and request properties default to sensible values. For
0122: * most clients of this interface, there are only two interesting
0123: * methods: <code>getInputStream</code> and <code>getContent</code>,
0124: * which are mirrored in the <code>URL</code> class by convenience methods.
0125: * <p>
0126: * More information on the request properties and header fields of
0127: * an <code>http</code> connection can be found at:
0128: * <blockquote><pre>
0129: * <a href="http://www.ietf.org/rfc/rfc2616.txt">http://www.ietf.org/rfc/rfc2616.txt</a>
0130: * </pre></blockquote>
0131: *
0132: * Note about <code>fileNameMap</code>: In versions prior to JDK 1.1.6,
0133: * field <code>fileNameMap</code> of <code>URLConnection</code> was public.
0134: * In JDK 1.1.6 and later, <code>fileNameMap</code> is private; accessor
0135: * and mutator methods {@link #getFileNameMap() getFileNameMap} and
0136: * {@link #setFileNameMap(java.net.FileNameMap) setFileNameMap} are added
0137: * to access it. This change is also described on the <a href=
0138: * "http://java.sun.com/products/jdk/1.2/compatibility.html">
0139: * Compatibility</a> page.
0140: *
0141: * Invoking the <tt>close()</tt> methods on the <tt>InputStream</tt> or <tt>OutputStream</tt> of an
0142: * <tt>URLConnection</tt> after a request may free network resources associated with this
0143: * instance, unless particular protocol specifications specify different behaviours
0144: * for it.
0145: *
0146: * @author James Gosling
0147: * @version 1.113, 05/05/07
0148: * @see java.net.URL#openConnection()
0149: * @see java.net.URLConnection#connect()
0150: * @see java.net.URLConnection#getContent()
0151: * @see java.net.URLConnection#getContentEncoding()
0152: * @see java.net.URLConnection#getContentLength()
0153: * @see java.net.URLConnection#getContentType()
0154: * @see java.net.URLConnection#getDate()
0155: * @see java.net.URLConnection#getExpiration()
0156: * @see java.net.URLConnection#getHeaderField(int)
0157: * @see java.net.URLConnection#getHeaderField(java.lang.String)
0158: * @see java.net.URLConnection#getInputStream()
0159: * @see java.net.URLConnection#getLastModified()
0160: * @see java.net.URLConnection#getOutputStream()
0161: * @see java.net.URLConnection#setAllowUserInteraction(boolean)
0162: * @see java.net.URLConnection#setDefaultUseCaches(boolean)
0163: * @see java.net.URLConnection#setDoInput(boolean)
0164: * @see java.net.URLConnection#setDoOutput(boolean)
0165: * @see java.net.URLConnection#setIfModifiedSince(long)
0166: * @see java.net.URLConnection#setRequestProperty(java.lang.String, java.lang.String)
0167: * @see java.net.URLConnection#setUseCaches(boolean)
0168: * @since JDK1.0
0169: */
0170: public abstract class URLConnection {
0171:
0172: /**
0173: * The URL represents the remote object on the World Wide Web to
0174: * which this connection is opened.
0175: * <p>
0176: * The value of this field can be accessed by the
0177: * <code>getURL</code> method.
0178: * <p>
0179: * The default value of this variable is the value of the URL
0180: * argument in the <code>URLConnection</code> constructor.
0181: *
0182: * @see java.net.URLConnection#getURL()
0183: * @see java.net.URLConnection#url
0184: */
0185: protected URL url;
0186:
0187: /**
0188: * This variable is set by the <code>setDoInput</code> method. Its
0189: * value is returned by the <code>getDoInput</code> method.
0190: * <p>
0191: * A URL connection can be used for input and/or output. Setting the
0192: * <code>doInput</code> flag to <code>true</code> indicates that
0193: * the application intends to read data from the URL connection.
0194: * <p>
0195: * The default value of this field is <code>true</code>.
0196: *
0197: * @see java.net.URLConnection#getDoInput()
0198: * @see java.net.URLConnection#setDoInput(boolean)
0199: */
0200: protected boolean doInput = true;
0201:
0202: /**
0203: * This variable is set by the <code>setDoOutput</code> method. Its
0204: * value is returned by the <code>getDoOutput</code> method.
0205: * <p>
0206: * A URL connection can be used for input and/or output. Setting the
0207: * <code>doOutput</code> flag to <code>true</code> indicates
0208: * that the application intends to write data to the URL connection.
0209: * <p>
0210: * The default value of this field is <code>false</code>.
0211: *
0212: * @see java.net.URLConnection#getDoOutput()
0213: * @see java.net.URLConnection#setDoOutput(boolean)
0214: */
0215: protected boolean doOutput = false;
0216:
0217: private static boolean defaultAllowUserInteraction = false;
0218:
0219: /**
0220: * If <code>true</code>, this <code>URL</code> is being examined in
0221: * a context in which it makes sense to allow user interactions such
0222: * as popping up an authentication dialog. If <code>false</code>,
0223: * then no user interaction is allowed.
0224: * <p>
0225: * The value of this field can be set by the
0226: * <code>setAllowUserInteraction</code> method.
0227: * Its value is returned by the
0228: * <code>getAllowUserInteraction</code> method.
0229: * Its default value is the value of the argument in the last invocation
0230: * of the <code>setDefaultAllowUserInteraction</code> method.
0231: *
0232: * @see java.net.URLConnection#getAllowUserInteraction()
0233: * @see java.net.URLConnection#setAllowUserInteraction(boolean)
0234: * @see java.net.URLConnection#setDefaultAllowUserInteraction(boolean)
0235: */
0236: protected boolean allowUserInteraction = defaultAllowUserInteraction;
0237:
0238: private static boolean defaultUseCaches = true;
0239:
0240: /**
0241: * If <code>true</code>, the protocol is allowed to use caching
0242: * whenever it can. If <code>false</code>, the protocol must always
0243: * try to get a fresh copy of the object.
0244: * <p>
0245: * This field is set by the <code>setUseCaches</code> method. Its
0246: * value is returned by the <code>getUseCaches</code> method.
0247: * <p>
0248: * Its default value is the value given in the last invocation of the
0249: * <code>setDefaultUseCaches</code> method.
0250: *
0251: * @see java.net.URLConnection#setUseCaches(boolean)
0252: * @see java.net.URLConnection#getUseCaches()
0253: * @see java.net.URLConnection#setDefaultUseCaches(boolean)
0254: */
0255: protected boolean useCaches = defaultUseCaches;
0256:
0257: /**
0258: * Some protocols support skipping the fetching of the object unless
0259: * the object has been modified more recently than a certain time.
0260: * <p>
0261: * A nonzero value gives a time as the number of milliseconds since
0262: * January 1, 1970, GMT. The object is fetched only if it has been
0263: * modified more recently than that time.
0264: * <p>
0265: * This variable is set by the <code>setIfModifiedSince</code>
0266: * method. Its value is returned by the
0267: * <code>getIfModifiedSince</code> method.
0268: * <p>
0269: * The default value of this field is <code>0</code>, indicating
0270: * that the fetching must always occur.
0271: *
0272: * @see java.net.URLConnection#getIfModifiedSince()
0273: * @see java.net.URLConnection#setIfModifiedSince(long)
0274: */
0275: protected long ifModifiedSince = 0;
0276:
0277: /**
0278: * If <code>false</code>, this connection object has not created a
0279: * communications link to the specified URL. If <code>true</code>,
0280: * the communications link has been established.
0281: */
0282: protected boolean connected = false;
0283:
0284: /**
0285: * @since 1.5
0286: */
0287: private int connectTimeout;
0288: private int readTimeout;
0289:
0290: /**
0291: * @since 1.6
0292: */
0293: private MessageHeader requests;
0294:
0295: /**
0296: * @since JDK1.1
0297: */
0298: private static FileNameMap fileNameMap;
0299:
0300: /**
0301: * @since 1.2.2
0302: */
0303: private static boolean fileNameMapLoaded = false;
0304:
0305: /**
0306: * Loads filename map (a mimetable) from a data file. It will
0307: * first try to load the user-specific table, defined
0308: * by "content.types.user.table" property. If that fails,
0309: * it tries to load the default built-in table at
0310: * lib/content-types.properties under java home.
0311: *
0312: * @return the FileNameMap
0313: * @since 1.2
0314: * @see #setFileNameMap(java.net.FileNameMap)
0315: */
0316: public static synchronized FileNameMap getFileNameMap() {
0317: if ((fileNameMap == null) && !fileNameMapLoaded) {
0318: fileNameMap = sun.net.www.MimeTable.loadTable();
0319: fileNameMapLoaded = true;
0320: }
0321:
0322: return new FileNameMap() {
0323: private FileNameMap map = fileNameMap;
0324:
0325: public String getContentTypeFor(String fileName) {
0326: return map.getContentTypeFor(fileName);
0327: }
0328: };
0329: }
0330:
0331: /**
0332: * Sets the FileNameMap.
0333: * <p>
0334: * If there is a security manager, this method first calls
0335: * the security manager's <code>checkSetFactory</code> method
0336: * to ensure the operation is allowed.
0337: * This could result in a SecurityException.
0338: *
0339: * @param map the FileNameMap to be set
0340: * @exception SecurityException if a security manager exists and its
0341: * <code>checkSetFactory</code> method doesn't allow the operation.
0342: * @see SecurityManager#checkSetFactory
0343: * @see #getFileNameMap()
0344: * @since 1.2
0345: */
0346: public static void setFileNameMap(FileNameMap map) {
0347: SecurityManager sm = System.getSecurityManager();
0348: if (sm != null)
0349: sm.checkSetFactory();
0350: fileNameMap = map;
0351: }
0352:
0353: /**
0354: * Opens a communications link to the resource referenced by this
0355: * URL, if such a connection has not already been established.
0356: * <p>
0357: * If the <code>connect</code> method is called when the connection
0358: * has already been opened (indicated by the <code>connected</code>
0359: * field having the value <code>true</code>), the call is ignored.
0360: * <p>
0361: * URLConnection objects go through two phases: first they are
0362: * created, then they are connected. After being created, and
0363: * before being connected, various options can be specified
0364: * (e.g., doInput and UseCaches). After connecting, it is an
0365: * error to try to set them. Operations that depend on being
0366: * connected, like getContentLength, will implicitly perform the
0367: * connection, if necessary.
0368: *
0369: * @throws SocketTimeoutException if the timeout expires before
0370: * the connection can be established
0371: * @exception IOException if an I/O error occurs while opening the
0372: * connection.
0373: * @see java.net.URLConnection#connected
0374: * @see #getConnectTimeout()
0375: * @see #setConnectTimeout(int)
0376: */
0377: abstract public void connect() throws IOException;
0378:
0379: /**
0380: * Sets a specified timeout value, in milliseconds, to be used
0381: * when opening a communications link to the resource referenced
0382: * by this URLConnection. If the timeout expires before the
0383: * connection can be established, a
0384: * java.net.SocketTimeoutException is raised. A timeout of zero is
0385: * interpreted as an infinite timeout.
0386:
0387: * <p> Some non-standard implmentation of this method may ignore
0388: * the specified timeout. To see the connect timeout set, please
0389: * call getConnectTimeout().
0390: *
0391: * @param timeout an <code>int</code> that specifies the connect
0392: * timeout value in milliseconds
0393: * @throws IllegalArgumentException if the timeout parameter is negative
0394: *
0395: * @see #getConnectTimeout()
0396: * @see #connect()
0397: * @since 1.5
0398: */
0399: public void setConnectTimeout(int timeout) {
0400: if (timeout < 0) {
0401: throw new IllegalArgumentException(
0402: "timeout can not be negative");
0403: }
0404: connectTimeout = timeout;
0405: }
0406:
0407: /**
0408: * Returns setting for connect timeout.
0409: * <p>
0410: * 0 return implies that the option is disabled
0411: * (i.e., timeout of infinity).
0412: *
0413: * @return an <code>int</code> that indicates the connect timeout
0414: * value in milliseconds
0415: * @see #setConnectTimeout(int)
0416: * @see #connect()
0417: * @since 1.5
0418: */
0419: public int getConnectTimeout() {
0420: return connectTimeout;
0421: }
0422:
0423: /**
0424: * Sets the read timeout to a specified timeout, in
0425: * milliseconds. A non-zero value specifies the timeout when
0426: * reading from Input stream when a connection is established to a
0427: * resource. If the timeout expires before there is data available
0428: * for read, a java.net.SocketTimeoutException is raised. A
0429: * timeout of zero is interpreted as an infinite timeout.
0430: *
0431: *<p> Some non-standard implementation of this method ignores the
0432: * specified timeout. To see the read timeout set, please call
0433: * getReadTimeout().
0434: *
0435: * @param timeout an <code>int</code> that specifies the timeout
0436: * value to be used in milliseconds
0437: * @throws IllegalArgumentException if the timeout parameter is negative
0438: *
0439: * @see #getReadTimeout()
0440: * @see InputStream#read()
0441: * @since 1.5
0442: */
0443: public void setReadTimeout(int timeout) {
0444: if (timeout < 0) {
0445: throw new IllegalArgumentException(
0446: "timeout can not be negative");
0447: }
0448: readTimeout = timeout;
0449: }
0450:
0451: /**
0452: * Returns setting for read timeout. 0 return implies that the
0453: * option is disabled (i.e., timeout of infinity).
0454: *
0455: * @return an <code>int</code> that indicates the read timeout
0456: * value in milliseconds
0457: *
0458: * @see #setReadTimeout(int)
0459: * @see InputStream#read()
0460: * @since 1.5
0461: */
0462: public int getReadTimeout() {
0463: return readTimeout;
0464: }
0465:
0466: /**
0467: * Constructs a URL connection to the specified URL. A connection to
0468: * the object referenced by the URL is not created.
0469: *
0470: * @param url the specified URL.
0471: */
0472: protected URLConnection(URL url) {
0473: this .url = url;
0474: }
0475:
0476: /**
0477: * Returns the value of this <code>URLConnection</code>'s <code>URL</code>
0478: * field.
0479: *
0480: * @return the value of this <code>URLConnection</code>'s <code>URL</code>
0481: * field.
0482: * @see java.net.URLConnection#url
0483: */
0484: public URL getURL() {
0485: return url;
0486: }
0487:
0488: /**
0489: * Returns the value of the <code>content-length</code> header field.
0490: *
0491: * @return the content length of the resource that this connection's URL
0492: * references, or <code>-1</code> if the content length is
0493: * not known.
0494: */
0495: public int getContentLength() {
0496: return getHeaderFieldInt("content-length", -1);
0497: }
0498:
0499: /**
0500: * Returns the value of the <code>content-type</code> header field.
0501: *
0502: * @return the content type of the resource that the URL references,
0503: * or <code>null</code> if not known.
0504: * @see java.net.URLConnection#getHeaderField(java.lang.String)
0505: */
0506: public String getContentType() {
0507: return getHeaderField("content-type");
0508: }
0509:
0510: /**
0511: * Returns the value of the <code>content-encoding</code> header field.
0512: *
0513: * @return the content encoding of the resource that the URL references,
0514: * or <code>null</code> if not known.
0515: * @see java.net.URLConnection#getHeaderField(java.lang.String)
0516: */
0517: public String getContentEncoding() {
0518: return getHeaderField("content-encoding");
0519: }
0520:
0521: /**
0522: * Returns the value of the <code>expires</code> header field.
0523: *
0524: * @return the expiration date of the resource that this URL references,
0525: * or 0 if not known. The value is the number of milliseconds since
0526: * January 1, 1970 GMT.
0527: * @see java.net.URLConnection#getHeaderField(java.lang.String)
0528: */
0529: public long getExpiration() {
0530: return getHeaderFieldDate("expires", 0);
0531: }
0532:
0533: /**
0534: * Returns the value of the <code>date</code> header field.
0535: *
0536: * @return the sending date of the resource that the URL references,
0537: * or <code>0</code> if not known. The value returned is the
0538: * number of milliseconds since January 1, 1970 GMT.
0539: * @see java.net.URLConnection#getHeaderField(java.lang.String)
0540: */
0541: public long getDate() {
0542: return getHeaderFieldDate("date", 0);
0543: }
0544:
0545: /**
0546: * Returns the value of the <code>last-modified</code> header field.
0547: * The result is the number of milliseconds since January 1, 1970 GMT.
0548: *
0549: * @return the date the resource referenced by this
0550: * <code>URLConnection</code> was last modified, or 0 if not known.
0551: * @see java.net.URLConnection#getHeaderField(java.lang.String)
0552: */
0553: public long getLastModified() {
0554: return getHeaderFieldDate("last-modified", 0);
0555: }
0556:
0557: /**
0558: * Returns the value of the named header field.
0559: * <p>
0560: * If called on a connection that sets the same header multiple times
0561: * with possibly different values, only the last value is returned.
0562: *
0563: *
0564: * @param name the name of a header field.
0565: * @return the value of the named header field, or <code>null</code>
0566: * if there is no such field in the header.
0567: */
0568: public String getHeaderField(String name) {
0569: return null;
0570: }
0571:
0572: /**
0573: * Returns an unmodifiable Map of the header fields.
0574: * The Map keys are Strings that represent the
0575: * response-header field names. Each Map value is an
0576: * unmodifiable List of Strings that represents
0577: * the corresponding field values.
0578: *
0579: * @return a Map of header fields
0580: * @since 1.4
0581: */
0582: public Map<String, List<String>> getHeaderFields() {
0583: return Collections.EMPTY_MAP;
0584: }
0585:
0586: /**
0587: * Returns the value of the named field parsed as a number.
0588: * <p>
0589: * This form of <code>getHeaderField</code> exists because some
0590: * connection types (e.g., <code>http-ng</code>) have pre-parsed
0591: * headers. Classes for that connection type can override this method
0592: * and short-circuit the parsing.
0593: *
0594: * @param name the name of the header field.
0595: * @param Default the default value.
0596: * @return the value of the named field, parsed as an integer. The
0597: * <code>Default</code> value is returned if the field is
0598: * missing or malformed.
0599: */
0600: public int getHeaderFieldInt(String name, int Default) {
0601: String value = getHeaderField(name);
0602: try {
0603: return Integer.parseInt(value);
0604: } catch (Exception e) {
0605: }
0606: return Default;
0607: }
0608:
0609: /**
0610: * Returns the value of the named field parsed as date.
0611: * The result is the number of milliseconds since January 1, 1970 GMT
0612: * represented by the named field.
0613: * <p>
0614: * This form of <code>getHeaderField</code> exists because some
0615: * connection types (e.g., <code>http-ng</code>) have pre-parsed
0616: * headers. Classes for that connection type can override this method
0617: * and short-circuit the parsing.
0618: *
0619: * @param name the name of the header field.
0620: * @param Default a default value.
0621: * @return the value of the field, parsed as a date. The value of the
0622: * <code>Default</code> argument is returned if the field is
0623: * missing or malformed.
0624: */
0625: public long getHeaderFieldDate(String name, long Default) {
0626: String value = getHeaderField(name);
0627: try {
0628: return Date.parse(value);
0629: } catch (Exception e) {
0630: }
0631: return Default;
0632: }
0633:
0634: /**
0635: * Returns the key for the <code>n</code><sup>th</sup> header field.
0636: * It returns <code>null</code> if there are fewer than <code>n+1</code> fields.
0637: *
0638: * @param n an index, where n>=0
0639: * @return the key for the <code>n</code><sup>th</sup> header field,
0640: * or <code>null</code> if there are fewer than <code>n+1</code>
0641: * fields.
0642: */
0643: public String getHeaderFieldKey(int n) {
0644: return null;
0645: }
0646:
0647: /**
0648: * Returns the value for the <code>n</code><sup>th</sup> header field.
0649: * It returns <code>null</code> if there are fewer than
0650: * <code>n+1</code>fields.
0651: * <p>
0652: * This method can be used in conjunction with the
0653: * {@link #getHeaderFieldKey(int) getHeaderFieldKey} method to iterate through all
0654: * the headers in the message.
0655: *
0656: * @param n an index, where n>=0
0657: * @return the value of the <code>n</code><sup>th</sup> header field
0658: * or <code>null</code> if there are fewer than <code>n+1</code> fields
0659: * @see java.net.URLConnection#getHeaderFieldKey(int)
0660: */
0661: public String getHeaderField(int n) {
0662: return null;
0663: }
0664:
0665: /**
0666: * Retrieves the contents of this URL connection.
0667: * <p>
0668: * This method first determines the content type of the object by
0669: * calling the <code>getContentType</code> method. If this is
0670: * the first time that the application has seen that specific content
0671: * type, a content handler for that content type is created:
0672: * <ol>
0673: * <li>If the application has set up a content handler factory instance
0674: * using the <code>setContentHandlerFactory</code> method, the
0675: * <code>createContentHandler</code> method of that instance is called
0676: * with the content type as an argument; the result is a content
0677: * handler for that content type.
0678: * <li>If no content handler factory has yet been set up, or if the
0679: * factory's <code>createContentHandler</code> method returns
0680: * <code>null</code>, then the application loads the class named:
0681: * <blockquote><pre>
0682: * sun.net.www.content.<<i>contentType</i>>
0683: * </pre></blockquote>
0684: * where <<i>contentType</i>> is formed by taking the
0685: * content-type string, replacing all slash characters with a
0686: * <code>period</code> ('.'), and all other non-alphanumeric characters
0687: * with the underscore character '<code>_</code>'. The alphanumeric
0688: * characters are specifically the 26 uppercase ASCII letters
0689: * '<code>A</code>' through '<code>Z</code>', the 26 lowercase ASCII
0690: * letters '<code>a</code>' through '<code>z</code>', and the 10 ASCII
0691: * digits '<code>0</code>' through '<code>9</code>'. If the specified
0692: * class does not exist, or is not a subclass of
0693: * <code>ContentHandler</code>, then an
0694: * <code>UnknownServiceException</code> is thrown.
0695: * </ol>
0696: *
0697: * @return the object fetched. The <code>instanceof</code> operator
0698: * should be used to determine the specific kind of object
0699: * returned.
0700: * @exception IOException if an I/O error occurs while
0701: * getting the content.
0702: * @exception UnknownServiceException if the protocol does not support
0703: * the content type.
0704: * @see java.net.ContentHandlerFactory#createContentHandler(java.lang.String)
0705: * @see java.net.URLConnection#getContentType()
0706: * @see java.net.URLConnection#setContentHandlerFactory(java.net.ContentHandlerFactory)
0707: */
0708: public Object getContent() throws IOException {
0709: // Must call getInputStream before GetHeaderField gets called
0710: // so that FileNotFoundException has a chance to be thrown up
0711: // from here without being caught.
0712: getInputStream();
0713: return getContentHandler().getContent(this );
0714: }
0715:
0716: /**
0717: * Retrieves the contents of this URL connection.
0718: *
0719: * @param classes the <code>Class</code> array
0720: * indicating the requested types
0721: * @return the object fetched that is the first match of the type
0722: * specified in the classes array. null if none of
0723: * the requested types are supported.
0724: * The <code>instanceof</code> operator should be used to
0725: * determine the specific kind of object returned.
0726: * @exception IOException if an I/O error occurs while
0727: * getting the content.
0728: * @exception UnknownServiceException if the protocol does not support
0729: * the content type.
0730: * @see java.net.URLConnection#getContent()
0731: * @see java.net.ContentHandlerFactory#createContentHandler(java.lang.String)
0732: * @see java.net.URLConnection#getContent(java.lang.Class[])
0733: * @see java.net.URLConnection#setContentHandlerFactory(java.net.ContentHandlerFactory)
0734: * @since 1.3
0735: */
0736: public Object getContent(Class[] classes) throws IOException {
0737: // Must call getInputStream before GetHeaderField gets called
0738: // so that FileNotFoundException has a chance to be thrown up
0739: // from here without being caught.
0740: getInputStream();
0741: return getContentHandler().getContent(this , classes);
0742: }
0743:
0744: /**
0745: * Returns a permission object representing the permission
0746: * necessary to make the connection represented by this
0747: * object. This method returns null if no permission is
0748: * required to make the connection. By default, this method
0749: * returns <code>java.security.AllPermission</code>. Subclasses
0750: * should override this method and return the permission
0751: * that best represents the permission required to make a
0752: * a connection to the URL. For example, a <code>URLConnection</code>
0753: * representing a <code>file:</code> URL would return a
0754: * <code>java.io.FilePermission</code> object.
0755: *
0756: * <p>The permission returned may dependent upon the state of the
0757: * connection. For example, the permission before connecting may be
0758: * different from that after connecting. For example, an HTTP
0759: * sever, say foo.com, may redirect the connection to a different
0760: * host, say bar.com. Before connecting the permission returned by
0761: * the connection will represent the permission needed to connect
0762: * to foo.com, while the permission returned after connecting will
0763: * be to bar.com.
0764: *
0765: * <p>Permissions are generally used for two purposes: to protect
0766: * caches of objects obtained through URLConnections, and to check
0767: * the right of a recipient to learn about a particular URL. In
0768: * the first case, the permission should be obtained
0769: * <em>after</em> the object has been obtained. For example, in an
0770: * HTTP connection, this will represent the permission to connect
0771: * to the host from which the data was ultimately fetched. In the
0772: * second case, the permission should be obtained and tested
0773: * <em>before</em> connecting.
0774: *
0775: * @return the permission object representing the permission
0776: * necessary to make the connection represented by this
0777: * URLConnection.
0778: *
0779: * @exception IOException if the computation of the permission
0780: * requires network or file I/O and an exception occurs while
0781: * computing it.
0782: */
0783: public Permission getPermission() throws IOException {
0784: return SecurityConstants.ALL_PERMISSION;
0785: }
0786:
0787: /**
0788: * Returns an input stream that reads from this open connection.
0789: *
0790: * A SocketTimeoutException can be thrown when reading from the
0791: * returned input stream if the read timeout expires before data
0792: * is available for read.
0793: *
0794: * @return an input stream that reads from this open connection.
0795: * @exception IOException if an I/O error occurs while
0796: * creating the input stream.
0797: * @exception UnknownServiceException if the protocol does not support
0798: * input.
0799: * @see #setReadTimeout(int)
0800: * @see #getReadTimeout()
0801: */
0802: public InputStream getInputStream() throws IOException {
0803: throw new UnknownServiceException(
0804: "protocol doesn't support input");
0805: }
0806:
0807: /**
0808: * Returns an output stream that writes to this connection.
0809: *
0810: * @return an output stream that writes to this connection.
0811: * @exception IOException if an I/O error occurs while
0812: * creating the output stream.
0813: * @exception UnknownServiceException if the protocol does not support
0814: * output.
0815: */
0816: public OutputStream getOutputStream() throws IOException {
0817: throw new UnknownServiceException(
0818: "protocol doesn't support output");
0819: }
0820:
0821: /**
0822: * Returns a <code>String</code> representation of this URL connection.
0823: *
0824: * @return a string representation of this <code>URLConnection</code>.
0825: */
0826: public String toString() {
0827: return this .getClass().getName() + ":" + url;
0828: }
0829:
0830: /**
0831: * Sets the value of the <code>doInput</code> field for this
0832: * <code>URLConnection</code> to the specified value.
0833: * <p>
0834: * A URL connection can be used for input and/or output. Set the DoInput
0835: * flag to true if you intend to use the URL connection for input,
0836: * false if not. The default is true.
0837: *
0838: * @param doinput the new value.
0839: * @throws IllegalStateException if already connected
0840: * @see java.net.URLConnection#doInput
0841: * @see #getDoInput()
0842: */
0843: public void setDoInput(boolean doinput) {
0844: if (connected)
0845: throw new IllegalStateException("Already connected");
0846: doInput = doinput;
0847: }
0848:
0849: /**
0850: * Returns the value of this <code>URLConnection</code>'s
0851: * <code>doInput</code> flag.
0852: *
0853: * @return the value of this <code>URLConnection</code>'s
0854: * <code>doInput</code> flag.
0855: * @see #setDoInput(boolean)
0856: */
0857: public boolean getDoInput() {
0858: return doInput;
0859: }
0860:
0861: /**
0862: * Sets the value of the <code>doOutput</code> field for this
0863: * <code>URLConnection</code> to the specified value.
0864: * <p>
0865: * A URL connection can be used for input and/or output. Set the DoOutput
0866: * flag to true if you intend to use the URL connection for output,
0867: * false if not. The default is false.
0868: *
0869: * @param dooutput the new value.
0870: * @throws IllegalStateException if already connected
0871: * @see #getDoOutput()
0872: */
0873: public void setDoOutput(boolean dooutput) {
0874: if (connected)
0875: throw new IllegalStateException("Already connected");
0876: doOutput = dooutput;
0877: }
0878:
0879: /**
0880: * Returns the value of this <code>URLConnection</code>'s
0881: * <code>doOutput</code> flag.
0882: *
0883: * @return the value of this <code>URLConnection</code>'s
0884: * <code>doOutput</code> flag.
0885: * @see #setDoOutput(boolean)
0886: */
0887: public boolean getDoOutput() {
0888: return doOutput;
0889: }
0890:
0891: /**
0892: * Set the value of the <code>allowUserInteraction</code> field of
0893: * this <code>URLConnection</code>.
0894: *
0895: * @param allowuserinteraction the new value.
0896: * @throws IllegalStateException if already connected
0897: * @see #getAllowUserInteraction()
0898: */
0899: public void setAllowUserInteraction(boolean allowuserinteraction) {
0900: if (connected)
0901: throw new IllegalStateException("Already connected");
0902: allowUserInteraction = allowuserinteraction;
0903: }
0904:
0905: /**
0906: * Returns the value of the <code>allowUserInteraction</code> field for
0907: * this object.
0908: *
0909: * @return the value of the <code>allowUserInteraction</code> field for
0910: * this object.
0911: * @see #setAllowUserInteraction(boolean)
0912: */
0913: public boolean getAllowUserInteraction() {
0914: return allowUserInteraction;
0915: }
0916:
0917: /**
0918: * Sets the default value of the
0919: * <code>allowUserInteraction</code> field for all future
0920: * <code>URLConnection</code> objects to the specified value.
0921: *
0922: * @param defaultallowuserinteraction the new value.
0923: * @see #getDefaultAllowUserInteraction()
0924: */
0925: public static void setDefaultAllowUserInteraction(
0926: boolean defaultallowuserinteraction) {
0927: defaultAllowUserInteraction = defaultallowuserinteraction;
0928: }
0929:
0930: /**
0931: * Returns the default value of the <code>allowUserInteraction</code>
0932: * field.
0933: * <p>
0934: * Ths default is "sticky", being a part of the static state of all
0935: * URLConnections. This flag applies to the next, and all following
0936: * URLConnections that are created.
0937: *
0938: * @return the default value of the <code>allowUserInteraction</code>
0939: * field.
0940: * @see #setDefaultAllowUserInteraction(boolean)
0941: */
0942: public static boolean getDefaultAllowUserInteraction() {
0943: return defaultAllowUserInteraction;
0944: }
0945:
0946: /**
0947: * Sets the value of the <code>useCaches</code> field of this
0948: * <code>URLConnection</code> to the specified value.
0949: * <p>
0950: * Some protocols do caching of documents. Occasionally, it is important
0951: * to be able to "tunnel through" and ignore the caches (e.g., the
0952: * "reload" button in a browser). If the UseCaches flag on a connection
0953: * is true, the connection is allowed to use whatever caches it can.
0954: * If false, caches are to be ignored.
0955: * The default value comes from DefaultUseCaches, which defaults to
0956: * true.
0957: *
0958: * @param usecaches a <code>boolean</code> indicating whether
0959: * or not to allow caching
0960: * @throws IllegalStateException if already connected
0961: * @see #getUseCaches()
0962: */
0963: public void setUseCaches(boolean usecaches) {
0964: if (connected)
0965: throw new IllegalStateException("Already connected");
0966: useCaches = usecaches;
0967: }
0968:
0969: /**
0970: * Returns the value of this <code>URLConnection</code>'s
0971: * <code>useCaches</code> field.
0972: *
0973: * @return the value of this <code>URLConnection</code>'s
0974: * <code>useCaches</code> field.
0975: * @see #setUseCaches(boolean)
0976: */
0977: public boolean getUseCaches() {
0978: return useCaches;
0979: }
0980:
0981: /**
0982: * Sets the value of the <code>ifModifiedSince</code> field of
0983: * this <code>URLConnection</code> to the specified value.
0984: *
0985: * @param ifmodifiedsince the new value.
0986: * @throws IllegalStateException if already connected
0987: * @see #getIfModifiedSince()
0988: */
0989: public void setIfModifiedSince(long ifmodifiedsince) {
0990: if (connected)
0991: throw new IllegalStateException("Already connected");
0992: ifModifiedSince = ifmodifiedsince;
0993: }
0994:
0995: /**
0996: * Returns the value of this object's <code>ifModifiedSince</code> field.
0997: *
0998: * @return the value of this object's <code>ifModifiedSince</code> field.
0999: * @see #setIfModifiedSince(long)
1000: */
1001: public long getIfModifiedSince() {
1002: return ifModifiedSince;
1003: }
1004:
1005: /**
1006: * Returns the default value of a <code>URLConnection</code>'s
1007: * <code>useCaches</code> flag.
1008: * <p>
1009: * Ths default is "sticky", being a part of the static state of all
1010: * URLConnections. This flag applies to the next, and all following
1011: * URLConnections that are created.
1012: *
1013: * @return the default value of a <code>URLConnection</code>'s
1014: * <code>useCaches</code> flag.
1015: * @see #setDefaultUseCaches(boolean)
1016: */
1017: public boolean getDefaultUseCaches() {
1018: return defaultUseCaches;
1019: }
1020:
1021: /**
1022: * Sets the default value of the <code>useCaches</code> field to the
1023: * specified value.
1024: *
1025: * @param defaultusecaches the new value.
1026: * @see #getDefaultUseCaches()
1027: */
1028: public void setDefaultUseCaches(boolean defaultusecaches) {
1029: defaultUseCaches = defaultusecaches;
1030: }
1031:
1032: /**
1033: * Sets the general request property. If a property with the key already
1034: * exists, overwrite its value with the new value.
1035: *
1036: * <p> NOTE: HTTP requires all request properties which can
1037: * legally have multiple instances with the same key
1038: * to use a comma-seperated list syntax which enables multiple
1039: * properties to be appended into a single property.
1040: *
1041: * @param key the keyword by which the request is known
1042: * (e.g., "<code>accept</code>").
1043: * @param value the value associated with it.
1044: * @throws IllegalStateException if already connected
1045: * @throws NullPointerException if key is <CODE>null</CODE>
1046: * @see #getRequestProperty(java.lang.String)
1047: */
1048: public void setRequestProperty(String key, String value) {
1049: if (connected)
1050: throw new IllegalStateException("Already connected");
1051: if (key == null)
1052: throw new NullPointerException("key is null");
1053:
1054: if (requests == null)
1055: requests = new MessageHeader();
1056:
1057: requests.set(key, value);
1058: }
1059:
1060: /**
1061: * Adds a general request property specified by a
1062: * key-value pair. This method will not overwrite
1063: * existing values associated with the same key.
1064: *
1065: * @param key the keyword by which the request is known
1066: * (e.g., "<code>accept</code>").
1067: * @param value the value associated with it.
1068: * @throws IllegalStateException if already connected
1069: * @throws NullPointerException if key is null
1070: * @see #getRequestProperties()
1071: * @since 1.4
1072: */
1073: public void addRequestProperty(String key, String value) {
1074: if (connected)
1075: throw new IllegalStateException("Already connected");
1076: if (key == null)
1077: throw new NullPointerException("key is null");
1078:
1079: if (requests == null)
1080: requests = new MessageHeader();
1081:
1082: requests.add(key, value);
1083: }
1084:
1085: /**
1086: * Returns the value of the named general request property for this
1087: * connection.
1088: *
1089: * @param key the keyword by which the request is known (e.g., "accept").
1090: * @return the value of the named general request property for this
1091: * connection. If key is null, then null is returned.
1092: * @throws IllegalStateException if already connected
1093: * @see #setRequestProperty(java.lang.String, java.lang.String)
1094: */
1095: public String getRequestProperty(String key) {
1096: if (connected)
1097: throw new IllegalStateException("Already connected");
1098:
1099: if (requests == null)
1100: return null;
1101:
1102: return requests.findValue(key);
1103: }
1104:
1105: /**
1106: * Returns an unmodifiable Map of general request
1107: * properties for this connection. The Map keys
1108: * are Strings that represent the request-header
1109: * field names. Each Map value is a unmodifiable List
1110: * of Strings that represents the corresponding
1111: * field values.
1112: *
1113: * @return a Map of the general request properties for this connection.
1114: * @throws IllegalStateException if already connected
1115: * @since 1.4
1116: */
1117: public Map<String, List<String>> getRequestProperties() {
1118: if (connected)
1119: throw new IllegalStateException("Already connected");
1120:
1121: if (requests == null)
1122: return Collections.EMPTY_MAP;
1123:
1124: return requests.getHeaders(null);
1125: }
1126:
1127: /**
1128: * Sets the default value of a general request property. When a
1129: * <code>URLConnection</code> is created, it is initialized with
1130: * these properties.
1131: *
1132: * @param key the keyword by which the request is known
1133: * (e.g., "<code>accept</code>").
1134: * @param value the value associated with the key.
1135: *
1136: * @see java.net.URLConnection#setRequestProperty(java.lang.String,java.lang.String)
1137: *
1138: * @deprecated The instance specific setRequestProperty method
1139: * should be used after an appropriate instance of URLConnection
1140: * is obtained. Invoking this method will have no effect.
1141: *
1142: * @see #getDefaultRequestProperty(java.lang.String)
1143: */
1144: @Deprecated
1145: public static void setDefaultRequestProperty(String key,
1146: String value) {
1147: }
1148:
1149: /**
1150: * Returns the value of the default request property. Default request
1151: * properties are set for every connection.
1152: *
1153: * @param key the keyword by which the request is known (e.g., "accept").
1154: * @return the value of the default request property
1155: * for the specified key.
1156: *
1157: * @see java.net.URLConnection#getRequestProperty(java.lang.String)
1158: *
1159: * @deprecated The instance specific getRequestProperty method
1160: * should be used after an appropriate instance of URLConnection
1161: * is obtained.
1162: *
1163: * @see #setDefaultRequestProperty(java.lang.String, java.lang.String)
1164: */
1165: @Deprecated
1166: public static String getDefaultRequestProperty(String key) {
1167: return null;
1168: }
1169:
1170: /**
1171: * The ContentHandler factory.
1172: */
1173: static ContentHandlerFactory factory;
1174:
1175: /**
1176: * Sets the <code>ContentHandlerFactory</code> of an
1177: * application. It can be called at most once by an application.
1178: * <p>
1179: * The <code>ContentHandlerFactory</code> instance is used to
1180: * construct a content handler from a content type
1181: * <p>
1182: * If there is a security manager, this method first calls
1183: * the security manager's <code>checkSetFactory</code> method
1184: * to ensure the operation is allowed.
1185: * This could result in a SecurityException.
1186: *
1187: * @param fac the desired factory.
1188: * @exception Error if the factory has already been defined.
1189: * @exception SecurityException if a security manager exists and its
1190: * <code>checkSetFactory</code> method doesn't allow the operation.
1191: * @see java.net.ContentHandlerFactory
1192: * @see java.net.URLConnection#getContent()
1193: * @see SecurityManager#checkSetFactory
1194: */
1195: public static synchronized void setContentHandlerFactory(
1196: ContentHandlerFactory fac) {
1197: if (factory != null) {
1198: throw new Error("factory already defined");
1199: }
1200: SecurityManager security = System.getSecurityManager();
1201: if (security != null) {
1202: security.checkSetFactory();
1203: }
1204: factory = fac;
1205: }
1206:
1207: private static Hashtable handlers = new Hashtable();
1208: private static final ContentHandler UnknownContentHandlerP = new UnknownContentHandler();
1209:
1210: /**
1211: * Gets the Content Handler appropriate for this connection.
1212: * @param connection the connection to use.
1213: */
1214: synchronized ContentHandler getContentHandler()
1215: throws UnknownServiceException {
1216: String contentType = stripOffParameters(getContentType());
1217: ContentHandler handler = null;
1218: if (contentType == null)
1219: throw new UnknownServiceException("no content-type");
1220: try {
1221: handler = (ContentHandler) handlers.get(contentType);
1222: if (handler != null)
1223: return handler;
1224: } catch (Exception e) {
1225: }
1226:
1227: if (factory != null)
1228: handler = factory.createContentHandler(contentType);
1229: if (handler == null) {
1230: try {
1231: handler = lookupContentHandlerClassFor(contentType);
1232: } catch (Exception e) {
1233: e.printStackTrace();
1234: handler = UnknownContentHandlerP;
1235: }
1236: handlers.put(contentType, handler);
1237: }
1238: return handler;
1239: }
1240:
1241: /*
1242: * Media types are in the format: type/subtype*(; parameter).
1243: * For looking up the content handler, we should ignore those
1244: * parameters.
1245: */
1246: private String stripOffParameters(String contentType) {
1247: if (contentType == null)
1248: return null;
1249: int index = contentType.indexOf(';');
1250:
1251: if (index > 0)
1252: return contentType.substring(0, index);
1253: else
1254: return contentType;
1255: }
1256:
1257: private static final String contentClassPrefix = "sun.net.www.content";
1258: private static final String contentPathProp = "java.content.handler.pkgs";
1259:
1260: /**
1261: * Looks for a content handler in a user-defineable set of places.
1262: * By default it looks in sun.net.www.content, but users can define a
1263: * vertical-bar delimited set of class prefixes to search through in
1264: * addition by defining the java.content.handler.pkgs property.
1265: * The class name must be of the form:
1266: * <pre>
1267: * {package-prefix}.{major}.{minor}
1268: * e.g.
1269: * YoyoDyne.experimental.text.plain
1270: * </pre>
1271: */
1272: private ContentHandler lookupContentHandlerClassFor(
1273: String contentType) throws InstantiationException,
1274: IllegalAccessException, ClassNotFoundException {
1275: String contentHandlerClassName = typeToPackageName(contentType);
1276:
1277: String contentHandlerPkgPrefixes = getContentHandlerPkgPrefixes();
1278:
1279: StringTokenizer packagePrefixIter = new StringTokenizer(
1280: contentHandlerPkgPrefixes, "|");
1281:
1282: while (packagePrefixIter.hasMoreTokens()) {
1283: String packagePrefix = packagePrefixIter.nextToken().trim();
1284:
1285: try {
1286: String clsName = packagePrefix + "."
1287: + contentHandlerClassName;
1288: Class cls = null;
1289: try {
1290: cls = Class.forName(clsName);
1291: } catch (ClassNotFoundException e) {
1292: ClassLoader cl = ClassLoader.getSystemClassLoader();
1293: if (cl != null) {
1294: cls = cl.loadClass(clsName);
1295: }
1296: }
1297: if (cls != null) {
1298: ContentHandler handler = (ContentHandler) cls
1299: .newInstance();
1300: return handler;
1301: }
1302: } catch (Exception e) {
1303: }
1304: }
1305:
1306: return UnknownContentHandlerP;
1307: }
1308:
1309: /**
1310: * Utility function to map a MIME content type into an equivalent
1311: * pair of class name components. For example: "text/html" would
1312: * be returned as "text.html"
1313: */
1314: private String typeToPackageName(String contentType) {
1315: // make sure we canonicalize the class name: all lower case
1316: contentType = contentType.toLowerCase();
1317: int len = contentType.length();
1318: char nm[] = new char[len];
1319: contentType.getChars(0, len, nm, 0);
1320: for (int i = 0; i < len; i++) {
1321: char c = nm[i];
1322: if (c == '/') {
1323: nm[i] = '.';
1324: } else if (!('A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c
1325: && c <= '9')) {
1326: nm[i] = '_';
1327: }
1328: }
1329: return new String(nm);
1330: }
1331:
1332: /**
1333: * Returns a vertical bar separated list of package prefixes for potential
1334: * content handlers. Tries to get the java.content.handler.pkgs property
1335: * to use as a set of package prefixes to search. Whether or not
1336: * that property has been defined, the sun.net.www.content is always
1337: * the last one on the returned package list.
1338: */
1339: private String getContentHandlerPkgPrefixes() {
1340: String packagePrefixList = (String) AccessController
1341: .doPrivileged(new sun.security.action.GetPropertyAction(
1342: contentPathProp, ""));
1343:
1344: if (packagePrefixList != "") {
1345: packagePrefixList += "|";
1346: }
1347:
1348: return packagePrefixList + contentClassPrefix;
1349: }
1350:
1351: /**
1352: * Tries to determine the content type of an object, based
1353: * on the specified "file" component of a URL.
1354: * This is a convenience method that can be used by
1355: * subclasses that override the <code>getContentType</code> method.
1356: *
1357: * @param fname a filename.
1358: * @return a guess as to what the content type of the object is,
1359: * based upon its file name.
1360: * @see java.net.URLConnection#getContentType()
1361: */
1362: public static String guessContentTypeFromName(String fname) {
1363: return getFileNameMap().getContentTypeFor(fname);
1364: }
1365:
1366: /**
1367: * Tries to determine the type of an input stream based on the
1368: * characters at the beginning of the input stream. This method can
1369: * be used by subclasses that override the
1370: * <code>getContentType</code> method.
1371: * <p>
1372: * Ideally, this routine would not be needed. But many
1373: * <code>http</code> servers return the incorrect content type; in
1374: * addition, there are many nonstandard extensions. Direct inspection
1375: * of the bytes to determine the content type is often more accurate
1376: * than believing the content type claimed by the <code>http</code> server.
1377: *
1378: * @param is an input stream that supports marks.
1379: * @return a guess at the content type, or <code>null</code> if none
1380: * can be determined.
1381: * @exception IOException if an I/O error occurs while reading the
1382: * input stream.
1383: * @see java.io.InputStream#mark(int)
1384: * @see java.io.InputStream#markSupported()
1385: * @see java.net.URLConnection#getContentType()
1386: */
1387: static public String guessContentTypeFromStream(InputStream is)
1388: throws IOException {
1389: // If we can't read ahead safely, just give up on guessing
1390: if (!is.markSupported())
1391: return null;
1392:
1393: is.mark(12);
1394: int c1 = is.read();
1395: int c2 = is.read();
1396: int c3 = is.read();
1397: int c4 = is.read();
1398: int c5 = is.read();
1399: int c6 = is.read();
1400: int c7 = is.read();
1401: int c8 = is.read();
1402: int c9 = is.read();
1403: int c10 = is.read();
1404: int c11 = is.read();
1405: is.reset();
1406:
1407: if (c1 == 0xCA && c2 == 0xFE && c3 == 0xBA && c4 == 0xBE) {
1408: return "application/java-vm";
1409: }
1410:
1411: if (c1 == 0xAC && c2 == 0xED) {
1412: // next two bytes are version number, currently 0x00 0x05
1413: return "application/x-java-serialized-object";
1414: }
1415:
1416: if (c1 == '<') {
1417: if (c2 == '!'
1418: || ((c2 == 'h'
1419: && (c3 == 't' && c4 == 'm' && c5 == 'l' || c3 == 'e'
1420: && c4 == 'a' && c5 == 'd') || (c2 == 'b'
1421: && c3 == 'o' && c4 == 'd' && c5 == 'y')))
1422: || ((c2 == 'H'
1423: && (c3 == 'T' && c4 == 'M' && c5 == 'L' || c3 == 'E'
1424: && c4 == 'A' && c5 == 'D') || (c2 == 'B'
1425: && c3 == 'O' && c4 == 'D' && c5 == 'Y')))) {
1426: return "text/html";
1427: }
1428:
1429: if (c2 == '?' && c3 == 'x' && c4 == 'm' && c5 == 'l'
1430: && c6 == ' ') {
1431: return "application/xml";
1432: }
1433: }
1434:
1435: // big and little endian UTF-16 encodings, with byte order mark
1436: if (c1 == 0xfe && c2 == 0xff) {
1437: if (c3 == 0 && c4 == '<' && c5 == 0 && c6 == '?' && c7 == 0
1438: && c8 == 'x') {
1439: return "application/xml";
1440: }
1441: }
1442:
1443: if (c1 == 0xff && c2 == 0xfe) {
1444: if (c3 == '<' && c4 == 0 && c5 == '?' && c6 == 0
1445: && c7 == 'x' && c8 == 0) {
1446: return "application/xml";
1447: }
1448: }
1449:
1450: if (c1 == 'G' && c2 == 'I' && c3 == 'F' && c4 == '8') {
1451: return "image/gif";
1452: }
1453:
1454: if (c1 == '#' && c2 == 'd' && c3 == 'e' && c4 == 'f') {
1455: return "image/x-bitmap";
1456: }
1457:
1458: if (c1 == '!' && c2 == ' ' && c3 == 'X' && c4 == 'P'
1459: && c5 == 'M' && c6 == '2') {
1460: return "image/x-pixmap";
1461: }
1462:
1463: if (c1 == 137 && c2 == 80 && c3 == 78 && c4 == 71 && c5 == 13
1464: && c6 == 10 && c7 == 26 && c8 == 10) {
1465: return "image/png";
1466: }
1467:
1468: if (c1 == 0xFF && c2 == 0xD8 && c3 == 0xFF) {
1469: if (c4 == 0xE0) {
1470: return "image/jpeg";
1471: }
1472:
1473: /**
1474: * File format used by digital cameras to store images.
1475: * Exif Format can be read by any application supporting
1476: * JPEG. Exif Spec can be found at:
1477: * http://www.pima.net/standards/it10/PIMA15740/Exif_2-1.PDF
1478: */
1479: if ((c4 == 0xE1)
1480: && (c7 == 'E' && c8 == 'x' && c9 == 'i'
1481: && c10 == 'f' && c11 == 0)) {
1482: return "image/jpeg";
1483: }
1484:
1485: if (c4 == 0xEE) {
1486: return "image/jpg";
1487: }
1488: }
1489:
1490: if (c1 == 0xD0 && c2 == 0xCF && c3 == 0x11 && c4 == 0xE0
1491: && c5 == 0xA1 && c6 == 0xB1 && c7 == 0x1A && c8 == 0xE1) {
1492:
1493: /* Above is signature of Microsoft Structured Storage.
1494: * Below this, could have tests for various SS entities.
1495: * For now, just test for FlashPix.
1496: */
1497: if (checkfpx(is)) {
1498: return "image/vnd.fpx";
1499: }
1500: }
1501:
1502: if (c1 == 0x2E && c2 == 0x73 && c3 == 0x6E && c4 == 0x64) {
1503: return "audio/basic"; // .au format, big endian
1504: }
1505:
1506: if (c1 == 0x64 && c2 == 0x6E && c3 == 0x73 && c4 == 0x2E) {
1507: return "audio/basic"; // .au format, little endian
1508: }
1509:
1510: if (c1 == 'R' && c2 == 'I' && c3 == 'F' && c4 == 'F') {
1511: /* I don't know if this is official but evidence
1512: * suggests that .wav files start with "RIFF" - brown
1513: */
1514: return "audio/x-wav";
1515: }
1516: return null;
1517: }
1518:
1519: /**
1520: * Check for FlashPix image data in InputStream is. Return true if
1521: * the stream has FlashPix data, false otherwise. Before calling this
1522: * method, the stream should have already been checked to be sure it
1523: * contains Microsoft Structured Storage data.
1524: */
1525: static private boolean checkfpx(InputStream is) throws IOException {
1526:
1527: /* Test for FlashPix image data in Microsoft Structured Storage format.
1528: * In general, should do this with calls to an SS implementation.
1529: * Lacking that, need to dig via offsets to get to the FlashPix
1530: * ClassID. Details:
1531: *
1532: * Offset to Fpx ClsID from beginning of stream should be:
1533: *
1534: * FpxClsidOffset = rootEntryOffset + clsidOffset
1535: *
1536: * where: clsidOffset = 0x50.
1537: * rootEntryOffset = headerSize + sectorSize*sectDirStart
1538: * + 128*rootEntryDirectory
1539: *
1540: * where: headerSize = 0x200 (always)
1541: * sectorSize = 2 raised to power of uSectorShift,
1542: * which is found in the header at
1543: * offset 0x1E.
1544: * sectDirStart = found in the header at offset 0x30.
1545: * rootEntryDirectory = in general, should search for
1546: * directory labelled as root.
1547: * We will assume value of 0 (i.e.,
1548: * rootEntry is in first directory)
1549: */
1550:
1551: // Mark the stream so we can reset it. 0x100 is enough for the first
1552: // few reads, but the mark will have to be reset and set again once
1553: // the offset to the root directory entry is computed. That offset
1554: // can be very large and isn't know until the stream has been read from
1555: is.mark(0x100);
1556:
1557: // Get the byte ordering located at 0x1E. 0xFE is Intel,
1558: // 0xFF is other
1559: long toSkip = (long) 0x1C;
1560: long posn;
1561:
1562: if ((posn = skipForward(is, toSkip)) < toSkip) {
1563: is.reset();
1564: return false;
1565: }
1566:
1567: int c[] = new int[16];
1568: if (readBytes(c, 2, is) < 0) {
1569: is.reset();
1570: return false;
1571: }
1572:
1573: int byteOrder = c[0];
1574:
1575: posn += 2;
1576: int uSectorShift;
1577: if (readBytes(c, 2, is) < 0) {
1578: is.reset();
1579: return false;
1580: }
1581:
1582: if (byteOrder == 0xFE) {
1583: uSectorShift = c[0];
1584: uSectorShift += c[1] << 8;
1585: } else {
1586: uSectorShift = c[0] << 8;
1587: uSectorShift += c[1];
1588: }
1589:
1590: posn += 2;
1591: toSkip = (long) 0x30 - posn;
1592: long skipped = 0;
1593: if ((skipped = skipForward(is, toSkip)) < toSkip) {
1594: is.reset();
1595: return false;
1596: }
1597: posn += skipped;
1598:
1599: if (readBytes(c, 4, is) < 0) {
1600: is.reset();
1601: return false;
1602: }
1603:
1604: int sectDirStart;
1605: if (byteOrder == 0xFE) {
1606: sectDirStart = c[0];
1607: sectDirStart += c[1] << 8;
1608: sectDirStart += c[2] << 16;
1609: sectDirStart += c[3] << 24;
1610: } else {
1611: sectDirStart = c[0] << 24;
1612: sectDirStart += c[1] << 16;
1613: sectDirStart += c[2] << 8;
1614: sectDirStart += c[3];
1615: }
1616: posn += 4;
1617: is.reset(); // Reset back to the beginning
1618:
1619: toSkip = (long) 0x200 + (long) ((int) 1 << uSectorShift)
1620: * sectDirStart + (long) 0x50;
1621:
1622: // Sanity check!
1623: if (toSkip < 0) {
1624: return false;
1625: }
1626:
1627: /*
1628: * How far can we skip? Is there any performance problem here?
1629: * This skip can be fairly long, at least 0x4c650 in at least
1630: * one case. Have to assume that the skip will fit in an int.
1631: * Leave room to read whole root dir
1632: */
1633: is.mark((int) toSkip + 0x30);
1634:
1635: if ((skipForward(is, toSkip)) < toSkip) {
1636: is.reset();
1637: return false;
1638: }
1639:
1640: /* should be at beginning of ClassID, which is as follows
1641: * (in Intel byte order):
1642: * 00 67 61 56 54 C1 CE 11 85 53 00 AA 00 A1 F9 5B
1643: *
1644: * This is stored from Windows as long,short,short,char[8]
1645: * so for byte order changes, the order only changes for
1646: * the first 8 bytes in the ClassID.
1647: *
1648: * Test against this, ignoring second byte (Intel) since
1649: * this could change depending on part of Fpx file we have.
1650: */
1651:
1652: if (readBytes(c, 16, is) < 0) {
1653: is.reset();
1654: return false;
1655: }
1656:
1657: // intel byte order
1658: if (byteOrder == 0xFE && c[0] == 0x00 && c[2] == 0x61
1659: && c[3] == 0x56 && c[4] == 0x54 && c[5] == 0xC1
1660: && c[6] == 0xCE && c[7] == 0x11 && c[8] == 0x85
1661: && c[9] == 0x53 && c[10] == 0x00 && c[11] == 0xAA
1662: && c[12] == 0x00 && c[13] == 0xA1 && c[14] == 0xF9
1663: && c[15] == 0x5B) {
1664: is.reset();
1665: return true;
1666: }
1667:
1668: // non-intel byte order
1669: else if (c[3] == 0x00 && c[1] == 0x61 && c[0] == 0x56
1670: && c[5] == 0x54 && c[4] == 0xC1 && c[7] == 0xCE
1671: && c[6] == 0x11 && c[8] == 0x85 && c[9] == 0x53
1672: && c[10] == 0x00 && c[11] == 0xAA && c[12] == 0x00
1673: && c[13] == 0xA1 && c[14] == 0xF9 && c[15] == 0x5B) {
1674: is.reset();
1675: return true;
1676: }
1677: is.reset();
1678: return false;
1679: }
1680:
1681: /**
1682: * Tries to read the specified number of bytes from the stream
1683: * Returns -1, If EOF is reached before len bytes are read, returns 0
1684: * otherwise
1685: */
1686: static private int readBytes(int c[], int len, InputStream is)
1687: throws IOException {
1688:
1689: byte buf[] = new byte[len];
1690: if (is.read(buf, 0, len) < len) {
1691: return -1;
1692: }
1693:
1694: // fill the passed in int array
1695: for (int i = 0; i < len; i++) {
1696: c[i] = buf[i] & 0xff;
1697: }
1698: return 0;
1699: }
1700:
1701: /**
1702: * Skips through the specified number of bytes from the stream
1703: * until either EOF is reached, or the specified
1704: * number of bytes have been skipped
1705: */
1706: static private long skipForward(InputStream is, long toSkip)
1707: throws IOException {
1708:
1709: long eachSkip = 0;
1710: long skipped = 0;
1711:
1712: while (skipped != toSkip) {
1713: eachSkip = is.skip(toSkip - skipped);
1714:
1715: // check if EOF is reached
1716: if (eachSkip <= 0) {
1717: if (is.read() == -1) {
1718: return skipped;
1719: } else {
1720: skipped++;
1721: }
1722: }
1723: skipped += eachSkip;
1724: }
1725: return skipped;
1726: }
1727:
1728: }
1729:
1730: class UnknownContentHandler extends ContentHandler {
1731: public Object getContent(URLConnection uc) throws IOException {
1732: return uc.getInputStream();
1733: }
1734: }
|