blob: 6264feefbf933f3e4e82e3a9029a7f757462991e [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package javax.xml.ws.spi;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.PrivilegedAction;
import java.util.Properties;
/**
* This code is designed to implement the pluggability
* feature and is designed to both compile and run on JDK version 1.1 and
* later. The code also runs both as part of an unbundled jar file and
* when bundled as part of the JDK.
* <p/>
* This class is duplicated for each subpackage so keep it in sync.
* It is package private and therefore is not exposed as part of the JAXRPC
* API.
*/
class FactoryFinder {
/**
* Set to true for debugging.
*/
private static final boolean debug = false;
private static void debugPrintln(String msg) {
if (debug) {
System.err.println("Factory Finder:" + msg);
}
}
/**
* Figure out which ClassLoader to use. For JDK 1.2 and later use
* the context ClassLoader.
*
* @return the <code>ClassLoader</code>
* @throws ConfigurationError if this class is unable to work with the
* host JDK
*/
private static ClassLoader findClassLoader()
throws ConfigurationError {
// REVIEW This doPriv block may be unnecessary because this method is private and
// the caller already has a doPriv. I added the doPriv in case someone changes the
// visibility of this method to non-private.
ClassLoader cl = (ClassLoader)
doPrivileged( new PrivilegedAction() {
public Object run() {
Method m = null;
try {
m = Thread.class.getMethod("getContextClassLoader", (Class []) null);
} catch (NoSuchMethodException e) {
// Assume that we are running JDK 1.1, use the current ClassLoader
debugPrintln("assuming JDK 1.1");
if (FactoryFinder.class.getClassLoader() == null) {
return ClassLoader.getSystemClassLoader();
}
return FactoryFinder.class.getClassLoader();
}
try {
return (ClassLoader) m.invoke(Thread.currentThread(), (Object []) null);
} catch (IllegalAccessException e) {
// assert(false)
throw new ConfigurationError("Unexpected IllegalAccessException",
e);
} catch (InvocationTargetException e) {
// assert(e.getTargetException() instanceof SecurityException)
throw new ConfigurationError("Unexpected InvocationTargetException",
e);
}
}
}
);
return cl;
}
/**
* Create an instance of a class using the specified
* <code>ClassLoader</code>, or if that fails from the
* <code>ClassLoader</code> that loaded this class.
*
* @param className the name of the class to instantiate
* @param classLoader a <code>ClassLoader</code> to load the class from
* @return a new <code>Object</code> that is an instance of the class of
* the given name from the given class loader
* @throws ConfigurationError if the class could not be found or
* instantiated
*/
private static Object newInstance(String className,
ClassLoader classLoader)
throws ConfigurationError {
final ClassLoader iClassLoader = classLoader;
final String iClassName = className;
// REVIEW This doPriv block may be unnecessary because this method is private and
// the caller already has a doPriv. I added the doPriv in case someone changes the
// visibility of this method to non-private.
Object obj =
doPrivileged( new PrivilegedAction() {
public Object run() {
try {
if (iClassLoader != null) {
try {
return iClassLoader.loadClass(iClassName).newInstance();
} catch (ClassNotFoundException x) {
// try again
}
}
return Class.forName(iClassName).newInstance();
} catch (ClassNotFoundException x) {
throw new ConfigurationError(
"Provider " + iClassName + " not found", x);
} catch (Exception x) {
throw new ConfigurationError(
"Provider " + iClassName + " could not be instantiated: " + x,
x);
}
}
});
return obj;
}
/**
* Finds the implementation Class object in the specified order. Main
* entry point.
*
* @param factoryId Name of the factory to find, same as
* a property name
* @param fallbackClassName Implementation class name, if nothing else
* is found. Use null to mean no fallback.
* @return Class object of factory, never null
* @throws FactoryFinder.ConfigurationError
* Package private so this code can be shared.
*/
static Object find(String factoryId, String fallbackClassName)
throws ConfigurationError {
final String iFactoryId = factoryId;
final String iFallbackClassName = fallbackClassName;
Object obj =
doPrivileged( new PrivilegedAction() {
public Object run() {
debugPrintln("debug is on");
ClassLoader classLoader = findClassLoader();
try {
// If we are deployed into an OSGi environment, leverage it
Class factoryClass;
if (FactoryFinder.class.getClassLoader() == null) {
factoryClass = Class.forName(iFactoryId);
} else {
factoryClass = FactoryFinder.class.getClassLoader().loadClass(iFactoryId);
}
Class spiClass = org.apache.servicemix.specs.locator.OsgiLocator.locate(factoryClass, iFactoryId);
if (spiClass != null) {
return spiClass.newInstance();
}
} catch (Throwable e) {
}
// Use the system property first
try {
String systemProp =
System.getProperty(iFactoryId);
if (systemProp != null) {
debugPrintln("found system property " + systemProp);
return newInstance(systemProp, classLoader);
}
} catch (SecurityException se) {
}
// try to read from $java.home/lib/xml.properties
try {
String javah = System.getProperty("java.home");
String configFile = javah + File.separator +
"lib" + File.separator + "jaxrpc.properties";
File f = new File(configFile);
if (f.exists()) {
Properties props = new Properties();
props.load(new FileInputStream(f));
String factoryClassName = props.getProperty(iFactoryId);
debugPrintln("found java.home property " + factoryClassName);
return newInstance(factoryClassName, classLoader);
}
} catch (Exception ex) {
if (debug) ex.printStackTrace();
}
String serviceId = "META-INF/services/" + iFactoryId;
// try to find services in CLASSPATH
try {
InputStream is = null;
if (classLoader == null) {
is = ClassLoader.getSystemResourceAsStream(serviceId);
} else {
is = classLoader.getResourceAsStream(serviceId);
}
if (is != null) {
debugPrintln("found " + serviceId);
// Read the service provider name in UTF-8 as specified in
// the jar spec. Unfortunately this fails in Microsoft
// VJ++, which does not implement the UTF-8
// encoding. Theoretically, we should simply let it fail in
// that case, since the JVM is obviously broken if it
// doesn't support such a basic standard. But since there
// are still some users attempting to use VJ++ for
// development, we have dropped in a fallback which makes a
// second attempt using the platform's default encoding. In
// VJ++ this is apparently ASCII, which is a subset of
// UTF-8... and since the strings we'll be reading here are
// also primarily limited to the 7-bit ASCII range (at
// least, in English versions), this should work well
// enough to keep us on the air until we're ready to
// officially decommit from VJ++. [Edited comment from
// jkesselm]
BufferedReader rd;
try {
rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
} catch (java.io.UnsupportedEncodingException e) {
rd = new BufferedReader(new InputStreamReader(is));
}
String factoryClassName = rd.readLine();
rd.close();
if (factoryClassName != null &&
! "".equals(factoryClassName)) {
debugPrintln("loaded from services: " + factoryClassName);
return newInstance(factoryClassName, classLoader);
}
}
} catch (Exception ex) {
if (debug) ex.printStackTrace();
}
if (iFallbackClassName == null) {
throw new ConfigurationError(
"Provider for " + iFactoryId + " cannot be found", null);
}
debugPrintln("loaded from fallback value: " + iFallbackClassName);
return newInstance(iFallbackClassName, classLoader);
}
});
return obj;
}
private static Object doPrivileged(PrivilegedAction action) {
SecurityManager sm = System.getSecurityManager();
if (sm == null) {
return(action.run());
} else {
return java.security.AccessController.doPrivileged(action);
}
}
static class ConfigurationError extends Error {
// fixme: should this be refactored to use the jdk1.4 exception
// wrapping?
private Exception exception;
/**
* Construct a new instance with the specified detail string and
* exception.
*
* @param msg the Message for this error
* @param x an Exception that caused this failure, or null
*/
ConfigurationError(String msg, Exception x) {
super(msg);
this.exception = x;
}
Exception getException() {
return exception;
}
}
}