KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > cyberneko > html > 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 package org.cyberneko.html;
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.1 2004/03/31 20:00:21 andyc Exp $
41  */

42 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                 try {
164                     // file existed last time
165
if(fLastModified >= 0) {
166                         if(propertiesFileExists &&
167                                 (fLastModified < (fLastModified = ss.getLastModified(propertiesFile)))) {
168                             loadProperties = true;
169                         } else {
170                             // file has stopped existing...
171
if(!propertiesFileExists) {
172                                 fLastModified = -1;
173                                 fXercesProperties = null;
174                             } // else, file wasn't modified!
175
}
176                     } else {
177                         // file has started to exist:
178
if(propertiesFileExists) {
179                             loadProperties = true;
180                             fLastModified = ss.getLastModified(propertiesFile);
181                         } // else, nothing's changed
182
}
183                     if(loadProperties) {
184                         // must never have attempted to read xerces.properties before (or it's outdeated)
185
fXercesProperties = new Properties JavaDoc();
186                         FileInputStream JavaDoc fis = ss.getFileInputStream(propertiesFile);
187                         fXercesProperties.load(fis);
188                         fis.close();
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             }
198             if(fXercesProperties != null) {
199                 factoryClassName = fXercesProperties.getProperty(factoryId);
200             }
201         } else {
202             try {
203                 FileInputStream JavaDoc fis = ss.getFileInputStream(new File JavaDoc(propertiesFilename));
204                 Properties JavaDoc props = new Properties JavaDoc();
205                 props.load(fis);
206                 fis.close();
207                 factoryClassName = props.getProperty(factoryId);
208             } catch (Exception JavaDoc x) {
209                 // assert(x instanceof FileNotFoundException
210
// || x instanceof SecurityException)
211
// In both cases, ignore and continue w/ next location
212
}
213         }
214         if (factoryClassName != null) {
215             if (DEBUG) debugPrintln("found in " + propertiesFilename + ", value=" + factoryClassName);
216             return newInstance(factoryClassName, cl, true);
217         }
218
219         // Try Jar Service Provider Mechanism
220
Object JavaDoc provider = findJarServiceProvider(factoryId);
221         if (provider != null) {
222             return provider;
223         }
224
225         if (fallbackClassName == null) {
226             throw new ConfigurationError(
227                 "Provider for " + factoryId + " cannot be found", null);
228         }
229
230         if (DEBUG) debugPrintln("using fallback, value=" + fallbackClassName);
231         return newInstance(fallbackClassName, cl, true);
232     } // createObject(String,String,String):Object
233

234     //
235
// Private static methods
236
//
237

238     /** Prints a message to standard error if debugging is enabled. */
239     private static void debugPrintln(String JavaDoc msg) {
240         if (DEBUG) {
241             System.err.println("JAXP: " + msg);
242         }
243     } // debugPrintln(String)
244

245     /**
246      * Figure out which ClassLoader to use. For JDK 1.2 and later use
247      * the context ClassLoader.
248      */

249     static ClassLoader JavaDoc findClassLoader()
250         throws ConfigurationError
251     {
252         SecuritySupport ss = SecuritySupport.getInstance();
253
254         // Figure out which ClassLoader to use for loading the provider
255
// class. If there is a Context ClassLoader then use it.
256
ClassLoader JavaDoc context = ss.getContextClassLoader();
257         ClassLoader JavaDoc system = ss.getSystemClassLoader();
258
259         ClassLoader JavaDoc chain = system;
260         while (true) {
261             if (context == chain) {
262                 // Assert: we are on JDK 1.1 or we have no Context ClassLoader
263
// or any Context ClassLoader in chain of system classloader
264
// (including extension ClassLoader) so extend to widest
265
// ClassLoader (always look in system ClassLoader if Xerces
266
// is in boot/extension/system classpath and in current
267
// ClassLoader otherwise); normal classloaders delegate
268
// back to system ClassLoader first so this widening doesn't
269
// change the fact that context ClassLoader will be consulted
270
ClassLoader JavaDoc current = ObjectFactory.class.getClassLoader();
271
272                 chain = system;
273                 while (true) {
274                     if (current == chain) {
275                         // Assert: Current ClassLoader in chain of
276
// boot/extension/system ClassLoaders
277
return system;
278                     }
279                     if (chain == null) {
280                         break;
281                     }
282                     chain = ss.getParentClassLoader(chain);
283                 }
284
285                 // Assert: Current ClassLoader not in chain of
286
// boot/extension/system ClassLoaders
287
return current;
288             }
289
290             if (chain == null) {
291                 // boot ClassLoader reached
292
break;
293             }
294
295             // Check for any extension ClassLoaders in chain up to
296
// boot ClassLoader
297
chain = ss.getParentClassLoader(chain);
298         };
299
300         // Assert: Context ClassLoader not in chain of
301
// boot/extension/system ClassLoaders
302
return context;
303     } // findClassLoader():ClassLoader
304

305     /**
306      * Create an instance of a class using the specified ClassLoader
307      */

308     static Object JavaDoc newInstance(String JavaDoc className, ClassLoader JavaDoc cl,
309                                       boolean doFallback)
310         throws ConfigurationError
311     {
312         // assert(className != null);
313
try{
314             Class JavaDoc providerClass = findProviderClass(className, cl, doFallback);
315             Object JavaDoc instance = providerClass.newInstance();
316             if (DEBUG) debugPrintln("created new instance of " + providerClass +
317                    " using ClassLoader: " + cl);
318             return instance;
319         } catch (ClassNotFoundException JavaDoc x) {
320             throw new ConfigurationError(
321                 "Provider " + className + " not found", x);
322         } catch (Exception JavaDoc x) {
323             throw new ConfigurationError(
324                 "Provider " + className + " could not be instantiated: " + x,
325                 x);
326         }
327     }
328
329     /**
330      * Find a Class using the specified ClassLoader
331      */

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

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

473     /**
474      * A configuration error.
475      */

476     static class ConfigurationError
477         extends Error JavaDoc {
478
479         //
480
// Data
481
//
482

483         /** Exception. */
484         private Exception JavaDoc exception;
485
486         //
487
// Constructors
488
//
489

490         /**
491          * Construct a new instance with the specified detail string and
492          * exception.
493          */

494         ConfigurationError(String JavaDoc msg, Exception JavaDoc x) {
495             super(msg);
496             this.exception = x;
497         } // <init>(String,Exception)
498

499         //
500
// methods
501
//
502

503         /** Returns the exception associated to this error. */
504         Exception JavaDoc getException() {
505             return exception;
506         } // getException():Exception
507

508     } // class ConfigurationError
509

510 } // class ObjectFactory
511
Popular Tags