51 * @author <ul><li>Ryan Shoemaker, Sun Microsystems, Inc.</li></ul> 52 * @see JAXBContext 53 */ 54 class ContextFinder { 55 56 /** 57 * When JAXB is in J2SE, rt.jar has to have a JAXB implementation. 58 * However, rt.jar cannot have META-INF/services/javax.xml.bind.JAXBContext 59 * because if it has, it will take precedence over any file that applications have 60 * in their jar files. 61 * 62 * <p> 63 * When the user bundles his own JAXB implementation, we'd like to use it, and we 64 * want the platform default to be used only when there's no other JAXB provider. 65 * 66 * <p> 67 * For this reason, we have to hard-code the class name into the API. 68 */ 69 private static final String PLATFORM_DEFAULT_FACTORY_CLASS = "com.sun.xml.internal.bind.v2.ContextFactory"; 70 71 private static final Logger logger; 72 73 static { 74 logger = Logger.getLogger("javax.xml.bind"); 75 try { 76 if (AccessController.doPrivileged(new GetPropertyAction("jaxb.debug")) != null) { 77 // disconnect the logger from a bigger framework (if any) 78 // and take the matters into our own hands 79 logger.setUseParentHandlers(false); 80 logger.setLevel(Level.ALL); 81 ConsoleHandler handler = new ConsoleHandler(); 82 handler.setLevel(Level.ALL); 83 logger.addHandler(handler); 84 } else { 85 // don't change the setting of this logger 86 // to honor what other frameworks 87 // have done on configurations. 88 } 89 } catch (Throwable t) { 90 // just to be extra safe. in particular System.getProperty may throw 91 // SecurityException. 92 } 93 } 94 95 /** 96 * If the {@link InvocationTargetException} wraps an exception that shouldn't be wrapped, 97 * throw the wrapped exception. 98 */ 99 private static void handleInvocationTargetException(InvocationTargetException x) throws JAXBException { 100 Throwable t = x.getTargetException(); 101 if (t != null) { 102 if (t instanceof JAXBException) 103 // one of our exceptions, just re-throw 104 throw (JAXBException) t; 105 if (t instanceof RuntimeException) 106 // avoid wrapping exceptions unnecessarily 107 throw (RuntimeException) t; 108 if (t instanceof Error) 109 throw (Error) t; 110 } 111 } 112 113 114 /** 258 x = e.getTargetException(); 259 260 throw new JAXBException(x); 261 } 262 } 263 264 static JAXBContext find(String factoryId, String contextPath, ClassLoader classLoader, Map properties) throws JAXBException { 265 266 // TODO: do we want/need another layer of searching in $java.home/lib/jaxb.properties like JAXP? 267 268 StringTokenizer packages = new StringTokenizer(contextPath, ":"); 269 if (!packages.hasMoreTokens()) { 270 // no context is specified 271 throw new JAXBException(Messages.format(Messages.NO_PACKAGE_IN_CONTEXTPATH)); 272 } 273 274 // search for jaxb.properties in the class loader of each class first 275 logger.fine("Searching jaxb.properties"); 276 while (packages.hasMoreTokens()) { 277 // com.acme.foo - > com/acme/foo/jaxb.properties 278 String className = classNameFromPackageProperties(factoryId, classLoader, packages.nextToken(":").replace('.', '/')); 279 if (className != null) return newInstance(contextPath, className, classLoader, properties); 280 } 281 282 String factoryName = classNameFromSystemProperties(); 283 if (factoryName != null) return newInstance(contextPath, factoryName, classLoader, properties); 284 285 Class ctxFactory = (Class) ServiceLoaderUtil.lookupUsingOSGiServiceLoader("javax.xml.bind.JAXBContext", logger); 286 if (ctxFactory != null) { 287 return newInstance(contextPath, ctxFactory, classLoader, properties); 288 } 289 290 // TODO: SPEC change required! This is supposed to be! 291 // JAXBContext obj = firstByServiceLoader(JAXBContext.class, EXCEPTION_HANDLER); 292 // if (obj != null) return obj; 293 294 // TODO: Deprecated - SPEC change required! 295 factoryName = firstByServiceLoaderDeprecated(JAXBContext.class, classLoader); 296 if (factoryName != null) return newInstance(contextPath, factoryName, classLoader, properties); 297 298 // else no provider found 299 logger.fine("Trying to create the platform default provider"); 300 return newInstance(contextPath, PLATFORM_DEFAULT_FACTORY_CLASS, classLoader, properties); 301 } 302 303 static JAXBContext find(Class[] classes, Map properties) throws JAXBException { 304 305 // search for jaxb.properties in the class loader of each class first 306 logger.fine("Searching jaxb.properties"); 307 for (final Class c : classes) { 308 // this classloader is used only to load jaxb.properties, so doing this should be safe. 309 if (c.getPackage() == null) continue; // this is possible for primitives, arrays, and classes that are loaded by poorly implemented ClassLoaders 310 311 // TODO: do we want to optimize away searching the same package? org.Foo, org.Bar, com.Baz 312 // classes from the same package might come from different class loades, so it might be a bad idea 313 // TODO: it's easier to look things up from the class 314 // c.getResourceAsStream("jaxb.properties"); 315 316 String className = classNameFromPackageProperties(JAXBContext.JAXB_CONTEXT_FACTORY, getClassClassLoader(c), c.getPackage().getName().replace('.', '/')); 317 if (className != null) return newInstance(classes, properties, className); 318 } 319 320 String factoryName = classNameFromSystemProperties(); 321 if (factoryName != null) return newInstance(classes, properties, factoryName); 322 323 Class ctxFactoryClass = (Class) ServiceLoaderUtil.lookupUsingOSGiServiceLoader("javax.xml.bind.JAXBContext", logger); 324 if (ctxFactoryClass != null) { 325 return newInstance(classes, properties, ctxFactoryClass); 326 } 327 328 // TODO: to be removed - deprecated!!! Requires SPEC change!!! 329 String className = firstByServiceLoaderDeprecated(JAXBContext.class, getContextClassLoader()); 330 if (className != null) return newInstance(classes, properties, className); 331 332 // // TODO: supposed to be: 333 // obj = firstByServiceLoader(JAXBContext.class, EXCEPTION_HANDLER); 334 // if (obj != null) return obj; 335 336 // else no provider found 337 logger.fine("Trying to create the platform default provider"); 338 return newInstance(classes, properties, PLATFORM_DEFAULT_FACTORY_CLASS); 339 } 340 341 342 private static String classNameFromPackageProperties(String factoryId, ClassLoader classLoader, String packageName) throws JAXBException { 343 String resourceName = packageName + "/jaxb.properties"; 344 logger.log(Level.FINE, "Trying to locate {0}", resourceName); 345 Properties props = loadJAXBProperties(classLoader, resourceName); 346 if (props != null) { 347 if (props.containsKey(factoryId)) { 348 return props.getProperty(factoryId); 349 } else { 350 throw new JAXBException(Messages.format(Messages.MISSING_PROPERTY, packageName, factoryId)); 351 } 352 } 353 return null; 354 } 355 356 private static String classNameFromSystemProperties() throws JAXBException { 357 logger.log(Level.FINE, "Checking system property {0}", JAXBContext.JAXB_CONTEXT_FACTORY); 358 // search for a system property second (javax.xml.bind.JAXBContext) 359 String factoryClassName = AccessController.doPrivileged(new GetPropertyAction(JAXBContext.JAXB_CONTEXT_FACTORY)); 360 if (factoryClassName != null) { 361 logger.log(Level.FINE, " found {0}", factoryClassName); 362 return factoryClassName; 363 } else { // leave this here to assure compatibility 364 logger.fine(" not found"); 365 logger.log(Level.FINE, "Checking system property {0}", JAXBContext.class.getName()); 366 factoryClassName = AccessController.doPrivileged(new GetPropertyAction(JAXBContext.class.getName())); 367 if (factoryClassName != null) { 368 logger.log(Level.FINE, " found {0}", factoryClassName); 369 return factoryClassName; 370 } else { 371 logger.fine(" not found"); 372 } 373 } 374 return null; 375 } 376 377 private static Properties loadJAXBProperties(ClassLoader classLoader, String propFileName) throws JAXBException { 378 379 Properties props = null; 380 try { 381 URL url; 382 if (classLoader == null) 383 url = ClassLoader.getSystemResource(propFileName); 384 else 385 url = classLoader.getResource(propFileName); 386 387 if (url != null) { 388 logger.log(Level.FINE, "loading props from {0}", url); 389 props = new Properties(); 390 InputStream is = url.openStream(); 391 props.load(is); 392 is.close(); 393 } 394 } catch (IOException ioe) { 395 logger.log(Level.FINE, "Unable to load " + propFileName, ioe); 396 throw new JAXBException(ioe.toString(), ioe); | 51 * @author <ul><li>Ryan Shoemaker, Sun Microsystems, Inc.</li></ul> 52 * @see JAXBContext 53 */ 54 class ContextFinder { 55 56 /** 57 * When JAXB is in J2SE, rt.jar has to have a JAXB implementation. 58 * However, rt.jar cannot have META-INF/services/javax.xml.bind.JAXBContext 59 * because if it has, it will take precedence over any file that applications have 60 * in their jar files. 61 * 62 * <p> 63 * When the user bundles his own JAXB implementation, we'd like to use it, and we 64 * want the platform default to be used only when there's no other JAXB provider. 65 * 66 * <p> 67 * For this reason, we have to hard-code the class name into the API. 68 */ 69 private static final String PLATFORM_DEFAULT_FACTORY_CLASS = "com.sun.xml.internal.bind.v2.ContextFactory"; 70 71 // previous value of JAXBContext.JAXB_CONTEXT_FACTORY, using also this to ensure backwards compatibility 72 private static final String JAXB_CONTEXT_FACTORY_DEPRECATED = "javax.xml.bind.context.factory"; 73 74 private static final Logger logger; 75 76 static { 77 logger = Logger.getLogger("javax.xml.bind"); 78 try { 79 if (AccessController.doPrivileged(new GetPropertyAction("jaxb.debug")) != null) { 80 // disconnect the logger from a bigger framework (if any) 81 // and take the matters into our own hands 82 logger.setUseParentHandlers(false); 83 logger.setLevel(Level.ALL); 84 ConsoleHandler handler = new ConsoleHandler(); 85 handler.setLevel(Level.ALL); 86 logger.addHandler(handler); 87 } else { 88 // don't change the setting of this logger 89 // to honor what other frameworks 90 // have done on configurations. 91 } 92 } catch (Throwable t) { 93 // just to be extra safe. in particular System.getProperty may throw 94 // SecurityException. 95 } 96 } 97 98 private static ServiceLoaderUtil.ExceptionHandler<JAXBException> EXCEPTION_HANDLER = new ServiceLoaderUtil.ExceptionHandler<JAXBException>() { 99 @Override 100 public JAXBException createException(Throwable throwable, String message) { 101 return new JAXBException(message, throwable); 102 } 103 }; 104 105 /** 106 * If the {@link InvocationTargetException} wraps an exception that shouldn't be wrapped, 107 * throw the wrapped exception. 108 */ 109 private static void handleInvocationTargetException(InvocationTargetException x) throws JAXBException { 110 Throwable t = x.getTargetException(); 111 if (t != null) { 112 if (t instanceof JAXBException) 113 // one of our exceptions, just re-throw 114 throw (JAXBException) t; 115 if (t instanceof RuntimeException) 116 // avoid wrapping exceptions unnecessarily 117 throw (RuntimeException) t; 118 if (t instanceof Error) 119 throw (Error) t; 120 } 121 } 122 123 124 /** 268 x = e.getTargetException(); 269 270 throw new JAXBException(x); 271 } 272 } 273 274 static JAXBContext find(String factoryId, String contextPath, ClassLoader classLoader, Map properties) throws JAXBException { 275 276 // TODO: do we want/need another layer of searching in $java.home/lib/jaxb.properties like JAXP? 277 278 StringTokenizer packages = new StringTokenizer(contextPath, ":"); 279 if (!packages.hasMoreTokens()) { 280 // no context is specified 281 throw new JAXBException(Messages.format(Messages.NO_PACKAGE_IN_CONTEXTPATH)); 282 } 283 284 // search for jaxb.properties in the class loader of each class first 285 logger.fine("Searching jaxb.properties"); 286 while (packages.hasMoreTokens()) { 287 // com.acme.foo - > com/acme/foo/jaxb.properties 288 String factoryClassName = classNameFromPackageProperties(classLoader, packages.nextToken(":").replace('.', '/'), factoryId, JAXB_CONTEXT_FACTORY_DEPRECATED); 289 if (factoryClassName != null) return newInstance(contextPath, factoryClassName, classLoader, properties); 290 } 291 292 String factoryName = classNameFromSystemProperties(); 293 if (factoryName != null) return newInstance(contextPath, factoryName, classLoader, properties); 294 295 // TODO: SPEC change required! java.util.ServiceLoader 296 JAXBContextFactory obj = ServiceLoaderUtil.firstByServiceLoader(JAXBContextFactory.class, logger, EXCEPTION_HANDLER); 297 if (obj != null) return obj.createContext(contextPath, classLoader, properties); 298 299 // to ensure backwards compatibility 300 factoryName = firstByServiceLoaderDeprecated(JAXBContext.class, classLoader); 301 if (factoryName != null) return newInstance(contextPath, factoryName, classLoader, properties); 302 303 Class ctxFactory = (Class) ServiceLoaderUtil.lookupUsingOSGiServiceLoader("javax.xml.bind.JAXBContext", logger); 304 if (ctxFactory != null) { 305 return newInstance(contextPath, ctxFactory, classLoader, properties); 306 } 307 308 // else no provider found 309 logger.fine("Trying to create the platform default provider"); 310 return newInstance(contextPath, PLATFORM_DEFAULT_FACTORY_CLASS, classLoader, properties); 311 } 312 313 static JAXBContext find(Class<?>[] classes, Map<String, ?> properties) throws JAXBException { 314 315 // search for jaxb.properties in the class loader of each class first 316 logger.fine("Searching jaxb.properties"); 317 for (final Class c : classes) { 318 // this classloader is used only to load jaxb.properties, so doing this should be safe. 319 if (c.getPackage() == null) continue; // this is possible for primitives, arrays, and classes that are loaded by poorly implemented ClassLoaders 320 321 // TODO: do we want to optimize away searching the same package? org.Foo, org.Bar, com.Baz 322 // classes from the same package might come from different class loades, so it might be a bad idea 323 // TODO: it's easier to look things up from the class 324 // c.getResourceAsStream("jaxb.properties"); 325 326 String factoryClassName = classNameFromPackageProperties(getClassClassLoader(c), c.getPackage().getName().replace('.', '/'), JAXBContext.JAXB_CONTEXT_FACTORY, JAXB_CONTEXT_FACTORY_DEPRECATED); 327 if (factoryClassName != null) return newInstance(classes, properties, factoryClassName); 328 } 329 330 String factoryClassName = classNameFromSystemProperties(); 331 if (factoryClassName != null) return newInstance(classes, properties, factoryClassName); 332 333 // TODO: SPEC change required! java.util.ServiceLoader 334 JAXBContextFactory factory = ServiceLoaderUtil.firstByServiceLoader(JAXBContextFactory.class, logger, EXCEPTION_HANDLER); 335 if (factory != null) return factory.createContext(classes, properties); 336 337 // to ensure backwards compatibility 338 String className = firstByServiceLoaderDeprecated(JAXBContext.class, getContextClassLoader()); 339 if (className != null) return newInstance(classes, properties, className); 340 341 logger.fine("Trying to create the platform default provider"); 342 Class ctxFactoryClass = (Class) ServiceLoaderUtil.lookupUsingOSGiServiceLoader("javax.xml.bind.JAXBContext", logger); 343 if (ctxFactoryClass != null) { 344 return newInstance(classes, properties, ctxFactoryClass); 345 } 346 347 // else no provider found 348 logger.fine("Trying to create the platform default provider"); 349 return newInstance(classes, properties, PLATFORM_DEFAULT_FACTORY_CLASS); 350 } 351 352 353 /** 354 * first factoryId should be the preffered one, more of those can be provided to support backwards compatibility 355 */ 356 private static String classNameFromPackageProperties(ClassLoader classLoader, String packageName, String ... factoryIds) throws JAXBException { 357 String resourceName = packageName + "/jaxb.properties"; 358 logger.log(Level.FINE, "Trying to locate {0}", resourceName); 359 Properties props = loadJAXBProperties(classLoader, resourceName); 360 if (props != null) { 361 for(String factoryId : factoryIds) { 362 if (props.containsKey(factoryId)) { 363 return props.getProperty(factoryId); 364 } 365 } 366 throw new JAXBException(Messages.format(Messages.MISSING_PROPERTY, packageName, factoryIds[0])); 367 } 368 return null; 369 } 370 371 private static String classNameFromSystemProperties() throws JAXBException { 372 String factoryClassName = getSystemProperty(JAXBContext.JAXB_CONTEXT_FACTORY); 373 if (factoryClassName != null) { 374 return factoryClassName; 375 } 376 // leave this here to assure compatibility 377 factoryClassName = getDeprecatedSystemProperty(JAXB_CONTEXT_FACTORY_DEPRECATED); 378 if (factoryClassName != null) { 379 return factoryClassName; 380 } 381 // leave this here to assure compatibility 382 factoryClassName = getDeprecatedSystemProperty(JAXBContext.class.getName()); 383 if (factoryClassName != null) { 384 return factoryClassName; 385 } 386 return null; 387 } 388 389 private static String getDeprecatedSystemProperty(String property) { 390 String value = getSystemProperty(property); 391 if (value != null) { 392 logger.log(Level.WARNING, "Using non-standard property: {0}. Property {1} should be used instead.", 393 new Object[] {property, JAXBContext.JAXB_CONTEXT_FACTORY}); 394 } 395 return value; 396 } 397 398 private static String getSystemProperty(String property) { 399 logger.log(Level.FINE, "Checking system property {0}", property); 400 String value = AccessController.doPrivileged(new GetPropertyAction(property)); 401 if (value != null) { 402 logger.log(Level.FINE, " found {0}", value); 403 } else { 404 logger.log(Level.FINE, " not found"); 405 } 406 return value; 407 } 408 409 private static Properties loadJAXBProperties(ClassLoader classLoader, String propFileName) throws JAXBException { 410 411 Properties props = null; 412 try { 413 URL url; 414 if (classLoader == null) 415 url = ClassLoader.getSystemResource(propFileName); 416 else 417 url = classLoader.getResource(propFileName); 418 419 if (url != null) { 420 logger.log(Level.FINE, "loading props from {0}", url); 421 props = new Properties(); 422 InputStream is = url.openStream(); 423 props.load(is); 424 is.close(); 425 } 426 } catch (IOException ioe) { 427 logger.log(Level.FINE, "Unable to load " + propFileName, ioe); 428 throw new JAXBException(ioe.toString(), ioe); |