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.HashMap;
20  import java.util.Map;
21  import javax.servlet.http.HttpServletRequest;
22  import javax.servlet.http.HttpServletResponse;
23  import javax.xml.transform.Source;
24  import javax.xml.transform.Transformer;
25  import javax.xml.transform.dom.DOMResult;
26  import javax.xml.transform.dom.DOMSource;
27  import javax.xml.transform.stream.StreamResult;
28  
29  import org.springframework.beans.factory.InitializingBean;
30  import org.springframework.web.servlet.HandlerAdapter;
31  import org.springframework.web.servlet.ModelAndView;
32  import org.springframework.ws.wsdl.WsdlDefinition;
33  import org.springframework.xml.xpath.XPathExpression;
34  import org.springframework.xml.xpath.XPathExpressionFactory;
35  
36  import org.w3c.dom.Document;
37  
38  /**
39   * Adapter to use the {@code WsdlDefinition} interface with the generic {@code DispatcherServlet}.
40   * <p/>
41   * Reads the source from the mapped {@code WsdlDefinition} implementation, and writes that as the result to the
42   * {@code HttpServletResponse}.
43   * <p/>
44   * If the property {@code transformLocations} is set to {@code true}, this adapter will change
45   * {@code location} attributes in the WSDL definition to reflect the URL of the incoming request. If the location
46   * field in the original WSDL is an absolute path, the scheme, hostname, and port will be changed. If the location is a
47   * relative path, the scheme, hostname, port, and context path will be prepended. This behavior can be customized by
48   * overriding the {@code transformLocation()} method.
49   * <p/>
50   * For instance, if the location attribute defined in the WSDL is {@code http://localhost:8080/context/services/myService},
51   * and the request URI for the WSDL is {@code http://example.com/context/myService.wsdl}, the location will be
52   * changed to {@code http://example.com/context/services/myService}.
53   * <p/>
54   * If the location attribute defined in the WSDL is {@code /services/myService}, and the request URI for the WSDL
55   * is {@code http://example.com:8080/context/myService.wsdl}, the location will be changed to
56   * {@code http://example.com:8080/context/services/myService}.
57   * <p/>
58   * When {@code transformLocations} is enabled, all {@code location} attributes found in the WSDL definition
59   * are changed by default. This behavior can be customized by changing the {@code locationExpression} property,
60   * which is an XPath expression that matches the attributes to change.
61   *
62   * @author Arjen Poutsma
63   * @see WsdlDefinition
64   * @see #setTransformLocations(boolean)
65   * @see #setLocationExpression(String)
66   * @see #transformLocation(String,javax.servlet.http.HttpServletRequest)
67   * @since 1.0.0
68   */
69  public class WsdlDefinitionHandlerAdapter extends LocationTransformerObjectSupport implements HandlerAdapter, InitializingBean {
70  
71      /** Default XPath expression used for extracting all {@code location} attributes from the WSDL definition. */
72      public static final String DEFAULT_LOCATION_EXPRESSION = "//@location";
73  
74      /** Default XPath expression used for extracting all {@code schemaLocation} attributes from the WSDL definition. */
75      public static final String DEFAULT_SCHEMA_LOCATION_EXPRESSION = "//@schemaLocation";
76  
77      private static final String CONTENT_TYPE = "text/xml";
78  
79      private Map<String, String> expressionNamespaces = new HashMap<String, String>();
80  
81      private String locationExpression = DEFAULT_LOCATION_EXPRESSION;
82  
83      private String schemaLocationExpression = DEFAULT_SCHEMA_LOCATION_EXPRESSION;
84  
85      private XPathExpression locationXPathExpression;
86  
87      private XPathExpression schemaLocationXPathExpression;
88  
89      private boolean transformLocations = false;
90  
91      private boolean transformSchemaLocations = false;
92  
93      /**
94       * Sets the XPath expression used for extracting the {@code location} attributes from the WSDL 1.1 definition.
95       * <p/>
96       * Defaults to {@code DEFAULT_LOCATION_EXPRESSION}.
97       */
98      public void setLocationExpression(String locationExpression) {
99          this.locationExpression = locationExpression;
100     }
101 
102     /**
103      * Sets the XPath expression used for extracting the {@code schemaLocation} attributes from the WSDL 1.1 definition.
104      * <p/>
105      * Defaults to {@code DEFAULT_SCHEMA_LOCATION_EXPRESSION}.
106      */
107     public void setSchemaLocationExpression(String schemaLocationExpression) {
108         this.schemaLocationExpression = schemaLocationExpression;
109     }
110 
111     /**
112      * Sets whether relative address locations in the WSDL are to be transformed using the request URI of the incoming
113      * {@code HttpServletRequest}. Defaults to {@code false}.
114      */
115     public void setTransformLocations(boolean transformLocations) {
116         this.transformLocations = transformLocations;
117     }
118 
119     /**
120      * Sets whether relative address schema locations in the WSDL are to be transformed using the request URI of the
121      * incoming {@code HttpServletRequest}. Defaults to {@code false}.
122      */
123     public void setTransformSchemaLocations(boolean transformSchemaLocations) {
124         this.transformSchemaLocations = transformSchemaLocations;
125     }
126 
127     public long getLastModified(HttpServletRequest request, Object handler) {
128         Source definitionSource = ((WsdlDefinition) handler).getSource();
129         return LastModifiedHelper.getLastModified(definitionSource);
130     }
131 
132     public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
133             throws Exception {
134         if (HttpTransportConstants.METHOD_GET.equals(request.getMethod())) {
135             WsdlDefinition definition = (WsdlDefinition) handler;
136 
137             Transformer transformer = createTransformer();
138             Source definitionSource = definition.getSource();
139 
140             if (transformLocations || transformSchemaLocations) {
141                 DOMResult domResult = new DOMResult();
142                 transformer.transform(definitionSource, domResult);
143                 Document definitionDocument = (Document) domResult.getNode();
144                 if (transformLocations) {
145                     transformLocations(definitionDocument, request);
146                 }
147                 if (transformSchemaLocations) {
148                     transformSchemaLocations(definitionDocument, request);
149                 }
150                 definitionSource = new DOMSource(definitionDocument);
151             }
152 
153             response.setContentType(CONTENT_TYPE);
154             StreamResult responseResult = new StreamResult(response.getOutputStream());
155             transformer.transform(definitionSource, responseResult);
156         }
157         else {
158             response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
159         }
160         return null;
161     }
162 
163     public boolean supports(Object handler) {
164         return handler instanceof WsdlDefinition;
165     }
166 
167     public void afterPropertiesSet() throws Exception {
168         locationXPathExpression =
169                 XPathExpressionFactory.createXPathExpression(locationExpression, expressionNamespaces);
170         schemaLocationXPathExpression =
171                 XPathExpressionFactory.createXPathExpression(schemaLocationExpression, expressionNamespaces);
172     }
173 
174     /**
175      * Transforms all {@code location} attributes to reflect the server name given {@code HttpServletRequest}.
176      * Determines the suitable attributes by evaluating the defined XPath expression, and delegates to
177      * {@code transformLocation} to do the transformation for all attributes that match.
178      * <p/>
179      * This method is only called when the {@code transformLocations} property is true.
180      *
181      * @see #setLocationExpression(String)
182      * @see #setTransformLocations(boolean)
183      * @see #transformLocation(String,javax.servlet.http.HttpServletRequest)
184      */
185     protected void transformLocations(Document definitionDocument, HttpServletRequest request) throws Exception {
186         transformLocations(locationXPathExpression, definitionDocument, request);
187     }
188 
189     /**
190      * Transforms all {@code schemaLocation} attributes to reflect the server name given {@code HttpServletRequest}.
191      * Determines the suitable attributes by evaluating the defined XPath expression, and delegates to
192      * {@code transformLocation} to do the transformation for all attributes that match.
193      * <p/>
194      * This method is only called when the {@code transformSchemaLocations} property is true.
195      *
196      * @see #setSchemaLocationExpression(String)
197      * @see #setTransformSchemaLocations(boolean)
198      * @see #transformLocation(String,javax.servlet.http.HttpServletRequest)
199      */
200     protected void transformSchemaLocations(Document definitionDocument, HttpServletRequest request) throws Exception {
201         transformLocations(schemaLocationXPathExpression, definitionDocument, request);
202     }
203 
204 }