jaxp/src/com/sun/org/apache/xerces/internal/xinclude/ObjectFactory.java
changeset 18311 312f05b0462d
parent 18310 6b514cf7a2c3
parent 17815 b72ae39e1329
child 18312 c940914e1849
equal deleted inserted replaced
18310:6b514cf7a2c3 18311:312f05b0462d
     1 /*
       
     2  * reserved comment block
       
     3  * DO NOT REMOVE OR ALTER!
       
     4  */
       
     5 /*
       
     6  * Copyright 2001-2005 The Apache Software Foundation.
       
     7  *
       
     8  * Licensed under the Apache License, Version 2.0 (the "License");
       
     9  * you may not use this file except in compliance with the License.
       
    10  * 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 
       
    21 package com.sun.org.apache.xerces.internal.xinclude;
       
    22 
       
    23 import java.io.InputStream;
       
    24 import java.io.IOException;
       
    25 import java.io.File;
       
    26 import java.io.FileInputStream;
       
    27 
       
    28 import java.util.Properties;
       
    29 import java.io.BufferedReader;
       
    30 import java.io.InputStreamReader;
       
    31 
       
    32 /**
       
    33  * This class is duplicated for each JAXP subpackage so keep it in sync.
       
    34  * It is package private and therefore is not exposed as part of the JAXP
       
    35  * API.
       
    36  * <p>
       
    37  * This code is designed to implement the JAXP 1.1 spec pluggability
       
    38  * feature and is designed to run on JDK version 1.1 and
       
    39  * later, and to compile on JDK 1.2 and onward.
       
    40  * The code also runs both as part of an unbundled jar file and
       
    41  * when bundled as part of the JDK.
       
    42  * <p>
       
    43  *
       
    44  */
       
    45 final class ObjectFactory {
       
    46 
       
    47     //
       
    48     // Constants
       
    49     //
       
    50 
       
    51     // name of default properties file to look for in JDK's jre/lib directory
       
    52     private static final String DEFAULT_PROPERTIES_FILENAME = "xerces.properties";
       
    53 
       
    54     /** Set to true for debugging */
       
    55     private static final boolean DEBUG = false;
       
    56 
       
    57     /**
       
    58      * Default columns per line.
       
    59      */
       
    60     private static final int DEFAULT_LINE_LENGTH = 80;
       
    61 
       
    62     /** cache the contents of the xerces.properties file.
       
    63      *  Until an attempt has been made to read this file, this will
       
    64      * be null; if the file does not exist or we encounter some other error
       
    65      * during the read, this will be empty.
       
    66      */
       
    67     private static Properties fXercesProperties = null;
       
    68 
       
    69     /***
       
    70      * Cache the time stamp of the xerces.properties file so
       
    71      * that we know if it's been modified and can invalidate
       
    72      * the cache when necessary.
       
    73      */
       
    74     private static long fLastModified = -1;
       
    75 
       
    76     //
       
    77     // static methods
       
    78     //
       
    79 
       
    80     /**
       
    81      * Finds the implementation Class object in the specified order.  The
       
    82      * specified order is the following:
       
    83      * <ol>
       
    84      *  <li>query the system property using <code>System.getProperty</code>
       
    85      *  <li>read <code>META-INF/services/<i>factoryId</i></code> file
       
    86      *  <li>use fallback classname
       
    87      * </ol>
       
    88      *
       
    89      * @return Class object of factory, never null
       
    90      *
       
    91      * @param factoryId             Name of the factory to find, same as
       
    92      *                              a property name
       
    93      * @param fallbackClassName     Implementation class name, if nothing else
       
    94      *                              is found.  Use null to mean no fallback.
       
    95      *
       
    96      * @exception ObjectFactory.ConfigurationError
       
    97      */
       
    98     static Object createObject(String factoryId, String fallbackClassName)
       
    99         throws ConfigurationError {
       
   100         return createObject(factoryId, null, fallbackClassName);
       
   101     } // createObject(String,String):Object
       
   102 
       
   103     /**
       
   104      * Finds the implementation Class object in the specified order.  The
       
   105      * specified order is the following:
       
   106      * <ol>
       
   107      *  <li>query the system property using <code>System.getProperty</code>
       
   108      *  <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
       
   109      *  <li>read <code>META-INF/services/<i>factoryId</i></code> file
       
   110      *  <li>use fallback classname
       
   111      * </ol>
       
   112      *
       
   113      * @return Class object of factory, never null
       
   114      *
       
   115      * @param factoryId             Name of the factory to find, same as
       
   116      *                              a property name
       
   117      * @param propertiesFilename The filename in the $java.home/lib directory
       
   118      *                           of the properties file.  If none specified,
       
   119      *                           ${java.home}/lib/xerces.properties will be used.
       
   120      * @param fallbackClassName     Implementation class name, if nothing else
       
   121      *                              is found.  Use null to mean no fallback.
       
   122      *
       
   123      * @exception ObjectFactory.ConfigurationError
       
   124      */
       
   125     static Object createObject(String factoryId,
       
   126                                       String propertiesFilename,
       
   127                                       String fallbackClassName)
       
   128         throws ConfigurationError
       
   129     {
       
   130         if (DEBUG) debugPrintln("debug is on");
       
   131 
       
   132         SecuritySupport ss = SecuritySupport.getInstance();
       
   133         ClassLoader cl = findClassLoader();
       
   134 
       
   135         // Use the system property first
       
   136         try {
       
   137             String systemProp = ss.getSystemProperty(factoryId);
       
   138             if (systemProp != null) {
       
   139                 if (DEBUG) debugPrintln("found system property, value=" + systemProp);
       
   140                 return newInstance(systemProp, cl, true);
       
   141             }
       
   142         } catch (SecurityException se) {
       
   143             // Ignore and continue w/ next location
       
   144         }
       
   145 
       
   146         // JAXP specific change
       
   147         // always use fallback class to avoid the expense of constantly
       
   148         // "stat"ing a non-existent "xerces.properties" and jar SPI entry
       
   149         // see CR 6400863: Expensive creating of SAX parser in Mustang
       
   150         if (true) {
       
   151             if (fallbackClassName == null) {
       
   152                 throw new ConfigurationError(
       
   153                     "Provider for " + factoryId + " cannot be found", null);
       
   154             }
       
   155 
       
   156             if (DEBUG) debugPrintln("using fallback, value=" + fallbackClassName);
       
   157             return newInstance(fallbackClassName, cl, true);
       
   158         }
       
   159 
       
   160         // Try to read from propertiesFilename, or $java.home/lib/xerces.properties
       
   161         String factoryClassName = null;
       
   162         // no properties file name specified; use $JAVA_HOME/lib/xerces.properties:
       
   163         if (propertiesFilename == null) {
       
   164             File propertiesFile = null;
       
   165             boolean propertiesFileExists = false;
       
   166             try {
       
   167                 String javah = ss.getSystemProperty("java.home");
       
   168                 propertiesFilename = javah + File.separator +
       
   169                     "lib" + File.separator + DEFAULT_PROPERTIES_FILENAME;
       
   170                 propertiesFile = new File(propertiesFilename);
       
   171                 propertiesFileExists = ss.getFileExists(propertiesFile);
       
   172             } catch (SecurityException e) {
       
   173                 // try again...
       
   174                 fLastModified = -1;
       
   175                 fXercesProperties = null;
       
   176             }
       
   177 
       
   178             synchronized (ObjectFactory.class) {
       
   179                 boolean loadProperties = false;
       
   180                 FileInputStream fis = null;
       
   181                 try {
       
   182                     // file existed last time
       
   183                     if(fLastModified >= 0) {
       
   184                         if(propertiesFileExists &&
       
   185                                 (fLastModified < (fLastModified = ss.getLastModified(propertiesFile)))) {
       
   186                             loadProperties = true;
       
   187                         } else {
       
   188                             // file has stopped existing...
       
   189                             if(!propertiesFileExists) {
       
   190                                 fLastModified = -1;
       
   191                                 fXercesProperties = null;
       
   192                             } // else, file wasn't modified!
       
   193                         }
       
   194                     } else {
       
   195                         // file has started to exist:
       
   196                         if(propertiesFileExists) {
       
   197                             loadProperties = true;
       
   198                             fLastModified = ss.getLastModified(propertiesFile);
       
   199                         } // else, nothing's changed
       
   200                     }
       
   201                     if(loadProperties) {
       
   202                         // must never have attempted to read xerces.properties before (or it's outdeated)
       
   203                         fXercesProperties = new Properties();
       
   204                         fis = ss.getFileInputStream(propertiesFile);
       
   205                         fXercesProperties.load(fis);
       
   206                     }
       
   207                 } catch (Exception x) {
       
   208                     fXercesProperties = null;
       
   209                     fLastModified = -1;
       
   210                     // assert(x instanceof FileNotFoundException
       
   211                     //        || x instanceof SecurityException)
       
   212                     // In both cases, ignore and continue w/ next location
       
   213                 }
       
   214                 finally {
       
   215                     // try to close the input stream if one was opened.
       
   216                     if (fis != null) {
       
   217                         try {
       
   218                             fis.close();
       
   219                         }
       
   220                         // Ignore the exception.
       
   221                         catch (IOException exc) {}
       
   222                     }
       
   223                 }
       
   224             }
       
   225             if(fXercesProperties != null) {
       
   226                 factoryClassName = fXercesProperties.getProperty(factoryId);
       
   227             }
       
   228         } else {
       
   229             FileInputStream fis = null;
       
   230             try {
       
   231                 fis = ss.getFileInputStream(new File(propertiesFilename));
       
   232                 Properties props = new Properties();
       
   233                 props.load(fis);
       
   234                 factoryClassName = props.getProperty(factoryId);
       
   235             } catch (Exception x) {
       
   236                 // assert(x instanceof FileNotFoundException
       
   237                 //        || x instanceof SecurityException)
       
   238                 // In both cases, ignore and continue w/ next location
       
   239             }
       
   240             finally {
       
   241                 // try to close the input stream if one was opened.
       
   242                 if (fis != null) {
       
   243                     try {
       
   244                         fis.close();
       
   245                     }
       
   246                     // Ignore the exception.
       
   247                     catch (IOException exc) {}
       
   248                 }
       
   249             }
       
   250         }
       
   251         if (factoryClassName != null) {
       
   252             if (DEBUG) debugPrintln("found in " + propertiesFilename + ", value=" + factoryClassName);
       
   253             return newInstance(factoryClassName, cl, true);
       
   254         }
       
   255 
       
   256         // Try Jar Service Provider Mechanism
       
   257         Object provider = findJarServiceProvider(factoryId);
       
   258         if (provider != null) {
       
   259             return provider;
       
   260         }
       
   261 
       
   262         if (fallbackClassName == null) {
       
   263             throw new ConfigurationError(
       
   264                 "Provider for " + factoryId + " cannot be found", null);
       
   265         }
       
   266 
       
   267         if (DEBUG) debugPrintln("using fallback, value=" + fallbackClassName);
       
   268         return newInstance(fallbackClassName, cl, true);
       
   269     } // createObject(String,String,String):Object
       
   270 
       
   271     //
       
   272     // Private static methods
       
   273     //
       
   274 
       
   275     /** Prints a message to standard error if debugging is enabled. */
       
   276     private static void debugPrintln(String msg) {
       
   277         if (DEBUG) {
       
   278             System.err.println("JAXP: " + msg);
       
   279         }
       
   280     } // debugPrintln(String)
       
   281 
       
   282     /**
       
   283      * Figure out which ClassLoader to use.  For JDK 1.2 and later use
       
   284      * the context ClassLoader.
       
   285      */
       
   286     static ClassLoader findClassLoader()
       
   287         throws ConfigurationError
       
   288     {
       
   289         SecuritySupport ss = SecuritySupport.getInstance();
       
   290 
       
   291         // Figure out which ClassLoader to use for loading the provider
       
   292         // class.  If there is a Context ClassLoader then use it.
       
   293         ClassLoader context = ss.getContextClassLoader();
       
   294         ClassLoader system = ss.getSystemClassLoader();
       
   295 
       
   296         ClassLoader chain = system;
       
   297         while (true) {
       
   298             if (context == chain) {
       
   299                 // Assert: we are on JDK 1.1 or we have no Context ClassLoader
       
   300                 // or any Context ClassLoader in chain of system classloader
       
   301                 // (including extension ClassLoader) so extend to widest
       
   302                 // ClassLoader (always look in system ClassLoader if Xerces
       
   303                 // is in boot/extension/system classpath and in current
       
   304                 // ClassLoader otherwise); normal classloaders delegate
       
   305                 // back to system ClassLoader first so this widening doesn't
       
   306                 // change the fact that context ClassLoader will be consulted
       
   307                 ClassLoader current = ObjectFactory.class.getClassLoader();
       
   308 
       
   309                 chain = system;
       
   310                 while (true) {
       
   311                     if (current == chain) {
       
   312                         // Assert: Current ClassLoader in chain of
       
   313                         // boot/extension/system ClassLoaders
       
   314                         return system;
       
   315                     }
       
   316                     if (chain == null) {
       
   317                         break;
       
   318                     }
       
   319                     chain = ss.getParentClassLoader(chain);
       
   320                 }
       
   321 
       
   322                 // Assert: Current ClassLoader not in chain of
       
   323                 // boot/extension/system ClassLoaders
       
   324                 return current;
       
   325             }
       
   326 
       
   327             if (chain == null) {
       
   328                 // boot ClassLoader reached
       
   329                 break;
       
   330             }
       
   331 
       
   332             // Check for any extension ClassLoaders in chain up to
       
   333             // boot ClassLoader
       
   334             chain = ss.getParentClassLoader(chain);
       
   335         };
       
   336 
       
   337         // Assert: Context ClassLoader not in chain of
       
   338         // boot/extension/system ClassLoaders
       
   339         return context;
       
   340     } // findClassLoader():ClassLoader
       
   341 
       
   342     /**
       
   343      * Create an instance of a class using the specified ClassLoader
       
   344      */
       
   345     static Object newInstance(String className, ClassLoader cl,
       
   346                                       boolean doFallback)
       
   347         throws ConfigurationError
       
   348     {
       
   349         // assert(className != null);
       
   350         try{
       
   351             Class providerClass = findProviderClass(className, cl, doFallback);
       
   352             Object instance = providerClass.newInstance();
       
   353             if (DEBUG) debugPrintln("created new instance of " + providerClass +
       
   354                    " using ClassLoader: " + cl);
       
   355             return instance;
       
   356         } catch (ClassNotFoundException x) {
       
   357             throw new ConfigurationError(
       
   358                 "Provider " + className + " not found", x);
       
   359         } catch (Exception x) {
       
   360             throw new ConfigurationError(
       
   361                 "Provider " + className + " could not be instantiated: " + x,
       
   362                 x);
       
   363         }
       
   364     }
       
   365 
       
   366     /**
       
   367      * Find a Class using the specified ClassLoader
       
   368      */
       
   369     static Class findProviderClass(String className, ClassLoader cl,
       
   370                                       boolean doFallback)
       
   371         throws ClassNotFoundException, ConfigurationError
       
   372     {
       
   373         //throw security exception if the calling thread is not allowed to access the package
       
   374         //restrict the access to package as speicified in java.security policy
       
   375         SecurityManager security = System.getSecurityManager();
       
   376         if (security != null) {
       
   377             final int lastDot = className.lastIndexOf(".");
       
   378             String packageName = className;
       
   379             if (lastDot != -1) packageName = className.substring(0, lastDot);
       
   380             security.checkPackageAccess(packageName);
       
   381         }
       
   382         Class providerClass;
       
   383         if (cl == null) {
       
   384             // XXX Use the bootstrap ClassLoader.  There is no way to
       
   385             // load a class using the bootstrap ClassLoader that works
       
   386             // in both JDK 1.1 and Java 2.  However, this should still
       
   387             // work b/c the following should be true:
       
   388             //
       
   389             // (cl == null) iff current ClassLoader == null
       
   390             //
       
   391             // Thus Class.forName(String) will use the current
       
   392             // ClassLoader which will be the bootstrap ClassLoader.
       
   393             providerClass = Class.forName(className);
       
   394         } else {
       
   395             try {
       
   396                 providerClass = cl.loadClass(className);
       
   397             } catch (ClassNotFoundException x) {
       
   398                 if (doFallback) {
       
   399                     // Fall back to current classloader
       
   400                     ClassLoader current = ObjectFactory.class.getClassLoader();
       
   401                     if (current == null) {
       
   402                         providerClass = Class.forName(className);
       
   403                     } else if (cl != current) {
       
   404                         cl = current;
       
   405                         providerClass = cl.loadClass(className);
       
   406                     } else {
       
   407                         throw x;
       
   408                     }
       
   409                 } else {
       
   410                     throw x;
       
   411                 }
       
   412             }
       
   413         }
       
   414 
       
   415         return providerClass;
       
   416     }
       
   417 
       
   418     /*
       
   419      * Try to find provider using Jar Service Provider Mechanism
       
   420      *
       
   421      * @return instance of provider class if found or null
       
   422      */
       
   423     private static Object findJarServiceProvider(String factoryId)
       
   424         throws ConfigurationError
       
   425     {
       
   426         SecuritySupport ss = SecuritySupport.getInstance();
       
   427         String serviceId = "META-INF/services/" + factoryId;
       
   428         InputStream is = null;
       
   429 
       
   430         // First try the Context ClassLoader
       
   431         ClassLoader cl = findClassLoader();
       
   432 
       
   433         is = ss.getResourceAsStream(cl, serviceId);
       
   434 
       
   435         // If no provider found then try the current ClassLoader
       
   436         if (is == null) {
       
   437             ClassLoader current = ObjectFactory.class.getClassLoader();
       
   438             if (cl != current) {
       
   439                 cl = current;
       
   440                 is = ss.getResourceAsStream(cl, serviceId);
       
   441             }
       
   442         }
       
   443 
       
   444         if (is == null) {
       
   445             // No provider found
       
   446             return null;
       
   447         }
       
   448 
       
   449         if (DEBUG) debugPrintln("found jar resource=" + serviceId +
       
   450                " using ClassLoader: " + cl);
       
   451 
       
   452         // Read the service provider name in UTF-8 as specified in
       
   453         // the jar spec.  Unfortunately this fails in Microsoft
       
   454         // VJ++, which does not implement the UTF-8
       
   455         // encoding. Theoretically, we should simply let it fail in
       
   456         // that case, since the JVM is obviously broken if it
       
   457         // doesn't support such a basic standard.  But since there
       
   458         // are still some users attempting to use VJ++ for
       
   459         // development, we have dropped in a fallback which makes a
       
   460         // second attempt using the platform's default encoding. In
       
   461         // VJ++ this is apparently ASCII, which is a subset of
       
   462         // UTF-8... and since the strings we'll be reading here are
       
   463         // also primarily limited to the 7-bit ASCII range (at
       
   464         // least, in English versions), this should work well
       
   465         // enough to keep us on the air until we're ready to
       
   466         // officially decommit from VJ++. [Edited comment from
       
   467         // jkesselm]
       
   468         BufferedReader rd;
       
   469         try {
       
   470             rd = new BufferedReader(new InputStreamReader(is, "UTF-8"), DEFAULT_LINE_LENGTH);
       
   471         } catch (java.io.UnsupportedEncodingException e) {
       
   472             rd = new BufferedReader(new InputStreamReader(is), DEFAULT_LINE_LENGTH);
       
   473         }
       
   474 
       
   475         String factoryClassName = null;
       
   476         try {
       
   477             // XXX Does not handle all possible input as specified by the
       
   478             // Jar Service Provider specification
       
   479             factoryClassName = rd.readLine();
       
   480         } catch (IOException x) {
       
   481             // No provider found
       
   482             return null;
       
   483         }
       
   484         finally {
       
   485             try {
       
   486                 // try to close the reader.
       
   487                 rd.close();
       
   488             }
       
   489             // Ignore the exception.
       
   490             catch (IOException exc) {}
       
   491         }
       
   492 
       
   493         if (factoryClassName != null &&
       
   494             ! "".equals(factoryClassName)) {
       
   495             if (DEBUG) debugPrintln("found in resource, value="
       
   496                    + factoryClassName);
       
   497 
       
   498             // Note: here we do not want to fall back to the current
       
   499             // ClassLoader because we want to avoid the case where the
       
   500             // resource file was found using one ClassLoader and the
       
   501             // provider class was instantiated using a different one.
       
   502             return newInstance(factoryClassName, cl, false);
       
   503         }
       
   504 
       
   505         // No provider found
       
   506         return null;
       
   507     }
       
   508 
       
   509     //
       
   510     // Classes
       
   511     //
       
   512 
       
   513     /**
       
   514      * A configuration error.
       
   515      */
       
   516     static final class ConfigurationError
       
   517         extends Error {
       
   518 
       
   519         /** Serialization version. */
       
   520         static final long serialVersionUID = 5061904944269807898L;
       
   521 
       
   522         //
       
   523         // Data
       
   524         //
       
   525 
       
   526         /** Exception. */
       
   527         private Exception exception;
       
   528 
       
   529         //
       
   530         // Constructors
       
   531         //
       
   532 
       
   533         /**
       
   534          * Construct a new instance with the specified detail string and
       
   535          * exception.
       
   536          */
       
   537         ConfigurationError(String msg, Exception x) {
       
   538             super(msg);
       
   539             this.exception = x;
       
   540         } // <init>(String,Exception)
       
   541 
       
   542         //
       
   543         // methods
       
   544         //
       
   545 
       
   546         /** Returns the exception associated to this error. */
       
   547         Exception getException() {
       
   548             return exception;
       
   549         } // getException():Exception
       
   550 
       
   551     } // class ConfigurationError
       
   552 
       
   553 } // class ObjectFactory