8152912: SAX XMLReaderFactory needs to be ServiceLoader compliant

Reviewed-by: dfuchs
This commit is contained in:
Joe Wang 2016-05-09 14:58:55 -07:00
parent dd927b90d5
commit 56be49516c
8 changed files with 170 additions and 158 deletions

View file

@ -86,5 +86,6 @@ module java.xml {
uses javax.xml.transform.TransformerFactory; uses javax.xml.transform.TransformerFactory;
uses javax.xml.validation.SchemaFactory; uses javax.xml.validation.SchemaFactory;
uses javax.xml.xpath.XPathFactory; uses javax.xml.xpath.XPathFactory;
uses org.xml.sax.XMLReader;
} }

View file

@ -93,6 +93,7 @@ package org.xml.sax;
* @see org.xml.sax.DocumentHandler#startElement startElement * @see org.xml.sax.DocumentHandler#startElement startElement
* @see org.xml.sax.helpers.AttributeListImpl AttributeListImpl * @see org.xml.sax.helpers.AttributeListImpl AttributeListImpl
*/ */
@Deprecated(since="5")
public interface AttributeList { public interface AttributeList {

View file

@ -68,6 +68,7 @@ package org.xml.sax;
* @see org.xml.sax.Locator * @see org.xml.sax.Locator
* @see org.xml.sax.HandlerBase * @see org.xml.sax.HandlerBase
*/ */
@Deprecated(since="5")
public interface DocumentHandler { public interface DocumentHandler {

View file

@ -73,6 +73,7 @@ import java.util.Locale;
* @see org.xml.sax.HandlerBase * @see org.xml.sax.HandlerBase
* @see org.xml.sax.InputSource * @see org.xml.sax.InputSource
*/ */
@Deprecated(since="5")
public interface Parser public interface Parser
{ {

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2001, 2016, 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
@ -32,8 +32,7 @@
package org.xml.sax.helpers; package org.xml.sax.helpers;
import java.lang.reflect.Method; import java.util.Objects;
import java.lang.reflect.InvocationTargetException;
/** /**
* Create a new instance of a class by name. * Create a new instance of a class by name.
@ -57,31 +56,26 @@ import java.lang.reflect.InvocationTargetException;
* @version 2.0.1 (sax2r2) * @version 2.0.1 (sax2r2)
*/ */
class NewInstance { class NewInstance {
private static final String DEFAULT_PACKAGE = "com.sun.org.apache.xerces.internal"; private static final String DEFAULT_PACKAGE = "com.sun.org.apache.xerces.internal";
/** /**
* Creates a new instance of the specified class name * Creates a new instance of the specified class name
* *
* Package private so this code is not exposed at the API level. * Package private so this code is not exposed at the API level.
*/ */
static Object newInstance (ClassLoader classLoader, String className) static <T> T newInstance (Class<T> type, ClassLoader loader, String clsName)
throws ClassNotFoundException, IllegalAccessException, throws ClassNotFoundException, IllegalAccessException,
InstantiationException InstantiationException
{ {
// make sure we have access to restricted packages ClassLoader classLoader = Objects.requireNonNull(loader);
boolean internal = false; String className = Objects.requireNonNull(clsName);
if (System.getSecurityManager() != null) {
if (className != null && className.startsWith(DEFAULT_PACKAGE)) { if (className.startsWith(DEFAULT_PACKAGE)) {
internal = true; return type.cast(new com.sun.org.apache.xerces.internal.parsers.SAXParser());
}
} }
Class driverClass; Class<?> driverClass = classLoader.loadClass(className);
if (classLoader == null || internal) { return type.cast(driverClass.newInstance());
driverClass = Class.forName(className);
} else {
driverClass = classLoader.loadClass(className);
}
return driverClass.newInstance();
} }
} }

View file

@ -30,8 +30,6 @@
package org.xml.sax.helpers; package org.xml.sax.helpers;
import org.xml.sax.Parser;
/** /**
* Java-specific class for dynamically loading SAX parsers. * Java-specific class for dynamically loading SAX parsers.
@ -65,6 +63,8 @@ import org.xml.sax.Parser;
* @author David Megginson * @author David Megginson
* @version 2.0.1 (sax2r2) * @version 2.0.1 (sax2r2)
*/ */
@SuppressWarnings( "deprecation" )
@Deprecated(since="5")
public class ParserFactory { public class ParserFactory {
private static SecuritySupport ss = new SecuritySupport(); private static SecuritySupport ss = new SecuritySupport();
@ -97,7 +97,7 @@ public class ParserFactory {
* @see #makeParser(java.lang.String) * @see #makeParser(java.lang.String)
* @see org.xml.sax.Parser * @see org.xml.sax.Parser
*/ */
public static Parser makeParser () public static org.xml.sax.Parser makeParser ()
throws ClassNotFoundException, throws ClassNotFoundException,
IllegalAccessException, IllegalAccessException,
InstantiationException, InstantiationException,
@ -134,14 +134,13 @@ public class ParserFactory {
* @see #makeParser() * @see #makeParser()
* @see org.xml.sax.Parser * @see org.xml.sax.Parser
*/ */
public static Parser makeParser (String className) public static org.xml.sax.Parser makeParser (String className)
throws ClassNotFoundException, throws ClassNotFoundException,
IllegalAccessException, IllegalAccessException,
InstantiationException, InstantiationException,
ClassCastException ClassCastException
{ {
return (Parser) NewInstance.newInstance ( return NewInstance.newInstance (org.xml.sax.Parser.class, ss.getClassLoader(), className);
ss.getContextClassLoader(), className);
} }
} }

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2004, 2016, 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
@ -37,54 +37,43 @@ import java.security.*;
*/ */
class SecuritySupport { class SecuritySupport {
/**
ClassLoader getContextClassLoader() throws SecurityException{ * Returns the current thread's context class loader, or the system class loader
return (ClassLoader) * if the context class loader is null.
AccessController.doPrivileged(new PrivilegedAction() { * @return the current thread's context class loader, or the system class loader
public Object run() { * @throws SecurityException
ClassLoader cl = null; */
//try { ClassLoader getClassLoader() throws SecurityException{
cl = Thread.currentThread().getContextClassLoader(); return AccessController.doPrivileged((PrivilegedAction<ClassLoader>)() -> {
//} catch (SecurityException ex) { } ClassLoader cl = Thread.currentThread().getContextClassLoader();
if (cl == null) {
if (cl == null)
cl = ClassLoader.getSystemClassLoader(); cl = ClassLoader.getSystemClassLoader();
}
return cl; return cl;
}
}); });
} }
String getSystemProperty(final String propName) { String getSystemProperty(final String propName) {
return (String) return AccessController.doPrivileged((PrivilegedAction<String>)()
AccessController.doPrivileged(new PrivilegedAction() { -> System.getProperty(propName));
public Object run() {
return System.getProperty(propName);
}
});
} }
FileInputStream getFileInputStream(final File file) FileInputStream getFileInputStream(final File file)
throws FileNotFoundException throws FileNotFoundException
{ {
try { try {
return (FileInputStream) return AccessController.doPrivileged((PrivilegedExceptionAction<FileInputStream>)() ->
AccessController.doPrivileged(new PrivilegedExceptionAction() { new FileInputStream(file));
public Object run() throws FileNotFoundException {
return new FileInputStream(file);
}
});
} catch (PrivilegedActionException e) { } catch (PrivilegedActionException e) {
throw (FileNotFoundException)e.getException(); throw (FileNotFoundException)e.getException();
} }
} }
InputStream getResourceAsStream(final ClassLoader cl,
final String name) InputStream getResourceAsStream(final ClassLoader cl, final String name)
{ {
return (InputStream) return AccessController.doPrivileged((PrivilegedAction<InputStream>) () -> {
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
InputStream ris; InputStream ris;
if (cl == null) { if (cl == null) {
ris = SecuritySupport.class.getResourceAsStream(name); ris = SecuritySupport.class.getResourceAsStream(name);
@ -92,17 +81,12 @@ class SecuritySupport {
ris = cl.getResourceAsStream(name); ris = cl.getResourceAsStream(name);
} }
return ris; return ris;
}
}); });
} }
boolean doesFileExist(final File f) { boolean doesFileExist(final File f) {
return ((Boolean) return (AccessController.doPrivileged((PrivilegedAction<Boolean>)() ->
AccessController.doPrivileged(new PrivilegedAction() { new Boolean(f.exists())));
public Object run() {
return new Boolean(f.exists());
}
})).booleanValue();
} }
} }

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2000, 2016, 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
@ -32,10 +32,17 @@
package org.xml.sax.helpers; package org.xml.sax.helpers;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import org.xml.sax.XMLReader; import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Iterator;
import java.util.Objects;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
/** /**
@ -70,7 +77,11 @@ import org.xml.sax.SAXException;
* @since 1.4, SAX 2.0 * @since 1.4, SAX 2.0
* @author David Megginson, David Brownell * @author David Megginson, David Brownell
* @version 2.0.1 (sax2r2) * @version 2.0.1 (sax2r2)
*
* @deprecated It is recommended to use {@link javax.xml.parsers.SAXParserFactory}
* instead.
*/ */
@Deprecated(since="9")
final public class XMLReaderFactory final public class XMLReaderFactory
{ {
/** /**
@ -83,47 +94,43 @@ final public class XMLReaderFactory
} }
private static final String property = "org.xml.sax.driver"; private static final String property = "org.xml.sax.driver";
private static SecuritySupport ss = new SecuritySupport(); private static final SecuritySupport ss = new SecuritySupport();
private static String _clsFromJar = null;
private static boolean _jarread = false;
/** /**
* Attempt to create an XMLReader from system defaults. * Obtains a new instance of a {@link org.xml.sax.XMLReader}.
* In environments which can support it, the name of the XMLReader * This method uses the following ordered lookup procedure to find and load
* class is determined by trying each these options in order, and * the {@link org.xml.sax.XMLReader} implementation class:
* using the first one which succeeds: * <p>
* <ul> * <ol>
*
* <li>If the system property {@code org.xml.sax.driver} * <li>If the system property {@code org.xml.sax.driver}
* has a value, that is used as an XMLReader class name. </li> * has a value, that is used as an XMLReader class name. </li>
* <li>
* Use the service-provider loading facility, defined by the
* {@link java.util.ServiceLoader} class, to attempt to locate and load an
* implementation of the service {@link org.xml.sax.XMLReader} by using the
* {@linkplain java.lang.Thread#getContextClassLoader() current thread's context class loader}.
* If the context class loader is null, the
* {@linkplain ClassLoader#getSystemClassLoader() system class loader} will
* be used.
* </li>
* <li>
* Deprecated. Look for a class name in the {@code META-INF/services/org.xml.sax.driver}
* file in a jar file available to the runtime.</li>
* <li>
* <p>
* Otherwise, the system-default implementation is returned.
* </li>
* </ol>
* *
* <li>The JAR "Services API" is used to look for a class name * @apiNote
* in the <em>META-INF/services/org.xml.sax.driver</em> file in * The process that looks for a class name in the
* jarfiles available to the runtime.</li> * {@code META-INF/services/org.xml.sax.driver} file in a jar file does not
* conform to the specification of the service-provider loading facility
* as defined in {@link java.util.ServiceLoader} and therefore does not
* support modularization. It is deprecated as of Java SE 9 and subject to
* removal in a future release.
* *
* <li> SAX parser distributions are strongly encouraged to provide * @return a new XMLReader.
* a default XMLReader class name that will take effect only when
* previous options (on this list) are not successful.</li>
*
* <li>Finally, if {@link ParserFactory#makeParser()} can
* return a system default SAX1 parser, that parser is wrapped in
* a {@link ParserAdapter}. (This is a migration aid for SAX1
* environments, where the {@code org.xml.sax.parser} system
* property will often be usable.) </li>
* </ul>
*
* <p> In environments such as small embedded systems, which can not
* support that flexibility, other mechanisms to determine the default
* may be used.
*
* <p>Note that many Java environments allow system properties to be
* initialized on a command line. This means that <em>in most cases</em>
* setting a good value for that property ensures that calls to this
* method will succeed, except when security policies intervene.
* This will also maximize application portability to older SAX
* environments, with less robust implementations of this method.
*
* @return A new XMLReader.
* @exception org.xml.sax.SAXException If no default XMLReader class * @exception org.xml.sax.SAXException If no default XMLReader class
* can be identified and instantiated. * can be identified and instantiated.
* @see #createXMLReader(java.lang.String) * @see #createXMLReader(java.lang.String)
@ -132,7 +139,7 @@ final public class XMLReaderFactory
throws SAXException throws SAXException
{ {
String className = null; String className = null;
ClassLoader cl = ss.getContextClassLoader(); ClassLoader cl = ss.getClassLoader();
// 1. try the JVM-instance-wide system property // 1. try the JVM-instance-wide system property
try { try {
@ -140,62 +147,26 @@ final public class XMLReaderFactory
} }
catch (RuntimeException e) { /* continue searching */ } catch (RuntimeException e) { /* continue searching */ }
// 2. if that fails, try META-INF/services/ // 2. try the ServiceLoader
if (className == null) { if (className == null) {
if (!_jarread) { final XMLReader provider = findServiceProvider(XMLReader.class, cl);
_jarread = true; if (provider != null) {
String service = "META-INF/services/" + property; return provider;
InputStream in;
BufferedReader reader;
try {
if (cl != null) {
in = ss.getResourceAsStream(cl, service);
// If no provider found then try the current ClassLoader
if (in == null) {
cl = null;
in = ss.getResourceAsStream(cl, service);
} }
} else {
// No Context ClassLoader, try the current ClassLoader
in = ss.getResourceAsStream(cl, service);
} }
if (in != null) { // 3. try META-INF/services/org.xml.sax.driver. This old process allows
reader = new BufferedReader (new InputStreamReader (in, "UTF8")); // legacy providers to be found
_clsFromJar = reader.readLine ();
in.close ();
}
} catch (Exception e) {
}
}
className = _clsFromJar;
}
// 3. Distro-specific fallback
if (className == null) { if (className == null) {
// BEGIN DISTRIBUTION-SPECIFIC className = jarLookup(cl);
}
// EXAMPLE:
// className = "com.example.sax.XmlReader"; // 4. Distro-specific fallback
// or a $JAVA_HOME/jre/lib/*properties setting... if (className == null) {
className = "com.sun.org.apache.xerces.internal.parsers.SAXParser"; return new com.sun.org.apache.xerces.internal.parsers.SAXParser();
// END DISTRIBUTION-SPECIFIC
} }
// do we know the XMLReader implementation class yet?
if (className != null)
return loadClass (cl, className); return loadClass (cl, className);
// 4. panic -- adapt any SAX1 parser
try {
return new ParserAdapter (ParserFactory.makeParser ());
} catch (Exception e) {
throw new SAXException ("Can't create default XMLReader; "
+ "is system property org.xml.sax.driver set?");
}
} }
@ -217,14 +188,14 @@ final public class XMLReaderFactory
public static XMLReader createXMLReader (String className) public static XMLReader createXMLReader (String className)
throws SAXException throws SAXException
{ {
return loadClass (ss.getContextClassLoader(), className); return loadClass (ss.getClassLoader(), className);
} }
private static XMLReader loadClass (ClassLoader loader, String className) private static XMLReader loadClass (ClassLoader loader, String className)
throws SAXException throws SAXException
{ {
try { try {
return (XMLReader) NewInstance.newInstance (loader, className); return NewInstance.newInstance (XMLReader.class, loader, className);
} catch (ClassNotFoundException e1) { } catch (ClassNotFoundException e1) {
throw new SAXException("SAX2 driver class " + className + throw new SAXException("SAX2 driver class " + className +
" not found", e1); " not found", e1);
@ -240,4 +211,64 @@ final public class XMLReaderFactory
" does not implement XMLReader", e4); " does not implement XMLReader", e4);
} }
} }
/**
* Locates a provider by directly reading the jar service file.
* @param loader the ClassLoader to be used to read the service file
* @return the name of the provider, or null if nothing is found
*/
private static String jarLookup(final ClassLoader loader) {
final ClassLoader cl = Objects.requireNonNull(loader);
String clsFromJar = null;
String service = "META-INF/services/" + property;
InputStream in;
BufferedReader reader;
try {
in = ss.getResourceAsStream(cl, service);
// If no provider found then try the current ClassLoader
if (in == null) {
in = ss.getResourceAsStream(null, service);
}
if (in != null) {
reader = new BufferedReader (new InputStreamReader (in, "UTF8"));
clsFromJar = reader.readLine ();
in.close ();
}
} catch (IOException e) {
}
return clsFromJar;
}
/*
* 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
*/
private static <T> T findServiceProvider(final Class<T> type, final ClassLoader loader)
throws SAXException {
ClassLoader cl = Objects.requireNonNull(loader);
try {
return AccessController.doPrivileged((PrivilegedAction<T>) () -> {
final ServiceLoader<T> serviceLoader;
serviceLoader = ServiceLoader.load(type, cl);
final Iterator<T> iterator = serviceLoader.iterator();
if (iterator.hasNext()) {
return iterator.next();
} else {
return null;
}
});
} catch(ServiceConfigurationError e) {
final RuntimeException x = new RuntimeException(
"Provider for " + type + " cannot be created", e);
throw new SAXException("Provider for " + type + " cannot be created", x);
}
}
} }