KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > xml > utils > ObjectFactory


1 /*
2  * Copyright 2001-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 /*
17  * $Id$
18  */

19
20 package org.apache.xml.utils;
21
22 import java.io.InputStream JavaDoc;
23 import java.io.IOException JavaDoc;
24 import java.io.File JavaDoc;
25 import java.io.FileInputStream JavaDoc;
26
27 import java.util.Properties JavaDoc;
28 import java.io.BufferedReader JavaDoc;
29 import java.io.InputStreamReader JavaDoc;
30
31 /**
32  * This class is duplicated for each JAXP subpackage so keep it in sync.
33  * It is package private and therefore is not exposed as part of the JAXP
34  * API.
35  * <p>
36  * This code is designed to implement the JAXP 1.1 spec pluggability
37  * feature and is designed to run on JDK version 1.1 and
38  * later, and to compile on JDK 1.2 and onward.
39  * The code also runs both as part of an unbundled jar file and
40  * when bundled as part of the JDK.
41  * <p>
42  * This class was moved from the <code>javax.xml.parsers.ObjectFactory</code>
43  * class and modified to be used as a general utility for creating objects
44  * dynamically.
45  *
46  * @version $Id: $
47  */

48 class ObjectFactory {
49
50     //
51
// Constants
52
//
53

54     // name of default properties file to look for in JDK's jre/lib directory
55
private static final String JavaDoc DEFAULT_PROPERTIES_FILENAME =
56                                                      "xalan.properties";
57
58     private static final String JavaDoc SERVICES_PATH = "META-INF/services/";
59
60     /** Set to true for debugging */
61     private static final boolean DEBUG = false;
62
63     /** cache the contents of the xalan.properties file.
64      * Until an attempt has been made to read this file, this will
65      * be null; if the file does not exist or we encounter some other error
66      * during the read, this will be empty.
67      */

68     private static Properties JavaDoc fXalanProperties = null;
69
70     /***
71      * Cache the time stamp of the xalan.properties file so
72      * that we know if it's been modified and can invalidate
73      * the cache when necessary.
74      */

75     private static long fLastModified = -1;
76
77     //
78
// Public static methods
79
//
80

81     /**
82      * Finds the implementation Class object in the specified order. The
83      * specified order is the following:
84      * <ol>
85      * <li>query the system property using <code>System.getProperty</code>
86      * <li>read <code>META-INF/services/<i>factoryId</i></code> file
87      * <li>use fallback classname
88      * </ol>
89      *
90      * @return instance of factory, never null
91      *
92      * @param factoryId Name of the factory to find, same as
93      * a property name
94      * @param fallbackClassName Implementation class name, if nothing else
95      * is found. Use null to mean no fallback.
96      *
97      * @exception ObjectFactory.ConfigurationError
98      */

99     static Object JavaDoc createObject(String JavaDoc factoryId, String JavaDoc fallbackClassName)
100         throws ConfigurationError {
101         return createObject(factoryId, null, fallbackClassName);
102     } // createObject(String,String):Object
103

104     /**
105      * Finds the implementation Class object in the specified order. The
106      * specified order is the following:
107      * <ol>
108      * <li>query the system property using <code>System.getProperty</code>
109      * <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
110      * <li>read <code>META-INF/services/<i>factoryId</i></code> file
111      * <li>use fallback classname
112      * </ol>
113      *
114      * @return instance of factory, never null
115      *
116      * @param factoryId Name of the factory to find, same as
117      * a property name
118      * @param propertiesFilename The filename in the $java.home/lib directory
119      * of the properties file. If none specified,
120      * ${java.home}/lib/xalan.properties will be used.
121      * @param fallbackClassName Implementation class name, if nothing else
122      * is found. Use null to mean no fallback.
123      *
124      * @exception ObjectFactory.ConfigurationError
125      */

126     static Object JavaDoc createObject(String JavaDoc factoryId,
127                                       String JavaDoc propertiesFilename,
128                                       String JavaDoc fallbackClassName)
129         throws ConfigurationError
130     {
131         Class JavaDoc factoryClass = lookUpFactoryClass(factoryId,
132                                                 propertiesFilename,
133                                                 fallbackClassName);
134
135         if (factoryClass == null) {
136             throw new ConfigurationError(
137                 "Provider for " + factoryId + " cannot be found", null);
138         }
139
140         try{
141             Object JavaDoc instance = factoryClass.newInstance();
142             debugPrintln("created new instance of factory " + factoryId);
143             return instance;
144         } catch (Exception JavaDoc x) {
145             throw new ConfigurationError(
146                 "Provider for factory " + factoryId
147                     + " could not be instantiated: " + x, x);
148         }
149     } // createObject(String,String,String):Object
150

151     /**
152      * Finds the implementation Class object in the specified order. The
153      * specified order is the following:
154      * <ol>
155      * <li>query the system property using <code>System.getProperty</code>
156      * <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
157      * <li>read <code>META-INF/services/<i>factoryId</i></code> file
158      * <li>use fallback classname
159      * </ol>
160      *
161      * @return Class object of factory, never null
162      *
163      * @param factoryId Name of the factory to find, same as
164      * a property name
165      * @param propertiesFilename The filename in the $java.home/lib directory
166      * of the properties file. If none specified,
167      * ${java.home}/lib/xalan.properties will be used.
168      * @param fallbackClassName Implementation class name, if nothing else
169      * is found. Use null to mean no fallback.
170      *
171      * @exception ObjectFactory.ConfigurationError
172      */

173     static Class JavaDoc lookUpFactoryClass(String JavaDoc factoryId)
174         throws ConfigurationError
175     {
176         return lookUpFactoryClass(factoryId, null, null);
177     } // lookUpFactoryClass(String):Class
178

179     /**
180      * Finds the implementation Class object in the specified order. The
181      * specified order is the following:
182      * <ol>
183      * <li>query the system property using <code>System.getProperty</code>
184      * <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
185      * <li>read <code>META-INF/services/<i>factoryId</i></code> file
186      * <li>use fallback classname
187      * </ol>
188      *
189      * @return Class object that provides factory service, never null
190      *
191      * @param factoryId Name of the factory to find, same as
192      * a property name
193      * @param propertiesFilename The filename in the $java.home/lib directory
194      * of the properties file. If none specified,
195      * ${java.home}/lib/xalan.properties will be used.
196      * @param fallbackClassName Implementation class name, if nothing else
197      * is found. Use null to mean no fallback.
198      *
199      * @exception ObjectFactory.ConfigurationError
200      */

201     static Class JavaDoc lookUpFactoryClass(String JavaDoc factoryId,
202                                            String JavaDoc propertiesFilename,
203                                            String JavaDoc fallbackClassName)
204         throws ConfigurationError
205     {
206         String JavaDoc factoryClassName = lookUpFactoryClassName(factoryId,
207                                                          propertiesFilename,
208                                                          fallbackClassName);
209         ClassLoader JavaDoc cl = findClassLoader();
210
211         if (factoryClassName == null) {
212             factoryClassName = fallbackClassName;
213         }
214
215         // assert(className != null);
216
try{
217             Class JavaDoc providerClass = findProviderClass(factoryClassName,
218                                                     cl,
219                                                     true);
220             debugPrintln("created new instance of " + providerClass +
221                    " using ClassLoader: " + cl);
222             return providerClass;
223         } catch (ClassNotFoundException JavaDoc x) {
224             throw new ConfigurationError(
225                 "Provider " + factoryClassName + " not found", x);
226         } catch (Exception JavaDoc x) {
227             throw new ConfigurationError(
228                 "Provider "+factoryClassName+" could not be instantiated: "+x,
229                 x);
230         }
231     } // lookUpFactoryClass(String,String,String):Class
232

233     /**
234      * Finds the name of the required implementation class in the specified
235      * order. The specified order is the following:
236      * <ol>
237      * <li>query the system property using <code>System.getProperty</code>
238      * <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
239      * <li>read <code>META-INF/services/<i>factoryId</i></code> file
240      * <li>use fallback classname
241      * </ol>
242      *
243      * @return name of class that provides factory service, never null
244      *
245      * @param factoryId Name of the factory to find, same as
246      * a property name
247      * @param propertiesFilename The filename in the $java.home/lib directory
248      * of the properties file. If none specified,
249      * ${java.home}/lib/xalan.properties will be used.
250      * @param fallbackClassName Implementation class name, if nothing else
251      * is found. Use null to mean no fallback.
252      *
253      * @exception ObjectFactory.ConfigurationError
254      */

255     static String JavaDoc lookUpFactoryClassName(String JavaDoc factoryId,
256                                                 String JavaDoc propertiesFilename,
257                                                 String JavaDoc fallbackClassName)
258     {
259         SecuritySupport ss = SecuritySupport.getInstance();
260
261         // Use the system property first
262
try {
263             String JavaDoc systemProp = ss.getSystemProperty(factoryId);
264             if (systemProp != null) {
265                 debugPrintln("found system property, value=" + systemProp);
266                 return systemProp;
267             }
268         } catch (SecurityException JavaDoc se) {
269             // Ignore and continue w/ next location
270
}
271
272         // Try to read from propertiesFilename, or
273
// $java.home/lib/xalan.properties
274
String JavaDoc factoryClassName = null;
275         // no properties file name specified; use
276
// $JAVA_HOME/lib/xalan.properties:
277
if (propertiesFilename == null) {
278             File JavaDoc propertiesFile = null;
279             boolean propertiesFileExists = false;
280             try {
281                 String JavaDoc javah = ss.getSystemProperty("java.home");
282                 propertiesFilename = javah + File.separator +
283                     "lib" + File.separator + DEFAULT_PROPERTIES_FILENAME;
284                 propertiesFile = new File JavaDoc(propertiesFilename);
285                 propertiesFileExists = ss.getFileExists(propertiesFile);
286             } catch (SecurityException JavaDoc e) {
287                 // try again...
288
fLastModified = -1;
289                 fXalanProperties = null;
290             }
291
292             synchronized (ObjectFactory.class) {
293                 boolean loadProperties = false;
294                 try {
295                     // file existed last time
296
if(fLastModified >= 0) {
297                         if(propertiesFileExists &&
298                                 (fLastModified < (fLastModified = ss.getLastModified(propertiesFile)))) {
299                             loadProperties = true;
300                         } else {
301                             // file has stopped existing...
302
if(!propertiesFileExists) {
303                                 fLastModified = -1;
304                                 fXalanProperties = null;
305                             } // else, file wasn't modified!
306
}
307                     } else {
308                         // file has started to exist:
309
if(propertiesFileExists) {
310                             loadProperties = true;
311                             fLastModified = ss.getLastModified(propertiesFile);
312                         } // else, nothing's changed
313
}
314                     if(loadProperties) {
315                         // must never have attempted to read xalan.properties
316
// before (or it's outdeated)
317
fXalanProperties = new Properties JavaDoc();
318                         FileInputStream JavaDoc fis =
319                                          ss.getFileInputStream(propertiesFile);
320                         fXalanProperties.load(fis);
321                         fis.close();
322                     }
323                 } catch (Exception JavaDoc x) {
324                     fXalanProperties = null;
325                     fLastModified = -1;
326                         // assert(x instanceof FileNotFoundException
327
// || x instanceof SecurityException)
328
// In both cases, ignore and continue w/ next location
329
}
330             }
331             if(fXalanProperties != null) {
332                 factoryClassName = fXalanProperties.getProperty(factoryId);
333             }
334         } else {
335             try {
336                 FileInputStream JavaDoc fis =
337                            ss.getFileInputStream(new File JavaDoc(propertiesFilename));
338                 Properties JavaDoc props = new Properties JavaDoc();
339                 props.load(fis);
340                 fis.close();
341                 factoryClassName = props.getProperty(factoryId);
342             } catch (Exception JavaDoc x) {
343                 // assert(x instanceof FileNotFoundException
344
// || x instanceof SecurityException)
345
// In both cases, ignore and continue w/ next location
346
}
347         }
348         if (factoryClassName != null) {
349             debugPrintln("found in " + propertiesFilename + ", value="
350                           + factoryClassName);
351             return factoryClassName;
352         }
353
354         // Try Jar Service Provider Mechanism
355
return findJarServiceProviderName(factoryId);
356     } // lookUpFactoryClass(String,String):String
357

358     //
359
// Private static methods
360
//
361

362     /** Prints a message to standard error if debugging is enabled. */
363     private static void debugPrintln(String JavaDoc msg) {
364         if (DEBUG) {
365             System.err.println("JAXP: " + msg);
366         }
367     } // debugPrintln(String)
368

369     /**
370      * Figure out which ClassLoader to use. For JDK 1.2 and later use
371      * the context ClassLoader.
372      */

373     static ClassLoader JavaDoc findClassLoader()
374         throws ConfigurationError
375     {
376         SecuritySupport ss = SecuritySupport.getInstance();
377
378         // Figure out which ClassLoader to use for loading the provider
379
// class. If there is a Context ClassLoader then use it.
380
ClassLoader JavaDoc context = ss.getContextClassLoader();
381         ClassLoader JavaDoc system = ss.getSystemClassLoader();
382
383         ClassLoader JavaDoc chain = system;
384         while (true) {
385             if (context == chain) {
386                 // Assert: we are on JDK 1.1 or we have no Context ClassLoader
387
// or any Context ClassLoader in chain of system classloader
388
// (including extension ClassLoader) so extend to widest
389
// ClassLoader (always look in system ClassLoader if Xalan
390
// is in boot/extension/system classpath and in current
391
// ClassLoader otherwise); normal classloaders delegate
392
// back to system ClassLoader first so this widening doesn't
393
// change the fact that context ClassLoader will be consulted
394
ClassLoader JavaDoc current = ObjectFactory.class.getClassLoader();
395
396                 chain = system;
397                 while (true) {
398                     if (current == chain) {
399                         // Assert: Current ClassLoader in chain of
400
// boot/extension/system ClassLoaders
401
return system;
402                     }
403                     if (chain == null) {
404                         break;
405                     }
406                     chain = ss.getParentClassLoader(chain);
407                 }
408
409                 // Assert: Current ClassLoader not in chain of
410
// boot/extension/system ClassLoaders
411
return current;
412             }
413
414             if (chain == null) {
415                 // boot ClassLoader reached
416
break;
417             }
418
419             // Check for any extension ClassLoaders in chain up to
420
// boot ClassLoader
421
chain = ss.getParentClassLoader(chain);
422         };
423
424         // Assert: Context ClassLoader not in chain of
425
// boot/extension/system ClassLoaders
426
return context;
427     } // findClassLoader():ClassLoader
428

429     /**
430      * Create an instance of a class using the specified ClassLoader
431      */

432     static Object JavaDoc newInstance(String JavaDoc className, ClassLoader JavaDoc cl,
433                                       boolean doFallback)
434         throws ConfigurationError
435     {
436         // assert(className != null);
437
try{
438             Class JavaDoc providerClass = findProviderClass(className, cl, doFallback);
439             Object JavaDoc instance = providerClass.newInstance();
440             debugPrintln("created new instance of " + providerClass +
441                    " using ClassLoader: " + cl);
442             return instance;
443         } catch (ClassNotFoundException JavaDoc x) {
444             throw new ConfigurationError(
445                 "Provider " + className + " not found", x);
446         } catch (Exception JavaDoc x) {
447             throw new ConfigurationError(
448                 "Provider " + className + " could not be instantiated: " + x,
449                 x);
450         }
451     }
452
453     /**
454      * Find a Class using the specified ClassLoader
455      */

456     static Class JavaDoc findProviderClass(String JavaDoc className, ClassLoader JavaDoc cl,
457                                            boolean doFallback)
458         throws ClassNotFoundException JavaDoc, ConfigurationError
459     {
460         //throw security exception if the calling thread is not allowed to access the
461
//class. Restrict the access to the package classes as specified in java.security policy.
462
SecurityManager JavaDoc security = System.getSecurityManager();
463         try{
464             if (security != null){
465                 security.checkPackageAccess(className);
466              }
467         }catch(SecurityException JavaDoc e){
468             throw e;
469         }
470         
471         Class JavaDoc providerClass;
472         if (cl == null) {
473             // XXX Use the bootstrap ClassLoader. There is no way to
474
// load a class using the bootstrap ClassLoader that works
475
// in both JDK 1.1 and Java 2. However, this should still
476
// work b/c the following should be true:
477
//
478
// (cl == null) iff current ClassLoader == null
479
//
480
// Thus Class.forName(String) will use the current
481
// ClassLoader which will be the bootstrap ClassLoader.
482
providerClass = Class.forName(className);
483         } else {
484             try {
485                 providerClass = cl.loadClass(className);
486             } catch (ClassNotFoundException JavaDoc x) {
487                 if (doFallback) {
488                     // Fall back to current classloader
489
ClassLoader JavaDoc current = ObjectFactory.class.getClassLoader();
490                     if (current == null) {
491                         providerClass = Class.forName(className);
492                     } else if (cl != current) {
493                         cl = current;
494                         providerClass = cl.loadClass(className);
495                     } else {
496                         throw x;
497                     }
498                 } else {
499                     throw x;
500                 }
501             }
502         }
503
504         return providerClass;
505     }
506
507     /**
508      * Find the name of service provider using Jar Service Provider Mechanism
509      *
510      * @return instance of provider class if found or null
511      */

512     private static String JavaDoc findJarServiceProviderName(String JavaDoc factoryId)
513     {
514         SecuritySupport ss = SecuritySupport.getInstance();
515         String JavaDoc serviceId = SERVICES_PATH + factoryId;
516         InputStream JavaDoc is = null;
517
518         // First try the Context ClassLoader
519
ClassLoader JavaDoc cl = findClassLoader();
520
521         is = ss.getResourceAsStream(cl, serviceId);
522
523         // If no provider found then try the current ClassLoader
524
if (is == null) {
525             ClassLoader JavaDoc current = ObjectFactory.class.getClassLoader();
526             if (cl != current) {
527                 cl = current;
528                 is = ss.getResourceAsStream(cl, serviceId);
529             }
530         }
531
532         if (is == null) {
533             // No provider found
534
return null;
535         }
536
537         debugPrintln("found jar resource=" + serviceId +
538                " using ClassLoader: " + cl);
539
540         // Read the service provider name in UTF-8 as specified in
541
// the jar spec. Unfortunately this fails in Microsoft
542
// VJ++, which does not implement the UTF-8
543
// encoding. Theoretically, we should simply let it fail in
544
// that case, since the JVM is obviously broken if it
545
// doesn't support such a basic standard. But since there
546
// are still some users attempting to use VJ++ for
547
// development, we have dropped in a fallback which makes a
548
// second attempt using the platform's default encoding. In
549
// VJ++ this is apparently ASCII, which is a subset of
550
// UTF-8... and since the strings we'll be reading here are
551
// also primarily limited to the 7-bit ASCII range (at
552
// least, in English versions), this should work well
553
// enough to keep us on the air until we're ready to
554
// officially decommit from VJ++. [Edited comment from
555
// jkesselm]
556
BufferedReader JavaDoc rd;
557         try {
558             rd = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(is, "UTF-8"));
559         } catch (java.io.UnsupportedEncodingException JavaDoc e) {
560             rd = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(is));
561         }
562         
563         String JavaDoc factoryClassName = null;
564         try {
565             // XXX Does not handle all possible input as specified by the
566
// Jar Service Provider specification
567
factoryClassName = rd.readLine();
568             rd.close();
569         } catch (IOException JavaDoc x) {
570             // No provider found
571
return null;
572         }
573
574         if (factoryClassName != null &&
575             ! "".equals(factoryClassName)) {
576             debugPrintln("found in resource, value="
577                    + factoryClassName);
578
579             // Note: here we do not want to fall back to the current
580
// ClassLoader because we want to avoid the case where the
581
// resource file was found using one ClassLoader and the
582
// provider class was instantiated using a different one.
583
return factoryClassName;
584         }
585
586         // No provider found
587
return null;
588     }
589
590     //
591
// Classes
592
//
593

594     /**
595      * A configuration error.
596      */

597     static class ConfigurationError
598         extends Error JavaDoc {
599
600         //
601
// Data
602
//
603

604         /** Exception. */
605         private Exception JavaDoc exception;
606
607         //
608
// Constructors
609
//
610

611         /**
612          * Construct a new instance with the specified detail string and
613          * exception.
614          */

615         ConfigurationError(String JavaDoc msg, Exception JavaDoc x) {
616             super(msg);
617             this.exception = x;
618         } // <init>(String,Exception)
619

620         //
621
// Public methods
622
//
623

624         /** Returns the exception associated to this error. */
625         Exception JavaDoc getException() {
626             return exception;
627         } // getException():Exception
628

629     } // class ConfigurationError
630

631 } // class ObjectFactory
632
Popular Tags