Chapter 4. Exception/SOAP Fault Handling

In this chapter, we will cover:

  • Handling server-side exceptions by returning an exception's message as a SOAP fault string

  • Mapping exception class names to SOAP faults

  • Annotating exception classes with the @SOAPFault

  • Writing your own exception resolvers in Spring-WS

Introduction

The server-side exceptions generated while processing a Web-Service are transmitted as SOAP faults. The SOAP <Fault> element is used to carry error and status information within a SOAP message.

The following code represents a general structure of the SOAP fault element in a SOAP message:

<SOAP-ENV:Fault>
<faultcode xsi:type="xsd:string">SOAFP-ENV:Client</faultcode>
<faultstring xsi:type="xsd:string">
A human readable summary of the fault
</faultstring>
<detail xsi:type="xsd:string">
Application specific error information related to the Body element
</detail>
</SOAP-ENV:Fault>

If a Fault element is present, it must appear as a child element of the Body element. A Fault element can only appear once in a SOAP message.

Spring Web-Services offer smart mechanisms to handle SOAP faults with its easy-to-use API. The exceptions that are thrown when handling the request get picked up by MessageDispatcher and get delegated to any of the endpoint exception resolvers that are declared in the application context (XML or annotation). This exception resolver-based handling mechanism allows the developer to define custom behaviors (such as returning a customized SOAP fault) when a particular exception gets thrown.

This chapter starts with recipes for easy exception-handling mechanisms and then moves on to slightly complex scenarios.

org.springframework.ws.server.EndpointExceptionResolver is the primary specification/contract for server-side exception handling in Spring-WS. org.springframework.ws.soap.server.endpoint.SimpleSoapExceptionResolver is the default implementation of EndpointExceptionResolver, available in the Spring-WS framework. If not explicitly handled by the developer, MessageDispatcher handles the server-side exceptions with SimpleSoapExceptionResolver.

The recipes in this chapter demonstrate different usages of org.springframework.ws.server.EndpointExceptionResolver and its implementations, including SimpleSoapExceptionResolver.

For demonstration purposes, the simplest recipe for building a Spring-WS is Simplifying the creation of a WebService using MessageDispatcherServlet.

Handling server-side exceptions by returning the exception's message as a SOAP fault string

Spring-WS framework automatically converts the description of application-level exception thrown in server-side into SOAP fault messages and includes it within response message and sends it back to the client. This recipe demonstrates catching the exception and setting a meaningful message to be sent back as a SOAP fault string in the response.

Getting ready

In this recipe, the project's name is LiveRestaurant_R-4.1 (for server-side Web-Service) and has the following Maven dependencies:

  • spring-ws-core-2.0.1.RELEASE.jar

  • log4j-1.2.9.jar

The following are the Maven dependencies for LiveRestaurant_R-4.1-Client (for the client-side Web-Service):

  • spring-ws-core-2.0.1.RELEASE.jar

  • spring-test-3.0.5.RELEASE.jar

  • log4j-1.2.9.jar

How to do it...

This recipe uses projects from Setting up an endpoint by annotating the payload-root, discussed in Chapter 1, Building SOAP Web-Services. The following steps describe how to modify the endpoint:

  1. Modify the endpoint to throw an exception when an application/system error occurs.

  2. Build and deploy the project in the Maven-embedded Tomcat server.

  3. Run the following command from the root of the project, in a command-line window:

    mvn clean package tomcat:run.
    
    
  4. To test, open a new command window, go to the folder LiveRestaurant_R-4.1-Client, and run the following command:

mvn clean package exec:java

  • The following is the output from the server-side console (note the SOAP-Env:Fault element generated in the message):

DEBUG [http-8080-1] (MessageDispatcher.java:167) - Received request.....
<SOAP-ENV:Fault><faultcode>SOAP-ENV:Server</faultcode>
<faultstring xml:lang="en">Reference number is not provided!</faultstring>
</SOAP-ENV:Fault>
For request
...
<tns:placeOrderRequest xmlns:tns="....">
......
</tns:placeOrderRequest>

  • The following is the output from the client-side console:

Received response ....
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:Server</faultcode>
<faultstring xml:lang="en">Reference number is not provided!</faultstring>
</SOAP-ENV:Fault>
... for request....
<tns:placeOrderRequest xmlns:tns="...">
.........
</tns:placeOrderRequest>
....
[WARNING]
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
.........
at org.codehaus.mojo.exec.ExecJavaMojo$1.run(ExecJavaMojo.java:297)
at java.lang.Thread.run(Thread.java:619)
Caused by: org.springframework.ws.soap.client.SoapFaultClientException: Reference number is not provided!
...........

How it works...

In the endpoint (OrderServiceEndpoint) in the handler method (handlePlaceOrderRequest), as the incoming message doesn't contain a reference number, a simple RuntimeException is thrown. This symbolizes any unexpected runtime exception. For clarification, a meaningful error description (Reference number is not provided!) is passed to the exception:

@PayloadRoot(localPart = "placeOrderRequest", namespace = SERVICE_NS)
public @ResponsePayload
Source handlePlaceOrderRequest(@RequestPayload Source source) throws Exception {
//extract data from input parameter
String fName="John";
String lName="Smith";
String refNumber="";
if(refNumber.length()>0)
return new StringSource(
"<tns:placeOrderResponse xmlns:tns="http://www.packtpub.com/liverestaurant/OrderService/schema"><tns:refNumber>"+orderService.placeOrder(fName, lName, refNumber)+"</tns:refNumber></tns:placeOrderResponse>");
else
throw new RuntimeException("Reference number is not provided!");
}

You can see that there are no explicit exception resolvers configured for this project. The smart MessageDispatcher of the Spring-WS framework allocates a default exception resolver to handle any exception when there is no exception resolver configured. It uses SimpleSoapExceptionResolver to handle the situation.

SimpleSoapExceptionResolver resolves the exception by performing the following operations:

  • Logs the exception to the logger (console, log file)

  • Generates the SOAP fault message with the exception message as the fault string and returns as part of the response message

When we check the response message at the client side, we can see the exact exception message (Reference number is not provided!) that is set inside the method, OrderServiceEndpoint. handlePlaceOrderRequest is returned as the SOAP fault string in the response message.

What is interesting here is that the developer doesn't need to do anything to handle and send the SOAP fault message, except for throwing an exception with a meaningful message.

See also

The recipe Setting up an endpoint by annotating the payload-root, discussed in Chapter 1,Building SOAP Web-Services.

The recipe Creating a Web-Service client on HTTP transport, discussed in Chapter 2,Building Clients for SOAP Web-Services.

Mapping exception class names to SOAP faults

Spring-WS framework allows the SOAP fault messages to be customized easily in the bean-configuration file, spring-ws-servlet.xml. It uses a special exception resolver, SoapFaultMappingExceptionResolver, to do that job. We can map exception classes to the corresponding SOAP fault to be generated and returned to the client.

Getting ready

In this recipe, the project's name is LiveRestaurant_R-4.2 (for server-side Web-Service) and has the following Maven dependencies:

  • spring-ws-core-2.0.1.RELEASE.jar

  • log4j-1.2.9.jar

The following are the Maven dependencies for LiveRestaurant_R-4.2-Client (for the client-side Web-Service):

  • spring-ws-core-2.0.1.RELEASE.jar

  • spring-test-3.0.5.RELEASE.jar

  • log4j-1.2.9.jar

How to do it...

This recipe uses the projects from Setting up an endpoint by annotating the payload-root, discussed in Chapter 1,Building SOAP Web-Services.

  1. Create a custom exception class DataOutOfRangeException.java.

  2. Modify OrderServiceEndpoint to throw DataOutOfRangeException.

  3. Register SoapFaultMappingExceptionResolver in spring-ws-servlet.xml.

  4. Build and deploy the project in the Maven-embedded Tomcat server.

  5. Run the following command from the root of the project, in a command-line window:

    mvn clean package tomcat:run
    
    
  6. To test, open a new command window, go to the folder LiveRestaurant_R-4.2-Client, and run the following command:

mvn clean package exec:java

  • The following is the output from the server-side console (note that the SOAP-Env:Fault element is generated in the message):

DEBUG [http-8080-1] (MessageDispatcher.java:177) -
Sent response
...
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:Server</faultcode>
<faultstring xml:lang="en">such a data is out of range!</faultstring>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
... for request
<tns:placeOrderRequest xmlns:tns="....">
.......
</tns:placeOrderRequest>

  • The following is the output from the client-side console:

Received response...
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:Server</faultcode>
<faultstring xml:lang="en">such a data is out of range!</faultstring>
</SOAP-ENV:Fault>
......
for request....
<tns:placeOrderRequest xmlns:tns="......">
.......
</tns:placeOrderRequest>
.....
[WARNING]
java.lang.reflect.InvocationTargetException
.........
Caused by: org.springframework.ws.soap.client.SoapFaultClientException: such a data is out of range!
.......

How it works...

In the preceding code, the OrderServiceEndpoint.placeOrderRequest method throws a custom exception, DataOutOfRangeException, which symbolizes a typical server-side exception:

@PayloadRoot(localPart = "placeOrderRequest", namespace = SERVICE_NS)
public @ResponsePayload
Source handlePlaceOrderRequest(@RequestPayload Source source) throws Exception {
//extract data from input parameter
String fName="John";
String lName="Smith";
String refNumber="123456789";
if(refNumber.length()<7)
return new StringSource(
"<tns:placeOrderResponse xmlns:tns="http://www.packtpub.com/liverestaurant/OrderService/schema"><tns:refNumber>"+orderService.placeOrder(fName, lName, refNumber)+"</tns:refNumber></tns:placeOrderResponse>");
else
throw new DataOutOfRangeException("RefNumber is out of range");
}

This exception is caught by MessageDispatcher and delegated to the configured exception resolvers. In this project, SoapFaultMappingExceptionResolver is used, which is a special kind of resolver that allows the exception classes to be mapped with custom messages in the configuration file. In this example, a different message is used to map against DataOutOfRangeException. It acts as an interceptor that converts the SOAP fault message into whatever is given in the following mapping:

<bean id="exceptionResolver"
class="org.springframework.ws.soap.server.endpoint.SoapFaultMappingExceptionResolver">
<property name="defaultFault" value="SERVER" />
<property name="exceptionMappings">
<value>
com.packtpub.liverestaurant.service.exception.DataOutOfRangeException=SERVER,
such a data is out of range!
</value>
</property>
</bean>

The generated SOAP fault message is logged in at both the server-side as well as the client-side console screens. It displays the mapped SOAP fault message instead of what is originally thrown by the DataOutOfRangeException class.

There's more...

This powerful facility to map exceptions with SOAP fault strings is very useful for externalizing SOAP fault management from the code. It gives developers the flexibility to change the SOAP fault string, based on any business requirements at a later stage, without touching the code and rebuilding it. Moreover, if properly designed, this feature, with its configuration (SOAP fault mapping) in the spring-ws.xml file, can serve as a single point of reference for all possible SOAP fault messages of the services that can be maintained easily.

Note

This is a good solution for B2B applications. Not good for B2C, when supporting multiple languages. In general, the best way to do this is by configuring messages in the database. This way, we can change them and fix them during runtime. The drawback in configuring in XML is that it needs to restart. In real time, one app runs on 30 servers. Deploying and restarting are painful processes.

See also

The recipe Setting up an endpoint by annotating the payload-root, discussed in Chapter 1,Building SOAP Web-Services.

The recipe Creating a Web-Service client on HTTP transport, discussed in Chapter 2,Building Clients for SOAP Web-Service

The recipe Handling server-side exceptions by returning exception's message as a SOAP fault string, discussed in this chapter.

Annotating exception classes with @SOAPFault

Spring-WS framework allows application exceptions to be annotated to SOAP fault messages and customized easily in the exception class itself. It uses a special exception-resolver, SoapFaultAnnotationExceptionResolver, for that job. SOAP fault string and fault code can be customized by annotating in the class.

Getting ready

In this recipe, the project's name is LiveRestaurant_R-4.3 (for the server-side Web-Service) and has the following Maven dependencies:

  • spring-ws-core-2.0.1.RELEASE.jar

  • log4j-1.2.9.jar

The following are the Maven dependencies for LiveRestaurant_R-4.3-Client (for the client-side Web-Service):

  • spring-ws-core-2.0.1.RELEASE.jar

  • spring-test-3.0.5.RELEASE.jar

  • log4j-1.2.9.jar

  • junit-4.7.jar

  • xmlunit-1.1.jar

How to do it...

This recipe uses the project from Setting up an endpoint by annotating the payload-root, discussed in Chapter 1, Building SOAP Web-Services, as server-side and the recipe How to integrate test using Spring-Junit support, discussed in Chapter 3, Testing and Monitoring Web-Services, as client-side.

  1. Create a custom exception class (InvalidOrdeRequestException.java), which is annotated with @SoapFault.

  2. Create a custom exception class (OrderProcessingFailedException.java), which is annotated with @SoapFault.

  3. Modify Endpoint(OrderServiceEndpoint) to throw both InvalidOrderRequestException and OrderProcessingFailedException.

  4. Register SoapFaultAnnotationExceptionResolver in the server application context file (spring-ws-servlet.xml).

  5. Build and deploy the project in the Maven-embedded Tomcat server.

  6. Run the following command from the root of the project, in a command-line window:

    mvn clean package tomcat:run.
    
    
  7. To test, open a new command window, go to the folder LiveRestaurant_R-4.3-Client, and run the following command:

mvn clean package

  • The following is the output from the client-side console (please note the SOAP-Env:Fault element generated in the message):

DEBUG [main] (WebServiceTemplate.java:632) -
Received response
.....
<SOAP-ENV:Fault><faultcode>SOAP-ENV:Client</faultcode>
<faultstring xml:lang="en">Invalid Order Request: Request message incomplete</faultstring>
</SOAP-ENV>
for request....
<tns:placeOrderRequest ....>
....
</tns:placeOrderRequest>
....................
Received response ...
<SOAP-ENV:Fault><faultcode>SOAP-ENV:Server</faultcode>
<faultstring xml:lang="en">Database server under maintenance, please try after some time.</faultstring>
</SOAP-ENV:Fault>...
for request ...
<tns:cancelOrderRequest ..>
<tns:refNumber>9999</tns:refNumber>
</tns:cancelOrderRequest>
...
Tests run: 2, Failures: 0, Errors: 2, Skipped: 0, Time elapsed: 0.874 sec <<< FAILURE!

How it works...

In the endpoint's methods, OrderServiceMethodEndoint.processOrder (placeOrderRequest and cancelOrderRequest), custom exceptions are thrown (ProcessingFailedException and InvalidOrderRequestException) that symbolize a typical server-side/client-side exception:

@PayloadRoot(localPart = "placeOrderRequest", namespace = SERVICE_NS)
public @ResponsePayload
Source handlePlaceOrderRequest(@RequestPayload Source source) throws Exception {
//extract data from input parameter
String fName="John";
String lName="Smith";
String refNumber="";
if(refNumber.length()>0)
return new StringSource(
"<tns:placeOrderResponse xmlns:tns="http://www.packtpub.com/liverestaurant/OrderService/schema"><tns:refNumber>"+orderService.placeOrder(fName, lName, refNumber)+"</tns:refNumber></tns:placeOrderResponse>");
else
throw new InvalidOrderRequestException("Reference number is not provided!");
}
@PayloadRoot(localPart = "cancelOrderRequest", namespace = SERVICE_NS)
public @ResponsePayload
Source handleCancelOrderRequest(@RequestPayload Source source) throws Exception {
//extract data from input parameter
boolean cancelled =true ;
if( isDataBaseServerRunning())
return new StringSource(
"<tns:cancelOrderResponse xmlns:tns="http://www.packtpub.com/liverestaurant/OrderService/schema"><cancelled>"+(cancelled?"true":"false")+"</cancelled></tns:cancelOrderResponse>");
else
throw new ProcessingFailedException("Database server is down!");
}
private boolean isDataBaseServerRunning(){
return false;
}

This exception is caught by the MessageDispatcher and delegates to the configured exception resolvers. In this project, SoapFaultAnnotationExceptionResolver is used, which is a special kind of resolver that allows the exception classes to be annotated with custom fault-code and fault-strings in the class. SoapFaultAnnotationExceptionResolver is configured to be used in spring-ws-servlet.xml, thus any exception handling is delegated to it by MessageDispatcherServlet at runtime:

<bean id="exceptionResolver"
class="org.springframework.ws.soap.server.endpoint.SoapFaultAnnotationExceptionResolver">
<property name="defaultFault" value="SERVER" />
</bean>

ProcessingFailedException represents a server-side system exception (faultCode = FaultCode.SERVER):

@SoapFault(faultCode = FaultCode.SERVER,
faultStringOrReason = "Database server under maintenance, please try after some time.")
public class ProcessingFailedException extends Exception {
public ProcessingFailedException(String message) {
super(message);
}
}

InvalidOrderRequestException represents a client-side business logic exception (faultCode = FaultCode.CLIENT):

@SoapFault(faultCode = FaultCode.CLIENT,
faultStringOrReason = "Invalid Order Request: Request message incomplete")
public class InvalidOrderRequestException extends Exception {
public InvalidOrderRequestException(String message) {
super(message);
}
}

You can see that the annotated faultStringOrReason is generated as a SOAP fault and is transmitted back to the client. The generated SOAP fault message, which is logged in both the server-side as well as client-side console screens, displays the annotated SOAP fault message instead of what is originally thrown in the Endpoint class.

There's more...

The attribute faultCode of the @SoapFault annotation has the following possible enumerated values:

  • CLIENT

  • CUSTOM

  • RECEIVER

  • SENDER

The selection of one from the enumerated list instructs the dispatcher which kind of SOAP fault is to be generated along with its specifics. Based on the preceding selection, dependent attributes become mandatory.

For example, if FaultCode.CUSTOM is selected for faultCode, the property customFaultCode string must be used instead of faultStringOrReason, as given in the code snippet of this recipe. The format used for customFaultCode is that of QName.toString(), that is, "{" + Namespace URI + "}" + local part, where the namespace is optional. Note that custom fault codes are only supported on SOAP 1.1.

The @SoaPFault annotation has one more attribute, namely, locale, which decides the language of the SOAP fault message. The default locale is English.

Note

In general practice, we use error codes rather than error messages. Mapping will be done on the client side using mapping information. This avoids any load on the network and there will be no issue with multiple language support.

See also

The recipe Setting up an endpoint by annotating the payload-root, discussed in Chapter 1,Building SOAP Web-Services.

The recipe How to integrate test using Spring-JUnit support, discussed in Chapter 3,Testing and Monitoring Web-Services.

The recipe Mapping exception class names to SOAP faults, discussed in this chapter.

Writing your own exception resolvers in Spring-WS

While Spring-WS framework provides default mechanisms to handle exceptions using the standard exception resolvers, it allows developers to handle exceptions in their own way by building their own exception resolvers. SOAP faults can be customized to add custom details in their own formats and transmitted back to the client.

This recipe illustrates a custom exception resolver that adds the exception stack trace to the SOAP fault detail element of the SOAP response, so that the client will get the complete stack trace of the server-side exception, which is very useful for certain cases. This custom exception resolver already carries the power of annotations, as in the previous recipe.

Getting ready

In this recipe, the project's name is LiveRestaurant_R-4.4 (for the server-side Web-Service) and has the following Maven dependencies:

  • spring-ws-core-2.0.1.RELEASE.jar

  • log4j-1.2.9.jar

LiveRestaurant_R-4.4-Client (for the client side) has the following Maven dependencies:

  • spring-ws-core-2.0.1.RELEASE.jar

  • spring-test-3.0.5.RELEASE.jar

  • log4j-1.2.9.jar

  • junit-4.7.jar

  • xmlunit-1.1.jar

How to do it...

This recipe uses the project from Setting up an endpoint by annotating the payload-root, discussed in Chapter 1,Building SOAP Web-Services.

  1. Create a custom exception resolver, DetailedSoapFaultExceptionResolver, extending SoapFaultAnnotationExceptionResolver.

  2. Register DetailedSoapFaultExceptionResolver in spring-ws-servlet.xml.

  3. Build and deploy the project in the Maven-embedded Tomcat server.

  4. Run the following command from the root of the project, in a command-line window:

    mvn clean package tomcat:run.
    
    
  5. To test, open a new command window, go to the folder LiveRestaurant_R-4.4-Client, and run the following command:

mvn clean package exec:java

  • The following is the output from the server-side console (note the SOAP-Env:Fault element generated in the message):

DEBUG [http-8080-1] (MessageDispatcher.java:167) - Received request.....
<tns:placeOrderRequest xmlns:tns="http://www.packtpub.com/liverestaurant/OrderService/schema">
......
</tns:placeOrderRequest></SOAP-ENV:Body>...
DEBUG [http-8080-1] (MessageDispatcher.java:177) - Sent response
...
<SOAP-ENV:Fault><faultcode>SOAP-ENV:Client</faultcode>
<faultstring xml:lang="en">Invalid Order Request: Request message incomplete</faultstring
><detail>
<stack-trace xmlns=".....">
at com.packtpub.liverestaurant.service.endpoint.OrderSeviceEndpoint.handlePlaceOrderRequest(OrderSeviceEndpoint.java:43)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.ws.server.endpoint.MethodEndpoint.invoke(MethodEndpoint.java:132)
at org.springframework.ws.server.endpoint.adapter.DefaultMethodEndpointAdapter.invokeInternal(DefaultMethodEndpointAdapter.java:229)
at org.springframework.ws.server.endpoint.adapter.AbstractMethodEndpointAdapter.invoke(AbstractMethodEndpointAdapter.java:53)
at org.springframework.ws.server.MessageDispatcher.dispatch(MessageDispatcher.java:230)
.......

</stack-trace></detail></SOAP-ENV:Fault>

How it works...

In the preceding code, our custom exception resolver, DetailedSoapFaultExceptionResolver, which is a subclass of SoapFaultAnnotationExceptionResolver, overrides the method custmizeFault() to add an exception stack trace into the SOAP fault detail element. The method stackTraceToString() returns the exception stack trace from a given exception, and it is used to set the stack trace to the detail element of the SOAP fault of the response message.

There's more...

There are many different ways of creating custom exception resolvers. It is not just SoapFaultAnnotationExceptionResolver that can be inherited for that purpose. Any implementation of org.springframework.ws.server.EndpointExceptionResolver can be configured appropriately to be used as an exception resolver. Developers can choose from a set of very convenient implementations of EndpointExceptionResolver, available in the Spring-WS API, leveraging the power of these implementations.

The place for customizing these classes is the method, customizeFault. The SOAP fault can be customized by overriding the method customizeFault. Take a look at the package org.springframework.ws.soap.server.endpoint for the readily available exception resolvers that suit your requirement.

AbstractSoapFaultDefinitionExceptionResolver would be an ideal starting point to extend, if an exclusively custom exception resolver needs to be developed that doesn't fit with the currently available implementations, as it has already implemented some of the very common and basic functionality that are needed for any exception resolvers. The developer just needs to implement the abstract method, resolveExceptionInternal(), to suit your specific need.

What needs to be taken care of is that MessageDispatcherServlet should be instructed to consider the resolver in use, either by registering in spring-ws-servlet.xml or annotating in the exception class (in addition to registering in spring-ws-servlet.xml).

See also

The recipe Setting up an endpoint by annotating the payload-root, discussed in Chapter 1,Building SOAP Web-Services

The recipe Annotating Exception classes with @SOAP fault, discussed in this chapter.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset