package com.github.blemale.fuse;

import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.dsl.Disruptor;

import java.time.Duration;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.function.Supplier;

public class CircuitBreaker {
    public static class CircuitBreakerOpenException extends RuntimeException {}

    private final StateMachine stateMachine;
    private final RingBuffer<Event> ringBuffer;

    public CircuitBreaker(Condition condition, Duration cooldown) {
        stateMachine = new StateMachine(condition, cooldown);

        Disruptor<Event> disruptor = new Disruptor<>(Event::new, 1024, Executors.defaultThreadFactory());
        disruptor.handleEventsWith(stateMachine);
        disruptor.start();
        ringBuffer = disruptor.getRingBuffer();
    }

    public <T> CompletableFuture<T> execute(Supplier<CompletableFuture<T>> action) {
        try {
            if (stateMachine.isExecutionAllowed()) {
                return action.get().whenCompleteAsync(this::reportResult);
            } else {
                report(CallStatus.OPEN);
                CompletableFuture<T> f = new CompletableFuture<>();
                f.completeExceptionally(new CircuitBreakerOpenException());
                return f;
            }
        } catch (Throwable ex) {
            report(CallStatus.FAILURE);
            CompletableFuture<T> f = new CompletableFuture<>();
            f.completeExceptionally(ex);
            return f;
        }
    }

    private <T> void reportResult(T t, Throwable ex) {
        if (ex != null) {
            report(CallStatus.FAILURE);
        } else {
            report(CallStatus.SUCCESS);
        }
    }

    private void report(CallStatus callStatus) {
        ringBuffer.publishEvent((e, seq, s) -> e.setCallStatus(s), callStatus);
    }
}

@@ -874,16 +874,21 @@
  execute
+Async
 (Supplie
@@ -1518,32 +1518,429 @@
        %7D%0A    %7D%0A%0A
+    public %3CT%3E T execute(Supplier%3CT%3E action) %7B%0A        try %7B%0A            if (stateMachine.isExecutionAllowed()) %7B%0A                return action.get();%0A            %7D else %7B%0A                report(CallStatus.OPEN);%0A                throw new CircuitBreakerOpenException();%0A            %7D%0A        %7D catch (Throwable ex) %7B%0A            report(CallStatus.FAILURE);%0A            throw ex;%0A        %7D%0A    %7D%0A%0A%0A
     private %3CT%3E 

Add a synchronous API