1 /* 2 * Copyright (c) 1997, 2015, 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 com.sun.xml.internal.ws.assembler; 27 28 import com.sun.istack.internal.NotNull; 29 import com.sun.istack.internal.logging.Logger; 30 import com.sun.xml.internal.ws.api.ResourceLoader; 31 import com.sun.xml.internal.ws.api.server.Container; 32 import com.sun.xml.internal.ws.resources.TubelineassemblyMessages; 33 import com.sun.xml.internal.ws.runtime.config.MetroConfig; 34 import com.sun.xml.internal.ws.runtime.config.TubeFactoryList; 35 import com.sun.xml.internal.ws.runtime.config.TubelineDefinition; 36 import com.sun.xml.internal.ws.runtime.config.TubelineMapping; 37 import com.sun.xml.internal.ws.util.xml.XmlUtil; 38 39 import javax.xml.bind.JAXBContext; 40 import javax.xml.bind.JAXBElement; 41 import javax.xml.bind.Unmarshaller; 42 import javax.xml.stream.XMLInputFactory; 43 import javax.xml.ws.WebServiceException; 44 import java.io.IOException; 45 import java.io.InputStream; 46 import java.lang.reflect.Method; 47 import java.net.MalformedURLException; 48 import java.net.URI; 49 import java.net.URISyntaxException; 50 import java.net.URL; 51 import java.security.AccessController; 52 import java.security.PrivilegedExceptionAction; 53 import java.util.logging.Level; 54 55 /** 56 * This class is responsible for locating and loading Metro configuration files 57 * (both application jaxws-tubes.xml and default jaxws-tubes-default.xml). 58 * <p/> 59 * Once the configuration is loaded the class is able to resolve which tubeline 60 * configuration belongs to each endpoint or endpoint client. This information is 61 * then used in {@link TubelineAssemblyController} to construct the list of 62 * {@link TubeCreator} objects that are used in the actual tubeline construction. 63 * 64 * @author Marek Potociar <marek.potociar at sun.com> 65 */ 66 // TODO Move the logic of this class directly into MetroConfig class. 67 class MetroConfigLoader { 68 69 private static final Logger LOGGER = Logger.getLogger(MetroConfigLoader.class); 70 71 private MetroConfigName defaultTubesConfigNames; 72 73 private static interface TubeFactoryListResolver { 74 75 TubeFactoryList getFactories(TubelineDefinition td); 76 } 77 78 private static final TubeFactoryListResolver ENDPOINT_SIDE_RESOLVER = new TubeFactoryListResolver() { 79 80 public TubeFactoryList getFactories(TubelineDefinition td) { 81 return (td != null) ? td.getEndpointSide() : null; 82 } 83 }; 84 private static final TubeFactoryListResolver CLIENT_SIDE_RESOLVER = new TubeFactoryListResolver() { 85 86 public TubeFactoryList getFactories(TubelineDefinition td) { 87 return (td != null) ? td.getClientSide() : null; 88 } 89 }; 90 // 91 private MetroConfig defaultConfig; 92 private URL defaultConfigUrl; 93 private MetroConfig appConfig; 94 private URL appConfigUrl; 95 96 MetroConfigLoader(Container container, MetroConfigName defaultTubesConfigNames) { 97 this.defaultTubesConfigNames = defaultTubesConfigNames; 98 ResourceLoader spiResourceLoader = null; 99 if (container != null) { 100 spiResourceLoader = container.getSPI(ResourceLoader.class); 101 } 102 // if spi resource can't load resource, default (MetroConfigUrlLoader) is used; 103 // it searches the classpath, so it would be most probably used 104 // when using jaxws- or metro-defaults from jaxws libraries 105 init(container, spiResourceLoader, new MetroConfigUrlLoader(container)); 106 } 107 108 private void init(Container container, ResourceLoader... loaders) { 109 110 String appFileName = null; 111 String defaultFileName = null; 112 if (container != null) { 113 MetroConfigName mcn = container.getSPI(MetroConfigName.class); 114 if (mcn != null) { 115 appFileName = mcn.getAppFileName(); 116 defaultFileName = mcn.getDefaultFileName(); 117 } 118 } 119 if (appFileName == null) { 120 appFileName = defaultTubesConfigNames.getAppFileName(); 121 } 122 123 if (defaultFileName == null) { 124 defaultFileName = defaultTubesConfigNames.getDefaultFileName(); 125 } 126 this.defaultConfigUrl = locateResource(defaultFileName, loaders); 127 128 // TODO-Miran: revisit 129 // if (defaultConfigUrl == null) { 130 // throw LOGGER.logSevereException(new IllegalStateException(TubelineassemblyMessages.MASM_0001_DEFAULT_CFG_FILE_NOT_FOUND(defaultFileName))); 131 // } 132 133 LOGGER.config(TubelineassemblyMessages.MASM_0002_DEFAULT_CFG_FILE_LOCATED(defaultFileName, defaultConfigUrl)); 134 this.defaultConfig = MetroConfigLoader.loadMetroConfig(defaultConfigUrl); 135 if (defaultConfig == null) { 136 throw LOGGER.logSevereException(new IllegalStateException(TubelineassemblyMessages.MASM_0003_DEFAULT_CFG_FILE_NOT_LOADED(defaultFileName))); 137 } 138 if (defaultConfig.getTubelines() == null) { 139 throw LOGGER.logSevereException(new IllegalStateException(TubelineassemblyMessages.MASM_0004_NO_TUBELINES_SECTION_IN_DEFAULT_CFG_FILE(defaultFileName))); 140 } 141 if (defaultConfig.getTubelines().getDefault() == null) { 142 throw LOGGER.logSevereException(new IllegalStateException(TubelineassemblyMessages.MASM_0005_NO_DEFAULT_TUBELINE_IN_DEFAULT_CFG_FILE(defaultFileName))); 143 } 144 145 this.appConfigUrl = locateResource(appFileName, loaders); 146 if (appConfigUrl != null) { 147 LOGGER.config(TubelineassemblyMessages.MASM_0006_APP_CFG_FILE_LOCATED(appConfigUrl)); 148 this.appConfig = MetroConfigLoader.loadMetroConfig(appConfigUrl); 149 } else { 150 LOGGER.config(TubelineassemblyMessages.MASM_0007_APP_CFG_FILE_NOT_FOUND()); 151 this.appConfig = null; 152 } 153 } 154 155 TubeFactoryList getEndpointSideTubeFactories(URI endpointReference) { 156 return getTubeFactories(endpointReference, ENDPOINT_SIDE_RESOLVER); 157 } 158 159 TubeFactoryList getClientSideTubeFactories(URI endpointReference) { 160 return getTubeFactories(endpointReference, CLIENT_SIDE_RESOLVER); 161 } 162 163 private TubeFactoryList getTubeFactories(URI endpointReference, TubeFactoryListResolver resolver) { 164 if (appConfig != null && appConfig.getTubelines() != null) { 165 for (TubelineMapping mapping : appConfig.getTubelines().getTubelineMappings()) { 166 if (mapping.getEndpointRef().equals(endpointReference.toString())) { 167 TubeFactoryList list = resolver.getFactories(getTubeline(appConfig, resolveReference(mapping.getTubelineRef()))); 168 if (list != null) { 169 return list; 170 } else { 171 break; 172 } 173 } 174 } 175 176 if (appConfig.getTubelines().getDefault() != null) { 177 TubeFactoryList list = resolver.getFactories(getTubeline(appConfig, resolveReference(appConfig.getTubelines().getDefault()))); 178 if (list != null) { 179 return list; 180 } 181 } 182 } 183 184 for (TubelineMapping mapping : defaultConfig.getTubelines().getTubelineMappings()) { 185 if (mapping.getEndpointRef().equals(endpointReference.toString())) { 186 TubeFactoryList list = resolver.getFactories(getTubeline(defaultConfig, resolveReference(mapping.getTubelineRef()))); 187 if (list != null) { 188 return list; 189 } else { 190 break; 191 } 192 } 193 } 194 195 return resolver.getFactories(getTubeline(defaultConfig, resolveReference(defaultConfig.getTubelines().getDefault()))); 196 } 197 198 TubelineDefinition getTubeline(MetroConfig config, URI tubelineDefinitionUri) { 199 if (config != null && config.getTubelines() != null) { 200 for (TubelineDefinition td : config.getTubelines().getTubelineDefinitions()) { 201 if (td.getName().equals(tubelineDefinitionUri.getFragment())) { 202 return td; 203 } 204 } 205 } 206 207 return null; 208 } 209 210 private static URI resolveReference(String reference) { 211 try { 212 return new URI(reference); 213 } catch (URISyntaxException ex) { 214 throw LOGGER.logSevereException(new WebServiceException(TubelineassemblyMessages.MASM_0008_INVALID_URI_REFERENCE(reference), ex)); 215 } 216 } 217 218 219 private static URL locateResource(String resource, ResourceLoader loader) { 220 if (loader == null) return null; 221 222 try { 223 return loader.getResource(resource); 224 } catch (MalformedURLException ex) { 225 LOGGER.severe(TubelineassemblyMessages.MASM_0009_CANNOT_FORM_VALID_URL(resource), ex); 226 } 227 return null; 228 } 229 230 private static URL locateResource(String resource, ResourceLoader[] loaders) { 231 232 for (ResourceLoader loader : loaders) { 233 URL url = locateResource(resource, loader); 234 if (url != null) { 235 return url; 236 } 237 } 238 return null; 239 } 240 241 private static MetroConfig loadMetroConfig(@NotNull URL resourceUrl) { 242 try (InputStream is = getConfigInputStream(resourceUrl)) { 243 JAXBContext jaxbContext = createJAXBContext(); 244 Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); 245 XMLInputFactory factory = XmlUtil.newXMLInputFactory(true); 246 JAXBElement<MetroConfig> configElement = unmarshaller.unmarshal(factory.createXMLStreamReader(is), MetroConfig.class); 247 return configElement.getValue(); 248 } catch (Exception e) { 249 String message = TubelineassemblyMessages.MASM_0010_ERROR_READING_CFG_FILE_FROM_LOCATION( 250 resourceUrl != null ? resourceUrl.toString() : null); 251 InternalError error = new InternalError(message); 252 LOGGER.logException(error, e, Level.SEVERE); 253 throw error; 254 } 255 } 256 257 private static InputStream getConfigInputStream(URL resourceUrl) throws IOException { 258 InputStream is; 259 if (resourceUrl != null) { 260 is = resourceUrl.openStream(); 261 } else { 262 is = MetroConfigLoader.class.getResourceAsStream("jaxws-tubes-default.xml"); 263 } 264 return is; 265 } 266 267 private static JAXBContext createJAXBContext() throws Exception { 268 if (isJDKInternal()) { 269 // since jdk classes are repackaged, extra privilege is necessary to create JAXBContext 270 return AccessController.doPrivileged( 271 new PrivilegedExceptionAction<JAXBContext>() { 272 @Override 273 public JAXBContext run() throws Exception { 274 return JAXBContext.newInstance(MetroConfig.class.getPackage().getName()); 275 } 276 }); 277 } else { 278 // usage from JAX-WS/Metro/Glassfish 279 return JAXBContext.newInstance(MetroConfig.class.getPackage().getName()); 280 } 281 } 282 283 private static boolean isJDKInternal() { 284 // avoid "string repackaging" 285 return MetroConfigLoader.class.getName().startsWith("com." + "sun.xml.internal.ws"); 286 } 287 288 private static class MetroConfigUrlLoader extends ResourceLoader { 289 290 Container container; // TODO remove the field together with the code path using it (see below) 291 ResourceLoader parentLoader; 292 293 MetroConfigUrlLoader(ResourceLoader parentLoader) { 294 this.parentLoader = parentLoader; 295 } 296 297 MetroConfigUrlLoader(Container container) { 298 this((container != null) ? container.getSPI(ResourceLoader.class) : null); 299 this.container = container; 300 } 301 302 @Override 303 public URL getResource(String resource) throws MalformedURLException { 304 LOGGER.entering(resource); 305 URL resourceUrl = null; 306 try { 307 if (parentLoader != null) { 308 if (LOGGER.isLoggable(Level.FINE)) { 309 LOGGER.fine(TubelineassemblyMessages.MASM_0011_LOADING_RESOURCE(resource, parentLoader)); 310 } 311 312 resourceUrl = parentLoader.getResource(resource); 313 } 314 315 if (resourceUrl == null) { 316 resourceUrl = loadViaClassLoaders("com/sun/xml/internal/ws/assembler/" + resource); 317 } 318 319 if (resourceUrl == null && container != null) { 320 // TODO: we should remove this code path, the config file should be loaded using ResourceLoader only 321 resourceUrl = loadFromServletContext(resource); 322 } 323 324 return resourceUrl; 325 } finally { 326 LOGGER.exiting(resourceUrl); 327 } 328 } 329 330 private static URL loadViaClassLoaders(final String resource) { 331 URL resourceUrl = tryLoadFromClassLoader(resource, Thread.currentThread().getContextClassLoader()); 332 if (resourceUrl == null) { 333 resourceUrl = tryLoadFromClassLoader(resource, MetroConfigLoader.class.getClassLoader()); 334 if (resourceUrl == null) { 335 return ClassLoader.getSystemResource(resource); 336 } 337 } 338 339 return resourceUrl; 340 } 341 342 private static URL tryLoadFromClassLoader(final String resource, final ClassLoader loader) { 343 return (loader != null) ? loader.getResource(resource) : null; 344 } 345 346 private URL loadFromServletContext(String resource) throws RuntimeException { 347 Object context = null; 348 try { 349 final Class<?> contextClass = Class.forName("javax.servlet.ServletContext"); 350 context = container.getSPI(contextClass); 351 if (context != null) { 352 if (LOGGER.isLoggable(Level.FINE)) { 353 LOGGER.fine(TubelineassemblyMessages.MASM_0012_LOADING_VIA_SERVLET_CONTEXT(resource, context)); 354 } 355 try { 356 final Method method = context.getClass().getMethod("getResource", String.class); 357 final Object result = method.invoke(context, "/WEB-INF/" + resource); 358 return URL.class.cast(result); 359 } catch (Exception e) { 360 throw LOGGER.logSevereException(new RuntimeException(TubelineassemblyMessages.MASM_0013_ERROR_INVOKING_SERVLET_CONTEXT_METHOD("getResource()")), e); 361 } 362 } 363 } catch (ClassNotFoundException e) { 364 if (LOGGER.isLoggable(Level.FINE)) { 365 LOGGER.fine(TubelineassemblyMessages.MASM_0014_UNABLE_TO_LOAD_CLASS("javax.servlet.ServletContext")); 366 } 367 } 368 return null; 369 } 370 } 371 372 }