Path: blob/master/src/java.xml/share/classes/javax/xml/stream/FactoryFinder.java
40948 views
/*1* Copyright (c) 2005, 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.stream;2627import java.io.File;28import java.security.AccessController;29import java.security.PrivilegedAction;30import java.util.Iterator;31import java.util.Properties;32import java.util.ServiceConfigurationError;33import java.util.ServiceLoader;34import java.util.function.Supplier;35import jdk.xml.internal.SecuritySupport;3637/**38* <p>Implements pluggable streams.</p>39*40* <p>This class is duplicated for each JAXP subpackage so keep it in41* sync. It is package private for secure class loading.</p>42*43* @author Santiago PericasGeertsen44*/45class FactoryFinder {46// Check we have access to package.47private static final String DEFAULT_PACKAGE = "com.sun.xml.internal.";4849/**50* Internal debug flag.51*/52private static boolean debug = false;5354/**55* Cache for properties in java.home/conf/jaxp.properties56*/57final private static Properties cacheProps = new Properties();5859/**60* Flag indicating if properties from java.home/conf/jaxp.properties61* have been cached.62*/63private static volatile boolean firstTime = true;6465// Define system property "jaxp.debug" to get output66static {67// Use try/catch block to support applets, which throws68// SecurityException out of this code.69try {70String val = SecuritySupport.getSystemProperty("jaxp.debug");71// Allow simply setting the prop to turn on debug72debug = val != null && !"false".equals(val);73}74catch (SecurityException se) {75debug = false;76}77}7879private static void dPrint(Supplier<String> msgGen) {80if (debug) {81System.err.println("JAXP: " + msgGen.get());82}83}8485/**86* Attempt to load a class using the class loader supplied. If that fails87* and fall back is enabled, the current (i.e. bootstrap) class loader is88* tried.89*90* If the class loader supplied is <code>null</code>, first try using the91* context class loader followed by the current (i.e. bootstrap) class92* loader.93*94* Use bootstrap classLoader if cl = null and useBSClsLoader is true95*/96static private Class<?> getProviderClass(String className, ClassLoader cl,97boolean doFallback, boolean useBSClsLoader) throws ClassNotFoundException98{99try {100if (cl == null) {101if (useBSClsLoader) {102return Class.forName(className, false, FactoryFinder.class.getClassLoader());103} else {104cl = SecuritySupport.getContextClassLoader();105if (cl == null) {106throw new ClassNotFoundException();107}108else {109return Class.forName(className, false, cl);110}111}112}113else {114return Class.forName(className, false, cl);115}116}117catch (ClassNotFoundException e1) {118if (doFallback) {119// Use current class loader - should always be bootstrap CL120return Class.forName(className, false, FactoryFinder.class.getClassLoader());121}122else {123throw e1;124}125}126}127128/**129* Create an instance of a class. Delegates to method130* <code>getProviderClass()</code> in order to load the class.131*132* @param type Base class / Service interface of the factory to133* instantiate.134*135* @param className Name of the concrete class corresponding to the136* service provider137*138* @param cl <code>ClassLoader</code> used to load the factory class. If <code>null</code>139* current <code>Thread</code>'s context classLoader is used to load the factory class.140*141* @param doFallback True if the current ClassLoader should be tried as142* a fallback if the class is not found using cl143*/144static <T> T newInstance(Class<T> type, String className, ClassLoader cl, boolean doFallback)145throws FactoryConfigurationError146{147return newInstance(type, className, cl, doFallback, false);148}149150/**151* Create an instance of a class. Delegates to method152* <code>getProviderClass()</code> in order to load the class.153*154* @param type Base class / Service interface of the factory to155* instantiate.156*157* @param className Name of the concrete class corresponding to the158* service provider159*160* @param cl <code>ClassLoader</code> used to load the factory class. If <code>null</code>161* current <code>Thread</code>'s context classLoader is used to load the factory class.162*163* @param doFallback True if the current ClassLoader should be tried as164* a fallback if the class is not found using cl165*166* @param useBSClsLoader True if cl=null actually meant bootstrap classLoader. This parameter167* is needed since DocumentBuilderFactory/SAXParserFactory defined null as context classLoader.168*/169@SuppressWarnings("removal")170static <T> T newInstance(Class<T> type, String className, ClassLoader cl,171boolean doFallback, boolean useBSClsLoader)172throws FactoryConfigurationError173{174assert type != null;175176// make sure we have access to restricted packages177if (System.getSecurityManager() != null) {178if (className != null && className.startsWith(DEFAULT_PACKAGE)) {179cl = null;180useBSClsLoader = true;181}182}183184try {185Class<?> providerClass = getProviderClass(className, cl, doFallback, useBSClsLoader);186if (!type.isAssignableFrom(providerClass)) {187throw new ClassCastException(className + " cannot be cast to " + type.getName());188}189Object instance = providerClass.getConstructor().newInstance();190final ClassLoader clD = cl;191dPrint(()->"created new instance of " + providerClass +192" using ClassLoader: " + clD);193return type.cast(instance);194}195catch (ClassNotFoundException x) {196throw new FactoryConfigurationError(197"Provider " + className + " not found", x);198}199catch (Exception x) {200throw new FactoryConfigurationError(201"Provider " + className + " could not be instantiated: " + x,202x);203}204}205206/**207* Finds the implementation Class object in the specified order.208*209* @return Class object of factory, never null210*211* @param type Base class / Service interface of the212* factory to find.213*214* @param fallbackClassName Implementation class name, if nothing else215* is found. Use null to mean no fallback.216*217* Package private so this code can be shared.218*/219static <T> T find(Class<T> type, String fallbackClassName)220throws FactoryConfigurationError221{222return find(type, type.getName(), null, fallbackClassName);223}224225/**226* Finds the implementation Class object in the specified order. Main227* entry point.228* @return Class object of factory, never null229*230* @param type Base class / Service interface of the231* factory to find.232*233* @param factoryId Name of the factory to find, same as234* a property name235*236* @param cl ClassLoader to be used to load the class, null means to use237* the bootstrap ClassLoader238*239* @param fallbackClassName Implementation class name, if nothing else240* is found. Use null to mean no fallback.241*242* Package private so this code can be shared.243*/244static <T> T find(Class<T> type, String factoryId, ClassLoader cl, String fallbackClassName)245throws FactoryConfigurationError246{247dPrint(()->"find factoryId =" + factoryId);248249// Use the system property first250try {251252final String systemProp;253if (type.getName().equals(factoryId)) {254systemProp = SecuritySupport.getSystemProperty(factoryId);255} else {256systemProp = System.getProperty(factoryId);257}258if (systemProp != null) {259dPrint(()->"found system property, value=" + systemProp);260return newInstance(type, systemProp, cl, true);261}262}263catch (SecurityException se) {264throw new FactoryConfigurationError(265"Failed to read factoryId '" + factoryId + "'", se);266}267268// Try read $java.home/conf/stax.properties followed by269// $java.home/conf/jaxp.properties if former not present270String configFile = null;271try {272if (firstTime) {273synchronized (cacheProps) {274if (firstTime) {275configFile = SecuritySupport.getSystemProperty("java.home") + File.separator +276"conf" + File.separator + "stax.properties";277final File fStax = new File(configFile);278firstTime = false;279if (SecuritySupport.doesFileExist(fStax)) {280dPrint(()->"Read properties file "+fStax);281cacheProps.load(SecuritySupport.getFileInputStream(fStax));282}283else {284configFile = SecuritySupport.getSystemProperty("java.home") + File.separator +285"conf" + File.separator + "jaxp.properties";286final File fJaxp = new File(configFile);287if (SecuritySupport.doesFileExist(fJaxp)) {288dPrint(()->"Read properties file "+fJaxp);289cacheProps.load(SecuritySupport.getFileInputStream(fJaxp));290}291}292}293}294}295final String factoryClassName = cacheProps.getProperty(factoryId);296297if (factoryClassName != null) {298final String foundIn = configFile;299dPrint(()->"found in " + foundIn + " value=" + factoryClassName);300return newInstance(type, factoryClassName, cl, true);301}302}303catch (Exception ex) {304if (debug) ex.printStackTrace();305}306307if (type.getName().equals(factoryId)) {308// Try Jar Service Provider Mechanism309final T provider = findServiceProvider(type, cl);310if (provider != null) {311return provider;312}313} else {314// We're in the case where a 'custom' factoryId was provided,315// and in every case where that happens, we expect that316// fallbackClassName will be null.317assert fallbackClassName == null;318}319if (fallbackClassName == null) {320throw new FactoryConfigurationError(321"Provider for " + factoryId + " cannot be found", null);322}323324dPrint(()->"loaded from fallback value: " + fallbackClassName);325return newInstance(type, fallbackClassName, cl, true);326}327328/*329* Try to find provider using the ServiceLoader API330*331* @param type Base class / Service interface of the factory to find.332*333* @return instance of provider class if found or null334*/335@SuppressWarnings("removal")336private static <T> T findServiceProvider(final Class<T> type, final ClassLoader cl) {337try {338return AccessController.doPrivileged(new PrivilegedAction<T>() {339@Override340public T run() {341final ServiceLoader<T> serviceLoader;342if (cl == null) {343//the current thread's context class loader344serviceLoader = ServiceLoader.load(type);345} else {346serviceLoader = ServiceLoader.load(type, cl);347}348final Iterator<T> iterator = serviceLoader.iterator();349if (iterator.hasNext()) {350return iterator.next();351} else {352return null;353}354}355});356} catch(ServiceConfigurationError e) {357// It is not possible to wrap an error directly in358// FactoryConfigurationError - so we need to wrap the359// ServiceConfigurationError in a RuntimeException.360// The alternative would be to modify the logic in361// FactoryConfigurationError to allow setting a362// Throwable as the cause, but that could cause363// compatibility issues down the road.364final RuntimeException x = new RuntimeException(365"Provider for " + type + " cannot be created", e);366final FactoryConfigurationError error =367new FactoryConfigurationError(x, x.getMessage());368throw error;369}370}371372}373374375