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.NoSuchBeanDefinitionException;
30  import org.springframework.core.io.ClassPathResource;
31  import org.springframework.web.servlet.DispatcherServlet;
32  import org.springframework.web.servlet.FrameworkServlet;
33  import org.springframework.web.util.WebUtils;
34  import org.springframework.ws.WebServiceMessageFactory;
35  import org.springframework.ws.server.EndpointAdapter;
36  import org.springframework.ws.server.EndpointExceptionResolver;
37  import org.springframework.ws.server.EndpointMapping;
38  import org.springframework.ws.server.MessageDispatcher;
39  import org.springframework.ws.transport.WebServiceMessageReceiver;
40  import org.springframework.ws.transport.support.DefaultStrategiesHelper;
41  import org.springframework.ws.wsdl.WsdlDefinition;
42  
43  /**
44   * Servlet for simplified dispatching of Web service messages.
45   * <p/>
46   * This servlet is a convenient alternative to the standard Spring-MVC {@link DispatcherServlet} with separate {@link
47   * WebServiceMessageReceiverHandlerAdapter}, {@link MessageDispatcher}, and {@link WsdlDefinitionHandlerAdapter}
48   * instances.
49   * <p/>
50   * This servlet automatically detects {@link EndpointAdapter EndpointAdapters}, {@link EndpointMapping
51   * EndpointMappings}, and {@link EndpointExceptionResolver EndpointExceptionResolvers} <i>by type</i>.
52   * <p/>
53   * This servlet also automatically detects any {@link WsdlDefinition} defined in its application context. This WSDL is
54   * exposed under the bean name: for example, a <code>WsdlDefinition</code> bean named '<code>echo</code>' will be
55   * exposed as <code>echo.wsdl</code> in this servlet's context: <code>http://localhost:8080/spring-ws/echo.wsdl</code>.
56   * When the <code>transformWsdlLocations</code> init-param is set to <code>true</code> in this servlet's configuration
57   * in <code>web.xml</code>, all <code>location</code> attributes in the WSDL definitions will reflect the URL of the
58   * incoming request.
59   *
60   * @author Arjen Poutsma
61   * @see org.springframework.web.servlet.DispatcherServlet
62   * @see org.springframework.ws.server.MessageDispatcher
63   * @see org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter
64   * @since 1.0.0
65   */
66  public class MessageDispatcherServlet extends FrameworkServlet {
67  
68      /** Well-known name for the {@link WebServiceMessageFactory} bean in the bean factory for this namespace. */
69      public static final String DEFAULT_MESSAGE_FACTORY_BEAN_NAME = "messageFactory";
70  
71      /** Well-known name for the {@link WebServiceMessageReceiver} object in the bean factory for this namespace. */
72      public static final String DEFAULT_MESSAGE_RECEIVER_BEAN_NAME = "messageReceiver";
73  
74      /**
75       * Well-known name for the {@link WebServiceMessageReceiverHandlerAdapter} object in the bean factory for this
76       * namespace.
77       */
78      public static final String DEFAULT_MESSAGE_RECEIVER_HANDLER_ADAPTER_BEAN_NAME = "messageReceiverHandlerAdapter";
79  
80      /** Well-known name for the {@link WsdlDefinitionHandlerAdapter} object in the bean factory for this namespace. */
81      public static final String DEFAULT_WSDL_DEFINITION_HANDLER_ADAPTER_BEAN_NAME = "wsdlDefinitionHandlerAdapter";
82  
83      /**
84       * Name of the class path resource (relative to the {@link MessageDispatcherServlet} class) that defines
85       * <code>MessageDispatcherServlet's</code> default strategy names.
86       */
87      private static final String DEFAULT_STRATEGIES_PATH = "MessageDispatcherServlet.properties";
88  
89      /** Suffix of a WSDL request uri. */
90      private static final String WSDL_SUFFIX_NAME = ".wsdl";
91  
92      private final DefaultStrategiesHelper defaultStrategiesHelper;
93  
94      private String messageFactoryBeanName = DEFAULT_MESSAGE_FACTORY_BEAN_NAME;
95  
96      private String messageReceiverHandlerAdapterBeanName = DEFAULT_MESSAGE_RECEIVER_HANDLER_ADAPTER_BEAN_NAME;
97  
98      /** The {@link WebServiceMessageReceiverHandlerAdapter} used by this servlet. */
99      private WebServiceMessageReceiverHandlerAdapter messageReceiverHandlerAdapter;
100 
101     private String wsdlDefinitionHandlerAdapterBeanName = DEFAULT_WSDL_DEFINITION_HANDLER_ADAPTER_BEAN_NAME;
102 
103     /** The {@link WsdlDefinitionHandlerAdapter} used by this servlet. */
104     private WsdlDefinitionHandlerAdapter wsdlDefinitionHandlerAdapter;
105 
106     private String messageReceiverBeanName = DEFAULT_MESSAGE_RECEIVER_BEAN_NAME;
107 
108     /** The {@link WebServiceMessageReceiver} used by this servlet. */
109     private WebServiceMessageReceiver messageReceiver;
110 
111     /** Keys are bean names, values are {@link WsdlDefinition WsdlDefinitions}. */
112     private Map wsdlDefinitions;
113 
114     private boolean transformWsdlLocations = false;
115 
116     /** Public constructor, necessary for some Web application servers. */
117     public MessageDispatcherServlet() {
118         defaultStrategiesHelper = new DefaultStrategiesHelper(
119                 new ClassPathResource(DEFAULT_STRATEGIES_PATH, MessageDispatcherServlet.class));
120     }
121 
122     /** Returns the bean name used to lookup a {@link WebServiceMessageFactory}. */
123     public String getMessageFactoryBeanName() {
124         return messageFactoryBeanName;
125     }
126 
127     /**
128      * Sets the bean name used to lookup a {@link WebServiceMessageFactory}. Defaults to {@link
129      * #DEFAULT_MESSAGE_FACTORY_BEAN_NAME}.
130      */
131     public void setMessageFactoryBeanName(String messageFactoryBeanName) {
132         this.messageFactoryBeanName = messageFactoryBeanName;
133     }
134 
135     /** Returns the bean name used to lookup a {@link WebServiceMessageReceiver}. */
136     public String getMessageReceiverBeanName() {
137         return messageReceiverBeanName;
138     }
139 
140     /**
141      * Sets the bean name used to lookup a {@link WebServiceMessageReceiver}. Defaults to {@link
142      * #DEFAULT_MESSAGE_RECEIVER_BEAN_NAME}.
143      */
144     public void setMessageReceiverBeanName(String messageReceiverBeanName) {
145         this.messageReceiverBeanName = messageReceiverBeanName;
146     }
147 
148     /**
149      * Indicates whether relative address locations in the WSDL are to be transformed using the request URI of the
150      * incoming {@link HttpServletRequest}.
151      */
152     public boolean isTransformWsdlLocations() {
153         return transformWsdlLocations;
154     }
155 
156     /** Returns the bean name used to lookup a {@link WebServiceMessageReceiverHandlerAdapter}. */
157     public String getMessageReceiverHandlerAdapterBeanName() {
158         return messageReceiverHandlerAdapterBeanName;
159     }
160 
161     /**
162      * Sets the bean name used to lookup a {@link WebServiceMessageReceiverHandlerAdapter}. Defaults to {@link
163      * #DEFAULT_MESSAGE_RECEIVER_HANDLER_ADAPTER_BEAN_NAME}.
164      */
165     public void setMessageReceiverHandlerAdapterBeanName(String messageReceiverHandlerAdapterBeanName) {
166         this.messageReceiverHandlerAdapterBeanName = messageReceiverHandlerAdapterBeanName;
167     }
168 
169     /** Returns the bean name used to lookup a {@link WsdlDefinitionHandlerAdapter}. */
170     public String getWsdlDefinitionHandlerAdapterBeanName() {
171         return wsdlDefinitionHandlerAdapterBeanName;
172     }
173 
174     /**
175      * Sets the bean name used to lookup a {@link WsdlDefinitionHandlerAdapter}. Defaults to {@link
176      * #DEFAULT_WSDL_DEFINITION_HANDLER_ADAPTER_BEAN_NAME}.
177      */
178     public void setWsdlDefinitionHandlerAdapterBeanName(String wsdlDefinitionHandlerAdapterBeanName) {
179         this.wsdlDefinitionHandlerAdapterBeanName = wsdlDefinitionHandlerAdapterBeanName;
180     }
181 
182     /**
183      * Sets whether relative address locations in the WSDL are to be transformed using the request URI of the incoming
184      * {@link HttpServletRequest}. Defaults to <code>false</code>.
185      */
186     public void setTransformWsdlLocations(boolean transformWsdlLocations) {
187         this.transformWsdlLocations = transformWsdlLocations;
188     }
189 
190     protected void doService(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
191             throws Exception {
192         WsdlDefinition definition = getWsdlDefinition(httpServletRequest);
193         if (definition != null) {
194             wsdlDefinitionHandlerAdapter.handle(httpServletRequest, httpServletResponse, definition);
195         }
196         else {
197             messageReceiverHandlerAdapter.handle(httpServletRequest, httpServletResponse, messageReceiver);
198         }
199     }
200 
201     protected void initFrameworkServlet() throws ServletException, BeansException {
202         initMessageReceiverHandlerAdapter();
203         initWsdlDefinitionHandlerAdapter();
204         initMessageReceiver();
205         initWsdlDefinitions();
206     }
207 
208     protected long getLastModified(HttpServletRequest httpServletRequest) {
209         WsdlDefinition definition = getWsdlDefinition(httpServletRequest);
210         if (definition != null) {
211             return wsdlDefinitionHandlerAdapter.getLastModified(httpServletRequest, definition);
212         }
213         else {
214             return messageReceiverHandlerAdapter.getLastModified(httpServletRequest, messageReceiver);
215         }
216     }
217 
218     /** Returns the {@link WebServiceMessageReceiver} used by this servlet. */
219     protected WebServiceMessageReceiver getMessageReceiver() {
220         return messageReceiver;
221     }
222 
223     /**
224      * Determines the {@link WsdlDefinition} for a given request, or <code>null</code> if none is found.
225      * <p/>
226      * Default implementation checks whether the request method is <code>GET</code>, whether the request uri ends with
227      * <code>".wsdl"</code>, and if there is a <code>WsdlDefinition</code> with the same name as the filename in the
228      * request uri.
229      *
230      * @param request the <code>HttpServletRequest</code>
231      * @return a definition, or <code>null</code>
232      */
233     protected WsdlDefinition getWsdlDefinition(HttpServletRequest request) {
234         if ("GET".equals(request.getMethod()) && request.getRequestURI().endsWith(WSDL_SUFFIX_NAME)) {
235             String fileName = WebUtils.extractFilenameFromUrlPath(request.getRequestURI());
236             return (WsdlDefinition) wsdlDefinitions.get(fileName);
237         }
238         else {
239             return null;
240         }
241     }
242 
243     private void initMessageReceiverHandlerAdapter() {
244         try {
245             try {
246                 messageReceiverHandlerAdapter = (WebServiceMessageReceiverHandlerAdapter) getWebApplicationContext()
247                         .getBean(getMessageReceiverHandlerAdapterBeanName(),
248                                 WebServiceMessageReceiverHandlerAdapter.class);
249             }
250             catch (NoSuchBeanDefinitionException ignored) {
251                 messageReceiverHandlerAdapter = new WebServiceMessageReceiverHandlerAdapter();
252             }
253             initWebServiceMessageFactory();
254             messageReceiverHandlerAdapter.afterPropertiesSet();
255         }
256         catch (Exception ex) {
257             throw new BeanInitializationException("Could not initialize WebServiceMessageReceiverHandlerAdapter", ex);
258         }
259     }
260 
261     private void initWebServiceMessageFactory() {
262         WebServiceMessageFactory messageFactory;
263         try {
264             messageFactory = (WebServiceMessageFactory) getWebApplicationContext()
265                     .getBean(getMessageFactoryBeanName(), WebServiceMessageFactory.class);
266         }
267         catch (NoSuchBeanDefinitionException ignored) {
268             messageFactory = (WebServiceMessageFactory) defaultStrategiesHelper
269                     .getDefaultStrategy(WebServiceMessageFactory.class, getWebApplicationContext());
270             if (logger.isDebugEnabled()) {
271                 logger.debug("No WebServiceMessageFactory found in servlet '" + getServletName() + "': using default");
272             }
273         }
274         messageReceiverHandlerAdapter.setMessageFactory(messageFactory);
275     }
276 
277     private void initWsdlDefinitionHandlerAdapter() {
278         try {
279             try {
280                 wsdlDefinitionHandlerAdapter = (WsdlDefinitionHandlerAdapter) getWebApplicationContext()
281                         .getBean(getWsdlDefinitionHandlerAdapterBeanName(), WsdlDefinitionHandlerAdapter.class);
282 
283             }
284             catch (NoSuchBeanDefinitionException ignored) {
285                 wsdlDefinitionHandlerAdapter = new WsdlDefinitionHandlerAdapter();
286             }
287             wsdlDefinitionHandlerAdapter.setTransformLocations(isTransformWsdlLocations());
288             wsdlDefinitionHandlerAdapter.afterPropertiesSet();
289         }
290         catch (Exception ex) {
291             throw new BeanInitializationException("Could not initialize WsdlDefinitionHandlerAdapter", ex);
292         }
293     }
294 
295     private void initMessageReceiver() {
296         try {
297             messageReceiver = (WebServiceMessageReceiver) getWebApplicationContext()
298                     .getBean(getMessageReceiverBeanName(), WebServiceMessageReceiver.class);
299         }
300         catch (NoSuchBeanDefinitionException ex) {
301             messageReceiver = (WebServiceMessageReceiver) defaultStrategiesHelper
302                     .getDefaultStrategy(WebServiceMessageReceiver.class, getWebApplicationContext());
303             if (messageReceiver instanceof BeanNameAware) {
304                 ((BeanNameAware) messageReceiver).setBeanName(getServletName());
305             }
306             if (logger.isDebugEnabled()) {
307                 logger.debug("No MessageDispatcher found in servlet '" + getServletName() + "': using default");
308             }
309         }
310     }
311 
312     /** Find all {@link WsdlDefinition WsdlDefinitions} in the ApplicationContext, incuding ancestor contexts. */
313     private void initWsdlDefinitions() {
314         wsdlDefinitions = BeanFactoryUtils
315                 .beansOfTypeIncludingAncestors(getWebApplicationContext(), WsdlDefinition.class, true, false);
316         if (logger.isDebugEnabled()) {
317             for (Iterator iterator = wsdlDefinitions.entrySet().iterator(); iterator.hasNext();) {
318                 Map.Entry entry = (Map.Entry) iterator.next();
319                 String beanName = (String) entry.getKey();
320                 WsdlDefinition definition = (WsdlDefinition) entry.getValue();
321                 logger.debug("Published [" + definition + "] as " + beanName + WSDL_SUFFIX_NAME);
322             }
323         }
324     }
325 }