From 10e47d5ab65e7dcf9034bbec70fc1376a986277a Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Mon, 14 May 2007 15:28:37 +0000 Subject: [PATCH] Added handleFault to EndpointInterceptor, moved SoapEndpointInterceptor and SoapEhndpointMapping to org.sfwrk.ws.soap.server package. --- ...ActionAnnotationMethodEndpointMapping.java | 2 +- .../ws/server/EndpointInterceptor.java | 57 +++++++++++++++---- .../ws/server/MessageDispatcher.java | 16 ++++-- .../endpoint/AbstractLoggingInterceptor.java | 13 +++-- .../AbstractValidatingInterceptor.java | 25 ++++---- .../PayloadTransformingInterceptor.java | 13 +++-- .../springframework/ws/soap/SoapElement.java | 8 ++- .../springframework/ws/soap/SoapHeader.java | 9 --- .../ws/soap/SoapHeaderElement.java | 17 +++++- .../ws/soap/axiom/AxiomSoapElement.java | 12 ++++ .../ws/soap/axiom/AxiomSoapHeaderElement.java | 8 +++ .../ws/soap/saaj/Saaj11Implementation.java | 18 ++++-- .../ws/soap/saaj/Saaj12Implementation.java | 14 ++++- .../ws/soap/saaj/Saaj13Implementation.java | 16 ++++-- .../ws/soap/saaj/SaajImplementation.java | 12 +++- .../ws/soap/saaj/SaajSoapElement.java | 9 +++ .../ws/soap/saaj/SaajSoapHeader.java | 10 ---- .../ws/soap/saaj/SaajSoapHeaderElement.java | 14 +++++ .../{ => server}/SoapEndpointInterceptor.java | 15 +---- .../{ => server}/SoapEndpointMapping.java | 10 +--- .../ws/soap/server/SoapMessageDispatcher.java | 47 +-------------- .../SoapEnvelopeLoggingInterceptor.java | 6 +- .../DelegatingSoapEndpointMapping.java | 6 +- .../mapping/SoapActionEndpointMapping.java | 2 +- .../ws/server/MessageDispatcherTest.java | 41 +++++++++++++ .../ws/soap/AbstractSoapHeaderTestCase.java | 11 ---- .../server/SoapMessageDispatcherTest.java | 1 - .../AbstractWsSecurityInterceptor.java | 2 +- src/changes/changes.xml | 3 + src/site/apt/upgrading.apt | 14 ++++- .../xml/xpath/XPathExpressionFactory.java | 4 +- 31 files changed, 268 insertions(+), 167 deletions(-) rename core/src/main/java/org/springframework/ws/soap/{ => server}/SoapEndpointInterceptor.java (64%) rename core/src/main/java/org/springframework/ws/soap/{ => server}/SoapEndpointMapping.java (81%) diff --git a/core-tiger/src/main/java/org/springframework/ws/soap/server/endpoint/mapping/SoapActionAnnotationMethodEndpointMapping.java b/core-tiger/src/main/java/org/springframework/ws/soap/server/endpoint/mapping/SoapActionAnnotationMethodEndpointMapping.java index bc087619..a8efbc0b 100644 --- a/core-tiger/src/main/java/org/springframework/ws/soap/server/endpoint/mapping/SoapActionAnnotationMethodEndpointMapping.java +++ b/core-tiger/src/main/java/org/springframework/ws/soap/server/endpoint/mapping/SoapActionAnnotationMethodEndpointMapping.java @@ -24,9 +24,9 @@ import org.springframework.ws.context.MessageContext; import org.springframework.ws.server.EndpointInterceptor; import org.springframework.ws.server.EndpointInvocationChain; import org.springframework.ws.server.endpoint.mapping.AbstractAnnotationMethodEndpointMapping; -import org.springframework.ws.soap.SoapEndpointMapping; import org.springframework.ws.soap.SoapMessage; import org.springframework.ws.soap.server.SoapEndpointInvocationChain; +import org.springframework.ws.soap.server.SoapEndpointMapping; import org.springframework.ws.soap.server.endpoint.annotation.SoapAction; /** diff --git a/core/src/main/java/org/springframework/ws/server/EndpointInterceptor.java b/core/src/main/java/org/springframework/ws/server/EndpointInterceptor.java index 50ea2a9d..a64fca77 100644 --- a/core/src/main/java/org/springframework/ws/server/EndpointInterceptor.java +++ b/core/src/main/java/org/springframework/ws/server/EndpointInterceptor.java @@ -23,14 +23,15 @@ import org.springframework.ws.context.MessageContext; * existing or custom interceptors for certain groups of endpoints, to add common preprocessing behavior without needing * to modify each endpoint implementation. *

- * A EndpointInterceptor gets called before the appropriate EndpointAdapter triggers the - * invocation of the endpoint itself. This mechanism can be used for a large field of preprocessing aspects, e.g. for - * authorization checks, or message header checks. Its main purpose is to allow for factoring out repetitive endpoint - * code. + * A EndpointInterceptor gets called before the appropriate {@link EndpointAdapter} triggers the invocation + * of the endpoint itself. This mechanism can be used for a large field of preprocessing aspects, e.g. for authorization + * checks, or message header checks. Its main purpose is to allow for factoring out repetitive endpoint code. *

- * Typically an interceptor chain is defined per EndpointMapping bean, sharing its granularity. To be able - * to apply a certain interceptor chain to a group of handlers, one needs to map the desired handlers via one - * EndpointMapping bean. + * Typically an interceptor chain is defined per {@link EndpointMapping} bean, sharing its granularity. To be able to + * apply a certain interceptor chain to a group of handlers, one needs to map the desired handlers via one + * EndpointMapping bean.The interceptors themselves are defined as beans in the application context, + * referenced by the mapping bean definition via its interceptors property (in XML: a <list> of + * <ref>). * * @author Arjen Poutsma * @see EndpointInvocationChain#getInterceptors() @@ -40,23 +41,57 @@ import org.springframework.ws.context.MessageContext; public interface EndpointInterceptor { /** - * Processes the incoming request message. + * Processes the incoming request message. Called after {@link EndpointMapping} determined an appropriate endpoint + * object, but before {@link EndpointAdapter} invokes the endpoint. + *

+ * {@link MessageDispatcher} processes an endpoint in an invocation chain, consisting of any number of interceptors, + * with the endpoint itself at the end. With this method, each interceptor can decide to abort the chain, typically + * creating a custom response. * * @param messageContext contains the incoming request message * @param endpoint chosen endpoint to invoke * @return true to continue processing of the request interceptor chain; false to indicate - * blocking of the request handler chain, without invoking the endpoint + * blocking of the request endpoint chain, without invoking the endpoint + * @throws Exception in case of errors + * @see org.springframework.ws.context.MessageContext#getRequest() */ boolean handleRequest(MessageContext messageContext, Object endpoint) throws Exception; /** - * Processes the outgoing response message. + * Processes the outgoing response message. Called after {@link EndpointAdapter} actually invoked the endpoint. Can + * manipulate the response, if any, by adding new headers, etc. + *

+ * {@link MessageDispatcher} processes an endpoint in an invocation chain, consisting of any number of interceptors, + * with the endpoint itself at the end. With this method, each interceptor can post-process an invocation, getting + * applied in inverse order of the execution chain. + *

+ * Note: Will only be called if this interceptor's {@link #handleRequest} method has successfully completed. * * @param messageContext contains both request and response messages * @param endpoint chosen endpoint to invoke * @return true to continue processing of the reponse interceptor chain; false to indicate - * blocking of the response handler chain. + * blocking of the response endpoint chain. + * @throws Exception in case of errors + * @see org.springframework.ws.context.MessageContext#getRequest() + * @see org.springframework.ws.context.MessageContext#hasResponse() + * @see org.springframework.ws.context.MessageContext#getResponse() */ boolean handleResponse(MessageContext messageContext, Object endpoint) throws Exception; + /** + * Processes the outgoing response fault. Called after {@link EndpointAdapter} actually invoked the endpoint. Can + * manipulate the response, if any, by adding new headers, etc. + *

+ * {@link MessageDispatcher} processes an endpoint in an invocation chain, consisting of any number of interceptors, + * with the endpoint itself at the end. With this method, each interceptor can post-process an invocation, getting + * applied in inverse order of the execution chain. + *

+ * Note: Will only be called if this interceptor's {@link #handleRequest} method has successfully completed. + * + * @param messageContext contains both request and response messages, the response should contains a Fault + * @param endpoint chosen endpoint to invoke + * @return true to continue processing of the reponse interceptor chain; false to indicate + * blocking of the response handler chain. + */ + boolean handleFault(MessageContext messageContext, Object endpoint) throws Exception; } diff --git a/core/src/main/java/org/springframework/ws/server/MessageDispatcher.java b/core/src/main/java/org/springframework/ws/server/MessageDispatcher.java index c315bd59..f4adb1ef 100644 --- a/core/src/main/java/org/springframework/ws/server/MessageDispatcher.java +++ b/core/src/main/java/org/springframework/ws/server/MessageDispatcher.java @@ -179,7 +179,7 @@ public class MessageDispatcher implements WebServiceMessageReceiver, BeanNameAwa return; } // Apply handleRequest of registered interceptors - if (!ObjectUtils.isEmpty(mappedEndpoint.getInterceptors())) { + if (mappedEndpoint.getInterceptors() != null) { for (int i = 0; i < mappedEndpoint.getInterceptors().length; i++) { EndpointInterceptor interceptor = mappedEndpoint.getInterceptors()[i]; interceptorIndex = i; @@ -303,15 +303,21 @@ public class MessageDispatcher implements WebServiceMessageReceiver, BeanNameAwa * @param messageContext the message context, whose request and response are filled * @see EndpointInterceptor#handleResponse(MessageContext,Object) */ - protected void triggerHandleResponse(EndpointInvocationChain mappedEndpoint, - int interceptorIndex, - MessageContext messageContext) throws Exception { + private void triggerHandleResponse(EndpointInvocationChain mappedEndpoint, + int interceptorIndex, + MessageContext messageContext) throws Exception { if (mappedEndpoint != null && messageContext.hasResponse() && !ObjectUtils.isEmpty(mappedEndpoint.getInterceptors())) { boolean resume = true; + boolean hasFault = messageContext.getResponse().hasFault(); for (int i = interceptorIndex; resume && i >= 0; i--) { EndpointInterceptor interceptor = mappedEndpoint.getInterceptors()[i]; - resume = interceptor.handleResponse(messageContext, mappedEndpoint.getEndpoint()); + if (!hasFault) { + resume = interceptor.handleResponse(messageContext, mappedEndpoint.getEndpoint()); + } + else { + resume = interceptor.handleFault(messageContext, mappedEndpoint.getEndpoint()); + } } } } diff --git a/core/src/main/java/org/springframework/ws/server/endpoint/AbstractLoggingInterceptor.java b/core/src/main/java/org/springframework/ws/server/endpoint/AbstractLoggingInterceptor.java index ba259ee3..4950dce9 100644 --- a/core/src/main/java/org/springframework/ws/server/endpoint/AbstractLoggingInterceptor.java +++ b/core/src/main/java/org/springframework/ws/server/endpoint/AbstractLoggingInterceptor.java @@ -42,16 +42,12 @@ public abstract class AbstractLoggingInterceptor extends TransformerObjectSuppor private boolean logResponse = true; - /** - * Indicates whether the request should be logged. Default is true. - */ + /** Indicates whether the request should be logged. Default is true. */ public final void setLogRequest(boolean logRequest) { this.logRequest = logRequest; } - /** - * Indicates whether the response should be logged. Default is true. - */ + /** Indicates whether the response should be logged. Default is true. */ public final void setLogResponse(boolean logResponse) { this.logResponse = logResponse; } @@ -86,6 +82,11 @@ public abstract class AbstractLoggingInterceptor extends TransformerObjectSuppor return true; } + /** Does nothing by default. Faults are not logged. */ + public boolean handleFault(MessageContext messageContext, Object endpoint) throws Exception { + return true; + } + private Transformer createNonIndentingTransformer() throws TransformerConfigurationException { Transformer transformer = createTransformer(); transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); diff --git a/core/src/main/java/org/springframework/ws/server/endpoint/interceptor/AbstractValidatingInterceptor.java b/core/src/main/java/org/springframework/ws/server/endpoint/interceptor/AbstractValidatingInterceptor.java index 2140cf42..a08798b1 100644 --- a/core/src/main/java/org/springframework/ws/server/endpoint/interceptor/AbstractValidatingInterceptor.java +++ b/core/src/main/java/org/springframework/ws/server/endpoint/interceptor/AbstractValidatingInterceptor.java @@ -111,6 +111,18 @@ public abstract class AbstractValidatingInterceptor extends TransformerObjectSup this.validateResponse = validateResponse; } + public void afterPropertiesSet() throws Exception { + Assert.notEmpty(schemas, "setting either the schema or schemas property is required"); + Assert.hasLength(schemaLanguage, "schemaLanguage is required"); + for (int i = 0; i < schemas.length; i++) { + Assert.isTrue(schemas[i].exists(), "schema [" + schemas[i] + "] does not exist"); + } + if (logger.isInfoEnabled()) { + logger.info("Validating using " + StringUtils.arrayToCommaDelimitedString(schemas)); + } + validator = XmlValidatorFactory.createValidator(schemas, schemaLanguage); + } + /** * Validates the request message in the given message context. Validation only occurs if * validateRequest is set to true, which is the default. @@ -196,16 +208,9 @@ public abstract class AbstractValidatingInterceptor extends TransformerObjectSup return false; } - public void afterPropertiesSet() throws Exception { - Assert.notEmpty(schemas, "setting either the schema or schemas property is required"); - Assert.hasLength(schemaLanguage, "schemaLanguage is required"); - for (int i = 0; i < schemas.length; i++) { - Assert.isTrue(schemas[i].exists(), "schema [" + schemas[i] + "] does not exist"); - } - if (logger.isInfoEnabled()) { - logger.info("Validating using " + StringUtils.arrayToCommaDelimitedString(schemas)); - } - validator = XmlValidatorFactory.createValidator(schemas, schemaLanguage); + /** Does nothing by default. Faults are not validated. */ + public boolean handleFault(MessageContext messageContext, Object endpoint) throws Exception { + return true; } /** diff --git a/core/src/main/java/org/springframework/ws/server/endpoint/interceptor/PayloadTransformingInterceptor.java b/core/src/main/java/org/springframework/ws/server/endpoint/interceptor/PayloadTransformingInterceptor.java index 5c665b41..ff9e2092 100644 --- a/core/src/main/java/org/springframework/ws/server/endpoint/interceptor/PayloadTransformingInterceptor.java +++ b/core/src/main/java/org/springframework/ws/server/endpoint/interceptor/PayloadTransformingInterceptor.java @@ -56,16 +56,12 @@ public class PayloadTransformingInterceptor implements EndpointInterceptor, Init private Templates responseTemplates; - /** - * Sets the XSLT stylesheet to use for transforming incoming request. - */ + /** Sets the XSLT stylesheet to use for transforming incoming request. */ public void setRequestXslt(Resource requestXslt) { this.requestXslt = requestXslt; } - /** - * Sets the XSLT stylesheet to use for transforming outgoing responses. - */ + /** Sets the XSLT stylesheet to use for transforming outgoing responses. */ public void setResponseXslt(Resource responseXslt) { this.responseXslt = responseXslt; } @@ -106,6 +102,11 @@ public class PayloadTransformingInterceptor implements EndpointInterceptor, Init return true; } + /** Does nothing by default. Faults are not transformed. */ + public boolean handleFault(MessageContext messageContext, Object endpoint) throws Exception { + return true; + } + public void afterPropertiesSet() throws Exception { if (requestXslt == null && responseXslt == null) { throw new IllegalArgumentException("Setting either 'requestXslt' or 'responseXslt' is required"); diff --git a/core/src/main/java/org/springframework/ws/soap/SoapElement.java b/core/src/main/java/org/springframework/ws/soap/SoapElement.java index ace3ccb5..87cfb582 100644 --- a/core/src/main/java/org/springframework/ws/soap/SoapElement.java +++ b/core/src/main/java/org/springframework/ws/soap/SoapElement.java @@ -51,6 +51,13 @@ public interface SoapElement { */ void addAttribute(QName name, String value); + /** + * Removes the attribute with the specified name. + * + * @param name the qualified name of the attribute to remove + */ + void removeAttribute(QName name); + /** * Returns the value of the attribute with the specified qualified name. * @@ -66,5 +73,4 @@ public interface SoapElement { */ Iterator getAllAttibutes(); - } diff --git a/core/src/main/java/org/springframework/ws/soap/SoapHeader.java b/core/src/main/java/org/springframework/ws/soap/SoapHeader.java index cb37982c..e4237a16 100644 --- a/core/src/main/java/org/springframework/ws/soap/SoapHeader.java +++ b/core/src/main/java/org/springframework/ws/soap/SoapHeader.java @@ -69,13 +69,4 @@ public interface SoapHeader extends SoapElement { */ Iterator examineAllHeaderElements() throws SoapHeaderException; - /** - * Returns an Iterator over all the SoapHeaderElements with the specified header. - * - * @param name the qualified name of header elements to look for - * @return an iterator over all the header elements - * @throws SoapHeaderException if the header cannot be returned - * @see SoapHeaderElement - */ - Iterator examineHeaderElements(QName name) throws SoapHeaderException; } diff --git a/core/src/main/java/org/springframework/ws/soap/SoapHeaderElement.java b/core/src/main/java/org/springframework/ws/soap/SoapHeaderElement.java index 0cc5fbf5..465b1379 100644 --- a/core/src/main/java/org/springframework/ws/soap/SoapHeaderElement.java +++ b/core/src/main/java/org/springframework/ws/soap/SoapHeaderElement.java @@ -59,9 +59,20 @@ public interface SoapHeaderElement extends SoapElement { */ void setMustUnderstand(boolean mustUnderstand) throws SoapHeaderException; - /** - * Returns a Result that allows for writing to the contents of the header element. - */ + /** Returns a Result that allows for writing to the contents of the header element. */ Result getResult() throws SoapHeaderException; + /** + * Returns the text content of this header element, if any. + * + * @return the text content of this header element + */ + String getText(); + + /** + * Sets the text content of this header element. + * + * @param content the new text content of this header element + */ + void setText(String content); } diff --git a/core/src/main/java/org/springframework/ws/soap/axiom/AxiomSoapElement.java b/core/src/main/java/org/springframework/ws/soap/axiom/AxiomSoapElement.java index ec09dfae..1333e0ce 100644 --- a/core/src/main/java/org/springframework/ws/soap/axiom/AxiomSoapElement.java +++ b/core/src/main/java/org/springframework/ws/soap/axiom/AxiomSoapElement.java @@ -79,6 +79,18 @@ class AxiomSoapElement implements SoapElement { } } + public void removeAttribute(QName name) { + try { + OMAttribute attribute = getAxiomElement().getAttribute(name); + if (attribute != null) { + getAxiomElement().removeAttribute(attribute); + } + } + catch (OMException ex) { + throw new AxiomSoapElementException(ex); + } + } + public final String getAttributeValue(QName name) { try { return getAxiomElement().getAttributeValue(name); diff --git a/core/src/main/java/org/springframework/ws/soap/axiom/AxiomSoapHeaderElement.java b/core/src/main/java/org/springframework/ws/soap/axiom/AxiomSoapHeaderElement.java index 5fd0cad9..6d4f565e 100644 --- a/core/src/main/java/org/springframework/ws/soap/axiom/AxiomSoapHeaderElement.java +++ b/core/src/main/java/org/springframework/ws/soap/axiom/AxiomSoapHeaderElement.java @@ -57,6 +57,14 @@ class AxiomSoapHeaderElement extends AxiomSoapElement implements SoapHeaderEleme } + public String getText() { + return getAxiomHeaderBlock().getText(); + } + + public void setText(String content) { + getAxiomHeaderBlock().setText(content); + } + protected SOAPHeaderBlock getAxiomHeaderBlock() { return (SOAPHeaderBlock) getAxiomElement(); } diff --git a/core/src/main/java/org/springframework/ws/soap/saaj/Saaj11Implementation.java b/core/src/main/java/org/springframework/ws/soap/saaj/Saaj11Implementation.java index 06b3c04e..eda9887d 100644 --- a/core/src/main/java/org/springframework/ws/soap/saaj/Saaj11Implementation.java +++ b/core/src/main/java/org/springframework/ws/soap/saaj/Saaj11Implementation.java @@ -81,11 +81,24 @@ public class Saaj11Implementation implements SaajImplementation { return new SAXResult(new SaajContentHandler(element)); } + public String getText(SOAPElement element) { + return element.getValue(); + } + + public void setText(SOAPElement element, String content) throws SOAPException { + element.addTextNode(content); + } + public void addAttribute(SOAPElement element, QName name, String value) throws SOAPException { Name attributeName = SaajUtils.toName(name, element); element.addAttribute(attributeName, value); } + public void removeAttribute(SOAPElement element, QName name) throws SOAPException { + Name attributeName = SaajUtils.toName(name, element); + element.removeAttribute(attributeName); + } + public String getAttributeValue(SOAPElement element, QName name) throws SOAPException { Name attributeName = SaajUtils.toName(name, element); return element.getAttributeValue(attributeName); @@ -100,11 +113,6 @@ public class Saaj11Implementation implements SaajImplementation { return results.iterator(); } - public Iterator getChildElements(SOAPElement element, QName name) throws SOAPException { - Name childName = SaajUtils.toName(name, element); - return element.getChildElements(childName); - } - public QName getFaultCode(SOAPFault fault) { String code = fault.getFaultCode(); int idx = code.indexOf(':'); diff --git a/core/src/main/java/org/springframework/ws/soap/saaj/Saaj12Implementation.java b/core/src/main/java/org/springframework/ws/soap/saaj/Saaj12Implementation.java index a1f09f3e..f38862cb 100644 --- a/core/src/main/java/org/springframework/ws/soap/saaj/Saaj12Implementation.java +++ b/core/src/main/java/org/springframework/ws/soap/saaj/Saaj12Implementation.java @@ -109,6 +109,11 @@ public class Saaj12Implementation implements SaajImplementation { element.addAttribute(attributeName, value); } + public void removeAttribute(SOAPElement element, QName name) throws SOAPException { + Name attributeName = SaajUtils.toName(name, element); + element.removeAttribute(attributeName); + } + public String getAttributeValue(SOAPElement element, QName name) throws SOAPException { Name attributeName = SaajUtils.toName(name, element); return element.getAttributeValue(attributeName); @@ -123,9 +128,12 @@ public class Saaj12Implementation implements SaajImplementation { return results.iterator(); } - public Iterator getChildElements(SOAPElement element, QName name) throws SOAPException { - Name childName = SaajUtils.toName(name, element); - return element.getChildElements(childName); + public String getText(SOAPElement element) { + return element.getValue(); + } + + public void setText(SOAPElement element, String content) { + element.setValue(content); } public SOAPEnvelope getEnvelope(SOAPMessage message) throws SOAPException { diff --git a/core/src/main/java/org/springframework/ws/soap/saaj/Saaj13Implementation.java b/core/src/main/java/org/springframework/ws/soap/saaj/Saaj13Implementation.java index 07f08c71..48f83156 100644 --- a/core/src/main/java/org/springframework/ws/soap/saaj/Saaj13Implementation.java +++ b/core/src/main/java/org/springframework/ws/soap/saaj/Saaj13Implementation.java @@ -139,10 +139,22 @@ public class Saaj13Implementation implements SaajImplementation { return new DOMResult(element); } + public String getText(SOAPElement element) { + return element.getValue(); + } + + public void setText(SOAPElement element, String content) { + element.setValue(content); + } + public void addAttribute(SOAPElement element, QName name, String value) throws SOAPException { element.addAttribute(name, value); } + public void removeAttribute(SOAPElement element, QName name) throws SOAPException { + element.removeAttribute(name); + } + public String getAttributeValue(SOAPElement element, QName name) throws SOAPException { return element.getAttributeValue(name); } @@ -151,10 +163,6 @@ public class Saaj13Implementation implements SaajImplementation { return element.getAllAttributesAsQNames(); } - public Iterator getChildElements(SOAPElement element, QName name) throws SOAPException { - return element.getChildElements(name); - } - public SOAPEnvelope getEnvelope(SOAPMessage message) throws SOAPException { return message.getSOAPPart().getEnvelope(); } diff --git a/core/src/main/java/org/springframework/ws/soap/saaj/SaajImplementation.java b/core/src/main/java/org/springframework/ws/soap/saaj/SaajImplementation.java index e13f3975..db1a7a26 100644 --- a/core/src/main/java/org/springframework/ws/soap/saaj/SaajImplementation.java +++ b/core/src/main/java/org/springframework/ws/soap/saaj/SaajImplementation.java @@ -53,18 +53,24 @@ public interface SaajImplementation { /** Returns the writable Result of the given element. */ Result getResult(SOAPElement element); + /** Returns the text of the given element */ + String getText(SOAPElement element); + + /** Returns the text of the given element */ + void setText(SOAPElement element, String content) throws SOAPException; + /** Adds an attribute to the specified element. */ void addAttribute(SOAPElement element, QName name, String value) throws SOAPException; + /** Removes an attribute from the specified element. */ + void removeAttribute(SOAPElement element, QName name) throws SOAPException; + /** Returns the attribute value * */ String getAttributeValue(SOAPElement element, QName name) throws SOAPException; /** Returns all attributes as an iterator of QNames. * */ Iterator getAllAttibutes(SOAPElement element); - /** Returns an iterator over the child elements with the given name. */ - Iterator getChildElements(SOAPElement element, QName name) throws SOAPException; - /** Returns the envelope of the given message. */ SOAPEnvelope getEnvelope(SOAPMessage message) throws SOAPException; diff --git a/core/src/main/java/org/springframework/ws/soap/saaj/SaajSoapElement.java b/core/src/main/java/org/springframework/ws/soap/saaj/SaajSoapElement.java index 1f67bdbd..2aefd1dc 100644 --- a/core/src/main/java/org/springframework/ws/soap/saaj/SaajSoapElement.java +++ b/core/src/main/java/org/springframework/ws/soap/saaj/SaajSoapElement.java @@ -57,6 +57,15 @@ class SaajSoapElement implements SoapElement { } } + public void removeAttribute(QName name) { + try { + getImplementation().removeAttribute(element, name); + } + catch (SOAPException ex) { + throw new SaajSoapElementException(ex); + } + } + public String getAttributeValue(QName name) { try { return getImplementation().getAttributeValue(element, name); diff --git a/core/src/main/java/org/springframework/ws/soap/saaj/SaajSoapHeader.java b/core/src/main/java/org/springframework/ws/soap/saaj/SaajSoapHeader.java index 77c31e9a..620358f1 100644 --- a/core/src/main/java/org/springframework/ws/soap/saaj/SaajSoapHeader.java +++ b/core/src/main/java/org/springframework/ws/soap/saaj/SaajSoapHeader.java @@ -44,16 +44,6 @@ class SaajSoapHeader extends SaajSoapElement implements SoapHeader { return new SaajSoapHeaderElementIterator(iterator); } - public Iterator examineHeaderElements(QName name) throws SoapHeaderException { - try { - Iterator iterator = getImplementation().getChildElements(getSaajHeader(), name); - return new SaajSoapHeaderElementIterator(iterator); - } - catch (SOAPException ex) { - throw new SaajSoapHeaderException(ex); - } - } - public Iterator examineMustUnderstandHeaderElements(String actorOrRole) throws SoapHeaderException { Iterator iterator = getImplementation().examineMustUnderstandHeaderElements(getSaajHeader(), actorOrRole); return new SaajSoapHeaderElementIterator(iterator); diff --git a/core/src/main/java/org/springframework/ws/soap/saaj/SaajSoapHeaderElement.java b/core/src/main/java/org/springframework/ws/soap/saaj/SaajSoapHeaderElement.java index 6e9760d4..bf8fc973 100644 --- a/core/src/main/java/org/springframework/ws/soap/saaj/SaajSoapHeaderElement.java +++ b/core/src/main/java/org/springframework/ws/soap/saaj/SaajSoapHeaderElement.java @@ -16,6 +16,7 @@ package org.springframework.ws.soap.saaj; +import javax.xml.soap.SOAPException; import javax.xml.soap.SOAPHeaderElement; import javax.xml.transform.Result; @@ -54,6 +55,19 @@ class SaajSoapHeaderElement extends SaajSoapElement implements SoapHeaderElement getImplementation().setMustUnderstand(getSaajHeaderElement(), mustUnderstand); } + public String getText() { + return getImplementation().getText(getSaajHeaderElement()); + } + + public void setText(String content) { + try { + getImplementation().setText(getSaajHeaderElement(), content); + } + catch (SOAPException ex) { + throw new SaajSoapHeaderException(ex); + } + } + protected SOAPHeaderElement getSaajHeaderElement() { return (SOAPHeaderElement) getSaajElement(); } diff --git a/core/src/main/java/org/springframework/ws/soap/SoapEndpointInterceptor.java b/core/src/main/java/org/springframework/ws/soap/server/SoapEndpointInterceptor.java similarity index 64% rename from core/src/main/java/org/springframework/ws/soap/SoapEndpointInterceptor.java rename to core/src/main/java/org/springframework/ws/soap/server/SoapEndpointInterceptor.java index ebc0b892..4491516f 100644 --- a/core/src/main/java/org/springframework/ws/soap/SoapEndpointInterceptor.java +++ b/core/src/main/java/org/springframework/ws/soap/server/SoapEndpointInterceptor.java @@ -14,10 +14,10 @@ * limitations under the License. */ -package org.springframework.ws.soap; +package org.springframework.ws.soap.server; -import org.springframework.ws.context.MessageContext; import org.springframework.ws.server.EndpointInterceptor; +import org.springframework.ws.soap.SoapHeaderElement; /** * SOAP-specific extension of the EndpointInterceptor interface. Allows for handling of SOAP faults, which @@ -27,17 +27,6 @@ import org.springframework.ws.server.EndpointInterceptor; */ public interface SoapEndpointInterceptor extends EndpointInterceptor { - /** - * Processes the fault. Both request and response of the message context should be filled; the body of the response - * message contains the fault. - * - * @param messageContext contains both request and response messages, the response should contains a SOAP Fault - * @param endpoint chosen endpoint to invoke - * @return true to continue processing of the reponse interceptor chain; false to indicate - * blocking of the response handler chain. - */ - boolean handleFault(MessageContext messageContext, Object endpoint) throws Exception; - /** * Given a SoapHeaderElement, return whether or not this SoapEndpointInterceptor * understands it. diff --git a/core/src/main/java/org/springframework/ws/soap/SoapEndpointMapping.java b/core/src/main/java/org/springframework/ws/soap/server/SoapEndpointMapping.java similarity index 81% rename from core/src/main/java/org/springframework/ws/soap/SoapEndpointMapping.java rename to core/src/main/java/org/springframework/ws/soap/server/SoapEndpointMapping.java index a6b6f6fc..0d4ead82 100644 --- a/core/src/main/java/org/springframework/ws/soap/SoapEndpointMapping.java +++ b/core/src/main/java/org/springframework/ws/soap/server/SoapEndpointMapping.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.ws.soap; +package org.springframework.ws.soap.server; import org.springframework.ws.server.EndpointMapping; @@ -30,14 +30,10 @@ import org.springframework.ws.server.EndpointMapping; */ public interface SoapEndpointMapping extends EndpointMapping { - /** - * Sets a single SOAP actor/actorOrRole to apply to all endpoints mapped by the delegate endpoint mapping. - */ + /** Sets a single SOAP actor/actorOrRole to apply to all endpoints mapped by the delegate endpoint mapping. */ void setActorOrRole(String actorOrRole); - /** - * Sets the array of SOAP actors/actorsOrRoles to apply to all endpoints mapped by the delegate endpoint mapping. - */ + /** Sets the array of SOAP actors/actorsOrRoles to apply to all endpoints mapped by the delegate endpoint mapping. */ void setActorsOrRoles(String[] actorsOrRoles); } diff --git a/core/src/main/java/org/springframework/ws/soap/server/SoapMessageDispatcher.java b/core/src/main/java/org/springframework/ws/soap/server/SoapMessageDispatcher.java index 2dd18110..a21993b2 100644 --- a/core/src/main/java/org/springframework/ws/soap/server/SoapMessageDispatcher.java +++ b/core/src/main/java/org/springframework/ws/soap/server/SoapMessageDispatcher.java @@ -22,14 +22,12 @@ import java.util.List; import java.util.Locale; import javax.xml.namespace.QName; -import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import org.springframework.ws.context.MessageContext; import org.springframework.ws.server.EndpointInterceptor; import org.springframework.ws.server.EndpointInvocationChain; import org.springframework.ws.server.MessageDispatcher; import org.springframework.ws.soap.SoapBody; -import org.springframework.ws.soap.SoapEndpointInterceptor; import org.springframework.ws.soap.SoapFault; import org.springframework.ws.soap.SoapHeader; import org.springframework.ws.soap.SoapHeaderElement; @@ -43,13 +41,11 @@ import org.springframework.ws.soap.soap12.Soap12Header; * * @author Arjen Poutsma * @see org.springframework.ws.soap.SoapMessage - * @see org.springframework.ws.soap.SoapEndpointInterceptor + * @see SoapEndpointInterceptor */ public class SoapMessageDispatcher extends MessageDispatcher { - /** - * Default message used when creating a SOAP MustUnderstand fault. - */ + /** Default message used when creating a SOAP MustUnderstand fault. */ public static final String DEFAULT_MUST_UNDERSTAND_FAULT = "One or more mandatory SOAP header blocks not understood"; @@ -130,7 +126,7 @@ public class SoapMessageDispatcher extends MessageDispatcher { * SoapEndpointInterceptor. If they are, returns true. If they are not, a SOAP fault is * created, and false is returned. * - * @see org.springframework.ws.soap.SoapEndpointInterceptor#understands(org.springframework.ws.soap.SoapHeaderElement) + * @see SoapEndpointInterceptor#understands(org.springframework.ws.soap.SoapHeaderElement) */ private boolean handleRequestForRole(EndpointInvocationChain mappedEndpoint, MessageContext messageContext, @@ -180,41 +176,4 @@ public class SoapMessageDispatcher extends MessageDispatcher { return false; } } - - /** - * Trigger handleResponse or handleFault on the mapped EndpointInterceptors. Will just invoke said method on all - * interceptors whose handleRequest invocation returned true, in addition to the last interceptor who - * returned false. - * - * @param mappedEndpoint the mapped EndpointInvocationChain - * @param interceptorIndex index of last interceptor that was called - * @param messageContext the message context, whose request and response are filled - * @see org.springframework.ws.server.EndpointInterceptor#handleResponse(org.springframework.ws.context.MessageContext, - *Object) - */ - protected void triggerHandleResponse(EndpointInvocationChain mappedEndpoint, - int interceptorIndex, - MessageContext messageContext) throws Exception { - if (mappedEndpoint != null && messageContext.hasResponse() && - !ObjectUtils.isEmpty(mappedEndpoint.getInterceptors())) { - boolean hasFault = false; - if (messageContext.getResponse() instanceof SoapMessage) { - SoapMessage soapResponse = (SoapMessage) messageContext.getResponse(); - hasFault = soapResponse.getSoapBody().hasFault(); - } - boolean resume = true; - for (int i = interceptorIndex; resume && i >= 0; i--) { - EndpointInterceptor interceptor = mappedEndpoint.getInterceptors()[i]; - if (hasFault) { - if (interceptor instanceof SoapEndpointInterceptor) { - SoapEndpointInterceptor soapEndpointInterceptor = (SoapEndpointInterceptor) interceptor; - resume = soapEndpointInterceptor.handleFault(messageContext, mappedEndpoint.getEndpoint()); - } - } - else { - resume = interceptor.handleResponse(messageContext, mappedEndpoint.getEndpoint()); - } - } - } - } } diff --git a/core/src/main/java/org/springframework/ws/soap/server/endpoint/interceptor/SoapEnvelopeLoggingInterceptor.java b/core/src/main/java/org/springframework/ws/soap/server/endpoint/interceptor/SoapEnvelopeLoggingInterceptor.java index 41ea567b..033d354e 100644 --- a/core/src/main/java/org/springframework/ws/soap/server/endpoint/interceptor/SoapEnvelopeLoggingInterceptor.java +++ b/core/src/main/java/org/springframework/ws/soap/server/endpoint/interceptor/SoapEnvelopeLoggingInterceptor.java @@ -21,9 +21,9 @@ import javax.xml.transform.Source; import org.springframework.ws.WebServiceMessage; import org.springframework.ws.context.MessageContext; import org.springframework.ws.server.endpoint.AbstractLoggingInterceptor; -import org.springframework.ws.soap.SoapEndpointInterceptor; import org.springframework.ws.soap.SoapHeaderElement; import org.springframework.ws.soap.SoapMessage; +import org.springframework.ws.soap.server.SoapEndpointInterceptor; /** * SOAP-specific EndpointInterceptor that logs the complete request and response envelope of @@ -39,9 +39,7 @@ public class SoapEnvelopeLoggingInterceptor extends AbstractLoggingInterceptor i private boolean logFault = true; - /** - * Indicates whether a SOAP Fault should be logged. Default is true. - */ + /** Indicates whether a SOAP Fault should be logged. Default is true. */ public void setLogFault(boolean logFault) { this.logFault = logFault; } diff --git a/core/src/main/java/org/springframework/ws/soap/server/endpoint/mapping/DelegatingSoapEndpointMapping.java b/core/src/main/java/org/springframework/ws/soap/server/endpoint/mapping/DelegatingSoapEndpointMapping.java index 60acae3b..f6c0da41 100644 --- a/core/src/main/java/org/springframework/ws/soap/server/endpoint/mapping/DelegatingSoapEndpointMapping.java +++ b/core/src/main/java/org/springframework/ws/soap/server/endpoint/mapping/DelegatingSoapEndpointMapping.java @@ -21,8 +21,8 @@ import org.springframework.util.Assert; import org.springframework.ws.context.MessageContext; import org.springframework.ws.server.EndpointInvocationChain; import org.springframework.ws.server.EndpointMapping; -import org.springframework.ws.soap.SoapEndpointMapping; import org.springframework.ws.soap.server.SoapEndpointInvocationChain; +import org.springframework.ws.soap.server.SoapEndpointMapping; /** * EndpointMapping implement that adds SOAP actors or roles to a delegate endpoint. Delegates to another @@ -46,9 +46,7 @@ public class DelegatingSoapEndpointMapping implements InitializingBean, SoapEndp private String[] actorsOrRoles; - /** - * Sets the delegate EndpointMapping to resolve the endpoint with. - */ + /** Sets the delegate EndpointMapping to resolve the endpoint with. */ public void setDelegate(EndpointMapping delegate) { this.delegate = delegate; } diff --git a/core/src/main/java/org/springframework/ws/soap/server/endpoint/mapping/SoapActionEndpointMapping.java b/core/src/main/java/org/springframework/ws/soap/server/endpoint/mapping/SoapActionEndpointMapping.java index 2bca365c..939c340a 100644 --- a/core/src/main/java/org/springframework/ws/soap/server/endpoint/mapping/SoapActionEndpointMapping.java +++ b/core/src/main/java/org/springframework/ws/soap/server/endpoint/mapping/SoapActionEndpointMapping.java @@ -22,10 +22,10 @@ import org.springframework.ws.context.MessageContext; import org.springframework.ws.server.EndpointInterceptor; import org.springframework.ws.server.EndpointInvocationChain; import org.springframework.ws.server.endpoint.mapping.AbstractMapBasedEndpointMapping; -import org.springframework.ws.soap.SoapEndpointMapping; import org.springframework.ws.soap.SoapMessage; import org.springframework.ws.soap.axiom.AxiomSoapMessageFactory; import org.springframework.ws.soap.server.SoapEndpointInvocationChain; +import org.springframework.ws.soap.server.SoapEndpointMapping; /** * Implementation of the EndpointMapping interface to map from SOAPAction headers to endpoint diff --git a/core/src/test/java/org/springframework/ws/server/MessageDispatcherTest.java b/core/src/test/java/org/springframework/ws/server/MessageDispatcherTest.java index 7af3934b..107bbf20 100644 --- a/core/src/test/java/org/springframework/ws/server/MessageDispatcherTest.java +++ b/core/src/test/java/org/springframework/ws/server/MessageDispatcherTest.java @@ -307,6 +307,47 @@ public class MessageDispatcherTest extends TestCase { factoryControl.verify(); } + public void testFaultFlow() throws Exception { + MockControl adapterControl = MockControl.createControl(EndpointAdapter.class); + EndpointAdapter adapterMock = (EndpointAdapter) adapterControl.getMock(); + dispatcher.setEndpointAdapters(Collections.singletonList(adapterMock)); + + Object endpoint = new Object(); + adapterControl.expectAndReturn(adapterMock.supports(endpoint), true); + + MockControl mappingControl = MockControl.createControl(EndpointMapping.class); + EndpointMapping mappingMock = (EndpointMapping) mappingControl.getMock(); + dispatcher.setEndpointMappings(Collections.singletonList(mappingMock)); + + MockControl interceptorControl = MockControl.createStrictControl(EndpointInterceptor.class); + EndpointInterceptor interceptorMock = (EndpointInterceptor) interceptorControl.getMock(); + + interceptorControl.expectAndReturn(interceptorMock.handleRequest(messageContext, endpoint), true); + adapterMock.invoke(messageContext, endpoint); + interceptorControl.expectAndReturn(interceptorMock.handleFault(messageContext, endpoint), true); + + EndpointInvocationChain chain = + new EndpointInvocationChain(endpoint, new EndpointInterceptor[]{interceptorMock}); + + mappingControl.expectAndReturn(mappingMock.getEndpoint(messageContext), chain); + MockWebServiceMessage response = new MockWebServiceMessage(); + response.setFault(true); + factoryControl.expectAndReturn(factoryMock.createWebServiceMessage(), response); + + mappingControl.replay(); + interceptorControl.replay(); + adapterControl.replay(); + factoryControl.replay(); + // response required for interceptor invocation + messageContext.getResponse(); + dispatcher.dispatch(messageContext); + + mappingControl.verify(); + interceptorControl.verify(); + adapterControl.verify(); + factoryControl.verify(); + } + public void testNoEndpointFound() throws Exception { dispatcher.setEndpointMappings(Collections.EMPTY_LIST); try { diff --git a/core/src/test/java/org/springframework/ws/soap/AbstractSoapHeaderTestCase.java b/core/src/test/java/org/springframework/ws/soap/AbstractSoapHeaderTestCase.java index b884ddce..f7dd1843 100644 --- a/core/src/test/java/org/springframework/ws/soap/AbstractSoapHeaderTestCase.java +++ b/core/src/test/java/org/springframework/ws/soap/AbstractSoapHeaderTestCase.java @@ -94,17 +94,6 @@ public abstract class AbstractSoapHeaderTestCase extends AbstractSoapElementTest assertHeaderElementEqual(headerElement, content); } - public void testExamineHeaderElements() throws Exception { - QName qName = new QName("http://www.springframework.org", "localName", "spring"); - soapHeader.addHeaderElement(qName); - Iterator iterator = soapHeader.examineHeaderElements(qName); - assertNotNull("No Iterator returned", iterator); - assertTrue("header element iterator has no elements", iterator.hasNext()); - SoapHeaderElement headerElement = (SoapHeaderElement) iterator.next(); - assertNotNull("No SoapHeaderElement returned", headerElement); - assertEquals("Invalid qName for element", qName, headerElement.getName()); - } - protected void assertHeaderElementEqual(SoapHeaderElement headerElement, String expected) throws Exception { StringResult result = new StringResult(); transformer.transform(headerElement.getSource(), result); diff --git a/core/src/test/java/org/springframework/ws/soap/server/SoapMessageDispatcherTest.java b/core/src/test/java/org/springframework/ws/soap/server/SoapMessageDispatcherTest.java index 459c6947..8f425263 100644 --- a/core/src/test/java/org/springframework/ws/soap/server/SoapMessageDispatcherTest.java +++ b/core/src/test/java/org/springframework/ws/soap/server/SoapMessageDispatcherTest.java @@ -29,7 +29,6 @@ import org.easymock.MockControl; import org.springframework.ws.context.DefaultMessageContext; import org.springframework.ws.context.MessageContext; import org.springframework.ws.soap.SoapBody; -import org.springframework.ws.soap.SoapEndpointInterceptor; import org.springframework.ws.soap.SoapHeader; import org.springframework.ws.soap.SoapHeaderElement; import org.springframework.ws.soap.SoapMessage; diff --git a/security/src/main/java/org/springframework/ws/soap/security/AbstractWsSecurityInterceptor.java b/security/src/main/java/org/springframework/ws/soap/security/AbstractWsSecurityInterceptor.java index 0cc62b05..c197b851 100644 --- a/security/src/main/java/org/springframework/ws/soap/security/AbstractWsSecurityInterceptor.java +++ b/security/src/main/java/org/springframework/ws/soap/security/AbstractWsSecurityInterceptor.java @@ -24,9 +24,9 @@ import org.apache.commons.logging.LogFactory; import org.springframework.util.Assert; import org.springframework.ws.context.MessageContext; import org.springframework.ws.soap.SoapBody; -import org.springframework.ws.soap.SoapEndpointInterceptor; import org.springframework.ws.soap.SoapHeaderElement; import org.springframework.ws.soap.SoapMessage; +import org.springframework.ws.soap.server.SoapEndpointInterceptor; /** * Interceptor base class for interceptors that handle WS-Security. diff --git a/src/changes/changes.xml b/src/changes/changes.xml index e9c21107..4002a8bf 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -6,6 +6,9 @@ + Pulled handleFault() from SoapEndpointInterceptor up to + EndpointInterceptor + Remove IOExceptions from WebServiceTemplate. A runtime exception hierarchy has been created in org.springframework.ws.client. diff --git a/src/site/apt/upgrading.apt b/src/site/apt/upgrading.apt index 0e14cad1..b00ff5e8 100644 --- a/src/site/apt/upgrading.apt +++ b/src/site/apt/upgrading.apt @@ -19,6 +19,16 @@ Upgrading from version 1.0-M3 to 1.0-RC1 The <<> no longer declares an IOException for each method. Rather, an runtime exception hierarchy has been created in <<>>. + +* EndpointInterceptor + + The <<>> has a <<>> method now, which gets invoked + (rather than <<>>) when <<>> is true for the response. + +* SoapMessageDispatcher + + * All of SoapMessageDispatcher's strategy interfaces (<<>>, <<>>) + have been moved from <<>> to <<>>. Upgrading from version 1.0-M2 to 1.0-M3 @@ -42,9 +52,9 @@ the project update their applications: To make a clear distinction between server-side and client-side code, we had to move the various server-side classes. - * The <<>> has been moved to the <<>> to <<>> package. + * The <<>> has been moved to the <<>> package. - * The <<>> has been moved to the EndpointInvocationChain to <<>> package. + * The <<>> has been moved to the <<>> package. * All of MessageDispatcher's strategy interfaces (<<>>, <<>>, <<>>, <<>>) have been moved from <<>> to <<>>. diff --git a/xml/src/main/java/org/springframework/xml/xpath/XPathExpressionFactory.java b/xml/src/main/java/org/springframework/xml/xpath/XPathExpressionFactory.java index 82569e0f..a66441c2 100644 --- a/xml/src/main/java/org/springframework/xml/xpath/XPathExpressionFactory.java +++ b/xml/src/main/java/org/springframework/xml/xpath/XPathExpressionFactory.java @@ -46,12 +46,12 @@ public abstract class XPathExpressionFactory { static { // Check whether JAXP 1.3, or Jaxen are available if (JaxpVersion.getJaxpVersion() >= JaxpVersion.JAXP_13) { - logger.info("JAXP 1.3 available"); + logger.debug("JAXP 1.3 available"); } try { ClassUtils.forName(JAXEN_CLASS_NAME); jaxenAvailable = true; - logger.info("Jaxen available"); + logger.debug("Jaxen available"); } catch (ClassNotFoundException ex) { jaxenAvailable = false;