diff --git a/core-tiger/pom.xml b/core-tiger/pom.xml
index 1ca513cf..76156e6e 100644
--- a/core-tiger/pom.xml
+++ b/core-tiger/pom.xml
@@ -46,6 +46,17 @@
javax.activation
activation
+
+ javax.xml.soap
+ saaj-api
+ test
+
+
+
+ com.sun.xml.messaging.saaj
+ saaj-impl
+ test
+
org.easymock
diff --git a/core-tiger/src/main/java/org/springframework/ws/soap/server/endpoint/SoapFaultAnnotationExceptionResolver.java b/core-tiger/src/main/java/org/springframework/ws/soap/server/endpoint/SoapFaultAnnotationExceptionResolver.java
new file mode 100644
index 00000000..27151a88
--- /dev/null
+++ b/core-tiger/src/main/java/org/springframework/ws/soap/server/endpoint/SoapFaultAnnotationExceptionResolver.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.ws.soap.server.endpoint;
+
+import javax.xml.namespace.QName;
+
+import org.springframework.util.StringUtils;
+import org.springframework.ws.soap.server.endpoint.annotation.FaultCode;
+import org.springframework.ws.soap.server.endpoint.annotation.SoapFault;
+
+/**
+ * Implementation of the {@link org.springframework.ws.server.EndpointExceptionResolver} interface that uses the {@link
+ * SoapFault} annotation to map exceptions to SOAP Faults.
+ *
+ * @author Arjen Poutsma
+ */
+public class SoapFaultAnnotationExceptionResolver extends AbstractSoapFaultDefinitionExceptionResolver {
+
+ protected final SoapFaultDefinition getFaultDefinition(Object endpoint, Exception ex) {
+ SoapFault faultAnnotation = ex.getClass().getAnnotation(SoapFault.class);
+ if (faultAnnotation != null) {
+ SoapFaultDefinition definition = new SoapFaultDefinition();
+ if (faultAnnotation.faultCode() != FaultCode.CUSTOM) {
+ definition.setFaultCode(faultAnnotation.faultCode().value());
+ }
+ else if (StringUtils.hasLength(faultAnnotation.customFaultCode())) {
+ definition.setFaultCode(QName.valueOf(faultAnnotation.customFaultCode()));
+ }
+ definition.setFaultStringOrReason(faultAnnotation.faultStringOrReason());
+ definition.setLocale(StringUtils.parseLocaleString(faultAnnotation.locale()));
+ return definition;
+ }
+ else {
+ return null;
+ }
+ }
+}
diff --git a/core-tiger/src/main/java/org/springframework/ws/soap/server/endpoint/annotation/FaultCode.java b/core-tiger/src/main/java/org/springframework/ws/soap/server/endpoint/annotation/FaultCode.java
new file mode 100644
index 00000000..da72f290
--- /dev/null
+++ b/core-tiger/src/main/java/org/springframework/ws/soap/server/endpoint/annotation/FaultCode.java
@@ -0,0 +1,81 @@
+/*
+* Copyright 2007 the original author or authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package org.springframework.ws.soap.server.endpoint.annotation;
+
+import javax.xml.namespace.QName;
+
+import org.springframework.ws.soap.SoapBody;
+import org.springframework.ws.soap.soap11.Soap11Body;
+
+/**
+ * Enumeration that represents the standard SOAP Fault codes for use with the JDK 1.5+ {@link SoapFault} annotation.
+ *
+ * @author Arjen Poutsma
+ */
+public enum FaultCode {
+
+ /**
+ * Constant used to indicate that a fault must be created with a custom fault code. When this value is used, the
+ * customFaultCode string property must be used on {@link SoapFault}.
+ *
+ * Note that custom Fault Codes are only supported on SOAP 1.1.
+ *
+ * @see SoapFault#customFaultCode()
+ * @see Soap11Body#addFault(javax.xml.namespace.QName,String,java.util.Locale)
+ */
+ CUSTOM(new QName("CUSTOM")),
+
+ /**
+ * Constant used to indicate that a Client fault must be created.
+ *
+ * @see SoapBody#addClientOrSenderFault(String,java.util.Locale)
+ */
+ CLIENT(new QName("CLIENT")),
+
+ /**
+ * Constant QName used to indicate that a Receiver fault must be created.
+ *
+ * @see SoapBody#addServerOrReceiverFault(String,java.util.Locale)
+ */
+ RECEIVER(new QName("RECEIVER")),
+
+ /**
+ * Constant QName used to indicate that a Sender fault must be created.
+ *
+ * @see SoapBody#addServerOrReceiverFault(String,java.util.Locale)
+ */
+ SENDER(new QName("SENDER")),
+
+ /**
+ * Constant QName used to indicate that a Server fault must be created.
+ *
+ * @see SoapBody#addClientOrSenderFault(String,java.util.Locale)
+ */
+ SERVER(new QName("SERVER"));
+
+ private final QName value;
+
+ private FaultCode(QName value) {
+ this.value = value;
+ }
+
+ public QName value() {
+ return value;
+ }
+
+
+}
diff --git a/core-tiger/src/main/java/org/springframework/ws/soap/server/endpoint/annotation/SoapFault.java b/core-tiger/src/main/java/org/springframework/ws/soap/server/endpoint/annotation/SoapFault.java
new file mode 100644
index 00000000..3c29f250
--- /dev/null
+++ b/core-tiger/src/main/java/org/springframework/ws/soap/server/endpoint/annotation/SoapFault.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.ws.soap.server.endpoint.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks an exception class with the fault elements that should be returned whenever this exception is thrown.
+ *
+ * @author Arjen Poutsma
+ * @see org.springframework.ws.soap.server.endpoint.SoapFaultAnnotationExceptionResolver
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+public @interface SoapFault {
+
+ /** The fault code. */
+ FaultCode faultCode();
+
+ /**
+ * The custom fault code, to be used if {@link #faultCode()} is set to {@link FaultCode#CUSTOM}.
+ *
+ * Note that custom Fault Codes are only supported on SOAP 1.1.
+ */
+ String customFaultCode() default "";
+
+ /** The fault string or reason text. By default, it is set to the exception message. */
+ String faultStringOrReason() default "";
+
+ /** The fault string locale. By default, it is English. */
+ String locale() default "en";
+
+
+}
diff --git a/core-tiger/src/test/java/org/springframework/ws/soap/server/endpoint/SoapFaultAnnotationExceptionResolverTest.java b/core-tiger/src/test/java/org/springframework/ws/soap/server/endpoint/SoapFaultAnnotationExceptionResolverTest.java
new file mode 100644
index 00000000..1be969ed
--- /dev/null
+++ b/core-tiger/src/test/java/org/springframework/ws/soap/server/endpoint/SoapFaultAnnotationExceptionResolverTest.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.ws.soap.server.endpoint;
+
+import java.util.Locale;
+import javax.xml.namespace.QName;
+import javax.xml.soap.MessageFactory;
+import javax.xml.soap.SOAPConstants;
+import javax.xml.soap.SOAPMessage;
+
+import junit.framework.TestCase;
+import org.springframework.ws.context.DefaultMessageContext;
+import org.springframework.ws.context.MessageContext;
+import org.springframework.ws.soap.SoapMessage;
+import org.springframework.ws.soap.SoapMessageFactory;
+import org.springframework.ws.soap.SoapVersion;
+import org.springframework.ws.soap.saaj.SaajSoapMessage;
+import org.springframework.ws.soap.saaj.SaajSoapMessageFactory;
+import org.springframework.ws.soap.server.endpoint.annotation.FaultCode;
+import org.springframework.ws.soap.server.endpoint.annotation.SoapFault;
+import org.springframework.ws.soap.soap11.Soap11Fault;
+import org.springframework.ws.soap.soap12.Soap12Fault;
+
+public class SoapFaultAnnotationExceptionResolverTest extends TestCase {
+
+ private SoapFaultAnnotationExceptionResolver resolver;
+
+ protected void setUp() throws Exception {
+ resolver = new SoapFaultAnnotationExceptionResolver();
+ }
+
+ public void testResolveExceptionClientSoap11() throws Exception {
+ MessageFactory saajFactory = MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL);
+ SoapMessageFactory factory = new SaajSoapMessageFactory(saajFactory);
+ MessageContext context = new DefaultMessageContext(factory);
+
+ boolean result = resolver.resolveException(context, null, new MyClientException());
+ assertTrue("resolveException returns false", result);
+ assertTrue("Context has no response", context.hasResponse());
+ SoapMessage response = (SoapMessage) context.getResponse();
+ assertTrue("Resonse has no fault", response.getSoapBody().hasFault());
+ Soap11Fault fault = (Soap11Fault) response.getSoapBody().getFault();
+ assertEquals("Invalid fault code on fault", SoapVersion.SOAP_11.getClientOrSenderFaultName(),
+ fault.getFaultCode());
+ assertEquals("Invalid fault string on fault", "Client error", fault.getFaultStringOrReason());
+ assertNull("Detail on fault", fault.getFaultDetail());
+ }
+
+ public void testResolveExceptionSenderSoap12() throws Exception {
+ MessageFactory saajFactory = MessageFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL);
+ SoapMessageFactory factory = new SaajSoapMessageFactory(saajFactory);
+ MessageContext context = new DefaultMessageContext(factory);
+
+ boolean result = resolver.resolveException(context, null, new MySenderException());
+ assertTrue("resolveException returns false", result);
+ assertTrue("Context has no response", context.hasResponse());
+ SoapMessage response = (SoapMessage) context.getResponse();
+ assertTrue("Resonse has no fault", response.getSoapBody().hasFault());
+ Soap12Fault fault = (Soap12Fault) response.getSoapBody().getFault();
+ assertEquals("Invalid fault code on fault", SoapVersion.SOAP_12.getClientOrSenderFaultName(),
+ fault.getFaultCode());
+ assertEquals("Invalid fault string on fault", "Sender error", fault.getFaultReasonText(Locale.ENGLISH));
+ assertNull("Detail on fault", fault.getFaultDetail());
+ }
+
+ public void testResolveExceptionServerSoap11() throws Exception {
+ MessageFactory saajFactory = MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL);
+ SoapMessageFactory factory = new SaajSoapMessageFactory(saajFactory);
+ MessageContext context = new DefaultMessageContext(factory);
+
+ boolean result = resolver.resolveException(context, null, new MyServerException());
+ assertTrue("resolveException returns false", result);
+ assertTrue("Context has no response", context.hasResponse());
+ SoapMessage response = (SoapMessage) context.getResponse();
+ assertTrue("Resonse has no fault", response.getSoapBody().hasFault());
+ Soap11Fault fault = (Soap11Fault) response.getSoapBody().getFault();
+ assertEquals("Invalid fault code on fault", SoapVersion.SOAP_11.getServerOrReceiverFaultName(),
+ fault.getFaultCode());
+ assertEquals("Invalid fault string on fault", "Server error", fault.getFaultStringOrReason());
+ assertNull("Detail on fault", fault.getFaultDetail());
+ }
+
+ public void testResolveExceptionReceiverSoap12() throws Exception {
+ MessageFactory saajFactory = MessageFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL);
+ SOAPMessage message = saajFactory.createMessage();
+ SoapMessageFactory factory = new SaajSoapMessageFactory(saajFactory);
+ MessageContext context = new DefaultMessageContext(new SaajSoapMessage(message), factory);
+
+ boolean result = resolver.resolveException(context, null, new MyReceiverException());
+ assertTrue("resolveException returns false", result);
+ assertTrue("Context has no response", context.hasResponse());
+ SoapMessage response = (SoapMessage) context.getResponse();
+ assertTrue("Resonse has no fault", response.getSoapBody().hasFault());
+ Soap12Fault fault = (Soap12Fault) response.getSoapBody().getFault();
+ assertEquals("Invalid fault code on fault", SoapVersion.SOAP_12.getServerOrReceiverFaultName(),
+ fault.getFaultCode());
+ assertEquals("Invalid fault string on fault", "Receiver error", fault.getFaultReasonText(Locale.ENGLISH));
+ assertNull("Detail on fault", fault.getFaultDetail());
+ }
+
+ public void testResolveExceptionDefault() throws Exception {
+ SoapFaultDefinition defaultFault = new SoapFaultDefinition();
+ defaultFault.setFaultCode(SoapFaultDefinition.CLIENT);
+ defaultFault.setFaultStringOrReason("faultstring");
+ resolver.setDefaultFault(defaultFault);
+ MessageFactory saajFactory = MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL);
+ SoapMessageFactory factory = new SaajSoapMessageFactory(saajFactory);
+ MessageContext context = new DefaultMessageContext(factory);
+
+ boolean result = resolver.resolveException(context, null, new NonAnnotatedException());
+ assertTrue("resolveException returns false", result);
+ assertTrue("Context has no response", context.hasResponse());
+ SoapMessage response = (SoapMessage) context.getResponse();
+ assertTrue("Resonse has no fault", response.getSoapBody().hasFault());
+ Soap11Fault fault = (Soap11Fault) response.getSoapBody().getFault();
+ assertEquals("Invalid fault code on fault", SoapVersion.SOAP_11.getClientOrSenderFaultName(),
+ fault.getFaultCode());
+ assertEquals("Invalid fault string on fault", "faultstring", fault.getFaultStringOrReason());
+ assertNull("Detail on fault", fault.getFaultDetail());
+ }
+
+ public void testResolveExceptionCustom() throws Exception {
+ MessageFactory saajFactory = MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL);
+ SoapMessageFactory factory = new SaajSoapMessageFactory(saajFactory);
+ MessageContext context = new DefaultMessageContext(factory);
+
+ boolean result = resolver.resolveException(context, null, new MyCustomException());
+ assertTrue("resolveException returns false", result);
+ assertTrue("Context has no response", context.hasResponse());
+ SoapMessage response = (SoapMessage) context.getResponse();
+ assertTrue("Resonse has no fault", response.getSoapBody().hasFault());
+ Soap11Fault fault = (Soap11Fault) response.getSoapBody().getFault();
+ assertEquals("Invalid fault code on fault", new QName("http://springframework.org/spring-ws", "Fault"),
+ fault.getFaultCode());
+ assertEquals("Invalid fault string on fault", "MyCustomException thrown", fault.getFaultStringOrReason());
+ assertEquals("Invalid fault locale on fault", new Locale("nl"), fault.getFaultStringLocale());
+ }
+
+ public void testResolveExceptionInheritance() throws Exception {
+ MessageFactory saajFactory = MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL);
+ SoapMessageFactory factory = new SaajSoapMessageFactory(saajFactory);
+ MessageContext context = new DefaultMessageContext(factory);
+
+ boolean result = resolver.resolveException(context, null, new MySubClientException());
+ assertTrue("resolveException returns false", result);
+ assertTrue("Context has no response", context.hasResponse());
+ SoapMessage response = (SoapMessage) context.getResponse();
+ assertTrue("Resonse has no fault", response.getSoapBody().hasFault());
+ Soap11Fault fault = (Soap11Fault) response.getSoapBody().getFault();
+ assertEquals("Invalid fault code on fault", SoapVersion.SOAP_11.getClientOrSenderFaultName(),
+ fault.getFaultCode());
+ assertEquals("Invalid fault string on fault", "Client error", fault.getFaultStringOrReason());
+ assertNull("Detail on fault", fault.getFaultDetail());
+ }
+
+ public void testResolveExceptionExceptionMessage() throws Exception {
+ MessageFactory saajFactory = MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL);
+ SoapMessageFactory factory = new SaajSoapMessageFactory(saajFactory);
+ MessageContext context = new DefaultMessageContext(factory);
+
+ boolean result = resolver.resolveException(context, null, new NoStringOrReasonException("Exception message"));
+ assertTrue("resolveException returns false", result);
+ assertTrue("Context has no response", context.hasResponse());
+ SoapMessage response = (SoapMessage) context.getResponse();
+ assertTrue("Resonse has no fault", response.getSoapBody().hasFault());
+ Soap11Fault fault = (Soap11Fault) response.getSoapBody().getFault();
+ assertEquals("Invalid fault code on fault", SoapVersion.SOAP_11.getClientOrSenderFaultName(),
+ fault.getFaultCode());
+ assertEquals("Invalid fault string on fault", "Exception message", fault.getFaultStringOrReason());
+ assertNull("Detail on fault", fault.getFaultDetail());
+ }
+
+ @SoapFault(faultCode = FaultCode.CLIENT, faultStringOrReason = "Client error")
+ public class MyClientException extends Exception {
+
+ }
+
+ public class MySubClientException extends MyClientException {
+
+ }
+
+ @SoapFault(faultCode = FaultCode.CLIENT)
+ public class NoStringOrReasonException extends Exception {
+
+ public NoStringOrReasonException(String message) {
+ super(message);
+ }
+ }
+
+ @SoapFault(faultCode = FaultCode.SENDER, faultStringOrReason = "Sender error")
+ public class MySenderException extends Exception {
+
+ }
+
+ @SoapFault(faultCode = FaultCode.SERVER, faultStringOrReason = "Server error")
+ public class MyServerException extends Exception {
+
+ }
+
+ @SoapFault(faultCode = FaultCode.RECEIVER, faultStringOrReason = "Receiver error")
+ public class MyReceiverException extends Exception {
+
+ }
+
+ @SoapFault(faultCode = FaultCode.CUSTOM, customFaultCode = "{http://springframework.org/spring-ws}Fault",
+ faultStringOrReason = "MyCustomException thrown", locale = "nl")
+ public class MyCustomException extends Exception {
+
+ }
+
+ public class NonAnnotatedException extends Exception {
+
+ }
+
+
+}
\ No newline at end of file
diff --git a/core/src/main/java/org/springframework/ws/server/endpoint/AbstractEndpointExceptionResolver.java b/core/src/main/java/org/springframework/ws/server/endpoint/AbstractEndpointExceptionResolver.java
index 4c7c95ef..f1909904 100644
--- a/core/src/main/java/org/springframework/ws/server/endpoint/AbstractEndpointExceptionResolver.java
+++ b/core/src/main/java/org/springframework/ws/server/endpoint/AbstractEndpointExceptionResolver.java
@@ -64,7 +64,7 @@ public abstract class AbstractEndpointExceptionResolver implements EndpointExcep
* Template method for resolving exceptions. Gets called after resolveException.
*
* @param messageContext current message context
- * @param endpoint the executed endpoint, or null if none chosen at the time of the exception
+ * @param endpoint the executed endpoint, or null if none chosen at the time of the exception
* @param ex the exception that got thrown during endpoint execution
* @return true if resolved; false otherwise
* @see #resolveException(org.springframework.ws.context.MessageContext,Object,Exception)
diff --git a/core/src/main/java/org/springframework/ws/soap/server/endpoint/AbstractSoapFaultDefinitionExceptionResolver.java b/core/src/main/java/org/springframework/ws/soap/server/endpoint/AbstractSoapFaultDefinitionExceptionResolver.java
new file mode 100644
index 00000000..1f821a07
--- /dev/null
+++ b/core/src/main/java/org/springframework/ws/soap/server/endpoint/AbstractSoapFaultDefinitionExceptionResolver.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.ws.soap.server.endpoint;
+
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+import org.springframework.ws.context.MessageContext;
+import org.springframework.ws.server.EndpointExceptionResolver;
+import org.springframework.ws.server.endpoint.AbstractEndpointExceptionResolver;
+import org.springframework.ws.soap.SoapBody;
+import org.springframework.ws.soap.SoapFault;
+import org.springframework.ws.soap.SoapMessage;
+import org.springframework.ws.soap.soap11.Soap11Body;
+
+/**
+ * Abstract base class for SOAP-based {@link EndpointExceptionResolver} implementations that depend on {@link
+ * SoapFaultDefinition}. Provides a default endpoint property, and a template method that provides the definition for a
+ * given exception.
+ *
+ * @author Arjen Poutsma
+ * @see #setDefaultFault(SoapFaultDefinition)
+ * @see #getFaultDefinition(Object,Exception)
+ */
+public abstract class AbstractSoapFaultDefinitionExceptionResolver extends AbstractEndpointExceptionResolver {
+
+ private SoapFaultDefinition defaultFault;
+
+ /** Set the default fault. This fault will be returned if no specific mapping was found. */
+ public void setDefaultFault(SoapFaultDefinition defaultFault) {
+ this.defaultFault = defaultFault;
+ }
+
+ /**
+ * Template method that returns the {@link SoapFaultDefinition} for the given exception.
+ *
+ * @param endpoint the executed endpoint, or null if none chosen at the time of the exception
+ * @param ex the exception to be handled
+ * @return the definition mapped to the exception, or null if none is found.
+ */
+ protected abstract SoapFaultDefinition getFaultDefinition(Object endpoint, Exception ex);
+
+ protected final boolean resolveExceptionInternal(MessageContext messageContext, Object endpoint, Exception ex) {
+ Assert.isTrue(messageContext.getResponse() instanceof SoapMessage,
+ "AbstractSoapFaultDefinitionExceptionResolver requires a SoapMessage");
+
+ SoapFaultDefinition definition = getFaultDefinition(endpoint, ex);
+ if (definition == null) {
+ definition = defaultFault;
+ }
+ if (definition == null) {
+ return false;
+ }
+ if (!StringUtils.hasLength(definition.getFaultStringOrReason())) {
+ definition.setFaultStringOrReason(ex.getMessage());
+ }
+ SoapBody soapBody = ((SoapMessage) messageContext.getResponse()).getSoapBody();
+ SoapFault fault = null;
+
+ if (SoapFaultDefinition.SERVER.equals(definition.getFaultCode()) ||
+ SoapFaultDefinition.RECEIVER.equals(definition.getFaultCode())) {
+ fault = soapBody.addServerOrReceiverFault(definition.getFaultStringOrReason(), definition.getLocale());
+ }
+ else if (SoapFaultDefinition.CLIENT.equals(definition.getFaultCode()) ||
+ SoapFaultDefinition.SENDER.equals(definition.getFaultCode())) {
+ fault = soapBody.addClientOrSenderFault(definition.getFaultStringOrReason(), definition.getLocale());
+ }
+ else {
+ // custom code, only supported for SOAP 1.1
+ if (soapBody instanceof Soap11Body) {
+ Soap11Body soap11Body = (Soap11Body) soapBody;
+ fault = soap11Body.addFault(definition.getFaultCode(), definition.getFaultStringOrReason(),
+ definition.getLocale());
+ }
+ else {
+ logger.warn("SOAP 1.2 does not allow custom FaultCodes, only SENDER or RECEIVER.");
+ }
+ }
+ if (fault != null) {
+ customizeFault(endpoint, ex, fault);
+ }
+ return true;
+ }
+
+ /**
+ * Customize the {@link SoapFault} created by this resolver. Called for each created fault
+ *
+ * The default implementation is empty. Can be overridden in subclasses to customize the properties of the fault,
+ * such as adding details, etc.
+ *
+ * @param endpoint the executed endpoint, or null if none chosen at the time of the exception
+ * @param ex the exception to be handled
+ * @param fault the created fault
+ */
+ protected void customizeFault(Object endpoint, Exception ex, SoapFault fault) {
+ }
+
+}
diff --git a/core/src/main/java/org/springframework/ws/soap/server/endpoint/SoapFaultDefinition.java b/core/src/main/java/org/springframework/ws/soap/server/endpoint/SoapFaultDefinition.java
index eee5a3c2..12732908 100644
--- a/core/src/main/java/org/springframework/ws/soap/server/endpoint/SoapFaultDefinition.java
+++ b/core/src/main/java/org/springframework/ws/soap/server/endpoint/SoapFaultDefinition.java
@@ -63,30 +63,22 @@ public class SoapFaultDefinition {
private Locale locale = Locale.ENGLISH;
- /**
- * Returns the fault code.
- */
+ /** Returns the fault code. */
public QName getFaultCode() {
return faultCode;
}
- /**
- * Sets the fault code.
- */
+ /** Sets the fault code. */
public void setFaultCode(QName faultCode) {
this.faultCode = faultCode;
}
- /**
- * Returns the fault string or reason text.
- */
+ /** Returns the fault string or reason text. By default, it is set to the exception message. */
public String getFaultStringOrReason() {
return faultStringOrReason;
}
- /**
- * Sets the fault string or reason text.
- */
+ /** Sets the fault string or reason text. By default, it is set to the exception message. */
public void setFaultStringOrReason(String faultStringOrReason) {
this.faultStringOrReason = faultStringOrReason;
}
diff --git a/core/src/main/java/org/springframework/ws/soap/server/endpoint/SoapFaultDefinitionEditor.java b/core/src/main/java/org/springframework/ws/soap/server/endpoint/SoapFaultDefinitionEditor.java
index fe916e05..932a506a 100644
--- a/core/src/main/java/org/springframework/ws/soap/server/endpoint/SoapFaultDefinitionEditor.java
+++ b/core/src/main/java/org/springframework/ws/soap/server/endpoint/SoapFaultDefinitionEditor.java
@@ -30,11 +30,12 @@ import org.springframework.xml.namespace.QNameEditor;
* faultCode,faultString,locale
*
* where faultCode is the string representation of a QName, faultStringOrReason
- * is the fault string, and locale is the optional string representations for the
- * faultStringOrReasonlanguage. By default, the language is set to English.
+ * is the optional fault string, and locale is the optional string representations for the
+ * faultStringOrReasonlanguage. By default, the language is set to English, and the fault string set to the
+ * exception message.
*
* Instead of supplying a custom fault code, you can use the constants SERVER or RECEIVER
- * indicate a Server/Receiver fault, or or CLIENT or SENDER
+ * indicate a Server/Receiver fault, or CLIENT or SENDER
* toClient/Sender fault respectivaly.
*
* For example:
@@ -54,7 +55,9 @@ import org.springframework.xml.namespace.QNameEditor;
* @see javax.xml.namespace.QName#toString()
* @see org.springframework.xml.namespace.QNameEditor
* @see SoapFaultDefinition#RECEIVER
+ * @see SoapFaultDefinition#SERVER
* @see SoapFaultDefinition#SENDER
+ * @see SoapFaultDefinition#CLIENT
* @see org.springframework.ws.soap.SoapFault#getFaultCode()
*/
public class SoapFaultDefinitionEditor extends PropertyEditorSupport {
@@ -71,19 +74,21 @@ public class SoapFaultDefinitionEditor extends PropertyEditorSupport {
}
else {
String[] tokens = StringUtils.commaDelimitedListToStringArray(text);
- if (tokens.length < 2) {
+ if (tokens.length < FAULT_STRING_INDEX) {
throw new IllegalArgumentException("Invalid amount of comma delimited values in [" + text +
- "]: SoapFaultDefinitionEditor requires at least 2");
+ "]: SoapFaultDefinitionEditor requires at least 1");
}
SoapFaultDefinition definition = new SoapFaultDefinition();
QNameEditor qNameEditor = new QNameEditor();
qNameEditor.setAsText(tokens[FAULT_CODE_INDEX].trim());
definition.setFaultCode((QName) qNameEditor.getValue());
- definition.setFaultStringOrReason(tokens[FAULT_STRING_INDEX].trim());
- if (tokens.length > 2) {
- LocaleEditor localeEditor = new LocaleEditor();
- localeEditor.setAsText(tokens[FAULT_STRING_LOCALE_INDEX].trim());
- definition.setLocale((Locale) localeEditor.getValue());
+ if (tokens.length > 1) {
+ definition.setFaultStringOrReason(tokens[FAULT_STRING_INDEX].trim());
+ if (tokens.length > 2) {
+ LocaleEditor localeEditor = new LocaleEditor();
+ localeEditor.setAsText(tokens[FAULT_STRING_LOCALE_INDEX].trim());
+ definition.setLocale((Locale) localeEditor.getValue());
+ }
}
setValue(definition);
}
diff --git a/core/src/main/java/org/springframework/ws/soap/server/endpoint/SoapFaultMappingExceptionResolver.java b/core/src/main/java/org/springframework/ws/soap/server/endpoint/SoapFaultMappingExceptionResolver.java
index 1264ce18..f827cf68 100644
--- a/core/src/main/java/org/springframework/ws/soap/server/endpoint/SoapFaultMappingExceptionResolver.java
+++ b/core/src/main/java/org/springframework/ws/soap/server/endpoint/SoapFaultMappingExceptionResolver.java
@@ -19,12 +19,7 @@ package org.springframework.ws.soap.server.endpoint;
import java.util.Enumeration;
import java.util.Properties;
-import org.springframework.util.Assert;
-import org.springframework.ws.context.MessageContext;
-import org.springframework.ws.server.endpoint.AbstractEndpointExceptionResolver;
-import org.springframework.ws.soap.SoapBody;
-import org.springframework.ws.soap.SoapMessage;
-import org.springframework.ws.soap.soap11.Soap11Body;
+import org.springframework.util.CollectionUtils;
/**
* Exception resolver that allows for mapping exception class names to SOAP Faults. The mappings are set using the
@@ -32,12 +27,10 @@ import org.springframework.ws.soap.soap11.Soap11Body;
*
* @author Arjen Poutsma
*/
-public class SoapFaultMappingExceptionResolver extends AbstractEndpointExceptionResolver {
+public class SoapFaultMappingExceptionResolver extends AbstractSoapFaultDefinitionExceptionResolver {
private Properties exceptionMappings;
- private SoapFaultDefinition defaultFault;
-
/**
* Set the mappings between exception class names and SOAP Faults. The exception class name can be a substring, with
* no wildcard support at present.
@@ -45,60 +38,19 @@ public class SoapFaultMappingExceptionResolver extends AbstractEndpointException
* The values of the given properties object should use the format described in
* SoapFaultDefinitionEditor.
*
- * Follows the same matching algorithm as RuleBasedTransactionAttribute and
- * RollbackRuleAttribute.
+ * Follows the same matching algorithm as SimpleMappingExceptionResolver.
*
* @param mappings exception patterns (can also be fully qualified class names) as keys, fault definition texts as
* values
* @see SoapFaultDefinitionEditor
* @see org.springframework.web.servlet.handler.SimpleMappingExceptionResolver
- * @see org.springframework.transaction.interceptor.RuleBasedTransactionAttribute
- * @see org.springframework.transaction.interceptor.RollbackRuleAttribute
*/
public void setExceptionMappings(Properties mappings) {
exceptionMappings = mappings;
}
- /**
- * Set the default fault. This fault will be returned if no specific mapping was found.
- */
- public void setDefaultFault(SoapFaultDefinition defaultFault) {
- this.defaultFault = defaultFault;
- }
-
- protected boolean resolveExceptionInternal(MessageContext messageContext, Object endpoint, Exception ex) {
- Assert.isTrue(messageContext.getResponse() instanceof SoapMessage,
- "SimpleSoapExceptionResolver requires a SoapMessage");
-
- SoapFaultDefinition definition = getFaultDefinition(ex);
- if (definition == null) {
- return false;
- }
- SoapMessage soapResponse = (SoapMessage) messageContext.getResponse();
- SoapBody soapBody = soapResponse.getSoapBody();
-
- if (SoapFaultDefinition.SERVER.equals(definition.getFaultCode()) ||
- SoapFaultDefinition.RECEIVER.equals(definition.getFaultCode())) {
- soapBody.addServerOrReceiverFault(definition.getFaultStringOrReason(), definition.getLocale());
- }
- else if (SoapFaultDefinition.CLIENT.equals(definition.getFaultCode()) ||
- SoapFaultDefinition.SENDER.equals(definition.getFaultCode())) {
- soapBody.addClientOrSenderFault(definition.getFaultStringOrReason(), definition.getLocale());
- }
- else {
- // custom code, only supported for SOAP 1.1
- if (soapBody instanceof Soap11Body) {
- Soap11Body soap11Body = (Soap11Body) soapBody;
- soap11Body.addFault(definition.getFaultCode(), definition.getFaultStringOrReason(),
- definition.getLocale());
- }
- }
- return true;
- }
-
- private SoapFaultDefinition getFaultDefinition(Exception ex) {
- SoapFaultDefinition definition = null;
- if (exceptionMappings != null) {
+ protected SoapFaultDefinition getFaultDefinition(Object endpoint, Exception ex) {
+ if (!CollectionUtils.isEmpty(exceptionMappings)) {
String definitionText = null;
int deepest = Integer.MAX_VALUE;
for (Enumeration names = exceptionMappings.propertyNames(); names.hasMoreElements();) {
@@ -112,14 +64,10 @@ public class SoapFaultMappingExceptionResolver extends AbstractEndpointException
if (definitionText != null) {
SoapFaultDefinitionEditor editor = new SoapFaultDefinitionEditor();
editor.setAsText(definitionText);
- definition = (SoapFaultDefinition) editor.getValue();
+ return (SoapFaultDefinition) editor.getValue();
}
}
- if (definition != null || defaultFault == null) {
- return definition;
- }
- definition = defaultFault;
- return definition;
+ return null;
}
/**
@@ -130,7 +78,7 @@ public class SoapFaultMappingExceptionResolver extends AbstractEndpointException
*
* @see org.springframework.web.servlet.handler.SimpleMappingExceptionResolver
*/
- public int getDepth(String exceptionMapping, Exception ex) {
+ protected int getDepth(String exceptionMapping, Exception ex) {
return getDepth(exceptionMapping, ex.getClass(), 0);
}
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 4002a8bf..4f62a055 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -6,8 +6,10 @@
- Pulled handleFault() from SoapEndpointInterceptor up to
- EndpointInterceptor
+ Allow SoapFaultMappingExceptionResolver to use strategy to
+ obtain SoapFaultDefinition
+
+ Pulled handleFault() from SoapEndpointInterceptor up to EndpointInterceptor
Remove IOExceptions from WebServiceTemplate. A runtime
exception hierarchy has been created in org.springframework.ws.client.