Implementing a Custom ErrorCase

This example demonstrates how to implement a custom ErrorCase. It is based on an example implementation of the IActivityInstanceMonitor SPI, but could be adapted to any SPI implementation that needs to provide custom error cases. The implementation is similar to the BpmRuntimeError ErrorCase.

Downloading the Example Implementation Sources

You can download the following ZIP file containing all tutorial files from here:

You find the custom error case example containing the example implementation classes as well as required property files in the error-case folder of the ZIP file accordingly.

Basic Setup of the custom ErrorCase

A custom error case can be created by simply extending the base class ErrorCase.

In our tutorial, we create a class ActivityInstanceMonitorErrorCase implementing the ErrorCase class. You find this class in the src/main/java/org/eclipse/stardust/examples/errorcases folder in the example zip file.

package org.eclipse.stardust.examples.errorcases;

import java.text.MessageFormat;

import org.eclipse.stardust.common.error.ErrorCase;
import org.eclipse.stardust.engine.api.runtime.BpmRuntimeErrorMessages;


public class ActivityInstanceMonitorErrorCase extends ErrorCase
{

   private static final long serialVersionUID = 1L;

   
   // Error Types
   public static final Args0 AIM_GENERIC_ERROR = newArgs0("CUST01000", ActivityInstanceMonitorErrorMessage.getString("CUST01000")); //$NON-NLS-1$ //$NON-NLS-2$
   public static final Args1 AIM_GENERIC_ERROR_WITH_SINGLE_ARG = newArgs1("CUST01010", ActivityInstanceMonitorErrorMessage.getString("CUST01010")); //$NON-NLS-1$ //$NON-NLS-2$   
   
   //
   
   private static final Object[] NONE = {};

   private final String defaultMessage;

   private final Object[] args;

   private ActivityInstanceMonitorErrorCase(String id)
   {
      this(id, null);
   }

   private ActivityInstanceMonitorErrorCase(String id, String defaultMessage)
   {
      this(id, defaultMessage, NONE);
   }

   private ActivityInstanceMonitorErrorCase(String code, String defaultMessage, Object msgArgs[])
   {
      super(code);

      this.defaultMessage = defaultMessage;
      this.args = msgArgs;
   }

   public String getDefaultMessage()
   {
      return defaultMessage;
   }

   public Object[] getMessageArgs()
   {
      return args;
   }

   public String toString()
   {
      return getId() + " - " + MessageFormat.format(getDefaultMessage(), args); //$NON-NLS-1$
   }
   
   public static Args0 newArgs0(String errorCode)
   {
      return new Args0(errorCode, BpmRuntimeErrorMessages.getString(errorCode));
   }


   public static Args0 newArgs0(String errorCode, String defaultMessage)
   {
      return new Args0(errorCode, defaultMessage);
   }   
   

   public static Args1 newArgs1(String errorCode)
   {
      return new Args1(errorCode, BpmRuntimeErrorMessages.getString(errorCode));
   }

   public static Args1 newArgs1(String errorCode, String defaultMessage)
   {
      return new Args1(errorCode, defaultMessage);
   }
   
   public static class Args0 extends AbstractErrorFactory
   {
      private Args0(String errorCode, String defaultMessage)
      {
         super(errorCode, defaultMessage);
      }

      public ActivityInstanceMonitorErrorCase raise()
      {
         return buildError(NONE);
      }
   }

   public static class Args1 extends AbstractErrorFactory
   {
      private Args1(String errorCode, String defaultMessage)
      {
         super(errorCode, defaultMessage);
      }

      public ActivityInstanceMonitorErrorCase raise(Object arg)
      {
         return buildError(new Object[] {arg});
      }

      public ActivityInstanceMonitorErrorCase raise(long arg)
      {
         return buildError(new Object[] {new Long(arg)});
      }
   }   
   
   static abstract class AbstractErrorFactory
   {
      private final String errorCode;

      private final String defaultMessage;

      protected AbstractErrorFactory(String errorCode, String defaultMessage)
      {
         this.errorCode = errorCode;
         this.defaultMessage = defaultMessage;
      }

      protected ActivityInstanceMonitorErrorCase buildError(Object[] args)
      {
         return new ActivityInstanceMonitorErrorCase(errorCode, defaultMessage, args);
      }

      public String getErrorCode()
      {
         return errorCode;
      }
   }   
   
}

The new ErrorCase can be utilized, by throwing a PublicExceptions with a new instance of the custom error case, for example:

new PublicException(new ActivityInstanceMonitorErrorCase("CUST-10001"));

This would result in a public Exception of the type ActivityInstanceMonitorErrorCase with the Error ID: CUST-10001.

Using custom ErrorCases in an advanced Implementation

The provided example of ActivityInstanceMonitorErrorCase shows a more advanced approach of instantiating error cases and providing messages and internationalization through resource bundles.

Setting Error Types

Different error types for this ErrorCase can be defined as final statics (constants) and instantiated through a factory method expecting a certain number of arguments (Args0, Args1, etc.). Create a resource bundle containing all clear text messages for a certain error type in the following format:

CUST01000 = A custom error with no Argument has been thrown!
CUST01010 = A custom error with one Argument has been thrown: {0}

The messages will be referenced by the error ID.

You find the example resource bundle in the ipp-activityinstancemonitor-errors.properties file in the example zip file. ipp-activityinstancemonitor-errors_de.properties is provided as example internationalized resource bundle.

Loading Messages through an Error Message Provider

The message (informational) text for a certain error type can be loaded through an error message provider, which loads the resource bundle from the classpath. In our example, we load org.eclipse.stardust.examples.errorcases.ipp-activityinstancemonitor-errors).

package org.eclipse.stardust.examples.errorcases;

import java.util.MissingResourceException;
import java.util.ResourceBundle;

public class ActivityInstanceMonitorErrorMessage
{
   private static final String BUNDLE_NAME = "org.eclipse.stardust.examples.errorcases.ipp-activityinstancemonitor-errors"; //$NON-NLS-1$
   private static ResourceBundle resourceBundle;
   
   private ActivityInstanceMonitorErrorMessage()
   {
   }

   public static String getString(String key)
   {
      try
      {
         if(resourceBundle == null)
         {
            resourceBundle = ResourceBundle.getBundle(BUNDLE_NAME);
         }
         
         return resourceBundle.getString(key);
      }
      catch (MissingResourceException e)
      {
         return '!' + key + '!';
      }
   }  
}

You find the example error message class ActivityInstanceMonitorErrorMessage in the example zip file in folder src/main/java/org/eclipse/stardust/examples/errorcases.

Throwing a Public Error with the specified Error Types

To throw a PublicError with one of the specific types, the call is made to the static variable of the type and by calling the raise method as follows:

ActivityInstanceMonitorErrorCase.AIM_GENERIC_ERROR_WITH_SINGLE_ARG.raise
("A custom error happened while suspending ActivityInstance <" + ai + ">"));

The parameter text is inserted into the corresponding place holder in the error type text.

The following example class throws a public exception using our specified types:

package org.eclipse.stardust.examples.errorcases;

import org.eclipse.stardust.common.error.PublicException;
import org.eclipse.stardust.common.log.LogManager;
import org.eclipse.stardust.common.log.Logger;
import org.eclipse.stardust.engine.api.query.WorklistQuery;
import org.eclipse.stardust.engine.api.runtime.ActivityInstanceState;
import org.eclipse.stardust.engine.core.runtime.beans.IActivityInstance;
import org.eclipse.stardust.engine.core.spi.monitoring.IActivityInstanceMonitor;

public class ActivityInstanceMonitorTest implements IActivityInstanceMonitor
{

   private static final Logger trace = LogManager.getLogger(WorklistQuery.class);

   @Override
   public void activityInstanceStateChanged(IActivityInstance ai, int state)
   {
      trace.info("Activating SPI for ActivityStateChange");

      if (state == ActivityInstanceState.COMPLETED)
      {         
         throw new PublicException(               
               ActivityInstanceMonitorErrorCase.AIM_GENERIC_ERROR.raise());
      }
      else if (state == ActivityInstanceState.SUSPENDED)
      {
         throw new PublicException(
               ActivityInstanceMonitorErrorCase.AIM_GENERIC_ERROR_WITH_SINGLE_ARG.raise("A custom error happened while suspending ActivityInstance <"
                     + ai + ">"));
      }

   }

}

You find the example public exception test class ActivityInstanceMonitorTest in the example zip file in folder src/main/java/org/eclipse/stardust/examples/errorcases.