聚合异常

时间:2011-05-09 15:49:40

标签: java validation exception-handling

我经常发现需要验证一组条件,而不是提前失败(在第一个条件未满足时返回false或抛出异常),我需要聚合结果并报告各个失败。

我目前要么使用带有自定义条目的列表(基本上是一个条目包含失败的类型和一些信息性消息)或某种观察者(也只是聚合失败),但我有一种感觉这应该是一个常见的问题,应该有一些现有的模式来解决这个问题。

3 个答案:

答案 0 :(得分:9)

是的,这是一个常见的问题,而且你的方法都很好。

javax.validation.Validator,这是java验证的标准,使用前者。它返回Set ConstraintViolations

如果它适合您的情况,我建议使用javax.validation而不是自定义。这是一个包含多个提供程序的规范,其中一个是hibernate-validator(不需要使用hibernate来使用验证项目)

答案 1 :(得分:1)

我认为您不需要复杂的解决方案。当我必须这样做时,我通常会写一些类似的东西:

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 ...

或者,如果我知道我将要处理的错误是显示一条大消息,我可能只是将错误字段设为字符串并继续以任何格式向其添加消息。

答案 2 :(得分:1)

我使用以下类来收集和显示几个异常。它只使用标准的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");
        }
    }


}