FactoryFinder.java revision 670:71dd8f764942
1/* 2 * Copyright (c) 2005, 2013, 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 26package javax.xml.stream; 27 28import java.io.File; 29import java.security.AccessController; 30import java.security.PrivilegedAction; 31import java.util.Iterator; 32import java.util.Properties; 33import java.util.ServiceConfigurationError; 34import java.util.ServiceLoader; 35 36/** 37 * <p>Implements pluggable streams.</p> 38 * 39 * <p>This class is duplicated for each JAXP subpackage so keep it in 40 * sync. It is package private for secure class loading.</p> 41 * 42 * @author Santiago.PericasGeertsen@sun.com 43 */ 44class FactoryFinder { 45 // Check we have access to package. 46 private static final String DEFAULT_PACKAGE = "com.sun.xml.internal."; 47 48 /** 49 * Internal debug flag. 50 */ 51 private static boolean debug = false; 52 53 /** 54 * Cache for properties in java.home/conf/jaxp.properties 55 */ 56 final private static Properties cacheProps = new Properties(); 57 58 /** 59 * Flag indicating if properties from java.home/conf/jaxp.properties 60 * have been cached. 61 */ 62 private static volatile boolean firstTime = true; 63 64 /** 65 * Security support class use to check access control before 66 * getting certain system resources. 67 */ 68 final private static SecuritySupport ss = new SecuritySupport(); 69 70 // Define system property "jaxp.debug" to get output 71 static { 72 // Use try/catch block to support applets, which throws 73 // SecurityException out of this code. 74 try { 75 String val = ss.getSystemProperty("jaxp.debug"); 76 // Allow simply setting the prop to turn on debug 77 debug = val != null && !"false".equals(val); 78 } 79 catch (SecurityException se) { 80 debug = false; 81 } 82 } 83 84 private static void dPrint(String msg) { 85 if (debug) { 86 System.err.println("JAXP: " + msg); 87 } 88 } 89 90 /** 91 * Attempt to load a class using the class loader supplied. If that fails 92 * and fall back is enabled, the current (i.e. bootstrap) class loader is 93 * tried. 94 * 95 * If the class loader supplied is <code>null</code>, first try using the 96 * context class loader followed by the current (i.e. bootstrap) class 97 * loader. 98 * 99 * Use bootstrap classLoader if cl = null and useBSClsLoader is true 100 */ 101 static private Class getProviderClass(String className, ClassLoader cl, 102 boolean doFallback, boolean useBSClsLoader) throws ClassNotFoundException 103 { 104 try { 105 if (cl == null) { 106 if (useBSClsLoader) { 107 return Class.forName(className, false, FactoryFinder.class.getClassLoader()); 108 } else { 109 cl = ss.getContextClassLoader(); 110 if (cl == null) { 111 throw new ClassNotFoundException(); 112 } 113 else { 114 return Class.forName(className, false, cl); 115 } 116 } 117 } 118 else { 119 return Class.forName(className, false, cl); 120 } 121 } 122 catch (ClassNotFoundException e1) { 123 if (doFallback) { 124 // Use current class loader - should always be bootstrap CL 125 return Class.forName(className, false, FactoryFinder.class.getClassLoader()); 126 } 127 else { 128 throw e1; 129 } 130 } 131 } 132 133 /** 134 * Create an instance of a class. Delegates to method 135 * <code>getProviderClass()</code> in order to load the class. 136 * 137 * @param type Base class / Service interface of the factory to 138 * instantiate. 139 * 140 * @param className Name of the concrete class corresponding to the 141 * service provider 142 * 143 * @param cl <code>ClassLoader</code> used to load the factory class. If <code>null</code> 144 * current <code>Thread</code>'s context classLoader is used to load the factory class. 145 * 146 * @param doFallback True if the current ClassLoader should be tried as 147 * a fallback if the class is not found using cl 148 */ 149 static <T> T newInstance(Class<T> type, String className, ClassLoader cl, boolean doFallback) 150 throws FactoryConfigurationError 151 { 152 return newInstance(type, className, cl, doFallback, false); 153 } 154 155 /** 156 * Create an instance of a class. Delegates to method 157 * <code>getProviderClass()</code> in order to load the class. 158 * 159 * @param type Base class / Service interface of the factory to 160 * instantiate. 161 * 162 * @param className Name of the concrete class corresponding to the 163 * service provider 164 * 165 * @param cl <code>ClassLoader</code> used to load the factory class. If <code>null</code> 166 * current <code>Thread</code>'s context classLoader is used to load the factory class. 167 * 168 * @param doFallback True if the current ClassLoader should be tried as 169 * a fallback if the class is not found using cl 170 * 171 * @param useBSClsLoader True if cl=null actually meant bootstrap classLoader. This parameter 172 * is needed since DocumentBuilderFactory/SAXParserFactory defined null as context classLoader. 173 */ 174 static <T> T newInstance(Class<T> type, String className, ClassLoader cl, 175 boolean doFallback, boolean useBSClsLoader) 176 throws FactoryConfigurationError 177 { 178 assert type != null; 179 180 // make sure we have access to restricted packages 181 if (System.getSecurityManager() != null) { 182 if (className != null && className.startsWith(DEFAULT_PACKAGE)) { 183 cl = null; 184 useBSClsLoader = true; 185 } 186 } 187 188 try { 189 Class<?> providerClass = getProviderClass(className, cl, doFallback, useBSClsLoader); 190 if (!type.isAssignableFrom(providerClass)) { 191 throw new ClassCastException(className + " cannot be cast to " + type.getName()); 192 } 193 Object instance = providerClass.newInstance(); 194 if (debug) { // Extra check to avoid computing cl strings 195 dPrint("created new instance of " + providerClass + 196 " using ClassLoader: " + cl); 197 } 198 return type.cast(instance); 199 } 200 catch (ClassNotFoundException x) { 201 throw new FactoryConfigurationError( 202 "Provider " + className + " not found", x); 203 } 204 catch (Exception x) { 205 throw new FactoryConfigurationError( 206 "Provider " + className + " could not be instantiated: " + x, 207 x); 208 } 209 } 210 211 /** 212 * Finds the implementation Class object in the specified order. 213 * 214 * @return Class object of factory, never null 215 * 216 * @param type Base class / Service interface of the 217 * factory to find. 218 * 219 * @param fallbackClassName Implementation class name, if nothing else 220 * is found. Use null to mean no fallback. 221 * 222 * Package private so this code can be shared. 223 */ 224 static <T> T find(Class<T> type, String fallbackClassName) 225 throws FactoryConfigurationError 226 { 227 return find(type, type.getName(), null, fallbackClassName); 228 } 229 230 /** 231 * Finds the implementation Class object in the specified order. Main 232 * entry point. 233 * @return Class object of factory, never null 234 * 235 * @param type Base class / Service interface of the 236 * factory to find. 237 * 238 * @param factoryId Name of the factory to find, same as 239 * a property name 240 * 241 * @param cl ClassLoader to be used to load the class, null means to use 242 * the bootstrap ClassLoader 243 * 244 * @param fallbackClassName Implementation class name, if nothing else 245 * is found. Use null to mean no fallback. 246 * 247 * Package private so this code can be shared. 248 */ 249 static <T> T find(Class<T> type, String factoryId, ClassLoader cl, String fallbackClassName) 250 throws FactoryConfigurationError 251 { 252 dPrint("find factoryId =" + factoryId); 253 254 // Use the system property first 255 try { 256 257 final String systemProp; 258 if (type.getName().equals(factoryId)) { 259 systemProp = ss.getSystemProperty(factoryId); 260 } else { 261 systemProp = System.getProperty(factoryId); 262 } 263 if (systemProp != null) { 264 dPrint("found system property, value=" + systemProp); 265 return newInstance(type, systemProp, cl, true); 266 } 267 } 268 catch (SecurityException se) { 269 throw new FactoryConfigurationError( 270 "Failed to read factoryId '" + factoryId + "'", se); 271 } 272 273 // Try read $java.home/lib/stax.properties followed by 274 // $java.home/conf/jaxp.properties if former not present 275 String configFile = null; 276 try { 277 if (firstTime) { 278 synchronized (cacheProps) { 279 if (firstTime) { 280 configFile = ss.getSystemProperty("java.home") + File.separator + 281 "lib" + File.separator + "stax.properties"; 282 File f = new File(configFile); 283 firstTime = false; 284 if (ss.doesFileExist(f)) { 285 dPrint("Read properties file "+f); 286 cacheProps.load(ss.getFileInputStream(f)); 287 } 288 else { 289 configFile = ss.getSystemProperty("java.home") + File.separator + 290 "conf" + File.separator + "jaxp.properties"; 291 f = new File(configFile); 292 if (ss.doesFileExist(f)) { 293 dPrint("Read properties file "+f); 294 cacheProps.load(ss.getFileInputStream(f)); 295 } 296 } 297 } 298 } 299 } 300 final String factoryClassName = cacheProps.getProperty(factoryId); 301 302 if (factoryClassName != null) { 303 dPrint("found in " + configFile + " value=" + factoryClassName); 304 return newInstance(type, factoryClassName, cl, true); 305 } 306 } 307 catch (Exception ex) { 308 if (debug) ex.printStackTrace(); 309 } 310 311 if (type.getName().equals(factoryId)) { 312 // Try Jar Service Provider Mechanism 313 final T provider = findServiceProvider(type, cl); 314 if (provider != null) { 315 return provider; 316 } 317 } else { 318 // We're in the case where a 'custom' factoryId was provided, 319 // and in every case where that happens, we expect that 320 // fallbackClassName will be null. 321 assert fallbackClassName == null; 322 } 323 if (fallbackClassName == null) { 324 throw new FactoryConfigurationError( 325 "Provider for " + factoryId + " cannot be found", null); 326 } 327 328 dPrint("loaded from fallback value: " + fallbackClassName); 329 return newInstance(type, fallbackClassName, cl, true); 330 } 331 332 /* 333 * Try to find provider using the ServiceLoader API 334 * 335 * @param type Base class / Service interface of the factory to find. 336 * 337 * @return instance of provider class if found or null 338 */ 339 private static <T> T findServiceProvider(final Class<T> type, final ClassLoader cl) { 340 try { 341 return AccessController.doPrivileged(new PrivilegedAction<T>() { 342 @Override 343 public T run() { 344 final ServiceLoader<T> serviceLoader; 345 if (cl == null) { 346 //the current thread's context class loader 347 serviceLoader = ServiceLoader.load(type); 348 } else { 349 serviceLoader = ServiceLoader.load(type, cl); 350 } 351 final Iterator<T> iterator = serviceLoader.iterator(); 352 if (iterator.hasNext()) { 353 return iterator.next(); 354 } else { 355 return null; 356 } 357 } 358 }); 359 } catch(ServiceConfigurationError e) { 360 // It is not possible to wrap an error directly in 361 // FactoryConfigurationError - so we need to wrap the 362 // ServiceConfigurationError in a RuntimeException. 363 // The alternative would be to modify the logic in 364 // FactoryConfigurationError to allow setting a 365 // Throwable as the cause, but that could cause 366 // compatibility issues down the road. 367 final RuntimeException x = new RuntimeException( 368 "Provider for " + type + " cannot be created", e); 369 final FactoryConfigurationError error = 370 new FactoryConfigurationError(x, x.getMessage()); 371 throw error; 372 } 373 } 374 375} 376