Home | History | Annotate | Download | only in datatype
      1 /*
      2  * Licensed to the Apache Software Foundation (ASF) under one or more
      3  * contributor license agreements.  See the NOTICE file distributed with
      4  * this work for additional information regarding copyright ownership.
      5  * The ASF licenses this file to You under the Apache License, Version 2.0
      6  * (the "License"); you may not use this file except in compliance with
      7  * the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 
     18 // $Id: FactoryFinder.java 670432 2008-06-23 02:02:08Z mrglavas $
     19 
     20 package javax.xml.datatype;
     21 
     22 import java.io.BufferedReader;
     23 import java.io.File;
     24 import java.io.FileInputStream;
     25 import java.io.IOException;
     26 import java.io.InputStream;
     27 import java.io.InputStreamReader;
     28 import java.net.URL;
     29 import java.util.Properties;
     30 import libcore.io.IoUtils;
     31 
     32 /**
     33  * <p>Implement pluggable data types.</p>
     34  *
     35  * <p>This class is duplicated for each JAXP subpackage so keep it in
     36  * sync.  It is package private for secure class loading.</p>
     37  *
     38  * @author <a href="mailto:Jeff.Suttor (at) Sun.com">Jeff Suttor</a>
     39  * @version $Revision: 670432 $, $Date: 2008-06-22 19:02:08 -0700 (Sun, 22 Jun 2008) $
     40  * @since 1.5
     41  */
     42 final class FactoryFinder {
     43 
     44     /** <p>Name of class to display in output messages.</p> */
     45     private static final String CLASS_NAME = "javax.xml.datatype.FactoryFinder";
     46 
     47     /** <p>Debug flag to trace loading process.</p> */
     48     private static boolean debug = false;
     49 
     50     /**
     51      * <p>Cache properties for performance. Use a static class to avoid double-checked
     52      * locking.</p>
     53      */
     54     private static class CacheHolder {
     55 
     56         private static Properties cacheProps = new Properties();
     57 
     58         static {
     59             String javah = System.getProperty("java.home");
     60             String configFile = javah + File.separator + "lib" + File.separator + "jaxp.properties";
     61             File f = new File(configFile);
     62             if (f.exists()) {
     63                 if (debug) debugPrintln("Read properties file " + f);
     64                 try (FileInputStream inputStream = new FileInputStream(f)) {
     65                     cacheProps.load(inputStream);
     66                 } catch (Exception ex) {
     67                     if (debug) {
     68                         ex.printStackTrace();
     69                     }
     70                 }
     71             }
     72         }
     73     }
     74 
     75     /** Default columns per line. */
     76     private static final int DEFAULT_LINE_LENGTH = 80;
     77 
     78     /**
     79      * <p>Check to see if debugging enabled by property.</p>
     80      *
     81      * <p>Use try/catch block to support applets, which throws
     82      * SecurityException out of this code.</p>
     83      */
     84     static {
     85         String val = System.getProperty("jaxp.debug");
     86         // Allow simply setting the prop to turn on debug
     87         debug = val != null && (! "false".equals(val));
     88     }
     89 
     90     private FactoryFinder() {}
     91 
     92     /**
     93      * <p>Output debugging messages.</p>
     94      *
     95      * @param msg <code>String</code> to print to <code>stderr</code>.
     96      */
     97     private static void debugPrintln(String msg) {
     98         if (debug) {
     99             System.err.println(
    100                 CLASS_NAME
    101                 + ":"
    102                 + msg);
    103         }
    104     }
    105 
    106     /**
    107      * <p>Find the appropriate <code>ClassLoader</code> to use.</p>
    108      *
    109      * <p>The context ClassLoader is preferred.</p>
    110      *
    111      * @return <code>ClassLoader</code> to use.
    112      *
    113      * @throws ConfigurationError If a valid <code>ClassLoader</code> cannot be identified.
    114      */
    115     private static ClassLoader findClassLoader() throws ConfigurationError {
    116         // Figure out which ClassLoader to use for loading the provider
    117         // class.  If there is a Context ClassLoader then use it.
    118 
    119         ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    120 
    121         if (debug) debugPrintln(
    122             "Using context class loader: "
    123             + classLoader);
    124 
    125         if (classLoader == null) {
    126             // if we have no Context ClassLoader
    127             // so use the current ClassLoader
    128             classLoader = FactoryFinder.class.getClassLoader();
    129             if (debug) debugPrintln(
    130                 "Using the class loader of FactoryFinder: "
    131                 + classLoader);
    132         }
    133 
    134         return classLoader;
    135     }
    136 
    137     /**
    138      * <p>Create an instance of a class using the specified ClassLoader.</p>
    139      *
    140      * @param className Name of class to create.
    141      * @param classLoader ClassLoader to use to create named class.
    142      *
    143      * @return New instance of specified class created using the specified ClassLoader.
    144      *
    145      * @throws ConfigurationError If class could not be created.
    146      */
    147     static Object newInstance(
    148         String className,
    149         ClassLoader classLoader)
    150         throws ConfigurationError {
    151 
    152         try {
    153             Class spiClass;
    154             if (classLoader == null) {
    155                 spiClass = Class.forName(className);
    156             } else {
    157                 spiClass = classLoader.loadClass(className);
    158             }
    159 
    160             if (debug) {
    161                 debugPrintln("Loaded " + className + " from " + which(spiClass));
    162             }
    163 
    164             return spiClass.newInstance();
    165         } catch (ClassNotFoundException x) {
    166             throw new ConfigurationError(
    167                 "Provider " + className + " not found", x);
    168         } catch (Exception x) {
    169             throw new ConfigurationError(
    170                 "Provider " + className + " could not be instantiated: " + x, x);
    171         }
    172     }
    173 
    174     /**
    175      * Finds the implementation Class object in the specified order.  Main
    176      * entry point.
    177      * Package private so this code can be shared.
    178      *
    179      * @param factoryId Name of the factory to find, same as a property name
    180      * @param fallbackClassName Implementation class name, if nothing else is found.  Use null to mean no fallback.
    181      *
    182      * @return Class Object of factory, never null
    183      *
    184      * @throws ConfigurationError If Class cannot be found.
    185      */
    186     static Object find(String factoryId, String fallbackClassName) throws ConfigurationError {
    187 
    188         ClassLoader classLoader = findClassLoader();
    189 
    190         // Use the system property first
    191         String systemProp = System.getProperty(factoryId);
    192         if (systemProp != null && systemProp.length() > 0) {
    193             if (debug) debugPrintln("found " + systemProp + " in the system property " + factoryId);
    194             return newInstance(systemProp, classLoader);
    195         }
    196 
    197         // try to read from $java.home/lib/jaxp.properties
    198         try {
    199             String factoryClassName = CacheHolder.cacheProps.getProperty(factoryId);
    200             if (debug) debugPrintln("found " + factoryClassName + " in $java.home/jaxp.properties");
    201 
    202             if (factoryClassName != null) {
    203                 return newInstance(factoryClassName, classLoader);
    204             }
    205         } catch (Exception ex) {
    206             if (debug) {
    207                 ex.printStackTrace();
    208             }
    209         }
    210 
    211         // Try Jar Service Provider Mechanism
    212         Object provider = findJarServiceProvider(factoryId);
    213         if (provider != null) {
    214             return provider;
    215         }
    216 
    217         if (fallbackClassName == null) {
    218             throw new ConfigurationError(
    219                 "Provider for " + factoryId + " cannot be found", null);
    220         }
    221 
    222         if (debug) debugPrintln("loaded from fallback value: " + fallbackClassName);
    223         return newInstance(fallbackClassName, classLoader);
    224     }
    225 
    226     /*
    227      * Try to find provider using Jar Service Provider Mechanism
    228      *
    229      * @return instance of provider class if found or null
    230      */
    231     private static Object findJarServiceProvider(String factoryId) throws ConfigurationError {
    232         String serviceId = "META-INF/services/" + factoryId;
    233         InputStream is = null;
    234 
    235         // First try the Context ClassLoader
    236         ClassLoader cl = Thread.currentThread().getContextClassLoader();
    237         if (cl != null) {
    238             is = cl.getResourceAsStream(serviceId);
    239         }
    240 
    241         if (is == null) {
    242             cl = FactoryFinder.class.getClassLoader();
    243             is = cl.getResourceAsStream(serviceId);
    244         }
    245 
    246         if (is == null) {
    247             // No provider found
    248             return null;
    249         }
    250 
    251         if (debug) debugPrintln("found jar resource=" + serviceId + " using ClassLoader: " + cl);
    252 
    253         BufferedReader rd;
    254         try {
    255             rd = new BufferedReader(new InputStreamReader(is, "UTF-8"), DEFAULT_LINE_LENGTH);
    256         } catch (java.io.UnsupportedEncodingException e) {
    257             rd = new BufferedReader(new InputStreamReader(is), DEFAULT_LINE_LENGTH);
    258         }
    259 
    260         String factoryClassName = null;
    261         try {
    262             // XXX Does not handle all possible input as specified by the
    263             // Jar Service Provider specification
    264             factoryClassName = rd.readLine();
    265         } catch (IOException x) {
    266             // No provider found
    267             return null;
    268         } finally {
    269             IoUtils.closeQuietly(rd);
    270         }
    271 
    272         if (factoryClassName != null &&
    273             ! "".equals(factoryClassName)) {
    274             if (debug) debugPrintln("found in resource, value="
    275                    + factoryClassName);
    276 
    277             return newInstance(factoryClassName, cl);
    278         }
    279 
    280         // No provider found
    281         return null;
    282     }
    283 
    284     /**
    285      * <p>Configuration Error.</p>
    286      */
    287     static class ConfigurationError extends Error {
    288 
    289         private static final long serialVersionUID = -3644413026244211347L;
    290 
    291         /**
    292          * <p>Exception that caused the error.</p>
    293          */
    294         private Exception exception;
    295 
    296         /**
    297          * <p>Construct a new instance with the specified detail string and
    298          * exception.</p>
    299          *
    300          * @param msg Detail message for this error.
    301          * @param x Exception that caused the error.
    302          */
    303         ConfigurationError(String msg, Exception x) {
    304             super(msg);
    305             this.exception = x;
    306         }
    307 
    308         /**
    309          * <p>Get the Exception that caused the error.</p>
    310          *
    311          * @return Exception that caused the error.
    312          */
    313         Exception getException() {
    314             return exception;
    315         }
    316     }
    317 
    318     /**
    319      * Returns the location where the given Class is loaded from.
    320      */
    321     private static String which(Class clazz) {
    322         try {
    323             String classnameAsResource = clazz.getName().replace('.', '/') + ".class";
    324 
    325             ClassLoader loader = clazz.getClassLoader();
    326 
    327             URL it;
    328 
    329             if (loader != null) {
    330                 it = loader.getResource(classnameAsResource);
    331             } else {
    332                 it = ClassLoader.getSystemResource(classnameAsResource);
    333             }
    334 
    335             if (it != null) {
    336                 return it.toString();
    337             }
    338         }
    339         // The VM ran out of memory or there was some other serious problem. Re-throw.
    340         catch (VirtualMachineError vme) {
    341             throw vme;
    342         }
    343         // ThreadDeath should always be re-thrown
    344         catch (ThreadDeath td) {
    345             throw td;
    346         }
    347         catch (Throwable t) {
    348             // work defensively.
    349             if (debug) {
    350                 t.printStackTrace();
    351             }
    352         }
    353         return "unknown location";
    354     }
    355 }
    356