1 /*
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
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20 package javax.xml.ws.spi;
21
22 import java.io.BufferedReader;
23 import java.io.File;
24 import java.io.FileInputStream;
25 import java.io.InputStream;
26 import java.io.InputStreamReader;
27 import java.lang.reflect.InvocationTargetException;
28 import java.lang.reflect.Method;
29 import java.security.PrivilegedAction;
30 import java.util.Properties;
31
32 /**
33 * This code is designed to implement the pluggability
34 * feature and is designed to both compile and run on JDK version 1.1 and
35 * later. The code also runs both as part of an unbundled jar file and
36 * when bundled as part of the JDK.
37 * <p/>
38 * This class is duplicated for each subpackage so keep it in sync.
39 * It is package private and therefore is not exposed as part of the JAXRPC
40 * API.
41 */
42 class FactoryFinder {
43 /**
44 * Set to true for debugging.
45 */
46 private static final boolean debug = false;
47
48 private static void debugPrintln(String msg) {
49 if (debug) {
50 System.err.println("Factory Finder:" + msg);
51 }
52 }
53
54 /**
55 * Figure out which ClassLoader to use. For JDK 1.2 and later use
56 * the context ClassLoader.
57 *
58 * @return the <code>ClassLoader</code>
59 * @throws ConfigurationError if this class is unable to work with the
60 * host JDK
61 */
62 private static ClassLoader findClassLoader()
63 throws ConfigurationError {
64 // REVIEW This doPriv block may be unnecessary because this method is private and
65 // the caller already has a doPriv. I added the doPriv in case someone changes the
66 // visibility of this method to non-private.
67 ClassLoader cl = (ClassLoader)
68 doPrivileged( new PrivilegedAction() {
69 public Object run() {
70
71 Method m = null;
72
73 try {
74
75 m = Thread.class.getMethod("getContextClassLoader", (Class []) null);
76 } catch (NoSuchMethodException e) {
77 // Assume that we are running JDK 1.1, use the current ClassLoader
78 debugPrintln("assuming JDK 1.1");
79 return FactoryFinder.class.getClassLoader();
80 }
81
82 try {
83 return (ClassLoader) m.invoke(Thread.currentThread(), (Object []) null);
84 } catch (IllegalAccessException e) {
85 // assert(false)
86 throw new ConfigurationError("Unexpected IllegalAccessException",
87 e);
88 } catch (InvocationTargetException e) {
89 // assert(e.getTargetException() instanceof SecurityException)
90 throw new ConfigurationError("Unexpected InvocationTargetException",
91 e);
92 }
93 }
94 }
95 );
96 return cl;
97
98 }
99
100 /**
101 * Create an instance of a class using the specified
102 * <code>ClassLoader</code>, or if that fails from the
103 * <code>ClassLoader</code> that loaded this class.
104 *
105 * @param className the name of the class to instantiate
106 * @param classLoader a <code>ClassLoader</code> to load the class from
107 * @return a new <code>Object</code> that is an instance of the class of
108 * the given name from the given class loader
109 * @throws ConfigurationError if the class could not be found or
110 * instantiated
111 */
112 private static Object newInstance(String className,
113 ClassLoader classLoader)
114 throws ConfigurationError {
115
116 final ClassLoader iClassLoader = classLoader;
117 final String iClassName = className;
118
119 // REVIEW This doPriv block may be unnecessary because this method is private and
120 // the caller already has a doPriv. I added the doPriv in case someone changes the
121 // visibility of this method to non-private.
122 Object obj =
123 doPrivileged( new PrivilegedAction() {
124 public Object run() {
125 try {
126 if (iClassLoader != null) {
127 try {
128 return iClassLoader.loadClass(iClassName).newInstance();
129 } catch (ClassNotFoundException x) {
130 // try again
131 }
132 }
133 return Class.forName(iClassName).newInstance();
134 } catch (ClassNotFoundException x) {
135 throw new ConfigurationError(
136 "Provider " + iClassName + " not found", x);
137 } catch (Exception x) {
138 throw new ConfigurationError(
139 "Provider " + iClassName + " could not be instantiated: " + x,
140 x);
141 }
142 }
143 });
144 return obj;
145 }
146
147 /**
148 * Finds the implementation Class object in the specified order. Main
149 * entry point.
150 *
151 * @param factoryId Name of the factory to find, same as
152 * a property name
153 * @param fallbackClassName Implementation class name, if nothing else
154 * is found. Use null to mean no fallback.
155 * @return Class object of factory, never null
156 * @throws FactoryFinder.ConfigurationError
157 * Package private so this code can be shared.
158 */
159 static Object find(String factoryId, String fallbackClassName)
160 throws ConfigurationError {
161
162 final String iFactoryId = factoryId;
163 final String iFallbackClassName = fallbackClassName;
164
165 Object obj =
166 doPrivileged( new PrivilegedAction() {
167 public Object run() {
168 debugPrintln("debug is on");
169
170 ClassLoader classLoader = findClassLoader();
171
172 // Use the system property first
173 try {
174 String systemProp =
175 System.getProperty(iFactoryId);
176 if (systemProp != null) {
177 debugPrintln("found system property " + systemProp);
178 return newInstance(systemProp, classLoader);
179 }
180 } catch (SecurityException se) {
181 }
182
183 // try to read from $java.home/lib/xml.properties
184 try {
185 String javah = System.getProperty("java.home");
186 String configFile = javah + File.separator +
187 "lib" + File.separator + "jaxrpc.properties";
188 File f = new File(configFile);
189 if (f.exists()) {
190 Properties props = new Properties();
191 props.load(new FileInputStream(f));
192 String factoryClassName = props.getProperty(iFactoryId);
193 debugPrintln("found java.home property " + factoryClassName);
194 return newInstance(factoryClassName, classLoader);
195 }
196 } catch (Exception ex) {
197 if (debug) ex.printStackTrace();
198 }
199
200 String serviceId = "META-INF/services/" + iFactoryId;
201 // try to find services in CLASSPATH
202 try {
203 InputStream is = null;
204 if (classLoader == null) {
205 is = ClassLoader.getSystemResourceAsStream(serviceId);
206 } else {
207 is = classLoader.getResourceAsStream(serviceId);
208 }
209
210 if (is != null) {
211 debugPrintln("found " + serviceId);
212
213 // Read the service provider name in UTF-8 as specified in
214 // the jar spec. Unfortunately this fails in Microsoft
215 // VJ++, which does not implement the UTF-8
216 // encoding. Theoretically, we should simply let it fail in
217 // that case, since the JVM is obviously broken if it
218 // doesn't support such a basic standard. But since there
219 // are still some users attempting to use VJ++ for
220 // development, we have dropped in a fallback which makes a
221 // second attempt using the platform's default encoding. In
222 // VJ++ this is apparently ASCII, which is a subset of
223 // UTF-8... and since the strings we'll be reading here are
224 // also primarily limited to the 7-bit ASCII range (at
225 // least, in English versions), this should work well
226 // enough to keep us on the air until we're ready to
227 // officially decommit from VJ++. [Edited comment from
228 // jkesselm]
229 BufferedReader rd;
230 try {
231 rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
232 } catch (java.io.UnsupportedEncodingException e) {
233 rd = new BufferedReader(new InputStreamReader(is));
234 }
235
236 String factoryClassName = rd.readLine();
237 rd.close();
238
239 if (factoryClassName != null &&
240 ! "".equals(factoryClassName)) {
241 debugPrintln("loaded from services: " + factoryClassName);
242 return newInstance(factoryClassName, classLoader);
243 }
244 }
245 } catch (Exception ex) {
246 if (debug) ex.printStackTrace();
247 }
248
249 if (iFallbackClassName == null) {
250 throw new ConfigurationError(
251 "Provider for " + iFactoryId + " cannot be found", null);
252 }
253
254 debugPrintln("loaded from fallback value: " + iFallbackClassName);
255 return newInstance(iFallbackClassName, classLoader);
256 }
257 });
258 return obj;
259 }
260
261 private static Object doPrivileged(PrivilegedAction action) {
262 SecurityManager sm = System.getSecurityManager();
263 if (sm == null) {
264 return(action.run());
265 } else {
266 return java.security.AccessController.doPrivileged(action);
267 }
268 }
269
270 static class ConfigurationError extends Error {
271 // fixme: should this be refactored to use the jdk1.4 exception
272 // wrapping?
273
274 private Exception exception;
275
276 /**
277 * Construct a new instance with the specified detail string and
278 * exception.
279 *
280 * @param msg the Message for this error
281 * @param x an Exception that caused this failure, or null
282 */
283 ConfigurationError(String msg, Exception x) {
284 super(msg);
285 this.exception = x;
286 }
287
288 Exception getException() {
289 return exception;
290 }
291 }
292 }