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