How to cope with many Failure causes (list exceptions, exception treeing)

There are some cases, when a Failure does not have only one cause. This typically occurs, when processing a list of items, each of them being necessary for the success of the complete operation.

For example we want to copy a list of files to a backup directory. Only if we succeed to copy every file, the whole method can be considered successful.

The first approach is

    /**Copies all files from directory sourceDir to directory destDir.*/
    public static void copyFiles1(final File sourceDir, final File destDir){
        try {
            final File[] files = sourceDir.listFiles();
            for(final File file: files){
                copyOneFile(file, destDir);
            }
        } catch (Exception e) {
            throw create(CopyFilesFailure1.class, sourceDir, destDir);
        }
    }

This method succeeds only, if all files can be successfully copied from the source directory to the destination directory. The first failing copying of a single file will cause the abrupt termination of the complete operation. The resulting CopyFilesFailure exception will have as cause only the exception of the first file copy failure.

Sometimes we need the exception of each failed single item. For example a compiler always works like this. It collects error messages, but goes on analyzing the source text. The compilation will fail, if there is at least one error, but all errrors will be reported.

If we want our copy files example to handle errors like this, we must collect the individual exceptions before creating the CopyFilesFailure2. Then we can pass in this collection to the polymorphic Object[] parameters. This is best done by using the constructor Failure(String,Collection) with a null String. If we want to pass in normal message parameters, too, as in our example sourceDir and destDir, we should add them to the collection before the exceptions. Then they have a fixed index for the message pattern. The following example with an exception collection is a bit longer, but gives much more diagnostic information.

/**Copies all files from directory sourceDir to directory destDir.*/
public static void copyFiles2(final File sourceDir, final File destDir){
    final Collection<Object> parameters = new ArrayList<Object>();
    parameters.add(sourceDir);
    parameters.add(destDir);
    final File[] files = sourceDir.listFiles();
    for(final File file: files){
        try {
            copyOneFile(file, destDir);
        } catch (Exception e) {
            parameters.add(e);
        }
    }
    if (parameters.size()>=2) {
        throw new CopyFilesFailure2(parameters);
    }        
}

/**Failure copying files from directory {0} to directory {1}*/
public static final class CopyFilesFailure2 extends Failure {
    public CopyFilesFailure2(Collection i_parameters) {
        super((String)null, i_parameters);
    }
}

By the way, giving each exception the potential of a list of causing exceptions, results in spanning a causal tree of exceptions. The standard MulTEx services for printing an exception will express the causal tree by indenting the lower exceptions by a + sign for each level.

Caution

The standard Java services for printing an exception (Throwable.toString() and Throwable.printStackTrace) will ignore causal trees built like this. So be certain, that you have installed centralized exception reporting using MulTEx services.