jaxp/src/share/classes/javax/xml/transform/FactoryFinder.java
changeset 12005 a754d69d5e60
equal deleted inserted replaced
11943:16ba58282d11 12005:a754d69d5e60
       
     1 /*
       
     2  * Copyright (c) 2003, 2006, 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 
       
    26 package javax.xml.transform;
       
    27 
       
    28 import java.io.File;
       
    29 import java.io.FileInputStream;
       
    30 
       
    31 import java.util.Properties;
       
    32 import java.io.BufferedReader;
       
    33 import java.io.IOException;
       
    34 import java.io.InputStream;
       
    35 import java.io.InputStreamReader;
       
    36 import java.net.URL;
       
    37 
       
    38 /**
       
    39  * <p>Implements pluggable Datatypes.</p>
       
    40  *
       
    41  * <p>This class is duplicated for each JAXP subpackage so keep it in
       
    42  * sync.  It is package private for secure class loading.</p>
       
    43  *
       
    44  * @author Santiago.PericasGeertsen@sun.com
       
    45  */
       
    46 class FactoryFinder {
       
    47 
       
    48     /**
       
    49      * Internal debug flag.
       
    50      */
       
    51     private static boolean debug = false;
       
    52 
       
    53     /**
       
    54      * Cache for properties in java.home/lib/jaxp.properties
       
    55      */
       
    56     static Properties cacheProps = new Properties();
       
    57 
       
    58     /**
       
    59      * Flag indicating if properties from java.home/lib/jaxp.properties
       
    60      * have been cached.
       
    61      */
       
    62     static boolean firstTime = true;
       
    63 
       
    64     /**
       
    65      * Security support class use to check access control before
       
    66      * getting certain system resources.
       
    67      */
       
    68     static SecuritySupport ss = new SecuritySupport();
       
    69 
       
    70     // Define system property "jaxp.debug" to get output
       
    71     static {
       
    72         // Use try/catch block to support applets, which throws
       
    73         // SecurityException out of this code.
       
    74         try {
       
    75             String val = ss.getSystemProperty("jaxp.debug");
       
    76             // Allow simply setting the prop to turn on debug
       
    77             debug = val != null && !"false".equals(val);
       
    78         }
       
    79         catch (SecurityException se) {
       
    80             debug = false;
       
    81         }
       
    82     }
       
    83 
       
    84     private static void dPrint(String msg) {
       
    85         if (debug) {
       
    86             System.err.println("JAXP: " + msg);
       
    87         }
       
    88     }
       
    89 
       
    90     /**
       
    91      * Attempt to load a class using the class loader supplied. If that fails
       
    92      * and fall back is enabled, the current (i.e. bootstrap) class loader is
       
    93      * tried.
       
    94      *
       
    95      * If the class loader supplied is <code>null</code>, first try using the
       
    96      * context class loader followed by the current (i.e. bootstrap) class
       
    97      * loader.
       
    98      */
       
    99     static private Class getProviderClass(String className, ClassLoader cl,
       
   100             boolean doFallback) throws ClassNotFoundException
       
   101     {
       
   102         try {
       
   103             if (cl == null) {
       
   104                 cl = ss.getContextClassLoader();
       
   105                 if (cl == null) {
       
   106                     throw new ClassNotFoundException();
       
   107                 }
       
   108                 else {
       
   109                     return cl.loadClass(className);
       
   110                 }
       
   111             }
       
   112             else {
       
   113                 return cl.loadClass(className);
       
   114             }
       
   115         }
       
   116         catch (ClassNotFoundException e1) {
       
   117             if (doFallback) {
       
   118                 // Use current class loader - should always be bootstrap CL
       
   119                 return Class.forName(className, true, FactoryFinder.class.getClassLoader());
       
   120             }
       
   121             else {
       
   122                 throw e1;
       
   123             }
       
   124         }
       
   125     }
       
   126 
       
   127     /**
       
   128      * Create an instance of a class. Delegates to method
       
   129      * <code>getProviderClass()</code> in order to load the class.
       
   130      *
       
   131      * @param className Name of the concrete class corresponding to the
       
   132      * service provider
       
   133      *
       
   134      * @param cl ClassLoader to use to load the class, null means to use
       
   135      * the bootstrap ClassLoader
       
   136      *
       
   137      * @param doFallback True if the current ClassLoader should be tried as
       
   138      * a fallback if the class is not found using cl
       
   139      */
       
   140     static Object newInstance(String className, ClassLoader cl, boolean doFallback)
       
   141         throws ConfigurationError
       
   142     {
       
   143         try {
       
   144             Class providerClass = getProviderClass(className, cl, doFallback);
       
   145             Object instance = providerClass.newInstance();
       
   146             if (debug) {    // Extra check to avoid computing cl strings
       
   147                 dPrint("created new instance of " + providerClass +
       
   148                        " using ClassLoader: " + cl);
       
   149             }
       
   150             return instance;
       
   151         }
       
   152         catch (ClassNotFoundException x) {
       
   153             throw new ConfigurationError(
       
   154                 "Provider " + className + " not found", x);
       
   155         }
       
   156         catch (Exception x) {
       
   157             throw new ConfigurationError(
       
   158                 "Provider " + className + " could not be instantiated: " + x,
       
   159                 x);
       
   160         }
       
   161     }
       
   162 
       
   163     /**
       
   164      * Finds the implementation Class object in the specified order.  Main
       
   165      * entry point.
       
   166      * @return Class object of factory, never null
       
   167      *
       
   168      * @param factoryId             Name of the factory to find, same as
       
   169      *                              a property name
       
   170      * @param fallbackClassName     Implementation class name, if nothing else
       
   171      *                              is found.  Use null to mean no fallback.
       
   172      *
       
   173      * Package private so this code can be shared.
       
   174      */
       
   175     static Object find(String factoryId, String fallbackClassName)
       
   176         throws ConfigurationError
       
   177     {
       
   178         dPrint("find factoryId =" + factoryId);
       
   179 
       
   180         // Use the system property first
       
   181         try {
       
   182             String systemProp = ss.getSystemProperty(factoryId);
       
   183             if (systemProp != null) {
       
   184                 dPrint("found system property, value=" + systemProp);
       
   185                 return newInstance(systemProp, null, true);
       
   186             }
       
   187         }
       
   188         catch (SecurityException se) {
       
   189             if (debug) se.printStackTrace();
       
   190         }
       
   191 
       
   192         // try to read from $java.home/lib/jaxp.properties
       
   193         try {
       
   194             String factoryClassName = null;
       
   195             if (firstTime) {
       
   196                 synchronized (cacheProps) {
       
   197                     if (firstTime) {
       
   198                         String configFile = ss.getSystemProperty("java.home") + File.separator +
       
   199                             "lib" + File.separator + "jaxp.properties";
       
   200                         File f = new File(configFile);
       
   201                         firstTime = false;
       
   202                         if (ss.doesFileExist(f)) {
       
   203                             dPrint("Read properties file "+f);
       
   204                             cacheProps.load(ss.getFileInputStream(f));
       
   205                         }
       
   206                     }
       
   207                 }
       
   208             }
       
   209             factoryClassName = cacheProps.getProperty(factoryId);
       
   210 
       
   211             if (factoryClassName != null) {
       
   212                 dPrint("found in $java.home/jaxp.properties, value=" + factoryClassName);
       
   213                 return newInstance(factoryClassName, null, true);
       
   214             }
       
   215         }
       
   216         catch (Exception ex) {
       
   217             if (debug) ex.printStackTrace();
       
   218         }
       
   219 
       
   220         // Try Jar Service Provider Mechanism
       
   221         Object provider = findJarServiceProvider(factoryId);
       
   222         if (provider != null) {
       
   223             return provider;
       
   224         }
       
   225         if (fallbackClassName == null) {
       
   226             throw new ConfigurationError(
       
   227                 "Provider for " + factoryId + " cannot be found", null);
       
   228         }
       
   229 
       
   230         dPrint("loaded from fallback value: " + fallbackClassName);
       
   231         return newInstance(fallbackClassName, null, true);
       
   232     }
       
   233 
       
   234     /*
       
   235      * Try to find provider using Jar Service Provider Mechanism
       
   236      *
       
   237      * @return instance of provider class if found or null
       
   238      */
       
   239     private static Object findJarServiceProvider(String factoryId)
       
   240         throws ConfigurationError
       
   241     {
       
   242         String serviceId = "META-INF/services/" + factoryId;
       
   243         InputStream is = null;
       
   244 
       
   245         // First try the Context ClassLoader
       
   246         ClassLoader cl = ss.getContextClassLoader();
       
   247         if (cl != null) {
       
   248             is = ss.getResourceAsStream(cl, serviceId);
       
   249 
       
   250             // If no provider found then try the current ClassLoader
       
   251             if (is == null) {
       
   252                 cl = FactoryFinder.class.getClassLoader();
       
   253                 is = ss.getResourceAsStream(cl, serviceId);
       
   254             }
       
   255         } else {
       
   256             // No Context ClassLoader, try the current ClassLoader
       
   257             cl = FactoryFinder.class.getClassLoader();
       
   258             is = ss.getResourceAsStream(cl, serviceId);
       
   259         }
       
   260 
       
   261         if (is == null) {
       
   262             // No provider found
       
   263             return null;
       
   264         }
       
   265 
       
   266         if (debug) {    // Extra check to avoid computing cl strings
       
   267             dPrint("found jar resource=" + serviceId + " using ClassLoader: " + cl);
       
   268         }
       
   269 
       
   270         BufferedReader rd;
       
   271         try {
       
   272             rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
       
   273         }
       
   274         catch (java.io.UnsupportedEncodingException e) {
       
   275             rd = new BufferedReader(new InputStreamReader(is));
       
   276         }
       
   277 
       
   278         String factoryClassName = null;
       
   279         try {
       
   280             // XXX Does not handle all possible input as specified by the
       
   281             // Jar Service Provider specification
       
   282             factoryClassName = rd.readLine();
       
   283             rd.close();
       
   284         } catch (IOException x) {
       
   285             // No provider found
       
   286             return null;
       
   287         }
       
   288 
       
   289         if (factoryClassName != null && !"".equals(factoryClassName)) {
       
   290             dPrint("found in resource, value=" + factoryClassName);
       
   291 
       
   292             // Note: here we do not want to fall back to the current
       
   293             // ClassLoader because we want to avoid the case where the
       
   294             // resource file was found using one ClassLoader and the
       
   295             // provider class was instantiated using a different one.
       
   296             return newInstance(factoryClassName, cl, false);
       
   297         }
       
   298 
       
   299         // No provider found
       
   300         return null;
       
   301     }
       
   302 
       
   303     static class ConfigurationError extends Error {
       
   304         private Exception exception;
       
   305 
       
   306         /**
       
   307          * Construct a new instance with the specified detail string and
       
   308          * exception.
       
   309          */
       
   310         ConfigurationError(String msg, Exception x) {
       
   311             super(msg);
       
   312             this.exception = x;
       
   313         }
       
   314 
       
   315         Exception getException() {
       
   316             return exception;
       
   317         }
       
   318         /**
       
   319         * use the exception chaining mechanism of JDK1.4
       
   320         */
       
   321         @Override
       
   322         public Throwable getCause() {
       
   323             return exception;
       
   324         }
       
   325     }
       
   326 
       
   327 }