View Javadoc
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  
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         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 
152     TubeFactoryList getEndpointSideTubeFactories(URI endpointReference) {
153         return getTubeFactories(endpointReference, ENDPOINT_SIDE_RESOLVER);
154     }
155 
156     TubeFactoryList getClientSideTubeFactories(URI endpointReference) {
157         return getTubeFactories(endpointReference, CLIENT_SIDE_RESOLVER);
158     }
159 
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 
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 
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 
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 
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 
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 
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 }