< prev index next >

src/java.xml.bind/share/classes/javax/xml/bind/ContextFinder.java

Print this page

        

@@ -66,10 +66,13 @@
      * <p>
      * For this reason, we have to hard-code the class name into the API.
      */
     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 {
         logger = Logger.getLogger("javax.xml.bind");
         try {

@@ -90,10 +93,18 @@
             // just to be extra safe. in particular System.getProperty may throw
             // SecurityException.
         }
     }
 
+    private static ServiceLoaderUtil.ExceptionHandler<JAXBException> EXCEPTION_HANDLER =
+            new ServiceLoaderUtil.ExceptionHandler<JAXBException>() {
+                @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.
      */
     private static void handleInvocationTargetException(InvocationTargetException x) throws JAXBException {

@@ -157,11 +168,14 @@
             // some other type of exception - just wrap it
             throw new JAXBException(Messages.format(Messages.COULD_NOT_INSTANTIATE, className, x), x);
         }
     }
 
-    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 {
             /*
              * javax.xml.bind.context.factory points to a class which has a
              * static method called 'createContext' that

@@ -237,21 +251,22 @@
 
     static JAXBContext newInstance(Class[] classes,
                                    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)) {
                 // the cast would fail, so generate an exception with a nice message
                 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);
 
             Throwable x = e;
             if (e.getTargetException() != null)

@@ -259,13 +274,14 @@
 
             throw new JAXBException(x);
         }
     }
 
-    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()) {
             // no context is specified
             throw new JAXBException(Messages.format(Messages.NO_PACKAGE_IN_CONTEXTPATH));

@@ -273,110 +289,159 @@
 
         // search for jaxb.properties in the class loader of each class first
         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<String, ?> 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");
         return newInstance(classes, properties, PLATFORM_DEFAULT_FACTORY_CLASS);
     }
 
 
-    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) {
+            for(String factoryId : factoryIds) {
             if (props.containsKey(factoryId)) {
                 return props.getProperty(factoryId);
-            } else {
-                throw new JAXBException(Messages.format(Messages.MISSING_PROPERTY, packageName, 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) {
-            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()));
+        }
+        // leave this here to assure compatibility
+        factoryClassName = getDeprecatedSystemProperty(JAXB_CONTEXT_FACTORY_DEPRECATED);
             if (factoryClassName != null) {
-                logger.log(Level.FINE, "  found {0}", factoryClassName);
                 return factoryClassName;
-            } else {
-                logger.fine("  not found");
             }
+        // leave this here to assure compatibility
+        factoryClassName = getDeprecatedSystemProperty(JAXBContext.class.getName());
+        if (factoryClassName != null) {
+            return factoryClassName;
         }
         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 {
             URL url;
             if (classLoader == null)

@@ -478,21 +543,22 @@
                         }
                     });
         }
     }
 
-    // TODO: to be removed - SPEC change required
     //    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) ?
                             ClassLoader.getSystemResourceAsStream(resource) :
                             classLoader.getResourceAsStream(resource);

@@ -508,13 +574,10 @@
                 return factoryClassName;
             } else {
                 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 {
             try {
                 if (r != null) {
< prev index next >