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 }