--- old/src/java.xml.bind/share/classes/javax/xml/bind/ContextFinder.java 2015-05-26 17:37:58.000000000 +0200 +++ new/src/java.xml.bind/share/classes/javax/xml/bind/ContextFinder.java 2015-05-26 17:37:56.000000000 +0200 @@ -68,6 +68,9 @@ */ private static final String PLATFORM_DEFAULT_FACTORY_CLASS = "com.sun.xml.internal.bind.v2.ContextFactory"; + // previous value of JAXBContext.JAXB_CONTEXT_FACTORY, using also this to ensure backwards compatibility + private static final String JAXB_CONTEXT_FACTORY_DEPRECATED = "javax.xml.bind.context.factory"; + private static final Logger logger; static { @@ -92,6 +95,14 @@ } } + private static ServiceLoaderUtil.ExceptionHandler EXCEPTION_HANDLER = + new ServiceLoaderUtil.ExceptionHandler() { + @Override + public JAXBException createException(Throwable throwable, String message) { + return new JAXBException(message, throwable); + } + }; + /** * If the {@link InvocationTargetException} wraps an exception that shouldn't be wrapped, * throw the wrapped exception. @@ -159,7 +170,10 @@ } } - static JAXBContext newInstance(String contextPath, Class spFactory, ClassLoader classLoader, Map properties) throws JAXBException { + static JAXBContext newInstance(String contextPath, + Class spFactory, + ClassLoader classLoader, + Map properties) throws JAXBException { try { /* @@ -239,6 +253,7 @@ Map properties, Class spFactory) throws JAXBException { try { + Method m = spFactory.getMethod("createContext", Class[].class, Map.class); Object context = m.invoke(null, classes, properties); if (!(context instanceof JAXBContext)) { @@ -246,10 +261,10 @@ throw handleClassCastException(context.getClass(), JAXBContext.class); } return (JAXBContext) context; - } catch (NoSuchMethodException e) { - throw new JAXBException(e); - } catch (IllegalAccessException e) { + + } catch (NoSuchMethodException | IllegalAccessException e) { throw new JAXBException(e); + } catch (InvocationTargetException e) { handleInvocationTargetException(e); @@ -261,9 +276,10 @@ } } - static JAXBContext find(String factoryId, String contextPath, ClassLoader classLoader, Map properties) throws JAXBException { - - // TODO: do we want/need another layer of searching in $java.home/lib/jaxb.properties like JAXP? + static JAXBContext find(String factoryId, + String contextPath, + ClassLoader classLoader, + Map properties) throws JAXBException { StringTokenizer packages = new StringTokenizer(contextPath, ":"); if (!packages.hasMoreTokens()) { @@ -275,63 +291,85 @@ logger.fine("Searching jaxb.properties"); while (packages.hasMoreTokens()) { // com.acme.foo - > com/acme/foo/jaxb.properties - String className = classNameFromPackageProperties(factoryId, classLoader, packages.nextToken(":").replace('.', '/')); - if (className != null) return newInstance(contextPath, className, classLoader, properties); + String factoryClassName = + classNameFromPackageProperties( + classLoader, + packages.nextToken(":").replace('.', '/'), + factoryId, + JAXB_CONTEXT_FACTORY_DEPRECATED); + + if (factoryClassName != null) { + return newInstance(contextPath, factoryClassName, classLoader, properties); + } } String factoryName = classNameFromSystemProperties(); if (factoryName != null) return newInstance(contextPath, factoryName, classLoader, properties); - Class ctxFactory = (Class) ServiceLoaderUtil.lookupUsingOSGiServiceLoader("javax.xml.bind.JAXBContext", logger); - if (ctxFactory != null) { - return newInstance(contextPath, ctxFactory, classLoader, properties); - } + JAXBContextFactory obj = ServiceLoaderUtil.firstByServiceLoader( + JAXBContextFactory.class, logger, EXCEPTION_HANDLER); - // TODO: SPEC change required! This is supposed to be! - // JAXBContext obj = firstByServiceLoader(JAXBContext.class, EXCEPTION_HANDLER); - // if (obj != null) return obj; + if (obj != null) return obj.createContext(contextPath, classLoader, properties); - // TODO: Deprecated - SPEC change required! + // to ensure backwards compatibility factoryName = firstByServiceLoaderDeprecated(JAXBContext.class, classLoader); if (factoryName != null) return newInstance(contextPath, factoryName, classLoader, properties); + Class ctxFactory = (Class) ServiceLoaderUtil.lookupUsingOSGiServiceLoader( + "javax.xml.bind.JAXBContext", logger); + + if (ctxFactory != null) { + return newInstance(contextPath, ctxFactory, classLoader, properties); + } + // else no provider found logger.fine("Trying to create the platform default provider"); return newInstance(contextPath, PLATFORM_DEFAULT_FACTORY_CLASS, classLoader, properties); } - static JAXBContext find(Class[] classes, Map properties) throws JAXBException { + static JAXBContext find(Class[] classes, Map properties) throws JAXBException { // search for jaxb.properties in the class loader of each class first logger.fine("Searching jaxb.properties"); for (final Class c : classes) { // this classloader is used only to load jaxb.properties, so doing this should be safe. - if (c.getPackage() == null) continue; // this is possible for primitives, arrays, and classes that are loaded by poorly implemented ClassLoaders + // this is possible for primitives, arrays, and classes that are + // loaded by poorly implemented ClassLoaders + if (c.getPackage() == null) continue; // TODO: do we want to optimize away searching the same package? org.Foo, org.Bar, com.Baz // classes from the same package might come from different class loades, so it might be a bad idea // TODO: it's easier to look things up from the class // c.getResourceAsStream("jaxb.properties"); - String className = classNameFromPackageProperties(JAXBContext.JAXB_CONTEXT_FACTORY, getClassClassLoader(c), c.getPackage().getName().replace('.', '/')); - if (className != null) return newInstance(classes, properties, className); + String factoryClassName = + classNameFromPackageProperties( + getClassClassLoader(c), + c.getPackage().getName().replace('.', '/'), + JAXBContext.JAXB_CONTEXT_FACTORY, JAXB_CONTEXT_FACTORY_DEPRECATED); + + if (factoryClassName != null) return newInstance(classes, properties, factoryClassName); } - String factoryName = classNameFromSystemProperties(); - if (factoryName != null) return newInstance(classes, properties, factoryName); + String factoryClassName = classNameFromSystemProperties(); + if (factoryClassName != null) return newInstance(classes, properties, factoryClassName); - Class ctxFactoryClass = (Class) ServiceLoaderUtil.lookupUsingOSGiServiceLoader("javax.xml.bind.JAXBContext", logger); - if (ctxFactoryClass != null) { - return newInstance(classes, properties, ctxFactoryClass); - } + JAXBContextFactory factory = + ServiceLoaderUtil.firstByServiceLoader(JAXBContextFactory.class, logger, EXCEPTION_HANDLER); + + if (factory != null) return factory.createContext(classes, properties); - // TODO: to be removed - deprecated!!! Requires SPEC change!!! + // to ensure backwards compatibility String className = firstByServiceLoaderDeprecated(JAXBContext.class, getContextClassLoader()); if (className != null) return newInstance(classes, properties, className); - // // TODO: supposed to be: - // obj = firstByServiceLoader(JAXBContext.class, EXCEPTION_HANDLER); - // if (obj != null) return obj; + logger.fine("Trying to create the platform default provider"); + Class ctxFactoryClass = + (Class) ServiceLoaderUtil.lookupUsingOSGiServiceLoader("javax.xml.bind.JAXBContext", logger); + + if (ctxFactoryClass != null) { + return newInstance(classes, properties, ctxFactoryClass); + } // else no provider found logger.fine("Trying to create the platform default provider"); @@ -339,42 +377,69 @@ } - private static String classNameFromPackageProperties(String factoryId, ClassLoader classLoader, String packageName) throws JAXBException { + /** + * first factoryId should be the preffered one, + * more of those can be provided to support backwards compatibility + */ + private static String classNameFromPackageProperties(ClassLoader classLoader, + String packageName, + String ... factoryIds) throws JAXBException { + String resourceName = packageName + "/jaxb.properties"; logger.log(Level.FINE, "Trying to locate {0}", resourceName); Properties props = loadJAXBProperties(classLoader, resourceName); if (props != null) { - if (props.containsKey(factoryId)) { - return props.getProperty(factoryId); - } else { - throw new JAXBException(Messages.format(Messages.MISSING_PROPERTY, packageName, factoryId)); + for(String factoryId : factoryIds) { + if (props.containsKey(factoryId)) { + return props.getProperty(factoryId); + } } + throw new JAXBException(Messages.format(Messages.MISSING_PROPERTY, packageName, factoryIds[0])); } return null; } private static String classNameFromSystemProperties() throws JAXBException { - logger.log(Level.FINE, "Checking system property {0}", JAXBContext.JAXB_CONTEXT_FACTORY); - // search for a system property second (javax.xml.bind.JAXBContext) - String factoryClassName = AccessController.doPrivileged(new GetPropertyAction(JAXBContext.JAXB_CONTEXT_FACTORY)); + + String factoryClassName = getSystemProperty(JAXBContext.JAXB_CONTEXT_FACTORY); + if (factoryClassName != null) { + return factoryClassName; + } + // leave this here to assure compatibility + factoryClassName = getDeprecatedSystemProperty(JAXB_CONTEXT_FACTORY_DEPRECATED); + if (factoryClassName != null) { + return factoryClassName; + } + // leave this here to assure compatibility + factoryClassName = getDeprecatedSystemProperty(JAXBContext.class.getName()); if (factoryClassName != null) { - logger.log(Level.FINE, " found {0}", factoryClassName); return factoryClassName; - } else { // leave this here to assure compatibility - logger.fine(" not found"); - logger.log(Level.FINE, "Checking system property {0}", JAXBContext.class.getName()); - factoryClassName = AccessController.doPrivileged(new GetPropertyAction(JAXBContext.class.getName())); - if (factoryClassName != null) { - logger.log(Level.FINE, " found {0}", factoryClassName); - return factoryClassName; - } else { - logger.fine(" not found"); - } } return null; } - private static Properties loadJAXBProperties(ClassLoader classLoader, String propFileName) throws JAXBException { + private static String getDeprecatedSystemProperty(String property) { + String value = getSystemProperty(property); + if (value != null) { + logger.log(Level.WARNING, "Using non-standard property: {0}. Property {1} should be used instead.", + new Object[] {property, JAXBContext.JAXB_CONTEXT_FACTORY}); + } + return value; + } + + private static String getSystemProperty(String property) { + logger.log(Level.FINE, "Checking system property {0}", property); + String value = AccessController.doPrivileged(new GetPropertyAction(property)); + if (value != null) { + logger.log(Level.FINE, " found {0}", value); + } else { + logger.log(Level.FINE, " not found"); + } + return value; + } + + private static Properties loadJAXBProperties(ClassLoader classLoader, + String propFileName) throws JAXBException { Properties props = null; try { @@ -480,17 +545,18 @@ } } - // TODO: to be removed - SPEC change required - // ServiceLoaderUtil.firstByServiceLoaderDeprecated should be used instead. + // ServiceLoaderUtil.firstByServiceLoaderDeprecated should be used instead. @Deprecated - static String firstByServiceLoaderDeprecated(Class spiClass, ClassLoader classLoader) throws JAXBException { + static String firstByServiceLoaderDeprecated(Class spiClass, + ClassLoader classLoader) throws JAXBException { + final String jaxbContextFQCN = spiClass.getName(); logger.fine("Searching META-INF/services"); // search META-INF services next BufferedReader r = null; - final String resource = new StringBuilder().append("META-INF/services/").append(jaxbContextFQCN).toString(); + final String resource = "META-INF/services/" + jaxbContextFQCN; try { final InputStream resourceStream = (classLoader == null) ? @@ -510,9 +576,6 @@ logger.log(Level.FINE, "Unable to load:{0}", resource); return null; } - } catch (UnsupportedEncodingException e) { - // should never happen - throw new JAXBException(e); } catch (IOException e) { throw new JAXBException(e); } finally {