View Javadoc

1   /*
2    * Copyright 2007 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.ws.transport.http;
18  
19  import java.util.Iterator;
20  import java.util.Map;
21  import javax.servlet.ServletException;
22  import javax.servlet.http.HttpServletRequest;
23  import javax.servlet.http.HttpServletResponse;
24  
25  import org.springframework.beans.BeansException;
26  import org.springframework.beans.factory.BeanFactoryUtils;
27  import org.springframework.beans.factory.BeanInitializationException;
28  import org.springframework.beans.factory.BeanNameAware;
29  import org.springframework.beans.factory.InitializingBean;
30  import org.springframework.beans.factory.NoSuchBeanDefinitionException;
31  import org.springframework.core.io.ClassPathResource;
32  import org.springframework.web.servlet.DispatcherServlet;
33  import org.springframework.web.servlet.FrameworkServlet;
34  import org.springframework.web.util.WebUtils;
35  import org.springframework.ws.WebServiceMessageFactory;
36  import org.springframework.ws.server.EndpointAdapter;
37  import org.springframework.ws.server.EndpointExceptionResolver;
38  import org.springframework.ws.server.EndpointMapping;
39  import org.springframework.ws.server.MessageDispatcher;
40  import org.springframework.ws.support.DefaultStrategiesHelper;
41  import org.springframework.ws.transport.WebServiceMessageReceiver;
42  import org.springframework.ws.wsdl.WsdlDefinition;
43  import org.springframework.xml.xsd.XsdSchema;
44  
45  /**
46   * Servlet for simplified dispatching of Web service messages.
47   * <p/>
48   * This servlet is a convenient alternative to the standard Spring-MVC {@link DispatcherServlet} with separate {@link
49   * WebServiceMessageReceiverHandlerAdapter}, {@link MessageDispatcher}, and {@link WsdlDefinitionHandlerAdapter}
50   * instances.
51   * <p/>
52   * This servlet automatically detects {@link EndpointAdapter EndpointAdapters}, {@link EndpointMapping
53   * EndpointMappings}, and {@link EndpointExceptionResolver EndpointExceptionResolvers} <i>by type</i>.
54   * <p/>
55   * This servlet also automatically detects any {@link WsdlDefinition} defined in its application context. This WSDL is
56   * exposed under the bean name: for example, a <code>WsdlDefinition</code> bean named '<code>echo</code>' will be
57   * exposed as <code>echo.wsdl</code> in this servlet's context: <code>http://localhost:8080/spring-ws/echo.wsdl</code>.
58   * When the <code>transformWsdlLocations</code> init-param is set to <code>true</code> in this servlet's configuration
59   * in <code>web.xml</code>, all <code>location</code> attributes in the WSDL definitions will reflect the URL of the
60   * incoming request.
61   *
62   * @author Arjen Poutsma
63   * @see org.springframework.web.servlet.DispatcherServlet
64   * @see org.springframework.ws.server.MessageDispatcher
65   * @see org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter
66   * @since 1.0.0
67   */
68  public class MessageDispatcherServlet extends FrameworkServlet {
69  
70      /** Well-known name for the {@link WebServiceMessageFactory} bean in the bean factory for this namespace. */
71      public static final String DEFAULT_MESSAGE_FACTORY_BEAN_NAME = "messageFactory";
72  
73      /** Well-known name for the {@link WebServiceMessageReceiver} object in the bean factory for this namespace. */
74      public static final String DEFAULT_MESSAGE_RECEIVER_BEAN_NAME = "messageReceiver";
75  
76      /**
77       * Well-known name for the {@link WebServiceMessageReceiverHandlerAdapter} object in the bean factory for this
78       * namespace.
79       */
80      public static final String DEFAULT_MESSAGE_RECEIVER_HANDLER_ADAPTER_BEAN_NAME = "messageReceiverHandlerAdapter";
81  
82      /** Well-known name for the {@link WsdlDefinitionHandlerAdapter} object in the bean factory for this namespace. */
83      public static final String DEFAULT_WSDL_DEFINITION_HANDLER_ADAPTER_BEAN_NAME = "wsdlDefinitionHandlerAdapter";
84  
85      /** Well-known name for the {@link XsdSchemaHandlerAdapter} object in the bean factory for this namespace. */
86      public static final String DEFAULT_XSD_SCHEMA_HANDLER_ADAPTER_BEAN_NAME = "xsdSchemaHandlerAdapter";
87  
88      /**
89       * Name of the class path resource (relative to the {@link MessageDispatcherServlet} class) that defines
90       * <code>MessageDispatcherServlet's</code> default strategy names.
91       */
92      private static final String DEFAULT_STRATEGIES_PATH = "MessageDispatcherServlet.properties";
93  
94      /** Suffix of a WSDL request uri. */
95      private static final String WSDL_SUFFIX_NAME = ".wsdl";
96  
97      /** Suffix of a XSD request uri. */
98      private static final String XSD_SUFFIX_NAME = ".xsd";
99  
100     private final DefaultStrategiesHelper defaultStrategiesHelper;
101 
102     private String messageFactoryBeanName = DEFAULT_MESSAGE_FACTORY_BEAN_NAME;
103 
104     private String messageReceiverHandlerAdapterBeanName = DEFAULT_MESSAGE_RECEIVER_HANDLER_ADAPTER_BEAN_NAME;
105 
106     /** The {@link WebServiceMessageReceiverHandlerAdapter} used by this servlet. */
107     private WebServiceMessageReceiverHandlerAdapter messageReceiverHandlerAdapter;
108 
109     private String wsdlDefinitionHandlerAdapterBeanName = DEFAULT_WSDL_DEFINITION_HANDLER_ADAPTER_BEAN_NAME;
110 
111     /** The {@link WsdlDefinitionHandlerAdapter} used by this servlet. */
112     private WsdlDefinitionHandlerAdapter wsdlDefinitionHandlerAdapter;
113 
114     private String xsdSchemaHandlerAdapterBeanName = DEFAULT_XSD_SCHEMA_HANDLER_ADAPTER_BEAN_NAME;
115 
116     /** The {@link XsdSchemaHandlerAdapter} used by this servlet. */
117     private XsdSchemaHandlerAdapter xsdSchemaHandlerAdapter;
118 
119     private String messageReceiverBeanName = DEFAULT_MESSAGE_RECEIVER_BEAN_NAME;
120 
121     /** The {@link WebServiceMessageReceiver} used by this servlet. */
122     private WebServiceMessageReceiver messageReceiver;
123 
124     /** Keys are bean names, values are {@link WsdlDefinition WsdlDefinitions}. */
125     private Map wsdlDefinitions;
126 
127     private Map xsdSchemas;
128 
129     private boolean transformWsdlLocations = false;
130 
131     /** Public constructor, necessary for some Web application servers. */
132     public MessageDispatcherServlet() {
133         defaultStrategiesHelper = new DefaultStrategiesHelper(
134                 new ClassPathResource(DEFAULT_STRATEGIES_PATH, MessageDispatcherServlet.class));
135     }
136 
137     /** Returns the bean name used to lookup a {@link WebServiceMessageFactory}. */
138     public String getMessageFactoryBeanName() {
139         return messageFactoryBeanName;
140     }
141 
142     /**
143      * Sets the bean name used to lookup a {@link WebServiceMessageFactory}. Defaults to {@link
144      * #DEFAULT_MESSAGE_FACTORY_BEAN_NAME}.
145      */
146     public void setMessageFactoryBeanName(String messageFactoryBeanName) {
147         this.messageFactoryBeanName = messageFactoryBeanName;
148     }
149 
150     /** Returns the bean name used to lookup a {@link WebServiceMessageReceiver}. */
151     public String getMessageReceiverBeanName() {
152         return messageReceiverBeanName;
153     }
154 
155     /**
156      * Sets the bean name used to lookup a {@link WebServiceMessageReceiver}. Defaults to {@link
157      * #DEFAULT_MESSAGE_RECEIVER_BEAN_NAME}.
158      */
159     public void setMessageReceiverBeanName(String messageReceiverBeanName) {
160         this.messageReceiverBeanName = messageReceiverBeanName;
161     }
162 
163     /**
164      * Indicates whether relative address locations in the WSDL are to be transformed using the request URI of the
165      * incoming {@link HttpServletRequest}.
166      */
167     public boolean isTransformWsdlLocations() {
168         return transformWsdlLocations;
169     }
170 
171     /** Returns the bean name used to lookup a {@link WebServiceMessageReceiverHandlerAdapter}. */
172     public String getMessageReceiverHandlerAdapterBeanName() {
173         return messageReceiverHandlerAdapterBeanName;
174     }
175 
176     /**
177      * Sets the bean name used to lookup a {@link WebServiceMessageReceiverHandlerAdapter}. Defaults to {@link
178      * #DEFAULT_MESSAGE_RECEIVER_HANDLER_ADAPTER_BEAN_NAME}.
179      */
180     public void setMessageReceiverHandlerAdapterBeanName(String messageReceiverHandlerAdapterBeanName) {
181         this.messageReceiverHandlerAdapterBeanName = messageReceiverHandlerAdapterBeanName;
182     }
183 
184     /** Returns the bean name used to lookup a {@link WsdlDefinitionHandlerAdapter}. */
185     public String getWsdlDefinitionHandlerAdapterBeanName() {
186         return wsdlDefinitionHandlerAdapterBeanName;
187     }
188 
189     /**
190      * Sets the bean name used to lookup a {@link WsdlDefinitionHandlerAdapter}. Defaults to {@link
191      * #DEFAULT_WSDL_DEFINITION_HANDLER_ADAPTER_BEAN_NAME}.
192      */
193     public void setWsdlDefinitionHandlerAdapterBeanName(String wsdlDefinitionHandlerAdapterBeanName) {
194         this.wsdlDefinitionHandlerAdapterBeanName = wsdlDefinitionHandlerAdapterBeanName;
195     }
196 
197     /** Returns the bean name used to lookup a {@link XsdSchemaHandlerAdapter}. */
198     public String getXsdSchemaHandlerAdapterBeanName() {
199         return xsdSchemaHandlerAdapterBeanName;
200     }
201 
202     /**
203      * Sets the bean name used to lookup a {@link XsdSchemaHandlerAdapter}. Defaults to {@link
204      * #DEFAULT_XSD_SCHEMA_HANDLER_ADAPTER_BEAN_NAME}.
205      */
206     public void setXsdSchemaHandlerAdapterBeanName(String xsdSchemaHandlerAdapterBeanName) {
207         this.xsdSchemaHandlerAdapterBeanName = xsdSchemaHandlerAdapterBeanName;
208     }
209 
210     /**
211      * Sets whether relative address locations in the WSDL are to be transformed using the request URI of the incoming
212      * {@link HttpServletRequest}. Defaults to <code>false</code>.
213      */
214     public void setTransformWsdlLocations(boolean transformWsdlLocations) {
215         this.transformWsdlLocations = transformWsdlLocations;
216     }
217 
218     protected void doService(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
219             throws Exception {
220         WsdlDefinition definition = getWsdlDefinition(httpServletRequest);
221         if (definition != null) {
222             wsdlDefinitionHandlerAdapter.handle(httpServletRequest, httpServletResponse, definition);
223             return;
224         }
225         XsdSchema schema = getXsdSchema(httpServletRequest);
226         if (schema != null) {
227             xsdSchemaHandlerAdapter.handle(httpServletRequest, httpServletResponse, schema);
228             return;
229         }
230         messageReceiverHandlerAdapter.handle(httpServletRequest, httpServletResponse, messageReceiver);
231     }
232 
233     protected void initFrameworkServlet() throws ServletException, BeansException {
234         initMessageReceiverHandlerAdapter();
235         initWsdlDefinitionHandlerAdapter();
236         initXsdSchemaHandlerAdapter();
237         initMessageReceiver();
238         initWsdlDefinitions();
239         initXsdSchemas();
240     }
241 
242     protected long getLastModified(HttpServletRequest httpServletRequest) {
243         WsdlDefinition definition = getWsdlDefinition(httpServletRequest);
244         if (definition != null) {
245             return wsdlDefinitionHandlerAdapter.getLastModified(httpServletRequest, definition);
246         }
247         XsdSchema schema = getXsdSchema(httpServletRequest);
248         if (schema != null) {
249             return xsdSchemaHandlerAdapter.getLastModified(httpServletRequest, schema);
250         }
251         return messageReceiverHandlerAdapter.getLastModified(httpServletRequest, messageReceiver);
252     }
253 
254     /** Returns the {@link WebServiceMessageReceiver} used by this servlet. */
255     protected WebServiceMessageReceiver getMessageReceiver() {
256         return messageReceiver;
257     }
258 
259     /**
260      * Determines the {@link WsdlDefinition} for a given request, or <code>null</code> if none is found.
261      * <p/>
262      * Default implementation checks whether the request method is <code>GET</code>, whether the request uri ends with
263      * <code>".wsdl"</code>, and if there is a <code>WsdlDefinition</code> with the same name as the filename in the
264      * request uri.
265      *
266      * @param request the <code>HttpServletRequest</code>
267      * @return a definition, or <code>null</code>
268      */
269     protected WsdlDefinition getWsdlDefinition(HttpServletRequest request) {
270         if (HttpTransportConstants.METHOD_GET.equals(request.getMethod()) &&
271                 request.getRequestURI().endsWith(WSDL_SUFFIX_NAME)) {
272             String fileName = WebUtils.extractFilenameFromUrlPath(request.getRequestURI());
273             return (WsdlDefinition) wsdlDefinitions.get(fileName);
274         }
275         else {
276             return null;
277         }
278     }
279 
280     /**
281      * Determines the {@link XsdSchema} for a given request, or <code>null</code> if none is found.
282      * <p/>
283      * Default implementation checks whether the request method is <code>GET</code>, whether the request uri ends with
284      * <code>".xsd"</code>, and if there is a <code>XsdSchema</code> with the same name as the filename in the request
285      * uri.
286      *
287      * @param request the <code>HttpServletRequest</code>
288      * @return a schema, or <code>null</code>
289      */
290     protected XsdSchema getXsdSchema(HttpServletRequest request) {
291         if (HttpTransportConstants.METHOD_GET.equals(request.getMethod()) &&
292                 request.getRequestURI().endsWith(XSD_SUFFIX_NAME)) {
293             String fileName = WebUtils.extractFilenameFromUrlPath(request.getRequestURI());
294             return (XsdSchema) xsdSchemas.get(fileName);
295         }
296         else {
297             return null;
298         }
299     }
300 
301     private void initMessageReceiverHandlerAdapter() {
302         try {
303             try {
304                 messageReceiverHandlerAdapter = (WebServiceMessageReceiverHandlerAdapter) getWebApplicationContext()
305                         .getBean(getMessageReceiverHandlerAdapterBeanName(),
306                                 WebServiceMessageReceiverHandlerAdapter.class);
307             }
308             catch (NoSuchBeanDefinitionException ignored) {
309                 messageReceiverHandlerAdapter = new WebServiceMessageReceiverHandlerAdapter();
310             }
311             initWebServiceMessageFactory();
312             messageReceiverHandlerAdapter.afterPropertiesSet();
313         }
314         catch (Exception ex) {
315             throw new BeanInitializationException("Could not initialize WebServiceMessageReceiverHandlerAdapter", ex);
316         }
317     }
318 
319     private void initWebServiceMessageFactory() {
320         WebServiceMessageFactory messageFactory;
321         try {
322             messageFactory = (WebServiceMessageFactory) getWebApplicationContext()
323                     .getBean(getMessageFactoryBeanName(), WebServiceMessageFactory.class);
324         }
325         catch (NoSuchBeanDefinitionException ignored) {
326             messageFactory = (WebServiceMessageFactory) defaultStrategiesHelper
327                     .getDefaultStrategy(WebServiceMessageFactory.class, getWebApplicationContext());
328             if (logger.isDebugEnabled()) {
329                 logger.debug("No WebServiceMessageFactory found in servlet '" + getServletName() + "': using default");
330             }
331         }
332         messageReceiverHandlerAdapter.setMessageFactory(messageFactory);
333     }
334 
335     private void initWsdlDefinitionHandlerAdapter() {
336         try {
337             try {
338                 wsdlDefinitionHandlerAdapter = (WsdlDefinitionHandlerAdapter) getWebApplicationContext()
339                         .getBean(getWsdlDefinitionHandlerAdapterBeanName(), WsdlDefinitionHandlerAdapter.class);
340 
341             }
342             catch (NoSuchBeanDefinitionException ignored) {
343                 wsdlDefinitionHandlerAdapter = new WsdlDefinitionHandlerAdapter();
344             }
345             wsdlDefinitionHandlerAdapter.setTransformLocations(isTransformWsdlLocations());
346             wsdlDefinitionHandlerAdapter.afterPropertiesSet();
347         }
348         catch (Exception ex) {
349             throw new BeanInitializationException("Could not initialize WsdlDefinitionHandlerAdapter", ex);
350         }
351     }
352 
353     private void initXsdSchemaHandlerAdapter() {
354         try {
355             try {
356                 xsdSchemaHandlerAdapter = (XsdSchemaHandlerAdapter) getWebApplicationContext()
357                         .getBean(getXsdSchemaHandlerAdapterBeanName(), XsdSchemaHandlerAdapter.class);
358 
359             }
360             catch (NoSuchBeanDefinitionException ignored) {
361                 xsdSchemaHandlerAdapter = new XsdSchemaHandlerAdapter();
362             }
363             if (xsdSchemaHandlerAdapter instanceof InitializingBean) {
364                 ((InitializingBean) xsdSchemaHandlerAdapter).afterPropertiesSet();
365             }
366         }
367         catch (Exception ex) {
368             throw new BeanInitializationException("Could not initialize XsdSchemaHandlerAdapter", ex);
369         }
370     }
371 
372     private void initMessageReceiver() {
373         try {
374             messageReceiver = (WebServiceMessageReceiver) getWebApplicationContext()
375                     .getBean(getMessageReceiverBeanName(), WebServiceMessageReceiver.class);
376         }
377         catch (NoSuchBeanDefinitionException ex) {
378             messageReceiver = (WebServiceMessageReceiver) defaultStrategiesHelper
379                     .getDefaultStrategy(WebServiceMessageReceiver.class, getWebApplicationContext());
380             if (messageReceiver instanceof BeanNameAware) {
381                 ((BeanNameAware) messageReceiver).setBeanName(getServletName());
382             }
383             if (logger.isDebugEnabled()) {
384                 logger.debug("No MessageDispatcher found in servlet '" + getServletName() + "': using default");
385             }
386         }
387     }
388 
389     /** Find all {@link WsdlDefinition WsdlDefinitions} in the ApplicationContext, incuding ancestor contexts. */
390     private void initWsdlDefinitions() {
391         wsdlDefinitions = BeanFactoryUtils
392                 .beansOfTypeIncludingAncestors(getWebApplicationContext(), WsdlDefinition.class, true, false);
393         if (logger.isDebugEnabled()) {
394             for (Iterator iterator = wsdlDefinitions.entrySet().iterator(); iterator.hasNext();) {
395                 Map.Entry entry = (Map.Entry) iterator.next();
396                 String beanName = (String) entry.getKey();
397                 WsdlDefinition definition = (WsdlDefinition) entry.getValue();
398                 logger.debug("Published [" + definition + "] as " + beanName + WSDL_SUFFIX_NAME);
399             }
400         }
401     }
402 
403     /** Find all {@link XsdSchema} in the ApplicationContext, incuding ancestor contexts. */
404     private void initXsdSchemas() {
405         xsdSchemas = BeanFactoryUtils
406                 .beansOfTypeIncludingAncestors(getWebApplicationContext(), XsdSchema.class, true, false);
407         if (logger.isDebugEnabled()) {
408             for (Iterator iterator = xsdSchemas.entrySet().iterator(); iterator.hasNext();) {
409                 Map.Entry entry = (Map.Entry) iterator.next();
410                 String beanName = (String) entry.getKey();
411                 XsdSchema schema = (XsdSchema) entry.getValue();
412                 logger.debug("Published [" + schema + "] as " + beanName + XSD_SUFFIX_NAME);
413             }
414         }
415     }
416 }