OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / libcore / luni / src / main / java / org / apache / xalan / xslt / ObjectFactory.java
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the  "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 /*
19  * $Id: ObjectFactory.java 468646 2006-10-28 06:57:58Z minchau $
20  */
21
22 package org.apache.xalan.xslt;
23
24 import java.io.InputStream;
25 import java.io.IOException;
26 import java.io.File;
27 import java.io.FileInputStream;
28
29 import java.util.Properties;
30 import java.io.BufferedReader;
31 import java.io.InputStreamReader;
32
33 /**
34  * This class is duplicated for each JAXP subpackage so keep it in sync.
35  * It is package private and therefore is not exposed as part of the JAXP
36  * API.
37  * <p>
38  * This code is designed to implement the JAXP 1.1 spec pluggability
39  * feature and is designed to run on JDK version 1.1 and
40  * later, and to compile on JDK 1.2 and onward.  
41  * The code also runs both as part of an unbundled jar file and
42  * when bundled as part of the JDK.
43  * <p>
44  * This class was moved from the <code>javax.xml.parsers.ObjectFactory</code>
45  * class and modified to be used as a general utility for creating objects 
46  * dynamically.
47  *
48  * @version $Id: ObjectFactory.java 468646 2006-10-28 06:57:58Z minchau $
49  */
50 class ObjectFactory {
51
52     //
53     // Constants
54     //
55
56     // name of default properties file to look for in JDK's jre/lib directory
57     private static final String DEFAULT_PROPERTIES_FILENAME =
58                                                      "xalan.properties";
59
60     private static final String SERVICES_PATH = "META-INF/services/";
61
62     /** Set to true for debugging */
63     private static final boolean DEBUG = false;
64
65     /** cache the contents of the xalan.properties file.
66      *  Until an attempt has been made to read this file, this will
67      * be null; if the file does not exist or we encounter some other error
68      * during the read, this will be empty.
69      */
70     private static Properties fXalanProperties = null;
71
72     /***
73      * Cache the time stamp of the xalan.properties file so
74      * that we know if it's been modified and can invalidate
75      * the cache when necessary.
76      */
77     private static long fLastModified = -1;
78
79     //
80     // Public static methods
81     //
82
83     /**
84      * Finds the implementation Class object in the specified order.  The
85      * specified order is the following:
86      * <ol>
87      *  <li>query the system property using <code>System.getProperty</code>
88      *  <li>read <code>META-INF/services/<i>factoryId</i></code> file
89      *  <li>use fallback classname
90      * </ol>
91      *
92      * @return instance of factory, never null
93      *
94      * @param factoryId             Name of the factory to find, same as
95      *                              a property name
96      * @param fallbackClassName     Implementation class name, if nothing else
97      *                              is found.  Use null to mean no fallback.
98      *
99      * @exception ObjectFactory.ConfigurationError
100      */
101     static Object createObject(String factoryId, String fallbackClassName)
102         throws ConfigurationError {
103         return createObject(factoryId, null, fallbackClassName);
104     } // createObject(String,String):Object
105
106     /**
107      * Finds the implementation Class object in the specified order.  The
108      * specified order is the following:
109      * <ol>
110      *  <li>query the system property using <code>System.getProperty</code>
111      *  <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
112      *  <li>read <code>META-INF/services/<i>factoryId</i></code> file
113      *  <li>use fallback classname
114      * </ol>
115      *
116      * @return instance of factory, never null
117      *
118      * @param factoryId             Name of the factory to find, same as
119      *                              a property name
120      * @param propertiesFilename The filename in the $java.home/lib directory
121      *                           of the properties file.  If none specified,
122      *                           ${java.home}/lib/xalan.properties will be used.
123      * @param fallbackClassName     Implementation class name, if nothing else
124      *                              is found.  Use null to mean no fallback.
125      *
126      * @exception ObjectFactory.ConfigurationError
127      */
128     static Object createObject(String factoryId, 
129                                       String propertiesFilename,
130                                       String fallbackClassName)
131         throws ConfigurationError
132     {
133         Class factoryClass = lookUpFactoryClass(factoryId,
134                                                 propertiesFilename,
135                                                 fallbackClassName);
136
137         if (factoryClass == null) {
138             throw new ConfigurationError(
139                 "Provider for " + factoryId + " cannot be found", null);
140         }
141
142         try{
143             Object instance = factoryClass.newInstance();
144             debugPrintln("created new instance of factory " + factoryId);
145             return instance;
146         } catch (Exception x) {
147             throw new ConfigurationError(
148                 "Provider for factory " + factoryId
149                     + " could not be instantiated: " + x, x);
150         }
151     } // createObject(String,String,String):Object
152
153     /**
154      * Finds the implementation Class object in the specified order.  The
155      * specified order is the following:
156      * <ol>
157      *  <li>query the system property using <code>System.getProperty</code>
158      *  <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
159      *  <li>read <code>META-INF/services/<i>factoryId</i></code> file
160      *  <li>use fallback classname
161      * </ol>
162      *
163      * @return Class object of factory, never null
164      *
165      * @param factoryId             Name of the factory to find, same as
166      *                              a property name
167      * @param propertiesFilename The filename in the $java.home/lib directory
168      *                           of the properties file.  If none specified,
169      *                           ${java.home}/lib/xalan.properties will be used.
170      * @param fallbackClassName     Implementation class name, if nothing else
171      *                              is found.  Use null to mean no fallback.
172      *
173      * @exception ObjectFactory.ConfigurationError
174      */
175     static Class lookUpFactoryClass(String factoryId) 
176         throws ConfigurationError
177     {
178         return lookUpFactoryClass(factoryId, null, null);
179     } // lookUpFactoryClass(String):Class
180
181     /**
182      * Finds the implementation Class object in the specified order.  The
183      * specified order is the following:
184      * <ol>
185      *  <li>query the system property using <code>System.getProperty</code>
186      *  <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
187      *  <li>read <code>META-INF/services/<i>factoryId</i></code> file
188      *  <li>use fallback classname
189      * </ol>
190      *
191      * @return Class object that provides factory service, never null
192      *
193      * @param factoryId             Name of the factory to find, same as
194      *                              a property name
195      * @param propertiesFilename The filename in the $java.home/lib directory
196      *                           of the properties file.  If none specified,
197      *                           ${java.home}/lib/xalan.properties will be used.
198      * @param fallbackClassName     Implementation class name, if nothing else
199      *                              is found.  Use null to mean no fallback.
200      *
201      * @exception ObjectFactory.ConfigurationError
202      */
203     static Class lookUpFactoryClass(String factoryId,
204                                            String propertiesFilename,
205                                            String fallbackClassName)
206         throws ConfigurationError
207     {
208         String factoryClassName = lookUpFactoryClassName(factoryId,
209                                                          propertiesFilename,
210                                                          fallbackClassName);
211         ClassLoader cl = findClassLoader();
212
213         if (factoryClassName == null) {
214             factoryClassName = fallbackClassName;
215         }
216
217         // assert(className != null);
218         try{
219             Class providerClass = findProviderClass(factoryClassName,
220                                                     cl,
221                                                     true);
222             debugPrintln("created new instance of " + providerClass +
223                    " using ClassLoader: " + cl);
224             return providerClass;
225         } catch (ClassNotFoundException x) {
226             throw new ConfigurationError(
227                 "Provider " + factoryClassName + " not found", x);
228         } catch (Exception x) {
229             throw new ConfigurationError(
230                 "Provider "+factoryClassName+" could not be instantiated: "+x,
231                 x);
232         }
233     } // lookUpFactoryClass(String,String,String):Class
234
235     /**
236      * Finds the name of the required implementation class in the specified
237      * order.  The specified order is the following:
238      * <ol>
239      *  <li>query the system property using <code>System.getProperty</code>
240      *  <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
241      *  <li>read <code>META-INF/services/<i>factoryId</i></code> file
242      *  <li>use fallback classname
243      * </ol>
244      *
245      * @return name of class that provides factory service, never null
246      *
247      * @param factoryId             Name of the factory to find, same as
248      *                              a property name
249      * @param propertiesFilename The filename in the $java.home/lib directory
250      *                           of the properties file.  If none specified,
251      *                           ${java.home}/lib/xalan.properties will be used.
252      * @param fallbackClassName     Implementation class name, if nothing else
253      *                              is found.  Use null to mean no fallback.
254      *
255      * @exception ObjectFactory.ConfigurationError
256      */
257     static String lookUpFactoryClassName(String factoryId,
258                                                 String propertiesFilename,
259                                                 String fallbackClassName)
260     {
261         SecuritySupport ss = SecuritySupport.getInstance();
262
263         // Use the system property first
264         try {
265             String systemProp = ss.getSystemProperty(factoryId);
266             if (systemProp != null) {
267                 debugPrintln("found system property, value=" + systemProp);
268                 return systemProp;
269             }
270         } catch (SecurityException se) {
271             // Ignore and continue w/ next location
272         }
273
274         // Try to read from propertiesFilename, or
275         // $java.home/lib/xalan.properties
276         String factoryClassName = null;
277         // no properties file name specified; use
278         // $JAVA_HOME/lib/xalan.properties:
279         if (propertiesFilename == null) {
280             File propertiesFile = null;
281             boolean propertiesFileExists = false;
282             try {
283                 String javah = ss.getSystemProperty("java.home");
284                 propertiesFilename = javah + File.separator +
285                     "lib" + File.separator + DEFAULT_PROPERTIES_FILENAME;
286                 propertiesFile = new File(propertiesFilename);
287                 propertiesFileExists = ss.getFileExists(propertiesFile);
288             } catch (SecurityException e) {
289                 // try again...
290                 fLastModified = -1;
291                 fXalanProperties = null;
292             }
293
294             synchronized (ObjectFactory.class) {
295                 boolean loadProperties = false;
296                 FileInputStream fis = null;
297                 try {
298                     // file existed last time
299                     if(fLastModified >= 0) {
300                         if(propertiesFileExists &&
301                                 (fLastModified < (fLastModified = ss.getLastModified(propertiesFile)))) {
302                             loadProperties = true;
303                         } else {
304                             // file has stopped existing...
305                             if(!propertiesFileExists) {
306                                 fLastModified = -1;
307                                 fXalanProperties = null;
308                             } // else, file wasn't modified!
309                         }
310                     } else {
311                         // file has started to exist:
312                         if(propertiesFileExists) {
313                             loadProperties = true;
314                             fLastModified = ss.getLastModified(propertiesFile);
315                         } // else, nothing's changed
316                     }
317                     if(loadProperties) {
318                         // must never have attempted to read xalan.properties
319                         // before (or it's outdeated)
320                         fXalanProperties = new Properties();
321                         fis = ss.getFileInputStream(propertiesFile);
322                         fXalanProperties.load(fis);
323                     }
324                 } catch (Exception x) {
325                     fXalanProperties = null;
326                     fLastModified = -1;
327                     // assert(x instanceof FileNotFoundException
328                     //        || x instanceof SecurityException)
329                     // In both cases, ignore and continue w/ next location
330                 }
331                 finally {
332                     // try to close the input stream if one was opened.
333                     if (fis != null) {
334                         try {
335                             fis.close();
336                         }
337                         // Ignore the exception.
338                         catch (IOException exc) {}
339                     }
340                 }                   
341             }
342             if(fXalanProperties != null) {
343                 factoryClassName = fXalanProperties.getProperty(factoryId);
344             }
345         } else {
346             FileInputStream fis = null;
347             try {
348                 fis = ss.getFileInputStream(new File(propertiesFilename));
349                 Properties props = new Properties();
350                 props.load(fis);
351                 factoryClassName = props.getProperty(factoryId);
352             } catch (Exception x) {
353                 // assert(x instanceof FileNotFoundException
354                 //        || x instanceof SecurityException)
355                 // In both cases, ignore and continue w/ next location
356             }
357             finally {
358                 // try to close the input stream if one was opened.
359                 if (fis != null) {
360                     try {
361                         fis.close();
362                     }
363                     // Ignore the exception.
364                     catch (IOException exc) {}
365                 }
366             }               
367         }
368         if (factoryClassName != null) {
369             debugPrintln("found in " + propertiesFilename + ", value="
370                           + factoryClassName);
371             return factoryClassName;
372         }
373
374         // Try Jar Service Provider Mechanism
375         return findJarServiceProviderName(factoryId);
376     } // lookUpFactoryClass(String,String):String
377
378     //
379     // Private static methods
380     //
381
382     /** Prints a message to standard error if debugging is enabled. */
383     private static void debugPrintln(String msg) {
384         if (DEBUG) {
385             System.err.println("JAXP: " + msg);
386         }
387     } // debugPrintln(String)
388
389     /**
390      * Figure out which ClassLoader to use.  For JDK 1.2 and later use
391      * the context ClassLoader.
392      */
393     static ClassLoader findClassLoader()
394         throws ConfigurationError
395     { 
396         SecuritySupport ss = SecuritySupport.getInstance();
397
398         // Figure out which ClassLoader to use for loading the provider
399         // class.  If there is a Context ClassLoader then use it.
400         ClassLoader context = ss.getContextClassLoader();
401         ClassLoader system = ss.getSystemClassLoader();
402
403         ClassLoader chain = system;
404         while (true) {
405             if (context == chain) {
406                 // Assert: we are on JDK 1.1 or we have no Context ClassLoader
407                 // or any Context ClassLoader in chain of system classloader
408                 // (including extension ClassLoader) so extend to widest
409                 // ClassLoader (always look in system ClassLoader if Xalan
410                 // is in boot/extension/system classpath and in current
411                 // ClassLoader otherwise); normal classloaders delegate
412                 // back to system ClassLoader first so this widening doesn't
413                 // change the fact that context ClassLoader will be consulted
414                 ClassLoader current = ObjectFactory.class.getClassLoader();
415
416                 chain = system;
417                 while (true) {
418                     if (current == chain) {
419                         // Assert: Current ClassLoader in chain of
420                         // boot/extension/system ClassLoaders
421                         return system;
422                     }
423                     if (chain == null) {
424                         break;
425                     }
426                     chain = ss.getParentClassLoader(chain);
427                 }
428
429                 // Assert: Current ClassLoader not in chain of
430                 // boot/extension/system ClassLoaders
431                 return current;
432             }
433
434             if (chain == null) {
435                 // boot ClassLoader reached
436                 break;
437             }
438
439             // Check for any extension ClassLoaders in chain up to
440             // boot ClassLoader
441             chain = ss.getParentClassLoader(chain);
442         };
443
444         // Assert: Context ClassLoader not in chain of
445         // boot/extension/system ClassLoaders
446         return context;
447     } // findClassLoader():ClassLoader
448
449     /**
450      * Create an instance of a class using the specified ClassLoader
451      */ 
452     static Object newInstance(String className, ClassLoader cl,
453                                       boolean doFallback)
454         throws ConfigurationError
455     {
456         // assert(className != null);
457         try{
458             Class providerClass = findProviderClass(className, cl, doFallback);
459             Object instance = providerClass.newInstance();
460             debugPrintln("created new instance of " + providerClass +
461                    " using ClassLoader: " + cl);
462             return instance;
463         } catch (ClassNotFoundException x) {
464             throw new ConfigurationError(
465                 "Provider " + className + " not found", x);
466         } catch (Exception x) {
467             throw new ConfigurationError(
468                 "Provider " + className + " could not be instantiated: " + x,
469                 x);
470         }
471     }
472
473     /**
474      * Find a Class using the specified ClassLoader
475      */ 
476     static Class findProviderClass(String className, ClassLoader cl,
477                                            boolean doFallback)
478         throws ClassNotFoundException, ConfigurationError
479     {   
480         //throw security exception if the calling thread is not allowed to access the
481         //class. Restrict the access to the package classes as specified in java.security policy.
482         SecurityManager security = System.getSecurityManager();
483         try{
484                 if (security != null){
485                     final int lastDot = className.lastIndexOf(".");
486                     String packageName = className;
487                     if (lastDot != -1) packageName = className.substring(0, lastDot);
488                     security.checkPackageAccess(packageName);
489                  }   
490         }catch(SecurityException e){
491             throw e;
492         }
493         
494         Class providerClass;
495         if (cl == null) {
496             // XXX Use the bootstrap ClassLoader.  There is no way to
497             // load a class using the bootstrap ClassLoader that works
498             // in both JDK 1.1 and Java 2.  However, this should still
499             // work b/c the following should be true:
500             //
501             // (cl == null) iff current ClassLoader == null
502             //
503             // Thus Class.forName(String) will use the current
504             // ClassLoader which will be the bootstrap ClassLoader.
505             providerClass = Class.forName(className);
506         } else {
507             try {
508                 providerClass = cl.loadClass(className);
509             } catch (ClassNotFoundException x) {
510                 if (doFallback) {
511                     // Fall back to current classloader
512                     ClassLoader current = ObjectFactory.class.getClassLoader();
513                     if (current == null) {
514                         providerClass = Class.forName(className);
515                     } else if (cl != current) {
516                         cl = current;
517                         providerClass = cl.loadClass(className);
518                     } else {
519                         throw x;
520                     }
521                 } else {
522                     throw x;
523                 }
524             }
525         }
526
527         return providerClass;
528     }
529
530     /**
531      * Find the name of service provider using Jar Service Provider Mechanism
532      *
533      * @return instance of provider class if found or null
534      */
535     private static String findJarServiceProviderName(String factoryId)
536     {
537         SecuritySupport ss = SecuritySupport.getInstance();
538         String serviceId = SERVICES_PATH + factoryId;
539         InputStream is = null;
540
541         // First try the Context ClassLoader
542         ClassLoader cl = findClassLoader();
543
544         is = ss.getResourceAsStream(cl, serviceId);
545
546         // If no provider found then try the current ClassLoader
547         if (is == null) {
548             ClassLoader current = ObjectFactory.class.getClassLoader();
549             if (cl != current) {
550                 cl = current;
551                 is = ss.getResourceAsStream(cl, serviceId);
552             }
553         }
554
555         if (is == null) {
556             // No provider found
557             return null;
558         }
559
560         debugPrintln("found jar resource=" + serviceId +
561                " using ClassLoader: " + cl);
562
563         // Read the service provider name in UTF-8 as specified in
564         // the jar spec.  Unfortunately this fails in Microsoft
565         // VJ++, which does not implement the UTF-8
566         // encoding. Theoretically, we should simply let it fail in
567         // that case, since the JVM is obviously broken if it
568         // doesn't support such a basic standard.  But since there
569         // are still some users attempting to use VJ++ for
570         // development, we have dropped in a fallback which makes a
571         // second attempt using the platform's default encoding. In
572         // VJ++ this is apparently ASCII, which is a subset of
573         // UTF-8... and since the strings we'll be reading here are
574         // also primarily limited to the 7-bit ASCII range (at
575         // least, in English versions), this should work well
576         // enough to keep us on the air until we're ready to
577         // officially decommit from VJ++. [Edited comment from
578         // jkesselm]
579         BufferedReader rd;
580         try {
581             rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
582         } catch (java.io.UnsupportedEncodingException e) {
583             rd = new BufferedReader(new InputStreamReader(is));
584         }
585         
586         String factoryClassName = null;
587         try {
588             // XXX Does not handle all possible input as specified by the
589             // Jar Service Provider specification
590             factoryClassName = rd.readLine();
591         } catch (IOException x) {
592             // No provider found
593             return null;
594         }
595         finally {
596             try {
597                 // try to close the reader.
598                 rd.close();
599             }
600             // Ignore the exception.
601             catch (IOException exc) {}
602         }          
603
604         if (factoryClassName != null &&
605             ! "".equals(factoryClassName)) {
606             debugPrintln("found in resource, value="
607                    + factoryClassName);
608
609             // Note: here we do not want to fall back to the current
610             // ClassLoader because we want to avoid the case where the
611             // resource file was found using one ClassLoader and the
612             // provider class was instantiated using a different one.
613             return factoryClassName;
614         }
615
616         // No provider found
617         return null;
618     }
619
620     //
621     // Classes
622     //
623
624     /**
625      * A configuration error.
626      */
627     static class ConfigurationError 
628         extends Error {
629                 static final long serialVersionUID = 2276082712114762609L;
630         //
631         // Data
632         //
633
634         /** Exception. */
635         private Exception exception;
636
637         //
638         // Constructors
639         //
640
641         /**
642          * Construct a new instance with the specified detail string and
643          * exception.
644          */
645         ConfigurationError(String msg, Exception x) {
646             super(msg);
647             this.exception = x;
648         } // <init>(String,Exception)
649
650         //
651         // Public methods
652         //
653
654         /** Returns the exception associated to this error. */
655         Exception getException() {
656             return exception;
657         } // getException():Exception
658
659     } // class ConfigurationError
660
661 } // class ObjectFactory