Path: blob/master/src/java.xml/share/classes/javax/xml/validation/SchemaFactoryFinder.java
40948 views
/*1* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package javax.xml.validation;2627import com.sun.org.apache.xerces.internal.jaxp.validation.XMLSchemaFactory;28import java.io.File;29import java.lang.reflect.InvocationTargetException;30import java.lang.reflect.Method;31import java.lang.reflect.Modifier;32import java.security.AccessControlContext;33import java.security.AccessController;34import java.security.PrivilegedAction;35import java.util.Properties;36import java.util.ServiceConfigurationError;37import java.util.ServiceLoader;38import java.util.function.Supplier;39import jdk.xml.internal.SecuritySupport;4041/**42* Implementation of {@link SchemaFactory#newInstance(String)}.43*44* @author Kohsuke Kawaguchi45* @since 1.546*/47class SchemaFactoryFinder {4849/** debug support code. */50private static boolean debug = false;5152private static final String DEFAULT_PACKAGE = "com.sun.org.apache.xerces.internal";53/**54* <p>Cache properties for performance.</p>55*/56private static final Properties cacheProps = new Properties();5758/**59* <p>First time requires initialization overhead.</p>60*/61private static volatile boolean firstTime = true;6263static {64// Use try/catch block to support applets65try {66debug = SecuritySupport.getSystemProperty("jaxp.debug") != null;67} catch (Exception unused) {68debug = false;69}70}7172/**73* <p>Conditional debug printing.</p>74*75* @param msgGen Supplier function that returns debug message76*/77private static void debugPrintln(Supplier<String> msgGen) {78if (debug) {79System.err.println("JAXP: " + msgGen.get());80}81}8283/**84* <p><code>ClassLoader</code> to use to find <code>SchemaFactory</code>.</p>85*/86private final ClassLoader classLoader;8788/**89* <p>Constructor that specifies <code>ClassLoader</code> to use90* to find <code>SchemaFactory</code>.</p>91*92* @param loader93* to be used to load resource, {@link SchemaFactory}, and94* {@link SchemaFactoryLoader} implementations during95* the resolution process.96* If this parameter is null, the default system class loader97* will be used.98*/99public SchemaFactoryFinder(ClassLoader loader) {100this.classLoader = loader;101if( debug ) {102debugDisplayClassLoader();103}104}105106private void debugDisplayClassLoader() {107try {108if( classLoader == SecuritySupport.getContextClassLoader() ) {109debugPrintln(()->"using thread context class loader ("+classLoader+") for search");110return;111}112} catch( Throwable unused ) {113// getContextClassLoader() undefined in JDK1.1114}115116if( classLoader==ClassLoader.getSystemClassLoader() ) {117debugPrintln(()->"using system class loader ("+classLoader+") for search");118return;119}120121debugPrintln(()->"using class loader ("+classLoader+") for search");122}123124/**125* <p>Creates a new {@link SchemaFactory} object for the specified126* schema language.</p>127*128* @param schemaLanguage129* See {@link SchemaFactory Schema Language} table in <code>SchemaFactory</code>130* for the list of available schema languages.131*132* @return <code>null</code> if the callee fails to create one.133*134* @throws NullPointerException135* If the <code>schemaLanguage</code> parameter is null.136* @throws SchemaFactoryConfigurationError137* If a configuration error is encountered.138*/139public SchemaFactory newFactory(String schemaLanguage) {140if(schemaLanguage==null) {141throw new NullPointerException();142}143SchemaFactory f = _newFactory(schemaLanguage);144if (f != null) {145debugPrintln(()->"factory '" + f.getClass().getName() + "' was found for " + schemaLanguage);146} else {147debugPrintln(()->"unable to find a factory for " + schemaLanguage);148}149return f;150}151152/**153* <p>Lookup a <code>SchemaFactory</code> for the given <code>schemaLanguage</code>.</p>154*155* @param schemaLanguage Schema language to lookup <code>SchemaFactory</code> for.156*157* @return <code>SchemaFactory</code> for the given <code>schemaLanguage</code>.158*/159private SchemaFactory _newFactory(String schemaLanguage) {160SchemaFactory sf;161162String propertyName = SERVICE_CLASS.getName() + ":" + schemaLanguage;163164// system property look up165try {166debugPrintln(()->"Looking up system property '"+propertyName+"'" );167String r = SecuritySupport.getSystemProperty(propertyName);168if(r!=null) {169debugPrintln(()->"The value is '"+r+"'");170sf = createInstance(r);171if(sf!=null) return sf;172} else173debugPrintln(()->"The property is undefined.");174} catch( Throwable t ) {175if( debug ) {176debugPrintln(()->"failed to look up system property '"+propertyName+"'" );177t.printStackTrace();178}179}180181String javah = SecuritySupport.getSystemProperty( "java.home" );182String configFile = javah + File.separator +183"conf" + File.separator + "jaxp.properties";184185186// try to read from $java.home/conf/jaxp.properties187try {188if(firstTime){189synchronized(cacheProps){190if(firstTime){191File f=new File( configFile );192firstTime = false;193if(SecuritySupport.doesFileExist(f)){194debugPrintln(()->"Read properties file " + f);195cacheProps.load(SecuritySupport.getFileInputStream(f));196}197}198}199}200final String factoryClassName = cacheProps.getProperty(propertyName);201debugPrintln(()->"found " + factoryClassName + " in $java.home/conf/jaxp.properties");202203if (factoryClassName != null) {204sf = createInstance(factoryClassName);205if(sf != null){206return sf;207}208}209} catch (Exception ex) {210if (debug) {211ex.printStackTrace();212}213}214215// Try with ServiceLoader216final SchemaFactory factoryImpl = findServiceProvider(schemaLanguage);217218// The following assertion should always be true.219// Uncomment it, recompile, and run with -ea in case of doubts:220// assert factoryImpl == null || factoryImpl.isSchemaLanguageSupported(schemaLanguage);221222if (factoryImpl != null) {223return factoryImpl;224}225226// platform default227if(schemaLanguage.equals("http://www.w3.org/2001/XMLSchema")) {228debugPrintln(()->"attempting to use the platform default XML Schema validator");229return new XMLSchemaFactory();230}231232debugPrintln(()->"all things were tried, but none was found. bailing out.");233return null;234}235236/** <p>Create class using appropriate ClassLoader.</p>237*238* @param className Name of class to create.239* @return Created class or <code>null</code>.240*/241@SuppressWarnings("removal")242private Class<?> createClass(String className) {243Class<?> clazz;244// make sure we have access to restricted packages245boolean internal = false;246if (System.getSecurityManager() != null) {247if (className != null && className.startsWith(DEFAULT_PACKAGE)) {248internal = true;249}250}251252try {253if (classLoader != null && !internal) {254clazz = Class.forName(className, false, classLoader);255} else {256clazz = Class.forName(className);257}258} catch (Throwable t) {259if(debug) {260t.printStackTrace();261}262return null;263}264265return clazz;266}267268/**269* <p>Creates an instance of the specified and returns it.</p>270*271* @param className272* fully qualified class name to be instantiated.273*274* @return null275* if it fails. Error messages will be printed by this method.276*/277SchemaFactory createInstance(String className) {278SchemaFactory schemaFactory = null;279280debugPrintln(()->"createInstance(" + className + ")");281282// get Class from className283Class<?> clazz = createClass(className);284if (clazz == null) {285debugPrintln(()->"failed to getClass(" + className + ")");286return null;287}288debugPrintln(()->"loaded " + className + " from " + which(clazz));289290// instantiate Class as a SchemaFactory291try {292if (!SchemaFactory.class.isAssignableFrom(clazz)) {293throw new ClassCastException(clazz.getName()294+ " cannot be cast to " + SchemaFactory.class);295}296schemaFactory = (SchemaFactory) clazz.getConstructor().newInstance();297} catch (ClassCastException | IllegalAccessException | IllegalArgumentException |298InstantiationException | InvocationTargetException | NoSuchMethodException |299SecurityException ex) {300debugPrintln(()->"could not instantiate " + clazz.getName());301if (debug) {302ex.printStackTrace();303}304return null;305}306307return schemaFactory;308}309310// Call isSchemaLanguageSupported with initial context.311@SuppressWarnings("removal")312private boolean isSchemaLanguageSupportedBy(final SchemaFactory factory,313final String schemaLanguage,314AccessControlContext acc) {315return AccessController.doPrivileged(new PrivilegedAction<Boolean>() {316public Boolean run() {317return factory.isSchemaLanguageSupported(schemaLanguage);318}319}, acc);320}321322/**323* Finds a service provider subclass of SchemaFactory that supports the324* given schema language using the ServiceLoader.325*326* @param schemaLanguage The schema language for which we seek a factory.327* @return A SchemaFactory supporting the specified schema language, or null328* if none is found.329* @throws SchemaFactoryConfigurationError if a configuration error is found.330*/331@SuppressWarnings("removal")332private SchemaFactory findServiceProvider(final String schemaLanguage) {333assert schemaLanguage != null;334// store current context.335final AccessControlContext acc = AccessController.getContext();336try {337return AccessController.doPrivileged(new PrivilegedAction<SchemaFactory>() {338public SchemaFactory run() {339final ServiceLoader<SchemaFactory> loader =340ServiceLoader.load(SERVICE_CLASS);341for (SchemaFactory factory : loader) {342// restore initial context to call343// factory.isSchemaLanguageSupported344if (isSchemaLanguageSupportedBy(factory, schemaLanguage, acc)) {345return factory;346}347}348return null; // no factory found.349}350});351} catch (ServiceConfigurationError error) {352throw new SchemaFactoryConfigurationError(353"Provider for " + SERVICE_CLASS + " cannot be created", error);354}355}356357private static final Class<SchemaFactory> SERVICE_CLASS = SchemaFactory.class;358359360// Used for debugging purposes361private static String which( Class<?> clazz ) {362return SecuritySupport.getClassSource(clazz);363}364}365366367