Search

    Indicating Errors

    This section describes the error handling capabilities provided by the CAP Java SDK.

    Content

    Overview

    The CAP Java SDK provides two different ways to indicate errors:

    • By throwing an exception: This completely aborts the event processing and rollbacks the transaction.
    • By using the Messages API: This adds errors, warnings, info, or success messages to the currently processed request, but doesn’t affect the event processing or the transaction.

    The message texts for both exceptions and the Messages API can use formatting and localization.

    Exceptions

    Any exception that is thrown by an event handler method aborts the processing of the current event and causes any active transaction to be rolled back. To indicate further details about the error, such as a suggested mapping to an HTTP response code, the CAP Java SDK provides a generic unchecked exception class, called ServiceException. It’s recommended to use this exception class, when throwing an exception in an event handler.

    When creating a new instance of ServiceException you can specify an ErrorStatus object, through which an internal error code and a mapping to an HTTP status code can be indicated. An enum ErrorStatuses exists, which lists many useful HTTP error codes already. If no such error status is set when creating the ServiceException, it defaults to an internal server error (HTTP status code 500).

    // default error status
    throw new ServiceException("An internal server error occurred", originalException);
    // specifying an error status
    throw new ServiceException(ErrorStatuses.CONFLICT, "Not enough stock available")
    // specifying an error status and the original exception
    throw new ServiceException(ErrorStatuses.BAD_REQUEST, "No book title specified", originalException);
    

    The OData V4 adapter turns all exceptions into an OData error response to indicate the error to the client.

    Messages

    The Messages API allows event handlers to add errors, warnings, info, or success messages to the currently processed request. Adding messages doesn’t affect the event processing or the transaction. This can, for example, be useful to indicate validation errors in a draft entity to the client, while still allowing the invalid draft state to be saved.

    The Messages interface provides a logger-like API to collect these messages. Additional optional details can be added to the Message using a builder API. You can access the Messages API from the Event Context:

    context.getMessages().success("The order was successfully placed");
    

    In Spring, you can also access it using Dependency Injection:

    @Autowired
    Messages messages;
    
    messages.warn("No book title specified");
    messages.error("The book is no longer available").code("BNA").longTextUrl("/help/book-not-available");
    

    The OData V4 adapter collects these messages and writes them into the sap-messages HTTP header by default. However, when an OData V4 error response is returned, because the request was aborted by an exception, the messages are instead written into the details section of the error response. Writing the messages into explicitly modeled messages properties isn’t yet supported.

    SAP Fiori uses these messages to display detailed information on the UI. The style how a message appears on the UI depends on the severity of the message.

    Throwing a ServiceException from Messages

    It is also possible to throw a ServiceException from error messages. This can, for example, be useful to cancel a request after collecting multiple validation errors. The individual validation checks will collect error messages in the Messages API. After the validation checks have been run, you call the throwIfError() method. Only if error messages have been collected, this method cancels the request with a ServiceException:

    // throw a ServiceException, if any error messages have been added to the current request
    messages.throwIfError();
    

    If there are any collected error messages, this method creates a ServiceException from one of these error messages. The OData V4 adapter turns this exception into an OData error response to indicate the error to the client. The remaining error messages are written into the details section of the error response.

    Formatting and Localization

    Texts passed to both ServiceException and the Messages API can be formatted and localized. By default you can use SLF4J’s messaging formatting style to format strings passed to both APIs.

    // message with placeholders
    messages.warn("Can't order {} books: Not enough on stock", orderAmount);
    // on ServiceException last argument can always be the causing exception
    throw new ServiceException(ErrorStatuses.BAD_REQUEST, "Invalid number: '{}'", wrongNumber, originalException);
    

    You can localize these strings, by putting them into property files and passing the key of the message from the properties file to the API instead of the message text.

    When running your application on Spring, the CAP Java SDK integrates with Spring’s support for handling text resource bundles. This handling by default expects translated texts in a messages.properties file under src/main/resources.

    The texts defined in the resource bundles can be formatted based on the syntax defined by java.text.MessageFormat. When the message or exception text is sent to the client it’s localized using the client’s locale, as described here.

    messages.properties

    my.message.key = This is a localized message with {0} parameters
    

    messages_de.properties

    my.message.key = Das ist ein übersetzter Text mit {0} Parametern
    
    // localized message with placeholders
    messages.warn("my.message.key", paramNumber);
    // localized message with placeholders and additional exception
    throw new ServiceException(ErrorStatuses.BAD_REQUEST, "my.message.key", paramNumber, originalException);
    

    Exporting the Default Messages

    As of CAP Java 1.10.0, you can extract the available default messages as a resource bundle file for further processing (for example, translation). Therefore, the delivery artifact cds-services-utils contains a resource bundle cds-messages-template.properties with all available error codes and default messages. Application developers can use this template to customize error messages thrown by the CAP Java SDK in the application.

    1. Download the artifact or get it from the local Maven repository in ~/.m2/repository/com/sap/cds/cds-services-utils/<VERSION>/cds-services-utils-<VERSION>.jar.
    2. Extract the file.
       jar -f cds-services-utils-<VERSION>.jar -x cds-messages-template.properties
      

      <VERSION> is the version of CAP Java you’re using in your project.

    3. Rename the extracted file cds-messages-template.properties appropriately (for example, to cds-messages.properties) and move it to the resource directory of your application.
    4. In your Spring Boot application, you have to register this additional resource bundle accordingly.

    Now, you’re able to customize the stack error messages in your application.

    With new CAP Java versions, there could be also new or changed error messages in the stack. To identify these changes, export cds-messages-template.properties from the new CAP Java version and compare it with the previous version using a diff tool.

    Target

    When SAP Fiori interprets messages it can handle an additional target property, which, for example, specifies which element of an entity the message refers to. SAP Fiori can use this information to display the message along the corresponding field on the UI. When specifying messages in the sap-messages HTTP header, SAP Fiori mostly ignores the target value. Therefore, specifying the target can only correctly be used when throwing a ServiceException as SAP Fiori correctly handles the target property in OData V4 error responses.

    The CAP Java SDK provides the ability to specify this target either as a plain string, or using a typed API backed by the CDS model.

    // plain String
    throw new ServiceException(ErrorStatuses.BAD_REQUEST, "No book title specified")
        .messageTarget("title");
    // typed API
    throw new ServiceException(ErrorStatuses.BAD_REQUEST, "No author name specified")
        .messageTarget(Books_.class, b -> b.author().name());
    
    Show/Hide Beta Features