1/*
2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
3 */
4/*
5 * Licensed to the Apache Software Foundation (ASF) under one or more
6 * contributor license agreements.  See the NOTICE file distributed with
7 * this work for additional information regarding copyright ownership.
8 * The ASF licenses this file to You under the Apache License, Version 2.0
9 * (the "License"); you may not use this file except in compliance with
10 * the License.  You may obtain a copy of the License at
11 *
12 *      http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21package com.sun.org.apache.xalan.internal.utils;
22
23import java.util.function.Supplier;
24
25/**
26 * This class is duplicated for each JAXP subpackage so keep it in sync.
27 * It is package private and therefore is not exposed as part of the JAXP
28 * API.
29 * <p>
30 * This class was moved from the <code>javax.xml.parsers.ObjectFactory</code>
31 * class and modified to be used as a general utility for creating objects
32 * dynamically.
33 *
34 */
35public class ObjectFactory {
36
37    //
38    // Constants
39    //
40     private static final String JAXP_INTERNAL = "com.sun.org.apache";
41     private static final String STAX_INTERNAL = "com.sun.xml.internal";
42
43    /** Set to true for debugging */
44    private static final boolean DEBUG = false;
45
46
47    /** Prints a message to standard error if debugging is enabled. */
48    private static void debugPrintln(Supplier<String> msgGen) {
49        if (DEBUG) {
50            System.err.println("JAXP: " + msgGen.get());
51        }
52    } // debugPrintln(String)
53
54    /**
55     * Figure out which ClassLoader to use.  For JDK 1.2 and later use
56     * the context ClassLoader.
57     */
58    public static ClassLoader findClassLoader()
59    {
60        if (System.getSecurityManager()!=null) {
61            //this will ensure bootclassloader is used
62            return null;
63        }
64
65        // Figure out which ClassLoader to use for loading the provider
66        // class.  If there is a Context ClassLoader then use it.
67        ClassLoader context = SecuritySupport.getContextClassLoader();
68        ClassLoader system = SecuritySupport.getSystemClassLoader();
69
70        ClassLoader chain = system;
71        while (true) {
72            if (context == chain) {
73                // Assert: we are on JDK 1.1 or we have no Context ClassLoader
74                // or any Context ClassLoader in chain of system classloader
75                // (including extension ClassLoader) so extend to widest
76                // ClassLoader (always look in system ClassLoader if Xalan
77                // is in boot/extension/system classpath and in current
78                // ClassLoader otherwise); normal classloaders delegate
79                // back to system ClassLoader first so this widening doesn't
80                // change the fact that context ClassLoader will be consulted
81                ClassLoader current = ObjectFactory.class.getClassLoader();
82
83                chain = system;
84                while (true) {
85                    if (current == chain) {
86                        // Assert: Current ClassLoader in chain of
87                        // boot/extension/system ClassLoaders
88                        return system;
89                    }
90                    if (chain == null) {
91                        break;
92                    }
93                    chain = SecuritySupport.getParentClassLoader(chain);
94                }
95
96                // Assert: Current ClassLoader not in chain of
97                // boot/extension/system ClassLoaders
98                return current;
99            }
100
101            if (chain == null) {
102                // boot ClassLoader reached
103                break;
104            }
105
106            // Check for any extension ClassLoaders in chain up to
107            // boot ClassLoader
108            chain = SecuritySupport.getParentClassLoader(chain);
109        }
110
111        // Assert: Context ClassLoader not in chain of
112        // boot/extension/system ClassLoaders
113        return context;
114    } // findClassLoader():ClassLoader
115
116    /**
117     * Create an instance of a class using the same class loader for the ObjectFactory by default
118     * or boot class loader when Security Manager is in place
119     */
120    public static Object newInstance(String className, boolean doFallback)
121        throws ConfigurationError
122    {
123        ClassLoader cl = System.getSecurityManager()!=null ? null : findClassLoader();
124        try{
125            Class providerClass = findProviderClass(className, cl, doFallback);
126            Object instance = providerClass.getConstructor().newInstance();
127            debugPrintln(()->"created new instance of " + providerClass +
128                             " using ClassLoader: " + cl);
129            return instance;
130        } catch (ClassNotFoundException x) {
131            throw new ConfigurationError(
132                "Provider " + className + " not found", x);
133        } catch (Exception x) {
134            throw new ConfigurationError(
135                "Provider " + className + " could not be instantiated: " + x,
136                x);
137        }
138    }
139
140    /**
141     * Find a Class using the same class loader for the ObjectFactory by default
142     * or boot class loader when Security Manager is in place
143     */
144    public static Class<?> findProviderClass(String className, boolean doFallback)
145        throws ClassNotFoundException, ConfigurationError
146    {
147        return findProviderClass (className,
148                findClassLoader (), doFallback);
149    }
150
151    /**
152     * Find a Class using the specified ClassLoader
153     */
154    private static Class<?> findProviderClass(String className, ClassLoader cl,
155                                           boolean doFallback)
156        throws ClassNotFoundException, ConfigurationError
157    {
158        //throw security exception if the calling thread is not allowed to access the
159        //class. Restrict the access to the package classes as specified in java.security policy.
160        SecurityManager security = System.getSecurityManager();
161        try{
162            if (security != null){
163                if (className.startsWith(JAXP_INTERNAL) ||
164                    className.startsWith(STAX_INTERNAL)) {
165                    cl = null;
166                } else {
167                    final int lastDot = className.lastIndexOf(".");
168                    String packageName = className;
169                    if (lastDot != -1) packageName = className.substring(0, lastDot);
170                    security.checkPackageAccess(packageName);
171                }
172             }
173        }catch(SecurityException e){
174            throw e;
175        }
176
177        Class<?> providerClass;
178        if (cl == null) {
179            providerClass = Class.forName(className, false, ObjectFactory.class.getClassLoader());
180        } else {
181            try {
182                providerClass = cl.loadClass(className);
183            } catch (ClassNotFoundException x) {
184                if (doFallback) {
185                    // Fall back to current classloader
186                    ClassLoader current = ObjectFactory.class.getClassLoader();
187                    if (current == null) {
188                        providerClass = Class.forName(className);
189                    } else if (cl != current) {
190                        cl = current;
191                        providerClass = cl.loadClass(className);
192                    } else {
193                        throw x;
194                    }
195                } else {
196                    throw x;
197                }
198            }
199        }
200
201        return providerClass;
202    }
203
204} // class ObjectFactory
205