KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > xni > 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 xni;
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  * This class was moved from the <code>javax.xml.parsers.ObjectFactory</code>
40  * class and modified to be used as a general utility for creating objects
41  * dynamically.
42  *
43  * @version $Id: ObjectFactory.java,v 1.9 2005/04/18 04:15:35 mrglavas Exp $
44  */

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

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

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

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

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

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

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

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

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

257     //
258
// Private static methods
259
//
260

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

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

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

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

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

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

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

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

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

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

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

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

528         //
529
// methods
530
//
531

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

537     } // class ConfigurationError
538

539 } // class ObjectFactory
540
Popular Tags