KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > xerces > parsers > ObjectFactory


1 /*
2  * Copyright 2001-2005 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 package org.apache.xerces.parsers;
18
19 import java.io.InputStream JavaDoc;
20 import java.io.IOException JavaDoc;
21 import java.io.File JavaDoc;
22 import java.io.FileInputStream JavaDoc;
23
24 import java.util.Properties JavaDoc;
25 import java.io.BufferedReader JavaDoc;
26 import java.io.InputStreamReader JavaDoc;
27
28 /**
29  * This class is duplicated for each JAXP subpackage so keep it in sync.
30  * It is package private and therefore is not exposed as part of the JAXP
31  * API.
32  * <p>
33  * This code is designed to implement the JAXP 1.1 spec pluggability
34  * feature and is designed to run on JDK version 1.1 and
35  * later, and to compile on JDK 1.2 and onward.
36  * The code also runs both as part of an unbundled jar file and
37  * when bundled as part of the JDK.
38  * <p>
39  *
40  * @version $Id: ObjectFactory.java,v 1.10 2005/04/18 04:14:19 mrglavas Exp $
41  */

42 final class ObjectFactory {
43
44     //
45
// Constants
46
//
47

48     // name of default properties file to look for in JDK's jre/lib directory
49
private static final String JavaDoc DEFAULT_PROPERTIES_FILENAME = "xerces.properties";
50
51     /** Set to true for debugging */
52     private static final boolean DEBUG = false;
53     
54     /**
55      * Default columns per line.
56      */

57     private static final int DEFAULT_LINE_LENGTH = 80;
58
59     /** cache the contents of the xerces.properties file.
60      * Until an attempt has been made to read this file, this will
61      * be null; if the file does not exist or we encounter some other error
62      * during the read, this will be empty.
63      */

64     private static Properties JavaDoc fXercesProperties = null;
65
66     /***
67      * Cache the time stamp of the xerces.properties file so
68      * that we know if it's been modified and can invalidate
69      * the cache when necessary.
70      */

71     private static long fLastModified = -1;
72
73     //
74
// static methods
75
//
76

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

95     static Object JavaDoc createObject(String JavaDoc factoryId, String JavaDoc fallbackClassName)
96         throws ConfigurationError {
97         return createObject(factoryId, null, fallbackClassName);
98     } // createObject(String,String):Object
99

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

122     static Object JavaDoc createObject(String JavaDoc factoryId,
123                                       String JavaDoc propertiesFilename,
124                                       String JavaDoc fallbackClassName)
125         throws ConfigurationError
126     {
127         if (DEBUG) debugPrintln("debug is on");
128
129         SecuritySupport ss = SecuritySupport.getInstance();
130         ClassLoader JavaDoc cl = findClassLoader();
131
132         // Use the system property first
133
try {
134             String JavaDoc systemProp = ss.getSystemProperty(factoryId);
135             if (systemProp != null) {
136                 if (DEBUG) debugPrintln("found system property, value=" + systemProp);
137                 return newInstance(systemProp, cl, true);
138             }
139         } catch (SecurityException JavaDoc se) {
140             // Ignore and continue w/ next location
141
}
142
143         // Try to read from propertiesFilename, or $java.home/lib/xerces.properties
144
String JavaDoc factoryClassName = null;
145         // no properties file name specified; use $JAVA_HOME/lib/xerces.properties:
146
if (propertiesFilename == null) {
147             File JavaDoc propertiesFile = null;
148             boolean propertiesFileExists = false;
149             try {
150                 String JavaDoc javah = ss.getSystemProperty("java.home");
151                 propertiesFilename = javah + File.separator +
152                     "lib" + File.separator + DEFAULT_PROPERTIES_FILENAME;
153                 propertiesFile = new File JavaDoc(propertiesFilename);
154                 propertiesFileExists = ss.getFileExists(propertiesFile);
155             } catch (SecurityException JavaDoc e) {
156                 // try again...
157
fLastModified = -1;
158                 fXercesProperties = null;
159             }
160             
161             synchronized (ObjectFactory.class) {
162                 boolean loadProperties = false;
163                 FileInputStream JavaDoc fis = null;
164                 try {
165                     // file existed last time
166
if(fLastModified >= 0) {
167                         if(propertiesFileExists &&
168                                 (fLastModified < (fLastModified = ss.getLastModified(propertiesFile)))) {
169                             loadProperties = true;
170                         } else {
171                             // file has stopped existing...
172
if(!propertiesFileExists) {
173                                 fLastModified = -1;
174                                 fXercesProperties = null;
175                             } // else, file wasn't modified!
176
}
177                     } else {
178                         // file has started to exist:
179
if(propertiesFileExists) {
180                             loadProperties = true;
181                             fLastModified = ss.getLastModified(propertiesFile);
182                         } // else, nothing's changed
183
}
184                     if(loadProperties) {
185                         // must never have attempted to read xerces.properties before (or it's outdeated)
186
fXercesProperties = new Properties JavaDoc();
187                         fis = ss.getFileInputStream(propertiesFile);
188                         fXercesProperties.load(fis);
189                     }
190                 } catch (Exception JavaDoc x) {
191                     fXercesProperties = null;
192                     fLastModified = -1;
193                     // assert(x instanceof FileNotFoundException
194
// || x instanceof SecurityException)
195
// In both cases, ignore and continue w/ next location
196
}
197                 finally {
198                     // try to close the input stream if one was opened.
199
if (fis != null) {
200                         try {
201                             fis.close();
202                         }
203                         // Ignore the exception.
204
catch (IOException JavaDoc exc) {}
205                     }
206                 }
207             }
208             if(fXercesProperties != null) {
209                 factoryClassName = fXercesProperties.getProperty(factoryId);
210             }
211         } else {
212             FileInputStream JavaDoc fis = null;
213             try {
214                 fis = ss.getFileInputStream(new File JavaDoc(propertiesFilename));
215                 Properties JavaDoc props = new Properties JavaDoc();
216                 props.load(fis);
217                 factoryClassName = props.getProperty(factoryId);
218             } catch (Exception JavaDoc x) {
219                 // assert(x instanceof FileNotFoundException
220
// || x instanceof SecurityException)
221
// In both cases, ignore and continue w/ next location
222
}
223             finally {
224                 // try to close the input stream if one was opened.
225
if (fis != null) {
226                     try {
227                         fis.close();
228                     }
229                     // Ignore the exception.
230
catch (IOException JavaDoc exc) {}
231                 }
232             }
233         }
234         if (factoryClassName != null) {
235             if (DEBUG) debugPrintln("found in " + propertiesFilename + ", value=" + factoryClassName);
236             return newInstance(factoryClassName, cl, true);
237         }
238
239         // Try Jar Service Provider Mechanism
240
Object JavaDoc provider = findJarServiceProvider(factoryId);
241         if (provider != null) {
242             return provider;
243         }
244
245         if (fallbackClassName == null) {
246             throw new ConfigurationError(
247                 "Provider for " + factoryId + " cannot be found", null);
248         }
249
250         if (DEBUG) debugPrintln("using fallback, value=" + fallbackClassName);
251         return newInstance(fallbackClassName, cl, true);
252     } // createObject(String,String,String):Object
253

254     //
255
// Private static methods
256
//
257

258     /** Prints a message to standard error if debugging is enabled. */
259     private static void debugPrintln(String JavaDoc msg) {
260         if (DEBUG) {
261             System.err.println("JAXP: " + msg);
262         }
263     } // debugPrintln(String)
264

265     /**
266      * Figure out which ClassLoader to use. For JDK 1.2 and later use
267      * the context ClassLoader.
268      */

269     static ClassLoader JavaDoc findClassLoader()
270         throws ConfigurationError
271     {
272         SecuritySupport ss = SecuritySupport.getInstance();
273
274         // Figure out which ClassLoader to use for loading the provider
275
// class. If there is a Context ClassLoader then use it.
276
ClassLoader JavaDoc context = ss.getContextClassLoader();
277         ClassLoader JavaDoc system = ss.getSystemClassLoader();
278
279         ClassLoader JavaDoc chain = system;
280         while (true) {
281             if (context == chain) {
282                 // Assert: we are on JDK 1.1 or we have no Context ClassLoader
283
// or any Context ClassLoader in chain of system classloader
284
// (including extension ClassLoader) so extend to widest
285
// ClassLoader (always look in system ClassLoader if Xerces
286
// is in boot/extension/system classpath and in current
287
// ClassLoader otherwise); normal classloaders delegate
288
// back to system ClassLoader first so this widening doesn't
289
// change the fact that context ClassLoader will be consulted
290
ClassLoader JavaDoc current = ObjectFactory.class.getClassLoader();
291
292                 chain = system;
293                 while (true) {
294                     if (current == chain) {
295                         // Assert: Current ClassLoader in chain of
296
// boot/extension/system ClassLoaders
297
return system;
298                     }
299                     if (chain == null) {
300                         break;
301                     }
302                     chain = ss.getParentClassLoader(chain);
303                 }
304
305                 // Assert: Current ClassLoader not in chain of
306
// boot/extension/system ClassLoaders
307
return current;
308             }
309
310             if (chain == null) {
311                 // boot ClassLoader reached
312
break;
313             }
314
315             // Check for any extension ClassLoaders in chain up to
316
// boot ClassLoader
317
chain = ss.getParentClassLoader(chain);
318         };
319
320         // Assert: Context ClassLoader not in chain of
321
// boot/extension/system ClassLoaders
322
return context;
323     } // findClassLoader():ClassLoader
324

325     /**
326      * Create an instance of a class using the specified ClassLoader
327      */

328     static Object JavaDoc newInstance(String JavaDoc className, ClassLoader JavaDoc cl,
329                                       boolean doFallback)
330         throws ConfigurationError
331     {
332         // assert(className != null);
333
try{
334             Class JavaDoc providerClass = findProviderClass(className, cl, doFallback);
335             Object JavaDoc instance = providerClass.newInstance();
336             if (DEBUG) debugPrintln("created new instance of " + providerClass +
337                    " using ClassLoader: " + cl);
338             return instance;
339         } catch (ClassNotFoundException JavaDoc x) {
340             throw new ConfigurationError(
341                 "Provider " + className + " not found", x);
342         } catch (Exception JavaDoc x) {
343             throw new ConfigurationError(
344                 "Provider " + className + " could not be instantiated: " + x,
345                 x);
346         }
347     }
348
349     /**
350      * Find a Class using the specified ClassLoader
351      */

352     static Class JavaDoc findProviderClass(String JavaDoc className, ClassLoader JavaDoc cl,
353                                       boolean doFallback)
354         throws ClassNotFoundException JavaDoc, ConfigurationError
355     {
356         //throw security exception if the calling thread is not allowed to access the package
357
//restrict the access to package as speicified in java.security policy
358
SecurityManager JavaDoc security = System.getSecurityManager();
359         if (security != null) {
360             final int lastDot = className.lastIndexOf(".");
361             String JavaDoc packageName = className;
362             if (lastDot != -1) packageName = className.substring(0, lastDot);
363             security.checkPackageAccess(packageName);
364         }
365         Class JavaDoc providerClass;
366         if (cl == null) {
367             // XXX Use the bootstrap ClassLoader. There is no way to
368
// load a class using the bootstrap ClassLoader that works
369
// in both JDK 1.1 and Java 2. However, this should still
370
// work b/c the following should be true:
371
//
372
// (cl == null) iff current ClassLoader == null
373
//
374
// Thus Class.forName(String) will use the current
375
// ClassLoader which will be the bootstrap ClassLoader.
376
providerClass = Class.forName(className);
377         } else {
378             try {
379                 providerClass = cl.loadClass(className);
380             } catch (ClassNotFoundException JavaDoc x) {
381                 if (doFallback) {
382                     // Fall back to current classloader
383
ClassLoader JavaDoc current = ObjectFactory.class.getClassLoader();
384                     if (current == null) {
385                         providerClass = Class.forName(className);
386                     } else if (cl != current) {
387                         cl = current;
388                         providerClass = cl.loadClass(className);
389                     } else {
390                         throw x;
391                     }
392                 } else {
393                     throw x;
394                 }
395             }
396         }
397
398         return providerClass;
399     }
400
401     /*
402      * Try to find provider using Jar Service Provider Mechanism
403      *
404      * @return instance of provider class if found or null
405      */

406     private static Object JavaDoc findJarServiceProvider(String JavaDoc factoryId)
407         throws ConfigurationError
408     {
409         SecuritySupport ss = SecuritySupport.getInstance();
410         String JavaDoc serviceId = "META-INF/services/" + factoryId;
411         InputStream JavaDoc is = null;
412
413         // First try the Context ClassLoader
414
ClassLoader JavaDoc cl = findClassLoader();
415
416         is = ss.getResourceAsStream(cl, serviceId);
417
418         // If no provider found then try the current ClassLoader
419
if (is == null) {
420             ClassLoader JavaDoc current = ObjectFactory.class.getClassLoader();
421             if (cl != current) {
422                 cl = current;
423                 is = ss.getResourceAsStream(cl, serviceId);
424             }
425         }
426
427         if (is == null) {
428             // No provider found
429
return null;
430         }
431
432         if (DEBUG) debugPrintln("found jar resource=" + serviceId +
433                " using ClassLoader: " + cl);
434
435         // Read the service provider name in UTF-8 as specified in
436
// the jar spec. Unfortunately this fails in Microsoft
437
// VJ++, which does not implement the UTF-8
438
// encoding. Theoretically, we should simply let it fail in
439
// that case, since the JVM is obviously broken if it
440
// doesn't support such a basic standard. But since there
441
// are still some users attempting to use VJ++ for
442
// development, we have dropped in a fallback which makes a
443
// second attempt using the platform's default encoding. In
444
// VJ++ this is apparently ASCII, which is a subset of
445
// UTF-8... and since the strings we'll be reading here are
446
// also primarily limited to the 7-bit ASCII range (at
447
// least, in English versions), this should work well
448
// enough to keep us on the air until we're ready to
449
// officially decommit from VJ++. [Edited comment from
450
// jkesselm]
451
BufferedReader JavaDoc rd;
452         try {
453             rd = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(is, "UTF-8"), DEFAULT_LINE_LENGTH);
454         } catch (java.io.UnsupportedEncodingException JavaDoc e) {
455             rd = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(is), DEFAULT_LINE_LENGTH);
456         }
457
458         String JavaDoc factoryClassName = null;
459         try {
460             // XXX Does not handle all possible input as specified by the
461
// Jar Service Provider specification
462
factoryClassName = rd.readLine();
463         } catch (IOException JavaDoc x) {
464             // No provider found
465
return null;
466         }
467         finally {
468             try {
469                 // try to close the reader.
470
rd.close();
471             }
472             // Ignore the exception.
473
catch (IOException JavaDoc exc) {}
474         }
475
476         if (factoryClassName != null &&
477             ! "".equals(factoryClassName)) {
478             if (DEBUG) debugPrintln("found in resource, value="
479                    + factoryClassName);
480
481             // Note: here we do not want to fall back to the current
482
// ClassLoader because we want to avoid the case where the
483
// resource file was found using one ClassLoader and the
484
// provider class was instantiated using a different one.
485
return newInstance(factoryClassName, cl, false);
486         }
487
488         // No provider found
489
return null;
490     }
491
492     //
493
// Classes
494
//
495

496     /**
497      * A configuration error.
498      */

499     static final class ConfigurationError
500         extends Error JavaDoc {
501
502         /** Serialization version. */
503         static final long serialVersionUID = -7285495612271660427L;
504         
505         //
506
// Data
507
//
508

509         /** Exception. */
510         private Exception JavaDoc exception;
511
512         //
513
// Constructors
514
//
515

516         /**
517          * Construct a new instance with the specified detail string and
518          * exception.
519          */

520         ConfigurationError(String JavaDoc msg, Exception JavaDoc x) {
521             super(msg);
522             this.exception = x;
523         } // <init>(String,Exception)
524

525         //
526
// methods
527
//
528

529         /** Returns the exception associated to this error. */
530         Exception JavaDoc getException() {
531             return exception;
532         } // getException():Exception
533

534     } // class ConfigurationError
535

536 } // class ObjectFactory
537
Popular Tags