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.soap.addressing.version;
18  
19  import java.net.URI;
20  import java.net.URISyntaxException;
21  import java.util.Collections;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.Locale;
25  import java.util.Properties;
26  import javax.xml.namespace.QName;
27  import javax.xml.parsers.DocumentBuilder;
28  import javax.xml.parsers.DocumentBuilderFactory;
29  import javax.xml.parsers.ParserConfigurationException;
30  import javax.xml.transform.Result;
31  import javax.xml.transform.TransformerException;
32  import javax.xml.transform.dom.DOMResult;
33  import javax.xml.transform.dom.DOMSource;
34  
35  import org.w3c.dom.Document;
36  import org.w3c.dom.Element;
37  import org.w3c.dom.Node;
38  
39  import org.springframework.util.StringUtils;
40  import org.springframework.ws.soap.SoapFault;
41  import org.springframework.ws.soap.SoapHeader;
42  import org.springframework.ws.soap.SoapHeaderElement;
43  import org.springframework.ws.soap.SoapMessage;
44  import org.springframework.ws.soap.addressing.AddressingException;
45  import org.springframework.ws.soap.addressing.core.EndpointReference;
46  import org.springframework.ws.soap.addressing.core.MessageAddressingProperties;
47  import org.springframework.ws.soap.soap11.Soap11Body;
48  import org.springframework.ws.soap.soap12.Soap12Body;
49  import org.springframework.ws.soap.soap12.Soap12Fault;
50  import org.springframework.xml.namespace.QNameUtils;
51  import org.springframework.xml.transform.TransformerObjectSupport;
52  import org.springframework.xml.xpath.XPathExpression;
53  import org.springframework.xml.xpath.XPathExpressionFactory;
54  
55  /**
56   * Abstract base class for {@link AddressingVersion} implementations. Uses {@link XPathExpression}s to retrieve
57   * addressing information.
58   *
59   * @author Arjen Poutsma
60   * @since 1.5.0
61   */
62  public abstract class AbstractAddressingVersion extends TransformerObjectSupport implements AddressingVersion {
63  
64      private static DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
65  
66      private final XPathExpression toExpression;
67  
68      private final XPathExpression actionExpression;
69  
70      private final XPathExpression messageIdExpression;
71  
72      private final XPathExpression fromExpression;
73  
74      private final XPathExpression replyToExpression;
75  
76      private final XPathExpression faultToExpression;
77  
78      private final XPathExpression addressExpression;
79  
80      private final XPathExpression referencePropertiesExpression;
81  
82      private final XPathExpression referenceParametersExpression;
83  
84      protected AbstractAddressingVersion() {
85          Properties namespaces = new Properties();
86          namespaces.setProperty(getNamespacePrefix(), getNamespaceUri());
87          toExpression = createNormalizedExpression(getToName(), namespaces);
88          actionExpression = createNormalizedExpression(getActionName(), namespaces);
89          messageIdExpression = createNormalizedExpression(getMessageIdName(), namespaces);
90          fromExpression = createExpression(getFromName(), namespaces);
91          replyToExpression = createExpression(getReplyToName(), namespaces);
92          faultToExpression = createExpression(getFaultToName(), namespaces);
93          addressExpression = createNormalizedExpression(getAddressName(), namespaces);
94          if (getReferencePropertiesName() != null) {
95              referencePropertiesExpression = createChildrenExpression(getReferencePropertiesName(), namespaces);
96          }
97          else {
98              referencePropertiesExpression = null;
99          }
100         if (getReferenceParametersName() != null) {
101             referenceParametersExpression = createChildrenExpression(getReferenceParametersName(), namespaces);
102         }
103         else {
104             referenceParametersExpression = null;
105         }
106     }
107 
108     private XPathExpression createExpression(QName name, Properties namespaces) {
109         String expression = name.getPrefix() + ":" + name.getLocalPart();
110         return XPathExpressionFactory.createXPathExpression(expression, namespaces);
111     }
112 
113     private XPathExpression createNormalizedExpression(QName name, Properties namespaces) {
114         String expression = "normalize-space(" + name.getPrefix() + ":" + name.getLocalPart() + ")";
115         return XPathExpressionFactory.createXPathExpression(expression, namespaces);
116     }
117 
118     private XPathExpression createChildrenExpression(QName name, Properties namespaces) {
119         String expression = name.getPrefix() + ":" + name.getLocalPart() + "/*";
120         return XPathExpressionFactory.createXPathExpression(expression, namespaces);
121     }
122 
123     public MessageAddressingProperties getMessageAddressingProperties(SoapMessage message) {
124         Element headerElement = getSoapHeaderElement(message);
125         URI to = getUri(headerElement, toExpression);
126         EndpointReference from = getEndpointReference(fromExpression.evaluateAsNode(headerElement));
127         EndpointReference replyTo = getEndpointReference(replyToExpression.evaluateAsNode(headerElement));
128         if (replyTo == null) {
129             replyTo = getDefaultReplyTo(from);
130         }
131         EndpointReference faultTo = getEndpointReference(faultToExpression.evaluateAsNode(headerElement));
132         if (faultTo == null) {
133             faultTo = replyTo;
134         }
135         URI action = getUri(headerElement, actionExpression);
136         URI messageId = getUri(headerElement, messageIdExpression);
137         return new MessageAddressingProperties(to, from, replyTo, faultTo, action, messageId);
138     }
139 
140     private URI getUri(Node node, XPathExpression expression) {
141         String messageId = expression.evaluateAsString(node);
142         if (!StringUtils.hasLength(messageId)) {
143             return null;
144         }
145         try {
146             return new URI(messageId);
147         }
148         catch (URISyntaxException e) {
149             return null;
150         }
151     }
152 
153     private Element getSoapHeaderElement(SoapMessage message) {
154         SoapHeader header = message.getSoapHeader();
155         if (header.getSource() instanceof DOMSource) {
156             DOMSource domSource = (DOMSource) header.getSource();
157             if (domSource.getNode() != null && domSource.getNode().getNodeType() == Node.ELEMENT_NODE) {
158                 return (Element) domSource.getNode();
159             }
160         }
161         try {
162             DOMResult domResult = new DOMResult();
163             transform(header.getSource(), domResult);
164             Document document = (Document) domResult.getNode();
165             return document.getDocumentElement();
166         }
167         catch (TransformerException ex) {
168             throw new AddressingException("Could not transform SoapHeader to Document", ex);
169         }
170     }
171 
172     /** Given a ReplyTo, FaultTo, or From node, returns an endpoint reference. */
173     private EndpointReference getEndpointReference(Node node) {
174         if (node == null) {
175             return null;
176         }
177         URI address = getUri(node, addressExpression);
178         if (address == null) {
179             return null;
180         }
181         List referenceProperties = referencePropertiesExpression != null ?
182                 referencePropertiesExpression.evaluateAsNodeList(node) : Collections.EMPTY_LIST;
183         List referenceParameters = referenceParametersExpression != null ?
184                 referenceParametersExpression.evaluateAsNodeList(node) : Collections.EMPTY_LIST;
185         return new EndpointReference(address, referenceProperties, referenceParameters);
186     }
187 
188     public void addAddressingHeaders(SoapMessage message, MessageAddressingProperties map) {
189         SoapHeader header = message.getSoapHeader();
190         // To
191         if (map.getTo() != null) {
192             SoapHeaderElement to = header.addHeaderElement(getToName());
193             to.setText(map.getTo().toString());
194             to.setMustUnderstand(true);
195         }
196         // From
197         if (map.getFrom() != null) {
198             SoapHeaderElement from = header.addHeaderElement(getFromName());
199             addEndpointReference(from, map.getFrom());
200         }
201         //ReplyTo
202         if (map.getReplyTo() != null) {
203             SoapHeaderElement replyTo = header.addHeaderElement(getReplyToName());
204             addEndpointReference(replyTo, map.getReplyTo());
205         }
206         // FaultTo
207         if (map.getFaultTo() != null) {
208             SoapHeaderElement faultTo = header.addHeaderElement(getFaultToName());
209             addEndpointReference(faultTo, map.getFaultTo());
210         }
211         // Action
212         SoapHeaderElement action = header.addHeaderElement(getActionName());
213         action.setText(map.getAction().toString());
214         // MessageID
215         if (map.getMessageId() != null) {
216             SoapHeaderElement messageId = header.addHeaderElement(getMessageIdName());
217             messageId.setText(map.getMessageId().toString());
218         }
219         // RelatesTo
220         if (map.getRelatesTo() != null) {
221             SoapHeaderElement relatesTo = header.addHeaderElement(getRelatesToName());
222             relatesTo.setText(map.getRelatesTo().toString());
223         }
224         addReferenceNodes(header.getResult(), map.getReferenceParameters());
225         addReferenceNodes(header.getResult(), map.getReferenceProperties());
226     }
227 
228     public final boolean understands(SoapHeaderElement headerElement) {
229         return getNamespaceUri().equals(headerElement.getName().getNamespaceURI());
230     }
231 
232     /** Adds ReplyTo, FaultTo, or From EPR to the given header Element. */
233     protected void addEndpointReference(SoapHeaderElement headerElement, EndpointReference epr) {
234         if (epr == null || epr.getAddress() == null) {
235             return;
236         }
237         try {
238             DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
239             Document document = documentBuilder.newDocument();
240             Element address = document.createElementNS(getNamespaceUri(), QNameUtils.toQualifiedName(getAddressName()));
241             address.setTextContent(epr.getAddress().toString());
242             transform(new DOMSource(address), headerElement.getResult());
243             if (getReferenceParametersName() != null && !epr.getReferenceParameters().isEmpty()) {
244                 Element referenceParams = document.createElementNS(getNamespaceUri(),
245                         QNameUtils.toQualifiedName(getReferenceParametersName()));
246                 addReferenceNodes(new DOMResult(referenceParams), epr.getReferenceParameters());
247                 transform(new DOMSource(referenceParams), headerElement.getResult());
248             }
249             if (getReferencePropertiesName() != null && !epr.getReferenceProperties().isEmpty()) {
250                 Element referenceProps = document.createElementNS(getNamespaceUri(),
251                         QNameUtils.toQualifiedName(getReferencePropertiesName()));
252                 addReferenceNodes(new DOMResult(referenceProps), epr.getReferenceProperties());
253                 transform(new DOMSource(referenceProps), headerElement.getResult());
254             }
255         }
256         catch (ParserConfigurationException ex) {
257             throw new AddressingException("Could not add Endpoint Reference [" + epr + "] to header element", ex);
258         }
259         catch (TransformerException ex) {
260             throw new AddressingException("Could not add reference properties/parameters to message", ex);
261         }
262     }
263 
264     protected void addReferenceNodes(Result result, List nodes) {
265         try {
266             for (Iterator iterator = nodes.iterator(); iterator.hasNext();) {
267                 Node node = (Node) iterator.next();
268                 DOMSource source = new DOMSource(node);
269                 transform(source, result);
270             }
271         }
272         catch (TransformerException ex) {
273             throw new AddressingException("Could not add reference properties/parameters to message", ex);
274         }
275     }
276 
277     public final SoapFault addInvalidAddressingHeaderFault(SoapMessage message) {
278         return addAddressingFault(message, getInvalidAddressingHeaderFaultSubcode(),
279                 getInvalidAddressingHeaderFaultReason());
280     }
281 
282     public final SoapFault addMessageAddressingHeaderRequiredFault(SoapMessage message) {
283         return addAddressingFault(message, getMessageAddressingHeaderRequiredFaultSubcode(),
284                 getMessageAddressingHeaderRequiredFaultReason());
285     }
286 
287     private SoapFault addAddressingFault(SoapMessage message, QName subcode, String reason) {
288         if (message.getSoapBody() instanceof Soap11Body) {
289             Soap11Body soapBody = (Soap11Body) message.getSoapBody();
290             return soapBody.addFault(subcode, reason, Locale.ENGLISH);
291         }
292         else if (message.getSoapBody() instanceof Soap12Body) {
293             Soap12Body soapBody = (Soap12Body) message.getSoapBody();
294             Soap12Fault soapFault = (Soap12Fault) soapBody.addClientOrSenderFault(reason, Locale.ENGLISH);
295             soapFault.addFaultSubcode(subcode);
296             return soapFault;
297         }
298         return null;
299     }
300 
301     /*
302     * Address URIs
303     */
304 
305     public final boolean hasAnonymousAddress(EndpointReference epr) {
306         URI anonymous = getAnonymous();
307         return anonymous != null && anonymous.equals(epr.getAddress());
308     }
309 
310     public final boolean hasNoneAddress(EndpointReference epr) {
311         URI none = getNone();
312         return none != null && none.equals(epr.getAddress());
313     }
314 
315     /** Returns the prefix associated with the WS-Addressing namespace handled by this specification. */
316     protected String getNamespacePrefix() {
317         return "wsa";
318     }
319 
320     /** Returns the WS-Addressing namespace handled by this specification. */
321     protected abstract String getNamespaceUri();
322 
323     /*
324      * Message addressing properties
325      */
326 
327     /** Returns the qualified name of the <code>To</code> addressing header. */
328     protected QName getToName() {
329         return QNameUtils.createQName(getNamespaceUri(), "To", getNamespacePrefix());
330     }
331 
332     /** Returns the qualified name of the <code>From</code> addressing header. */
333     protected QName getFromName() {
334         return QNameUtils.createQName(getNamespaceUri(), "From", getNamespacePrefix());
335     }
336 
337     /** Returns the qualified name of the <code>ReplyTo</code> addressing header. */
338     protected QName getReplyToName() {
339         return QNameUtils.createQName(getNamespaceUri(), "ReplyTo", getNamespacePrefix());
340     }
341 
342     /** Returns the qualified name of the <code>FaultTo</code> addressing header. */
343     protected QName getFaultToName() {
344         return QNameUtils.createQName(getNamespaceUri(), "FaultTo", getNamespacePrefix());
345     }
346 
347     /** Returns the qualified name of the <code>Action</code> addressing header. */
348     protected QName getActionName() {
349         return QNameUtils.createQName(getNamespaceUri(), "Action", getNamespacePrefix());
350     }
351 
352     /** Returns the qualified name of the <code>MessageID</code> addressing header. */
353     protected QName getMessageIdName() {
354         return QNameUtils.createQName(getNamespaceUri(), "MessageID", getNamespacePrefix());
355     }
356 
357     /** Returns the qualified name of the <code>RelatesTo</code> addressing header. */
358     protected QName getRelatesToName() {
359         return QNameUtils.createQName(getNamespaceUri(), "RelatesTo", getNamespacePrefix());
360     }
361 
362     /** Returns the qualified name of the <code>RelatesTo</code> addressing header. */
363     protected QName getRelationshipTypeName() {
364         return new QName("RelationshipType");
365     }
366 
367     /**
368      * Returns the qualified name of the <code>ReferenceProperties</code> in the endpoint reference. Returns
369      * <code>null</code> when reference properties are not supported by this version of the spec.
370      */
371     protected QName getReferencePropertiesName() {
372         return QNameUtils.createQName(getNamespaceUri(), "ReferenceProperties", getNamespacePrefix());
373     }
374 
375     /**
376      * Returns the qualified name of the <code>ReferenceParameters</code> in the endpoint reference. Returns
377      * <code>null</code> when reference parameters are not supported by this version of the spec.
378      */
379     protected QName getReferenceParametersName() {
380         return QNameUtils.createQName(getNamespaceUri(), "ReferenceParameters", getNamespacePrefix());
381     }
382 
383     /*
384      * Endpoint Reference
385      */
386 
387     /** The qualified name of the <code>Address</code> in <code>EndpointReference</code>. */
388     protected QName getAddressName() {
389         return QNameUtils.createQName(getNamespaceUri(), "Address", getNamespacePrefix());
390     }
391 
392     /** Returns the default ReplyTo EPR. Can be based on the From EPR, or the anonymous URI. */
393     protected abstract EndpointReference getDefaultReplyTo(EndpointReference from);
394 
395     /*
396      * Address URIs
397      */
398 
399     /** Returns the anonymous URI. */
400     protected abstract URI getAnonymous();
401 
402     /** Returns the none URI, or <code>null</code> if the spec does not define it. */
403     protected abstract URI getNone();
404 
405     /*
406      * Faults
407      */
408 
409     /** Returns the qualified name of the fault subcode that indicates that a header is missing. */
410     protected abstract QName getMessageAddressingHeaderRequiredFaultSubcode();
411 
412     /** Returns the reason of the fault that indicates that a header is missing. */
413     protected abstract String getMessageAddressingHeaderRequiredFaultReason();
414 
415     /** Returns the qualified name of the fault subcode that indicates that a header is invalid. */
416     protected abstract QName getInvalidAddressingHeaderFaultSubcode();
417 
418     /** Returns the reason of the fault that indicates that a header is invalid. */
419     protected abstract String getInvalidAddressingHeaderFaultReason();
420 }