CircuitBreakerImpl implements CircuitBreaker<ET>, CircuitBreaker.ManualModeTask<ET> { protected void scheduleHalfOpenStateTimeout() { logger.debug("Scheduling half-open state to occur in {}. circuit_breaker_id={}", resetTimeoutDurationString, id); halfOpenScheduledFuture.set(scheduler.schedule(() -> { boolean stateChanged = false; synchronized (this) { if (State.CLOSED.equals(currentState)) { logger.debug( "Half-open state check - breaker is already CLOSED. Nothing to do. circuit_breaker_id={}", id); } else { logger.info( "Half-open state check - breaker is OPEN. The next call will be allowed through in a half-open " + "state. circuit_breaker_state_changed_to={}, circuit_breaker_id={}", "HALF_OPEN", id ); halfOpenAllowSingleCall.set(true); stateChanged = true; } } if (stateChanged) { notifyOnHalfOpenListeners(); } }, resetTimeoutNanos, TimeUnit.NANOSECONDS)); } CircuitBreakerImpl();  CircuitBreakerImpl(Optional<ScheduledExecutorService> scheduler,
                              Optional<ExecutorService> stateChangeNotificationExecutor,
                              Optional<Integer> maxConsecutiveFailuresAllowed,
                              Optional<Duration> resetTimeout,
                              Optional<Duration> callTimeout,
                              Optional<String> id,
                              Optional<BreakingEventStrategy<ET>> breakingEventStrategy,
                              Optional<BreakingExceptionStrategy> breakingExceptionStrategy); static Builder<E> newBuilder(); static Builder<E> newBuilder(BreakingEventStrategy<E> breakingEventStrategy); @Override ManualModeTask<ET> newManualModeTask(); @Override void throwExceptionIfCircuitBreakerIsOpen(); @Override void handleEvent(ET event); @Override void handleException(Throwable throwable); @Override CircuitBreaker<ET> originatingCircuitBreaker(); @Override CompletableFuture<ET> executeAsyncCall(Supplier<CompletableFuture<ET>> eventFutureSupplier); @Override ET executeBlockingCall(Callable<ET> eventSupplier); @Override CircuitBreakerImpl<ET> onClose(Runnable listener); @Override CircuitBreakerImpl<ET> onHalfOpen(Runnable listener); @Override CircuitBreakerImpl<ET> onOpen(Runnable listener); @Override String getId(); static final String DEFAULT_MAX_CONSECUTIVE_FAILURES_ALLOWED_SYSTEM_PROP_KEY; static final String DEFAULT_RESET_TIMEOUT_SECONDS_SYSTEM_PROP_KEY; static final int DEFAULT_MAX_CONSECUTIVE_FAILURES_ALLOWED; static final long DEFAULT_RESET_TIMEOUT_SECONDS; static final int FALLBACK_DEFAULT_MAX_CONSECUTIVE_FAILURES_ALLOWED; static final long FALLBACK_DEFAULT_RESET_TIMEOUT_SECONDS; static final BreakingEventStrategy<?> DEFAULT_BREAKING_EVENT_STRATEGY; static final BreakingExceptionStrategy DEFAULT_BREAKING_EXCEPTION_STRATEGY; }
@Test public void scheduleHalfOpenStateTimeout_schedules_task_to_occur_after_resetTimeoutNanos_nanoseconds() { ScheduledExecutorService schedulerMock = mock(ScheduledExecutorService.class); Whitebox.setInternalState(cbSpy, "scheduler", schedulerMock); long resetTimeout = 42; Whitebox.setInternalState(cbSpy, "resetTimeoutNanos", resetTimeout); cbSpy.scheduleHalfOpenStateTimeout(); verify(schedulerMock).schedule(any(Runnable.class), eq(resetTimeout), eq(TimeUnit.NANOSECONDS)); }
@Test public void throwExceptionIfCircuitBreakerIsOpen_throws_CircuitBreakerOpenException_if_circuit_is_OPEN() { Whitebox.setInternalState(cbSpy, "currentState", State.OPEN); Throwable cbExplosion = catchThrowable(cbSpy::throwExceptionIfCircuitBreakerIsOpen); assertThat(cbExplosion).isNotNull(); assertThat(cbExplosion).isInstanceOf(CircuitBreakerOpenException.class); verify(cbSpy, times(0)).scheduleHalfOpenStateTimeout(); }
@Test public void throwExceptionIfCircuitBreakerIsOpen_does_nothing_if_circuit_is_CLOSED() { Whitebox.setInternalState(cbSpy, "currentState", State.CLOSED); Throwable cbExplosion = catchThrowable(cbSpy::throwExceptionIfCircuitBreakerIsOpen); assertThat(cbExplosion).isNull(); verify(cbSpy, times(0)).scheduleHalfOpenStateTimeout(); }
@Test public void throwExceptionIfCircuitBreakerIsOpen_schedules_half_open_check_and_does_not_explode_if_circuit_is_half_open() { Whitebox.setInternalState(cbSpy, "currentState", State.OPEN); ((AtomicBoolean) Whitebox.getInternalState(cbSpy, "halfOpenAllowSingleCall")).set(true); Throwable cbExplosion = catchThrowable(cbSpy::throwExceptionIfCircuitBreakerIsOpen); assertThat(cbExplosion).isNull(); verify(cbSpy).scheduleHalfOpenStateTimeout(); }