Error handling in the BOD command framework
There are two types of exceptions that service modules can throw, application errors and system errors. You will usually be throwing the first type of exception, an application exception. This is a service module typed exception that represents a business logic problem, such as input validation, authorization issues, or other business reasons. The second type of exception is a system application error, which is an unchecked exception which does not need to be explicitly caught by clients. These system exceptions indicate an internal problem which the framework would catch and handle and not the service module. If you want to throw system exceptions, extend the abstract system exception class for common logging and other quality of service reasons.
When an application exceptions is encountered, the main business object document command will throw this exception. When this exception occurs, the Business Object Document processor will perform the appropriate runtime logic to account for the exception and then call the instance of the command, passing the exception that occurred. The command will notified of the exception and should change the response Business Object Document to represent the exception that occurred. This means the basic exception data must be populated but the command can add more information such as areas in the noun that caused problems.
Although clients to a service can perform some initial validation on input data, they can only perform simple validation. Any validation that cannot be expressed by the XSD, or very simple rules on how to construct the request, is server validation. In the case of server validation, the service will validate the BOD input and return a list of application errors in the ChangeStatus area of the response verb.
An example of throwing an application exception is seen in the following code sample:
AuthorizationException exception = new AuthorizationException(searchExpression.getAccessProfile());
LOGGER.throwing(CLASSNAME, METHODNAME, exception);
throw exception;
This is an example of an application exception class. Note that it extends AbstractApplicationException:
/**
* An exception to represent an authorization exception where the current
* context cannot executed the specified request object.
*/
public class AuthorizationException extends AbstractApplicationException {
private static final String MESSAGE_KEY = FoundationUserMessageKeys.NOT_AUTHORIZED_TO_EXECUTE;
/**
* Creates an instance of the authorization exception.
*/
public AuthorizationException() {
super(MESSAGE_KEY, null, null);
}
/**
* Creates an instance of the authorization exception.
* @param resource The name of the resource that cannot be executed due to
* authorization.
*/
public AuthorizationException(String resource) {
super(MESSAGE_KEY, new Object[] { resource } , null, null);
}
}
In the SOI implementation, using name-value pair commands, only one action is supported, and so only one application error can be returned by the service response. If the request BOD contains multiple problems in the input, then it may take repeated requests before the client understands all of the issues with the request BOD.
The BOD command framework implementation support multiple actions, and so the framework supports returning multiple errors. Although the request BOD is still considered one transactional unit of work and either the entire BOD commits or rolls back, if multiple errors exists in the BOD, all the application errors can be returned. All application errors are represented as an ApplicationError. ApplicationErrors can be associated with the AbstractApplicationException to provide a list of reasons why the service cannot process the input BOD. This would include invalid data along with additional business logic validation.
Applications errors can represent a set of issues, and you can add them to the exception, as seen in the following example code:
exception.addApplicationError(new ApplicationError(ApplicationError.TYPE_GENERIC_ERROR,
FoundationUserMessageKeys.NOT_AUTHORIZED_TO_EXECUTE_ACTIONS_ON_BUSINESS_OBJECT,
new Object[] { action, action} ,
LOGGER.getResourceBundleName()
);
);
Compensation
When an error occurs during processing and the business logic must be rolled back, your business logic may require compensation. The handleException() method of your BOD command is called whenever an exception occurs which may require some compensation logic. The purpose of the compensation logic is to undo any operation as a result of the current operation being undone. For example, if a ProcessOrder service is processed and part of that business logic makes requests to the Payment service, the payment request may need some compensation to account for the ProcessOrder failing. Without the additional compensation logic, erroneous requests to the payment system may be processed. This compensation can issue requests to cancel the previous requests.