8005954: JAXP Plugability Layer should use java.util.ServiceLoader

This fix replaces manual processing of files under META-INF/services in JAXP factories by calls to java.util.ServiceLoader.

Reviewed-by: alanb, joehw, mchung
This commit is contained in:
Daniel Fuchs 2013-04-17 15:23:19 +02:00
parent b10f2efea3
commit 9a94591b8e
16 changed files with 1829 additions and 2136 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2004, 2006, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -26,14 +26,12 @@
package javax.xml.datatype; package javax.xml.datatype;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.security.AccessController;
import java.io.IOException; import java.security.PrivilegedAction;
import java.io.InputStream; import java.util.Iterator;
import java.util.Properties; import java.util.Properties;
import java.io.BufferedReader; import java.util.ServiceConfigurationError;
import java.io.InputStreamReader; import java.util.ServiceLoader;
import java.net.URL;
/** /**
* <p>Implements pluggable Datatypes.</p> * <p>Implements pluggable Datatypes.</p>
@ -54,19 +52,19 @@ class FactoryFinder {
/** /**
* Cache for properties in java.home/lib/jaxp.properties * Cache for properties in java.home/lib/jaxp.properties
*/ */
static Properties cacheProps = new Properties(); private final static Properties cacheProps = new Properties();
/** /**
* Flag indicating if properties from java.home/lib/jaxp.properties * Flag indicating if properties from java.home/lib/jaxp.properties
* have been cached. * have been cached.
*/ */
static volatile boolean firstTime = true; private static volatile boolean firstTime = true;
/** /**
* Security support class use to check access control before * Security support class use to check access control before
* getting certain system resources. * getting certain system resources.
*/ */
static SecuritySupport ss = new SecuritySupport(); private final static SecuritySupport ss = new SecuritySupport();
// Define system property "jaxp.debug" to get output // Define system property "jaxp.debug" to get output
static { static {
@ -99,31 +97,31 @@ class FactoryFinder {
* *
* Use bootstrap classLoader if cl = null and useBSClsLoader is true * Use bootstrap classLoader if cl = null and useBSClsLoader is true
*/ */
static private Class getProviderClass(String className, ClassLoader cl, static private Class<?> getProviderClass(String className, ClassLoader cl,
boolean doFallback, boolean useBSClsLoader) throws ClassNotFoundException boolean doFallback, boolean useBSClsLoader) throws ClassNotFoundException
{ {
try { try {
if (cl == null) { if (cl == null) {
if (useBSClsLoader) { if (useBSClsLoader) {
return Class.forName(className, true, FactoryFinder.class.getClassLoader()); return Class.forName(className, false, FactoryFinder.class.getClassLoader());
} else { } else {
cl = ss.getContextClassLoader(); cl = ss.getContextClassLoader();
if (cl == null) { if (cl == null) {
throw new ClassNotFoundException(); throw new ClassNotFoundException();
} }
else { else {
return cl.loadClass(className); return Class.forName(className, false, cl);
} }
} }
} }
else { else {
return cl.loadClass(className); return Class.forName(className, false, cl);
} }
} }
catch (ClassNotFoundException e1) { catch (ClassNotFoundException e1) {
if (doFallback) { if (doFallback) {
// Use current class loader - should always be bootstrap CL // Use current class loader - should always be bootstrap CL
return Class.forName(className, true, FactoryFinder.class.getClassLoader()); return Class.forName(className, false, FactoryFinder.class.getClassLoader());
} }
else { else {
throw e1; throw e1;
@ -135,6 +133,9 @@ class FactoryFinder {
* Create an instance of a class. Delegates to method * Create an instance of a class. Delegates to method
* <code>getProviderClass()</code> in order to load the class. * <code>getProviderClass()</code> in order to load the class.
* *
* @param type Base class / Service interface of the factory to
* instantiate.
*
* @param className Name of the concrete class corresponding to the * @param className Name of the concrete class corresponding to the
* service provider * service provider
* *
@ -144,16 +145,19 @@ class FactoryFinder {
* @param doFallback True if the current ClassLoader should be tried as * @param doFallback True if the current ClassLoader should be tried as
* a fallback if the class is not found using cl * a fallback if the class is not found using cl
*/ */
static Object newInstance(String className, ClassLoader cl, boolean doFallback) static <T> T newInstance(Class<T> type, String className, ClassLoader cl, boolean doFallback)
throws ConfigurationError throws DatatypeConfigurationException
{ {
return newInstance(className, cl, doFallback, false); return newInstance(type, className, cl, doFallback, false);
} }
/** /**
* Create an instance of a class. Delegates to method * Create an instance of a class. Delegates to method
* <code>getProviderClass()</code> in order to load the class. * <code>getProviderClass()</code> in order to load the class.
* *
* @param type Base class / Service interface of the factory to
* instantiate.
*
* @param className Name of the concrete class corresponding to the * @param className Name of the concrete class corresponding to the
* service provider * service provider
* *
@ -166,9 +170,12 @@ class FactoryFinder {
* @param useBSClsLoader True if cl=null actually meant bootstrap classLoader. This parameter * @param useBSClsLoader True if cl=null actually meant bootstrap classLoader. This parameter
* is needed since DocumentBuilderFactory/SAXParserFactory defined null as context classLoader. * is needed since DocumentBuilderFactory/SAXParserFactory defined null as context classLoader.
*/ */
static Object newInstance(String className, ClassLoader cl, boolean doFallback, boolean useBSClsLoader) static <T> T newInstance(Class<T> type, String className, ClassLoader cl,
throws ConfigurationError boolean doFallback, boolean useBSClsLoader)
throws DatatypeConfigurationException
{ {
assert type != null;
// make sure we have access to restricted packages // make sure we have access to restricted packages
if (System.getSecurityManager() != null) { if (System.getSecurityManager() != null) {
if (className != null && className.startsWith(DEFAULT_PACKAGE)) { if (className != null && className.startsWith(DEFAULT_PACKAGE)) {
@ -178,20 +185,23 @@ class FactoryFinder {
} }
try { try {
Class providerClass = getProviderClass(className, cl, doFallback, useBSClsLoader); Class<?> providerClass = getProviderClass(className, cl, doFallback, useBSClsLoader);
if (!type.isAssignableFrom(providerClass)) {
throw new ClassCastException(className + " cannot be cast to " + type.getName());
}
Object instance = providerClass.newInstance(); Object instance = providerClass.newInstance();
if (debug) { // Extra check to avoid computing cl strings if (debug) { // Extra check to avoid computing cl strings
dPrint("created new instance of " + providerClass + dPrint("created new instance of " + providerClass +
" using ClassLoader: " + cl); " using ClassLoader: " + cl);
} }
return instance; return type.cast(instance);
} }
catch (ClassNotFoundException x) { catch (ClassNotFoundException x) {
throw new ConfigurationError( throw new DatatypeConfigurationException(
"Provider " + className + " not found", x); "Provider " + className + " not found", x);
} }
catch (Exception x) { catch (Exception x) {
throw new ConfigurationError( throw new DatatypeConfigurationException(
"Provider " + className + " could not be instantiated: " + x, "Provider " + className + " could not be instantiated: " + x,
x); x);
} }
@ -202,16 +212,17 @@ class FactoryFinder {
* entry point. * entry point.
* @return Class object of factory, never null * @return Class object of factory, never null
* *
* @param factoryId Name of the factory to find, same as * @param type Base class / Service interface of the
* a property name * factory to find.
* @param fallbackClassName Implementation class name, if nothing else * @param fallbackClassName Implementation class name, if nothing else
* is found. Use null to mean no fallback. * is found. Use null to mean no fallback.
* *
* Package private so this code can be shared. * Package private so this code can be shared.
*/ */
static Object find(String factoryId, String fallbackClassName) static <T> T find(Class<T> type, String fallbackClassName)
throws ConfigurationError throws DatatypeConfigurationException
{ {
final String factoryId = type.getName();
dPrint("find factoryId =" + factoryId); dPrint("find factoryId =" + factoryId);
// Use the system property first // Use the system property first
@ -219,7 +230,7 @@ class FactoryFinder {
String systemProp = ss.getSystemProperty(factoryId); String systemProp = ss.getSystemProperty(factoryId);
if (systemProp != null) { if (systemProp != null) {
dPrint("found system property, value=" + systemProp); dPrint("found system property, value=" + systemProp);
return newInstance(systemProp, null, true); return newInstance(type, systemProp, null, true);
} }
} }
catch (SecurityException se) { catch (SecurityException se) {
@ -228,7 +239,6 @@ class FactoryFinder {
// try to read from $java.home/lib/jaxp.properties // try to read from $java.home/lib/jaxp.properties
try { try {
String factoryClassName = null;
if (firstTime) { if (firstTime) {
synchronized (cacheProps) { synchronized (cacheProps) {
if (firstTime) { if (firstTime) {
@ -243,11 +253,11 @@ class FactoryFinder {
} }
} }
} }
factoryClassName = cacheProps.getProperty(factoryId); final String factoryClassName = cacheProps.getProperty(factoryId);
if (factoryClassName != null) { if (factoryClassName != null) {
dPrint("found in $java.home/jaxp.properties, value=" + factoryClassName); dPrint("found in $java.home/jaxp.properties, value=" + factoryClassName);
return newInstance(factoryClassName, null, true); return newInstance(type, factoryClassName, null, true);
} }
} }
catch (Exception ex) { catch (Exception ex) {
@ -255,112 +265,46 @@ class FactoryFinder {
} }
// Try Jar Service Provider Mechanism // Try Jar Service Provider Mechanism
Object provider = findJarServiceProvider(factoryId); final T provider = findServiceProvider(type);
if (provider != null) { if (provider != null) {
return provider; return provider;
} }
if (fallbackClassName == null) { if (fallbackClassName == null) {
throw new ConfigurationError( throw new DatatypeConfigurationException(
"Provider for " + factoryId + " cannot be found", null); "Provider for " + factoryId + " cannot be found");
} }
dPrint("loaded from fallback value: " + fallbackClassName); dPrint("loaded from fallback value: " + fallbackClassName);
return newInstance(fallbackClassName, null, true); return newInstance(type, fallbackClassName, null, true);
} }
/* /*
* Try to find provider using Jar Service Provider Mechanism * Try to find provider using the ServiceLoader API
*
* @param type Base class / Service interface of the factory to find.
* *
* @return instance of provider class if found or null * @return instance of provider class if found or null
*/ */
private static Object findJarServiceProvider(String factoryId) private static <T> T findServiceProvider(final Class<T> type)
throws ConfigurationError throws DatatypeConfigurationException
{ {
String serviceId = "META-INF/services/" + factoryId;
InputStream is = null;
// First try the Context ClassLoader
ClassLoader cl = ss.getContextClassLoader();
boolean useBSClsLoader = false;
if (cl != null) {
is = ss.getResourceAsStream(cl, serviceId);
// If no provider found then try the current ClassLoader
if (is == null) {
cl = FactoryFinder.class.getClassLoader();
is = ss.getResourceAsStream(cl, serviceId);
useBSClsLoader = true;
}
} else {
// No Context ClassLoader, try the current ClassLoader
cl = FactoryFinder.class.getClassLoader();
is = ss.getResourceAsStream(cl, serviceId);
useBSClsLoader = true;
}
if (is == null) {
// No provider found
return null;
}
if (debug) { // Extra check to avoid computing cl strings
dPrint("found jar resource=" + serviceId + " using ClassLoader: " + cl);
}
BufferedReader rd;
try { try {
rd = new BufferedReader(new InputStreamReader(is, "UTF-8")); return AccessController.doPrivileged(new PrivilegedAction<T>() {
} public T run() {
catch (java.io.UnsupportedEncodingException e) { final ServiceLoader<T> serviceLoader = ServiceLoader.load(type);
rd = new BufferedReader(new InputStreamReader(is)); final Iterator<T> iterator = serviceLoader.iterator();
} if (iterator.hasNext()) {
return iterator.next();
String factoryClassName = null; } else {
try { return null;
// XXX Does not handle all possible input as specified by the }
// Jar Service Provider specification }
factoryClassName = rd.readLine(); });
rd.close(); } catch(ServiceConfigurationError e) {
} catch (IOException x) { final DatatypeConfigurationException error =
// No provider found new DatatypeConfigurationException(
return null; "Provider for " + type + " cannot be found", e);
} throw error;
if (factoryClassName != null && !"".equals(factoryClassName)) {
dPrint("found in resource, value=" + factoryClassName);
// Note: here we do not want to fall back to the current
// ClassLoader because we want to avoid the case where the
// resource file was found using one ClassLoader and the
// provider class was instantiated using a different one.
return newInstance(factoryClassName, cl, false, useBSClsLoader);
}
// No provider found
return null;
}
static class ConfigurationError extends Error {
private Exception exception;
/**
* Construct a new instance with the specified detail string and
* exception.
*/
ConfigurationError(String msg, Exception x) {
super(msg);
this.exception = x;
}
Exception getException() {
return exception;
}
/**
* use the exception chaining mechanism of JDK1.4
*/
@Override
public Throwable getCause() {
return exception;
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -40,9 +40,6 @@ import javax.xml.validation.Schema;
public abstract class DocumentBuilderFactory { public abstract class DocumentBuilderFactory {
/** The default property name according to the JAXP spec */
private static final String DEFAULT_PROPERTY_NAME = "javax.xml.parsers.DocumentBuilderFactory";
private boolean validating = false; private boolean validating = false;
private boolean namespaceAware = false; private boolean namespaceAware = false;
private boolean whitespace = false; private boolean whitespace = false;
@ -50,8 +47,6 @@ public abstract class DocumentBuilderFactory {
private boolean ignoreComments = false; private boolean ignoreComments = false;
private boolean coalescing = false; private boolean coalescing = false;
private boolean canonicalState = false;
/** /**
* <p>Protected constructor to prevent instantiation. * <p>Protected constructor to prevent instantiation.
* Use {@link #newInstance()}.</p> * Use {@link #newInstance()}.</p>
@ -85,14 +80,12 @@ public abstract class DocumentBuilderFactory {
* of any property in jaxp.properties after it has been read for the first time. * of any property in jaxp.properties after it has been read for the first time.
* </li> * </li>
* <li> * <li>
* Use the Services API (as detailed in the JAR specification), if * Uses the service-provider loading facilities, defined by the
* available, to determine the classname. The Services API will look * {@link java.util.ServiceLoader} class, to attempt to locate and load an
* for a classname in the file * implementation of the service.
* <code>META-INF/services/javax.xml.parsers.DocumentBuilderFactory</code>
* in jars available to the runtime.
* </li> * </li>
* <li> * <li>
* Platform default <code>DocumentBuilderFactory</code> instance. * Otherwise, the system-default implementation is returned.
* </li> * </li>
* </ul> * </ul>
* *
@ -113,21 +106,16 @@ public abstract class DocumentBuilderFactory {
* *
* @return New instance of a <code>DocumentBuilderFactory</code> * @return New instance of a <code>DocumentBuilderFactory</code>
* *
* @throws FactoryConfigurationError if the implementation is not * @throws FactoryConfigurationError in case of {@linkplain
* available or cannot be instantiated. * java.util.ServiceConfigurationError service configuration error} or if
* the implementation is not available or cannot be instantiated.
*/ */
public static DocumentBuilderFactory newInstance() { public static DocumentBuilderFactory newInstance() {
try { return FactoryFinder.find(
return (DocumentBuilderFactory) FactoryFinder.find(
/* The default property name according to the JAXP spec */ /* The default property name according to the JAXP spec */
"javax.xml.parsers.DocumentBuilderFactory", DocumentBuilderFactory.class, // "javax.xml.parsers.DocumentBuilderFactory"
/* The fallback implementation class name */ /* The fallback implementation class name */
"com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl"); "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl");
} catch (FactoryFinder.ConfigurationError e) {
throw new FactoryConfigurationError(e.getException(),
e.getMessage());
}
} }
/** /**
@ -165,13 +153,9 @@ public abstract class DocumentBuilderFactory {
* @since 1.6 * @since 1.6
*/ */
public static DocumentBuilderFactory newInstance(String factoryClassName, ClassLoader classLoader){ public static DocumentBuilderFactory newInstance(String factoryClassName, ClassLoader classLoader){
try {
//do not fallback if given classloader can't find the class, throw exception //do not fallback if given classloader can't find the class, throw exception
return (DocumentBuilderFactory) FactoryFinder.newInstance(factoryClassName, classLoader, false); return FactoryFinder.newInstance(DocumentBuilderFactory.class,
} catch (FactoryFinder.ConfigurationError e) { factoryClassName, classLoader, false);
throw new FactoryConfigurationError(e.getException(),
e.getMessage());
}
} }
/** /**
@ -391,75 +375,64 @@ public abstract class DocumentBuilderFactory {
public abstract Object getAttribute(String name) public abstract Object getAttribute(String name)
throws IllegalArgumentException; throws IllegalArgumentException;
/** /**
* <p>Set a feature for this <code>DocumentBuilderFactory</code> and <code>DocumentBuilder</code>s created by this factory.</p> * <p>Set a feature for this <code>DocumentBuilderFactory</code> and <code>DocumentBuilder</code>s created by this factory.</p>
*
* <p>
* Feature names are fully qualified {@link java.net.URI}s.
* Implementations may define their own features.
* A {@link ParserConfigurationException} is thrown if this <code>DocumentBuilderFactory</code> or the
* <code>DocumentBuilder</code>s it creates cannot support the feature.
* It is possible for a <code>DocumentBuilderFactory</code> to expose a feature value but be unable to change its state.
* </p>
*
* <p>
* All implementations are required to support the {@link javax.xml.XMLConstants#FEATURE_SECURE_PROCESSING} feature.
* When the feature is:</p>
* <ul>
* <li>
* <code>true</code>: the implementation will limit XML processing to conform to implementation limits.
* Examples include enity expansion limits and XML Schema constructs that would consume large amounts of resources.
* If XML processing is limited for security reasons, it will be reported via a call to the registered
* {@link org.xml.sax.ErrorHandler#fatalError(SAXParseException exception)}.
* See {@link DocumentBuilder#setErrorHandler(org.xml.sax.ErrorHandler errorHandler)}.
* </li>
* <li>
* <code>false</code>: the implementation will processing XML according to the XML specifications without
* regard to possible implementation limits.
* </li>
* </ul>
*
* @param name Feature name.
* @param value Is feature state <code>true</code> or <code>false</code>.
*
* @throws ParserConfigurationException if this <code>DocumentBuilderFactory</code> or the <code>DocumentBuilder</code>s
* it creates cannot support this feature.
* @throws NullPointerException If the <code>name</code> parameter is null.
*/
public abstract void setFeature(String name, boolean value)
throws ParserConfigurationException;
/**
* <p>Get the state of the named feature.</p>
*
* <p>
* Feature names are fully qualified {@link java.net.URI}s.
* Implementations may define their own features.
* An {@link ParserConfigurationException} is thrown if this <code>DocumentBuilderFactory</code> or the
* <code>DocumentBuilder</code>s it creates cannot support the feature.
* It is possible for an <code>DocumentBuilderFactory</code> to expose a feature value but be unable to change its state.
* </p>
*
* @param name Feature name.
*
* @return State of the named feature.
*
* @throws ParserConfigurationException if this <code>DocumentBuilderFactory</code>
* or the <code>DocumentBuilder</code>s it creates cannot support this feature.
*/
public abstract boolean getFeature(String name)
throws ParserConfigurationException;
/** <p>Get current state of canonicalization.</p>
* *
* @return current state canonicalization control * <p>
* Feature names are fully qualified {@link java.net.URI}s.
* Implementations may define their own features.
* A {@link ParserConfigurationException} is thrown if this <code>DocumentBuilderFactory</code> or the
* <code>DocumentBuilder</code>s it creates cannot support the feature.
* It is possible for a <code>DocumentBuilderFactory</code> to expose a feature value but be unable to change its state.
* </p>
*
* <p>
* All implementations are required to support the {@link javax.xml.XMLConstants#FEATURE_SECURE_PROCESSING} feature.
* When the feature is:</p>
* <ul>
* <li>
* <code>true</code>: the implementation will limit XML processing to conform to implementation limits.
* Examples include enity expansion limits and XML Schema constructs that would consume large amounts of resources.
* If XML processing is limited for security reasons, it will be reported via a call to the registered
* {@link org.xml.sax.ErrorHandler#fatalError(SAXParseException exception)}.
* See {@link DocumentBuilder#setErrorHandler(org.xml.sax.ErrorHandler errorHandler)}.
* </li>
* <li>
* <code>false</code>: the implementation will processing XML according to the XML specifications without
* regard to possible implementation limits.
* </li>
* </ul>
*
* @param name Feature name.
* @param value Is feature state <code>true</code> or <code>false</code>.
*
* @throws ParserConfigurationException if this <code>DocumentBuilderFactory</code> or the <code>DocumentBuilder</code>s
* it creates cannot support this feature.
* @throws NullPointerException If the <code>name</code> parameter is null.
*/ */
/* public abstract void setFeature(String name, boolean value)
public boolean getCanonicalization() { throws ParserConfigurationException;
return canonicalState;
} /**
*/ * <p>Get the state of the named feature.</p>
*
* <p>
* Feature names are fully qualified {@link java.net.URI}s.
* Implementations may define their own features.
* An {@link ParserConfigurationException} is thrown if this <code>DocumentBuilderFactory</code> or the
* <code>DocumentBuilder</code>s it creates cannot support the feature.
* It is possible for an <code>DocumentBuilderFactory</code> to expose a feature value but be unable to change its state.
* </p>
*
* @param name Feature name.
*
* @return State of the named feature.
*
* @throws ParserConfigurationException if this <code>DocumentBuilderFactory</code>
* or the <code>DocumentBuilder</code>s it creates cannot support this feature.
*/
public abstract boolean getFeature(String name)
throws ParserConfigurationException;
/** /**
@ -488,17 +461,6 @@ public abstract class DocumentBuilderFactory {
} }
/* <p>Set canonicalization control to <code>true</code> or
* </code>false</code>.</p>
*
* @param state of canonicalization
*/
/*
public void setCanonicalization(boolean state) {
canonicalState = state;
}
*/
/** /**
* <p>Set the {@link Schema} to be used by parsers created * <p>Set the {@link Schema} to be used by parsers created
* from this factory. * from this factory.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -25,15 +25,16 @@
package javax.xml.parsers; package javax.xml.parsers;
import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.IOException; import java.security.AccessController;
import java.io.InputStream; import java.security.PrivilegedAction;
import java.io.InputStreamReader; import java.util.Iterator;
import java.util.Properties; import java.util.Properties;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
/** /**
* <p>Implements pluggable Datatypes.</p> * <p>Implements pluggable Parsers.</p>
* *
* <p>This class is duplicated for each JAXP subpackage so keep it in * <p>This class is duplicated for each JAXP subpackage so keep it in
* sync. It is package private for secure class loading.</p> * sync. It is package private for secure class loading.</p>
@ -51,7 +52,7 @@ class FactoryFinder {
/** /**
* Cache for properties in java.home/lib/jaxp.properties * Cache for properties in java.home/lib/jaxp.properties
*/ */
static Properties cacheProps = new Properties(); private static final Properties cacheProps = new Properties();
/** /**
* Flag indicating if properties from java.home/lib/jaxp.properties * Flag indicating if properties from java.home/lib/jaxp.properties
@ -63,7 +64,7 @@ class FactoryFinder {
* Security support class use to check access control before * Security support class use to check access control before
* getting certain system resources. * getting certain system resources.
*/ */
static SecuritySupport ss = new SecuritySupport(); private static final SecuritySupport ss = new SecuritySupport();
// Define system property "jaxp.debug" to get output // Define system property "jaxp.debug" to get output
static { static {
@ -96,31 +97,31 @@ class FactoryFinder {
* *
* Use bootstrap classLoader if cl = null and useBSClsLoader is true * Use bootstrap classLoader if cl = null and useBSClsLoader is true
*/ */
static private Class getProviderClass(String className, ClassLoader cl, static private Class<?> getProviderClass(String className, ClassLoader cl,
boolean doFallback, boolean useBSClsLoader) throws ClassNotFoundException boolean doFallback, boolean useBSClsLoader) throws ClassNotFoundException
{ {
try { try {
if (cl == null) { if (cl == null) {
if (useBSClsLoader) { if (useBSClsLoader) {
return Class.forName(className, true, FactoryFinder.class.getClassLoader()); return Class.forName(className, false, FactoryFinder.class.getClassLoader());
} else { } else {
cl = ss.getContextClassLoader(); cl = ss.getContextClassLoader();
if (cl == null) { if (cl == null) {
throw new ClassNotFoundException(); throw new ClassNotFoundException();
} }
else { else {
return cl.loadClass(className); return Class.forName(className, false, cl);
} }
} }
} }
else { else {
return cl.loadClass(className); return Class.forName(className, false, cl);
} }
} }
catch (ClassNotFoundException e1) { catch (ClassNotFoundException e1) {
if (doFallback) { if (doFallback) {
// Use current class loader - should always be bootstrap CL // Use current class loader - should always be bootstrap CL
return Class.forName(className, true, FactoryFinder.class.getClassLoader()); return Class.forName(className, false, FactoryFinder.class.getClassLoader());
} }
else { else {
throw e1; throw e1;
@ -132,6 +133,9 @@ class FactoryFinder {
* Create an instance of a class. Delegates to method * Create an instance of a class. Delegates to method
* <code>getProviderClass()</code> in order to load the class. * <code>getProviderClass()</code> in order to load the class.
* *
* @param type Base class / Service interface of the factory to
* instantiate.
*
* @param className Name of the concrete class corresponding to the * @param className Name of the concrete class corresponding to the
* service provider * service provider
* *
@ -141,16 +145,20 @@ class FactoryFinder {
* @param doFallback True if the current ClassLoader should be tried as * @param doFallback True if the current ClassLoader should be tried as
* a fallback if the class is not found using cl * a fallback if the class is not found using cl
*/ */
static Object newInstance(String className, ClassLoader cl, boolean doFallback) static <T> T newInstance(Class<T> type, String className, ClassLoader cl,
throws ConfigurationError boolean doFallback)
throws FactoryConfigurationError
{ {
return newInstance(className, cl, doFallback, false); return newInstance(type, className, cl, doFallback, false);
} }
/** /**
* Create an instance of a class. Delegates to method * Create an instance of a class. Delegates to method
* <code>getProviderClass()</code> in order to load the class. * <code>getProviderClass()</code> in order to load the class.
* *
* @param type Base class / Service interface of the factory to
* instantiate.
*
* @param className Name of the concrete class corresponding to the * @param className Name of the concrete class corresponding to the
* service provider * service provider
* *
@ -163,9 +171,11 @@ class FactoryFinder {
* @param useBSClsLoader True if cl=null actually meant bootstrap classLoader. This parameter * @param useBSClsLoader True if cl=null actually meant bootstrap classLoader. This parameter
* is needed since DocumentBuilderFactory/SAXParserFactory defined null as context classLoader. * is needed since DocumentBuilderFactory/SAXParserFactory defined null as context classLoader.
*/ */
static Object newInstance(String className, ClassLoader cl, boolean doFallback, boolean useBSClsLoader) static <T> T newInstance(Class<T> type, String className, ClassLoader cl,
throws ConfigurationError boolean doFallback, boolean useBSClsLoader)
throws FactoryConfigurationError
{ {
assert type != null;
// make sure we have access to restricted packages // make sure we have access to restricted packages
if (System.getSecurityManager() != null) { if (System.getSecurityManager() != null) {
if (className != null && className.startsWith(DEFAULT_PACKAGE)) { if (className != null && className.startsWith(DEFAULT_PACKAGE)) {
@ -175,22 +185,24 @@ class FactoryFinder {
} }
try { try {
Class providerClass = getProviderClass(className, cl, doFallback, useBSClsLoader); Class<?> providerClass = getProviderClass(className, cl, doFallback, useBSClsLoader);
if (!type.isAssignableFrom(providerClass)) {
throw new ClassCastException(className + " cannot be cast to " + type.getName());
}
Object instance = providerClass.newInstance(); Object instance = providerClass.newInstance();
if (debug) { // Extra check to avoid computing cl strings if (debug) { // Extra check to avoid computing cl strings
dPrint("created new instance of " + providerClass + dPrint("created new instance of " + providerClass +
" using ClassLoader: " + cl); " using ClassLoader: " + cl);
} }
return instance; return type.cast(instance);
} }
catch (ClassNotFoundException x) { catch (ClassNotFoundException x) {
throw new ConfigurationError( throw new FactoryConfigurationError(x,
"Provider " + className + " not found", x); "Provider " + className + " not found");
} }
catch (Exception x) { catch (Exception x) {
throw new ConfigurationError( throw new FactoryConfigurationError(x,
"Provider " + className + " could not be instantiated: " + x, "Provider " + className + " could not be instantiated: " + x);
x);
} }
} }
@ -199,16 +211,17 @@ class FactoryFinder {
* entry point. * entry point.
* @return Class object of factory, never null * @return Class object of factory, never null
* *
* @param factoryId Name of the factory to find, same as * @param type Base class / Service interface of the
* a property name * factory to find.
* @param fallbackClassName Implementation class name, if nothing else * @param fallbackClassName Implementation class name, if nothing else
* is found. Use null to mean no fallback. * is found. Use null to mean no fallback.
* *
* Package private so this code can be shared. * Package private so this code can be shared.
*/ */
static Object find(String factoryId, String fallbackClassName) static <T> T find(Class<T> type, String fallbackClassName)
throws ConfigurationError throws FactoryConfigurationError
{ {
final String factoryId = type.getName();
dPrint("find factoryId =" + factoryId); dPrint("find factoryId =" + factoryId);
// Use the system property first // Use the system property first
@ -216,7 +229,7 @@ class FactoryFinder {
String systemProp = ss.getSystemProperty(factoryId); String systemProp = ss.getSystemProperty(factoryId);
if (systemProp != null) { if (systemProp != null) {
dPrint("found system property, value=" + systemProp); dPrint("found system property, value=" + systemProp);
return newInstance(systemProp, null, true); return newInstance(type, systemProp, null, true);
} }
} }
catch (SecurityException se) { catch (SecurityException se) {
@ -225,7 +238,6 @@ class FactoryFinder {
// try to read from $java.home/lib/jaxp.properties // try to read from $java.home/lib/jaxp.properties
try { try {
String factoryClassName = null;
if (firstTime) { if (firstTime) {
synchronized (cacheProps) { synchronized (cacheProps) {
if (firstTime) { if (firstTime) {
@ -240,11 +252,11 @@ class FactoryFinder {
} }
} }
} }
factoryClassName = cacheProps.getProperty(factoryId); final String factoryClassName = cacheProps.getProperty(factoryId);
if (factoryClassName != null) { if (factoryClassName != null) {
dPrint("found in $java.home/jaxp.properties, value=" + factoryClassName); dPrint("found in $java.home/jaxp.properties, value=" + factoryClassName);
return newInstance(factoryClassName, null, true); return newInstance(type, factoryClassName, null, true);
} }
} }
catch (Exception ex) { catch (Exception ex) {
@ -252,112 +264,52 @@ class FactoryFinder {
} }
// Try Jar Service Provider Mechanism // Try Jar Service Provider Mechanism
Object provider = findJarServiceProvider(factoryId); T provider = findServiceProvider(type);
if (provider != null) { if (provider != null) {
return provider; return provider;
} }
if (fallbackClassName == null) { if (fallbackClassName == null) {
throw new ConfigurationError( throw new FactoryConfigurationError(
"Provider for " + factoryId + " cannot be found", null); "Provider for " + factoryId + " cannot be found");
} }
dPrint("loaded from fallback value: " + fallbackClassName); dPrint("loaded from fallback value: " + fallbackClassName);
return newInstance(fallbackClassName, null, true); return newInstance(type, fallbackClassName, null, true);
} }
/* /*
* Try to find provider using Jar Service Provider Mechanism * Try to find provider using the ServiceLoader API
*
* @param type Base class / Service interface of the factory to find.
* *
* @return instance of provider class if found or null * @return instance of provider class if found or null
*/ */
private static Object findJarServiceProvider(String factoryId) private static <T> T findServiceProvider(final Class<T> type) {
throws ConfigurationError
{
String serviceId = "META-INF/services/" + factoryId;
InputStream is = null;
// First try the Context ClassLoader
ClassLoader cl = ss.getContextClassLoader();
boolean useBSClsLoader = false;
if (cl != null) {
is = ss.getResourceAsStream(cl, serviceId);
// If no provider found then try the current ClassLoader
if (is == null) {
cl = FactoryFinder.class.getClassLoader();
is = ss.getResourceAsStream(cl, serviceId);
useBSClsLoader = true;
}
} else {
// No Context ClassLoader, try the current ClassLoader
cl = FactoryFinder.class.getClassLoader();
is = ss.getResourceAsStream(cl, serviceId);
useBSClsLoader = true;
}
if (is == null) {
// No provider found
return null;
}
if (debug) { // Extra check to avoid computing cl strings
dPrint("found jar resource=" + serviceId + " using ClassLoader: " + cl);
}
BufferedReader rd;
try { try {
rd = new BufferedReader(new InputStreamReader(is, "UTF-8")); return AccessController.doPrivileged(new PrivilegedAction<T>() {
} public T run() {
catch (java.io.UnsupportedEncodingException e) { final ServiceLoader<T> serviceLoader = ServiceLoader.load(type);
rd = new BufferedReader(new InputStreamReader(is)); final Iterator<T> iterator = serviceLoader.iterator();
} if (iterator.hasNext()) {
return iterator.next();
String factoryClassName = null; } else {
try { return null;
// XXX Does not handle all possible input as specified by the }
// Jar Service Provider specification }
factoryClassName = rd.readLine(); });
rd.close(); } catch(ServiceConfigurationError e) {
} catch (IOException x) { // It is not possible to wrap an error directly in
// No provider found // FactoryConfigurationError - so we need to wrap the
return null; // ServiceConfigurationError in a RuntimeException.
} // The alternative would be to modify the logic in
// FactoryConfigurationError to allow setting a
if (factoryClassName != null && !"".equals(factoryClassName)) { // Throwable as the cause, but that could cause
dPrint("found in resource, value=" + factoryClassName); // compatibility issues down the road.
final RuntimeException x = new RuntimeException(
// Note: here we do not want to fall back to the current "Provider for " + type + " cannot be created", e);
// ClassLoader because we want to avoid the case where the final FactoryConfigurationError error =
// resource file was found using one ClassLoader and the new FactoryConfigurationError(x, x.getMessage());
// provider class was instantiated using a different one. throw error;
return newInstance(factoryClassName, cl, false, useBSClsLoader);
}
// No provider found
return null;
}
static class ConfigurationError extends Error {
private Exception exception;
/**
* Construct a new instance with the specified detail string and
* exception.
*/
ConfigurationError(String msg, Exception x) {
super(msg);
this.exception = x;
}
Exception getException() {
return exception;
}
/**
* use the exception chaining mechanism of JDK1.4
*/
@Override
public Throwable getCause() {
return exception;
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -26,7 +26,6 @@
package javax.xml.parsers; package javax.xml.parsers;
import javax.xml.validation.Schema; import javax.xml.validation.Schema;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException; import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException; import org.xml.sax.SAXNotSupportedException;
@ -42,8 +41,6 @@ import org.xml.sax.SAXNotSupportedException;
* *
*/ */
public abstract class SAXParserFactory { public abstract class SAXParserFactory {
/** The default property name according to the JAXP spec */
private static final String DEFAULT_PROPERTY_NAME = "javax.xml.parsers.SAXParserFactory";
/** /**
* <p>Should Parsers be validating?</p> * <p>Should Parsers be validating?</p>
@ -87,14 +84,12 @@ public abstract class SAXParserFactory {
* of any property in jaxp.properties after it has been read for the first time. * of any property in jaxp.properties after it has been read for the first time.
* </li> * </li>
* <li> * <li>
* Use the Services API (as detailed in the JAR specification), if * Use the service-provider loading facilities, defined by the
* available, to determine the classname. The Services API will look * {@link java.util.ServiceLoader} class, to attempt to locate and load an
* for a classname in the file * implementation of the service.
* <code>META-INF/services/javax.xml.parsers.SAXParserFactory</code>
* in jars available to the runtime.
* </li> * </li>
* <li> * <li>
* Platform default <code>SAXParserFactory</code> instance. * Otherwise the system-default implementation is returned.
* </li> * </li>
* </ul> * </ul>
* *
@ -109,7 +104,7 @@ public abstract class SAXParserFactory {
* this method to print a lot of debug messages * this method to print a lot of debug messages
* to <code>System.err</code> about what it is doing and where it is looking at.</p> * to <code>System.err</code> about what it is doing and where it is looking at.</p>
* *
* <p> If you have problems loading {@link DocumentBuilder}s, try:</p> * <p> If you have problems loading {@link SAXParser}s, try:</p>
* <pre> * <pre>
* java -Djaxp.debug=1 YourProgram .... * java -Djaxp.debug=1 YourProgram ....
* </pre> * </pre>
@ -117,21 +112,17 @@ public abstract class SAXParserFactory {
* *
* @return A new instance of a SAXParserFactory. * @return A new instance of a SAXParserFactory.
* *
* @throws FactoryConfigurationError if the implementation is * @throws FactoryConfigurationError in case of {@linkplain
* not available or cannot be instantiated. * java.util.ServiceConfigurationError service configuration error} or if
* the implementation is not available or cannot be instantiated.
*/ */
public static SAXParserFactory newInstance() { public static SAXParserFactory newInstance() {
try { return FactoryFinder.find(
return (SAXParserFactory) FactoryFinder.find(
/* The default property name according to the JAXP spec */ /* The default property name according to the JAXP spec */
"javax.xml.parsers.SAXParserFactory", SAXParserFactory.class,
/* The fallback implementation class name */ /* The fallback implementation class name */
"com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl"); "com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl");
} catch (FactoryFinder.ConfigurationError e) {
throw new FactoryConfigurationError(e.getException(),
e.getMessage());
}
} }
/** /**
@ -169,13 +160,9 @@ public abstract class SAXParserFactory {
* @since 1.6 * @since 1.6
*/ */
public static SAXParserFactory newInstance(String factoryClassName, ClassLoader classLoader){ public static SAXParserFactory newInstance(String factoryClassName, ClassLoader classLoader){
try {
//do not fallback if given classloader can't find the class, throw exception //do not fallback if given classloader can't find the class, throw exception
return (SAXParserFactory) FactoryFinder.newInstance(factoryClassName, classLoader, false); return FactoryFinder.newInstance(SAXParserFactory.class,
} catch (FactoryFinder.ConfigurationError e) { factoryClassName, classLoader, false);
throw new FactoryConfigurationError(e.getException(),
e.getMessage());
}
} }
/** /**
@ -266,22 +253,22 @@ public abstract class SAXParserFactory {
* A list of the core features and properties can be found at * A list of the core features and properties can be found at
* <a href="http://www.saxproject.org/">http://www.saxproject.org/</a></p> * <a href="http://www.saxproject.org/">http://www.saxproject.org/</a></p>
* *
* <p>All implementations are required to support the {@link javax.xml.XMLConstants#FEATURE_SECURE_PROCESSING} feature. * <p>All implementations are required to support the {@link javax.xml.XMLConstants#FEATURE_SECURE_PROCESSING} feature.
* When the feature is</p> * When the feature is</p>
* <ul> * <ul>
* <li> * <li>
* <code>true</code>: the implementation will limit XML processing to conform to implementation limits. * <code>true</code>: the implementation will limit XML processing to conform to implementation limits.
* Examples include enity expansion limits and XML Schema constructs that would consume large amounts of resources. * Examples include entity expansion limits and XML Schema constructs that would consume large amounts of resources.
* If XML processing is limited for security reasons, it will be reported via a call to the registered * If XML processing is limited for security reasons, it will be reported via a call to the registered
* {@link org.xml.sax.ErrorHandler#fatalError(SAXParseException exception)}. * {@link org.xml.sax.ErrorHandler#fatalError(SAXParseException exception)}.
* See {@link SAXParser} <code>parse</code> methods for handler specification. * See {@link SAXParser} <code>parse</code> methods for handler specification.
* </li> * </li>
* <li> * <li>
* When the feature is <code>false</code>, the implementation will processing XML according to the XML specifications without * When the feature is <code>false</code>, the implementation will processing XML according to the XML specifications without
* regard to possible implementation limits. * regard to possible implementation limits.
* </li> * </li>
* </ul> * </ul>
* *
* @param name The name of the feature to be set. * @param name The name of the feature to be set.
* @param value The value of the feature to be set. * @param value The value of the feature to be set.
* *
@ -320,17 +307,6 @@ public abstract class SAXParserFactory {
SAXNotSupportedException; SAXNotSupportedException;
/* <p>Get current state of canonicalization.</p>
*
* @return current state canonicalization control
*/
/*
public boolean getCanonicalization() {
return canonicalState;
}
*/
/** /**
* Gets the {@link Schema} object specified through * Gets the {@link Schema} object specified through
* the {@link #setSchema(Schema schema)} method. * the {@link #setSchema(Schema schema)} method.
@ -357,17 +333,6 @@ public abstract class SAXParserFactory {
); );
} }
/** <p>Set canonicalization control to <code>true</code> or
* </code>false</code>.</p>
*
* @param state of canonicalization
*/
/*
public void setCanonicalization(boolean state) {
canonicalState = state;
}
*/
/** /**
* <p>Set the {@link Schema} to be used by parsers created * <p>Set the {@link Schema} to be used by parsers created
* from this factory.</p> * from this factory.</p>
@ -400,7 +365,7 @@ public abstract class SAXParserFactory {
* Such configuration will cause a {@link SAXException} * Such configuration will cause a {@link SAXException}
* exception when those properties are set on a {@link SAXParser}.</p> * exception when those properties are set on a {@link SAXParser}.</p>
* *
* <h4>Note for implmentors</h4> * <h4>Note for implementors</h4>
* <p> * <p>
* A parser must be able to work with any {@link Schema} * A parser must be able to work with any {@link Schema}
* implementation. However, parsers and schemas are allowed * implementation. However, parsers and schemas are allowed

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -25,15 +25,16 @@
package javax.xml.stream; package javax.xml.stream;
import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.IOException; import java.security.AccessController;
import java.io.InputStream; import java.security.PrivilegedAction;
import java.io.InputStreamReader; import java.util.Iterator;
import java.util.Properties; import java.util.Properties;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
/** /**
* <p>Implements pluggable Datatypes.</p> * <p>Implements pluggable streams.</p>
* *
* <p>This class is duplicated for each JAXP subpackage so keep it in * <p>This class is duplicated for each JAXP subpackage so keep it in
* sync. It is package private for secure class loading.</p> * sync. It is package private for secure class loading.</p>
@ -52,19 +53,19 @@ class FactoryFinder {
/** /**
* Cache for properties in java.home/lib/jaxp.properties * Cache for properties in java.home/lib/jaxp.properties
*/ */
static Properties cacheProps = new Properties(); final private static Properties cacheProps = new Properties();
/** /**
* Flag indicating if properties from java.home/lib/jaxp.properties * Flag indicating if properties from java.home/lib/jaxp.properties
* have been cached. * have been cached.
*/ */
static volatile boolean firstTime = true; private static volatile boolean firstTime = true;
/** /**
* Security support class use to check access control before * Security support class use to check access control before
* getting certain system resources. * getting certain system resources.
*/ */
static SecuritySupport ss = new SecuritySupport(); final private static SecuritySupport ss = new SecuritySupport();
// Define system property "jaxp.debug" to get output // Define system property "jaxp.debug" to get output
static { static {
@ -103,25 +104,25 @@ class FactoryFinder {
try { try {
if (cl == null) { if (cl == null) {
if (useBSClsLoader) { if (useBSClsLoader) {
return Class.forName(className, true, FactoryFinder.class.getClassLoader()); return Class.forName(className, false, FactoryFinder.class.getClassLoader());
} else { } else {
cl = ss.getContextClassLoader(); cl = ss.getContextClassLoader();
if (cl == null) { if (cl == null) {
throw new ClassNotFoundException(); throw new ClassNotFoundException();
} }
else { else {
return cl.loadClass(className); return Class.forName(className, false, cl);
} }
} }
} }
else { else {
return cl.loadClass(className); return Class.forName(className, false, cl);
} }
} }
catch (ClassNotFoundException e1) { catch (ClassNotFoundException e1) {
if (doFallback) { if (doFallback) {
// Use current class loader - should always be bootstrap CL // Use current class loader - should always be bootstrap CL
return Class.forName(className, true, FactoryFinder.class.getClassLoader()); return Class.forName(className, false, FactoryFinder.class.getClassLoader());
} }
else { else {
throw e1; throw e1;
@ -133,6 +134,9 @@ class FactoryFinder {
* Create an instance of a class. Delegates to method * Create an instance of a class. Delegates to method
* <code>getProviderClass()</code> in order to load the class. * <code>getProviderClass()</code> in order to load the class.
* *
* @param type Base class / Service interface of the factory to
* instantiate.
*
* @param className Name of the concrete class corresponding to the * @param className Name of the concrete class corresponding to the
* service provider * service provider
* *
@ -142,16 +146,19 @@ class FactoryFinder {
* @param doFallback True if the current ClassLoader should be tried as * @param doFallback True if the current ClassLoader should be tried as
* a fallback if the class is not found using cl * a fallback if the class is not found using cl
*/ */
static Object newInstance(String className, ClassLoader cl, boolean doFallback) static <T> T newInstance(Class<T> type, String className, ClassLoader cl, boolean doFallback)
throws ConfigurationError throws FactoryConfigurationError
{ {
return newInstance(className, cl, doFallback, false); return newInstance(type, className, cl, doFallback, false);
} }
/** /**
* Create an instance of a class. Delegates to method * Create an instance of a class. Delegates to method
* <code>getProviderClass()</code> in order to load the class. * <code>getProviderClass()</code> in order to load the class.
* *
* @param type Base class / Service interface of the factory to
* instantiate.
*
* @param className Name of the concrete class corresponding to the * @param className Name of the concrete class corresponding to the
* service provider * service provider
* *
@ -164,9 +171,12 @@ class FactoryFinder {
* @param useBSClsLoader True if cl=null actually meant bootstrap classLoader. This parameter * @param useBSClsLoader True if cl=null actually meant bootstrap classLoader. This parameter
* is needed since DocumentBuilderFactory/SAXParserFactory defined null as context classLoader. * is needed since DocumentBuilderFactory/SAXParserFactory defined null as context classLoader.
*/ */
static Object newInstance(String className, ClassLoader cl, boolean doFallback, boolean useBSClsLoader) static <T> T newInstance(Class<T> type, String className, ClassLoader cl,
throws ConfigurationError boolean doFallback, boolean useBSClsLoader)
throws FactoryConfigurationError
{ {
assert type != null;
// make sure we have access to restricted packages // make sure we have access to restricted packages
if (System.getSecurityManager() != null) { if (System.getSecurityManager() != null) {
if (className != null && className.startsWith(DEFAULT_PACKAGE)) { if (className != null && className.startsWith(DEFAULT_PACKAGE)) {
@ -176,20 +186,23 @@ class FactoryFinder {
} }
try { try {
Class providerClass = getProviderClass(className, cl, doFallback, useBSClsLoader); Class<?> providerClass = getProviderClass(className, cl, doFallback, useBSClsLoader);
if (!type.isAssignableFrom(providerClass)) {
throw new ClassCastException(className + " cannot be cast to " + type.getName());
}
Object instance = providerClass.newInstance(); Object instance = providerClass.newInstance();
if (debug) { // Extra check to avoid computing cl strings if (debug) { // Extra check to avoid computing cl strings
dPrint("created new instance of " + providerClass + dPrint("created new instance of " + providerClass +
" using ClassLoader: " + cl); " using ClassLoader: " + cl);
} }
return instance; return type.cast(instance);
} }
catch (ClassNotFoundException x) { catch (ClassNotFoundException x) {
throw new ConfigurationError( throw new FactoryConfigurationError(
"Provider " + className + " not found", x); "Provider " + className + " not found", x);
} }
catch (Exception x) { catch (Exception x) {
throw new ConfigurationError( throw new FactoryConfigurationError(
"Provider " + className + " could not be instantiated: " + x, "Provider " + className + " could not be instantiated: " + x,
x); x);
} }
@ -200,17 +213,18 @@ class FactoryFinder {
* *
* @return Class object of factory, never null * @return Class object of factory, never null
* *
* @param factoryId Name of the factory to find, same as * @param type Base class / Service interface of the
* a property name * factory to find.
*
* @param fallbackClassName Implementation class name, if nothing else * @param fallbackClassName Implementation class name, if nothing else
* is found. Use null to mean no fallback. * is found. Use null to mean no fallback.
* *
* Package private so this code can be shared. * Package private so this code can be shared.
*/ */
static Object find(String factoryId, String fallbackClassName) static <T> T find(Class<T> type, String fallbackClassName)
throws ConfigurationError throws FactoryConfigurationError
{ {
return find(factoryId, null, fallbackClassName); return find(type, type.getName(), null, fallbackClassName);
} }
/** /**
@ -218,6 +232,9 @@ class FactoryFinder {
* entry point. * entry point.
* @return Class object of factory, never null * @return Class object of factory, never null
* *
* @param type Base class / Service interface of the
* factory to find.
*
* @param factoryId Name of the factory to find, same as * @param factoryId Name of the factory to find, same as
* a property name * a property name
* *
@ -229,8 +246,8 @@ class FactoryFinder {
* *
* Package private so this code can be shared. * Package private so this code can be shared.
*/ */
static Object find(String factoryId, ClassLoader cl, String fallbackClassName) static <T> T find(Class<T> type, String factoryId, ClassLoader cl, String fallbackClassName)
throws ConfigurationError throws FactoryConfigurationError
{ {
dPrint("find factoryId =" + factoryId); dPrint("find factoryId =" + factoryId);
@ -239,7 +256,9 @@ class FactoryFinder {
String systemProp = ss.getSystemProperty(factoryId); String systemProp = ss.getSystemProperty(factoryId);
if (systemProp != null) { if (systemProp != null) {
dPrint("found system property, value=" + systemProp); dPrint("found system property, value=" + systemProp);
return newInstance(systemProp, null, true); // There's a bug here - because 'cl' is ignored.
// This will be handled separately.
return newInstance(type, systemProp, null, true);
} }
} }
catch (SecurityException se) { catch (SecurityException se) {
@ -250,7 +269,6 @@ class FactoryFinder {
// $java.home/lib/jaxp.properties if former not present // $java.home/lib/jaxp.properties if former not present
String configFile = null; String configFile = null;
try { try {
String factoryClassName = null;
if (firstTime) { if (firstTime) {
synchronized (cacheProps) { synchronized (cacheProps) {
if (firstTime) { if (firstTime) {
@ -269,130 +287,80 @@ class FactoryFinder {
if (ss.doesFileExist(f)) { if (ss.doesFileExist(f)) {
dPrint("Read properties file "+f); dPrint("Read properties file "+f);
cacheProps.load(ss.getFileInputStream(f)); cacheProps.load(ss.getFileInputStream(f));
}
}
} }
} }
} }
} final String factoryClassName = cacheProps.getProperty(factoryId);
}
factoryClassName = cacheProps.getProperty(factoryId);
if (factoryClassName != null) { if (factoryClassName != null) {
dPrint("found in " + configFile + " value=" + factoryClassName); dPrint("found in " + configFile + " value=" + factoryClassName);
return newInstance(factoryClassName, null, true); // There's a bug here - because 'cl' is ignored.
// This will be handled separately.
return newInstance(type, factoryClassName, null, true);
} }
} }
catch (Exception ex) { catch (Exception ex) {
if (debug) ex.printStackTrace(); if (debug) ex.printStackTrace();
} }
// Try Jar Service Provider Mechanism if (type.getName().equals(factoryId)) {
Object provider = findJarServiceProvider(factoryId); // Try Jar Service Provider Mechanism
if (provider != null) { final T provider = findServiceProvider(type);
return provider; if (provider != null) {
return provider;
}
} else {
// We're in the case where a 'custom' factoryId was provided,
// and in every case where that happens, we expect that
// fallbackClassName will be null.
assert fallbackClassName == null;
} }
if (fallbackClassName == null) { if (fallbackClassName == null) {
throw new ConfigurationError( throw new FactoryConfigurationError(
"Provider for " + factoryId + " cannot be found", null); "Provider for " + factoryId + " cannot be found", null);
} }
dPrint("loaded from fallback value: " + fallbackClassName); dPrint("loaded from fallback value: " + fallbackClassName);
return newInstance(fallbackClassName, cl, true); return newInstance(type, fallbackClassName, cl, true);
} }
/* /*
* Try to find provider using Jar Service Provider Mechanism * Try to find provider using the ServiceLoader API
*
* @param type Base class / Service interface of the factory to find.
* *
* @return instance of provider class if found or null * @return instance of provider class if found or null
*/ */
private static Object findJarServiceProvider(String factoryId) private static <T> T findServiceProvider(final Class<T> type) {
throws ConfigurationError
{
String serviceId = "META-INF/services/" + factoryId;
InputStream is = null;
// First try the Context ClassLoader
ClassLoader cl = ss.getContextClassLoader();
boolean useBSClsLoader = false;
if (cl != null) {
is = ss.getResourceAsStream(cl, serviceId);
// If no provider found then try the current ClassLoader
if (is == null) {
cl = FactoryFinder.class.getClassLoader();
is = ss.getResourceAsStream(cl, serviceId);
useBSClsLoader = true;
}
} else {
// No Context ClassLoader, try the current ClassLoader
cl = FactoryFinder.class.getClassLoader();
is = ss.getResourceAsStream(cl, serviceId);
useBSClsLoader = true;
}
if (is == null) {
// No provider found
return null;
}
if (debug) { // Extra check to avoid computing cl strings
dPrint("found jar resource=" + serviceId + " using ClassLoader: " + cl);
}
BufferedReader rd;
try { try {
rd = new BufferedReader(new InputStreamReader(is, "UTF-8")); return AccessController.doPrivileged(new PrivilegedAction<T>() {
} @Override
catch (java.io.UnsupportedEncodingException e) { public T run() {
rd = new BufferedReader(new InputStreamReader(is)); final ServiceLoader<T> serviceLoader = ServiceLoader.load(type);
} final Iterator<T> iterator = serviceLoader.iterator();
if (iterator.hasNext()) {
String factoryClassName = null; return iterator.next();
try { } else {
// XXX Does not handle all possible input as specified by the return null;
// Jar Service Provider specification }
factoryClassName = rd.readLine(); }
rd.close(); });
} catch (IOException x) { } catch(ServiceConfigurationError e) {
// No provider found // It is not possible to wrap an error directly in
return null; // FactoryConfigurationError - so we need to wrap the
} // ServiceConfigurationError in a RuntimeException.
// The alternative would be to modify the logic in
if (factoryClassName != null && !"".equals(factoryClassName)) { // FactoryConfigurationError to allow setting a
dPrint("found in resource, value=" + factoryClassName); // Throwable as the cause, but that could cause
// compatibility issues down the road.
// Note: here we do not want to fall back to the current final RuntimeException x = new RuntimeException(
// ClassLoader because we want to avoid the case where the "Provider for " + type + " cannot be created", e);
// resource file was found using one ClassLoader and the final FactoryConfigurationError error =
// provider class was instantiated using a different one. new FactoryConfigurationError(x, x.getMessage());
return newInstance(factoryClassName, cl, false, useBSClsLoader); throw error;
} }
}
// No provider found
return null;
}
static class ConfigurationError extends Error {
private Exception exception;
/**
* Construct a new instance with the specified detail string and
* exception.
*/
ConfigurationError(String msg, Exception x) {
super(msg);
this.exception = x;
}
Exception getException() {
return exception;
}
/**
* use the exception chaining mechanism of JDK1.4
*/
@Override
public Throwable getCause() {
return exception;
}
}
} }

View File

@ -23,14 +23,14 @@
*/ */
/* /*
* Copyright (c) 2009 by Oracle Corporation. All Rights Reserved. * Copyright (c) 2009, 2013, by Oracle Corporation. All Rights Reserved.
*/ */
package javax.xml.stream; package javax.xml.stream;
import javax.xml.stream.events.*; import java.util.Iterator;
import javax.xml.namespace.NamespaceContext; import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName; import javax.xml.namespace.QName;
import java.util.Iterator; import javax.xml.stream.events.*;
/** /**
* This interface defines a utility class for creating instances of * This interface defines a utility class for creating instances of
* XMLEvents * XMLEvents
@ -54,48 +54,59 @@ public abstract class XMLEventFactory {
/** /**
* Create a new instance of the factory * Creates a new instance of the factory in exactly the same manner as the
* {@link #newFactory()} method.
* @throws FactoryConfigurationError if an instance of this factory cannot be loaded * @throws FactoryConfigurationError if an instance of this factory cannot be loaded
*/ */
public static XMLEventFactory newInstance() public static XMLEventFactory newInstance()
throws FactoryConfigurationError throws FactoryConfigurationError
{ {
return (XMLEventFactory) FactoryFinder.find( return FactoryFinder.find(XMLEventFactory.class, DEFAULIMPL);
JAXPFACTORYID,
DEFAULIMPL);
} }
/** /**
* Create a new instance of the factory. * Create a new instance of the factory.
* <p>
* This static method creates a new factory instance. * This static method creates a new factory instance.
* This method uses the following ordered lookup procedure to determine * This method uses the following ordered lookup procedure to determine
* the XMLEventFactory implementation class to load: * the XMLEventFactory implementation class to load:
* </p>
* <ul>
* <li>
* Use the javax.xml.stream.XMLEventFactory system property. * Use the javax.xml.stream.XMLEventFactory system property.
* </li>
* <li>
* Use the properties file "lib/stax.properties" in the JRE directory. * Use the properties file "lib/stax.properties" in the JRE directory.
* This configuration file is in standard java.util.Properties format * This configuration file is in standard java.util.Properties format
* and contains the fully qualified name of the implementation class * and contains the fully qualified name of the implementation class
* with the key being the system property defined above. * with the key being the system property defined above.
* Use the Services API (as detailed in the JAR specification), if available, * </li>
* to determine the classname. The Services API will look for a classname * <li>
* in the file META-INF/services/javax.xml.stream.XMLEventFactory in jars * Use the service-provider loading facilities, defined by the
* available to the runtime. * {@link java.util.ServiceLoader} class, to attempt to locate and load an
* Platform default XMLEventFactory instance. * implementation of the service.
* * </li>
* <li>
* Otherwise, the system-default implementation is returned.
* </li>
* </ul>
* <p>
* Once an application has obtained a reference to a XMLEventFactory it * Once an application has obtained a reference to a XMLEventFactory it
* can use the factory to configure and obtain stream instances. * can use the factory to configure and obtain stream instances.
* * </p>
* <p>
* Note that this is a new method that replaces the deprecated newInstance() method. * Note that this is a new method that replaces the deprecated newInstance() method.
* No changes in behavior are defined by this replacement method relative to * No changes in behavior are defined by this replacement method relative to
* the deprecated method. * the deprecated method.
* * </p>
* @throws FactoryConfigurationError if an instance of this factory cannot be loaded * @throws FactoryConfigurationError in case of {@linkplain
* java.util.ServiceConfigurationError service configuration error} or if
* the implementation is not available or cannot be instantiated.
*/ */
public static XMLEventFactory newFactory() public static XMLEventFactory newFactory()
throws FactoryConfigurationError throws FactoryConfigurationError
{ {
return (XMLEventFactory) FactoryFinder.find( return FactoryFinder.find(XMLEventFactory.class, DEFAULIMPL);
JAXPFACTORYID,
DEFAULIMPL);
} }
/** /**
@ -116,40 +127,59 @@ public abstract class XMLEventFactory {
public static XMLEventFactory newInstance(String factoryId, public static XMLEventFactory newInstance(String factoryId,
ClassLoader classLoader) ClassLoader classLoader)
throws FactoryConfigurationError { throws FactoryConfigurationError {
try { //do not fallback if given classloader can't find the class, throw exception
//do not fallback if given classloader can't find the class, throw exception return FactoryFinder.find(XMLEventFactory.class, factoryId, classLoader, null);
return (XMLEventFactory) FactoryFinder.find(factoryId, classLoader, null);
} catch (FactoryFinder.ConfigurationError e) {
throw new FactoryConfigurationError(e.getException(),
e.getMessage());
}
} }
/** /**
* Create a new instance of the factory. * Create a new instance of the factory.
* If the classLoader argument is null, then the ContextClassLoader is used. * If the classLoader argument is null, then the ContextClassLoader is used.
* <p>
* This method uses the following ordered lookup procedure to determine
* the XMLEventFactory implementation class to load:
* </p>
* <ul>
* <li>
* Use the value of the system property identified by {@code factoryId}.
* </li>
* <li>
* Use the properties file "lib/stax.properties" in the JRE directory.
* This configuration file is in standard java.util.Properties format
* and contains the fully qualified name of the implementation class
* with the key being the given {@code factoryId}.
* </li>
* <li>
* If {@code factoryId} is "javax.xml.stream.XMLEventFactory",
* use the service-provider loading facilities, defined by the
* {@link java.util.ServiceLoader} class, to attempt to locate and load an
* implementation of the service.
* </li>
* <li>
* Otherwise, throws a {@link FactoryConfigurationError}.
* </li>
* </ul>
* *
* <p>
* Note that this is a new method that replaces the deprecated * Note that this is a new method that replaces the deprecated
* newInstance(String factoryId, ClassLoader classLoader) method. * {@link #newInstance(java.lang.String, java.lang.ClassLoader)
* newInstance(String factoryId, ClassLoader classLoader)} method.
* No changes in behavior are defined by this replacement method relative * No changes in behavior are defined by this replacement method relative
* to the deprecated method. * to the deprecated method.
* </p>
* *
* @param factoryId Name of the factory to find, same as * @param factoryId Name of the factory to find, same as
* a property name * a property name
* @param classLoader classLoader to use * @param classLoader classLoader to use
* @return the factory implementation * @return the factory implementation
* @throws FactoryConfigurationError if an instance of this factory cannot be loaded * @throws FactoryConfigurationError in case of {@linkplain
* java.util.ServiceConfigurationError service configuration error} or if
* the implementation is not available or cannot be instantiated.
*/ */
public static XMLEventFactory newFactory(String factoryId, public static XMLEventFactory newFactory(String factoryId,
ClassLoader classLoader) ClassLoader classLoader)
throws FactoryConfigurationError { throws FactoryConfigurationError {
try { //do not fallback if given classloader can't find the class, throw exception
//do not fallback if given classloader can't find the class, throw exception return FactoryFinder.find(XMLEventFactory.class, factoryId, classLoader, null);
return (XMLEventFactory) FactoryFinder.find(factoryId, classLoader, null);
} catch (FactoryFinder.ConfigurationError e) {
throw new FactoryConfigurationError(e.getException(),
e.getMessage());
}
} }
/** /**

View File

@ -23,13 +23,13 @@
*/ */
/* /*
* Copyright (c) 2009 by Oracle Corporation. All Rights Reserved. * Copyright (c) 2009, 2013, by Oracle Corporation. All Rights Reserved.
*/ */
package javax.xml.stream; package javax.xml.stream;
import javax.xml.transform.Source;
import javax.xml.stream.util.XMLEventAllocator; import javax.xml.stream.util.XMLEventAllocator;
import javax.xml.transform.Source;
/** /**
* Defines an abstract implementation of a factory for getting streams. * Defines an abstract implementation of a factory for getting streams.
@ -144,48 +144,59 @@ public abstract class XMLInputFactory {
protected XMLInputFactory(){} protected XMLInputFactory(){}
/** /**
* Create a new instance of the factory. * Creates a new instance of the factory in exactly the same manner as the
* {@link #newFactory()} method.
* @throws FactoryConfigurationError if an instance of this factory cannot be loaded * @throws FactoryConfigurationError if an instance of this factory cannot be loaded
*/ */
public static XMLInputFactory newInstance() public static XMLInputFactory newInstance()
throws FactoryConfigurationError throws FactoryConfigurationError
{ {
return (XMLInputFactory) FactoryFinder.find( return FactoryFinder.find(XMLInputFactory.class, DEFAULIMPL);
"javax.xml.stream.XMLInputFactory",
DEFAULIMPL);
} }
/** /**
* Create a new instance of the factory. * Create a new instance of the factory.
* <p>
* This static method creates a new factory instance. * This static method creates a new factory instance.
* This method uses the following ordered lookup procedure to determine * This method uses the following ordered lookup procedure to determine
* the XMLInputFactory implementation class to load: * the XMLInputFactory implementation class to load:
* </p>
* <ul>
* <li>
* Use the javax.xml.stream.XMLInputFactory system property. * Use the javax.xml.stream.XMLInputFactory system property.
* </li>
* <li>
* Use the properties file "lib/stax.properties" in the JRE directory. * Use the properties file "lib/stax.properties" in the JRE directory.
* This configuration file is in standard java.util.Properties format * This configuration file is in standard java.util.Properties format
* and contains the fully qualified name of the implementation class * and contains the fully qualified name of the implementation class
* with the key being the system property defined above. * with the key being the system property defined above.
* Use the Services API (as detailed in the JAR specification), if available, * </li>
* to determine the classname. The Services API will look for a classname * <li>
* in the file META-INF/services/javax.xml.stream.XMLInputFactory in jars * Use the service-provider loading facilities, defined by the
* available to the runtime. * {@link java.util.ServiceLoader} class, to attempt to locate and load an
* Platform default XMLInputFactory instance. * implementation of the service.
* * </li>
* <li>
* Otherwise, the system-default implementation is returned.
* </li>
* </ul>
* <p>
* Once an application has obtained a reference to a XMLInputFactory it * Once an application has obtained a reference to a XMLInputFactory it
* can use the factory to configure and obtain stream instances. * can use the factory to configure and obtain stream instances.
* * </p>
* <p>
* Note that this is a new method that replaces the deprecated newInstance() method. * Note that this is a new method that replaces the deprecated newInstance() method.
* No changes in behavior are defined by this replacement method relative to * No changes in behavior are defined by this replacement method relative to
* the deprecated method. * the deprecated method.
* * </p>
* @throws FactoryConfigurationError if an instance of this factory cannot be loaded * @throws FactoryConfigurationError in case of {@linkplain
* java.util.ServiceConfigurationError service configuration error} or if
* the implementation is not available or cannot be instantiated.
*/ */
public static XMLInputFactory newFactory() public static XMLInputFactory newFactory()
throws FactoryConfigurationError throws FactoryConfigurationError
{ {
return (XMLInputFactory) FactoryFinder.find( return FactoryFinder.find(XMLInputFactory.class, DEFAULIMPL);
"javax.xml.stream.XMLInputFactory",
DEFAULIMPL);
} }
/** /**
@ -206,40 +217,60 @@ public abstract class XMLInputFactory {
public static XMLInputFactory newInstance(String factoryId, public static XMLInputFactory newInstance(String factoryId,
ClassLoader classLoader) ClassLoader classLoader)
throws FactoryConfigurationError { throws FactoryConfigurationError {
try { //do not fallback if given classloader can't find the class, throw exception
//do not fallback if given classloader can't find the class, throw exception return FactoryFinder.find(XMLInputFactory.class, factoryId, classLoader, null);
return (XMLInputFactory) FactoryFinder.find(factoryId, classLoader, null);
} catch (FactoryFinder.ConfigurationError e) {
throw new FactoryConfigurationError(e.getException(),
e.getMessage());
}
} }
/** /**
* Create a new instance of the factory. * Create a new instance of the factory.
* If the classLoader argument is null, then the ContextClassLoader is used. * If the classLoader argument is null, then the ContextClassLoader is used.
* <p>
* This method uses the following ordered lookup procedure to determine
* the XMLInputFactory implementation class to load:
* </p>
* <ul>
* <li>
* Use the value of the system property identified by {@code factoryId}.
* </li>
* <li>
* Use the properties file "lib/stax.properties" in the JRE directory.
* This configuration file is in standard java.util.Properties format
* and contains the fully qualified name of the implementation class
* with the key being the given {@code factoryId}.
* </li>
* <li>
* If {@code factoryId} is "javax.xml.stream.XMLInputFactory",
* use the service-provider loading facilities, defined by the
* {@link java.util.ServiceLoader} class, to attempt to locate and load an
* implementation of the service.
* </li>
* <li>
* Otherwise, throws a {@link FactoryConfigurationError}.
* </li>
* </ul>
* *
* <p>
* Note that this is a new method that replaces the deprecated * Note that this is a new method that replaces the deprecated
* newInstance(String factoryId, ClassLoader classLoader) method. * {@link #newInstance(java.lang.String, java.lang.ClassLoader)
* newInstance(String factoryId, ClassLoader classLoader)} method.
* No changes in behavior are defined by this replacement method relative * No changes in behavior are defined by this replacement method relative
* to the deprecated method. * to the deprecated method.
* </p>
* *
* @param factoryId Name of the factory to find, same as * @param factoryId Name of the factory to find, same as
* a property name * a property name
* @param classLoader classLoader to use * @param classLoader classLoader to use
* @return the factory implementation * @return the factory implementation
* @throws FactoryConfigurationError in case of {@linkplain
* java.util.ServiceConfigurationError service configuration error} or if
* the implementation is not available or cannot be instantiated.
* @throws FactoryConfigurationError if an instance of this factory cannot be loaded * @throws FactoryConfigurationError if an instance of this factory cannot be loaded
*/ */
public static XMLInputFactory newFactory(String factoryId, public static XMLInputFactory newFactory(String factoryId,
ClassLoader classLoader) ClassLoader classLoader)
throws FactoryConfigurationError { throws FactoryConfigurationError {
try { //do not fallback if given classloader can't find the class, throw exception
//do not fallback if given classloader can't find the class, throw exception return FactoryFinder.find(XMLInputFactory.class, factoryId, classLoader, null);
return (XMLInputFactory) FactoryFinder.find(factoryId, classLoader, null);
} catch (FactoryFinder.ConfigurationError e) {
throw new FactoryConfigurationError(e.getException(),
e.getMessage());
}
} }
/** /**

View File

@ -23,7 +23,7 @@
*/ */
/* /*
* Copyright (c) 2009 by Oracle Corporation. All Rights Reserved. * Copyright (c) 2009, 2013, by Oracle Corporation. All Rights Reserved.
*/ */
package javax.xml.stream; package javax.xml.stream;
@ -120,46 +120,58 @@ public abstract class XMLOutputFactory {
protected XMLOutputFactory(){} protected XMLOutputFactory(){}
/** /**
* Create a new instance of the factory. * Creates a new instance of the factory in exactly the same manner as the
* {@link #newFactory()} method.
* @throws FactoryConfigurationError if an instance of this factory cannot be loaded * @throws FactoryConfigurationError if an instance of this factory cannot be loaded
*/ */
public static XMLOutputFactory newInstance() public static XMLOutputFactory newInstance()
throws FactoryConfigurationError throws FactoryConfigurationError
{ {
return (XMLOutputFactory) FactoryFinder.find("javax.xml.stream.XMLOutputFactory", return FactoryFinder.find(XMLOutputFactory.class, DEFAULIMPL);
DEFAULIMPL);
} }
/** /**
* Create a new instance of the factory. * Create a new instance of the factory.
* <p>
* This static method creates a new factory instance. This method uses the * This static method creates a new factory instance. This method uses the
* following ordered lookup procedure to determine the XMLOutputFactory * following ordered lookup procedure to determine the XMLOutputFactory
* implementation class to load: * implementation class to load:
* </p>
* <ul>
* <li>
* Use the javax.xml.stream.XMLOutputFactory system property. * Use the javax.xml.stream.XMLOutputFactory system property.
* </li>
* <li>
* Use the properties file "lib/stax.properties" in the JRE directory. * Use the properties file "lib/stax.properties" in the JRE directory.
* This configuration file is in standard java.util.Properties format * This configuration file is in standard java.util.Properties format
* and contains the fully qualified name of the implementation class * and contains the fully qualified name of the implementation class
* with the key being the system property defined above. * with the key being the system property defined above.
* Use the Services API (as detailed in the JAR specification), if available, * </li>
* to determine the classname. The Services API will look for a classname * <li>
* in the file META-INF/services/javax.xml.stream.XMLOutputFactory in jars * Use the service-provider loading facilities, defined by the
* available to the runtime. * {@link java.util.ServiceLoader} class, to attempt to locate and load an
* Platform default XMLOutputFactory instance. * implementation of the service.
* * </li>
* <li>
* Otherwise, the system-default implementation is returned.
* </li>
* <p>
* Once an application has obtained a reference to a XMLOutputFactory it * Once an application has obtained a reference to a XMLOutputFactory it
* can use the factory to configure and obtain stream instances. * can use the factory to configure and obtain stream instances.
* * </p>
* <p>
* Note that this is a new method that replaces the deprecated newInstance() method. * Note that this is a new method that replaces the deprecated newInstance() method.
* No changes in behavior are defined by this replacement method relative to the * No changes in behavior are defined by this replacement method relative to the
* deprecated method. * deprecated method.
* * </p>
* @throws FactoryConfigurationError if an instance of this factory cannot be loaded * @throws FactoryConfigurationError in case of {@linkplain
* java.util.ServiceConfigurationError service configuration error} or if
* the implementation is not available or cannot be instantiated.
*/ */
public static XMLOutputFactory newFactory() public static XMLOutputFactory newFactory()
throws FactoryConfigurationError throws FactoryConfigurationError
{ {
return (XMLOutputFactory) FactoryFinder.find("javax.xml.stream.XMLOutputFactory", return FactoryFinder.find(XMLOutputFactory.class, DEFAULIMPL);
DEFAULIMPL);
} }
/** /**
@ -179,42 +191,59 @@ public abstract class XMLOutputFactory {
public static XMLInputFactory newInstance(String factoryId, public static XMLInputFactory newInstance(String factoryId,
ClassLoader classLoader) ClassLoader classLoader)
throws FactoryConfigurationError { throws FactoryConfigurationError {
try { //do not fallback if given classloader can't find the class, throw exception
//do not fallback if given classloader can't find the class, throw exception return FactoryFinder.find(XMLInputFactory.class, factoryId, classLoader, null);
return (XMLInputFactory) FactoryFinder.find(factoryId, classLoader, null);
} catch (FactoryFinder.ConfigurationError e) {
throw new FactoryConfigurationError(e.getException(),
e.getMessage());
}
} }
/** /**
* Create a new instance of the factory. * Create a new instance of the factory.
* If the classLoader argument is null, then the ContextClassLoader is used. * If the classLoader argument is null, then the ContextClassLoader is used.
* <p>
* This method uses the following ordered lookup procedure to determine
* the XMLOutputFactory implementation class to load:
* </p>
* <ul>
* <li>
* Use the value of the system property identified by {@code factoryId}.
* </li>
* <li>
* Use the properties file "lib/stax.properties" in the JRE directory.
* This configuration file is in standard java.util.Properties format
* and contains the fully qualified name of the implementation class
* with the key being the given {@code factoryId}.
* </li>
* <li>
* If {@code factoryId} is "javax.xml.stream.XMLOutputFactory",
* use the service-provider loading facilities, defined by the
* {@link java.util.ServiceLoader} class, to attempt to locate and load an
* implementation of the service.
* </li>
* <li>
* Otherwise, throws a {@link FactoryConfigurationError}.
* </li>
* </ul>
* *
* <p>
* Note that this is a new method that replaces the deprecated * Note that this is a new method that replaces the deprecated
* newInstance(String factoryId, ClassLoader classLoader) method. * {@link #newInstance(java.lang.String, java.lang.ClassLoader)
* * newInstance(String factoryId, ClassLoader classLoader)} method.
* No changes in behavior are defined by this replacement method relative * No changes in behavior are defined by this replacement method relative
* to the deprecated method. * to the deprecated method.
* * </p>
* *
* @param factoryId Name of the factory to find, same as * @param factoryId Name of the factory to find, same as
* a property name * a property name
* @param classLoader classLoader to use * @param classLoader classLoader to use
* @return the factory implementation * @return the factory implementation
* @throws FactoryConfigurationError if an instance of this factory cannot be loaded * @throws FactoryConfigurationError in case of {@linkplain
* java.util.ServiceConfigurationError service configuration error} or if
* the implementation is not available or cannot be instantiated.
*/ */
public static XMLOutputFactory newFactory(String factoryId, public static XMLOutputFactory newFactory(String factoryId,
ClassLoader classLoader) ClassLoader classLoader)
throws FactoryConfigurationError { throws FactoryConfigurationError {
try { //do not fallback if given classloader can't find the class, throw exception
//do not fallback if given classloader can't find the class, throw exception return FactoryFinder.find(XMLOutputFactory.class, factoryId, classLoader, null);
return (XMLOutputFactory) FactoryFinder.find(factoryId, classLoader, null);
} catch (FactoryFinder.ConfigurationError e) {
throw new FactoryConfigurationError(e.getException(),
e.getMessage());
}
} }
/** /**

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -25,13 +25,15 @@
package javax.xml.transform; package javax.xml.transform;
import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Iterator;
import java.util.Properties; import java.util.Properties;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
/** /**
* <p>Implements pluggable Datatypes.</p> * <p>Implements pluggable Datatypes.</p>
@ -53,7 +55,7 @@ class FactoryFinder {
/** /**
* Cache for properties in java.home/lib/jaxp.properties * Cache for properties in java.home/lib/jaxp.properties
*/ */
static Properties cacheProps = new Properties(); private final static Properties cacheProps = new Properties();
/** /**
* Flag indicating if properties from java.home/lib/jaxp.properties * Flag indicating if properties from java.home/lib/jaxp.properties
@ -65,7 +67,7 @@ class FactoryFinder {
* Security support class use to check access control before * Security support class use to check access control before
* getting certain system resources. * getting certain system resources.
*/ */
static SecuritySupport ss = new SecuritySupport(); private final static SecuritySupport ss = new SecuritySupport();
// Define system property "jaxp.debug" to get output // Define system property "jaxp.debug" to get output
static { static {
@ -98,31 +100,31 @@ class FactoryFinder {
* *
* Use bootstrap classLoader if cl = null and useBSClsLoader is true * Use bootstrap classLoader if cl = null and useBSClsLoader is true
*/ */
static private Class getProviderClass(String className, ClassLoader cl, static private Class<?> getProviderClass(String className, ClassLoader cl,
boolean doFallback, boolean useBSClsLoader) throws ClassNotFoundException boolean doFallback, boolean useBSClsLoader) throws ClassNotFoundException
{ {
try { try {
if (cl == null) { if (cl == null) {
if (useBSClsLoader) { if (useBSClsLoader) {
return Class.forName(className, true, FactoryFinder.class.getClassLoader()); return Class.forName(className, false, FactoryFinder.class.getClassLoader());
} else { } else {
cl = ss.getContextClassLoader(); cl = ss.getContextClassLoader();
if (cl == null) { if (cl == null) {
throw new ClassNotFoundException(); throw new ClassNotFoundException();
} }
else { else {
return cl.loadClass(className); return Class.forName(className, false, cl);
} }
} }
} }
else { else {
return cl.loadClass(className); return Class.forName(className, false, cl);
} }
} }
catch (ClassNotFoundException e1) { catch (ClassNotFoundException e1) {
if (doFallback) { if (doFallback) {
// Use current class loader - should always be bootstrap CL // Use current class loader - should always be bootstrap CL
return Class.forName(className, true, FactoryFinder.class.getClassLoader()); return Class.forName(className, false, FactoryFinder.class.getClassLoader());
} }
else { else {
throw e1; throw e1;
@ -134,24 +136,8 @@ class FactoryFinder {
* Create an instance of a class. Delegates to method * Create an instance of a class. Delegates to method
* <code>getProviderClass()</code> in order to load the class. * <code>getProviderClass()</code> in order to load the class.
* *
* @param className Name of the concrete class corresponding to the * @param type Base class / Service interface of the factory to
* service provider * instantiate.
*
* @param cl <code>ClassLoader</code> used to load the factory class. If <code>null</code>
* current <code>Thread</code>'s context classLoader is used to load the factory class.
*
* @param doFallback True if the current ClassLoader should be tried as
* a fallback if the class is not found using cl
*/
static Object newInstance(String className, ClassLoader cl, boolean doFallback)
throws ConfigurationError
{
return newInstance(className, cl, doFallback, false, false);
}
/**
* Create an instance of a class. Delegates to method
* <code>getProviderClass()</code> in order to load the class.
* *
* @param className Name of the concrete class corresponding to the * @param className Name of the concrete class corresponding to the
* service provider * service provider
@ -162,14 +148,15 @@ class FactoryFinder {
* @param doFallback True if the current ClassLoader should be tried as * @param doFallback True if the current ClassLoader should be tried as
* a fallback if the class is not found using cl * a fallback if the class is not found using cl
* *
* @param useBSClsLoader True if cl=null actually meant bootstrap classLoader. This parameter
* is needed since DocumentBuilderFactory/SAXParserFactory defined null as context classLoader.
*
* @param useServicesMechanism True use services mechanism * @param useServicesMechanism True use services mechanism
*/ */
static Object newInstance(String className, ClassLoader cl, boolean doFallback, boolean useBSClsLoader, boolean useServicesMechanism) static <T> T newInstance(Class<T> type, String className, ClassLoader cl,
throws ConfigurationError boolean doFallback, boolean useServicesMechanism)
throws TransformerFactoryConfigurationError
{ {
assert type != null;
boolean useBSClsLoader = false;
// make sure we have access to restricted packages // make sure we have access to restricted packages
if (System.getSecurityManager() != null) { if (System.getSecurityManager() != null) {
if (className != null && className.startsWith(DEFAULT_PACKAGE)) { if (className != null && className.startsWith(DEFAULT_PACKAGE)) {
@ -179,10 +166,13 @@ class FactoryFinder {
} }
try { try {
Class providerClass = getProviderClass(className, cl, doFallback, useBSClsLoader); Class<?> providerClass = getProviderClass(className, cl, doFallback, useBSClsLoader);
if (!type.isAssignableFrom(providerClass)) {
throw new ClassCastException(className + " cannot be cast to " + type.getName());
}
Object instance = null; Object instance = null;
if (!useServicesMechanism) { if (!useServicesMechanism) {
instance = newInstanceNoServiceLoader(providerClass); instance = newInstanceNoServiceLoader(type, providerClass);
} }
if (instance == null) { if (instance == null) {
instance = providerClass.newInstance(); instance = providerClass.newInstance();
@ -191,63 +181,87 @@ class FactoryFinder {
dPrint("created new instance of " + providerClass + dPrint("created new instance of " + providerClass +
" using ClassLoader: " + cl); " using ClassLoader: " + cl);
} }
return instance; return type.cast(instance);
} }
catch (ClassNotFoundException x) { catch (ClassNotFoundException x) {
throw new ConfigurationError( throw new TransformerFactoryConfigurationError(x,
"Provider " + className + " not found", x); "Provider " + className + " not found");
} }
catch (Exception x) { catch (Exception x) {
throw new ConfigurationError( throw new TransformerFactoryConfigurationError(x,
"Provider " + className + " could not be instantiated: " + x, "Provider " + className + " could not be instantiated: " + x);
x);
} }
} }
/** /**
* Try to construct using newTransformerFactoryNoServiceLoader * Try to construct using newTransformerFactoryNoServiceLoader
* method if available. * method if available.
*/ */
private static Object newInstanceNoServiceLoader( private static <T> T newInstanceNoServiceLoader(Class<T> type, Class<?> providerClass) {
Class<?> providerClass
) {
// Retain maximum compatibility if no security manager. // Retain maximum compatibility if no security manager.
if (System.getSecurityManager() == null) { if (System.getSecurityManager() == null) {
return null; return null;
} }
try { try {
Method creationMethod = final Method creationMethod =
providerClass.getDeclaredMethod( providerClass.getDeclaredMethod(
"newTransformerFactoryNoServiceLoader" "newTransformerFactoryNoServiceLoader"
); );
return creationMethod.invoke(null, (Object[])null); final int modifiers = creationMethod.getModifiers();
} catch (NoSuchMethodException exc) {
return null; // Do not call the method if it's not public static.
} catch (Exception exc) { if (!Modifier.isPublic(modifiers) || !Modifier.isStatic(modifiers)) {
return null; return null;
}
// Only call the method if it's declared to return an instance of
// TransformerFactory
final Class<?> returnType = creationMethod.getReturnType();
if (type.isAssignableFrom(returnType)) {
final Object result = creationMethod.invoke(null, (Object[])null);
return type.cast(result);
} else {
// This should not happen, as
// TransformerFactoryImpl.newTransformerFactoryNoServiceLoader is
// declared to return TransformerFactory.
throw new ClassCastException(returnType + " cannot be cast to " + type);
}
} catch (ClassCastException e) {
throw new TransformerFactoryConfigurationError(e, e.getMessage());
} catch (NoSuchMethodException exc) {
return null;
} catch (Exception exc) {
return null;
} }
} }
/** /**
* Finds the implementation Class object in the specified order. Main * Finds the implementation Class object in the specified order. Main
* entry point. * entry point.
* @return Class object of factory, never null * @return Class object of factory, never null
* *
* @param factoryId Name of the factory to find, same as * @param type Base class / Service interface of the
* a property name * factory to find.
*
* @param fallbackClassName Implementation class name, if nothing else * @param fallbackClassName Implementation class name, if nothing else
* is found. Use null to mean no fallback. * is found. Use null to mean no fallback.
* *
* Package private so this code can be shared. * Package private so this code can be shared.
*/ */
static Object find(String factoryId, String fallbackClassName) static <T> T find(Class<T> type, String fallbackClassName)
throws ConfigurationError throws TransformerFactoryConfigurationError
{ {
assert type != null;
final String factoryId = type.getName();
dPrint("find factoryId =" + factoryId); dPrint("find factoryId =" + factoryId);
// Use the system property first // Use the system property first
try { try {
String systemProp = ss.getSystemProperty(factoryId); String systemProp = ss.getSystemProperty(factoryId);
if (systemProp != null) { if (systemProp != null) {
dPrint("found system property, value=" + systemProp); dPrint("found system property, value=" + systemProp);
return newInstance(systemProp, null, true, false, true); return newInstance(type, systemProp, null, true, true);
} }
} }
catch (SecurityException se) { catch (SecurityException se) {
@ -256,7 +270,6 @@ class FactoryFinder {
// try to read from $java.home/lib/jaxp.properties // try to read from $java.home/lib/jaxp.properties
try { try {
String factoryClassName = null;
if (firstTime) { if (firstTime) {
synchronized (cacheProps) { synchronized (cacheProps) {
if (firstTime) { if (firstTime) {
@ -271,11 +284,11 @@ class FactoryFinder {
} }
} }
} }
factoryClassName = cacheProps.getProperty(factoryId); final String factoryClassName = cacheProps.getProperty(factoryId);
if (factoryClassName != null) { if (factoryClassName != null) {
dPrint("found in $java.home/jaxp.properties, value=" + factoryClassName); dPrint("found in $java.home/jaxp.properties, value=" + factoryClassName);
return newInstance(factoryClassName, null, true, false, true); return newInstance(type, factoryClassName, null, true, true);
} }
} }
catch (Exception ex) { catch (Exception ex) {
@ -283,113 +296,54 @@ class FactoryFinder {
} }
// Try Jar Service Provider Mechanism // Try Jar Service Provider Mechanism
Object provider = findJarServiceProvider(factoryId); T provider = findServiceProvider(type);
if (provider != null) { if (provider != null) {
return provider; return provider;
} }
if (fallbackClassName == null) { if (fallbackClassName == null) {
throw new ConfigurationError( throw new TransformerFactoryConfigurationError(null,
"Provider for " + factoryId + " cannot be found", null); "Provider for " + factoryId + " cannot be found");
} }
dPrint("loaded from fallback value: " + fallbackClassName); dPrint("loaded from fallback value: " + fallbackClassName);
return newInstance(fallbackClassName, null, true, false, true); return newInstance(type, fallbackClassName, null, true, true);
} }
/* /*
* Try to find provider using Jar Service Provider Mechanism * Try to find provider using the ServiceLoader.
*
* @param type Base class / Service interface of the factory to find.
* *
* @return instance of provider class if found or null * @return instance of provider class if found or null
*/ */
private static Object findJarServiceProvider(String factoryId) private static <T> T findServiceProvider(final Class<T> type)
throws ConfigurationError throws TransformerFactoryConfigurationError
{ {
String serviceId = "META-INF/services/" + factoryId; try {
InputStream is = null; return AccessController.doPrivileged(new PrivilegedAction<T>() {
public T run() {
// First try the Context ClassLoader final ServiceLoader<T> serviceLoader = ServiceLoader.load(type);
ClassLoader cl = ss.getContextClassLoader(); final Iterator<T> iterator = serviceLoader.iterator();
boolean useBSClsLoader = false; if (iterator.hasNext()) {
if (cl != null) { return iterator.next();
is = ss.getResourceAsStream(cl, serviceId); } else {
return null;
// If no provider found then try the current ClassLoader }
if (is == null) { }
cl = FactoryFinder.class.getClassLoader(); });
is = ss.getResourceAsStream(cl, serviceId); } catch(ServiceConfigurationError e) {
useBSClsLoader = true; // It is not possible to wrap an error directly in
} // FactoryConfigurationError - so we need to wrap the
} else { // ServiceConfigurationError in a RuntimeException.
// No Context ClassLoader, try the current ClassLoader // The alternative would be to modify the logic in
cl = FactoryFinder.class.getClassLoader(); // FactoryConfigurationError to allow setting a
is = ss.getResourceAsStream(cl, serviceId); // Throwable as the cause, but that could cause
useBSClsLoader = true; // compatibility issues down the road.
} final RuntimeException x = new RuntimeException(
"Provider for " + type + " cannot be created", e);
if (is == null) { final TransformerFactoryConfigurationError error =
// No provider found new TransformerFactoryConfigurationError(x, x.getMessage());
return null; throw error;
}
if (debug) { // Extra check to avoid computing cl strings
dPrint("found jar resource=" + serviceId + " using ClassLoader: " + cl);
}
BufferedReader rd;
try {
rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
}
catch (java.io.UnsupportedEncodingException e) {
rd = new BufferedReader(new InputStreamReader(is));
}
String factoryClassName = null;
try {
// XXX Does not handle all possible input as specified by the
// Jar Service Provider specification
factoryClassName = rd.readLine();
rd.close();
} catch (IOException x) {
// No provider found
return null;
}
if (factoryClassName != null && !"".equals(factoryClassName)) {
dPrint("found in resource, value=" + factoryClassName);
// Note: here we do not want to fall back to the current
// ClassLoader because we want to avoid the case where the
// resource file was found using one ClassLoader and the
// provider class was instantiated using a different one.
return newInstance(factoryClassName, cl, false, useBSClsLoader, true);
}
// No provider found
return null;
}
static class ConfigurationError extends Error {
private Exception exception;
/**
* Construct a new instance with the specified detail string and
* exception.
*/
ConfigurationError(String msg, Exception x) {
super(msg);
this.exception = x;
}
Exception getException() {
return exception;
}
/**
* use the exception chaining mechanism of JDK1.4
*/
@Override
public Throwable getCause() {
return exception;
} }
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -52,8 +52,8 @@ public abstract class TransformerFactory {
/** /**
* <p>Obtain a new instance of a <code>TransformerFactory</code>. * <p>Obtain a new instance of a <code>TransformerFactory</code>.
* This static method creates a new factory instance * This static method creates a new factory instance.</p>
* This method uses the following ordered lookup procedure to determine * <p>This method uses the following ordered lookup procedure to determine
* the <code>TransformerFactory</code> implementation class to * the <code>TransformerFactory</code> implementation class to
* load:</p> * load:</p>
* <ul> * <ul>
@ -67,7 +67,7 @@ public abstract class TransformerFactory {
* </code> format and contains the fully qualified name of the * </code> format and contains the fully qualified name of the
* implementation class with the key being the system property defined * implementation class with the key being the system property defined
* above. * above.
* * <br>
* The jaxp.properties file is read only once by the JAXP implementation * The jaxp.properties file is read only once by the JAXP implementation
* and it's values are then cached for future use. If the file does not exist * and it's values are then cached for future use. If the file does not exist
* when the first attempt is made to read from it, no further attempts are * when the first attempt is made to read from it, no further attempts are
@ -75,14 +75,12 @@ public abstract class TransformerFactory {
* of any property in jaxp.properties after it has been read for the first time. * of any property in jaxp.properties after it has been read for the first time.
* </li> * </li>
* <li> * <li>
* Use the Services API (as detailed in the JAR specification), if * Use the service-provider loading facilities, defined by the
* available, to determine the classname. The Services API will look * {@link java.util.ServiceLoader} class, to attempt to locate and load an
* for a classname in the file * implementation of the service.
* <code>META-INF/services/javax.xml.transform.TransformerFactory</code>
* in jars available to the runtime.
* </li> * </li>
* <li> * <li>
* Platform default <code>TransformerFactory</code> instance. * Otherwise, the system-default implementation is returned.
* </li> * </li>
* </ul> * </ul>
* *
@ -92,22 +90,18 @@ public abstract class TransformerFactory {
* *
* @return new TransformerFactory instance, never null. * @return new TransformerFactory instance, never null.
* *
* @throws TransformerFactoryConfigurationError Thrown if the implementation * @throws TransformerFactoryConfigurationError Thrown in case of {@linkplain
* is not available or cannot be instantiated. * java.util.ServiceConfigurationError service configuration error} or if
* the implementation is not available or cannot be instantiated.
*/ */
public static TransformerFactory newInstance() public static TransformerFactory newInstance()
throws TransformerFactoryConfigurationError { throws TransformerFactoryConfigurationError {
try {
return (TransformerFactory) FactoryFinder.find( return FactoryFinder.find(
/* The default property name according to the JAXP spec */ /* The default property name according to the JAXP spec */
"javax.xml.transform.TransformerFactory", TransformerFactory.class,
/* The fallback implementation class name, XSLTC */ /* The fallback implementation class name, XSLTC */
"com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl"); "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl");
} catch (FactoryFinder.ConfigurationError e) {
throw new TransformerFactoryConfigurationError(
e.getException(),
e.getMessage());
}
} }
/** /**
@ -147,14 +141,10 @@ public abstract class TransformerFactory {
*/ */
public static TransformerFactory newInstance(String factoryClassName, ClassLoader classLoader) public static TransformerFactory newInstance(String factoryClassName, ClassLoader classLoader)
throws TransformerFactoryConfigurationError{ throws TransformerFactoryConfigurationError{
try {
//do not fallback if given classloader can't find the class, throw exception //do not fallback if given classloader can't find the class, throw exception
return (TransformerFactory) FactoryFinder.newInstance(factoryClassName, classLoader, false); return FactoryFinder.newInstance(TransformerFactory.class,
} catch (FactoryFinder.ConfigurationError e) { factoryClassName, classLoader, false, false);
throw new TransformerFactoryConfigurationError(
e.getException(),
e.getMessage());
}
} }
/** /**
* <p>Process the <code>Source</code> into a <code>Transformer</code> * <p>Process the <code>Source</code> into a <code>Transformer</code>

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -27,15 +27,14 @@ package javax.xml.validation;
import java.io.File; import java.io.File;
import java.net.URL; import java.net.URL;
import javax.xml.transform.Source; import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource; import javax.xml.transform.stream.StreamSource;
import org.w3c.dom.ls.LSResourceResolver; import org.w3c.dom.ls.LSResourceResolver;
import org.xml.sax.ErrorHandler; import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException; import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException; import org.xml.sax.SAXNotSupportedException;
import org.xml.sax.SAXParseException;
/** /**
* Factory that creates {@link Schema} objects&#x2E; Entry-point to * Factory that creates {@link Schema} objects&#x2E; Entry-point to
@ -79,7 +78,7 @@ import org.xml.sax.SAXNotSupportedException;
* and has a significant effect on the parsing process, it is impossible * and has a significant effect on the parsing process, it is impossible
* to define the DTD validation as a process independent from parsing. * to define the DTD validation as a process independent from parsing.
* For this reason, this specification does not define the semantics for * For this reason, this specification does not define the semantics for
* the XML DTD. This doesn't prohibit implentors from implementing it * the XML DTD. This doesn't prohibit implementors from implementing it
* in a way they see fit, but <em>users are warned that any DTD * in a way they see fit, but <em>users are warned that any DTD
* validation implemented on this interface necessarily deviate from * validation implemented on this interface necessarily deviate from
* the XML DTD semantics as defined in the XML 1.0</em>. * the XML DTD semantics as defined in the XML 1.0</em>.
@ -147,14 +146,17 @@ public abstract class SchemaFactory {
* is looked for. If present, the value is processed just like above. * is looked for. If present, the value is processed just like above.
* </li> * </li>
* <li> * <li>
* <p>The class loader is asked for service provider provider-configuration files matching * Use the service-provider loading facilities, defined by the
* <code>javax.xml.validation.SchemaFactory</code> in the resource directory META-INF/services. * {@link java.util.ServiceLoader} class, to attempt to locate and load an
* See the JAR File Specification for file format and parsing rules. * implementation of the service.<br>
* Each potential service provider is required to implement the method:</p> * Each potential service provider is required to implement the method
* <pre> * {@link #isSchemaLanguageSupported(String schemaLanguage)}.
* {@link #isSchemaLanguageSupported(String schemaLanguage)} * <br>
* </pre> * The first service provider found that supports the specified schema
* The first service provider found in class loader order that supports the specified schema language is returned. * language is returned.
* <br>
* In case of {@link java.util.ServiceConfigurationError} a
* {@link SchemaFactoryConfigurationError} will be thrown.
* </li> * </li>
* <li> * <li>
* Platform default <code>SchemaFactory</code> is located * Platform default <code>SchemaFactory</code> is located
@ -186,10 +188,12 @@ public abstract class SchemaFactory {
* If no implementation of the schema language is available. * If no implementation of the schema language is available.
* @throws NullPointerException * @throws NullPointerException
* If the <code>schemaLanguage</code> parameter is null. * If the <code>schemaLanguage</code> parameter is null.
* @throws SchemaFactoryConfigurationError
* If a configuration error is encountered.
* *
* @see #newInstance(String schemaLanguage, String factoryClassName, ClassLoader classLoader) * @see #newInstance(String schemaLanguage, String factoryClassName, ClassLoader classLoader)
*/ */
public static final SchemaFactory newInstance(String schemaLanguage) { public static SchemaFactory newInstance(String schemaLanguage) {
ClassLoader cl; ClassLoader cl;
cl = ss.getContextClassLoader(); cl = ss.getContextClassLoader();
@ -275,19 +279,19 @@ public abstract class SchemaFactory {
} }
/** /**
* <p>Is specified schema supported by this <code>SchemaFactory</code>?</p> * <p>Is specified schema supported by this <code>SchemaFactory</code>?</p>
* *
* @param schemaLanguage Specifies the schema language which the returned <code>SchemaFactory</code> will understand. * @param schemaLanguage Specifies the schema language which the returned <code>SchemaFactory</code> will understand.
* <code>schemaLanguage</code> must specify a <a href="#schemaLanguage">valid</a> schema language. * <code>schemaLanguage</code> must specify a <a href="#schemaLanguage">valid</a> schema language.
* *
* @return <code>true</code> if <code>SchemaFactory</code> supports <code>schemaLanguage</code>, else <code>false</code>. * @return <code>true</code> if <code>SchemaFactory</code> supports <code>schemaLanguage</code>, else <code>false</code>.
* *
* @throws NullPointerException If <code>schemaLanguage</code> is <code>null</code>. * @throws NullPointerException If <code>schemaLanguage</code> is <code>null</code>.
* @throws IllegalArgumentException If <code>schemaLanguage.length() == 0</code> * @throws IllegalArgumentException If <code>schemaLanguage.length() == 0</code>
* or <code>schemaLanguage</code> does not specify a <a href="#schemaLanguage">valid</a> schema language. * or <code>schemaLanguage</code> does not specify a <a href="#schemaLanguage">valid</a> schema language.
*/ */
public abstract boolean isSchemaLanguageSupported(String schemaLanguage); public abstract boolean isSchemaLanguageSupported(String schemaLanguage);
/** /**
* Look up the value of a feature flag. * Look up the value of a feature flag.

View File

@ -0,0 +1,80 @@
/*
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.xml.validation;
/**
* Thrown when a problem with configuration with the Schema Factories
* exists. This error will typically be thrown when the class of a
* schema factory specified in the system properties cannot be found
* or instantiated.
* @since 1.8
*/
public final class SchemaFactoryConfigurationError extends Error {
static final long serialVersionUID = 3531438703147750126L;
/**
* Create a new <code>SchemaFactoryConfigurationError</code> with no
* detail message.
*/
public SchemaFactoryConfigurationError() {
}
/**
* Create a new <code>SchemaFactoryConfigurationError</code> with
* the <code>String</code> specified as an error message.
*
* @param message The error message for the exception.
*/
public SchemaFactoryConfigurationError(String message) {
super(message);
}
/**
* Create a new <code>SchemaFactoryConfigurationError</code> with the
* given <code>Throwable</code> base cause.
*
* @param cause The exception or error to be encapsulated in a
* SchemaFactoryConfigurationError.
*/
public SchemaFactoryConfigurationError(Throwable cause) {
super(cause);
}
/**
* Create a new <code>SchemaFactoryConfigurationError</code> with the
* given <code>Throwable</code> base cause and detail message.
*
* @param cause The exception or error to be encapsulated in a
* SchemaFactoryConfigurationError.
* @param message The detail message.
*/
public SchemaFactoryConfigurationError(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -25,19 +25,16 @@
package javax.xml.validation; package javax.xml.validation;
import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.security.AccessControlContext;
import java.util.Enumeration; import java.security.AccessController;
import java.util.Iterator; import java.security.PrivilegedAction;
import java.util.NoSuchElementException;
import java.util.Properties; import java.util.Properties;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
/** /**
* Implementation of {@link SchemaFactory#newInstance(String)}. * Implementation of {@link SchemaFactory#newInstance(String)}.
@ -53,17 +50,17 @@ class SchemaFactoryFinder {
/** /**
*<p> Take care of restrictions imposed by java security model </p> *<p> Take care of restrictions imposed by java security model </p>
*/ */
private static SecuritySupport ss = new SecuritySupport(); private static final SecuritySupport ss = new SecuritySupport();
private static final String DEFAULT_PACKAGE = "com.sun.org.apache.xerces.internal"; private static final String DEFAULT_PACKAGE = "com.sun.org.apache.xerces.internal";
/** /**
* <p>Cache properties for performance.</p> * <p>Cache properties for performance.</p>
*/ */
private static Properties cacheProps = new Properties(); private static final Properties cacheProps = new Properties();
/** /**
* <p>First time requires initialization overhead.</p> * <p>First time requires initialization overhead.</p>
*/ */
private static volatile boolean firstTime = true; private static volatile boolean firstTime = true;
static { static {
// Use try/catch block to support applets // Use try/catch block to support applets
@ -115,7 +112,7 @@ class SchemaFactoryFinder {
return; return;
} }
} catch( Throwable unused ) { } catch( Throwable unused ) {
; // getContextClassLoader() undefined in JDK1.1 // getContextClassLoader() undefined in JDK1.1
} }
if( classLoader==ClassLoader.getSystemClassLoader() ) { if( classLoader==ClassLoader.getSystemClassLoader() ) {
@ -138,9 +135,13 @@ class SchemaFactoryFinder {
* *
* @throws NullPointerException * @throws NullPointerException
* If the <code>schemaLanguage</code> parameter is null. * If the <code>schemaLanguage</code> parameter is null.
* @throws SchemaFactoryConfigurationError
* If a configuration error is encountered.
*/ */
public SchemaFactory newFactory(String schemaLanguage) { public SchemaFactory newFactory(String schemaLanguage) {
if(schemaLanguage==null) throw new NullPointerException(); if(schemaLanguage==null) {
throw new NullPointerException();
}
SchemaFactory f = _newFactory(schemaLanguage); SchemaFactory f = _newFactory(schemaLanguage);
if (f != null) { if (f != null) {
debugPrintln("factory '" + f.getClass().getName() + "' was found for " + schemaLanguage); debugPrintln("factory '" + f.getClass().getName() + "' was found for " + schemaLanguage);
@ -183,7 +184,6 @@ class SchemaFactoryFinder {
String configFile = javah + File.separator + String configFile = javah + File.separator +
"lib" + File.separator + "jaxp.properties"; "lib" + File.separator + "jaxp.properties";
String factoryClassName = null ;
// try to read from $java.home/lib/jaxp.properties // try to read from $java.home/lib/jaxp.properties
try { try {
@ -199,7 +199,7 @@ class SchemaFactoryFinder {
} }
} }
} }
factoryClassName = cacheProps.getProperty(propertyName); final String factoryClassName = cacheProps.getProperty(propertyName);
debugPrintln("found " + factoryClassName + " in $java.home/jaxp.properties"); debugPrintln("found " + factoryClassName + " in $java.home/jaxp.properties");
if (factoryClassName != null) { if (factoryClassName != null) {
@ -214,21 +214,15 @@ class SchemaFactoryFinder {
} }
} }
// try META-INF/services files // Try with ServiceLoader
Iterator sitr = createServiceFileIterator(); final SchemaFactory factoryImpl = findServiceProvider(schemaLanguage);
while(sitr.hasNext()) {
URL resource = (URL)sitr.next(); // The following assertion should always be true.
debugPrintln("looking into " + resource); // Uncomment it, recompile, and run with -ea in case of doubts:
try { // assert factoryImpl == null || factoryImpl.isSchemaLanguageSupported(schemaLanguage);
sf = loadFromService(schemaLanguage,resource.toExternalForm(),
ss.getURLInputStream(resource)); if (factoryImpl != null) {
if(sf!=null) return sf; return factoryImpl;
} catch(IOException e) {
if( debug ) {
debugPrintln("failed to read "+resource);
e.printStackTrace();
}
}
} }
// platform default // platform default
@ -246,8 +240,8 @@ class SchemaFactoryFinder {
* @param className Name of class to create. * @param className Name of class to create.
* @return Created class or <code>null</code>. * @return Created class or <code>null</code>.
*/ */
private Class createClass(String className) { private Class<?> createClass(String className) {
Class clazz; Class<?> clazz;
// make sure we have access to restricted packages // make sure we have access to restricted packages
boolean internal = false; boolean internal = false;
if (System.getSecurityManager() != null) { if (System.getSecurityManager() != null) {
@ -256,25 +250,27 @@ class SchemaFactoryFinder {
} }
} }
try { try {
if (classLoader != null && !internal) { if (classLoader != null && !internal) {
clazz = classLoader.loadClass(className); clazz = Class.forName(className, false, classLoader);
} else { } else {
clazz = Class.forName(className); clazz = Class.forName(className);
}
} catch (Throwable t) {
if(debug) t.printStackTrace();
return null;
} }
} catch (Throwable t) {
if(debug) {
t.printStackTrace();
}
return null;
}
return clazz; return clazz;
} }
/** /**
* <p>Creates an instance of the specified and returns it.</p> * <p>Creates an instance of the specified and returns it.</p>
* *
* @param className * @param className
* fully qualified class name to be instanciated. * fully qualified class name to be instantiated.
* *
* @return null * @return null
* if it fails. Error messages will be printed by this method. * if it fails. Error messages will be printed by this method.
@ -289,7 +285,7 @@ class SchemaFactoryFinder {
debugPrintln("createInstance(" + className + ")"); debugPrintln("createInstance(" + className + ")");
// get Class from className // get Class from className
Class clazz = createClass(className); Class<?> clazz = createClass(className);
if (clazz == null) { if (clazz == null) {
debugPrintln("failed to getClass(" + className + ")"); debugPrintln("failed to getClass(" + className + ")");
return null; return null;
@ -298,9 +294,13 @@ class SchemaFactoryFinder {
// instantiate Class as a SchemaFactory // instantiate Class as a SchemaFactory
try { try {
if (!useServicesMechanism) { if (!SchemaFactory.class.isAssignableFrom(clazz)) {
schemaFactory = (SchemaFactory) newInstanceNoServiceLoader(clazz); throw new ClassCastException(clazz.getName()
} + " cannot be cast to " + SchemaFactory.class);
}
if (!useServicesMechanism) {
schemaFactory = newInstanceNoServiceLoader(clazz);
}
if (schemaFactory == null) { if (schemaFactory == null) {
schemaFactory = (SchemaFactory) clazz.newInstance(); schemaFactory = (SchemaFactory) clazz.newInstance();
} }
@ -326,11 +326,12 @@ class SchemaFactoryFinder {
return schemaFactory; return schemaFactory;
} }
/** /**
* Try to construct using newTransformerFactoryNoServiceLoader * Try to construct using newXMLSchemaFactoryNoServiceLoader
* method if available. * method if available.
*/ */
private static Object newInstanceNoServiceLoader( private static SchemaFactory newInstanceNoServiceLoader(
Class<?> providerClass Class<?> providerClass
) { ) {
// Retain maximum compatibility if no security manager. // Retain maximum compatibility if no security manager.
@ -338,196 +339,87 @@ class SchemaFactoryFinder {
return null; return null;
} }
try { try {
Method creationMethod = final Method creationMethod =
providerClass.getDeclaredMethod( providerClass.getDeclaredMethod(
"newXMLSchemaFactoryNoServiceLoader" "newXMLSchemaFactoryNoServiceLoader"
); );
return creationMethod.invoke(null, (Object[])null); final int modifiers = creationMethod.getModifiers();
} catch (NoSuchMethodException exc) {
// Do not call the method if it's not public static.
if (!Modifier.isStatic(modifiers) || !Modifier.isPublic(modifiers)) {
return null; return null;
} catch (Exception exc) { }
return null;
}
}
/** Iterator that lazily computes one value and returns it. */ // Only calls "newXMLSchemaFactoryNoServiceLoader" if it's
private static abstract class SingleIterator implements Iterator { // declared to return an instance of SchemaFactory.
private boolean seen = false; final Class<?> returnType = creationMethod.getReturnType();
if (SERVICE_CLASS.isAssignableFrom(returnType)) {
public final void remove() { throw new UnsupportedOperationException(); } return SERVICE_CLASS.cast(creationMethod.invoke(null, (Object[])null));
public final boolean hasNext() { return !seen; } } else {
public final Object next() { // Should not happen since
if(seen) throw new NoSuchElementException(); // XMLSchemaFactory.newXMLSchemaFactoryNoServiceLoader is
seen = true; // declared to return XMLSchemaFactory.
return value(); throw new ClassCastException(returnType
} + " cannot be cast to " + SERVICE_CLASS);
}
protected abstract Object value(); } catch(ClassCastException e) {
} throw new SchemaFactoryConfigurationError(e.getMessage(), e);
} catch (NoSuchMethodException exc) {
/** return null;
* Looks up a value in a property file } catch (Exception exc) {
* while producing all sorts of debug messages.
*
* @return null
* if there was an error.
*/
private SchemaFactory loadFromProperty( String keyName, String resourceName, InputStream in )
throws IOException {
debugPrintln("Reading "+resourceName );
Properties props=new Properties();
props.load(in);
in.close();
String factoryClassName = props.getProperty(keyName);
if(factoryClassName != null){
debugPrintln("found "+keyName+" = " + factoryClassName);
return createInstance(factoryClassName);
} else {
debugPrintln(keyName+" is not in the property file");
return null; return null;
} }
} }
/** // Call isSchemaLanguageSupported with initial context.
* <p>Look up a value in a property file.</p> private boolean isSchemaLanguageSupportedBy(final SchemaFactory factory,
* final String schemaLanguage,
* <p>Set <code>debug</code> to <code>true</code> to trace property evaluation.</p> AccessControlContext acc) {
* return AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
* @param schemaLanguage Schema Language to support. public Boolean run() {
* @param inputName Name of <code>InputStream</code>. return factory.isSchemaLanguageSupported(schemaLanguage);
* @param in <code>InputStream</code> of properties.
*
* @return <code>SchemaFactory</code> as determined by <code>keyName</code> value or <code>null</code> if there was an error.
*
* @throws IOException If IO error reading from <code>in</code>.
*/
private SchemaFactory loadFromService(
String schemaLanguage,
String inputName,
InputStream in)
throws IOException {
SchemaFactory schemaFactory = null;
final Class[] stringClassArray = {"".getClass()};
final Object[] schemaLanguageObjectArray = {schemaLanguage};
final String isSchemaLanguageSupportedMethod = "isSchemaLanguageSupported";
debugPrintln("Reading " + inputName);
// read from InputStream until a match is found
BufferedReader configFile = new BufferedReader(new InputStreamReader(in));
String line = null;
while ((line = configFile.readLine()) != null) {
// '#' is comment char
int comment = line.indexOf("#");
switch (comment) {
case -1: break; // no comment
case 0: line = ""; break; // entire line is a comment
default: line = line.substring(0, comment); break; // trim comment
}
// trim whitespace
line = line.trim();
// any content left on line?
if (line.length() == 0) {
continue;
}
// line content is now the name of the class
Class clazz = createClass(line);
if (clazz == null) {
continue;
}
// create an instance of the Class
try {
schemaFactory = (SchemaFactory) clazz.newInstance();
} catch (ClassCastException classCastExcpetion) {
schemaFactory = null;
continue;
} catch (InstantiationException instantiationException) {
schemaFactory = null;
continue;
} catch (IllegalAccessException illegalAccessException) {
schemaFactory = null;
continue;
}
// does this Class support desired Schema?
try {
Method isSchemaLanguageSupported = clazz.getMethod(isSchemaLanguageSupportedMethod, stringClassArray);
Boolean supported = (Boolean) isSchemaLanguageSupported.invoke(schemaFactory, schemaLanguageObjectArray);
if (supported.booleanValue()) {
break;
}
} catch (NoSuchMethodException noSuchMethodException) {
} catch (IllegalAccessException illegalAccessException) {
} catch (InvocationTargetException invocationTargetException) {
}
schemaFactory = null;
} }
}, acc);
// clean up
configFile.close();
// return new instance of SchemaFactory or null
return schemaFactory;
} }
/** /**
* Returns an {@link Iterator} that enumerates all * Finds a service provider subclass of SchemaFactory that supports the
* the META-INF/services files that we care. * given schema language using the ServiceLoader.
*
* @param schemaLanguage The schema language for which we seek a factory.
* @return A SchemaFactory supporting the specified schema language, or null
* if none is found.
* @throws SchemaFactoryConfigurationError if a configuration error is found.
*/ */
private Iterator createServiceFileIterator() { private SchemaFactory findServiceProvider(final String schemaLanguage) {
if (classLoader == null) { assert schemaLanguage != null;
return new SingleIterator() { // store current context.
protected Object value() { final AccessControlContext acc = AccessController.getContext();
ClassLoader classLoader = SchemaFactoryFinder.class.getClassLoader(); try {
//return (ClassLoader.getSystemResource( SERVICE_ID )); return AccessController.doPrivileged(new PrivilegedAction<SchemaFactory>() {
return ss.getResourceAsURL(classLoader, SERVICE_ID); public SchemaFactory run() {
final ServiceLoader<SchemaFactory> loader =
ServiceLoader.load(SERVICE_CLASS);
for (SchemaFactory factory : loader) {
// restore initial context to call
// factory.isSchemaLanguageSupported
if (isSchemaLanguageSupportedBy(factory, schemaLanguage, acc)) {
return factory;
}
}
return null; // no factory found.
} }
}; });
} else { } catch (ServiceConfigurationError error) {
try { throw new SchemaFactoryConfigurationError(
//final Enumeration e = classLoader.getResources(SERVICE_ID); "Provider for " + SERVICE_CLASS + " cannot be created", error);
final Enumeration e = ss.getResources(classLoader, SERVICE_ID);
if(!e.hasMoreElements()) {
debugPrintln("no "+SERVICE_ID+" file was found");
}
// wrap it into an Iterator.
return new Iterator() {
public void remove() {
throw new UnsupportedOperationException();
}
public boolean hasNext() {
return e.hasMoreElements();
}
public Object next() {
return e.nextElement();
}
};
} catch (IOException e) {
debugPrintln("failed to enumerate resources "+SERVICE_ID);
if(debug) e.printStackTrace();
return new ArrayList().iterator(); // empty iterator
}
} }
} }
private static final Class SERVICE_CLASS = SchemaFactory.class; private static final Class<SchemaFactory> SERVICE_CLASS = SchemaFactory.class;
private static final String SERVICE_ID = "META-INF/services/" + SERVICE_CLASS.getName();
private static String which( Class<?> clazz ) {
private static String which( Class clazz ) {
return which( clazz.getName(), clazz.getClassLoader() ); return which( clazz.getName(), clazz.getClassLoader() );
} }

View File

@ -90,7 +90,7 @@ public abstract class XPathFactory {
* @throws RuntimeException When there is a failure in creating an * @throws RuntimeException When there is a failure in creating an
* <code>XPathFactory</code> for the default object model. * <code>XPathFactory</code> for the default object model.
*/ */
public static final XPathFactory newInstance() { public static XPathFactory newInstance() {
try { try {
return newInstance(DEFAULT_OBJECT_MODEL_URI); return newInstance(DEFAULT_OBJECT_MODEL_URI);
@ -121,14 +121,17 @@ public abstract class XPathFactory {
* If present, the value is processed just like above. * If present, the value is processed just like above.
* </li> * </li>
* <li> * <li>
* The class loader is asked for service provider provider-configuration files matching <code>javax.xml.xpath.XPathFactory</code> * Use the service-provider loading facilities, defined by the
* in the resource directory META-INF/services. * {@link java.util.ServiceLoader} class, to attempt to locate and load an
* See the JAR File Specification for file format and parsing rules. * implementation of the service.
* Each potential service provider is required to implement the method: * <br>
* <pre> * Each potential service provider is required to implement the method
* {@link #isObjectModelSupported(String objectModel)} * {@link #isObjectModelSupported(String objectModel)}.
* </pre> * The first service provider found that supports the specified object
* The first service provider found in class loader order that supports the specified object model is returned. * model is returned.
* <br>
* In case of {@link java.util.ServiceConfigurationError} an
* {@link XPathFactoryConfigurationException} will be thrown.
* </li> * </li>
* <li> * <li>
* Platform default <code>XPathFactory</code> is located in a platform specific way. * Platform default <code>XPathFactory</code> is located in a platform specific way.
@ -152,43 +155,41 @@ public abstract class XPathFactory {
* *
* @return Instance of an <code>XPathFactory</code>. * @return Instance of an <code>XPathFactory</code>.
* *
* @throws XPathFactoryConfigurationException If the specified object model is unavailable. * @throws XPathFactoryConfigurationException If the specified object model
* is unavailable, or if there is a configuration error.
* @throws NullPointerException If <code>uri</code> is <code>null</code>. * @throws NullPointerException If <code>uri</code> is <code>null</code>.
* @throws IllegalArgumentException If <code>uri</code> is <code>null</code> * @throws IllegalArgumentException If <code>uri</code> is <code>null</code>
* or <code>uri.length() == 0</code>. * or <code>uri.length() == 0</code>.
*/ */
public static final XPathFactory newInstance(final String uri) public static XPathFactory newInstance(final String uri)
throws XPathFactoryConfigurationException { throws XPathFactoryConfigurationException {
if (uri == null) { if (uri == null) {
throw new NullPointerException( throw new NullPointerException(
"XPathFactory#newInstance(String uri) cannot be called with uri == null" "XPathFactory#newInstance(String uri) cannot be called with uri == null");
);
} }
if (uri.length() == 0) { if (uri.length() == 0) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"XPathFactory#newInstance(String uri) cannot be called with uri == \"\"" "XPathFactory#newInstance(String uri) cannot be called with uri == \"\"");
); }
}
ClassLoader classLoader = ss.getContextClassLoader(); ClassLoader classLoader = ss.getContextClassLoader();
if (classLoader == null) { if (classLoader == null) {
//use the current class loader //use the current class loader
classLoader = XPathFactory.class.getClassLoader(); classLoader = XPathFactory.class.getClassLoader();
} }
XPathFactory xpathFactory = new XPathFactoryFinder(classLoader).newFactory(uri); XPathFactory xpathFactory = new XPathFactoryFinder(classLoader).newFactory(uri);
if (xpathFactory == null) { if (xpathFactory == null) {
throw new XPathFactoryConfigurationException( throw new XPathFactoryConfigurationException(
"No XPathFactory implementation found for the object model: " "No XPathFactory implementation found for the object model: "
+ uri + uri);
); }
}
return xpathFactory; return xpathFactory;
} }
/** /**
@ -242,16 +243,14 @@ public abstract class XPathFactory {
ClassLoader cl = classLoader; ClassLoader cl = classLoader;
if (uri == null) { if (uri == null) {
throw new NullPointerException( throw new NullPointerException(
"XPathFactory#newInstance(String uri) cannot be called with uri == null" "XPathFactory#newInstance(String uri) cannot be called with uri == null");
);
} }
if (uri.length() == 0) { if (uri.length() == 0) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"XPathFactory#newInstance(String uri) cannot be called with uri == \"\"" "XPathFactory#newInstance(String uri) cannot be called with uri == \"\"");
); }
}
if (cl == null) { if (cl == null) {
cl = ss.getContextClassLoader(); cl = ss.getContextClassLoader();
@ -260,31 +259,32 @@ public abstract class XPathFactory {
XPathFactory f = new XPathFactoryFinder(cl).createInstance(factoryClassName); XPathFactory f = new XPathFactoryFinder(cl).createInstance(factoryClassName);
if (f == null) { if (f == null) {
throw new XPathFactoryConfigurationException( throw new XPathFactoryConfigurationException(
"No XPathFactory implementation found for the object model: " "No XPathFactory implementation found for the object model: "
+ uri + uri);
);
} }
//if this factory supports the given schemalanguage return this factory else thrown exception //if this factory supports the given schemalanguage return this factory else thrown exception
if(f.isObjectModelSupported(uri)){ if (f.isObjectModelSupported(uri)) {
return f; return f;
}else{ } else {
throw new XPathFactoryConfigurationException("Factory " + factoryClassName + " doesn't support given " + uri + " object model"); throw new XPathFactoryConfigurationException("Factory "
+ factoryClassName + " doesn't support given " + uri
+ " object model");
} }
} }
/** /**
* <p>Is specified object model supported by this <code>XPathFactory</code>?</p> * <p>Is specified object model supported by this <code>XPathFactory</code>?</p>
* *
* @param objectModel Specifies the object model which the returned <code>XPathFactory</code> will understand. * @param objectModel Specifies the object model which the returned <code>XPathFactory</code> will understand.
* *
* @return <code>true</code> if <code>XPathFactory</code> supports <code>objectModel</code>, else <code>false</code>. * @return <code>true</code> if <code>XPathFactory</code> supports <code>objectModel</code>, else <code>false</code>.
* *
* @throws NullPointerException If <code>objectModel</code> is <code>null</code>. * @throws NullPointerException If <code>objectModel</code> is <code>null</code>.
* @throws IllegalArgumentException If <code>objectModel.length() == 0</code>. * @throws IllegalArgumentException If <code>objectModel.length() == 0</code>.
*/ */
public abstract boolean isObjectModelSupported(String objectModel); public abstract boolean isObjectModelSupported(String objectModel);
/** /**
* <p>Set a feature for this <code>XPathFactory</code> and * <p>Set a feature for this <code>XPathFactory</code> and
@ -314,8 +314,8 @@ public abstract class XPathFactory {
* it creates cannot support this feature. * it creates cannot support this feature.
* @throws NullPointerException if <code>name</code> is <code>null</code>. * @throws NullPointerException if <code>name</code> is <code>null</code>.
*/ */
public abstract void setFeature(String name, boolean value) public abstract void setFeature(String name, boolean value)
throws XPathFactoryConfigurationException; throws XPathFactoryConfigurationException;
/** /**
* <p>Get the state of the named feature.</p> * <p>Get the state of the named feature.</p>
@ -339,8 +339,8 @@ public abstract class XPathFactory {
* it creates cannot support this feature. * it creates cannot support this feature.
* @throws NullPointerException if <code>name</code> is <code>null</code>. * @throws NullPointerException if <code>name</code> is <code>null</code>.
*/ */
public abstract boolean getFeature(String name) public abstract boolean getFeature(String name)
throws XPathFactoryConfigurationException; throws XPathFactoryConfigurationException;
/** /**
* <p>Establish a default variable resolver.</p> * <p>Establish a default variable resolver.</p>
@ -359,19 +359,19 @@ public abstract class XPathFactory {
public abstract void setXPathVariableResolver(XPathVariableResolver resolver); public abstract void setXPathVariableResolver(XPathVariableResolver resolver);
/** /**
* <p>Establish a default function resolver.</p> * <p>Establish a default function resolver.</p>
* *
* <p>Any <code>XPath</code> objects constructed from this factory will * <p>Any <code>XPath</code> objects constructed from this factory will
* use the specified resolver by default.</p> * use the specified resolver by default.</p>
* *
* <p>A <code>NullPointerException</code> is thrown if * <p>A <code>NullPointerException</code> is thrown if
* <code>resolver</code> is <code>null</code>.</p> * <code>resolver</code> is <code>null</code>.</p>
* *
* @param resolver XPath function resolver. * @param resolver XPath function resolver.
* *
* @throws NullPointerException If <code>resolver</code> is * @throws NullPointerException If <code>resolver</code> is
* <code>null</code>. * <code>null</code>.
*/ */
public abstract void setXPathFunctionResolver(XPathFunctionResolver resolver); public abstract void setXPathFunctionResolver(XPathFunctionResolver resolver);
/** /**

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2004, 2005, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -25,20 +25,16 @@
package javax.xml.xpath; package javax.xml.xpath;
import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.security.AccessControlContext;
import java.util.Enumeration; import java.security.AccessController;
import java.util.Iterator; import java.security.PrivilegedAction;
import java.util.NoSuchElementException;
import java.util.Properties; import java.util.Properties;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
/** /**
* Implementation of {@link XPathFactory#newInstance(String)}. * Implementation of {@link XPathFactory#newInstance(String)}.
@ -50,7 +46,7 @@ import java.util.Properties;
class XPathFactoryFinder { class XPathFactoryFinder {
private static final String DEFAULT_PACKAGE = "com.sun.org.apache.xpath.internal"; private static final String DEFAULT_PACKAGE = "com.sun.org.apache.xpath.internal";
private static SecuritySupport ss = new SecuritySupport() ; private static final SecuritySupport ss = new SecuritySupport() ;
/** debug support code. */ /** debug support code. */
private static boolean debug = false; private static boolean debug = false;
static { static {
@ -65,12 +61,12 @@ class XPathFactoryFinder {
/** /**
* <p>Cache properties for performance.</p> * <p>Cache properties for performance.</p>
*/ */
private static Properties cacheProps = new Properties(); private static final Properties cacheProps = new Properties();
/** /**
* <p>First time requires initialization overhead.</p> * <p>First time requires initialization overhead.</p>
*/ */
private volatile static boolean firstTime = true; private volatile static boolean firstTime = true;
/** /**
* <p>Conditional debug printing.</p> * <p>Conditional debug printing.</p>
@ -93,9 +89,8 @@ class XPathFactoryFinder {
* to find <code>XPathFactory</code>.</p> * to find <code>XPathFactory</code>.</p>
* *
* @param loader * @param loader
* to be used to load resource, {@link XPathFactory}, and * to be used to load resource and {@link XPathFactory}
* {@link SchemaFactoryLoader} implementations during * implementations during the resolution process.
* the resolution process.
* If this parameter is null, the default system class loader * If this parameter is null, the default system class loader
* will be used. * will be used.
*/ */
@ -113,7 +108,7 @@ class XPathFactoryFinder {
return; return;
} }
} catch( Throwable unused ) { } catch( Throwable unused ) {
; // getContextClassLoader() undefined in JDK1.1 // getContextClassLoader() undefined in JDK1.1
} }
if( classLoader==ClassLoader.getSystemClassLoader() ) { if( classLoader==ClassLoader.getSystemClassLoader() ) {
@ -126,7 +121,7 @@ class XPathFactoryFinder {
/** /**
* <p>Creates a new {@link XPathFactory} object for the specified * <p>Creates a new {@link XPathFactory} object for the specified
* schema language.</p> * object model.</p>
* *
* @param uri * @param uri
* Identifies the underlying object model. * Identifies the underlying object model.
@ -136,8 +131,10 @@ class XPathFactoryFinder {
* @throws NullPointerException * @throws NullPointerException
* If the parameter is null. * If the parameter is null.
*/ */
public XPathFactory newFactory(String uri) { public XPathFactory newFactory(String uri) throws XPathFactoryConfigurationException {
if(uri==null) throw new NullPointerException(); if (uri == null) {
throw new NullPointerException();
}
XPathFactory f = _newFactory(uri); XPathFactory f = _newFactory(uri);
if (f != null) { if (f != null) {
debugPrintln("factory '" + f.getClass().getName() + "' was found for " + uri); debugPrintln("factory '" + f.getClass().getName() + "' was found for " + uri);
@ -154,8 +151,8 @@ class XPathFactoryFinder {
* *
* @return {@link XPathFactory} for the given object model. * @return {@link XPathFactory} for the given object model.
*/ */
private XPathFactory _newFactory(String uri) { private XPathFactory _newFactory(String uri) throws XPathFactoryConfigurationException {
XPathFactory xpathFactory; XPathFactory xpathFactory = null;
String propertyName = SERVICE_CLASS.getName() + ":" + uri; String propertyName = SERVICE_CLASS.getName() + ":" + uri;
@ -166,7 +163,9 @@ class XPathFactoryFinder {
if(r!=null) { if(r!=null) {
debugPrintln("The value is '"+r+"'"); debugPrintln("The value is '"+r+"'");
xpathFactory = createInstance(r, true); xpathFactory = createInstance(r, true);
if(xpathFactory != null) return xpathFactory; if (xpathFactory != null) {
return xpathFactory;
}
} else } else
debugPrintln("The property is undefined."); debugPrintln("The property is undefined.");
} catch( Throwable t ) { } catch( Throwable t ) {
@ -180,8 +179,6 @@ class XPathFactoryFinder {
String configFile = javah + File.separator + String configFile = javah + File.separator +
"lib" + File.separator + "jaxp.properties"; "lib" + File.separator + "jaxp.properties";
String factoryClassName = null ;
// try to read from $java.home/lib/jaxp.properties // try to read from $java.home/lib/jaxp.properties
try { try {
if(firstTime){ if(firstTime){
@ -196,7 +193,7 @@ class XPathFactoryFinder {
} }
} }
} }
factoryClassName = cacheProps.getProperty(propertyName); final String factoryClassName = cacheProps.getProperty(propertyName);
debugPrintln("found " + factoryClassName + " in $java.home/jaxp.properties"); debugPrintln("found " + factoryClassName + " in $java.home/jaxp.properties");
if (factoryClassName != null) { if (factoryClassName != null) {
@ -211,23 +208,16 @@ class XPathFactoryFinder {
} }
} }
// try META-INF/services files // Try with ServiceLoader
Iterator sitr = createServiceFileIterator(); assert xpathFactory == null;
while(sitr.hasNext()) { xpathFactory = findServiceProvider(uri);
URL resource = (URL)sitr.next();
debugPrintln("looking into " + resource); // The following assertion should always be true.
try { // Uncomment it, recompile, and run with -ea in case of doubts:
xpathFactory = loadFromService(uri, resource.toExternalForm(), // assert xpathFactory == null || xpathFactory.isObjectModelSupported(uri);
ss.getURLInputStream(resource));
if (xpathFactory != null) { if (xpathFactory != null) {
return xpathFactory; return xpathFactory;
}
} catch(IOException e) {
if( debug ) {
debugPrintln("failed to read "+resource);
e.printStackTrace();
}
}
} }
// platform default // platform default
@ -245,8 +235,8 @@ class XPathFactoryFinder {
* @param className Name of class to create. * @param className Name of class to create.
* @return Created class or <code>null</code>. * @return Created class or <code>null</code>.
*/ */
private Class createClass(String className) { private Class<?> createClass(String className) {
Class clazz; Class clazz;
// make sure we have access to restricted packages // make sure we have access to restricted packages
boolean internal = false; boolean internal = false;
if (System.getSecurityManager() != null) { if (System.getSecurityManager() != null) {
@ -258,47 +248,54 @@ class XPathFactoryFinder {
// use approprite ClassLoader // use approprite ClassLoader
try { try {
if (classLoader != null && !internal) { if (classLoader != null && !internal) {
clazz = classLoader.loadClass(className); clazz = Class.forName(className, false, classLoader);
} else { } else {
clazz = Class.forName(className); clazz = Class.forName(className);
} }
} catch (Throwable t) { } catch (Throwable t) {
if(debug) t.printStackTrace(); if(debug) {
return null; t.printStackTrace();
}
return null;
} }
return clazz; return clazz;
} }
/** /**
* <p>Creates an instance of the specified and returns it.</p> * <p>Creates an instance of the specified and returns it.</p>
* *
* @param className * @param className
* fully qualified class name to be instanciated. * fully qualified class name to be instantiated.
* *
* @return null * @return null
* if it fails. Error messages will be printed by this method. * if it fails. Error messages will be printed by this method.
*/ */
XPathFactory createInstance( String className ) { XPathFactory createInstance( String className )
throws XPathFactoryConfigurationException
{
return createInstance( className, false ); return createInstance( className, false );
} }
XPathFactory createInstance( String className, boolean useServicesMechanism ) {
XPathFactory createInstance( String className, boolean useServicesMechanism )
throws XPathFactoryConfigurationException
{
XPathFactory xPathFactory = null; XPathFactory xPathFactory = null;
debugPrintln("createInstance(" + className + ")"); debugPrintln("createInstance(" + className + ")");
// get Class from className // get Class from className
Class clazz = createClass(className); Class<?> clazz = createClass(className);
if (clazz == null) { if (clazz == null) {
debugPrintln("failed to getClass(" + className + ")"); debugPrintln("failed to getClass(" + className + ")");
return null; return null;
} }
debugPrintln("loaded " + className + " from " + which(clazz)); debugPrintln("loaded " + className + " from " + which(clazz));
// instantiate Class as a XPathFactory // instantiate Class as a XPathFactory
try { try {
if (!useServicesMechanism) { if (!useServicesMechanism) {
xPathFactory = (XPathFactory) newInstanceNoServiceLoader(clazz); xPathFactory = newInstanceNoServiceLoader(clazz);
} }
if (xPathFactory == null) { if (xPathFactory == null) {
xPathFactory = (XPathFactory) clazz.newInstance(); xPathFactory = (XPathFactory) clazz.newInstance();
@ -329,203 +326,94 @@ class XPathFactoryFinder {
* Try to construct using newXPathFactoryNoServiceLoader * Try to construct using newXPathFactoryNoServiceLoader
* method if available. * method if available.
*/ */
private static Object newInstanceNoServiceLoader( private static XPathFactory newInstanceNoServiceLoader(
Class<?> providerClass Class<?> providerClass
) { ) throws XPathFactoryConfigurationException {
// Retain maximum compatibility if no security manager. // Retain maximum compatibility if no security manager.
if (System.getSecurityManager() == null) { if (System.getSecurityManager() == null) {
return null; return null;
} }
try { try {
Method creationMethod = Method creationMethod =
providerClass.getDeclaredMethod( providerClass.getDeclaredMethod(
"newXPathFactoryNoServiceLoader" "newXPathFactoryNoServiceLoader"
); );
return creationMethod.invoke(null, (Object[])null); final int modifiers = creationMethod.getModifiers();
} catch (NoSuchMethodException exc) {
// Do not call "newXPathFactoryNoServiceLoader" if it's
// not public static.
if (!Modifier.isStatic(modifiers) || !Modifier.isPublic(modifiers)) {
return null; return null;
} catch (Exception exc) {
return null;
}
}
/**
* <p>Look up a value in a property file.</p>
*
* <p>Set <code>debug</code> to <code>true</code> to trace property evaluation.</p>
*
* @param objectModel URI of object model to support.
* @param inputName Name of <code>InputStream</code>.
* @param in <code>InputStream</code> of properties.
*
* @return <code>XPathFactory</code> as determined by <code>keyName</code> value or <code>null</code> if there was an error.
*
* @throws IOException If IO error reading from <code>in</code>.
*/
private XPathFactory loadFromService(
String objectModel,
String inputName,
InputStream in)
throws IOException {
XPathFactory xPathFactory = null;
final Class[] stringClassArray = {"".getClass()};
final Object[] objectModelObjectArray = {objectModel};
final String isObjectModelSupportedMethod = "isObjectModelSupported";
debugPrintln("Reading " + inputName);
// read from InputStream until a match is found
BufferedReader configFile = new BufferedReader(new InputStreamReader(in));
String line = null;
while ((line = configFile.readLine()) != null) {
// '#' is comment char
int comment = line.indexOf("#");
switch (comment) {
case -1: break; // no comment
case 0: line = ""; break; // entire line is a comment
default: line = line.substring(0, comment); break; // trim comment
}
// trim whitespace
line = line.trim();
// any content left on line?
if (line.length() == 0) {
continue;
}
// line content is now the name of the class
Class clazz = createClass(line);
if (clazz == null) {
continue;
}
// create an instance of the Class
try {
xPathFactory = (XPathFactory) clazz.newInstance();
} catch (ClassCastException classCastExcpetion) {
xPathFactory = null;
continue;
} catch (InstantiationException instantiationException) {
xPathFactory = null;
continue;
} catch (IllegalAccessException illegalAccessException) {
xPathFactory = null;
continue;
}
// does this Class support desired object model?
try {
Method isObjectModelSupported = clazz.getMethod(isObjectModelSupportedMethod, stringClassArray);
Boolean supported = (Boolean) isObjectModelSupported.invoke(xPathFactory, objectModelObjectArray);
if (supported.booleanValue()) {
break;
}
} catch (NoSuchMethodException noSuchMethodException) {
} catch (IllegalAccessException illegalAccessException) {
} catch (InvocationTargetException invocationTargetException) {
}
xPathFactory = null;
} }
// clean up // Only calls "newXPathFactoryNoServiceLoader" if it's
configFile.close(); // declared to return an instance of XPathFactory.
final Class<?> returnType = creationMethod.getReturnType();
// return new instance of XPathFactory or null if (SERVICE_CLASS.isAssignableFrom(returnType)) {
return xPathFactory; return SERVICE_CLASS.cast(creationMethod.invoke(null, (Object[])null));
} } else {
// Should not happen since
/** Iterator that lazily computes one value and returns it. */ // XPathFactoryImpl.newXPathFactoryNoServiceLoader is
private static abstract class SingleIterator implements Iterator { // declared to return XPathFactory.
private boolean seen = false; throw new ClassCastException(returnType
+ " cannot be cast to " + SERVICE_CLASS);
public final void remove() { throw new UnsupportedOperationException(); } }
public final boolean hasNext() { return !seen; } } catch (ClassCastException e) {
public final Object next() { throw new XPathFactoryConfigurationException(e);
if(seen) throw new NoSuchElementException(); } catch (NoSuchMethodException exc) {
seen = true; return null;
return value(); } catch (Exception exc) {
}
protected abstract Object value();
}
/**
* Looks up a value in a property file
* while producing all sorts of debug messages.
*
* @return null
* if there was an error.
*/
private XPathFactory loadFromProperty( String keyName, String resourceName, InputStream in )
throws IOException {
debugPrintln("Reading "+resourceName );
Properties props = new Properties();
props.load(in);
in.close();
String factoryClassName = props.getProperty(keyName);
if(factoryClassName != null){
debugPrintln("found "+keyName+" = " + factoryClassName);
return createInstance(factoryClassName, true);
} else {
debugPrintln(keyName+" is not in the property file");
return null; return null;
} }
} }
// Call isObjectModelSupportedBy with initial context.
private boolean isObjectModelSupportedBy(final XPathFactory factory,
final String objectModel,
AccessControlContext acc) {
return AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
public Boolean run() {
return factory.isObjectModelSupported(objectModel);
}
}, acc);
}
/** /**
* Returns an {@link Iterator} that enumerates all * Finds a service provider subclass of XPathFactory that supports the
* the META-INF/services files that we care. * given object model using the ServiceLoader.
*
* @param objectModel URI of object model to support.
* @return An XPathFactory supporting the specified object model, or null
* if none is found.
* @throws XPathFactoryConfigurationException if a configuration error is found.
*/ */
private Iterator createServiceFileIterator() { private XPathFactory findServiceProvider(final String objectModel)
if (classLoader == null) { throws XPathFactoryConfigurationException {
return new SingleIterator() {
protected Object value() { assert objectModel != null;
ClassLoader classLoader = XPathFactoryFinder.class.getClassLoader(); // store current context.
return ss.getResourceAsURL(classLoader, SERVICE_ID); final AccessControlContext acc = AccessController.getContext();
//return (ClassLoader.getSystemResource( SERVICE_ID )); try {
return AccessController.doPrivileged(new PrivilegedAction<XPathFactory>() {
public XPathFactory run() {
final ServiceLoader<XPathFactory> loader =
ServiceLoader.load(SERVICE_CLASS);
for (XPathFactory factory : loader) {
// restore initial context to call
// factory.isObjectModelSupportedBy
if (isObjectModelSupportedBy(factory, objectModel, acc)) {
return factory;
}
}
return null; // no factory found.
} }
}; });
} else { } catch (ServiceConfigurationError error) {
try { throw new XPathFactoryConfigurationException(error);
//final Enumeration e = classLoader.getResources(SERVICE_ID);
final Enumeration e = ss.getResources(classLoader, SERVICE_ID);
if(!e.hasMoreElements()) {
debugPrintln("no "+SERVICE_ID+" file was found");
}
// wrap it into an Iterator.
return new Iterator() {
public void remove() {
throw new UnsupportedOperationException();
}
public boolean hasNext() {
return e.hasMoreElements();
}
public Object next() {
return e.nextElement();
}
};
} catch (IOException e) {
debugPrintln("failed to enumerate resources "+SERVICE_ID);
if(debug) e.printStackTrace();
return new ArrayList().iterator(); // empty iterator
}
} }
} }
private static final Class SERVICE_CLASS = XPathFactory.class; private static final Class<XPathFactory> SERVICE_CLASS = XPathFactory.class;
private static final String SERVICE_ID = "META-INF/services/" + SERVICE_CLASS.getName();
private static String which( Class clazz ) { private static String which( Class clazz ) {
return which( clazz.getName(), clazz.getClassLoader() ); return which( clazz.getName(), clazz.getClassLoader() );