KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > html > dom > 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.html.dom;
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  * @xerces.internal
41  *
42  * @version $Id: ObjectFactory.java,v 1.11 2005/04/18 04:14:18 mrglavas Exp $
43  */

44 final class ObjectFactory {
45
46     //
47
// Constants
48
//
49

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

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

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

73     private static long fLastModified = -1;
74
75     //
76
// static methods
77
//
78

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

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

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

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

256     //
257
// Private static methods
258
//
259

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

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

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

327     /**
328      * Create an instance of a class using the specified ClassLoader
329      */

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

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

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

498     /**
499      * A configuration error.
500      */

501     static final class ConfigurationError
502         extends Error JavaDoc {
503
504         /** Serialization version. */
505         static final long serialVersionUID = 2646822752226280048L;
506         
507         //
508
// Data
509
//
510

511         /** Exception. */
512         private Exception JavaDoc exception;
513
514         //
515
// Constructors
516
//
517

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

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

527         //
528
// methods
529
//
530

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

536     } // class ConfigurationError
537

538 } // class ObjectFactory
539
Popular Tags