Совокупные исключения - PullRequest
10 голосов
/ 09 мая 2011

Я часто нахожу необходимость проверки набора условий, и вместо того, чтобы сбоить рано (возвращая false или выбрасывая исключение, когда первое условие не выполнено), мне нужно агрегировать результаты и сообщать об отдельных ошибках.

В настоящее время я использую список с пользовательскими записями (в основном запись состоит из типа сбоя и некоторого информативного сообщения) или какой-то наблюдатель (который также просто объединяет ошибки), но у меня есть чувство, это должно быть распространенной проблемой, и для решения этой проблемы должен существовать некоторый шаблон.

Ответы [ 3 ]

9 голосов
/ 09 мая 2011

Да, это общая проблема, и оба ваших подхода хороши.

javax.validation.Validator, который является стандартом для проверки Java, использует первый.Он возвращает Set из ConstraintViolations s

. Если это соответствует вашему случаю, я бы рекомендовал использовать javax.validation вместо чего-то нестандартного.Это спецификация с несколькими провайдерами, одним из которых является hibernate-validator (нет необходимости использовать hibernate для использования проекта проверки)

1 голос
/ 30 октября 2012

Я использую следующий класс для сбора и отображения нескольких исключений. Он использует только стандартную 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");
        }
    }


}
1 голос
/ 09 мая 2011

Не думаю, что вам нужно комплексное решение.Когда мне нужно это сделать, я обычно просто пишу что-то вроде:

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

Или, если я знаю, что все, что я собираюсь сделать с ошибками, это отображение одного большого сообщения, я могу просто сделать ошибкувведите строку и продолжайте добавлять к ней сообщения в любом формате.

...