1 /* 2 * Copyright (c) 1997, 2013, 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.lang.reflect.ReflectPermission; 46 import java.net.MalformedURLException; 47 import java.net.URI; 48 import java.net.URISyntaxException; 49 import java.net.URL; 50 import java.security.*; 51 import java.util.PropertyPermission; 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 getFactories(TubelineDefinition td)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 MetroConfigLoader(Container container, MetroConfigName defaultTubesConfigNames)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 init(Container container, ResourceLoader... loaders)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 if (defaultConfigUrl == null) { 127 throw LOGGER.logSevereException(new IllegalStateException(TubelineassemblyMessages.MASM_0001_DEFAULT_CFG_FILE_NOT_FOUND(defaultFileName))); 128 } 129 130 LOGGER.config(TubelineassemblyMessages.MASM_0002_DEFAULT_CFG_FILE_LOCATED(defaultFileName, defaultConfigUrl)); 131 this.defaultConfig = MetroConfigLoader.loadMetroConfig(defaultConfigUrl); 132 if (defaultConfig == null) { 133 throw LOGGER.logSevereException(new IllegalStateException(TubelineassemblyMessages.MASM_0003_DEFAULT_CFG_FILE_NOT_LOADED(defaultFileName))); 134 } 135 if (defaultConfig.getTubelines() == null) { 136 throw LOGGER.logSevereException(new IllegalStateException(TubelineassemblyMessages.MASM_0004_NO_TUBELINES_SECTION_IN_DEFAULT_CFG_FILE(defaultFileName))); 137 } 138 if (defaultConfig.getTubelines().getDefault() == null) { 139 throw LOGGER.logSevereException(new IllegalStateException(TubelineassemblyMessages.MASM_0005_NO_DEFAULT_TUBELINE_IN_DEFAULT_CFG_FILE(defaultFileName))); 140 } 141 142 this.appConfigUrl = locateResource(appFileName, loaders); 143 if (appConfigUrl != null) { 144 LOGGER.config(TubelineassemblyMessages.MASM_0006_APP_CFG_FILE_LOCATED(appConfigUrl)); 145 this.appConfig = MetroConfigLoader.loadMetroConfig(appConfigUrl); 146 } else { 147 LOGGER.config(TubelineassemblyMessages.MASM_0007_APP_CFG_FILE_NOT_FOUND()); 148 this.appConfig = null; 149 } 150 } 151 getEndpointSideTubeFactories(URI endpointReference)152 TubeFactoryList getEndpointSideTubeFactories(URI endpointReference) { 153 return getTubeFactories(endpointReference, ENDPOINT_SIDE_RESOLVER); 154 } 155 getClientSideTubeFactories(URI endpointReference)156 TubeFactoryList getClientSideTubeFactories(URI endpointReference) { 157 return getTubeFactories(endpointReference, CLIENT_SIDE_RESOLVER); 158 } 159 getTubeFactories(URI endpointReference, TubeFactoryListResolver resolver)160 private TubeFactoryList getTubeFactories(URI endpointReference, TubeFactoryListResolver resolver) { 161 if (appConfig != null && appConfig.getTubelines() != null) { 162 for (TubelineMapping mapping : appConfig.getTubelines().getTubelineMappings()) { 163 if (mapping.getEndpointRef().equals(endpointReference.toString())) { 164 TubeFactoryList list = resolver.getFactories(getTubeline(appConfig, resolveReference(mapping.getTubelineRef()))); 165 if (list != null) { 166 return list; 167 } else { 168 break; 169 } 170 } 171 } 172 173 if (appConfig.getTubelines().getDefault() != null) { 174 TubeFactoryList list = resolver.getFactories(getTubeline(appConfig, resolveReference(appConfig.getTubelines().getDefault()))); 175 if (list != null) { 176 return list; 177 } 178 } 179 } 180 181 for (TubelineMapping mapping : defaultConfig.getTubelines().getTubelineMappings()) { 182 if (mapping.getEndpointRef().equals(endpointReference.toString())) { 183 TubeFactoryList list = resolver.getFactories(getTubeline(defaultConfig, resolveReference(mapping.getTubelineRef()))); 184 if (list != null) { 185 return list; 186 } else { 187 break; 188 } 189 } 190 } 191 192 return resolver.getFactories(getTubeline(defaultConfig, resolveReference(defaultConfig.getTubelines().getDefault()))); 193 } 194 getTubeline(MetroConfig config, URI tubelineDefinitionUri)195 TubelineDefinition getTubeline(MetroConfig config, URI tubelineDefinitionUri) { 196 if (config != null && config.getTubelines() != null) { 197 for (TubelineDefinition td : config.getTubelines().getTubelineDefinitions()) { 198 if (td.getName().equals(tubelineDefinitionUri.getFragment())) { 199 return td; 200 } 201 } 202 } 203 204 return null; 205 } 206 resolveReference(String reference)207 private static URI resolveReference(String reference) { 208 try { 209 return new URI(reference); 210 } catch (URISyntaxException ex) { 211 throw LOGGER.logSevereException(new WebServiceException(TubelineassemblyMessages.MASM_0008_INVALID_URI_REFERENCE(reference), ex)); 212 } 213 } 214 215 locateResource(String resource, ResourceLoader loader)216 private static URL locateResource(String resource, ResourceLoader loader) { 217 if (loader == null) return null; 218 219 try { 220 return loader.getResource(resource); 221 } catch (MalformedURLException ex) { 222 LOGGER.severe(TubelineassemblyMessages.MASM_0009_CANNOT_FORM_VALID_URL(resource), ex); 223 } 224 return null; 225 } 226 locateResource(String resource, ResourceLoader[] loaders)227 private static URL locateResource(String resource, ResourceLoader[] loaders) { 228 229 for (ResourceLoader loader : loaders) { 230 URL url = locateResource(resource, loader); 231 if (url != null) { 232 return url; 233 } 234 } 235 return null; 236 } 237 loadMetroConfig(@otNull URL resourceUrl)238 private static MetroConfig loadMetroConfig(@NotNull URL resourceUrl) { 239 MetroConfig result = null; 240 try { 241 JAXBContext jaxbContext = createJAXBContext(); 242 Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); 243 XMLInputFactory factory = XmlUtil.newXMLInputFactory(true); 244 final JAXBElement<MetroConfig> configElement = unmarshaller.unmarshal(factory.createXMLStreamReader(resourceUrl.openStream()), MetroConfig.class); 245 result = configElement.getValue(); 246 } catch (Exception e) { 247 LOGGER.warning(TubelineassemblyMessages.MASM_0010_ERROR_READING_CFG_FILE_FROM_LOCATION(resourceUrl.toString()), e); 248 } 249 return result; 250 } 251 createJAXBContext()252 private static JAXBContext createJAXBContext() throws Exception { 253 if (isJDKInternal()) { 254 // since jdk classes are repackaged, extra privilege is necessary to create JAXBContext 255 return AccessController.doPrivileged( 256 new PrivilegedExceptionAction<JAXBContext>() { 257 @Override 258 public JAXBContext run() throws Exception { 259 return JAXBContext.newInstance(MetroConfig.class.getPackage().getName()); 260 } 261 }, createSecurityContext() 262 ); 263 } else { 264 // usage from JAX-WS/Metro/Glassfish 265 return JAXBContext.newInstance(MetroConfig.class.getPackage().getName()); 266 } 267 } 268 269 private static AccessControlContext createSecurityContext() { 270 PermissionCollection perms = new Permissions(); 271 perms.add(new RuntimePermission("accessClassInPackage.com" + ".sun.xml.internal.ws.runtime.config")); // avoid repackaging 272 perms.add(new ReflectPermission("suppressAccessChecks")); 273 return new AccessControlContext( 274 new ProtectionDomain[]{ 275 new ProtectionDomain(null, perms), 276 }); 277 } 278 279 private static boolean isJDKInternal() { 280 // avoid "string repackaging" 281 return MetroConfigLoader.class.getName().startsWith("com." + "sun.xml.internal.ws"); 282 } 283 284 private static class MetroConfigUrlLoader extends ResourceLoader { 285 286 Container container; // TODO remove the field together with the code path using it (see below) 287 ResourceLoader parentLoader; 288 289 MetroConfigUrlLoader(ResourceLoader parentLoader) { 290 this.parentLoader = parentLoader; 291 } 292 293 MetroConfigUrlLoader(Container container) { 294 this((container != null) ? container.getSPI(ResourceLoader.class) : null); 295 this.container = container; 296 } 297 298 @Override 299 public URL getResource(String resource) throws MalformedURLException { 300 LOGGER.entering(resource); 301 URL resourceUrl = null; 302 try { 303 if (parentLoader != null) { 304 if (LOGGER.isLoggable(Level.FINE)) { 305 LOGGER.fine(TubelineassemblyMessages.MASM_0011_LOADING_RESOURCE(resource, parentLoader)); 306 } 307 308 resourceUrl = parentLoader.getResource(resource); 309 } 310 311 if (resourceUrl == null) { 312 resourceUrl = loadViaClassLoaders("com/sun/xml/internal/ws/assembler/" + resource); 313 } 314 315 if (resourceUrl == null && container != null) { 316 // TODO: we should remove this code path, the config file should be loaded using ResourceLoader only 317 resourceUrl = loadFromServletContext(resource); 318 } 319 320 return resourceUrl; 321 } finally { 322 LOGGER.exiting(resourceUrl); 323 } 324 } 325 326 private static URL loadViaClassLoaders(final String resource) { 327 URL resourceUrl = tryLoadFromClassLoader(resource, Thread.currentThread().getContextClassLoader()); 328 if (resourceUrl == null) { 329 resourceUrl = tryLoadFromClassLoader(resource, MetroConfigLoader.class.getClassLoader()); 330 if (resourceUrl == null) { 331 return ClassLoader.getSystemResource(resource); 332 } 333 } 334 335 return resourceUrl; 336 } 337 338 private static URL tryLoadFromClassLoader(final String resource, final ClassLoader loader) { 339 return (loader != null) ? loader.getResource(resource) : null; 340 } 341 342 private URL loadFromServletContext(String resource) throws RuntimeException { 343 Object context = null; 344 try { 345 final Class<?> contextClass = Class.forName("javax.servlet.ServletContext"); 346 context = container.getSPI(contextClass); 347 if (context != null) { 348 if (LOGGER.isLoggable(Level.FINE)) { 349 LOGGER.fine(TubelineassemblyMessages.MASM_0012_LOADING_VIA_SERVLET_CONTEXT(resource, context)); 350 } 351 try { 352 final Method method = context.getClass().getMethod("getResource", String.class); 353 final Object result = method.invoke(context, "/WEB-INF/" + resource); 354 return URL.class.cast(result); 355 } catch (Exception e) { 356 throw LOGGER.logSevereException(new RuntimeException(TubelineassemblyMessages.MASM_0013_ERROR_INVOKING_SERVLET_CONTEXT_METHOD("getResource()")), e); 357 } 358 } 359 } catch (ClassNotFoundException e) { 360 if (LOGGER.isLoggable(Level.FINE)) { 361 LOGGER.fine(TubelineassemblyMessages.MASM_0014_UNABLE_TO_LOAD_CLASS("javax.servlet.ServletContext")); 362 } 363 } 364 return null; 365 } 366 } 367 368 } 369