Home » xml-commons-external-1.4.01-src » javax » xml » parsers » [javadoc | source]

    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 670431 2008-06-23 01:40:03Z mrglavas $
   19   
   20   package javax.xml.parsers;
   21   
   22   import java.io.BufferedReader;
   23   import java.io.File;
   24   import java.io.IOException;
   25   import java.io.InputStream;
   26   import java.io.InputStreamReader;
   27   import java.util.Properties;
   28   
   29   /**
   30    * This class is duplicated for each JAXP subpackage so keep it in
   31    * sync.  It is package private.
   32    *
   33    * This code is designed to implement the JAXP 1.1 spec pluggability
   34    * feature and is designed to run on JDK version 1.1 and later including
   35    * JVMs that perform early linking like the Microsoft JVM in IE 5.  Note
   36    * however that it must be compiled on a JDK version 1.2 or later system
   37    * since it calls Thread#getContextClassLoader().  The code also runs both
   38    * as part of an unbundled jar file and when bundled as part of the JDK.
   39    */
   40   final class FactoryFinder {
   41       
   42       /**
   43        * <p>Debug flag to trace loading process.</p>
   44        */
   45       private static boolean debug = false;
   46       
   47       /**
   48        * <p>Cache properties for performance.</p>
   49        */
   50       private static Properties cacheProps = new Properties();
   51       
   52       /**
   53        * <p>First time requires initialization overhead.</p>
   54        */
   55       private static boolean firstTime = true;
   56       
   57       /**
   58        * Default columns per line.
   59        */
   60       private static final int DEFAULT_LINE_LENGTH = 80;
   61   
   62       // Define system property "jaxp.debug" to get output
   63       static {
   64           // Use try/catch block to support applets, which throws
   65           // SecurityException out of this code.
   66           try {
   67               String val = SecuritySupport.getSystemProperty("jaxp.debug");
   68               // Allow simply setting the prop to turn on debug
   69               debug = val != null && (! "false".equals(val));
   70           } catch (SecurityException se) {
   71               debug = false;
   72           }
   73       }
   74       
   75       private FactoryFinder() {}
   76   
   77       private static void dPrint(String msg) {
   78           if (debug) {
   79               System.err.println("JAXP: " + msg);
   80           }
   81       }
   82       
   83       /**
   84        * Create an instance of a class using the specified ClassLoader and
   85        * optionally fall back to the current ClassLoader if not found.
   86        *
   87        * @param className Name of the concrete class corresponding to the
   88        * service provider
   89        *
   90        * @param cl ClassLoader to use to load the class, null means to use
   91        * the bootstrap ClassLoader
   92        *
   93        * @param doFallback true if the current ClassLoader should be tried as
   94        * a fallback if the class is not found using cl
   95        */
   96       static Object newInstance(String className, ClassLoader cl,
   97                                         boolean doFallback)
   98           throws ConfigurationError
   99       {
  100           // assert(className != null);
  101   
  102           try {
  103               Class providerClass;
  104               if (cl == null) {
  105                   // If classloader is null Use the bootstrap ClassLoader.  
  106                   // Thus Class.forName(String) will use the current
  107                   // ClassLoader which will be the bootstrap ClassLoader.
  108                   providerClass = Class.forName(className);
  109               } else {
  110                   try {
  111                       providerClass = cl.loadClass(className);
  112                   } catch (ClassNotFoundException x) {
  113                       if (doFallback) {
  114                           // Fall back to current classloader
  115                           cl = FactoryFinder.class.getClassLoader();
  116                           if (cl != null) {
  117                               providerClass = cl.loadClass(className);
  118                           }
  119                           else {
  120                               providerClass = Class.forName(className);
  121                           }
  122                       } else {
  123                           throw x;
  124                       }
  125                   }
  126               }
  127                           
  128               Object instance = providerClass.newInstance();
  129               if (debug) dPrint("created new instance of " + providerClass +
  130                      " using ClassLoader: " + cl);
  131               return instance;
  132           } catch (ClassNotFoundException x) {
  133               throw new ConfigurationError(
  134                   "Provider " + className + " not found", x);
  135           } catch (Exception x) {
  136               throw new ConfigurationError(
  137                   "Provider " + className + " could not be instantiated: " + x,
  138                   x);
  139           }
  140       }
  141       
  142       /**
  143        * Finds the implementation Class object in the specified order.  Main
  144        * entry point.
  145        * @return Class object of factory, never null
  146        *
  147        * @param factoryId             Name of the factory to find, same as
  148        *                              a property name
  149        * @param fallbackClassName     Implementation class name, if nothing else
  150        *                              is found.  Use null to mean no fallback.
  151        *
  152        * Package private so this code can be shared.
  153        */
  154       static Object find(String factoryId, String fallbackClassName)
  155           throws ConfigurationError
  156       {        
  157   
  158           // Figure out which ClassLoader to use for loading the provider
  159           // class.  If there is a Context ClassLoader then use it.
  160           
  161           ClassLoader classLoader = SecuritySupport.getContextClassLoader();
  162           
  163           if (classLoader == null) {
  164               // if we have no Context ClassLoader
  165               // so use the current ClassLoader
  166               classLoader = FactoryFinder.class.getClassLoader();
  167           }
  168   
  169           if (debug) dPrint("find factoryId =" + factoryId);
  170           
  171           // Use the system property first
  172           try {
  173               String systemProp = SecuritySupport.getSystemProperty(factoryId);
  174               if (systemProp != null && systemProp.length() > 0) {
  175                   if (debug) dPrint("found system property, value=" + systemProp);
  176                   return newInstance(systemProp, classLoader, true);
  177               }
  178           } catch (SecurityException se) {
  179               //if first option fails due to any reason we should try next option in the
  180               //look up algorithm.
  181           }
  182   
  183           // try to read from $java.home/lib/jaxp.properties
  184           try {
  185               String javah = SecuritySupport.getSystemProperty("java.home");
  186               String configFile = javah + File.separator +
  187                   "lib" + File.separator + "jaxp.properties";
  188               String factoryClassName = null;
  189               if(firstTime){
  190                   synchronized(cacheProps){
  191                       if(firstTime){
  192                           File f=new File( configFile );
  193                           firstTime = false;
  194                           if(SecuritySupport.doesFileExist(f)){
  195                               if (debug) dPrint("Read properties file "+f);
  196                               //cacheProps.load( new FileInputStream(f));
  197                               cacheProps.load(SecuritySupport.getFileInputStream(f));
  198                           }
  199                       }
  200                   }
  201               }
  202               factoryClassName = cacheProps.getProperty(factoryId);            
  203   
  204               if(factoryClassName != null){
  205                   if (debug) dPrint("found in $java.home/jaxp.properties, value=" + factoryClassName);
  206                   return newInstance(factoryClassName, classLoader, true);
  207               }
  208           } catch(Exception ex ) {
  209               if( debug ) ex.printStackTrace();
  210           }
  211   
  212           // Try Jar Service Provider Mechanism
  213           Object provider = findJarServiceProvider(factoryId);
  214           if (provider != null) {
  215               return provider;
  216           }
  217           if (fallbackClassName == null) {
  218               throw new ConfigurationError(
  219                   "Provider for " + factoryId + " cannot be found", null);
  220           }
  221   
  222           if (debug) dPrint("loaded from fallback value: " + fallbackClassName);
  223           return newInstance(fallbackClassName, classLoader, true);
  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)
  232           throws ConfigurationError
  233       {
  234   
  235           String serviceId = "META-INF/services/" + factoryId;
  236           InputStream is = null;
  237   
  238           // First try the Context ClassLoader
  239           ClassLoader cl = SecuritySupport.getContextClassLoader();
  240           if (cl != null) {
  241               is = SecuritySupport.getResourceAsStream(cl, serviceId);
  242   
  243               // If no provider found then try the current ClassLoader
  244               if (is == null) {
  245                   cl = FactoryFinder.class.getClassLoader();
  246                   is = SecuritySupport.getResourceAsStream(cl, serviceId);
  247               }
  248           } else {
  249               // No Context ClassLoader, try the current
  250               // ClassLoader
  251               cl = FactoryFinder.class.getClassLoader();
  252               is = SecuritySupport.getResourceAsStream(cl, serviceId);
  253           }
  254   
  255           if (is == null) {
  256               // No provider found
  257               return null;
  258           }
  259   
  260           if (debug) dPrint("found jar resource=" + serviceId +
  261                  " using ClassLoader: " + cl);
  262   
  263           // Read the service provider name in UTF-8 as specified in
  264           // the jar spec.  Unfortunately this fails in Microsoft
  265           // VJ++, which does not implement the UTF-8
  266           // encoding. Theoretically, we should simply let it fail in
  267           // that case, since the JVM is obviously broken if it
  268           // doesn't support such a basic standard.  But since there
  269           // are still some users attempting to use VJ++ for
  270           // development, we have dropped in a fallback which makes a
  271           // second attempt using the platform's default encoding. In
  272           // VJ++ this is apparently ASCII, which is a subset of
  273           // UTF-8... and since the strings we'll be reading here are
  274           // also primarily limited to the 7-bit ASCII range (at
  275           // least, in English versions), this should work well
  276           // enough to keep us on the air until we're ready to
  277           // officially decommit from VJ++. [Edited comment from
  278           // jkesselm]
  279           BufferedReader rd;
  280           try {
  281               rd = new BufferedReader(new InputStreamReader(is, "UTF-8"), DEFAULT_LINE_LENGTH);
  282           } catch (java.io.UnsupportedEncodingException e) {
  283               rd = new BufferedReader(new InputStreamReader(is), DEFAULT_LINE_LENGTH);
  284           }
  285           
  286           String factoryClassName = null;
  287           try {
  288               // XXX Does not handle all possible input as specified by the
  289               // Jar Service Provider specification
  290               factoryClassName = rd.readLine();
  291           } 
  292           catch (IOException x) {
  293               // No provider found
  294               return null;
  295           }
  296           finally {
  297               try { 
  298                   // try to close the reader. 
  299                   rd.close(); 
  300               } 
  301               // Ignore the exception. 
  302               catch (IOException exc) {}
  303           }
  304   
  305           if (factoryClassName != null &&
  306               ! "".equals(factoryClassName)) {
  307               if (debug) dPrint("found in resource, value="
  308                      + factoryClassName);
  309   
  310           // Note: here we do not want to fall back to the current
  311           // ClassLoader because we want to avoid the case where the
  312           // resource file was found using one ClassLoader and the
  313           // provider class was instantiated using a different one.
  314           return newInstance(factoryClassName, cl, false);
  315           }
  316   
  317           // No provider found
  318           return null;
  319       }
  320   
  321       static class ConfigurationError extends Error {
  322           private Exception exception;
  323   
  324           /**
  325            * Construct a new instance with the specified detail string and
  326            * exception.
  327            */
  328           ConfigurationError(String msg, Exception x) {
  329               super(msg);
  330               this.exception = x;
  331           }
  332   
  333           Exception getException() {
  334               return exception;
  335           }
  336       }
  337   
  338   }

Home » xml-commons-external-1.4.01-src » javax » xml » parsers » [javadoc | source]