< prev index next >

src/java.xml.ws/share/classes/javax/xml/soap/FactoryFinder.java

Print this page

        

@@ -24,163 +24,104 @@
  */
 
 package javax.xml.soap;
 
 import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 import java.util.Properties;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 
 class FactoryFinder {
 
-    /**
-     * Creates an instance of the specified class using the specified
-     * {@code ClassLoader} object.
-     *
-     * @exception SOAPException if the given class could not be found
-     *            or could not be instantiated
-     */
-    private static Object newInstance(String className,
-                                      ClassLoader classLoader)
-            throws SOAPException
-    {
-        try {
-            Class spiClass = safeLoadClass(className, classLoader);
-            return spiClass.newInstance();
-
-        } catch (ClassNotFoundException x) {
-            throw new SOAPException("Provider " + className + " not found", x);
-        } catch (Exception x) {
-            throw new SOAPException("Provider " + className + " could not be instantiated: " + x, x);
-        }
-    }
+    private static final Logger logger = Logger.getLogger("javax.xml.soap");
 
-    /**
-     * Finds the implementation {@code Class} object for the given
-     * factory name, or null if that fails.
-     * <P>
-     * This method is package private so that this code can be shared.
-     *
-     * @return the {@code Class} object of the specified message factory;
-     *         or {@code null}
-     *
-     * @param factoryId             the name of the factory to find, which is
-     *                              a system property
-     * @exception SOAPException if there is a SOAP error
-     */
-    static Object find(String factoryId)
-            throws SOAPException
-    {
-        return find(factoryId, null, false);
+    private static final ServiceLoaderUtil.ExceptionHandler<SOAPException> EXCEPTION_HANDLER =
+            new ServiceLoaderUtil.ExceptionHandler<SOAPException>() {
+                @Override
+                public SOAPException createException(Throwable throwable, String message) {
+                    return new SOAPException(message, throwable);
     }
+            };
 
     /**
      * Finds the implementation {@code Class} object for the given
-     * factory name, or if that fails, finds the {@code Class} object
-     * for the given fallback class name. The arguments supplied must be
-     * used in order. If using the first argument is successful, the second
-     * one will not be used.
-     * <P>
-     * This method is package private so that this code can be shared.
-     *
-     * @return the {@code Class} object of the specified message factory;
-     *         may be {@code null}
-     *
-     * @param factoryId             the name of the factory to find, which is
-     *                              a system property
-     * @param fallbackClassName     the implementation class name, which is
-     *                              to be used only if nothing else
-     *                              is found; {@code null} to indicate that
-     *                              there is no fallback class name
-     * @exception SOAPException if there is a SOAP error
-     */
-    static Object find(String factoryId, String fallbackClassName)
-            throws SOAPException
-    {
-        return find(factoryId, fallbackClassName, true);
-    }
-
-    /**
-     * Finds the implementation {@code Class} object for the given
-     * factory name, or if that fails, finds the {@code Class} object
-     * for the given default class name, but only if {@code tryFallback}
-     * is {@code true}.  The arguments supplied must be used in order
-     * If using the first argument is successful, the second one will not
-     * be used.  Note the default class name may be needed even if fallback
-     * is not to be attempted, so certain error conditions can be handled.
+     * factory type.  If it fails and {@code tryFallback} is {@code true}
+     * finds the {@code Class} object for the given default class name.
+     * The arguments supplied must be used in order
+     * Note the default class name may be needed even if fallback
+     * is not to be attempted in order to check if requested type is fallback.
      * <P>
      * This method is package private so that this code can be shared.
      *
      * @return the {@code Class} object of the specified message factory;
      *         may not be {@code null}
      *
-     * @param factoryId             the name of the factory to find, which is
-     *                              a system property
+     * @param factoryClass          factory abstract class or interface to be found
+     * @param deprecatedFactoryId   deprecated name of a factory; it is used for types
+     *                              where class name is different from a name
+     *                              being searched (in previous spec).
      * @param defaultClassName      the implementation class name, which is
      *                              to be used only if nothing else
      *                              is found; {@code null} to indicate
      *                              that there is no default class name
      * @param tryFallback           whether to try the default class as a
      *                              fallback
      * @exception SOAPException if there is a SOAP error
      */
-    static Object find(String factoryId, String defaultClassName,
-                       boolean tryFallback) throws SOAPException {
-        ClassLoader classLoader;
-        try {
-            classLoader = Thread.currentThread().getContextClassLoader();
-        } catch (Exception x) {
-            throw new SOAPException(x.toString(), x);
-        }
+    @SuppressWarnings("unchecked")
+    static <T> T find(Class<T> factoryClass,
+                      String defaultClassName,
+                      boolean tryFallback, String deprecatedFactoryId) throws SOAPException {
+
+        ClassLoader tccl = ServiceLoaderUtil.contextClassLoader(EXCEPTION_HANDLER);
+        String factoryId = factoryClass.getName();
 
         // Use the system property first
-        try {
-            String systemProp =
-                    System.getProperty( factoryId );
-            if( systemProp!=null) {
-                return newInstance(systemProp, classLoader);
+        String className = fromSystemProperty(factoryId, deprecatedFactoryId);
+        if (className != null) {
+            Object result = newInstance(className, defaultClassName, tccl);
+            if (result != null) {
+                return (T) result;
             }
-        } catch (SecurityException se) {
         }
 
         // try to read from $java.home/lib/jaxm.properties
-        try {
-            String javah=System.getProperty( "java.home" );
-            String configFile = javah + File.separator +
-                    "lib" + File.separator + "jaxm.properties";
-            File f=new File( configFile );
-            if( f.exists()) {
-                Properties props=new Properties();
-                props.load( new FileInputStream(f));
-                String factoryClassName = props.getProperty(factoryId);
-                return newInstance(factoryClassName, classLoader);
+        className = fromJDKProperties(factoryId, deprecatedFactoryId);
+        if (className != null) {
+            Object result = newInstance(className, defaultClassName, tccl);
+            if (result != null) {
+                return (T) result;
             }
-        } catch(Exception ex ) {
         }
 
-        String serviceId = "META-INF/services/" + factoryId;
-        // try to find services in CLASSPATH
-        try {
-            InputStream is=null;
-            if (classLoader == null) {
-                is=ClassLoader.getSystemResourceAsStream(serviceId);
-            } else {
-                is=classLoader.getResourceAsStream(serviceId);
+        // standard services: java.util.ServiceLoader
+        T factory = ServiceLoaderUtil.firstByServiceLoader(
+                factoryClass,
+                logger,
+                EXCEPTION_HANDLER);
+        if (factory != null) {
+            return factory;
             }
 
-            if( is!=null ) {
-                BufferedReader rd =
-                        new BufferedReader(new InputStreamReader(is, "UTF-8"));
-
-                String factoryClassName = rd.readLine();
-                rd.close();
-
-                if (factoryClassName != null &&
-                        ! "".equals(factoryClassName)) {
-                    return newInstance(factoryClassName, classLoader);
-                }
+        // try to find services in CLASSPATH
+        className = fromMetaInfServices(deprecatedFactoryId, tccl);
+        if (className != null) {
+            logger.log(Level.WARNING,
+                    "Using deprecated META-INF/services mechanism with non-standard property: {0}. " +
+                            "Property {1} should be used instead.",
+                    new Object[]{deprecatedFactoryId, factoryId});
+            Object result = newInstance(className, defaultClassName, tccl);
+            if (result != null) {
+                return (T) result;
             }
-        } catch( Exception ex ) {
         }
 
         // If not found and fallback should not be tried, return a null result.
         if (!tryFallback)
             return null;

@@ -189,48 +130,135 @@
         // (built in) factory if specified.
         if (defaultClassName == null) {
             throw new SOAPException(
                     "Provider for " + factoryId + " cannot be found", null);
         }
-        return newInstance(defaultClassName, classLoader);
+        return (T) newInstance(defaultClassName, defaultClassName, tccl);
     }
 
-    /**
-     * Loads the class, provided that the calling thread has an access to the
-     * class being loaded. If this is the specified default factory class and it
-     * is restricted by package.access we get a SecurityException and can do a
-     * Class.forName() on it so it will be loaded by the bootstrap class loader.
-     */
-    private static Class safeLoadClass(String className,
-                                       ClassLoader classLoader)
-            throws ClassNotFoundException {
+    // in most cases there is no deprecated factory id
+    static <T> T find(Class<T> factoryClass,
+                      String defaultClassName,
+                      boolean tryFallback) throws SOAPException {
+        return find(factoryClass, defaultClassName, tryFallback, null);
+    }
+
+    private static Object newInstance(String className, String defaultClassName, ClassLoader tccl) throws SOAPException {
+        return ServiceLoaderUtil.newInstance(
+                className,
+                defaultClassName,
+                tccl,
+                EXCEPTION_HANDLER);
+    }
+
+    // used only for deprecatedFactoryId;
+    // proper factoryId searched by java.util.ServiceLoader
+    private static String fromMetaInfServices(String deprecatedFactoryId, ClassLoader tccl) {
+        String serviceId = "META-INF/services/" + deprecatedFactoryId;
+        logger.log(Level.FINE, "Checking deprecated {0} resource", serviceId);
+
+        try (InputStream is =
+                     tccl == null ?
+                             ClassLoader.getSystemResourceAsStream(serviceId)
+                             :
+                             tccl.getResourceAsStream(serviceId)) {
+
+            if (is != null) {
+                String factoryClassName;
+                try (InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8);
+                     BufferedReader rd = new BufferedReader(isr)) {
+                    factoryClassName = rd.readLine();
+                }
+
+                logFound(factoryClassName);
+                if (factoryClassName != null && !"".equals(factoryClassName)) {
+                    return factoryClassName;
+                }
+            }
+
+        } catch (IOException e) {
+            // keep original behavior
+        }
+        return null;
+    }
+
+    private static String fromJDKProperties(String factoryId, String deprecatedFactoryId) {
+        Path path = null;
         try {
-            // make sure that the current thread has an access to the package of the given name.
-            SecurityManager s = System.getSecurityManager();
-            if (s != null) {
-                int i = className.lastIndexOf('.');
-                if (i != -1) {
-                    s.checkPackageAccess(className.substring(0, i));
+            String JAVA_HOME = System.getProperty("java.home");
+            path = Paths.get(JAVA_HOME, "conf", "jaxm.properties");
+            logger.log(Level.FINE, "Checking configuration in {0}", path);
+
+            // to ensure backwards compatibility
+            if (!Files.exists(path)) {
+                path = Paths.get(JAVA_HOME, "lib", "jaxm.properties");
                 }
+
+            logger.log(Level.FINE, "Checking configuration in {0}", path);
+            if (Files.exists(path)) {
+                Properties props = new Properties();
+                try (InputStream inputStream = Files.newInputStream(path)) {
+                    props.load(inputStream);
             }
 
-            if (classLoader == null)
-                return Class.forName(className);
-            else
-                return classLoader.loadClass(className);
-        } catch (SecurityException se) {
-            // (only) default implementation can be loaded
-            // using bootstrap class loader:
-            if (isDefaultImplementation(className))
-                return Class.forName(className);
-
-            throw se;
+                // standard property
+                logger.log(Level.FINE, "Checking property {0}", factoryId);
+                String factoryClassName = props.getProperty(factoryId);
+                logFound(factoryClassName);
+                if (factoryClassName != null) {
+                    return factoryClassName;
+                }
+
+                // deprecated property
+                if (deprecatedFactoryId != null) {
+                    logger.log(Level.FINE, "Checking deprecated property {0}", deprecatedFactoryId);
+                    factoryClassName = props.getProperty(deprecatedFactoryId);
+                    logFound(factoryClassName);
+                    if (factoryClassName != null) {
+                        logger.log(Level.WARNING,
+                                "Using non-standard property: {0}. Property {1} should be used instead.",
+                                new Object[]{deprecatedFactoryId, factoryId});
+                        return factoryClassName;
         }
     }
+            }
+        } catch (Exception ignored) {
+            logger.log(Level.SEVERE, "Error reading SAAJ configuration from ["  + path +
+                    "] file. Check it is accessible and has correct format.", ignored);
+        }
+        return null;
+    }
 
-    private static boolean isDefaultImplementation(String className) {
-        return MessageFactory.DEFAULT_MESSAGE_FACTORY.equals(className) ||
-                SOAPFactory.DEFAULT_SOAP_FACTORY.equals(className) ||
-                SOAPConnectionFactory.DEFAULT_SOAP_CONNECTION_FACTORY.equals(className) ||
-                SAAJMetaFactory.DEFAULT_META_FACTORY_CLASS.equals(className);
+    private static String fromSystemProperty(String factoryId, String deprecatedFactoryId) {
+        String systemProp = getSystemProperty(factoryId);
+        if (systemProp != null) {
+            return systemProp;
+        }
+        if (deprecatedFactoryId != null) {
+            systemProp = getSystemProperty(deprecatedFactoryId);
+            if (systemProp != null) {
+                logger.log(Level.WARNING,
+                        "Using non-standard property: {0}. Property {1} should be used instead.",
+                        new Object[] {deprecatedFactoryId, factoryId});
+                return systemProp;
+            }
     }
+        return null;
+    }
+
+    private static String getSystemProperty(String property) {
+        logger.log(Level.FINE, "Checking system property {0}", property);
+        String value = AccessController.doPrivileged(
+                (PrivilegedAction<String>) () -> System.getProperty(property));
+        logFound(value);
+        return value;
+    }
+
+    private static void logFound(String value) {
+        if (value != null) {
+            logger.log(Level.FINE, "  found {0}", value);
+        } else {
+            logger.log(Level.FINE, "  not found");
+        }
+    }
+
 }
< prev index next >