View Javadoc

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 }