From 2fc2446980711533cf11c31ca535f3f677c389b6 Mon Sep 17 00:00:00 2001 From: Lukas Jungmann Date: Tue, 13 Jul 2021 16:47:25 +0200 Subject: [PATCH] #194: Simplify implementation lookup Signed-off-by: Lukas Jungmann --- .../java/jakarta/xml/bind/ContextFinder.java | 177 +----------------- .../java/jakarta/xml/bind/JAXBContext.java | 95 +--------- spec/src/main/asciidoc/appI-changelog.adoc | 6 +- .../main/asciidoc/ch04-binding_framework.adoc | 23 +-- 4 files changed, 14 insertions(+), 287 deletions(-) diff --git a/jaxb-api/src/main/java/jakarta/xml/bind/ContextFinder.java b/jaxb-api/src/main/java/jakarta/xml/bind/ContextFinder.java index 65667d8..6435cd1 100644 --- a/jaxb-api/src/main/java/jakarta/xml/bind/ContextFinder.java +++ b/jaxb-api/src/main/java/jakarta/xml/bind/ContextFinder.java @@ -10,10 +10,8 @@ package jakarta.xml.bind; -import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URL; @@ -32,16 +30,13 @@ * This class is package private and therefore is not exposed as part of the * Jakarta XML Binding API. * - * This code is designed to implement the JAXB 1.0 spec pluggability feature + * This code is designed to implement the XML Binding spec pluggability feature * * @author * @see JAXBContext */ class ContextFinder { - // previous value of JAXBContext.JAXB_CONTEXT_FACTORY, using also this to ensure backwards compatibility - private static final String JAXB_CONTEXT_FACTORY_DEPRECATED = "jakarta.xml.bind.context.factory"; - private static final Logger logger; /** @@ -303,18 +298,6 @@ static JAXBContext find(String factoryId, //ModuleUtil is mr-jar class, scans context path for jaxb classes on jdk9 and higher Class[] contextPathClasses = ModuleUtil.getClassesFromContextPath(contextPath, classLoader); - //first try with classloader#getResource - String factoryClassName = jaxbProperties(contextPath, classLoader, factoryId); - if (factoryClassName == null && contextPathClasses != null) { - //try with class#getResource - factoryClassName = jaxbProperties(contextPathClasses, factoryId); - } - - if (factoryClassName != null) { - return newInstance(contextPath, contextPathClasses, factoryClassName, classLoader, properties); - } - - String factoryName = classNameFromSystemProperties(); if (factoryName != null) return newInstance(contextPath, contextPathClasses, factoryName, classLoader, properties); @@ -326,12 +309,8 @@ static JAXBContext find(String factoryId, return obj.createContext(contextPath, classLoader, properties); } - // to ensure backwards compatibility - factoryName = firstByServiceLoaderDeprecated(JAXBContext.class, classLoader); - if (factoryName != null) return newInstance(contextPath, contextPathClasses, factoryName, classLoader, properties); - Class ctxFactory = (Class) ServiceLoaderUtil.lookupUsingOSGiServiceLoader( - "jakarta.xml.bind.JAXBContext", logger); + JAXBContext.JAXB_CONTEXT_FACTORY, logger); if (ctxFactory != null) { return newInstance(contextPath, contextPathClasses, ctxFactory, classLoader, properties); @@ -343,34 +322,6 @@ static JAXBContext find(String factoryId, } 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. - // 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"); - - URL jaxbPropertiesUrl = getResourceUrl(c, "jaxb.properties"); - - if (jaxbPropertiesUrl != null) { - - String factoryClassName = - classNameFromPackageProperties( - jaxbPropertiesUrl, - JAXBContext.JAXB_CONTEXT_FACTORY, JAXB_CONTEXT_FACTORY_DEPRECATED); - - return newInstance(classes, properties, factoryClassName, getClassClassLoader(c)); - } - - } - String factoryClassName = classNameFromSystemProperties(); if (factoryClassName != null) return newInstance(classes, properties, factoryClassName); @@ -382,22 +333,9 @@ static JAXBContext find(Class[] classes, Map properties) throws JA return factory.createContext(classes, properties); } - // to ensure backwards compatibility - ClassLoader loader = getContextClassLoader(); - // it is guaranteed classes are not null but it is not guaranteed, that array is not empty - if (classes.length > 0) { - ClassLoader c = getClassClassLoader(classes[0]); - //switch to classloader which loaded the class if it is not a bootstrap cl - if (c != null) { - loader = c; - } - } - String className = firstByServiceLoaderDeprecated(JAXBContext.class, loader); - if (className != null) return newInstance(classes, properties, className, loader); - logger.fine("Trying to create the platform default provider"); Class ctxFactoryClass = - (Class) ServiceLoaderUtil.lookupUsingOSGiServiceLoader("jakarta.xml.bind.JAXBContext", logger); + (Class) ServiceLoaderUtil.lookupUsingOSGiServiceLoader(JAXBContext.JAXB_CONTEXT_FACTORY, logger); if (ctxFactoryClass != null) { return newInstance(classes, properties, ctxFactoryClass); @@ -408,53 +346,14 @@ static JAXBContext find(Class[] classes, Map properties) throws JA return newInstance(classes, properties, DEFAULT_FACTORY_CLASS); } - - /** - * first factoryId should be the preferred one, - * more of those can be provided to support backwards compatibility - */ - private static String classNameFromPackageProperties(URL packagePropertiesUrl, - String ... factoryIds) throws JAXBException { - - logger.log(Level.FINE, "Trying to locate {0}", packagePropertiesUrl.toString()); - Properties props = loadJAXBProperties(packagePropertiesUrl); - for(String factoryId : factoryIds) { - if (props.containsKey(factoryId)) { - return props.getProperty(factoryId); - } - } - //Factory key not found - String propertiesUrl = packagePropertiesUrl.toExternalForm(); - String packageName = propertiesUrl.substring(0, propertiesUrl.indexOf("/jaxb.properties")); - throw new JAXBException(Messages.format(Messages.MISSING_PROPERTY, packageName, factoryIds[0])); - } - private static String classNameFromSystemProperties() throws JAXBException { 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) { - return factoryClassName; - } - return null; - } - 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; + return null; } private static String getSystemProperty(String property) { @@ -588,72 +487,4 @@ public ClassLoader run() { } } - // ServiceLoaderUtil.firstByServiceLoaderDeprecated should be used instead. - @Deprecated - 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 = "META-INF/services/" + jaxbContextFQCN; - try { - final InputStream resourceStream = - (classLoader == null) ? - ClassLoader.getSystemResourceAsStream(resource) : - classLoader.getResourceAsStream(resource); - - if (resourceStream != null) { - r = new BufferedReader(new InputStreamReader(resourceStream, "UTF-8")); - String factoryClassName = r.readLine(); - if (factoryClassName != null) { - factoryClassName = factoryClassName.trim(); - } - r.close(); - logger.log(Level.FINE, "Configured factorty class:{0}", factoryClassName); - return factoryClassName; - } else { - logger.log(Level.FINE, "Unable to load:{0}", resource); - return null; - } - } catch (IOException e) { - throw new JAXBException(e); - } finally { - try { - if (r != null) { - r.close(); - } - } catch (IOException ex) { - logger.log(Level.SEVERE, "Unable to close resource: " + resource, ex); - } - } - } - - private static String jaxbProperties(String contextPath, ClassLoader classLoader, String factoryId) throws JAXBException { - String[] packages = contextPath.split(":"); - - for (String pkg : packages) { - String pkgUrl = pkg.replace('.', '/'); - URL jaxbPropertiesUrl = getResourceUrl(classLoader, pkgUrl + "/jaxb.properties"); - if (jaxbPropertiesUrl != null) { - return classNameFromPackageProperties(jaxbPropertiesUrl, - factoryId, JAXB_CONTEXT_FACTORY_DEPRECATED); - } - } - return null; - } - - private static String jaxbProperties(Class[] classesFromContextPath, String factoryId) throws JAXBException { - for (Class c : classesFromContextPath) { - URL jaxbPropertiesUrl = getResourceUrl(c, "jaxb.properties"); - if (jaxbPropertiesUrl != null) { - return classNameFromPackageProperties(jaxbPropertiesUrl, factoryId, JAXB_CONTEXT_FACTORY_DEPRECATED); - } - } - return null; - } - } diff --git a/jaxb-api/src/main/java/jakarta/xml/bind/JAXBContext.java b/jaxb-api/src/main/java/jakarta/xml/bind/JAXBContext.java index 5967785..e3a99bc 100644 --- a/jaxb-api/src/main/java/jakarta/xml/bind/JAXBContext.java +++ b/jaxb-api/src/main/java/jakarta/xml/bind/JAXBContext.java @@ -13,10 +13,8 @@ import org.w3c.dom.Node; import java.io.IOException; -import java.io.InputStream; import java.util.Collections; import java.util.Map; -import java.util.Properties; /** * The {@code JAXBContext} class provides the client's entry point to the @@ -44,21 +42,7 @@ * * *

- * The following JAXB 1.0 requirement is only required for schema to - * java interface/implementation binding. It does not apply to Jakarta XML Binding annotated - * classes. Jakarta XML Binding Providers must generate a {@code jaxb.properties} file in - * each package containing schema derived classes. The property file must - * contain a property named {@code jakarta.xml.bind.context.factory} whose - * value is the name of the class that implements the {@code createContext} - * APIs. - * - *

- * The class supplied by the provider does not have to be assignable to - * {@code jakarta.xml.bind.JAXBContext}, it simply has to provide a class that - * implements the {@code createContext} APIs. - * - *

- * In addition, the provider must call the + * The provider must call the * {@link DatatypeConverter#setDatatypeConverter(DatatypeConverterInterface) * DatatypeConverter.setDatatypeConverter} api prior to any client * invocations of the marshal and unmarshal methods. This is necessary to @@ -191,29 +175,8 @@ *

    * *
  1. - * Packages/classes explicitly passed in to the {@link #newInstance} method are processed in the order they are - * specified, until {@code jaxb.properties} file is looked up in its package, by using the associated classloader — - * this is {@link Class#getClassLoader() the owner class loader} for a {@link Class} argument, and for a package - * the specified {@link ClassLoader}. - * - *

    - * If such a resource is discovered, it is {@link Properties#load(InputStream) loaded} as a property file, and - * the value of the {@link #JAXB_CONTEXT_FACTORY} key will be assumed to be the provider factory class. If no value - * found, {@code "jakarta.xml.bind.context.factory"} is used as a key for backwards compatibility reasons. This class is - * then loaded by the associated class loader discussed above. - * - *

    - * This phase of the look up allows some packages to force the use of a certain Jakarta XML Binding implementation. - * (For example, perhaps the schema compiler has generated some vendor extension in the code.) - * - *

    - * This configuration method is deprecated. - * - *

  2. * If the system property {@link #JAXB_CONTEXT_FACTORY} exists, then its value is assumed to be the provider - * factory class. If no such property exists, properties {@code "jakarta.xml.bind.context.factory"} and - * {@code "jakarta.xml.bind.JAXBContext"} are checked too (in this order), for backwards compatibility reasons. This phase - * of the look up enables per-JVM override of the Jakarta XML Binding implementation. + * factory class. This phase of the look up enables per-JVM override of the Jakarta XML Binding implementation. * *
  3. * Provider of {@link jakarta.xml.bind.JAXBContextFactory} is loaded using the service-provider loading @@ -228,57 +191,15 @@ * configuration error} a {@link jakarta.xml.bind.JAXBException} will be thrown. * *
  4. - * Look for resource {@code /META-INF/services/jakarta.xml.bind.JAXBContext} using provided class loader. - * Methods without class loader parameter use {@code Thread.currentThread().getContextClassLoader()}. - * If such a resource exists, its content is assumed to be the provider factory class. - * - * This configuration method is deprecated. - * - *
  5. * Finally, if all the steps above fail, then the rest of the look up is unspecified. That said, * the recommended behavior is to simply look for some hard-coded platform default Jakarta XML Binding implementation. - * This phase of the look up is so that Java SE can have its own JAXB implementation as the last resort. + * This phase of the look up is so that the environment can have its own Jakarta XML Binding implementation as the last resort. *
* *

* Once the provider factory class is discovered, context creation is delegated to one of its * {@code createContext(...)} methods. * - * For backward compatibility reasons, there are two ways how to implement provider factory class: - *

    - *
  1. the class is implementation of {@link jakarta.xml.bind.JAXBContextFactory}. It must also implement no-arg - * constructor. If discovered in other step then 3, new instance using no-arg constructor is created first. - * After that, appropriate instance method is invoked on this instance. - *
  2. the class is not implementation of interface above and then it is mandated to implement the following - * static method signatures: - *
    - *
    - * public static JAXBContext createContext(
    - *                                      String contextPath,
    - *                                      ClassLoader classLoader,
    - *                                      Map<String,Object> properties ) throws JAXBException
    - *
    - * public static JAXBContext createContext(
    - *                                      Class[] classes,
    - *                                      Map<String,Object> properties ) throws JAXBException
    - * 
    - * In this scenario, appropriate static method is used instead of instance method. This approach is incompatible - * with {@link java.util.ServiceLoader} so it can't be used with step 3. - *
- *

- * There is no difference in behavior of given method {@code createContext(...)} regardless of whether it uses approach - * 1 (JAXBContextFactory) or 2 (no interface, static methods). - * - * @apiNote - * Service discovery method using resource {@code /META-INF/services/jakarta.xml.bind.JAXBContext} (described in step 4) - * is supported only to allow backwards compatibility, it is strongly recommended to migrate to standard - * {@link java.util.ServiceLoader} mechanism (described in step 3). The difference here is the resource name, which - * doesn't match service's type name. - *

- * Also using providers implementing interface {@link JAXBContextFactory} is preferred over using ones defining - * static methods, same as {@link JAXBContext#JAXB_CONTEXT_FACTORY} property is preferred over property - * {@code "jakarta.xml.bind.context.factory"} - * * @implNote * Within the last step, if Glassfish AS environment detected, its specific service loader is used to find factory class. * @@ -381,16 +302,6 @@ public static JAXBContext newInstance( String contextPath ) * * *

- * To maintain compatibility with JAXB 1.0 schema to java - * interface/implementation binding, enabled by schema customization - * {@code }, - * the Jakarta XML Binding provider will ensure that each package on the context path - * has a {@code jaxb.properties} file which contains a value for the - * {@code jakarta.xml.bind.context.factory} property and that all values - * resolve to the same provider. This requirement does not apply to - * Jakarta XML Binding annotated classes. - * - *

* If there are any global XML element name collisions across the various * packages listed on the {@code contextPath}, a {@code JAXBException} * will be thrown. diff --git a/spec/src/main/asciidoc/appI-changelog.adoc b/spec/src/main/asciidoc/appI-changelog.adoc index b6f941b..0245fe9 100644 --- a/spec/src/main/asciidoc/appI-changelog.adoc +++ b/spec/src/main/asciidoc/appI-changelog.adoc @@ -8,8 +8,12 @@ === Changes in Version 4 * fixed cross-references in the specification document -* removed deprecated jakarta.xml.bind.Validator +* removed deprecated `jakarta.xml.bind.Validator` * removed constraints on using `java.beans.Introspector` +* removed deprecated steps in implementation lookup algorithm - dropped search +through `jaxb.properties` file, `jakarta.xml.bind.context.factory` and +`jakarta.xml.bind.JAXBContext` properties and `/META-INF/services/jakarta.xml.bind.JAXBContext` +resource file === Changes in Version 3 diff --git a/spec/src/main/asciidoc/ch04-binding_framework.adoc b/spec/src/main/asciidoc/ch04-binding_framework.adoc index 2b1d522..65e15b0 100644 --- a/spec/src/main/asciidoc/ch04-binding_framework.adoc +++ b/spec/src/main/asciidoc/ch04-binding_framework.adoc @@ -792,34 +792,15 @@ discovery happens each time `JAXBContext.newInstance` is invoked. Implementation discovery consists of following steps in the order specified (first successful resolution applies): -. Context path or classes’ packages explicitly passed in -to the newInstance method are searched for the jaxb.properties file. + - + -If such a resource is discovered, it is loaded as a property file, -and the value of the `jakarta.xml.bind.JAXBContextFactory` key -will be assumed to be the provider factory class. -If no value found, `jakarta.xml.bind.context.factory` is used -as a key for backwards compatibility reasons. + - + -This configuration method is deprecated. - . If the system property `jakarta.xml.bind.JAXBContextFactory` exists, -then its value is assumed to be the provider factory class. -If no such property exists, properties `jakarta.xml.bind.context.factory` -and `jakarta.xml.bind.JAXBContext` are checked too (in this order), -for backwards compatibility reasons. +then its value is assumed to be the provider factory class. This phase +of the look up enables per-JVM override of the Jakarta XML Binding implementation. . Provider of `jakarta.xml.bind.JAXBContextFactory` is loaded using the service-provider loading facilities, as defined by Java SE Platform, to attempt to locate and load an implementation of the service. -. Look for resource `/META-INF/services/jakarta.xml.bind.JAXBContext`. -If such a resource exists, its content is assumed to be the provider -factory class. + - + - This configuration method is deprecated. - . Finally, if all of the steps above fail, then the rest of the look up is unspecified.