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