Invalid Business State Errors
Invalid Business State Errors
An invalid business state error is an error thrown during a state change operation where a business condition is not fulfilled. Invalid business state error types prevent a state change operation from happening. Often the client can solve the underlying business error and then retry the state change operation successfully.
For example, when purchasing an order, the following scenarios could generate invalid business state errors:
- An item in the cart isn't available in the requested quantity.
- The shopping cart is empty.
- A shipping address is missing.
Invalid business state error is mapped to the HTTP status code 409, which indicates the presence of a conflict. To help the client localize error messages and recover from this error type, the body of the HTTP response contains structured error messages.
Constructing Invalid Business State Errors
Unlike validation errors, which are generated during validation, invalid business state errors must be constructed manually.
In Commerce Engine, an invalid business state error is an Exception which implements the InvalidBusinessStateException interface. Each exception of type InvalidBusinessStateException can contain multiple StructuredErrorMessage objects, indicating why the failure state occurred.
For example, AvalabilityException is a fully constructed invalid business state error:
public class AvailabilityException extends EpSystemException implements InvalidBusinessStateException { private final List<StructuredErrorMessage> structuredErrorMessages; public AvailabilityException(final String message, final Collection<StructuredErrorMessage> structuredErrorMessages) { super(message); this.structuredErrorMessages = structuredErrorMessages == null ? emptyList() : ImmutableList.copyOf(structuredErrorMessages); } @Override public List<StructuredErrorMessage< getStructuredErrorMessages() { return structuredErrorMessages; } @Override public String getExceptionMessage() { return getMessage(); } }
Handling InvalidBusinessStateException Exceptions in Cortex
To handle InvalidBusinessStateException and extract its StructuredErrorMessage objects, first convert InvalidBusinessStateException to ExecutionResult in an exception catch block.
Next, as a part of the conversion:
- Extract the StructuredErrorMessage objects from the InvalidBusinessStateException.
- Use Spring's org.springframework.core.convert.ConversionService to convert StructuredErrorMessage objects to Message or LinkedMessage objects consumable by Cortex.
- Construct an ExecutionResult containing the Message or LinkedMessage objects.
The following is an example of converting an InvalidBusinessStateExeption:
public ExecutionResult<Void> addItemToCart() { try { cartService.addItemToCart(); return ExecutionResultFactory.createUpdateOK(); } catch (AvailabilityException exception) { List<Message> messages = exception.getStructuredErrorMessages() .stream() .map(sem -> conversionService.convert(sem, Message.class)) .collect(Collectors.toList()); return ExecutionResultFactory.createStateFailureWithMessages(exception.getMessage(), messages); } }
In addition to being consumable by Cortex, converting StructuredErrorMessage objects to Message or LinkedMessage allows these objects to contain information about links.
Inject ConversionService into the JavaBean where the mapping should occur. ConversionService is able to locate the preexisting Spring converter, as it is already properly registered in the Spring Context of Cortex's repository bundle.
To use ConversionService in another OSGi bundle, add the following to the bundle's blueprint.xml configuration file:
<reference id="conversionService" interface="org.springframework.core.convert.ConversionService"/commerce-legacy/>
Handling Multiple InvalidBusinesStateException Exceptions
Since InvalidBusinesStateException is an interface it doesn't inherit from java.lang.Exception. This means it can't be used in a catch block, and you cannot directly catch InvalidBusinessStateExeption exceptions. To handle exceptions for InvalidBusinessStateException, use generic exception handling in the following pattern:
catch (Exception exception) { if (exception instanceof InvalidBusinessStateException) { return handleInvalidBusinessStateException((InvalidBusinessStateException) exception); }
Then, implement a method for handing the exception:
private ExecutionResult<Void> handleInvalidBusinessStateException(final InvalidBusinessStateException exception) { List<Message> messages = exception.getStructuredErrorMessages() .stream() .map(sem -> conversionService.convert(sem, Message.class)) .collect(Collectors.toList()); return ExecutionResultFactory.createStateFailureWithMessages(exception.getExceptionMessage(),messages); }