jaxws/src/java.xml.ws/share/classes/javax/xml/soap/FactoryFinder.java
changeset 34467 024f1ce7da02
parent 31746 7573de6b8e46
child 36263 d5333008e409
equal deleted inserted replaced
33718:09206c6513b3 34467:024f1ce7da02
    24  */
    24  */
    25 
    25 
    26 package javax.xml.soap;
    26 package javax.xml.soap;
    27 
    27 
    28 import java.io.*;
    28 import java.io.*;
       
    29 import java.nio.charset.StandardCharsets;
       
    30 import java.nio.file.Files;
       
    31 import java.nio.file.Path;
       
    32 import java.nio.file.Paths;
       
    33 import java.security.AccessController;
       
    34 import java.security.PrivilegedAction;
    29 import java.util.Properties;
    35 import java.util.Properties;
       
    36 import java.util.logging.Level;
       
    37 import java.util.logging.Logger;
    30 
    38 
    31 
    39 
    32 class FactoryFinder {
    40 class FactoryFinder {
    33 
    41 
    34     /**
    42     private static final Logger logger = Logger.getLogger("javax.xml.soap");
    35      * Creates an instance of the specified class using the specified
    43 
    36      * {@code ClassLoader} object.
    44     private static final ServiceLoaderUtil.ExceptionHandler<SOAPException> EXCEPTION_HANDLER =
    37      *
    45             new ServiceLoaderUtil.ExceptionHandler<SOAPException>() {
    38      * @exception SOAPException if the given class could not be found
    46                 @Override
    39      *            or could not be instantiated
    47                 public SOAPException createException(Throwable throwable, String message) {
    40      */
    48                     return new SOAPException(message, throwable);
    41     private static Object newInstance(String className,
    49                 }
    42                                       ClassLoader classLoader)
    50             };
    43             throws SOAPException
       
    44     {
       
    45         try {
       
    46             Class spiClass = safeLoadClass(className, classLoader);
       
    47             return spiClass.newInstance();
       
    48 
       
    49         } catch (ClassNotFoundException x) {
       
    50             throw new SOAPException("Provider " + className + " not found", x);
       
    51         } catch (Exception x) {
       
    52             throw new SOAPException("Provider " + className + " could not be instantiated: " + x, x);
       
    53         }
       
    54     }
       
    55 
    51 
    56     /**
    52     /**
    57      * Finds the implementation {@code Class} object for the given
    53      * Finds the implementation {@code Class} object for the given
    58      * factory name, or null if that fails.
    54      * factory type.  If it fails and {@code tryFallback} is {@code true}
    59      * <P>
    55      * finds the {@code Class} object for the given default class name.
    60      * This method is package private so that this code can be shared.
    56      * The arguments supplied must be used in order
    61      *
    57      * Note the default class name may be needed even if fallback
    62      * @return the {@code Class} object of the specified message factory;
    58      * is not to be attempted in order to check if requested type is fallback.
    63      *         or {@code null}
       
    64      *
       
    65      * @param factoryId             the name of the factory to find, which is
       
    66      *                              a system property
       
    67      * @exception SOAPException if there is a SOAP error
       
    68      */
       
    69     static Object find(String factoryId)
       
    70             throws SOAPException
       
    71     {
       
    72         return find(factoryId, null, false);
       
    73     }
       
    74 
       
    75     /**
       
    76      * Finds the implementation {@code Class} object for the given
       
    77      * factory name, or if that fails, finds the {@code Class} object
       
    78      * for the given fallback class name. The arguments supplied must be
       
    79      * used in order. If using the first argument is successful, the second
       
    80      * one will not be used.
       
    81      * <P>
       
    82      * This method is package private so that this code can be shared.
       
    83      *
       
    84      * @return the {@code Class} object of the specified message factory;
       
    85      *         may be {@code null}
       
    86      *
       
    87      * @param factoryId             the name of the factory to find, which is
       
    88      *                              a system property
       
    89      * @param fallbackClassName     the implementation class name, which is
       
    90      *                              to be used only if nothing else
       
    91      *                              is found; {@code null} to indicate that
       
    92      *                              there is no fallback class name
       
    93      * @exception SOAPException if there is a SOAP error
       
    94      */
       
    95     static Object find(String factoryId, String fallbackClassName)
       
    96             throws SOAPException
       
    97     {
       
    98         return find(factoryId, fallbackClassName, true);
       
    99     }
       
   100 
       
   101     /**
       
   102      * Finds the implementation {@code Class} object for the given
       
   103      * factory name, or if that fails, finds the {@code Class} object
       
   104      * for the given default class name, but only if {@code tryFallback}
       
   105      * is {@code true}.  The arguments supplied must be used in order
       
   106      * If using the first argument is successful, the second one will not
       
   107      * be used.  Note the default class name may be needed even if fallback
       
   108      * is not to be attempted, so certain error conditions can be handled.
       
   109      * <P>
    59      * <P>
   110      * This method is package private so that this code can be shared.
    60      * This method is package private so that this code can be shared.
   111      *
    61      *
   112      * @return the {@code Class} object of the specified message factory;
    62      * @return the {@code Class} object of the specified message factory;
   113      *         may not be {@code null}
    63      *         may not be {@code null}
   114      *
    64      *
   115      * @param factoryId             the name of the factory to find, which is
    65      * @param factoryClass          factory abstract class or interface to be found
   116      *                              a system property
    66      * @param deprecatedFactoryId   deprecated name of a factory; it is used for types
       
    67      *                              where class name is different from a name
       
    68      *                              being searched (in previous spec).
   117      * @param defaultClassName      the implementation class name, which is
    69      * @param defaultClassName      the implementation class name, which is
   118      *                              to be used only if nothing else
    70      *                              to be used only if nothing else
   119      *                              is found; {@code null} to indicate
    71      *                              is found; {@code null} to indicate
   120      *                              that there is no default class name
    72      *                              that there is no default class name
   121      * @param tryFallback           whether to try the default class as a
    73      * @param tryFallback           whether to try the default class as a
   122      *                              fallback
    74      *                              fallback
   123      * @exception SOAPException if there is a SOAP error
    75      * @exception SOAPException if there is a SOAP error
   124      */
    76      */
   125     static Object find(String factoryId, String defaultClassName,
    77     @SuppressWarnings("unchecked")
   126                        boolean tryFallback) throws SOAPException {
    78     static <T> T find(Class<T> factoryClass,
   127         ClassLoader classLoader;
    79                       String defaultClassName,
   128         try {
    80                       boolean tryFallback, String deprecatedFactoryId) throws SOAPException {
   129             classLoader = Thread.currentThread().getContextClassLoader();
    81 
   130         } catch (Exception x) {
    82         ClassLoader tccl = ServiceLoaderUtil.contextClassLoader(EXCEPTION_HANDLER);
   131             throw new SOAPException(x.toString(), x);
    83         String factoryId = factoryClass.getName();
   132         }
       
   133 
    84 
   134         // Use the system property first
    85         // Use the system property first
   135         try {
    86         String className = fromSystemProperty(factoryId, deprecatedFactoryId);
   136             String systemProp =
    87         if (className != null) {
   137                     System.getProperty( factoryId );
    88             Object result = newInstance(className, defaultClassName, tccl);
   138             if( systemProp!=null) {
    89             if (result != null) {
   139                 return newInstance(systemProp, classLoader);
    90                 return (T) result;
   140             }
    91             }
   141         } catch (SecurityException se) {
       
   142         }
    92         }
   143 
    93 
   144         // try to read from $java.home/lib/jaxm.properties
    94         // try to read from $java.home/lib/jaxm.properties
   145         try {
    95         className = fromJDKProperties(factoryId, deprecatedFactoryId);
   146             String javah=System.getProperty( "java.home" );
    96         if (className != null) {
   147             String configFile = javah + File.separator +
    97             Object result = newInstance(className, defaultClassName, tccl);
   148                     "lib" + File.separator + "jaxm.properties";
    98             if (result != null) {
   149             File f=new File( configFile );
    99                 return (T) result;
   150             if( f.exists()) {
   100             }
   151                 Properties props=new Properties();
   101         }
   152                 props.load( new FileInputStream(f));
   102 
   153                 String factoryClassName = props.getProperty(factoryId);
   103         // standard services: java.util.ServiceLoader
   154                 return newInstance(factoryClassName, classLoader);
   104         T factory = ServiceLoaderUtil.firstByServiceLoader(
   155             }
   105                 factoryClass,
   156         } catch(Exception ex ) {
   106                 logger,
   157         }
   107                 EXCEPTION_HANDLER);
   158 
   108         if (factory != null) {
   159         String serviceId = "META-INF/services/" + factoryId;
   109             return factory;
       
   110         }
       
   111 
   160         // try to find services in CLASSPATH
   112         // try to find services in CLASSPATH
   161         try {
   113         className = fromMetaInfServices(deprecatedFactoryId, tccl);
   162             InputStream is=null;
   114         if (className != null) {
   163             if (classLoader == null) {
   115             logger.log(Level.WARNING,
   164                 is=ClassLoader.getSystemResourceAsStream(serviceId);
   116                     "Using deprecated META-INF/services mechanism with non-standard property: {0}. " +
   165             } else {
   117                             "Property {1} should be used instead.",
   166                 is=classLoader.getResourceAsStream(serviceId);
   118                     new Object[]{deprecatedFactoryId, factoryId});
   167             }
   119             Object result = newInstance(className, defaultClassName, tccl);
   168 
   120             if (result != null) {
   169             if( is!=null ) {
   121                 return (T) result;
   170                 BufferedReader rd =
   122             }
   171                         new BufferedReader(new InputStreamReader(is, "UTF-8"));
       
   172 
       
   173                 String factoryClassName = rd.readLine();
       
   174                 rd.close();
       
   175 
       
   176                 if (factoryClassName != null &&
       
   177                         ! "".equals(factoryClassName)) {
       
   178                     return newInstance(factoryClassName, classLoader);
       
   179                 }
       
   180             }
       
   181         } catch( Exception ex ) {
       
   182         }
   123         }
   183 
   124 
   184         // If not found and fallback should not be tried, return a null result.
   125         // If not found and fallback should not be tried, return a null result.
   185         if (!tryFallback)
   126         if (!tryFallback)
   186             return null;
   127             return null;
   189         // (built in) factory if specified.
   130         // (built in) factory if specified.
   190         if (defaultClassName == null) {
   131         if (defaultClassName == null) {
   191             throw new SOAPException(
   132             throw new SOAPException(
   192                     "Provider for " + factoryId + " cannot be found", null);
   133                     "Provider for " + factoryId + " cannot be found", null);
   193         }
   134         }
   194         return newInstance(defaultClassName, classLoader);
   135         return (T) newInstance(defaultClassName, defaultClassName, tccl);
   195     }
   136     }
   196 
   137 
   197     /**
   138     // in most cases there is no deprecated factory id
   198      * Loads the class, provided that the calling thread has an access to the
   139     static <T> T find(Class<T> factoryClass,
   199      * class being loaded. If this is the specified default factory class and it
   140                       String defaultClassName,
   200      * is restricted by package.access we get a SecurityException and can do a
   141                       boolean tryFallback) throws SOAPException {
   201      * Class.forName() on it so it will be loaded by the bootstrap class loader.
   142         return find(factoryClass, defaultClassName, tryFallback, null);
   202      */
   143     }
   203     private static Class safeLoadClass(String className,
   144 
   204                                        ClassLoader classLoader)
   145     private static Object newInstance(String className, String defaultClassName, ClassLoader tccl) throws SOAPException {
   205             throws ClassNotFoundException {
   146         return ServiceLoaderUtil.newInstance(
       
   147                 className,
       
   148                 defaultClassName,
       
   149                 tccl,
       
   150                 EXCEPTION_HANDLER);
       
   151     }
       
   152 
       
   153     // used only for deprecatedFactoryId;
       
   154     // proper factoryId searched by java.util.ServiceLoader
       
   155     private static String fromMetaInfServices(String deprecatedFactoryId, ClassLoader tccl) {
       
   156         String serviceId = "META-INF/services/" + deprecatedFactoryId;
       
   157         logger.log(Level.FINE, "Checking deprecated {0} resource", serviceId);
       
   158 
       
   159         try (InputStream is =
       
   160                      tccl == null ?
       
   161                              ClassLoader.getSystemResourceAsStream(serviceId)
       
   162                              :
       
   163                              tccl.getResourceAsStream(serviceId)) {
       
   164 
       
   165             if (is != null) {
       
   166                 String factoryClassName;
       
   167                 try (InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8);
       
   168                      BufferedReader rd = new BufferedReader(isr)) {
       
   169                     factoryClassName = rd.readLine();
       
   170                 }
       
   171 
       
   172                 logFound(factoryClassName);
       
   173                 if (factoryClassName != null && !"".equals(factoryClassName)) {
       
   174                     return factoryClassName;
       
   175                 }
       
   176             }
       
   177 
       
   178         } catch (IOException e) {
       
   179             // keep original behavior
       
   180         }
       
   181         return null;
       
   182     }
       
   183 
       
   184     private static String fromJDKProperties(String factoryId, String deprecatedFactoryId) {
       
   185         Path path = null;
   206         try {
   186         try {
   207             // make sure that the current thread has an access to the package of the given name.
   187             String JAVA_HOME = System.getProperty("java.home");
   208             SecurityManager s = System.getSecurityManager();
   188             path = Paths.get(JAVA_HOME, "conf", "jaxm.properties");
   209             if (s != null) {
   189             logger.log(Level.FINE, "Checking configuration in {0}", path);
   210                 int i = className.lastIndexOf('.');
   190 
   211                 if (i != -1) {
   191             // to ensure backwards compatibility
   212                     s.checkPackageAccess(className.substring(0, i));
   192             if (!Files.exists(path)) {
   213                 }
   193                 path = Paths.get(JAVA_HOME, "lib", "jaxm.properties");
   214             }
   194             }
   215 
   195 
   216             if (classLoader == null)
   196             logger.log(Level.FINE, "Checking configuration in {0}", path);
   217                 return Class.forName(className);
   197             if (Files.exists(path)) {
   218             else
   198                 Properties props = new Properties();
   219                 return classLoader.loadClass(className);
   199                 try (InputStream inputStream = Files.newInputStream(path)) {
   220         } catch (SecurityException se) {
   200                     props.load(inputStream);
   221             // (only) default implementation can be loaded
   201                 }
   222             // using bootstrap class loader:
   202 
   223             if (isDefaultImplementation(className))
   203                 // standard property
   224                 return Class.forName(className);
   204                 logger.log(Level.FINE, "Checking property {0}", factoryId);
   225 
   205                 String factoryClassName = props.getProperty(factoryId);
   226             throw se;
   206                 logFound(factoryClassName);
   227         }
   207                 if (factoryClassName != null) {
   228     }
   208                     return factoryClassName;
   229 
   209                 }
   230     private static boolean isDefaultImplementation(String className) {
   210 
   231         return MessageFactory.DEFAULT_MESSAGE_FACTORY.equals(className) ||
   211                 // deprecated property
   232                 SOAPFactory.DEFAULT_SOAP_FACTORY.equals(className) ||
   212                 if (deprecatedFactoryId != null) {
   233                 SOAPConnectionFactory.DEFAULT_SOAP_CONNECTION_FACTORY.equals(className) ||
   213                     logger.log(Level.FINE, "Checking deprecated property {0}", deprecatedFactoryId);
   234                 SAAJMetaFactory.DEFAULT_META_FACTORY_CLASS.equals(className);
   214                     factoryClassName = props.getProperty(deprecatedFactoryId);
   235     }
   215                     logFound(factoryClassName);
       
   216                     if (factoryClassName != null) {
       
   217                         logger.log(Level.WARNING,
       
   218                                 "Using non-standard property: {0}. Property {1} should be used instead.",
       
   219                                 new Object[]{deprecatedFactoryId, factoryId});
       
   220                         return factoryClassName;
       
   221                     }
       
   222                 }
       
   223             }
       
   224         } catch (Exception ignored) {
       
   225             logger.log(Level.SEVERE, "Error reading SAAJ configuration from ["  + path +
       
   226                     "] file. Check it is accessible and has correct format.", ignored);
       
   227         }
       
   228         return null;
       
   229     }
       
   230 
       
   231     private static String fromSystemProperty(String factoryId, String deprecatedFactoryId) {
       
   232         String systemProp = getSystemProperty(factoryId);
       
   233         if (systemProp != null) {
       
   234             return systemProp;
       
   235         }
       
   236         if (deprecatedFactoryId != null) {
       
   237             systemProp = getSystemProperty(deprecatedFactoryId);
       
   238             if (systemProp != null) {
       
   239                 logger.log(Level.WARNING,
       
   240                         "Using non-standard property: {0}. Property {1} should be used instead.",
       
   241                         new Object[] {deprecatedFactoryId, factoryId});
       
   242                 return systemProp;
       
   243             }
       
   244         }
       
   245         return null;
       
   246     }
       
   247 
       
   248     private static String getSystemProperty(String property) {
       
   249         logger.log(Level.FINE, "Checking system property {0}", property);
       
   250         String value = AccessController.doPrivileged(
       
   251                 (PrivilegedAction<String>) () -> System.getProperty(property));
       
   252         logFound(value);
       
   253         return value;
       
   254     }
       
   255 
       
   256     private static void logFound(String value) {
       
   257         if (value != null) {
       
   258             logger.log(Level.FINE, "  found {0}", value);
       
   259         } else {
       
   260             logger.log(Level.FINE, "  not found");
       
   261         }
       
   262     }
       
   263 
   236 }
   264 }