Chapter 106. How to handle errors in activities

106.1. How to handle errors in activities that are caused by the client
106.2. How to handle errors in activities that arise in the server
106.3. How to handle activity termination errors
106.4. How to handle errors in pipes
106.5. How to handle errors with binary and character inputs
106.6. An example

There are three types of error that can arise when an activity is processing. Each of these has an exception associated with it that the activity can throw. In addition the pipes used by activities for their inputs and outputs can also throw exceptions. In this section we describe what exceptions should be used when and how to handle them.

106.1. How to handle errors in activities that are caused by the client

Errors of this type includes:

  • The client provides an input that is incorrect e.g. some column index is out of bounds, an SQL query is syntactically incorrect, a directory they name does not exist etc.
  • The client has connected the input of this activity to the output of another activity and that other activity is providing data blocks that are not of the expected type.

In such conditions activities should raise an exception that is wrapped within an exception of type:

uk.org.ogsadai.activity.ActivityUserException

106.2. How to handle errors in activities that arise in the server

There are error that may not be the client's fault. For example, there is a problem using a server-side resource, the activity is not configured correctly, some server-side component experiences problems. In such conditions activities should raise an exception that is wrapped within an exception of type:

uk.org.ogsadai.activity.ActivityProcessingException

106.3. How to handle activity termination errors

An activity which is providing input to this activity is terminated. This will be indicated by the throwing of a PipeTerminatedException when an attempt is made to use the block reader corresponding to the input pipe (we discuss this further below).

In such conditions activities should raise an exception that is wrapped within an exception of type:

uk.org.ogsadai.activity.ActivityTerminatedException

It is important that PipeTerminatedException caught and is mapped to a ActivityTerminatedException so that the activity status can record that it terminated as a consequence of another activity rather than something occuring within it itself.

106.4. How to handle errors in pipes

The pipe interfaces:

uk.org.ogsadai.activity.io.BlockReader
uk.org.ogsadai.activity.io.BlockWriter

can throw exceptions when an activity reads and writes data. These exceptions are as follows:

uk.org.ogsadai.activity.io.PipeClosedException

This exception is thrown if the activity at the other end of the pipe has indicated that it wants no more data. In this situations most activities simply stop producing their data.

uk.org.ogsadai.activity.io.PipeIOException

This exception is thrown if a problem occurs when using the pipe that is not due to it being closed or terminated.

uk.org.ogsadai.activity.io.PipeTerminatedException

This exception is thrown if the request to which the pipe belongs is terminated prematurely due to an error elsewhere.

BlockReader can throw:

  • PipeIOException
  • PipeTerminatedException

BlockWriter can throw:

  • PipeClosedException
  • PipeIOException
  • PipeTerminatedException

You will never have to throw a pipe exception but your activities will have to handle them if it has inputs or outputs. How these exceptions are handled by an activity affects how the activity behaves. In, particular, activities are typically notified that the are to terminate by receiving a PipeTerminatedException. We therefore recommend the following when handling pipe exceptions:

  • PipeClosedException - iterative activities that sub-class uk.org.ogsadai.activity.MatchedIterativeActivity may want to invoke iterativeStageComplete(). Other activities may just swallow the exception.
  • PipeIOException - should be caught and wrapped and rethrown as an uk.org.ogsadai.activity.io.ActivityPipeProcessingException. This is a sub-class of ActivityProcessingException ,
  • PipeTerminatedException - should be caught and an ActivityTerminatedExxception thrown.

106.5. How to handle errors with binary and character inputs

In section Chapter 107, How to declare activity inputs for matched activities we describe the various ways to declare activity inputs for matched activities and also how inputs are provided to these activities. For character or binary data you will be given a reference to a java.io.Reader or a java.io.InputStream.

If this is the canse then, if an error occurs it is important not to close these objects. These objects wrap the associated input pipes and so closing them can lead to problems.

If an error has occured just throw an appropriate exception. The activity framework will close all the inputs and outputs in such a way that the error is correctly propagated through the workflow.

106.6. An example

Here is an example of how errors are handled in:

uk.org.ogsadai.activity.files.ReadFromFileActivity

try
{
...
       
}        
catch (FileResourceUseException e) 
{
    // The client doesn't have permission to access the file.
    // Client's fault for trying to access it.
    throw new ActivityUserException(e);
} 
catch (FileNotFoundException e) 
{
    // The file is not found or is a directory.
    // Client's fault for specifying this.
    throw new ActivityUserException(e);
} 
catch (PipeClosedException e)
{
    // No more output wanted, so ignore.
    iterativeStageComplete();
}
catch (PipeIOException e)
{
    // Something internal went wrong with input or output pipe.
    throw new ActivityPipeProcessingException(e);
}
catch (PipeTerminatedException e)
{
    // The input or output pipe was terminated.
    throw new ActivityTerminatedException();
}
catch (IOException e)
{
    // Something went wrong when using the file that was
    // not the client's fault e.g. a problem with the file system.
    throw new ActivityProcessingException(ErrorID.GENERAL_IO_ERROR, e);
}