All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.testifyproject.failsafe.FailsafeConfig Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */
package org.testifyproject.failsafe;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;

import org.testifyproject.failsafe.event.ContextualResultListener;
import org.testifyproject.failsafe.function.CheckedBiConsumer;
import org.testifyproject.failsafe.function.CheckedBiFunction;
import org.testifyproject.failsafe.function.CheckedRunnable;
import org.testifyproject.failsafe.function.CheckedConsumer;
import org.testifyproject.failsafe.function.CheckedFunction;
import org.testifyproject.failsafe.internal.util.Assert;

/**
 * Failsafe configuration.
 * 
 * @author Jonathan Halterman
 * @param  result type
 * @param  failsafe type - {@link SyncFailsafe} or {@link AsyncFailsafe}
 */
@SuppressWarnings("unchecked")
public class FailsafeConfig {
  RetryPolicy retryPolicy = RetryPolicy.NEVER;
  CircuitBreaker circuitBreaker;
  CheckedBiFunction fallback;
  Listeners listeners;
  ListenerRegistry listenerRegistry;

  FailsafeConfig() {
  }

  FailsafeConfig(FailsafeConfig config) {
    retryPolicy = config.retryPolicy;
    circuitBreaker = config.circuitBreaker;
    fallback = config.fallback;
    listeners = config.listeners;
    listenerRegistry = config.listenerRegistry;
  }

  static class ListenerRegistry {
    private List> abortListeners;
    private List> completeListeners;
    private List> failedAttemptListeners;
    private List> failureListeners;
    private List> retriesExceededListeners;
    private List> retryListeners;
    private List> successListeners;

    List> abort() {
      return abortListeners != null ? abortListeners
          : (abortListeners = new ArrayList>(2));
    }

    List> complete() {
      return completeListeners != null ? completeListeners
          : (completeListeners = new ArrayList>(2));
    }

    List> failedAttempt() {
      return failedAttemptListeners != null ? failedAttemptListeners
          : (failedAttemptListeners = new ArrayList>(2));
    }

    List> failure() {
      return failureListeners != null ? failureListeners
          : (failureListeners = new ArrayList>(2));
    }

    List> retriesExceeded() {
      return retriesExceededListeners != null ? retriesExceededListeners
          : (retriesExceededListeners = new ArrayList>(2));
    }

    List> retry() {
      return retryListeners != null ? retryListeners
          : (retryListeners = new ArrayList>(2));
    }

    List> success() {
      return successListeners != null ? successListeners
          : (successListeners = new ArrayList>(2));
    }
  }

  static  void call(List> listeners, T result, Throwable failure,
      ExecutionContext context) {
    for (ContextualResultListener listener : listeners) {
      try {
        listener.onResult(result, failure, context);
      } catch (Exception ignore) {
      }
    }
  }

  /**
   * Registers the {@code listener} to be called when an execution is aborted.
   */
  public F onAbort(CheckedBiConsumer listener) {
    registry().abort().add(Listeners.of(listener));
    return (F) this;
  }

  /**
   * Registers the {@code listener} to be called when an execution is aborted.
   */
  public F onAbort(CheckedConsumer listener) {
    registry().abort().add(Listeners.of(listener));
    return (F) this;
  }

  /**
   * Registers the {@code listener} to be called when an execution is aborted.
   */
  public F onAbort(ContextualResultListener listener) {
    registry().abort().add((ContextualResultListener) Assert.notNull(listener, "listener"));
    return (F) this;
  }

  /**
   * Registers the {@code listener} to be called asynchronously on the {@code executor} when an execution is aborted.
   */
  public F onAbortAsync(CheckedBiConsumer listener, ExecutorService executor) {
    registry().abort().add(Listeners.of(Listeners.of(listener), Assert.notNull(executor, "executor"), null));
    return (F) this;
  }

  /**
   * Registers the {@code listener} to be called asynchronously on the {@code executor} when an execution is aborted.
   */
  public F onAbortAsync(CheckedConsumer listener, ExecutorService executor) {
    registry().abort().add(Listeners.of(Listeners.of(listener), Assert.notNull(executor, "executor"), null));
    return (F) this;
  }

  /**
   * Registers the {@code listener} to be called asynchronously on the {@code executor} when an execution is aborted.
   */
  public F onAbortAsync(ContextualResultListener listener, ExecutorService executor) {
    registry().abort().add(Listeners.of(listener, Assert.notNull(executor, "executor"), null));
    return (F) this;
  }

  /**
   * Registers the {@code listener} to be called when an execution is completed.
   */
  public F onComplete(CheckedBiConsumer listener) {
    registry().complete().add(Listeners.of(listener));
    return (F) this;
  }

  /**
   * Registers the {@code listener} to be called when an execution is completed.
   */
  public F onComplete(ContextualResultListener listener) {
    registry().complete().add((ContextualResultListener) Assert.notNull(listener, "listener"));
    return (F) this;
  }

  /**
   * Registers the {@code listener} to be called asynchronously on the {@code executor} when an execution is completed.
   */
  public F onCompleteAsync(CheckedBiConsumer listener, ExecutorService executor) {
    registry().complete().add(Listeners.of(Listeners.of(listener), Assert.notNull(executor, "executor"), null));
    return (F) this;
  }

  /**
   * Registers the {@code listener} to be called asynchronously on the {@code executor} when an execution is completed.
   */
  public F onCompleteAsync(ContextualResultListener listener,
      ExecutorService executor) {
    registry().complete().add(Listeners.of(listener, Assert.notNull(executor, "executor"), null));
    return (F) this;
  }

  /**
   * Registers the {@code listener} to be called when an execution attempt fails.
   */
  public F onFailedAttempt(CheckedBiConsumer listener) {
    registry().failedAttempt().add(Listeners.of(listener));
    return (F) this;
  }

  /**
   * Registers the {@code listener} to be called when an execution attempt fails.
   */
  public F onFailedAttempt(CheckedConsumer listener) {
    registry().failedAttempt().add(Listeners.of(listener));
    return (F) this;
  }

  /**
   * Registers the {@code listener} to be called when an execution attempt fails.
   */
  public F onFailedAttempt(ContextualResultListener listener) {
    registry().failedAttempt().add((ContextualResultListener) Assert.notNull(listener, "listener"));
    return (F) this;
  }

  /**
   * Registers the {@code listener} to be called asynchronously on the {@code executor} when an execution attempt fails.
   */
  public F onFailedAttemptAsync(CheckedBiConsumer listener,
      ExecutorService executor) {
    registry().failedAttempt().add(Listeners.of(Listeners.of(listener), Assert.notNull(executor, "executor"), null));
    return (F) this;
  }

  /**
   * Registers the {@code listener} to be called asynchronously on the {@code executor} when an execution attempt fails.
   */
  public F onFailedAttemptAsync(CheckedConsumer listener, ExecutorService executor) {
    registry().failedAttempt().add(Listeners.of(Listeners.of(listener), Assert.notNull(executor, "executor"), null));
    return (F) this;
  }

  /**
   * Registers the {@code listener} to be called asynchronously on the {@code executor} when an execution attempt fails.
   */
  public F onFailedAttemptAsync(ContextualResultListener listener,
      ExecutorService executor) {
    registry().failedAttempt().add(Listeners.of(listener, Assert.notNull(executor, "executor"), null));
    return (F) this;
  }

  /**
   * Registers the {@code listener} to be called when an execution fails and cannot be retried.
   */
  public F onFailure(CheckedBiConsumer listener) {
    registry().failure().add(Listeners.of(listener));
    return (F) this;
  }

  /**
   * Registers the {@code listener} to be called when an execution fails and cannot be retried.
   */
  public F onFailure(CheckedConsumer listener) {
    registry().failure().add(Listeners.of(listener));
    return (F) this;
  }

  /**
   * Registers the {@code listener} to be called when an execution fails and cannot be retried.
   */
  public F onFailure(ContextualResultListener listener) {
    registry().failure().add((ContextualResultListener) Assert.notNull(listener, "listener"));
    return (F) this;
  }

  /**
   * Registers the {@code listener} to be called asynchronously on the {@code executor} when an execution fails and
   * cannot be retried.
   */
  public F onFailureAsync(CheckedBiConsumer listener, ExecutorService executor) {
    registry().failure().add(Listeners.of(Listeners.of(listener), Assert.notNull(executor, "executor"), null));
    return (F) this;
  }

  /**
   * Registers the {@code listener} to be called asynchronously on the {@code executor} when an execution fails and
   * cannot be retried.
   */
  public F onFailureAsync(CheckedConsumer listener, ExecutorService executor) {
    registry().failure().add(Listeners.of(Listeners.of(listener), Assert.notNull(executor, "executor"), null));
    return (F) this;
  }

  /**
   * Registers the {@code listener} to be called asynchronously on the {@code executor} when an execution fails and
   * cannot be retried.
   */
  public F onFailureAsync(ContextualResultListener listener,
      ExecutorService executor) {
    registry().failure().add(Listeners.of(listener, Assert.notNull(executor, "executor"), null));
    return (F) this;
  }

  /**
   * Registers the {@code listener} to be called when an execution fails and the {@link RetryPolicy#withMaxRetries(int)
   * max retry attempts} or {@link RetryPolicy#withMaxDuration(long, java.util.concurrent.TimeUnit) max duration} are
   * exceeded.
   */
  public F onRetriesExceeded(CheckedBiConsumer listener) {
    registry().retriesExceeded().add(Listeners.of(listener));
    return (F) this;
  }

  /**
   * Registers the {@code listener} to be called when an execution fails and the {@link RetryPolicy#withMaxRetries(int)
   * max retry attempts} or {@link RetryPolicy#withMaxDuration(long, java.util.concurrent.TimeUnit) max duration} are
   * exceeded.
   */
  public F onRetriesExceeded(CheckedConsumer listener) {
    registry().retriesExceeded().add(Listeners.of(listener));
    return (F) this;
  }

  /**
   * Registers the {@code listener} to be called asynchronously on the {@code executor} when an execution fails and the
   * {@link RetryPolicy#withMaxRetries(int) max retry attempts} or
   * {@link RetryPolicy#withMaxDuration(long, java.util.concurrent.TimeUnit) max duration} are exceeded.
   */
  public F onRetriesExceededAsync(CheckedBiConsumer listener,
      ExecutorService executor) {
    registry().retriesExceeded().add(Listeners.of(Listeners.of(listener), Assert.notNull(executor, "executor"), null));
    return (F) this;
  }

  /**
   * Registers the {@code listener} to be called asynchronously on the {@code executor} when an execution fails and the
   * {@link RetryPolicy#withMaxRetries(int) max retry attempts} or
   * {@link RetryPolicy#withMaxDuration(long, java.util.concurrent.TimeUnit) max duration} are exceeded.
   */
  public F onRetriesExceededAsync(CheckedConsumer listener, ExecutorService executor) {
    registry().retriesExceeded()
        .add(Listeners.of(Listeners.of(listener), Assert.notNull(executor, "executor"), null));
    return (F) this;
  }

  /**
   * Registers the {@code listener} to be called before an execution is retried.
   */
  public F onRetry(CheckedBiConsumer listener) {
    registry().retry().add(Listeners.of(listener));
    return (F) this;
  }

  /**
   * Registers the {@code listener} to be called before an execution is retried.
   */
  public F onRetry(CheckedConsumer listener) {
    registry().retry().add(Listeners.of(listener));
    return (F) this;
  }

  /**
   * Registers the {@code listener} to be called before an execution is retried.
   */
  public F onRetry(ContextualResultListener listener) {
    registry().retry().add((ContextualResultListener) Assert.notNull(listener, "listener"));
    return (F) this;
  }

  /**
   * Registers the {@code listener} to be called asynchronously on the {@code executor} before an execution is retried.
   */
  public F onRetryAsync(CheckedBiConsumer listener, ExecutorService executor) {
    registry().retry().add(Listeners.of(Listeners.of(listener), Assert.notNull(executor, "executor"), null));
    return (F) this;
  }

  /**
   * Registers the {@code listener} to be called asynchronously on the {@code executor} before an execution is retried.
   */
  public F onRetryAsync(CheckedConsumer listener, ExecutorService executor) {
    registry().retry().add(Listeners.of(Listeners.of(listener), Assert.notNull(executor, "executor"), null));
    return (F) this;
  }

  /**
   * Registers the {@code listener} to be called asynchronously on the {@code executor} before an execution is retried.
   */
  public F onRetryAsync(ContextualResultListener listener, ExecutorService executor) {
    registry().retry().add(Listeners.of(listener, Assert.notNull(executor, "executor"), null));
    return (F) this;
  }

  /**
   * Registers the {@code listener} to be called when an execution is successful.
   */
  public F onSuccess(CheckedBiConsumer listener) {
    registry().success().add(Listeners.ofResult(listener));
    return (F) this;
  }

  /**
   * Registers the {@code listener} to be called when an execution is successful.
   */
  public F onSuccess(CheckedConsumer listener) {
    registry().success().add(Listeners.ofResult(listener));
    return (F) this;
  }

  /**
   * Registers the {@code listener} to be called asynchronously on the {@code executor} when an execution is successful.
   */
  public F onSuccessAsync(CheckedBiConsumer listener, ExecutorService executor) {
    registry().success().add(Listeners.of(Listeners.ofResult(listener), Assert.notNull(executor, "executor"), null));
    return (F) this;
  }

  /**
   * Registers the {@code listener} to be called asynchronously on the {@code executor} when an execution is successful.
   */
  public F onSuccessAsync(CheckedConsumer listener, ExecutorService executor) {
    registry().success().add(Listeners.of(Listeners.ofResult(listener), Assert.notNull(executor, "executor"), null));
    return (F) this;
  }

  /**
   * Configures the {@code circuitBreaker} to be used to control the rate of event execution.
   * 
   * @throws NullPointerException if {@code circuitBreaker} is null
   * @throws IllegalStateException if a circuit breaker is already configured
   */
  public F with(CircuitBreaker circuitBreaker) {
    Assert.state(this.circuitBreaker == null, "A circuit breaker has already been configured");
    this.circuitBreaker = Assert.notNull(circuitBreaker, "circuitBreaker");
    return (F) this;
  }

  /**
   * Configures the {@code listeners} to be called as execution events occur.
   * 
   * @throws NullPointerException if {@code listeners} is null
   */
  public  F with(Listeners listeners) {
    this.listeners = (Listeners) Assert.notNull(listeners, "listeners");
    return (F) this;
  }

  /**
   * Configures the {@code retryPolicy} to be used for retrying failed executions.
   * 
   * @throws NullPointerException if {@code retryPolicy} is null
   * @throws IllegalStateException if a retry policy is already configured
   */
  public F with(RetryPolicy retryPolicy) {
    Assert.state(this.retryPolicy == RetryPolicy.NEVER, "A retry policy has already been configurd");
    this.retryPolicy = Assert.notNull(retryPolicy, "retryPolicy");
    return (F) this;
  }

  /**
   * Configures the {@code fallback} action to be executed if execution fails.
   * 
   * @throws NullPointerException if {@code fallback} is null
   * @throws IllegalStateException if {@code withFallback} method has already been called
   */
  @SuppressWarnings("rawtypes")
  public F withFallback(Callable fallback) {
    return (F) withFallback((CheckedBiFunction) Functions.fnOf((Callable) Assert.notNull(fallback, "fallback")));
  }

  /**
   * Configures the {@code fallback} action to be executed if execution fails.
   * 
   * @throws NullPointerException if {@code fallback} is null
   * @throws IllegalStateException if {@code withFallback} method has already been called
   */
  @SuppressWarnings("rawtypes")
  public F withFallback(CheckedBiConsumer fallback) {
    return (F) withFallback(
        (CheckedBiFunction) Functions.fnOf((CheckedBiConsumer) Assert.notNull(fallback, "fallback")));
  }

  /**
   * Configures the {@code fallback} action to be executed if execution fails.
   * 
   * @throws NullPointerException if {@code fallback} is null
   * @throws IllegalStateException if {@code withFallback} method has already been called
   */
  public F withFallback(CheckedBiFunction fallback) {
    Assert.state(this.fallback == null, "withFallback has already been called");
    this.fallback = (CheckedBiFunction) Assert.notNull(fallback, "fallback");
    return (F) this;
  }

  /**
   * Configures the {@code fallback} action to be executed if execution fails.
   * 
   * @throws NullPointerException if {@code fallback} is null
   * @throws IllegalStateException if {@code withFallback} method has already been called
   */
  @SuppressWarnings("rawtypes")
  public F withFallback(CheckedConsumer fallback) {
    return (F) withFallback(
        (CheckedBiFunction) Functions.fnOf((CheckedConsumer) Assert.notNull(fallback, "fallback")));
  }

  /**
   * Configures the {@code fallback} action to be executed if execution fails.
   * 
   * @throws NullPointerException if {@code fallback} is null
   * @throws IllegalStateException if {@code withFallback} method has already been called
   */
  @SuppressWarnings("rawtypes")
  public F withFallback(CheckedFunction fallback) {
    return (F) withFallback(
        (CheckedBiFunction) Functions.fnOf((CheckedFunction) Assert.notNull(fallback, "fallback")));
  }

  /**
   * Configures the {@code fallback} action to be executed if execution fails.
   * 
   * @throws NullPointerException if {@code fallback} is null
   * @throws IllegalStateException if {@code withFallback} method has already been called
   */
  @SuppressWarnings("rawtypes")
  public F withFallback(CheckedRunnable fallback) {
    return (F) withFallback((CheckedBiFunction) Functions.fnOf(Assert.notNull(fallback, "fallback")));
  }

  /**
   * Configures the {@code fallback} result to be returned if execution fails.
   * 
   * @throws NullPointerException if {@code fallback} is null
   * @throws IllegalStateException if {@code withFallback} method has already been called
   */
  @SuppressWarnings("rawtypes")
  public F withFallback(R fallback) {
    return (F) withFallback((CheckedBiFunction) Functions.fnOf(Assert.notNull(fallback, "fallback")));
  }

  void handleAbort(R result, Throwable failure, ExecutionContext context) {
    if (listenerRegistry != null && listenerRegistry.abortListeners != null) {
      context = context.copy();
      call(listenerRegistry.abortListeners, result, failure, context);
    }

    if (listeners != null) {
      try {
        listeners.onAbort(result, failure);
      } catch (Exception ignore) {
      }
      try {
        listeners.onAbort(result, failure, context);
      } catch (Exception ignore) {
      }
    }
  }

  void handleComplete(R result, Throwable failure, ExecutionContext context, boolean success) {
    if (success)
      handleSuccess(result, context);
    else
      handleFailure(result, failure, context);
    handleComplete(result, failure, context);
  }

  void handleFailedAttempt(R result, Throwable failure, ExecutionContext context) {
    if (listenerRegistry != null && listenerRegistry.failedAttemptListeners != null) {
      context = context.copy();
      call(listenerRegistry.failedAttemptListeners, result, failure, context);
    }

    if (listeners != null) {
      try {
        listeners.onFailedAttempt(result, failure);
      } catch (Exception ignore) {
      }
      try {
        listeners.onFailedAttempt(result, failure, context);
      } catch (Exception ignore) {
      }
    }
  }

  void handleRetriesExceeded(R result, Throwable failure, ExecutionContext context) {
    if (listenerRegistry != null && listenerRegistry.retriesExceededListeners != null) {
      context = context.copy();
      call(listenerRegistry.retriesExceededListeners, result, failure, context);
    }

    if (listeners != null) {
      try {
        listeners.onRetriesExceeded(result, failure);
      } catch (Exception ignore) {
      }
    }
  }

  void handleRetry(R result, Throwable failure, ExecutionContext context) {
    if (listenerRegistry != null && listenerRegistry.retryListeners != null) {
      context = context.copy();
      call(listenerRegistry.retryListeners, result, failure, context);
    }

    if (listeners != null) {
      try {
        listeners.onRetry(result, failure);
      } catch (Exception ignore) {
      }
      try {
        listeners.onRetry(result, failure, context);
      } catch (Exception ignore) {
      }
    }
  }

  ListenerRegistry registry() {
    return listenerRegistry != null ? listenerRegistry : (listenerRegistry = new ListenerRegistry());
  }

  private void handleComplete(R result, Throwable failure, ExecutionContext context) {
    if (listenerRegistry != null && listenerRegistry.completeListeners != null) {
      context = context.copy();
      call(listenerRegistry.completeListeners, result, failure, context);
    }

    if (listeners != null) {
      try {
        listeners.onComplete(result, failure);
      } catch (Exception ignore) {
      }
      try {
        listeners.onComplete(result, failure, context);
      } catch (Exception ignore) {
      }
    }
  }

  private void handleFailure(R result, Throwable failure, ExecutionContext context) {
    if (listenerRegistry != null && listenerRegistry.failureListeners != null) {
      context = context.copy();
      call(listenerRegistry.failureListeners, result, failure, context);
    }

    if (listeners != null) {
      try {
        listeners.onFailure(result, failure);
      } catch (Exception ignore) {
      }
      try {
        listeners.onFailure(result, failure, context);
      } catch (Exception ignore) {
      }
    }
  }

  private void handleSuccess(R result, ExecutionContext context) {
    if (listenerRegistry != null && listenerRegistry.successListeners != null) {
      context = context.copy();
      call(listenerRegistry.successListeners, result, null, context);
    }

    if (listeners != null) {
      try {
        listeners.onSuccess(result);
      } catch (Exception ignore) {
      }
      try {
        listeners.onSuccess(result, context);
      } catch (Exception ignore) {
      }
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy