OnePerExecutionHandlerFactory.java

package net.secodo.jcircuitbreaker.breakhandler;

import net.secodo.jcircuitbreaker.breaker.CircuitBreaker;
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.task.Task;


/**
 * An implementation of {@link BreakHandlerFactory} which creates new instance of given {@link BreakHandler} if it
 * does not exist in current execution context, otherwise returns {@link BreakHandler} bound to current execution
 * context (reuses the same instance for handling subsequent calls to
 * {@link BreakHandler#onBreak(ContextAwareCircuitBreaker, Task, BreakStrategy, ExecutionContext)}).
 *
 * <p>NOTE: Each call to {@link CircuitBreaker#execute(Task, BreakStrategy, BreakHandler)} method creates new
 * context.
 * If implementation of {@link OnePerExecutionHandlerFactory} is used as a {@link BreakHandler}, "one and only one"
 * instance of given {@link BreakHandler} is created for per execution context (this is guaranteed by default
 * implementation of {@link #makeHandler(Task, ExecutionContext)} provided by this interface)
 */
public interface OnePerExecutionHandlerFactory<R> extends BreakHandlerFactory<R> {
  @Override
  default BreakHandler<R> makeHandler(Task<R> task, ExecutionContext<R> executionContext)
                               throws BreakHandlerException {
    String executionContextParamName = getOnePerExecutionContextParamName(task, executionContext);

    if (!executionContext.hasContextAttribute(executionContextParamName)) {
      // no retry handler was create for current execution so we need to create it

      BreakHandler<R> breakHandler = createNewHandler(task, executionContext);
      executionContext.setContextAttribute(executionContextParamName, breakHandler);
    }

    return executionContext.getContextAttribute(executionContextParamName);
  }

  /**
   * Constructs and returns new {@link BreakHandler}. This method is invoked break handler has not yet been created in
   * current execution context.
   *
   * @param task the originally executed task
   * @param executionContext current execution context
   * @return new instance of {@link BreakHandler}
   */
  BreakHandler<R> createNewHandler(Task<R> task, ExecutionContext<R> executionContext);

  /**
   * Returns execution context parameter name under which produced break handler will be stored.
   *
   * @param task the originally executed task
   * @param executionContext current execution context
   *
   * @see ExecutionContext#getContextAttribute(String)
   * @return a String representing attribute name
   */
  default String getOnePerExecutionContextParamName(Task<R> task, ExecutionContext<R> executionContext) {
    return "one_per_execution_handler_factory_" + this.getClass().getName();
  }
}