001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package javax.xml.ws.spi;
020:
021: import org.apache.commons.logging.Log;
022: import org.apache.commons.logging.LogFactory;
023:
024: import java.io.BufferedReader;
025: import java.io.File;
026: import java.io.FileInputStream;
027: import java.io.InputStream;
028: import java.io.InputStreamReader;
029: import java.lang.reflect.InvocationTargetException;
030: import java.lang.reflect.Method;
031: import java.security.PrivilegedAction;
032: import java.util.Properties;
033:
034: /**
035: * This code is designed to implement the pluggability
036: * feature and is designed to both compile and run on JDK version 1.1 and
037: * later. The code also runs both as part of an unbundled jar file and
038: * when bundled as part of the JDK.
039: * <p/>
040: * This class is duplicated for each subpackage so keep it in sync.
041: * It is package private and therefore is not exposed as part of the JAXRPC
042: * API.
043: */
044: class FactoryFinder {
045: /**
046: * Set to true for debugging.
047: */
048: private static final Log log = LogFactory
049: .getLog(FactoryFinder.class);
050: private static final boolean debug = false;
051:
052: private static void debugPrintln(String msg) {
053: if (debug && log.isDebugEnabled()) {
054: log.debug("Factory Finder:" + msg);
055: }
056: }
057:
058: /**
059: * Figure out which ClassLoader to use. For JDK 1.2 and later use
060: * the context ClassLoader.
061: *
062: * @return the <code>ClassLoader</code>
063: * @throws ConfigurationError if this class is unable to work with the
064: * host JDK
065: */
066: private static ClassLoader findClassLoader()
067: throws ConfigurationError {
068: // REVIEW This doPriv block may be unnecessary because this method is private and
069: // the caller already has a doPriv. I added the doPriv in case someone changes the
070: // visibility of this method to non-private.
071: ClassLoader cl = (ClassLoader) doPrivileged(new PrivilegedAction() {
072: public Object run() {
073:
074: Method m = null;
075:
076: try {
077:
078: m = Thread.class.getMethod("getContextClassLoader",
079: (Class[]) null);
080: } catch (NoSuchMethodException e) {
081: // Assume that we are running JDK 1.1, use the current ClassLoader
082: debugPrintln("assuming JDK 1.1");
083: return FactoryFinder.class.getClassLoader();
084: }
085:
086: try {
087: return (ClassLoader) m.invoke(Thread
088: .currentThread(), (Object[]) null);
089: } catch (IllegalAccessException e) {
090: // assert(false)
091: throw new ConfigurationError(
092: "Unexpected IllegalAccessException", e);
093: } catch (InvocationTargetException e) {
094: // assert(e.getTargetException() instanceof SecurityException)
095: throw new ConfigurationError(
096: "Unexpected InvocationTargetException", e);
097: }
098: }
099: });
100: return cl;
101:
102: }
103:
104: /**
105: * Create an instance of a class using the specified
106: * <code>ClassLoader</code>, or if that fails from the
107: * <code>ClassLoader</code> that loaded this class.
108: *
109: * @param className the name of the class to instantiate
110: * @param classLoader a <code>ClassLoader</code> to load the class from
111: * @return a new <code>Object</code> that is an instance of the class of
112: * the given name from the given class loader
113: * @throws ConfigurationError if the class could not be found or
114: * instantiated
115: */
116: private static Object newInstance(String className,
117: ClassLoader classLoader) throws ConfigurationError {
118:
119: final ClassLoader iClassLoader = classLoader;
120: final String iClassName = className;
121:
122: // REVIEW This doPriv block may be unnecessary because this method is private and
123: // the caller already has a doPriv. I added the doPriv in case someone changes the
124: // visibility of this method to non-private.
125: Object obj = doPrivileged(new PrivilegedAction() {
126: public Object run() {
127: try {
128: if (iClassLoader != null) {
129: try {
130: return iClassLoader.loadClass(iClassName)
131: .newInstance();
132: } catch (ClassNotFoundException x) {
133: // try again
134: }
135: }
136: return Class.forName(iClassName).newInstance();
137: } catch (ClassNotFoundException x) {
138: throw new ConfigurationError("Provider "
139: + iClassName + " not found", x);
140: } catch (Exception x) {
141: throw new ConfigurationError("Provider "
142: + iClassName
143: + " could not be instantiated: " + x, x);
144: }
145: }
146: });
147: return obj;
148: }
149:
150: /**
151: * Finds the implementation Class object in the specified order. Main
152: * entry point.
153: *
154: * @param factoryId Name of the factory to find, same as
155: * a property name
156: * @param fallbackClassName Implementation class name, if nothing else
157: * is found. Use null to mean no fallback.
158: * @return Class object of factory, never null
159: * @throws FactoryFinder.ConfigurationError
160: * Package private so this code can be shared.
161: */
162: static Object find(String factoryId, String fallbackClassName)
163: throws ConfigurationError {
164:
165: final String iFactoryId = factoryId;
166: final String iFallbackClassName = fallbackClassName;
167:
168: Object obj = doPrivileged(new PrivilegedAction() {
169: public Object run() {
170: debugPrintln("debug is on");
171:
172: ClassLoader classLoader = findClassLoader();
173:
174: // Use the system property first
175: try {
176: String systemProp = System.getProperty(iFactoryId);
177: if (systemProp != null) {
178: debugPrintln("found system property "
179: + systemProp);
180: return newInstance(systemProp, classLoader);
181: }
182: } catch (SecurityException se) {
183: }
184:
185: // try to read from $java.home/lib/xml.properties
186: try {
187: String javah = System.getProperty("java.home");
188: String configFile = javah + File.separator + "lib"
189: + File.separator + "jaxrpc.properties";
190: File f = new File(configFile);
191: if (f.exists()) {
192: Properties props = new Properties();
193: props.load(new FileInputStream(f));
194: String factoryClassName = props
195: .getProperty(iFactoryId);
196: debugPrintln("found java.home property "
197: + factoryClassName);
198: return newInstance(factoryClassName,
199: classLoader);
200: }
201: } catch (Exception ex) {
202: if (debug)
203: ex.printStackTrace();
204: }
205:
206: String serviceId = "META-INF/services/" + iFactoryId;
207: // try to find services in CLASSPATH
208: try {
209: InputStream is = null;
210: if (classLoader == null) {
211: is = ClassLoader
212: .getSystemResourceAsStream(serviceId);
213: } else {
214: is = classLoader.getResourceAsStream(serviceId);
215: }
216:
217: if (is != null) {
218: debugPrintln("found " + serviceId);
219:
220: // Read the service provider name in UTF-8 as specified in
221: // the jar spec. Unfortunately this fails in Microsoft
222: // VJ++, which does not implement the UTF-8
223: // encoding. Theoretically, we should simply let it fail in
224: // that case, since the JVM is obviously broken if it
225: // doesn't support such a basic standard. But since there
226: // are still some users attempting to use VJ++ for
227: // development, we have dropped in a fallback which makes a
228: // second attempt using the platform's default encoding. In
229: // VJ++ this is apparently ASCII, which is a subset of
230: // UTF-8... and since the strings we'll be reading here are
231: // also primarily limited to the 7-bit ASCII range (at
232: // least, in English versions), this should work well
233: // enough to keep us on the air until we're ready to
234: // officially decommit from VJ++. [Edited comment from
235: // jkesselm]
236: BufferedReader rd;
237: try {
238: rd = new BufferedReader(
239: new InputStreamReader(is, "UTF-8"));
240: } catch (java.io.UnsupportedEncodingException e) {
241: rd = new BufferedReader(
242: new InputStreamReader(is));
243: }
244:
245: String factoryClassName = rd.readLine();
246: rd.close();
247:
248: if (factoryClassName != null
249: && !"".equals(factoryClassName)) {
250: debugPrintln("loaded from services: "
251: + factoryClassName);
252: return newInstance(factoryClassName,
253: classLoader);
254: }
255: }
256: } catch (Exception ex) {
257: if (debug)
258: ex.printStackTrace();
259: }
260:
261: if (iFallbackClassName == null) {
262: throw new ConfigurationError("Provider for "
263: + iFactoryId + " cannot be found", null);
264: }
265:
266: debugPrintln("loaded from fallback value: "
267: + iFallbackClassName);
268: return newInstance(iFallbackClassName, classLoader);
269: }
270: });
271: return obj;
272: }
273:
274: private static Object doPrivileged(PrivilegedAction action) {
275: SecurityManager sm = System.getSecurityManager();
276: if (sm == null) {
277: return (action.run());
278: } else {
279: return java.security.AccessController.doPrivileged(action);
280: }
281: }
282:
283: static class ConfigurationError extends Error {
284: // fixme: should this be refactored to use the jdk1.4 exception
285: // wrapping?
286:
287: private Exception exception;
288:
289: /**
290: * Construct a new instance with the specified detail string and
291: * exception.
292: *
293: * @param msg the Message for this error
294: * @param x an Exception that caused this failure, or null
295: */
296: ConfigurationError(String msg, Exception x) {
297: super (msg);
298: this .exception = x;
299: }
300:
301: Exception getException() {
302: return exception;
303: }
304: }
305: }
|