1 /* 2 * Copyright (c) 2004, 2005, 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.xpath; 27 28 import java.io.File; 29 import java.lang.reflect.InvocationTargetException; 30 import java.lang.reflect.Method; 31 import java.net.URL; 32 import java.security.AccessController; 33 import java.security.PrivilegedAction; 34 import java.util.Properties; 35 import java.util.ServiceConfigurationError; 36 import java.util.ServiceLoader; 37 38 /** 39 * Implementation of {@link XPathFactory#newInstance(String)}. 40 * 41 * @author <a href="Kohsuke.Kawaguchi@Sun.com">Kohsuke Kawaguchi</a> 42 * @version $Revision: 1.7 $, $Date: 2010-11-01 04:36:14 $ 43 * @since 1.5 44 */ 45 class XPathFactoryFinder { 46 47 private static final String DEFAULT_IMPL_NAME = "com.sun.org.apache.xpath.internal.jaxp.XPathFactoryImpl"; 48 private static SecuritySupport ss = new SecuritySupport(); 49 /** 50 * debug support code. 51 */ 52 private static boolean debug = false; 53 54 static { 55 // Use try/catch block to support applets 56 try { 57 debug = ss.getSystemProperty("jaxp.debug") != null; 58 } catch (Exception _) { 59 debug = false; 60 } 61 } 62 /** 63 * <p>Cache properties for performance.</p> 64 */ 65 private static Properties cacheProps = new Properties(); 66 /** 67 * <p>First time requires initialization overhead.</p> 68 */ 69 private volatile static boolean firstTime = true; 70 71 /** 72 * <p>Conditional debug printing.</p> 73 * 74 * @param msg to print 75 */ 76 private static void debugPrintln(String msg) { 77 if (debug) { 78 System.err.println("JAXP: " + msg); 79 } 80 } 81 /** 82 * <p><code>ClassLoader</code> to use to find 83 * <code>XPathFactory</code>.</p> 84 */ 85 private final ClassLoader classLoader; 86 87 /** 88 * <p>Constructor that specifies 89 * <code>ClassLoader</code> to use to find 90 * <code>XPathFactory</code>.</p> 91 * 92 * @param loader to be used to load resource, {@link XPathFactory}, and 93 * {@link SchemaFactoryLoader} implementations during the resolution 94 * process. If this parameter is null, the default system class loader will 95 * be used. 96 */ 97 public XPathFactoryFinder(ClassLoader loader) { 98 this.classLoader = loader; 99 if (debug) { 100 debugDisplayClassLoader(); 101 } 102 } 103 104 private void debugDisplayClassLoader() { 105 try { 106 if (classLoader == ss.getContextClassLoader()) { 107 debugPrintln("using thread context class loader (" + classLoader + ") for search"); 108 return; 109 } 110 } catch (Throwable _) { 111 ; // getContextClassLoader() undefined in JDK1.1 112 } 113 114 if (classLoader == ClassLoader.getSystemClassLoader()) { 115 debugPrintln("using system class loader (" + classLoader + ") for search"); 116 return; 117 } 118 119 debugPrintln("using class loader (" + classLoader + ") for search"); 120 } 121 122 /** 123 * <p>Creates a new {@link XPathFactory} object for the specified schema 124 * language.</p> 125 * 126 * @param uri Identifies the underlying object model. 127 * 128 * @return <code>null</code> if the callee fails to create one. 129 * 130 * @throws NullPointerException If the parameter is null. 131 */ 132 public XPathFactory newFactory(String uri) throws XPathFactoryConfigurationException { 133 if (uri == null) { 134 throw new NullPointerException(); 135 } 136 XPathFactory f = _newFactory(uri); 137 if (f != null) { 138 debugPrintln("factory '" + f.getClass().getName() + "' was found for " + uri); 139 } else { 140 debugPrintln("unable to find a factory for " + uri); 141 } 142 return f; 143 } 144 145 /** 146 * <p>Lookup a {@link XPathFactory} for the given object model.</p> 147 * 148 * @param uri identifies the object model. 149 * 150 * @return {@link XPathFactory} for the given object model. 151 */ 152 private XPathFactory _newFactory(String uri) throws XPathFactoryConfigurationException { 153 XPathFactory xpathFactory; 154 155 String propertyName = SERVICE_CLASS.getName() + ":" + uri; 156 157 // system property look up 158 try { 159 debugPrintln("Looking up system property '" + propertyName + "'"); 160 String r = ss.getSystemProperty(propertyName); 161 if (r != null) { 162 debugPrintln("The value is '" + r + "'"); 163 xpathFactory = createInstance(r, true); 164 if (xpathFactory != null) { 165 return xpathFactory; 166 } 167 } else { 168 debugPrintln("The property is undefined."); 169 } 170 } catch (Throwable t) { 171 if (debug) { 172 debugPrintln("failed to look up system property '" + propertyName + "'"); 173 t.printStackTrace(); 174 } 175 } 176 177 String javah = ss.getSystemProperty("java.home"); 178 String configFile = javah + File.separator 179 + "lib" + File.separator + "jaxp.properties"; 180 181 String factoryClassName = null; 182 183 // try to read from $java.home/lib/jaxp.properties 184 try { 185 if (firstTime) { 186 synchronized (cacheProps) { 187 if (firstTime) { 188 File f = new File(configFile); 189 firstTime = false; 190 if (ss.doesFileExist(f)) { 191 debugPrintln("Read properties file " + f); 192 cacheProps.load(ss.getFileInputStream(f)); 193 } 194 } 195 } 196 } 197 factoryClassName = cacheProps.getProperty(propertyName); 198 debugPrintln("found " + factoryClassName + " in $java.home/jaxp.properties"); 199 200 if (factoryClassName != null) { 201 xpathFactory = createInstance(factoryClassName, true); 202 if (xpathFactory != null) { 203 return xpathFactory; 204 } 205 } 206 } catch (Exception ex) { 207 if (debug) { 208 ex.printStackTrace(); 209 } 210 } 211 212 // try finding a service provider 213 xpathFactory = findServiceProvider(uri, DEFAULT_IMPL_NAME); 214 if (xpathFactory != null) { 215 return xpathFactory; 216 } 217 218 // platform default 219 if (uri.equals(XPathFactory.DEFAULT_OBJECT_MODEL_URI)) { 220 debugPrintln("attempting to use the platform default W3C DOM XPath lib"); 221 return createInstance(DEFAULT_IMPL_NAME, true); 222 } 223 224 debugPrintln("all things were tried, but none was found. bailing out."); 225 return null; 226 } 227 228 /** 229 * <p>Create class using appropriate ClassLoader.</p> 230 * 231 * @param className Name of class to create. 232 * @return Created class or <code>null</code>. 233 */ 234 private Class createClass(String className) { 235 Class clazz; 236 237 // use approprite ClassLoader 238 try { 239 if (classLoader != null) { 240 clazz = classLoader.loadClass(className); 241 } else { 242 clazz = Class.forName(className); 243 } 244 } catch (Throwable t) { 245 if (debug) { 246 t.printStackTrace(); 247 } 248 return null; 249 } 250 251 return clazz; 252 } 253 254 /** 255 * <p>Creates an instance of the specified and returns it.</p> 256 * 257 * @param className fully qualified class name to be instanciated. 258 * 259 * @return null if it fails. Error messages will be printed by this method. 260 */ 261 XPathFactory createInstance(String className) { 262 return createInstance(className, false); 263 } 264 265 XPathFactory createInstance(String className, boolean useServicesMechanism) { 266 XPathFactory xPathFactory = null; 267 268 debugPrintln("createInstance(" + className + ")"); 269 270 // get Class from className 271 Class clazz = createClass(className); 272 if (clazz == null) { 273 debugPrintln("failed to getClass(" + className + ")"); 274 return null; 275 } 276 debugPrintln("loaded " + className + " from " + which(clazz)); 277 278 // instantiate Class as a XPathFactory 279 try { 280 if (!useServicesMechanism) { 281 xPathFactory = (XPathFactory) newInstanceNoServiceLoader(clazz); 282 } 283 if (xPathFactory == null) { 284 xPathFactory = (XPathFactory) clazz.newInstance(); 285 } 286 } catch (ClassCastException classCastException) { 287 debugPrintln("could not instantiate " + clazz.getName()); 288 if (debug) { 289 classCastException.printStackTrace(); 290 } 291 return null; 292 } catch (IllegalAccessException illegalAccessException) { 293 debugPrintln("could not instantiate " + clazz.getName()); 294 if (debug) { 295 illegalAccessException.printStackTrace(); 296 } 297 return null; 298 } catch (InstantiationException instantiationException) { 299 debugPrintln("could not instantiate " + clazz.getName()); 300 if (debug) { 301 instantiationException.printStackTrace(); 302 } 303 return null; 304 } 305 306 return xPathFactory; 307 } 308 309 /** 310 * Try to construct using newXPathFactoryNoServiceLoader method if 311 * available. 312 */ 313 private static Object newInstanceNoServiceLoader( 314 Class<?> providerClass) { 315 // Retain maximum compatibility if no security manager. 316 if (System.getSecurityManager() == null) { 317 return null; 318 } 319 try { 320 Method creationMethod = 321 providerClass.getDeclaredMethod( 322 "newXPathFactoryNoServiceLoader"); 323 return creationMethod.invoke(null, null); 324 } catch (NoSuchMethodException exc) { 325 return null; 326 } catch (Exception exc) { 327 return null; 328 } 329 } 330 331 /* 332 * Try to find a provider using Service Loader 333 * 334 * @return instance of provider class if found or null 335 */ 336 private XPathFactory findServiceProvider(final String objectModel, final String fallbackClassName) 337 throws XPathFactoryConfigurationException { 338 try { 339 return (XPathFactory) AccessController.doPrivileged(new PrivilegedAction() { 340 public XPathFactory run() { 341 XPathFactory defaultProvider = null; 342 for (XPathFactory factory : ServiceLoader.load(XPathFactory.class)) { 343 if (factory.getClass().getName().equals(fallbackClassName)) { 344 defaultProvider = factory; 345 } else { 346 if (isObjectModelSupported(factory, objectModel)) { 347 return factory; 348 } 349 } 350 } 351 if (defaultProvider != null) { 352 return defaultProvider; 353 } 354 return null; 355 } 356 }); 357 } catch (ServiceConfigurationError e) { 358 throw new XPathFactoryConfigurationException((Exception) e.getCause()); 359 } 360 } 361 362 /** 363 * <p>Test if the specified schemas are supported by the provider.</p> 364 * 365 * @param schemaFactory a Schema factory provider. 366 * @param schemaLanguage Schema Language to support. 367 * 368 * @return <code>true</code> if the Schema Language is supported by the 369 * provider. 370 */ 371 private static boolean isObjectModelSupported(XPathFactory xPathFactory, String objectModel) { 372 final Class[] stringClassArray = {"".getClass()}; 373 final Object[] objectModelObjectArray = {objectModel}; 374 final String isObjectModelSupportedMethod = "isObjectModelSupported"; 375 // does this Class support desired Schema? 376 try { 377 Method isObjectModelSupported = xPathFactory.getClass().getMethod(isObjectModelSupportedMethod, stringClassArray); 378 Boolean supported = (Boolean) isObjectModelSupported.invoke(xPathFactory, objectModelObjectArray); 379 if (supported.booleanValue()) { 380 return true; 381 } 382 } catch (NoSuchMethodException noSuchMethodException) { 383 } catch (IllegalAccessException illegalAccessException) { 384 } catch (InvocationTargetException invocationTargetException) { 385 } 386 387 return false; 388 } 389 private static final Class SERVICE_CLASS = XPathFactory.class; 390 391 private static String which(Class clazz) { 392 return which(clazz.getName(), clazz.getClassLoader()); 393 } 394 395 /** 396 * <p>Search the specified classloader for the given classname.</p> 397 * 398 * @param classname the fully qualified name of the class to search for 399 * @param loader the classloader to search 400 * 401 * @return the source location of the resource, or null if it wasn't found 402 */ 403 private static String which(String classname, ClassLoader loader) { 404 405 String classnameAsResource = classname.replace('.', '/') + ".class"; 406 407 if (loader == null) { 408 loader = ClassLoader.getSystemClassLoader(); 409 } 410 411 //URL it = loader.getResource(classnameAsResource); 412 URL it = ss.getResourceAsURL(loader, classnameAsResource); 413 if (it != null) { 414 return it.toString(); 415 } else { 416 return null; 417 } 418 } 419 }