View Javadoc

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