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.