BreakHandlerFactory.java

package net.secodo.jcircuitbreaker.breakhandler;

import static java.util.Objects.isNull;

import java.util.concurrent.Callable;

import net.secodo.jcircuitbreaker.breaker.ContextAwareCircuitBreaker;
import net.secodo.jcircuitbreaker.breaker.execution.ExecutionContext;
import net.secodo.jcircuitbreaker.breakhandler.exception.BreakHandlerException;
import net.secodo.jcircuitbreaker.breakstrategy.BreakStrategy;
import net.secodo.jcircuitbreaker.exception.CircuitBreakerException;
import net.secodo.jcircuitbreaker.exception.TaskExecutionException;
import net.secodo.jcircuitbreaker.task.Task;


/**
 * This is generic factory method interface. Each implementation should implement 
 * {@link #makeHandler(Task, ExecutionContext)} method which returns instance of a {@link BreakHandler}. The
 * returned instance should be shareable between different executions of the target-method (between execution of
 * different tasks). This means that the implementation should conform to one of following statements:
 * <ul>
 * <li>should be stateless - the returned handler should not allow modification of its internal state. This prevents
 * possible {@link java.util.ConcurrentModificationException} when accessed by different threads</li>
 * <li>should return new instance (but the instances may not share a common state via static fields etc. which may
 * result in {@link java.util.ConcurrentModificationException} when accessed by different threads)</li>
 * <li>may share state (or be the same instance) as long as any possible concurrent modifications are protected by
 * synchronization/locking mechanism, so that multiple threads can access the instance on the same time</li>
 * </ul>
 *
 * @param <R> the return type of onBreak method of the created {@link BreakHandler}. This must be the same return
 *            type as the of of executed Task
 */
public interface BreakHandlerFactory<R> extends BreakHandler<R> {
  @Override
  default R onBreak(ContextAwareCircuitBreaker<R> circuitBreaker, Task<R> task, BreakStrategy<R> breakStrategy,
                    ExecutionContext<R> executionContext) throws TaskExecutionException, CircuitBreakerException,
                                                                 BreakHandlerException {
    BreakHandler<R> handler = makeHandler(task, executionContext);
    if (isNull(handler)) {
      throw new BreakHandlerException("makeHandler() method returned handler which is null");
    }

    return handler.onBreak(circuitBreaker, task, breakStrategy, executionContext);
  }

  /**
   * Returns an instance of {@link BreakHandler} which should be used to handle the <i>break</i>.
   *
   * @param task             the task which is executed by this {@link BreakHandler}
   * @param executionContext contains current execution data (specific to current execution)
   * @return an instance of {@link BreakHandler}
   * @throws BreakHandlerException when the handler could not have been produced
   */
  BreakHandler<R> makeHandler(Task<R> task, ExecutionContext<R> executionContext) throws BreakHandlerException;
}