1 /* 2 * Copyright (c) 2004, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.xml.soap; 27 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; 35 import java.util.Properties; 36 import java.util.logging.Level; 37 import java.util.logging.Logger; 38 39 40 class FactoryFinder { 41 42 private static final Logger logger = Logger.getLogger("javax.xml.soap"); 43 44 private static final ServiceLoaderUtil.ExceptionHandler<SOAPException> EXCEPTION_HANDLER = 45 new ServiceLoaderUtil.ExceptionHandler<SOAPException>() { 46 @Override 47 public SOAPException createException(Throwable throwable, String message) { 48 return new SOAPException(message, throwable); 49 } 50 }; 51 52 /** 53 * Finds the implementation {@code Class} object for the given 54 * factory type. If it fails and {@code tryFallback} is {@code true} 55 * finds the {@code Class} object for the given default class name. 56 * The arguments supplied must be used in order 57 * Note the default class name may be needed even if fallback 58 * is not to be attempted in order to check if requested type is fallback. 59 * <P> 60 * This method is package private so that this code can be shared. 61 * 62 * @return the {@code Class} object of the specified message factory; 63 * may not be {@code null} 64 * 65 * @param factoryClass factory abstract class or interface to be found 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). 69 * @param defaultClassName the implementation class name, which is 70 * to be used only if nothing else 71 * is found; {@code null} to indicate 72 * that there is no default class name 73 * @param tryFallback whether to try the default class as a 74 * fallback 75 * @exception SOAPException if there is a SOAP error 76 */ 77 @SuppressWarnings("unchecked") 78 static <T> T find(Class<T> factoryClass, 79 String defaultClassName, 80 boolean tryFallback, String deprecatedFactoryId) throws SOAPException { 81 82 ClassLoader tccl = ServiceLoaderUtil.contextClassLoader(EXCEPTION_HANDLER); 83 String factoryId = factoryClass.getName(); 84 85 // Use the system property first 86 String className = fromSystemProperty(factoryId, deprecatedFactoryId); 87 if (className != null) { 88 Object result = newInstance(className, defaultClassName, tccl); 89 if (result != null) { 90 return (T) result; 91 } 92 } 93 94 // try to read from $java.home/lib/jaxm.properties 95 className = fromJDKProperties(factoryId, deprecatedFactoryId); 96 if (className != null) { 97 Object result = newInstance(className, defaultClassName, tccl); 98 if (result != null) { 99 return (T) result; 100 } 101 } 102 103 // standard services: java.util.ServiceLoader 104 T factory = ServiceLoaderUtil.firstByServiceLoader( 105 factoryClass, 106 logger, 107 EXCEPTION_HANDLER); 108 if (factory != null) { 109 return factory; 110 } 111 112 // try to find services in CLASSPATH 113 className = fromMetaInfServices(deprecatedFactoryId, tccl); 114 if (className != null) { 115 logger.log(Level.WARNING, 116 "Using deprecated META-INF/services mechanism with non-standard property: {0}. " + 117 "Property {1} should be used instead.", 118 new Object[]{deprecatedFactoryId, factoryId}); 119 Object result = newInstance(className, defaultClassName, tccl); 120 if (result != null) { 121 return (T) result; 122 } 123 } 124 125 // If not found and fallback should not be tried, return a null result. 126 if (!tryFallback) 127 return null; 128 129 // We didn't find the class through the usual means so try the default 130 // (built in) factory if specified. 131 if (defaultClassName == null) { 132 throw new SOAPException( 133 "Provider for " + factoryId + " cannot be found", null); 134 } 135 return (T) newInstance(defaultClassName, defaultClassName, tccl); 136 } 137 138 // in most cases there is no deprecated factory id 139 static <T> T find(Class<T> factoryClass, 140 String defaultClassName, 141 boolean tryFallback) throws SOAPException { 142 return find(factoryClass, defaultClassName, tryFallback, null); 143 } 144 145 private static Object newInstance(String className, String defaultClassName, ClassLoader tccl) throws SOAPException { 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; 186 try { 187 String JAVA_HOME = getSystemProperty("java.home"); 188 path = Paths.get(JAVA_HOME, "conf", "jaxm.properties"); 189 logger.log(Level.FINE, "Checking configuration in {0}", path); 190 191 // to ensure backwards compatibility 192 if (!Files.exists(path)) { 193 path = Paths.get(JAVA_HOME, "lib", "jaxm.properties"); 194 } 195 196 logger.log(Level.FINE, "Checking configuration in {0}", path); 197 if (Files.exists(path)) { 198 Properties props = new Properties(); 199 try (InputStream inputStream = Files.newInputStream(path)) { 200 props.load(inputStream); 201 } 202 203 // standard property 204 logger.log(Level.FINE, "Checking property {0}", factoryId); 205 String factoryClassName = props.getProperty(factoryId); 206 logFound(factoryClassName); 207 if (factoryClassName != null) { 208 return factoryClassName; 209 } 210 211 // deprecated property 212 if (deprecatedFactoryId != null) { 213 logger.log(Level.FINE, "Checking deprecated property {0}", deprecatedFactoryId); 214 factoryClassName = props.getProperty(deprecatedFactoryId); 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 264 }