FactoryFinder.java revision 1188:86157a0bf14f
1/*
2 * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package javax.xml.transform;
27
28import java.io.File;
29import java.lang.reflect.Method;
30import java.lang.reflect.Modifier;
31import java.security.AccessController;
32import java.security.PrivilegedAction;
33import java.util.Iterator;
34import java.util.Properties;
35import java.util.ServiceConfigurationError;
36import java.util.ServiceLoader;
37import java.util.function.Supplier;
38
39/**
40 * <p>Implements pluggable Datatypes.</p>
41 *
42 * <p>This class is duplicated for each JAXP subpackage so keep it in
43 * sync.  It is package private for secure class loading.</p>
44 *
45 * @author Santiago.PericasGeertsen@sun.com
46 * @author Huizhe.Wang@oracle.com
47 */
48class FactoryFinder {
49    private static final String DEFAULT_PACKAGE = "com.sun.org.apache.xalan.internal.";
50
51    /**
52     * Internal debug flag.
53     */
54    private static boolean debug = false;
55
56    /**
57     * Cache for properties in java.home/conf/jaxp.properties
58     */
59    private final static Properties cacheProps = new Properties();
60
61    /**
62     * Flag indicating if properties from java.home/conf/jaxp.properties
63     * have been cached.
64     */
65    static volatile boolean firstTime = true;
66
67    /**
68     * Security support class use to check access control before
69     * getting certain system resources.
70     */
71    private final static SecuritySupport ss = new SecuritySupport();
72
73    // Define system property "jaxp.debug" to get output
74    static {
75        // Use try/catch block to support applets, which throws
76        // SecurityException out of this code.
77        try {
78            String val = ss.getSystemProperty("jaxp.debug");
79            // Allow simply setting the prop to turn on debug
80            debug = val != null && !"false".equals(val);
81        }
82        catch (SecurityException se) {
83            debug = false;
84        }
85    }
86
87    private static void dPrint(Supplier<String> msgGen) {
88        if (debug) {
89            System.err.println("JAXP: " + msgGen.get());
90        }
91    }
92
93    /**
94     * Attempt to load a class using the class loader supplied. If that fails
95     * and fall back is enabled, the current (i.e. bootstrap) class loader is
96     * tried.
97     *
98     * If the class loader supplied is <code>null</code>, first try using the
99     * context class loader followed by the current (i.e. bootstrap) class
100     * loader.
101     *
102     * Use bootstrap classLoader if cl = null and useBSClsLoader is true
103     */
104    static private Class<?> getProviderClass(String className, ClassLoader cl,
105            boolean doFallback, boolean useBSClsLoader) throws ClassNotFoundException
106    {
107        try {
108            if (cl == null) {
109                if (useBSClsLoader) {
110                    return Class.forName(className, false, FactoryFinder.class.getClassLoader());
111                } else {
112                    cl = ss.getContextClassLoader();
113                    if (cl == null) {
114                        throw new ClassNotFoundException();
115                    }
116                    else {
117                        return Class.forName(className, false, cl);
118                    }
119                }
120            }
121            else {
122                return Class.forName(className, false, cl);
123            }
124        }
125        catch (ClassNotFoundException e1) {
126            if (doFallback) {
127                // Use current class loader - should always be bootstrap CL
128                return Class.forName(className, false, FactoryFinder.class.getClassLoader());
129            }
130            else {
131                throw e1;
132            }
133        }
134    }
135
136    /**
137     * Create an instance of a class. Delegates to method
138     * <code>getProviderClass()</code> in order to load the class.
139     *
140     * @param type Base class / Service interface  of the factory to
141     *             instantiate.
142     *
143     * @param className Name of the concrete class corresponding to the
144     * service provider
145     *
146     * @param cl <code>ClassLoader</code> used to load the factory class. If <code>null</code>
147     * current <code>Thread</code>'s context classLoader is used to load the factory class.
148     *
149     * @param doFallback True if the current ClassLoader should be tried as
150     * a fallback if the class is not found using cl
151     *
152     * @param useServicesMechanism True use services mechanism
153     */
154    static <T> T newInstance(Class<T> type, String className, ClassLoader cl,
155                             boolean doFallback, boolean useServicesMechanism)
156        throws TransformerFactoryConfigurationError
157    {
158        assert type != null;
159
160        boolean useBSClsLoader = false;
161        // make sure we have access to restricted packages
162        if (System.getSecurityManager() != null) {
163            if (className != null && className.startsWith(DEFAULT_PACKAGE)) {
164                cl = null;
165                useBSClsLoader = true;
166            }
167        }
168
169        try {
170            Class<?> providerClass = getProviderClass(className, cl, doFallback, useBSClsLoader);
171            if (!type.isAssignableFrom(providerClass)) {
172                throw new ClassCastException(className + " cannot be cast to " + type.getName());
173            }
174            Object instance = null;
175            if (!useServicesMechanism) {
176                instance = newInstanceNoServiceLoader(type, providerClass);
177            }
178            if (instance == null) {
179                instance = providerClass.getConstructor().newInstance();
180            }
181            final ClassLoader clD = cl;
182            dPrint(()->"created new instance of " + providerClass +
183                       " using ClassLoader: " + clD);
184            return type.cast(instance);
185        }
186        catch (ClassNotFoundException x) {
187            throw new TransformerFactoryConfigurationError(x,
188                "Provider " + className + " not found");
189        }
190        catch (Exception x) {
191            throw new TransformerFactoryConfigurationError(x,
192                "Provider " + className + " could not be instantiated: " + x);
193        }
194    }
195
196    /**
197     * Try to construct using newTransformerFactoryNoServiceLoader
198     *   method if available.
199     */
200    private static <T> T newInstanceNoServiceLoader(Class<T> type, Class<?> providerClass) {
201        // Retain maximum compatibility if no security manager.
202        if (System.getSecurityManager() == null) {
203            return null;
204        }
205        try {
206            final Method creationMethod =
207                    providerClass.getDeclaredMethod(
208                        "newTransformerFactoryNoServiceLoader"
209                    );
210            final int modifiers = creationMethod.getModifiers();
211
212            // Do not call the method if it's not public static.
213            if (!Modifier.isPublic(modifiers) || !Modifier.isStatic(modifiers)) {
214                return null;
215            }
216
217            // Only call the method if it's declared to return an instance of
218            // TransformerFactory
219            final Class<?> returnType = creationMethod.getReturnType();
220            if (type.isAssignableFrom(returnType)) {
221                final Object result = creationMethod.invoke(null, (Object[])null);
222                return type.cast(result);
223            } else {
224                // This should not happen, as
225                // TransformerFactoryImpl.newTransformerFactoryNoServiceLoader is
226                // declared to return TransformerFactory.
227                throw new ClassCastException(returnType + " cannot be cast to " + type);
228            }
229        } catch (ClassCastException e) {
230            throw new TransformerFactoryConfigurationError(e, e.getMessage());
231        } catch (NoSuchMethodException exc) {
232            return null;
233        } catch (Exception exc) {
234            return null;
235        }
236    }
237
238    /**
239     * Finds the implementation Class object in the specified order.  Main
240     * entry point.
241     * @return Class object of factory, never null
242     *
243     * @param type                  Base class / Service interface  of the
244     *                              factory to find.
245     *
246     * @param fallbackClassName     Implementation class name, if nothing else
247     *                              is found.  Use null to mean no fallback.
248     *
249     * Package private so this code can be shared.
250     */
251    static <T> T find(Class<T> type, String fallbackClassName)
252        throws TransformerFactoryConfigurationError
253    {
254        assert type != null;
255
256        final String factoryId = type.getName();
257
258        dPrint(()->"find factoryId =" + factoryId);
259        // Use the system property first
260        try {
261            String systemProp = ss.getSystemProperty(factoryId);
262            if (systemProp != null) {
263                dPrint(()->"found system property, value=" + systemProp);
264                return newInstance(type, systemProp, null, true, true);
265            }
266        }
267        catch (SecurityException se) {
268            if (debug) se.printStackTrace();
269        }
270
271        // try to read from $java.home/conf/jaxp.properties
272        try {
273            if (firstTime) {
274                synchronized (cacheProps) {
275                    if (firstTime) {
276                        String configFile = ss.getSystemProperty("java.home") + File.separator +
277                            "conf" + File.separator + "jaxp.properties";
278                        File f = new File(configFile);
279                        firstTime = false;
280                        if (ss.doesFileExist(f)) {
281                            dPrint(()->"Read properties file "+f);
282                            cacheProps.load(ss.getFileInputStream(f));
283                        }
284                    }
285                }
286            }
287            final String factoryClassName = cacheProps.getProperty(factoryId);
288
289            if (factoryClassName != null) {
290                dPrint(()->"found in ${java.home}/conf/jaxp.properties, value=" + factoryClassName);
291                return newInstance(type, factoryClassName, null, true, true);
292            }
293        }
294        catch (Exception ex) {
295            if (debug) ex.printStackTrace();
296        }
297
298        // Try Jar Service Provider Mechanism
299        T provider = findServiceProvider(type);
300        if (provider != null) {
301            return provider;
302        }
303        if (fallbackClassName == null) {
304            throw new TransformerFactoryConfigurationError(null,
305                "Provider for " + factoryId + " cannot be found");
306        }
307
308        dPrint(()->"loaded from fallback value: " + fallbackClassName);
309        return newInstance(type, fallbackClassName, null, true, true);
310    }
311
312    /*
313     * Try to find provider using the ServiceLoader.
314     *
315     * @param type Base class / Service interface  of the factory to find.
316     *
317     * @return instance of provider class if found or null
318     */
319    private static <T> T findServiceProvider(final Class<T> type)
320        throws TransformerFactoryConfigurationError
321    {
322      try {
323            return AccessController.doPrivileged(new PrivilegedAction<T>() {
324                public T run() {
325                    final ServiceLoader<T> serviceLoader = ServiceLoader.load(type);
326                    final Iterator<T> iterator = serviceLoader.iterator();
327                    if (iterator.hasNext()) {
328                        return iterator.next();
329                    } else {
330                        return null;
331                    }
332                 }
333            });
334        } catch(ServiceConfigurationError e) {
335            // It is not possible to wrap an error directly in
336            // FactoryConfigurationError - so we need to wrap the
337            // ServiceConfigurationError in a RuntimeException.
338            // The alternative would be to modify the logic in
339            // FactoryConfigurationError to allow setting a
340            // Throwable as the cause, but that could cause
341            // compatibility issues down the road.
342            final RuntimeException x = new RuntimeException(
343                    "Provider for " + type + " cannot be created", e);
344            final TransformerFactoryConfigurationError error =
345                    new TransformerFactoryConfigurationError(x, x.getMessage());
346            throw error;
347        }
348    }
349}
350