开发者

Aggregate exceptions

I'm often finding the need to validate a set of conditions, and instead of failing early (returning false or throwing an exception when the first condition is not fulfilled), I need to aggregate the results and report the individual failures.

I'm currently either using a list with custom entries (basically an entry consists of the type of the failure and some informative message) or some kind of observer (which also just aggregates the failures), but I have a feeling that this should be a common problem and that there should be some existing pattern for solving this.开发者_JAVA百科


Yes, it is a common problem, and both your approaches are good.

javax.validation.Validator, which is the standard for java validation, uses the former. It returns a Set of ConstraintViolationss

If it fits your case, I would recommend using javax.validation instead of something custom. It's a spec with multiple providers, one of which is hibernate-validator (no need to use hibernate to use the validation project)


I don't think you need a complex solution. When I have to do this, I generally just write something like:

List<String> errors=new ArrayList<String>();
...
if (foo<0)
  errors.add("Bad foo");
if (!bar.contains(plugh))
  errors.add("No plugh in bar");
... etc, whatever other errors ...
... then at the bottom ...
if (errors.size()>0)
{
  ... throw exception, display errors, whatever ...
}
... else celebrate and get on with it ...

Or if I know that all I'm going to do with the errors is display one big message, I may just make the error field a string and keep appending messages to it in whatever format.


I use following class to collect and display several exceptions. It make only use of standard java.

package util;

import java.io.ByteArrayOutputStream;
import java.io.IOError;
import java.io.IOException;
import java.io.PrintStream;
import java.util.*;

/**
 * This abstract class is to be used for Exception generating by a collection of causes.
 * <p />
 * Typically: several tries take place to do something in different ways and each one fails. We therefore
 * have to collect the exceptions to document why it was not possible at all to do the thing.
 */
public abstract class AggregateException extends Exception
{
    /** A generator of random numbers */
    private final static Random rand = new Random();

    /** The causes of the exception */
    private final Vector<Throwable> causes;

    /** A (reasonably unique) id for this exception. Used for a better output of the stacktraces */
    private final long id = rand.nextLong();

    /**
     * @see Exception#Exception(String)
     * @param message
     */
    public AggregateException(String message, Collection<? extends Throwable> causes)
    {
        super(message);
        this.causes = new Vector<Throwable>(causes);
    }

    /**
     * Prints this throwable and its backtrace to the specified print stream.
     *
     * @param s <code>PrintStream</code> to use for output
     */
    public void printStackTrace(PrintStream s) {
        synchronized (s) {
            s.println(this);
            StackTraceElement[] trace = getStackTrace();
            for (int i=0; i < trace.length; i++)
                s.println("\tat " + trace[i]);

            final Throwable ourCause = getCause();
            if (ourCause != null)
                throw new AssertionError("The cause of an AggregateException should be null");

            for (int i = 0; i<causes.size(); i++)
            {
                final Throwable cause = causes.get(i);
                s.println(String.format(
                        "Cause number %s for AggregateException %s: %s ",
                        i,
                        getId(),
                        cause.toString()
                ));

                final ByteArrayOutputStream byteArrayOS = new ByteArrayOutputStream();
                final PrintStream ps = new PrintStream(byteArrayOS);
                cause.printStackTrace(ps);
                ps.close();
                final String causeStackTrace = byteArrayOS.toString();
                int firstCR = causeStackTrace.indexOf("\n");

                s.append(causeStackTrace.substring(firstCR == -1 ? 0 : firstCR+1));
            }
        }
    }

    @Override
    public String toString()
    {
        return String.format(
                "%s. AggregateException %s with %s causes.",
                super.toString(),
                getId(),
                causes.size()
                        );
    }

    @Override
    public Throwable initCause(Throwable cause)
    {
        if (cause != null)
            throw new AssertionError("The cause of an AggregateException must be null");

        return null;
    }

    /**
     *
     * @return {@link #id}
     */
    private String getId ()
    {
        return String.format("%xs", id);
    }

    /**
     * Test class
     */
    public static class TestException extends AggregateException
    {
        /**
         * Constructor
         * @param message
         * @param causes
         */
        public TestException(String message, Collection<? extends Throwable> causes)
        {
            super(message, causes);
        }

        /**
         * Test program
         *
         * @param notused
         * @throws AggregateException
         */
        public static void main (final String[] notused) throws AggregateException
        {
            final List<Error> causes = new LinkedList<Error> ();
            causes.add(new OutOfMemoryError());
            try
            {
                generateIOError();
            }
            catch (final Error th)
            {
                causes.add(th);
            }

            final AggregateException ae = new TestException("No test has sucessed", causes);

            throw ae;
        }

        /**
         * For test: generate an IOError caused by an IOException
         */
        private static void generateIOError()
        {
            try
            {
                generateIOException();
            }
            catch (final IOException ioex)
            {
                throw new IOError(ioex);
            }
        }

        /**
         * For test: throws an IOException
         * @throws IOException
         */
        private static void generateIOException() throws IOException
        {
            throw new IOException("xxx");
        }
    }


}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜