Exceptions and ErrorCode

Beside the exception thrown by EJBs as specified in EJB 2.0 such as:

every method in the remote interface of a Infinity Process Platform bean can throw the following checked exceptions:

The first type of exception is thrown whenever the following information about an exception is sufficiently the fact, whether the exception has to be displayed to the user or is internal and the error message itself. The latter type of exceptions is used whenever more information is available about an exception and specific handling for this exception in a client is expected.

For handling exceptions thrown by invoked applications or services, please refer to the section Engine Exception Handling. The section Exception Handling in EJB Environment provides a description on how exceptions are handled in an EJB environment.

Engine Exception Handling

It is possible to propagate exceptions from invoked applications or services to synchronous callers of Infinity Process Platform services by using a global option. The engine handling of exceptions can be controlled via the server side property:

Carnot.Engine.ErrorHandling.ApplicationExceptionPropagation = [never | onRollback | always]

The default value is never. Please refer to chapter Server Side Properties for information on this server side property.

Before defining the intended behavior, please consider the following two scenarios:

Scenario a):
An exception is raised, but the transaction is not yet forced to rollback:
the engine has a choice to either interrupt the current activity thread, or to undo the current activity thread by forcing the current transaction to rollback.
Scenario b):
An exception is raised, and the current transaction is already forced to rollback:
the engine has no real choice, any attempt to interrupt the current activity thread will be undone when the transaction eventually rolls back.

The behavior is defined as follows:

The onRollback behavior allows the implementor to control which exceptions will interrupt the execution flow and which exceptions will be propagated to the client by setting the transaction state.

Note
Please note that in case of never, the engine does not check the status of the transaction, and therefore if the transaction is already set to rollback then all the actions performed in this transaction will be lost.

Transaction Behavior of Exceptions during Execution

Infinity provides the option to specify the transaction behavior in case of exceptions during execution. Set the server-side property Carnot.Engine.RollbackOnError in your carnot.properties file to determine the rollback behavior. For details on this property please refer to chapter Server Side Properties in the Operation Guide.

The property can have the following values:

AdministrationService behavior

The following AdministrationService methods will not trigger a rollback in case of exceptions:

WorkflowService behavior

In case of the WorkflowService, no method will trigger a rollback with the exception of:

Public Exceptions

The org.eclipse.stardust.common.error.WorkflowException serves as a wrapper for PublicExceptions. If you receive a WorkflowException use the method getCause() to return the inner Throwable which caused this workflow exception.

The following table lists the possible Infinity Process Platform PublicExceptions:

Exception Description
org.eclipse.stardust.common.error.AccessForbiddenException Thrown when the performing user is not valid or if he doesn't have the necessary permissions
org.eclipse.stardust.common.error.ConcurrencyException Thrown whenever the workflow engine detects concurrency problems with workflow enactment
org.eclipse.stardust.common.error.ExpectedFailureException failure exception
org.eclipse.stardust.common.error.InvalidValueException Thrown if a write operation to workflow data fails as of an invalid input value. Most probably in consequence of a type conflict between the input value and the static type of the data or data's attribute
org.eclipse.stardust.common.error.ObjectNotFoundException Thrown when an object was not found
org.eclipse.stardust.common.error.ValidationException Thrown when validation fails
org.eclipse.stardust.common.error.LoginFailedException This exception is thrown by the authentication framework due to various exceptions
org.eclipse.stardust.engine.api.model.ModelParsingException Thrown when model parsing failed
org.eclipse.stardust.engine.api.query.UnsupportedFilterException Will be thrown if filter criteria are not supported in the envisioned context
org.eclipse.stardust.engine.api.runtime.BindingException Thrown when binding error occurs
org.eclipse.stardust.engine.api.runtime.DeploymentException Thrown if an exception occurred during deployment
org.eclipse.stardust.engine.api.runtime.IllegalOperationException Thrown when an operation is illegal in the current context
org.eclipse.stardust.engine.api.runtime.IllegalStateChangeException Thrown when an activity instance is required to perform an illegal state change (i.e. from completed to application)
org.eclipse.stardust.engine.api.runtime.UserExistsException Thrown if another user with the specified account already exists
org.eclipse.stardust.engine.api.runtime.UserGroupExistsException Thrown if another user group with the specified account already exists
org.eclipse.stardust.engine.api.runtime.UserRealmExistsException Thrown if another user realm with the specified account already exists

Note: Note that after declared Exceptions have been thrown, transaction contexts can be marked for rollback. If this is ignored and a Session Bean method acting as a client continues to work with the bean after catching exceptions, very hard-to-debug errors can occur.

Exception Handling in EJB Environment

Basically we have to distinguish between the following two scenarios during exception handling in EJB environment:

The section Use Case provides you with error handling strategies in a use case.

Application Exceptions

Application exceptions are exceptions declared in the remote interface of the bean. When an application exception occurs, the EJB container doesn't roll back the transaction automatically and the exception is handed back to the calling client application.

Generally application exceptions are used for functional predictable errors and give the client of the application the possibility to handle these error cases explicitly.

The standard error handling for application exceptions in Infinity Process Platform consists in setting the relevant session bean activity to the status "Interrupted" (after writing appropriate log messages), assuming that the exception is due to the unavailability of the application. A recovery of the workflow engine can then restart these specific activities at a later date, when the external application is available again and lead them to a successful processing.

Alternatively application exceptions defined inside the workflow model through OnException event handler can be treated technically.

System Exceptions

System exceptions are exceptions, which can't explicitly be declared in the signature of the accordant method. In case such an exception is thrown, the EJB container will automatically invoke a rollback.

To guarantee maximum data consistency, generally the transactional attributes of the methods will be set in a way that the Infinity Process Platform session beans and session beans called by Infinity Process Platform take place in the same transaction.

For that reason a rollback of an EJB container, for example caused by a system exception, also leads to a rollback of the precedent Infinity Process Platform activities up to the last explicitly set commit point. This allows a consistent error handling for several external applications (session beans). a Infinity Process Platform recovery call would handle this case through a restart of the activity thread and eventually all rolled-back method calls. However, because of this behavior it could happen that after the rollback no active activities exist to the accordant process.

Use Case

Generally, from the view of a Infinity Process Platform application, even the non-existence of an external session bean application is a predictable error case, which can be handled by the Infinity Process Platform recovery. For that reason preferably all exceptions in the external session beans should be thrown as ApplicationExceptions only. A setRollbackOnly() of the container should be avoided.

This means in detail:

RuntimeExceptions thrown so far will be caught inside the EJB method and wrapped in application exceptions. By adapting the workflow model inside the Infinity Process Platform application you can decide to just set the activity to "Interrupted" (standard behavior) or if an advanced technical error handling makes sense.

Thus the scenario described in section System Exceptions could be reduced to those cases, in which the container or the transaction manager itself cause a rollback for technical reasons.

For detailed information on Spring transaction management, please refer to the Spring documentation (www.springframework.net).

Authorization Handling

Note that Authorization validation is always checked before any other validation or code is performed. Thus, in case the user is not authorized, an AccessForbiddenException occurs rather than an anticipated Exception.

Creating Error Cases

The ErrorCase class provided by IPP is the general container for errors. This container provides a pair of the kind (errorcode, message), whereby the message can be localized in some cases. The sub-classes have the following tasks:

  1. Specify the range of values of Error Codes, by defining a factory for creating a specific Error Case for each Error Code
  2. These factories determine which parameter supports the according message respectively (e.g. an OID for "No such model {0}")
  3. The factory then defines a raise() method with an appropriate number of arguments.
  4. ErrorCase finally is the type that Infinity Exceptions receive to deliver an explication of the error cause
  5. Factories are defined locally by the sub-classes
Args1 args1 = BpmRuntimeError.AUTHx_USER_PASSWORD_NOT_VALID_TRY_AGAIN;
BpmRuntimeError bpmrtre = args1.raise("id");
             
return ExternalLoginResult.testifyFailure(new LoginFailedException(bpmrtre, LoginFailedException.INVALID_PASSWORD));
String userId = ...;
   ExternalLoginResult.testifyFailure(new LoginFailedException(
      BpmRuntimeError.AUTHx_USER_PASSWORD_NOT_VALID_TRY_AGAIN.raise(userId),
      LoginFailedException.INVALID_PASSWORD));

Creating custom Error Cases

new LoginFailedException(
// ErrorCase
new ErrorCase("My Custom ErrorCase") {

   private static final long serialVersionUID = 1L;
   
   @Override
   public String toString() {
      return super.getId() + " - Soemthing went wrong.";
   }
},
// reason
LoginFailedException.INVALID_PASSWORD)
new LoginFailedException(
//ErrorCase
BpmRuntimeError.AUTHx_USER_PASSWORD_NOT_VALID_TRY_AGAIN
//parameter for ErrorCase
.raise("id"),
//reason
LoginFailedException.INVALID_PASSWORD)