1 /* 2 * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.xml.stream; 27 28 import java.io.BufferedReader; 29 import java.io.File; 30 import java.io.IOException; 31 import java.io.InputStream; 32 import java.io.InputStreamReader; 33 import java.util.Properties; 34 35 /** 36 * <p>Implements pluggable Datatypes.</p> 37 * 38 * <p>This class is duplicated for each JAXP subpackage so keep it in 39 * sync. It is package private for secure class loading.</p> 40 * 41 * @author Santiago.PericasGeertsen@sun.com 42 */ 43 class FactoryFinder { 44 // Check we have access to package. 45 private static final String DEFAULT_PACKAGE = "com.sun.xml.internal."; 46 47 /** 48 * Internal debug flag. 49 */ 50 private static boolean debug = false; 51 52 /** 53 * Cache for properties in java.home/lib/jaxp.properties 54 */ 55 static Properties cacheProps = new Properties(); 56 57 /** 58 * Flag indicating if properties from java.home/lib/jaxp.properties 59 * have been cached. 60 */ 61 static volatile boolean firstTime = true; 62 63 /** 64 * Security support class use to check access control before 65 * getting certain system resources. 66 */ 67 static SecuritySupport ss = new SecuritySupport(); 68 69 // Define system property "jaxp.debug" to get output 70 static { 71 // Use try/catch block to support applets, which throws 72 // SecurityException out of this code. 73 try { 74 String val = ss.getSystemProperty("jaxp.debug"); 75 // Allow simply setting the prop to turn on debug 76 debug = val != null && !"false".equals(val); 77 } 78 catch (SecurityException se) { 79 debug = false; 80 } 81 } 82 83 private static void dPrint(String msg) { 84 if (debug) { 85 System.err.println("JAXP: " + msg); 86 } 87 } 88 89 /** 90 * Attempt to load a class using the class loader supplied. If that fails 91 * and fall back is enabled, the current (i.e. bootstrap) class loader is 92 * tried. 93 * 94 * If the class loader supplied is <code>null</code>, first try using the 95 * context class loader followed by the current (i.e. bootstrap) class 96 * loader. 97 * 98 * Use bootstrap classLoader if cl = null and useBSClsLoader is true 99 */ 100 static private Class getProviderClass(String className, ClassLoader cl, 101 boolean doFallback, boolean useBSClsLoader) throws ClassNotFoundException 102 { 103 try { 104 if (cl == null) { 105 if (useBSClsLoader) { 106 return Class.forName(className, true, FactoryFinder.class.getClassLoader()); 107 } else { 108 cl = ss.getContextClassLoader(); 109 if (cl == null) { 110 throw new ClassNotFoundException(); 111 } 112 else { 113 return cl.loadClass(className); 114 } 115 } 116 } 117 else { 118 return cl.loadClass(className); 119 } 120 } 121 catch (ClassNotFoundException e1) { 122 if (doFallback) { 123 // Use current class loader - should always be bootstrap CL 124 return Class.forName(className, true, FactoryFinder.class.getClassLoader()); 125 } 126 else { 127 throw e1; 128 } 129 } 130 } 131 132 /** 133 * Create an instance of a class. Delegates to method 134 * <code>getProviderClass()</code> in order to load the class. 135 * 136 * @param className Name of the concrete class corresponding to the 137 * service provider 138 * 139 * @param cl <code>ClassLoader</code> used to load the factory class. If <code>null</code> 140 * current <code>Thread</code>'s context classLoader is used to load the factory class. 141 * 142 * @param doFallback True if the current ClassLoader should be tried as 143 * a fallback if the class is not found using cl 144 */ 145 static Object newInstance(String className, ClassLoader cl, boolean doFallback) 146 throws ConfigurationError 147 { 148 return newInstance(className, cl, doFallback, false); 149 } 150 151 /** 152 * Create an instance of a class. Delegates to method 153 * <code>getProviderClass()</code> in order to load the class. 154 * 155 * @param className Name of the concrete class corresponding to the 156 * service provider 157 * 158 * @param cl <code>ClassLoader</code> used to load the factory class. If <code>null</code> 159 * current <code>Thread</code>'s context classLoader is used to load the factory class. 160 * 161 * @param doFallback True if the current ClassLoader should be tried as 162 * a fallback if the class is not found using cl 163 * 164 * @param useBSClsLoader True if cl=null actually meant bootstrap classLoader. This parameter 165 * is needed since DocumentBuilderFactory/SAXParserFactory defined null as context classLoader. 166 */ 167 static Object newInstance(String className, ClassLoader cl, boolean doFallback, boolean useBSClsLoader) 168 throws ConfigurationError 169 { 170 // make sure we have access to restricted packages 171 if (System.getSecurityManager() != null) { 172 if (className != null && className.startsWith(DEFAULT_PACKAGE)) { 173 cl = null; 174 useBSClsLoader = true; 175 } 176 } 177 178 try { 179 Class providerClass = getProviderClass(className, cl, doFallback, useBSClsLoader); 180 Object instance = providerClass.newInstance(); 181 if (debug) { // Extra check to avoid computing cl strings 182 dPrint("created new instance of " + providerClass + 183 " using ClassLoader: " + cl); 184 } 185 return instance; 186 } 187 catch (ClassNotFoundException x) { 188 throw new ConfigurationError( 189 "Provider " + className + " not found", x); 190 } 191 catch (Exception x) { 192 throw new ConfigurationError( 193 "Provider " + className + " could not be instantiated: " + x, 194 x); 195 } 196 } 197 198 /** 199 * Finds the implementation Class object in the specified order. 200 * 201 * @return Class object of factory, never null 202 * 203 * @param factoryId Name of the factory to find, same as 204 * a property name 205 * @param fallbackClassName Implementation class name, if nothing else 206 * is found. Use null to mean no fallback. 207 * 208 * Package private so this code can be shared. 209 */ 210 static Object find(String factoryId, String fallbackClassName) 211 throws ConfigurationError 212 { 213 return find(factoryId, null, fallbackClassName); 214 } 215 216 /** 217 * Finds the implementation Class object in the specified order. Main 218 * entry point. 219 * @return Class object of factory, never null 220 * 221 * @param factoryId Name of the factory to find, same as 222 * a property name 223 * 224 * @param cl ClassLoader to be used to load the class, null means to use 225 * the bootstrap ClassLoader 226 * 227 * @param fallbackClassName Implementation class name, if nothing else 228 * is found. Use null to mean no fallback. 229 * 230 * Package private so this code can be shared. 231 */ 232 static Object find(String factoryId, ClassLoader cl, String fallbackClassName) 233 throws ConfigurationError 234 { 235 dPrint("find factoryId =" + factoryId); 236 237 // Use the system property first 238 try { 239 String systemProp = ss.getSystemProperty(factoryId); 240 if (systemProp != null) { 241 dPrint("found system property, value=" + systemProp); 242 return newInstance(systemProp, null, true); 243 } 244 } 245 catch (SecurityException se) { 246 if (debug) se.printStackTrace(); 247 } 248 249 // Try read $java.home/lib/stax.properties followed by 250 // $java.home/lib/jaxp.properties if former not present 251 String configFile = null; 252 try { 253 String factoryClassName = null; 254 if (firstTime) { 255 synchronized (cacheProps) { 256 if (firstTime) { 257 configFile = ss.getSystemProperty("java.home") + File.separator + 258 "lib" + File.separator + "stax.properties"; 259 File f = new File(configFile); 260 firstTime = false; 261 if (ss.doesFileExist(f)) { 262 dPrint("Read properties file "+f); 263 cacheProps.load(ss.getFileInputStream(f)); 264 } 265 else { 266 configFile = ss.getSystemProperty("java.home") + File.separator + 267 "lib" + File.separator + "jaxp.properties"; 268 f = new File(configFile); 269 if (ss.doesFileExist(f)) { 270 dPrint("Read properties file "+f); 271 cacheProps.load(ss.getFileInputStream(f)); 272 } 273 } 274 } 275 } 276 } 277 factoryClassName = cacheProps.getProperty(factoryId); 278 279 if (factoryClassName != null) { 280 dPrint("found in " + configFile + " value=" + factoryClassName); 281 return newInstance(factoryClassName, null, true); 282 } 283 } 284 catch (Exception ex) { 285 if (debug) ex.printStackTrace(); 286 } 287 288 // Try Jar Service Provider Mechanism 289 Object provider = findJarServiceProvider(factoryId); 290 if (provider != null) { 291 return provider; 292 } 293 if (fallbackClassName == null) { 294 throw new ConfigurationError( 295 "Provider for " + factoryId + " cannot be found", null); 296 } 297 298 dPrint("loaded from fallback value: " + fallbackClassName); 299 return newInstance(fallbackClassName, cl, true); 300 } 301 302 /* 303 * Try to find provider using Jar Service Provider Mechanism 304 * 305 * @return instance of provider class if found or null 306 */ 307 private static Object findJarServiceProvider(String factoryId) 308 throws ConfigurationError 309 { 310 String serviceId = "META-INF/services/" + factoryId; 311 InputStream is = null; 312 313 // First try the Context ClassLoader 314 ClassLoader cl = ss.getContextClassLoader(); 315 boolean useBSClsLoader = false; 316 if (cl != null) { 317 is = ss.getResourceAsStream(cl, serviceId); 318 319 // If no provider found then try the current ClassLoader 320 if (is == null) { 321 cl = FactoryFinder.class.getClassLoader(); 322 is = ss.getResourceAsStream(cl, serviceId); 323 useBSClsLoader = true; 324 } 325 } else { 326 // No Context ClassLoader, try the current ClassLoader 327 cl = FactoryFinder.class.getClassLoader(); 328 is = ss.getResourceAsStream(cl, serviceId); 329 useBSClsLoader = true; 330 } 331 332 if (is == null) { 333 // No provider found 334 return null; 335 } 336 337 if (debug) { // Extra check to avoid computing cl strings 338 dPrint("found jar resource=" + serviceId + " using ClassLoader: " + cl); 339 } 340 341 BufferedReader rd; 342 try { 343 rd = new BufferedReader(new InputStreamReader(is, "UTF-8")); 344 } 345 catch (java.io.UnsupportedEncodingException e) { 346 rd = new BufferedReader(new InputStreamReader(is)); 347 } 348 349 String factoryClassName = null; 350 try { 351 // XXX Does not handle all possible input as specified by the 352 // Jar Service Provider specification 353 factoryClassName = rd.readLine(); 354 rd.close(); 355 } catch (IOException x) { 356 // No provider found 357 return null; 358 } 359 360 if (factoryClassName != null && !"".equals(factoryClassName)) { 361 dPrint("found in resource, value=" + factoryClassName); 362 363 // Note: here we do not want to fall back to the current 364 // ClassLoader because we want to avoid the case where the 365 // resource file was found using one ClassLoader and the 366 // provider class was instantiated using a different one. 367 return newInstance(factoryClassName, cl, false, useBSClsLoader); 368 } 369 370 // No provider found 371 return null; 372 } 373 374 static class ConfigurationError extends Error { 375 private Exception exception; 376 377 /** 378 * Construct a new instance with the specified detail string and 379 * exception. 380 */ 381 ConfigurationError(String msg, Exception x) { 382 super(msg); 383 this.exception = x; 384 } 385 386 Exception getException() { 387 return exception; 388 } 389 /** 390 * use the exception chaining mechanism of JDK1.4 391 */ 392 @Override 393 public Throwable getCause() { 394 return exception; 395 } 396 } 397 398 }