Home » xml-commons-external-1.4.01-src » javax » xml » datatype » [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 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.IOException;
   25   import java.io.InputStream;
   26   import java.io.InputStreamReader;
   27   import java.net.URL;
   28   import java.util.Properties;
   29   
   30   /**
   31    * <p>Implement pluggabile Datatypes.</p>
   32    * 
   33    * <p>This class is duplicated for each JAXP subpackage so keep it in
   34    * sync.  It is package private for secure class loading.</p>
   35    *
   36    * @author <a href="mailto:Jeff.Suttor@Sun.com">Jeff Suttor</a>
   37    * @version $Revision: 670432 $, $Date: 2008-06-22 22:02:08 -0400 (Sun, 22 Jun 2008) $
   38    * @since 1.5
   39    */
   40   final class FactoryFinder {
   41   	
   42   	/**
   43   	 * <p>Name of class to display in output messages.</p>
   44   	 */
   45   	private static final String CLASS_NAME = "javax.xml.datatype.FactoryFinder";
   46   	
   47       /**
   48        * <p>Debug flag to trace loading process.</p>
   49        */
   50       private static boolean debug = false;
   51       
   52       /**
   53        * <p>Cache properties for performance.</p>
   54        */
   55   	private static Properties cacheProps = new Properties();
   56   	
   57   	/**
   58   	 * <p>First time requires initialization overhead.</p>
   59   	 */
   60   	private static boolean firstTime = true;
   61       
   62       /**
   63        * Default columns per line.
   64        */
   65       private static final int DEFAULT_LINE_LENGTH = 80;
   66   	
   67   	/**
   68   	 * <p>Check to see if debugging enabled by property.</p>
   69   	 * 
   70   	 * <p>Use try/catch block to support applets, which throws
   71        * SecurityException out of this code.</p>
   72   	 * 
   73   	 */
   74       static {
   75           try {
   76               String val = SecuritySupport.getSystemProperty("jaxp.debug");
   77               // Allow simply setting the prop to turn on debug
   78               debug = val != null && (! "false".equals(val));
   79           } catch (Exception x) {
   80               debug = false;
   81           }
   82       }
   83       
   84       private FactoryFinder() {}
   85   
   86   	/**
   87   	 * <p>Output debugging messages.</p>
   88   	 * 
   89   	 * @param msg <code>String</code> to print to <code>stderr</code>.
   90   	 */
   91       private static void debugPrintln(String msg) {
   92           if (debug) {
   93               System.err.println(
   94               	CLASS_NAME
   95               	+ ":"
   96               	+ msg);
   97           }
   98       }
   99   
  100       /**
  101        * <p>Find the appropriate <code>ClassLoader</code> to use.</p>
  102        * 
  103        * <p>The context ClassLoader is prefered.</p>
  104        * 
  105        * @return <code>ClassLoader</code> to use.
  106        * 
  107        * @throws ConfigurationError If a valid <code>ClassLoader</code> cannot be identified. 
  108        */
  109       private static ClassLoader findClassLoader()
  110           throws ConfigurationError {
  111           ClassLoader classLoader;
  112   
  113           // Figure out which ClassLoader to use for loading the provider
  114           // class.  If there is a Context ClassLoader then use it.
  115   
  116           classLoader = SecuritySupport.getContextClassLoader();            
  117   
  118           if (debug) debugPrintln(
  119               "Using context class loader: "
  120               + classLoader);
  121   
  122           if (classLoader == null) {
  123               // if we have no Context ClassLoader
  124               // so use the current ClassLoader
  125               classLoader = FactoryFinder.class.getClassLoader();
  126               if (debug) debugPrintln(
  127                   "Using the class loader of FactoryFinder: "
  128                   + classLoader);                
  129           }
  130                       
  131           return classLoader;
  132       }
  133   
  134       /**
  135        * <p>Create an instance of a class using the specified ClassLoader.</p>
  136        * 
  137        * @param className Name of class to create.
  138        * @param classLoader ClassLoader to use to create named class.
  139        * 
  140        * @return New instance of specified class created using the specified ClassLoader.
  141        * 
  142        * @throws ConfigurationError If class could not be created.
  143        */
  144       static Object newInstance(
  145       	String className,
  146           ClassLoader classLoader)
  147           throws ConfigurationError {
  148           	
  149           try {
  150               Class spiClass;
  151               if (classLoader == null) {
  152                   spiClass = Class.forName(className);
  153               } else {
  154                   spiClass = classLoader.loadClass(className);
  155               }
  156               
  157               if (debug) {
  158               	debugPrintln("Loaded " + className + " from " + which(spiClass));
  159               }
  160                
  161               return spiClass.newInstance();
  162           } catch (ClassNotFoundException x) {
  163               throw new ConfigurationError(
  164                   "Provider " + className + " not found", x);
  165           } catch (Exception x) {
  166               throw new ConfigurationError(
  167                   "Provider " + className + " could not be instantiated: " + x,
  168                   x);
  169           }
  170       }
  171   
  172       /**
  173        * Finds the implementation Class object in the specified order.  Main
  174        * entry point.
  175        * Package private so this code can be shared.
  176        *
  177        * @param factoryId Name of the factory to find, same as a property name
  178        * @param fallbackClassName Implementation class name, if nothing else is found.  Use null to mean no fallback.
  179        *
  180        * @return Class Object of factory, never null
  181        * 
  182        * @throws ConfigurationError If Class cannot be found.
  183        */
  184       static Object find(String factoryId, String fallbackClassName)
  185           throws ConfigurationError {
  186           	
  187           ClassLoader classLoader = findClassLoader();
  188   
  189           // Use the system property first
  190           try {
  191               String systemProp = SecuritySupport.getSystemProperty(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           } catch (SecurityException se) {
  197           	; // NOP, explicitly ignore SecurityException
  198           }
  199   
  200           // try to read from $java.home/lib/jaxp.properties
  201           try {
  202               String javah = SecuritySupport.getSystemProperty("java.home");
  203               String configFile = javah + File.separator + "lib" + File.separator + "jaxp.properties";
  204   			String factoryClassName = null;
  205   			if (firstTime) {
  206   				synchronized (cacheProps) {
  207   					if (firstTime) {
  208   						File f = new File(configFile);
  209   						firstTime = false;
  210   						if (SecuritySupport.doesFileExist(f)) {
  211   							if (debug) debugPrintln("Read properties file " + f);
  212   							cacheProps.load(SecuritySupport.getFileInputStream(f));
  213   						}
  214   					}
  215   				}
  216   			}
  217   			factoryClassName = cacheProps.getProperty(factoryId);
  218               if (debug) debugPrintln("found " + factoryClassName + " in $java.home/jaxp.properties"); 
  219   			
  220   			if (factoryClassName != null) {
  221   				return newInstance(factoryClassName, classLoader);
  222   			}
  223           } catch (Exception ex) {
  224               if (debug) {
  225               	ex.printStackTrace();
  226               } 
  227           }
  228           
  229           // Try Jar Service Provider Mechanism
  230           Object provider = findJarServiceProvider(factoryId);
  231           if (provider != null) {
  232               return provider;
  233           }
  234   
  235           if (fallbackClassName == null) {
  236               throw new ConfigurationError(
  237                   "Provider for " + factoryId + " cannot be found", null);
  238           }
  239   
  240           if (debug) debugPrintln("loaded from fallback value: " + fallbackClassName);
  241           return newInstance(fallbackClassName, classLoader);
  242       }
  243   
  244       /*
  245        * Try to find provider using Jar Service Provider Mechanism
  246        *
  247        * @return instance of provider class if found or null
  248        */
  249       private static Object findJarServiceProvider(String factoryId)
  250           throws ConfigurationError
  251       {
  252   
  253           String serviceId = "META-INF/services/" + factoryId;
  254           InputStream is = null;
  255   
  256           // First try the Context ClassLoader
  257           ClassLoader cl = SecuritySupport.getContextClassLoader();
  258           if (cl != null) {
  259               is = SecuritySupport.getResourceAsStream(cl, serviceId);
  260   
  261               // If no provider found then try the current ClassLoader
  262               if (is == null) {
  263                   cl = FactoryFinder.class.getClassLoader();
  264                   is = SecuritySupport.getResourceAsStream(cl, serviceId);
  265               }
  266           } else {
  267               // No Context ClassLoader, try the current
  268               // ClassLoader
  269               cl = FactoryFinder.class.getClassLoader();
  270               is = SecuritySupport.getResourceAsStream(cl, serviceId);
  271           }
  272   
  273           if (is == null) {
  274               // No provider found
  275               return null;
  276           }
  277   
  278           if (debug) debugPrintln("found jar resource=" + serviceId +
  279                  " using ClassLoader: " + cl);
  280   
  281           BufferedReader rd;
  282           try {
  283               rd = new BufferedReader(new InputStreamReader(is, "UTF-8"), DEFAULT_LINE_LENGTH);
  284           } catch (java.io.UnsupportedEncodingException e) {
  285               rd = new BufferedReader(new InputStreamReader(is), DEFAULT_LINE_LENGTH);
  286           }
  287           
  288           String factoryClassName = null;
  289           try {
  290               // XXX Does not handle all possible input as specified by the
  291               // Jar Service Provider specification
  292               factoryClassName = rd.readLine();
  293           } 
  294           catch (IOException x) {
  295               // No provider found
  296               return null;
  297           }
  298           finally {
  299               try { 
  300                   // try to close the reader. 
  301                   rd.close(); 
  302               } 
  303               // Ignore the exception. 
  304               catch (IOException exc) {}
  305           }
  306   
  307           if (factoryClassName != null &&
  308               ! "".equals(factoryClassName)) {
  309               if (debug) debugPrintln("found in resource, value="
  310                      + factoryClassName);
  311   
  312               return newInstance(factoryClassName, cl);
  313           }
  314   
  315           // No provider found
  316           return null;
  317       }
  318       
  319   	/**
  320   	 * <p>Configuration Error.</p>
  321   	 */
  322       static class ConfigurationError extends Error {
  323           
  324           private static final long serialVersionUID = -3644413026244211347L;
  325       	
  326       	/**
  327       	 * <p>Exception that caused the error.</p>
  328       	 */
  329           private Exception exception;
  330   
  331           /**
  332            * <p>Construct a new instance with the specified detail string and
  333            * exception.</p>
  334            * 
  335            * @param msg Detail message for this error.
  336            * @param x Exception that caused the error.
  337            */
  338           ConfigurationError(String msg, Exception x) {
  339               super(msg);
  340               this.exception = x;
  341           }
  342   
  343   		/**
  344   		 * <p>Get the Exception that caused the error.</p>
  345   		 * 
  346   		 * @return Exception that caused the error.
  347   		 */
  348           Exception getException() {
  349               return exception;
  350           }
  351       }
  352   
  353   
  354   
  355       /**
  356        * Returns the location where the given Class is loaded from.
  357        * 
  358        * @param clazz Class to find load location.
  359        * 
  360        * @return Location where class would be loaded from.
  361        */
  362       private static String which(Class clazz) {
  363           try {
  364               String classnameAsResource = clazz.getName().replace('.', '/') + ".class";
  365       
  366               ClassLoader loader = clazz.getClassLoader();
  367               
  368               URL it;
  369       
  370               if (loader != null) {
  371               	it = loader.getResource(classnameAsResource);
  372               } else {
  373               	it = ClassLoader.getSystemResource(classnameAsResource);
  374               } 
  375       
  376               if (it != null) {
  377               	return it.toString();
  378               } 
  379           }
  380           // The VM ran out of memory or there was some other serious problem. Re-throw.
  381           catch (VirtualMachineError vme) {
  382               throw vme;
  383           }
  384           // ThreadDeath should always be re-thrown
  385           catch (ThreadDeath td) {
  386               throw td;
  387           }
  388           catch (Throwable t) {
  389               // work defensively.
  390               if (debug) {
  391               	t.printStackTrace();
  392               } 
  393           }
  394           return "unknown location";
  395       }
  396   }

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