< prev index next >
src/java.base/share/classes/java/net/URLConnection.java
Print this page
@@ -26,12 +26,16 @@
package java.net;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.security.PrivilegedAction;
import java.util.Hashtable;
import java.util.Date;
+import java.util.Iterator;
+import java.util.ServiceConfigurationError;
+import java.util.ServiceLoader;
import java.util.StringTokenizer;
import java.util.Collections;
import java.util.Map;
import java.util.List;
import java.security.Permission;
@@ -105,11 +109,11 @@
* <li>{@code getContentEncoding}
* <li>{@code getContentLength}
* <li>{@code getContentType}
* <li>{@code getDate}
* <li>{@code getExpiration}
- * <li>{@code getLastModifed}
+ * <li>{@code getLastModified}
* </ul>
* <p>
* provide convenient access to these fields. The
* {@code getContentType} method is used by the
* {@code getContent} method to determine the type of the remote
@@ -693,20 +697,34 @@
* Retrieves the contents of this URL connection.
* <p>
* This method first determines the content type of the object by
* calling the {@code getContentType} method. If this is
* the first time that the application has seen that specific content
- * type, a content handler for that content type is created:
+ * type, a content handler for that content type is created.
+ * <p> This is done as follows:
* <ol>
* <li>If the application has set up a content handler factory instance
* using the {@code setContentHandlerFactory} method, the
* {@code createContentHandler} method of that instance is called
* with the content type as an argument; the result is a content
* handler for that content type.
- * <li>If no content handler factory has yet been set up, or if the
- * factory's {@code createContentHandler} method returns
- * {@code null}, then this method tries to load a content handler
+ * <li>If no {@code ContentHandlerFactory} has yet been set up,
+ * or if the factory's {@code createContentHandler} method
+ * returns {@code null}, then the {@linkplain java.util.ServiceLoader
+ * ServiceLoader} mechanism is used to locate {@linkplain
+ * java.net.ContentHandlerFactory ContentHandlerFactory}
+ * implementations using the system class
+ * loader. The order that factories are located is implementation
+ * specific, and an implementation is free to cache the located
+ * factories. A {@linkplain java.util.ServiceConfigurationError
+ * ServiceConfigurationError}, {@code Error} or {@code RuntimeException}
+ * thrown from the {@code createContentHandler}, if encountered, will
+ * be propagated to the calling thread. The {@code
+ * createContentHandler} method of each factory, if instantiated, is
+ * invoked, with the content type, until a factory returns non-null,
+ * or all factories have been exhausted.
+ * <li>Failing that, this method tries to load a content handler
* class as defined by {@link java.net.ContentHandler ContentHandler}.
* If the class does not exist, or is not a subclass of {@code
* ContentHandler}, then an {@code UnknownServiceException} is thrown.
* </ol>
*
@@ -853,12 +871,11 @@
* @throws IllegalStateException if already connected
* @see java.net.URLConnection#doInput
* @see #getDoInput()
*/
public void setDoInput(boolean doinput) {
- if (connected)
- throw new IllegalStateException("Already connected");
+ checkConnected();
doInput = doinput;
}
/**
* Returns the value of this {@code URLConnection}'s
@@ -883,12 +900,11 @@
* @param dooutput the new value.
* @throws IllegalStateException if already connected
* @see #getDoOutput()
*/
public void setDoOutput(boolean dooutput) {
- if (connected)
- throw new IllegalStateException("Already connected");
+ checkConnected();
doOutput = dooutput;
}
/**
* Returns the value of this {@code URLConnection}'s
@@ -909,12 +925,11 @@
* @param allowuserinteraction the new value.
* @throws IllegalStateException if already connected
* @see #getAllowUserInteraction()
*/
public void setAllowUserInteraction(boolean allowuserinteraction) {
- if (connected)
- throw new IllegalStateException("Already connected");
+ checkConnected();
allowUserInteraction = allowuserinteraction;
}
/**
* Returns the value of the {@code allowUserInteraction} field for
@@ -972,12 +987,11 @@
* or not to allow caching
* @throws IllegalStateException if already connected
* @see #getUseCaches()
*/
public void setUseCaches(boolean usecaches) {
- if (connected)
- throw new IllegalStateException("Already connected");
+ checkConnected();
useCaches = usecaches;
}
/**
* Returns the value of this {@code URLConnection}'s
@@ -998,12 +1012,11 @@
* @param ifmodifiedsince the new value.
* @throws IllegalStateException if already connected
* @see #getIfModifiedSince()
*/
public void setIfModifiedSince(long ifmodifiedsince) {
- if (connected)
- throw new IllegalStateException("Already connected");
+ checkConnected();
ifModifiedSince = ifmodifiedsince;
}
/**
* Returns the value of this object's {@code ifModifiedSince} field.
@@ -1053,16 +1066,15 @@
*
* @param key the keyword by which the request is known
* (e.g., "{@code Accept}").
* @param value the value associated with it.
* @throws IllegalStateException if already connected
- * @throws NullPointerException if key is <CODE>null</CODE>
+ * @throws NullPointerException if key is {@code null}
* @see #getRequestProperty(java.lang.String)
*/
public void setRequestProperty(String key, String value) {
- if (connected)
- throw new IllegalStateException("Already connected");
+ checkConnected();
if (key == null)
throw new NullPointerException ("key is null");
if (requests == null)
requests = new MessageHeader();
@@ -1082,12 +1094,11 @@
* @throws NullPointerException if key is null
* @see #getRequestProperties()
* @since 1.4
*/
public void addRequestProperty(String key, String value) {
- if (connected)
- throw new IllegalStateException("Already connected");
+ checkConnected();
if (key == null)
throw new NullPointerException ("key is null");
if (requests == null)
requests = new MessageHeader();
@@ -1105,12 +1116,11 @@
* connection. If key is null, then null is returned.
* @throws IllegalStateException if already connected
* @see #setRequestProperty(java.lang.String, java.lang.String)
*/
public String getRequestProperty(String key) {
- if (connected)
- throw new IllegalStateException("Already connected");
+ checkConnected();
if (requests == null)
return null;
return requests.findValue(key);
@@ -1127,12 +1137,11 @@
* @return a Map of the general request properties for this connection.
* @throws IllegalStateException if already connected
* @since 1.4
*/
public Map<String,List<String>> getRequestProperties() {
- if (connected)
- throw new IllegalStateException("Already connected");
+ checkConnected();
if (requests == null)
return Collections.emptyMap();
return requests.getHeaders(null);
@@ -1181,11 +1190,11 @@
}
/**
* The ContentHandler factory.
*/
- static ContentHandlerFactory factory;
+ private static volatile ContentHandlerFactory factory;
/**
* Sets the {@code ContentHandlerFactory} of an
* application. It can be called at most once by an application.
* <p>
@@ -1214,41 +1223,49 @@
security.checkSetFactory();
}
factory = fac;
}
- private static Hashtable<String, ContentHandler> handlers = new Hashtable<>();
+ private static final Hashtable<String, ContentHandler> handlers = new Hashtable<>();
/**
* Gets the Content Handler appropriate for this connection.
*/
- synchronized ContentHandler getContentHandler()
- throws UnknownServiceException
- {
+ private ContentHandler getContentHandler() throws UnknownServiceException {
String contentType = stripOffParameters(getContentType());
- ContentHandler handler = null;
- if (contentType == null)
+ if (contentType == null) {
throw new UnknownServiceException("no content-type");
- try {
- handler = handlers.get(contentType);
+ }
+
+ ContentHandler handler = handlers.get(contentType);
if (handler != null)
return handler;
- } catch(Exception e) {
- }
- if (factory != null)
+ if (factory != null) {
handler = factory.createContentHandler(contentType);
- if (handler == null) {
+ if (handler != null)
+ return handler;
+ }
+
+ handler = lookupContentHandlerViaProvider(contentType);
+
+ if (handler != null) {
+ ContentHandler h = handlers.putIfAbsent(contentType, handler);
+ return h != null ? h : handler;
+ }
+
try {
handler = lookupContentHandlerClassFor(contentType);
- } catch(Exception e) {
+ } catch (Exception e) {
e.printStackTrace();
handler = UnknownContentHandler.INSTANCE;
}
- handlers.put(contentType, handler);
- }
- return handler;
+
+ assert handler != null;
+
+ ContentHandler h = handlers.putIfAbsent(contentType, handler);
+ return h != null ? h : handler;
}
/*
* Media types are in the format: type/subtype*(; parameter).
* For looking up the content handler, we should ignore those
@@ -1268,26 +1285,25 @@
private static final String contentClassPrefix = "sun.net.www.content";
private static final String contentPathProp = "java.content.handler.pkgs";
/**
- * Looks for a content handler in a user-defineable set of places.
- * By default it looks in sun.net.www.content, but users can define a
- * vertical-bar delimited set of class prefixes to search through in
- * addition by defining the java.content.handler.pkgs property.
+ * Looks for a content handler in a user-definable set of places.
+ * By default it looks in {@value #contentClassPrefix}, but users can define
+ * a vertical-bar delimited set of class prefixes to search through in
+ * addition by defining the {@value #contentPathProp} property.
* The class name must be of the form:
* <pre>
* {package-prefix}.{major}.{minor}
* e.g.
* YoyoDyne.experimental.text.plain
* </pre>
*/
- private ContentHandler lookupContentHandlerClassFor(String contentType)
- throws InstantiationException, IllegalAccessException, ClassNotFoundException {
+ private ContentHandler lookupContentHandlerClassFor(String contentType) {
String contentHandlerClassName = typeToPackageName(contentType);
- String contentHandlerPkgPrefixes =getContentHandlerPkgPrefixes();
+ String contentHandlerPkgPrefixes = getContentHandlerPkgPrefixes();
StringTokenizer packagePrefixIter =
new StringTokenizer(contentHandlerPkgPrefixes, "|");
while (packagePrefixIter.hasMoreTokens()) {
@@ -1303,21 +1319,50 @@
if (cl != null) {
cls = cl.loadClass(clsName);
}
}
if (cls != null) {
- ContentHandler handler =
- (ContentHandler)cls.newInstance();
- return handler;
- }
- } catch(Exception e) {
+ return (ContentHandler) cls.newInstance();
}
+ } catch(Exception ignored) { }
}
return UnknownContentHandler.INSTANCE;
}
+ private ContentHandler lookupContentHandlerViaProvider(String contentType) {
+ return AccessController.doPrivileged(
+ new PrivilegedAction<>() {
+ @Override
+ public ContentHandler run() {
+ ClassLoader cl = ClassLoader.getSystemClassLoader();
+ ServiceLoader<ContentHandlerFactory> sl =
+ ServiceLoader.load(ContentHandlerFactory.class, cl);
+
+ Iterator<ContentHandlerFactory> iterator = sl.iterator();
+
+ ContentHandler handler = null;
+ while (iterator.hasNext()) {
+ ContentHandlerFactory f;
+ try {
+ f = iterator.next();
+ } catch (ServiceConfigurationError e) {
+ if (e.getCause() instanceof SecurityException) {
+ continue;
+ }
+ throw e;
+ }
+ handler = f.createContentHandler(contentType);
+ if (handler != null) {
+ break;
+ }
+ }
+ return handler;
+ }
+ });
+ }
+
/**
* Utility function to map a MIME content type into an equivalent
* pair of class name components. For example: "text/html" would
* be returned as "text.html"
*/
@@ -1343,12 +1388,12 @@
/**
* Returns a vertical bar separated list of package prefixes for potential
* content handlers. Tries to get the java.content.handler.pkgs property
* to use as a set of package prefixes to search. Whether or not
- * that property has been defined, the sun.net.www.content is always
- * the last one on the returned package list.
+ * that property has been defined, the {@value #contentClassPrefix}
+ * is always the last one on the returned package list.
*/
private String getContentHandlerPkgPrefixes() {
String packagePrefixList = AccessController.doPrivileged(
new sun.security.action.GetPropertyAction(contentPathProp, ""));
@@ -1762,13 +1807,16 @@
skipped += eachSkip;
}
return skipped;
}
+ private void checkConnected() {
+ if (connected)
+ throw new IllegalStateException("Already connected");
+ }
}
-
class UnknownContentHandler extends ContentHandler {
static final ContentHandler INSTANCE = new UnknownContentHandler();
public Object getContent(URLConnection uc) throws IOException {
return uc.getInputStream();
< prev index next >