View Javadoc

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