# HG changeset patch # User mkos # Date 1442576818 -7200 # Node ID ed5d9f5bf8905ae6091ee50590a49152eb048f1c # Parent 52d9ad2536ba6c6f1cc5561c0a0ee2b4847fd62c 8131667: JAX-WS Plugability Layer: using java.util.ServiceLoader Reviewed-by: alanb diff -r 52d9ad2536ba -r ed5d9f5bf890 src/java.xml.ws/share/classes/javax/xml/ws/package-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.xml.ws/share/classes/javax/xml/ws/package-info.java Fri Sep 18 13:46:58 2015 +0200 @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/** + * This package contains the core JAX-WS APIs. + */ +package javax.xml.ws; diff -r 52d9ad2536ba -r ed5d9f5bf890 src/java.xml.ws/share/classes/javax/xml/ws/package.html --- a/src/java.xml.ws/share/classes/javax/xml/ws/package.html Fri Sep 11 10:26:35 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +0,0 @@ - - - - -This package contains the core JAX-WS APIs. - - diff -r 52d9ad2536ba -r ed5d9f5bf890 src/java.xml.ws/share/classes/javax/xml/ws/spi/FactoryFinder.java --- a/src/java.xml.ws/share/classes/javax/xml/ws/spi/FactoryFinder.java Fri Sep 11 10:26:35 2015 -0700 +++ b/src/java.xml.ws/share/classes/javax/xml/ws/spi/FactoryFinder.java Fri Sep 18 13:46:58 2015 +0200 @@ -27,33 +27,25 @@ import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Properties; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.xml.ws.WebServiceException; class FactoryFinder { - /** - * Creates an instance of the specified class using the specified - * {@code ClassLoader} object. - * - * @exception WebServiceException if the given class could not be found - * or could not be instantiated - */ - private static Object newInstance(String className, - ClassLoader classLoader) - { - try { - Class spiClass = safeLoadClass(className, classLoader); - return spiClass.newInstance(); - } catch (ClassNotFoundException x) { - throw new WebServiceException( - "Provider " + className + " not found", x); - } catch (Exception x) { - throw new WebServiceException( - "Provider " + className + " could not be instantiated: " + x, - x); - } - } + private static final Logger logger = Logger.getLogger("javax.xml.ws"); + + private static final ServiceLoaderUtil.ExceptionHandler EXCEPTION_HANDLER = + new ServiceLoaderUtil.ExceptionHandler() { + @Override + public WebServiceException createException(Throwable throwable, String message) { + return new WebServiceException(message, throwable); + } + }; /** * Finds the implementation {@code Class} object for the given @@ -67,7 +59,7 @@ * @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 + * @param factoryClass 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 @@ -75,72 +67,26 @@ * there is no fallback class name * @exception WebServiceException if there is an error */ - static Object find(String factoryId, String fallbackClassName) - { - if (isOsgi()) { - return lookupUsingOSGiServiceLoader(factoryId); - } - ClassLoader classLoader; - try { - classLoader = Thread.currentThread().getContextClassLoader(); - } catch (Exception x) { - throw new WebServiceException(x.toString(), x); - } + @SuppressWarnings("unchecked") + static T find(Class factoryClass, String fallbackClassName) { + ClassLoader classLoader = ServiceLoaderUtil.contextClassLoader(EXCEPTION_HANDLER); - String serviceId = "META-INF/services/" + factoryId; - // try to find services in CLASSPATH - BufferedReader rd = null; - try { - InputStream is; - if (classLoader == null) { - is=ClassLoader.getSystemResourceAsStream(serviceId); - } else { - is=classLoader.getResourceAsStream(serviceId); - } + T provider = ServiceLoaderUtil.firstByServiceLoader(factoryClass, logger, EXCEPTION_HANDLER); + if (provider != null) return provider; - if( is!=null ) { - rd = new BufferedReader(new InputStreamReader(is, "UTF-8")); - - String factoryClassName = rd.readLine(); - - if (factoryClassName != null && - ! "".equals(factoryClassName)) { - return newInstance(factoryClassName, classLoader); - } - } - } catch( Exception ignored) { - } finally { - close(rd); - } - + String factoryId = factoryClass.getName(); // try to read from $java.home/lib/jaxws.properties - FileInputStream inStream = null; - try { - String javah=System.getProperty( "java.home" ); - String configFile = javah + File.separator + - "lib" + File.separator + "jaxws.properties"; - File f=new File( configFile ); - if( f.exists()) { - Properties props=new Properties(); - inStream = new FileInputStream(f); - props.load(inStream); - String factoryClassName = props.getProperty(factoryId); - return newInstance(factoryClassName, classLoader); - } - } catch(Exception ignored) { - } finally { - close(inStream); - } + provider = (T) fromJDKProperties(factoryId, fallbackClassName, classLoader); + if (provider != null) return provider; // Use the system property - try { - String systemProp = - System.getProperty( factoryId ); - if( systemProp!=null) { - return newInstance(systemProp, classLoader); - } - } catch (SecurityException ignored) { + provider = (T) fromSystemProperty(factoryId, fallbackClassName, classLoader); + if (provider != null) return provider; + + // handling Glassfish (platform specific default) + if (isOsgi()) { + return (T) lookupUsingOSGiServiceLoader(factoryId); } if (fallbackClassName == null) { @@ -148,43 +94,51 @@ "Provider for " + factoryId + " cannot be found", null); } - return newInstance(fallbackClassName, classLoader); + return (T) ServiceLoaderUtil.newInstance(fallbackClassName, + fallbackClassName, classLoader, EXCEPTION_HANDLER); } - private static void close(Closeable closeable) { - if (closeable != null) { - try { - closeable.close(); - } catch (IOException ignored) { + private static Object fromSystemProperty(String factoryId, + String fallbackClassName, + ClassLoader classLoader) { + try { + String systemProp = System.getProperty(factoryId); + if (systemProp != null) { + return ServiceLoaderUtil.newInstance(systemProp, + fallbackClassName, classLoader, EXCEPTION_HANDLER); } + } catch (SecurityException ignored) { } + return null; } - - /** - * Loads the class, provided that the calling thread has an access to the class being loaded. - */ - private static Class safeLoadClass(String className, ClassLoader classLoader) throws ClassNotFoundException { + private static Object fromJDKProperties(String factoryId, + String fallbackClassName, + ClassLoader classLoader) { + 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", "jaxws.properties"); + + // to ensure backwards compatibility + if (!Files.exists(path)) { + path = Paths.get(JAVA_HOME, "lib", "jaxws.properties"); } - if (classLoader == null) - return Class.forName(className); - else - return classLoader.loadClass(className); - } catch (SecurityException se) { - // anyone can access the platform default factory class without permission - if (Provider.DEFAULT_JAXWSPROVIDER.equals(className)) - return Class.forName(className); - throw se; + if (!Files.exists(path)) { + Properties props = new Properties(); + try (InputStream inStream = Files.newInputStream(path)) { + props.load(inStream); + } + String factoryClassName = props.getProperty(factoryId); + return ServiceLoaderUtil.newInstance(factoryClassName, + fallbackClassName, classLoader, EXCEPTION_HANDLER); + } + } catch (Exception ignored) { + logger.log(Level.SEVERE, "Error reading JAX-WS configuration from [" + path + + "] file. Check it is accessible and has correct format.", ignored); } + return null; } private static final String OSGI_SERVICE_LOADER_CLASS_NAME = "com.sun.org.glassfish.hk2.osgiresourcelocator.ServiceLoader"; diff -r 52d9ad2536ba -r ed5d9f5bf890 src/java.xml.ws/share/classes/javax/xml/ws/spi/Provider.java --- a/src/java.xml.ws/share/classes/javax/xml/ws/spi/Provider.java Fri Sep 11 10:26:35 2015 -0700 +++ b/src/java.xml.ws/share/classes/javax/xml/ws/spi/Provider.java Fri Sep 18 13:46:58 2015 +0200 @@ -45,19 +45,12 @@ public abstract class Provider { /** - * A constant representing the property used to lookup the - * name of a {@code Provider} implementation - * class. - */ - static public final String JAXWSPROVIDER_PROPERTY = "javax.xml.ws.spi.Provider"; - - /** * A constant representing the name of the default * {@code Provider} implementation class. **/ // Using two strings so that package renaming doesn't change it - static final String DEFAULT_JAXWSPROVIDER - = "com.sun"+".xml.internal.ws.spi.ProviderImpl"; + private static final String DEFAULT_JAXWSPROVIDER = + "com.sun"+".xml.internal.ws.spi.ProviderImpl"; /** * Creates a new instance of Provider @@ -72,47 +65,22 @@ * The algorithm used to locate the provider subclass to use consists * of the following steps: *
    - *
  • - * If a resource with the name of - * {@code META-INF/services/javax.xml.ws.spi.Provider} - * exists, then its first line, if present, is used as the UTF-8 encoded - * name of the implementation class. - *
  • - *
  • - * If the $java.home/lib/jaxws.properties file exists and it is readable by - * the {@code java.util.Properties.load(InputStream)} method and it contains - * an entry whose key is {@code javax.xml.ws.spi.Provider}, then the value of - * that entry is used as the name of the implementation class. - *
  • - *
  • - * If a system property with the name {@code javax.xml.ws.spi.Provider} - * is defined, then its value is used as the name of the implementation class. - *
  • - *
  • - * Finally, a default implementation class name is used. - *
  • + *
  • Use the service-provider loading facilities, defined by the {@link java.util.ServiceLoader} class, + * to attempt to locate and load an implementation of {@link javax.xml.ws.spi.Provider} service using + * the {@linkplain java.util.ServiceLoader#load(java.lang.Class) default loading mechanism}. + *
  • Use the configuration file "jaxws.properties". The file is in standard + * {@link java.util.Properties} format and typically located in the + * {@code conf} directory of the Java installation. It contains the fully qualified + * name of the implementation class with the key {@code javax.xml.ws.spi.Provider}. + *
  • If a system property with the name {@code javax.xml.ws.spi.Provider} + * is defined, then its value is used as the name of the implementation class. + *
  • Finally, a platform default implementation is used. *
* */ public static Provider provider() { try { - Object provider = getProviderUsingServiceLoader(); - if (provider == null) { - provider = FactoryFinder.find(JAXWSPROVIDER_PROPERTY, DEFAULT_JAXWSPROVIDER); - } - if (!(provider instanceof Provider)) { - Class pClass = Provider.class; - String classnameAsResource = pClass.getName().replace('.', '/') + ".class"; - ClassLoader loader = pClass.getClassLoader(); - if(loader == null) { - loader = ClassLoader.getSystemClassLoader(); - } - URL targetTypeURL = loader.getResource(classnameAsResource); - throw new LinkageError("ClassCastException: attempting to cast" + - provider.getClass().getClassLoader().getResource(classnameAsResource) + - "to" + targetTypeURL.toString() ); - } - return (Provider) provider; + return FactoryFinder.find(Provider.class, DEFAULT_JAXWSPROVIDER); } catch (WebServiceException ex) { throw ex; } catch (Exception ex) { @@ -120,18 +88,6 @@ } } - private static Provider getProviderUsingServiceLoader() { - ServiceLoader sl; - Iterator it; - try { - sl = ServiceLoader.load(Provider.class); - it = (Iterator)sl.iterator(); - } catch (Exception e) { - throw new WebServiceException("Cannot invoke java.util.ServiceLoader#iterator()", e); - } - return ((it != null) && it.hasNext()) ? it.next() : null; - } - /** * Creates a service delegate object. * diff -r 52d9ad2536ba -r ed5d9f5bf890 src/java.xml.ws/share/classes/javax/xml/ws/spi/ServiceLoaderUtil.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.xml.ws/share/classes/javax/xml/ws/spi/ServiceLoaderUtil.java Fri Sep 18 13:46:58 2015 +0200 @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package javax.xml.ws.spi; + +import java.util.ServiceLoader; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Shared ServiceLoader/FactoryFinder Utils shared among SAAJ, JAXB and JAXWS + * Class duplicated to all those projects. + * + * @author Miroslav.Kos@oracle.com + */ +class ServiceLoaderUtil { + + static P firstByServiceLoader(Class

spiClass, + Logger logger, + ExceptionHandler handler) throws T { + logger.log(Level.FINE, "Using java.util.ServiceLoader to find {0}", spiClass.getName()); + // service discovery + try { + ServiceLoader

serviceLoader = ServiceLoader.load(spiClass); + + for (P impl : serviceLoader) { + logger.fine("ServiceProvider loading Facility used; returning object [" + + impl.getClass().getName() + "]"); + + return impl; + } + } catch (Throwable t) { + throw handler.createException(t, "Error while searching for service [" + spiClass.getName() + "]"); + } + return null; + } + + static void checkPackageAccess(String className) { + // 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)); + } + } + } + + static Class nullSafeLoadClass(String className, ClassLoader classLoader) throws ClassNotFoundException { + if (classLoader == null) { + return Class.forName(className); + } else { + return classLoader.loadClass(className); + } + } + + // Returns instance of required class. It checks package access (security) + // unless it is defaultClassname. It means if you are trying to instantiate + // default implementation (fallback), pass the class name to both first and second parameter. + static Object newInstance(String className, + String defaultImplClassName, ClassLoader classLoader, + final ExceptionHandler handler) throws T { + try { + return safeLoadClass(className, defaultImplClassName, classLoader).newInstance(); + } catch (ClassNotFoundException x) { + throw handler.createException(x, "Provider " + className + " not found"); + } catch (Exception x) { + throw handler.createException(x, "Provider " + className + " could not be instantiated: " + x); + } + } + + static Class safeLoadClass(String className, + String defaultImplClassName, + ClassLoader classLoader) throws ClassNotFoundException { + + try { + checkPackageAccess(className); + } catch (SecurityException se) { + // anyone can access the platform default factory class without permission + if (defaultImplClassName != null && defaultImplClassName.equals(className)) { + return Class.forName(className); + } + // not platform default implementation ... + throw se; + } + return nullSafeLoadClass(className, classLoader); + } + + static ClassLoader contextClassLoader(ExceptionHandler exceptionHandler) throws T { + try { + return Thread.currentThread().getContextClassLoader(); + } catch (Exception x) { + throw exceptionHandler.createException(x, x.toString()); + } + } + + static abstract class ExceptionHandler { + + public abstract T createException(Throwable throwable, String message); + + } + +}