Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
matt_steiner
Active Contributor
0 Kudos

Introduction

It's been really quiet around here so far this year as due to private affairs I was not able to blog as frequently as I did last year. However, to kick off a new blogging season I'd like to cover an ever-popular topic, which is also an all-time high-ranking task of every developers TODO list... exception handling. 😉

But don't worry, this will not be just another rant on how to handle error management, but instead I take the basics for granted and move straight onto a more specialized topic: exception handling when consuming Enterprise Services (ES) and their kinsman (e.g. SAP Business ByDesign A2X Services or any kind of Web Services that is modeled in adherance to ES design guidelines.)

Note: In a previous blog post I have been talking about how to develop ES-like services using Java Standards and ellaborated in detail about the underlying design pricinples of ES. Please feel encouraged to refresh your memories 😉

Basic Enterprise Service Interface Design

As most of you probably know the interfaces of all ES operations look somewhat alike, as shown in the pseudo-coding below:

Response doSomething(Request) throws ServiceException

In order to better illustrate the concept I've browsed through the ES Workplace and selected a prominent Enterprise Service: Find Sales Order Basic Data by Elements

As you can see the response object contains both a list of Sales Orders found (which match the query criteria) as well as a Log structure used to report back status messages to the consumer of the service. So, in case no matching sales order were found a corresponding message is returned within the Log structure.

In case of technical issues, error messages are returned in the form of a StandardMessageFault structure, which contains very similar fields as the Log structure.

Consuming ES-like WebServices with Composite Application Framework (CAF)

As shown in numerous examples on SDN the standard way of consuming an ES-like WebService is to create an External Service based on the WSDL file. This External Service is then wrapped in a default mapping Application Service.

More often than not the default mapping features will not be sufficient and more complex mapping functionality is required, hence a custom Application Services has to be created that references/uses/wraps the default mapping Application Service. Within the implementation class of this custom-coded Application Service the mapping logic is done manually on API level using the Java objects(which have been automatically created during the creation of the External Service.)

As every default mapping Application Services declares a CAFServiceException to be thrown in case of errors the developer faces the following decision:

  • simply declare a CAFServiceException in the custom-coding operation as well or
  • catch the CAFServiceException and handle it as appropriate

Of course the decision should be made based on a number of criteria like who's consuming this service etc., but 9 out of 10 times I think it's fair to say that option no 2 is the best approach as usability guidelines emphasize on the need to provide reasonable error messages to the end-user.

So, as the title of this post suggest... there's a rule for every exception. 😉

Some of the most common use-cases is localization of error messages to the language (to be more precise the locale) of the end-user. Technical error messages should be translated to the end-user so that they know on how to react or whom to notify about problems.

Last, but not least... some (potential) clients of the service may not be able to handle an exception approriatly which also speaks in favor of option no 2.

Accessing the details of the SOAPFaultMessage

So, in order to handle an exception caught during a WebService call we would need to access the SOAPFaultMessage structure, which is nested in the CAFServiceException. The coding below illustrates the basic concept:

catch (CAFServiceException cafEx)
{           
    Throwable rootEx = cafEx.getCause();
           
    while (rootEx.getCause() != null)
    {                              
        rootEx = rootEx.getCause();
    }

    if (rootEx instanceof SOAPFaultException)
    {
        SOAPFaultException soapEx = (SOAPFaultException) rootEx;
        Detail detail = soapEx.getDetail(); 
               
        Element faultMessage = (Element) detail.getChildNodes().item(0);               
        Element severity = (Element) details.getElementsByTagName("severity").item(0);
        Element text = (Element) detail.getElementsByTagName("text").item(0);
        Element id = (Element) detail.getElementsByTagName("id").item(0);

        ...
     }
}

Note: Some may wonder why I manually retrieve the details by navigating the XML DOM tree instead of leveraging JAXB. Unfortunately, we cannot leverage JAXB to convert the XML into the corresponding (and existing) StandardFaultMessage objects due to the fact that the StandardFaultMessage is a top level element of anonymous type and therefore cannot be instantiated by the ObjectFactory.

Converting a StandardFaultMessage to Log

 

Even though it somewhat  contradicts to what I've said earlier there may be scenarios where you'd simply want to convert SOAPFaultExceptions 1:1 to corresponding LogItems in order to report them back to the client (e.g. the error handling is done on UI level) in a unified manner.

One of these scenarios could be when consuming A2X services as exposed by SAP Business ByDesign (BYD) , because - as of now  - BYD reports back all error messages as StandardMessageFaults (yes, even business related errors.)

Here's a code snippet showing how to do the mapping:

LogType log = new LogType();
               
log.setMaximumLogItemSeverityCode(severity.getChildNodes().item(0).getNodeValue());
               
LogItemType logItem = new LogItemType();
               
logItem.setSeverityCode(severity.getChildNodes().item(0).getNodeValue());
logItem.setNote(text.getChildNodes().item(0).getNodeValue());
logItem.setTypeID(id.getChildNodes().item(0).getNodeValue());
               
log.getItem().add(logItem);
                              
response.setLog(log);
           

OK, I call it a day. Hope this was helpful and that you consider the time reading all this well-spend. Please feel encouraged to comment...



Special thanks to Katharina, Manish, Marton and Ivan for supporting me on my exception handling excursion 😉