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