2
* Licensed to the Apache Software Foundation (ASF) under one
3
* or more contributor license agreements. See the NOTICE file
4
* distributed with this work for additional information
5
* regarding copyright ownership. The ASF licenses this file
6
* to you under the Apache License, Version 2.0 (the "License");
7
* you may not use this file except in compliance with the License.
8
* You may obtain a copy of the License at
10
* http://www.apache.org/licenses/LICENSE-2.0
12
* Unless required by applicable law or agreed to in writing, software
13
* distributed under the License is distributed on an "AS IS" BASIS,
14
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
* See the License for the specific language governing permissions and
16
* limitations under the License.
19
* $Id: ObjectFactory.java,v 1.2 2009/12/10 03:18:23 matthewoliver Exp $
22
package org.apache.xml.utils;
24
import java.io.InputStream;
25
import java.io.IOException;
27
import java.io.FileInputStream;
29
import java.util.Properties;
30
import java.io.BufferedReader;
31
import java.io.InputStreamReader;
34
* This class is duplicated for each JAXP subpackage so keep it in sync.
35
* It is package private and therefore is not exposed as part of the JAXP
38
* This code is designed to implement the JAXP 1.1 spec pluggability
39
* feature and is designed to run on JDK version 1.1 and
40
* later, and to compile on JDK 1.2 and onward.
41
* The code also runs both as part of an unbundled jar file and
42
* when bundled as part of the JDK.
44
* This class was moved from the <code>javax.xml.parsers.ObjectFactory</code>
45
* class and modified to be used as a general utility for creating objects
48
* @version $Id: ObjectFactory.java,v 1.2 2009/12/10 03:18:23 matthewoliver Exp $
56
// name of default properties file to look for in JDK's jre/lib directory
57
private static final String DEFAULT_PROPERTIES_FILENAME =
60
private static final String SERVICES_PATH = "META-INF/services/";
62
/** Set to true for debugging */
63
private static final boolean DEBUG = false;
65
/** cache the contents of the xalan.properties file.
66
* Until an attempt has been made to read this file, this will
67
* be null; if the file does not exist or we encounter some other error
68
* during the read, this will be empty.
70
private static Properties fXalanProperties = null;
73
* Cache the time stamp of the xalan.properties file so
74
* that we know if it's been modified and can invalidate
75
* the cache when necessary.
77
private static long fLastModified = -1;
80
// Public static methods
84
* Finds the implementation Class object in the specified order. The
85
* specified order is the following:
87
* <li>query the system property using <code>System.getProperty</code>
88
* <li>read <code>META-INF/services/<i>factoryId</i></code> file
89
* <li>use fallback classname
92
* @return instance of factory, never null
94
* @param factoryId Name of the factory to find, same as
96
* @param fallbackClassName Implementation class name, if nothing else
97
* is found. Use null to mean no fallback.
99
* @exception ObjectFactory.ConfigurationError
101
static Object createObject(String factoryId, String fallbackClassName)
102
throws ConfigurationError {
103
return createObject(factoryId, null, fallbackClassName);
104
} // createObject(String,String):Object
107
* Finds the implementation Class object in the specified order. The
108
* specified order is the following:
110
* <li>query the system property using <code>System.getProperty</code>
111
* <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
112
* <li>read <code>META-INF/services/<i>factoryId</i></code> file
113
* <li>use fallback classname
116
* @return instance of factory, never null
118
* @param factoryId Name of the factory to find, same as
120
* @param propertiesFilename The filename in the $java.home/lib directory
121
* of the properties file. If none specified,
122
* ${java.home}/lib/xalan.properties will be used.
123
* @param fallbackClassName Implementation class name, if nothing else
124
* is found. Use null to mean no fallback.
126
* @exception ObjectFactory.ConfigurationError
128
static Object createObject(String factoryId,
129
String propertiesFilename,
130
String fallbackClassName)
131
throws ConfigurationError
133
Class factoryClass = lookUpFactoryClass(factoryId,
137
if (factoryClass == null) {
138
throw new ConfigurationError(
139
"Provider for " + factoryId + " cannot be found", null);
143
Object instance = factoryClass.newInstance();
144
debugPrintln("created new instance of factory " + factoryId);
146
} catch (Exception x) {
147
throw new ConfigurationError(
148
"Provider for factory " + factoryId
149
+ " could not be instantiated: " + x, x);
151
} // createObject(String,String,String):Object
154
* Finds the implementation Class object in the specified order. The
155
* specified order is the following:
157
* <li>query the system property using <code>System.getProperty</code>
158
* <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
159
* <li>read <code>META-INF/services/<i>factoryId</i></code> file
160
* <li>use fallback classname
163
* @return Class object of factory, never null
165
* @param factoryId Name of the factory to find, same as
167
* @param propertiesFilename The filename in the $java.home/lib directory
168
* of the properties file. If none specified,
169
* ${java.home}/lib/xalan.properties will be used.
170
* @param fallbackClassName Implementation class name, if nothing else
171
* is found. Use null to mean no fallback.
173
* @exception ObjectFactory.ConfigurationError
175
static Class lookUpFactoryClass(String factoryId)
176
throws ConfigurationError
178
return lookUpFactoryClass(factoryId, null, null);
179
} // lookUpFactoryClass(String):Class
182
* Finds the implementation Class object in the specified order. The
183
* specified order is the following:
185
* <li>query the system property using <code>System.getProperty</code>
186
* <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
187
* <li>read <code>META-INF/services/<i>factoryId</i></code> file
188
* <li>use fallback classname
191
* @return Class object that provides factory service, never null
193
* @param factoryId Name of the factory to find, same as
195
* @param propertiesFilename The filename in the $java.home/lib directory
196
* of the properties file. If none specified,
197
* ${java.home}/lib/xalan.properties will be used.
198
* @param fallbackClassName Implementation class name, if nothing else
199
* is found. Use null to mean no fallback.
201
* @exception ObjectFactory.ConfigurationError
203
static Class lookUpFactoryClass(String factoryId,
204
String propertiesFilename,
205
String fallbackClassName)
206
throws ConfigurationError
208
String factoryClassName = lookUpFactoryClassName(factoryId,
211
ClassLoader cl = findClassLoader();
213
if (factoryClassName == null) {
214
factoryClassName = fallbackClassName;
217
// assert(className != null);
219
Class providerClass = findProviderClass(factoryClassName,
222
debugPrintln("created new instance of " + providerClass +
223
" using ClassLoader: " + cl);
224
return providerClass;
225
} catch (ClassNotFoundException x) {
226
throw new ConfigurationError(
227
"Provider " + factoryClassName + " not found", x);
228
} catch (Exception x) {
229
throw new ConfigurationError(
230
"Provider "+factoryClassName+" could not be instantiated: "+x,
233
} // lookUpFactoryClass(String,String,String):Class
236
* Finds the name of the required implementation class in the specified
237
* order. The specified order is the following:
239
* <li>query the system property using <code>System.getProperty</code>
240
* <li>read <code>$java.home/lib/<i>propertiesFilename</i></code> file
241
* <li>read <code>META-INF/services/<i>factoryId</i></code> file
242
* <li>use fallback classname
245
* @return name of class that provides factory service, never null
247
* @param factoryId Name of the factory to find, same as
249
* @param propertiesFilename The filename in the $java.home/lib directory
250
* of the properties file. If none specified,
251
* ${java.home}/lib/xalan.properties will be used.
252
* @param fallbackClassName Implementation class name, if nothing else
253
* is found. Use null to mean no fallback.
255
* @exception ObjectFactory.ConfigurationError
257
static String lookUpFactoryClassName(String factoryId,
258
String propertiesFilename,
259
String fallbackClassName)
261
SecuritySupport ss = SecuritySupport.getInstance();
263
// Use the system property first
265
String systemProp = ss.getSystemProperty(factoryId);
266
if (systemProp != null) {
267
debugPrintln("found system property, value=" + systemProp);
270
} catch (SecurityException se) {
271
// Ignore and continue w/ next location
274
// Try to read from propertiesFilename, or
275
// $java.home/lib/xalan.properties
276
String factoryClassName = null;
277
// no properties file name specified; use
278
// $JAVA_HOME/lib/xalan.properties:
279
if (propertiesFilename == null) {
280
File propertiesFile = null;
281
boolean propertiesFileExists = false;
283
String javah = ss.getSystemProperty("java.home");
284
propertiesFilename = javah + File.separator +
285
"lib" + File.separator + DEFAULT_PROPERTIES_FILENAME;
286
propertiesFile = new File(propertiesFilename);
287
propertiesFileExists = ss.getFileExists(propertiesFile);
288
} catch (SecurityException e) {
291
fXalanProperties = null;
294
synchronized (ObjectFactory.class) {
295
boolean loadProperties = false;
296
FileInputStream fis = null;
298
// file existed last time
299
if(fLastModified >= 0) {
300
if(propertiesFileExists &&
301
(fLastModified < (fLastModified = ss.getLastModified(propertiesFile)))) {
302
loadProperties = true;
304
// file has stopped existing...
305
if(!propertiesFileExists) {
307
fXalanProperties = null;
308
} // else, file wasn't modified!
311
// file has started to exist:
312
if(propertiesFileExists) {
313
loadProperties = true;
314
fLastModified = ss.getLastModified(propertiesFile);
315
} // else, nothing's changed
318
// must never have attempted to read xalan.properties
319
// before (or it's outdeated)
320
fXalanProperties = new Properties();
321
fis = ss.getFileInputStream(propertiesFile);
322
fXalanProperties.load(fis);
324
} catch (Exception x) {
325
fXalanProperties = null;
327
// assert(x instanceof FileNotFoundException
328
// || x instanceof SecurityException)
329
// In both cases, ignore and continue w/ next location
332
// try to close the input stream if one was opened.
337
// Ignore the exception.
338
catch (IOException exc) {}
342
if(fXalanProperties != null) {
343
factoryClassName = fXalanProperties.getProperty(factoryId);
346
FileInputStream fis = null;
348
fis = ss.getFileInputStream(new File(propertiesFilename));
349
Properties props = new Properties();
351
factoryClassName = props.getProperty(factoryId);
352
} catch (Exception x) {
353
// assert(x instanceof FileNotFoundException
354
// || x instanceof SecurityException)
355
// In both cases, ignore and continue w/ next location
358
// try to close the input stream if one was opened.
363
// Ignore the exception.
364
catch (IOException exc) {}
368
if (factoryClassName != null) {
369
debugPrintln("found in " + propertiesFilename + ", value="
371
return factoryClassName;
374
// Try Jar Service Provider Mechanism
375
return findJarServiceProviderName(factoryId);
376
} // lookUpFactoryClass(String,String):String
379
// Private static methods
382
/** Prints a message to standard error if debugging is enabled. */
383
private static void debugPrintln(String msg) {
385
System.err.println("JAXP: " + msg);
387
} // debugPrintln(String)
390
* Figure out which ClassLoader to use. For JDK 1.2 and later use
391
* the context ClassLoader.
393
static ClassLoader findClassLoader()
394
throws ConfigurationError
396
SecuritySupport ss = SecuritySupport.getInstance();
398
// Figure out which ClassLoader to use for loading the provider
399
// class. If there is a Context ClassLoader then use it.
400
ClassLoader context = ss.getContextClassLoader();
401
ClassLoader system = ss.getSystemClassLoader();
403
ClassLoader chain = system;
405
if (context == chain) {
406
// Assert: we are on JDK 1.1 or we have no Context ClassLoader
407
// or any Context ClassLoader in chain of system classloader
408
// (including extension ClassLoader) so extend to widest
409
// ClassLoader (always look in system ClassLoader if Xalan
410
// is in boot/extension/system classpath and in current
411
// ClassLoader otherwise); normal classloaders delegate
412
// back to system ClassLoader first so this widening doesn't
413
// change the fact that context ClassLoader will be consulted
414
ClassLoader current = ObjectFactory.class.getClassLoader();
418
if (current == chain) {
419
// Assert: Current ClassLoader in chain of
420
// boot/extension/system ClassLoaders
426
chain = ss.getParentClassLoader(chain);
429
// Assert: Current ClassLoader not in chain of
430
// boot/extension/system ClassLoaders
435
// boot ClassLoader reached
439
// Check for any extension ClassLoaders in chain up to
441
chain = ss.getParentClassLoader(chain);
444
// Assert: Context ClassLoader not in chain of
445
// boot/extension/system ClassLoaders
447
} // findClassLoader():ClassLoader
450
* Create an instance of a class using the specified ClassLoader
452
static Object newInstance(String className, ClassLoader cl,
454
throws ConfigurationError
456
// assert(className != null);
458
Class providerClass = findProviderClass(className, cl, doFallback);
459
Object instance = providerClass.newInstance();
460
debugPrintln("created new instance of " + providerClass +
461
" using ClassLoader: " + cl);
463
} catch (ClassNotFoundException x) {
464
throw new ConfigurationError(
465
"Provider " + className + " not found", x);
466
} catch (Exception x) {
467
throw new ConfigurationError(
468
"Provider " + className + " could not be instantiated: " + x,
474
* Find a Class using the specified ClassLoader
476
static Class findProviderClass(String className, ClassLoader cl,
478
throws ClassNotFoundException, ConfigurationError
480
//throw security exception if the calling thread is not allowed to access the
481
//class. Restrict the access to the package classes as specified in java.security policy.
482
SecurityManager security = System.getSecurityManager();
484
if (security != null){
485
final int lastDot = className.lastIndexOf(".");
486
String packageName = className;
487
if (lastDot != -1) packageName = className.substring(0, lastDot);
488
security.checkPackageAccess(packageName);
490
}catch(SecurityException e){
496
// XXX Use the bootstrap ClassLoader. There is no way to
497
// load a class using the bootstrap ClassLoader that works
498
// in both JDK 1.1 and Java 2. However, this should still
499
// work b/c the following should be true:
501
// (cl == null) iff current ClassLoader == null
503
// Thus Class.forName(String) will use the current
504
// ClassLoader which will be the bootstrap ClassLoader.
505
providerClass = Class.forName(className);
508
providerClass = cl.loadClass(className);
509
} catch (ClassNotFoundException x) {
511
// Fall back to current classloader
512
ClassLoader current = ObjectFactory.class.getClassLoader();
513
if (current == null) {
514
providerClass = Class.forName(className);
515
} else if (cl != current) {
517
providerClass = cl.loadClass(className);
527
return providerClass;
531
* Find the name of service provider using Jar Service Provider Mechanism
533
* @return instance of provider class if found or null
535
private static String findJarServiceProviderName(String factoryId)
537
SecuritySupport ss = SecuritySupport.getInstance();
538
String serviceId = SERVICES_PATH + factoryId;
539
InputStream is = null;
541
// First try the Context ClassLoader
542
ClassLoader cl = findClassLoader();
544
is = ss.getResourceAsStream(cl, serviceId);
546
// If no provider found then try the current ClassLoader
548
ClassLoader current = ObjectFactory.class.getClassLoader();
551
is = ss.getResourceAsStream(cl, serviceId);
560
debugPrintln("found jar resource=" + serviceId +
561
" using ClassLoader: " + cl);
563
// Read the service provider name in UTF-8 as specified in
564
// the jar spec. Unfortunately this fails in Microsoft
565
// VJ++, which does not implement the UTF-8
566
// encoding. Theoretically, we should simply let it fail in
567
// that case, since the JVM is obviously broken if it
568
// doesn't support such a basic standard. But since there
569
// are still some users attempting to use VJ++ for
570
// development, we have dropped in a fallback which makes a
571
// second attempt using the platform's default encoding. In
572
// VJ++ this is apparently ASCII, which is a subset of
573
// UTF-8... and since the strings we'll be reading here are
574
// also primarily limited to the 7-bit ASCII range (at
575
// least, in English versions), this should work well
576
// enough to keep us on the air until we're ready to
577
// officially decommit from VJ++. [Edited comment from
581
rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
582
} catch (java.io.UnsupportedEncodingException e) {
583
rd = new BufferedReader(new InputStreamReader(is));
586
String factoryClassName = null;
588
// XXX Does not handle all possible input as specified by the
589
// Jar Service Provider specification
590
factoryClassName = rd.readLine();
591
} catch (IOException x) {
597
// try to close the reader.
600
// Ignore the exception.
601
catch (IOException exc) {}
604
if (factoryClassName != null &&
605
! "".equals(factoryClassName)) {
606
debugPrintln("found in resource, value="
609
// Note: here we do not want to fall back to the current
610
// ClassLoader because we want to avoid the case where the
611
// resource file was found using one ClassLoader and the
612
// provider class was instantiated using a different one.
613
return factoryClassName;
625
* A configuration error.
627
static class ConfigurationError
629
static final long serialVersionUID = 2036619216663421552L;
635
private Exception exception;
642
* Construct a new instance with the specified detail string and
645
ConfigurationError(String msg, Exception x) {
648
} // <init>(String,Exception)
654
/** Returns the exception associated to this error. */
655
Exception getException() {
657
} // getException():Exception
659
} // class ConfigurationError
661
} // class ObjectFactory