FailureHandler.java
package de.uka.ipd.sdq.beagle.core.failurehandling;
import org.apache.commons.lang3.Validate;
import java.util.function.Supplier;
/**
* Handler for any failures that may occur while running Beagle. Clients may obtain an
* instance of this class to report that an exceptional situation occurred.
*
* <p>Failures reported through this API may be <em>recoverable</em>. This means that
* Beagle’s execution does not have to be stopped and that there is a way to continue
* without or to retry the failed action. Recoverable failures are handled by recover
* functions that may optionally return a value to continue with after the failure.
*
* <p>The reaction to failures can be configured by setting a provider for
* {@link FailureResolver FailureResolvers} through {@linkplain #setProvider(Supplier)}.
* The resolvers provided by the provider will be used when a failure is handled by this
* class.
*
* <p>Given that the class has a failure handler defined in the constant
* {@code FAILURE_HANDLER}, these are some usage examples:
*
* <pre>
* <code>
* public ReturnType myMethod(ParameterType1 param1, ParameterType2 param2) {
* // code
* try {
* // code that may fail
* } catch (MyExceptionType exception) {
* final FailureReport<ReturnTyp> failure = new FailureReport<>()
* .message("doing something with %s and %s failed", param1, param2)
* .cause(exception)
* .recoverable()
* .retryWith(() -< myMethod(param1, param2));
* return FAILURE_HANDLER.handle(failure);
* }
* // more code
* }
* </code>
* </pre>
*
* <pre>
* <code>
* public ReturnType myMethod(ParameterType1 param1, ParameterType2 param2) {
* // code
* if (failed) {
* final FailureReport<Void> failure = new FailureReport<>()
* .message("doing something with %s and %s failed", param1, param2)
* .details("We tried this and that, but doing so was not possible")
* .continueWith(this::otherMethod);
* FAILURE_HANDLER.handle(failure);
* return null;
* }
* // more code
* }
* </code>
* </pre>
*
* @author Joshua Gleitze
*/
public final class FailureHandler {
/**
* The provider of handlers.
*/
private static Supplier<FailureResolver> provider = ExceptionThrowingFailureResolver::new;
/**
* Name of the client this handle was created for.
*/
private final String clientName;
/**
* Creates a handler for the client identifed by {@code clientName}.
*
* @param clientName The name of the client creating this handler.
*/
private FailureHandler(final String clientName) {
this.clientName = clientName;
}
/**
* Creates a handler a client identified by {@code clientName} may report failures to.
*
* @param clientName A name identifying the client that may report failures to this
* handler. Must not be {@code null}.
* @return A handler for failures of a client called {@code clientName}.
*/
public static FailureHandler getHandler(final String clientName) {
Validate.notNull(clientName);
return new FailureHandler(clientName);
}
/**
* Creates a handler instances of {@code clientType} may report failures to.
*
* @param clientType Class of the clients that may report failures to this handler.
* Must not be {@code null}.
* @return A handler for failures of clients assignable to {@code clientType}.
*/
public static FailureHandler getHandler(final Class<?> clientType) {
Validate.notNull(clientType);
return new FailureHandler(clientType.getSimpleName());
}
/**
* Sets the provider of failure resolvers. The provider will be used to generate a
* resolver when a failure is reported until another provider is set through this
* method.
*
* @param provider A factory for resolvers. Must not be {@code null}.
*/
public static void setProvider(final Supplier<FailureResolver> provider) {
Validate.notNull(provider);
FailureHandler.provider = provider;
}
/**
* Reports a failure to this handler and makes it take action. This may include
* calling the recover functions potentially provided through {@code report}. This
* method will return the value generated by the recover functions. If the
* {@code report} describes a non-recoverable failure, this method will not return.
* This method might also not return if the handler thinks it’s not appropriate to
* continue.
*
* <p>A call to this method will obtain a resolver from the provider set through
* {@link #setProvider(Supplier)}. This resolver will be called to handle the failure.
*
* @param <RECOVER_TYPE> The recover value’s type.
* @param report Information about the failure.
* @return The value the reporter should use instead of the one that was expected to
* be generated by the failed action. Is created by the recover functions in
* {@code report}.
*/
public <RECOVER_TYPE> RECOVER_TYPE handle(final FailureReport<RECOVER_TYPE> report) {
return provider.get().handle(report, this.clientName);
}
}