Совокупные исключения
Я часто нахожу необходимость проверки набора условий и вместо неудачного раннего (возвращающ false или бросаю исключение, когда первое условие не выполняется), мне нужно агрегировать результаты и сообщать о отдельных сбоях.
В настоящее время я использую список с пользовательскими записями (в основном запись состоит из типа сбоя и некоторого информационного сообщения) или какого-то наблюдателя (который также просто объединяет сбои), но у меня возникает ощущение, что это должно быть общей проблемой и что для ее решения должна быть какая-то существующая модель.
Ответы
Ответ 1
Да, это обычная проблема, и оба подхода хороши.
javax.validation.Validator
, который является стандартом для проверки Java, использует первое. Он возвращает Set
of ConstraintViolations
s
Если это соответствует вашему делу, я бы рекомендовал вместо javax.validation
использовать javax.validation
. Это спецификация с несколькими провайдерами, одна из которых hibernate-validator (нет необходимости использовать спящий режим для использования проекта проверки)
Ответ 2
Я не думаю, что вам нужно комплексное решение. Когда я должен это сделать, я обычно пишу что-то вроде:
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 ...
Или, если я знаю, что все, что я собираюсь делать с ошибками, отображает одно большое сообщение, я могу просто сделать поле ошибки строкой и продолжать добавлять к нему сообщения в любом формате.
Ответ 3
Я использую следующий класс для сбора и отображения нескольких исключений. Он использует только стандартную 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");
}
}
}