Skip to content

Commit

Permalink
Final implementation of issue ReactiveX#3
Browse files Browse the repository at this point in the history
  • Loading branch information
Robert Winkler committed Dec 30, 2015
1 parent 8ec1dad commit 8edfad4
Show file tree
Hide file tree
Showing 11 changed files with 106 additions and 45 deletions.
30 changes: 30 additions & 0 deletions src/main/java/io/github/robwin/circuitbreaker/CircuitBreaker.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,36 @@ enum State {
HALF_CLOSED
}

enum StateTransition {
CLOSED_TO_OPEN(State.CLOSED, State.OPEN),
HALF_CLOSED_TO_CLOSED(State.HALF_CLOSED, State.CLOSED),
HALF_CLOSED_TO_OPEN(State.HALF_CLOSED, State.OPEN),
OPEN_TO_CLOSED(State.OPEN, State.CLOSED),
OPEN_TO_HALF_CLOSED(State.OPEN, State.HALF_CLOSED),
CLOSED_TO_CLOSED(State.CLOSED, State.CLOSED);

State fromState;
State toState;

StateTransition(State fromState, State toState) {
this.fromState = fromState;
this.toState = toState;
}

public State getFromState() {
return fromState;
}

public State getToState() {
return toState;
}

@Override
public String toString(){
return String.format("State transition from %s to %s", fromState, toState);
}
}

static <T> Try.CheckedSupplier<T> decorateCheckedSupplier(Try.CheckedSupplier<T> supplier, CircuitBreaker circuitBreaker){
return () -> {
CircuitBreakerUtils.isCallPermitted(circuitBreaker);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;


public class CircuitBreakerConfig {
Expand All @@ -33,14 +32,14 @@ public class CircuitBreakerConfig {
// The wait interval which specifies how long the CircuitBreaker should stay OPEN
private final int waitInterval;
// The CircuitBreakerEventListener which should handle CircuitBreaker events.
private Optional<CircuitBreakerEventListener> circuitBreakerEventListener;
private CircuitBreakerEventListener circuitBreakerEventListener;
// Exceptions which do not count as failures and thus not trigger the circuit breaker.
private final List<Class<? extends Throwable>> ignoredExceptions;

private CircuitBreakerConfig(int maxFailures,
int waitInterval,
List<Class<? extends Throwable>> ignoredExceptions,
Optional<CircuitBreakerEventListener> circuitBreakerEventListener){
CircuitBreakerEventListener circuitBreakerEventListener){
this.maxFailures = maxFailures;
this.waitInterval = waitInterval;
this.ignoredExceptions = ignoredExceptions;
Expand All @@ -59,7 +58,7 @@ public List<Class<? extends Throwable>> getIgnoredExceptions() {
return ignoredExceptions;
}

public Optional<CircuitBreakerEventListener> getCircuitBreakerEventListener() {
public CircuitBreakerEventListener getCircuitBreakerEventListener() {
return circuitBreakerEventListener;
}

Expand All @@ -75,7 +74,7 @@ public static CircuitBreakerConfig.Builder custom(){
public static class Builder {
private int maxFailures = DEFAULT_MAX_FAILURES;
private int waitInterval = DEFAULT_WAIT_INTERVAL;
private Optional<CircuitBreakerEventListener> circuitBreakerEventListener = Optional.empty();
private CircuitBreakerEventListener circuitBreakerEventListener = new DefaultCircuitBreakerEventListener();
private List<Class<? extends Throwable>> ignoredExceptions = new ArrayList<>();

/**
Expand Down Expand Up @@ -144,7 +143,7 @@ public Builder onCircuitBreakerEvent(CircuitBreakerEventListener circuitBreakerE
if (circuitBreakerEventListener == null) {
throw new IllegalArgumentException("circuitBreakerEventListener must not be null");
}
this.circuitBreakerEventListener = Optional.of(circuitBreakerEventListener);
this.circuitBreakerEventListener = circuitBreakerEventListener;
return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@
package io.github.robwin.circuitbreaker;

public interface CircuitBreakerEvent {
String getCircuitBreakerName();
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@
public interface CircuitBreakerRegistry {

/**
* Returns the managed {@link CircuitBreaker} or creates a new one with the default configuration.
* Returns a managed {@link CircuitBreaker} or creates a new one with the default configuration.
*
* @param name the name of the CircuitBreaker
* @return The {@link CircuitBreaker}
*/
CircuitBreaker circuitBreaker(String name);

/**
* Returns the managed {@link CircuitBreaker} or creates a new one with a custom configuration.
* Returns a managed {@link CircuitBreaker} or creates a new one with a custom configuration.
*
* @param name the name of the CircuitBreaker
* @param circuitBreakerConfig the CircuitBreaker configuration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,24 +105,18 @@ public String toString() {
return String.format("CircuitBreaker '%s'", this.name);
}

void resetState(CircuitBreakerState currentState) {
void resetState(StateTransition stateTransition) {
stateReference.set(new ClosedState(this));
circuitBreakerConfig.getCircuitBreakerEventListener()
.ifPresent(listener ->
listener.onCircuitBreakerEvent(new CircuitBreakerStateChangeEvent(currentState.getState(), State.CLOSED)));
circuitBreakerConfig.getCircuitBreakerEventListener().onCircuitBreakerEvent(new CircuitBreakerStateTransitionEvent(getName(), stateTransition));
}

void transitionToOpenState(CircuitBreakerState currentState) {
void transitionToOpenState(CircuitBreakerState currentState, StateTransition stateTransition) {
stateReference.set(new OpenState(this, currentState));
circuitBreakerConfig.getCircuitBreakerEventListener()
.ifPresent(listener ->
listener.onCircuitBreakerEvent(new CircuitBreakerStateChangeEvent(currentState.getState(), State.OPEN)));
circuitBreakerConfig.getCircuitBreakerEventListener().onCircuitBreakerEvent(new CircuitBreakerStateTransitionEvent(getName(), stateTransition));
}

void transitionToHalfClosedState(CircuitBreakerState currentState) {
void transitionToHalfClosedState(CircuitBreakerState currentState, StateTransition stateTransition) {
stateReference.set(new HalfClosedState(this, currentState));
circuitBreakerConfig.getCircuitBreakerEventListener()
.ifPresent(listener ->
listener.onCircuitBreakerEvent(new CircuitBreakerStateChangeEvent(currentState.getState(), State.HALF_CLOSED)));
circuitBreakerConfig.getCircuitBreakerEventListener().onCircuitBreakerEvent(new CircuitBreakerStateTransitionEvent(getName(), stateTransition));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
*
* Copyright 2015 Robert Winkler
*
* 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 io.github.robwin.circuitbreaker;

public class CircuitBreakerStateTransitionEvent implements CircuitBreakerEvent{

private String circuitBreakerName;
private CircuitBreaker.StateTransition stateTransition;

public CircuitBreakerStateTransitionEvent(String circuitBreakerName, CircuitBreaker.StateTransition stateTransition) {
this.circuitBreakerName = circuitBreakerName;
this.stateTransition = stateTransition;
}

public CircuitBreaker.StateTransition getStateTransition() {
return stateTransition;
}

@Override
public String getCircuitBreakerName() {
return circuitBreakerName;
}

@Override
public String toString(){
return String.format("CircuitBreaker '%s' changes state from %s to %s", getCircuitBreakerName(), getStateTransition().getFromState(), getStateTransition().getToState());

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public void recordFailure() {
if (currentNumOfFailures > this.maxFailures) {
// Too many failures, set new retryAfter to current time + wait interval
retryAfter.set(System.currentTimeMillis() + this.waitInterval);
stateMachine.transitionToOpenState(this);
stateMachine.transitionToOpenState(this, CircuitBreaker.StateTransition.CLOSED_TO_OPEN);
}
}

Expand All @@ -55,7 +55,7 @@ public void recordFailure() {
*/
@Override
public void recordSuccess() {
stateMachine.resetState(this);
stateMachine.resetState(CircuitBreaker.StateTransition.CLOSED_TO_CLOSED);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,16 @@
*/
package io.github.robwin.circuitbreaker;

public class CircuitBreakerStateChangeEvent implements CircuitBreakerEvent{

private CircuitBreaker.State previousState, newState;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public CircuitBreakerStateChangeEvent(CircuitBreaker.State previousState, CircuitBreaker.State newState) {
this.previousState = previousState;
this.newState = newState;
}
public class DefaultCircuitBreakerEventListener implements CircuitBreakerEventListener {

public CircuitBreaker.State getPreviousState() {
return previousState;
}
private static final Logger LOG = LoggerFactory.getLogger(DefaultCircuitBreakerEventListener.class);

public CircuitBreaker.State getNewState() {
return newState;
@Override
public void onCircuitBreakerEvent(CircuitBreakerEvent circuitBreakerEvent) {
LOG.info(circuitBreakerEvent.toString());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public boolean isCallPermitted() {
public void recordFailure() {
numOfFailures.incrementAndGet();
retryAfter.set(System.currentTimeMillis() + this.waitInterval);
stateMachine.transitionToOpenState(this);
stateMachine.transitionToOpenState(this, CircuitBreaker.StateTransition.HALF_CLOSED_TO_OPEN);
}

/**
Expand All @@ -51,7 +51,7 @@ public void recordFailure() {
*/
@Override
public void recordSuccess() {
stateMachine.resetState(this);
stateMachine.resetState(CircuitBreaker.StateTransition.HALF_CLOSED_TO_CLOSED);
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/io/github/robwin/circuitbreaker/OpenState.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ final public class OpenState extends CircuitBreakerState {
@Override
public boolean isCallPermitted() {
if (System.currentTimeMillis() >= retryAfter.get()) {
stateMachine.transitionToHalfClosedState(this);
stateMachine.transitionToHalfClosedState(this, CircuitBreaker.StateTransition.OPEN_TO_HALF_CLOSED);
return true;
}
return false;
Expand All @@ -53,7 +53,7 @@ public void recordFailure() {
*/
@Override
public void recordSuccess() {
stateMachine.resetState(this);
stateMachine.resetState(CircuitBreaker.StateTransition.OPEN_TO_CLOSED);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
*/
package io.github.robwin.circuitbreaker;

import javaslang.control.Match;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -54,18 +53,15 @@ public void shouldSetWaitInterval() {
@Test
public void shouldAddACircuitBreakerEventListener() {
CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
.onCircuitBreakerEvent((CircuitBreakerEvent circuitBreakerEvent)
-> Match.of(circuitBreakerEvent)
.whenType(CircuitBreakerStateChangeEvent.class)
.then((event) -> event.getNewState().toString()))
.onCircuitBreakerEvent((event) -> LOG.info(event.toString()))
.build();
then(circuitBreakerConfig.getCircuitBreakerEventListener().isPresent()).isTrue();
then(circuitBreakerConfig.getCircuitBreakerEventListener()).isNotNull();
}

@Test
public void circuitBreakerEventListenerShouldBeEmpty() {
public void shouldUseTheDefaultEventListener() {
CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
.build();
then(circuitBreakerConfig.getCircuitBreakerEventListener().isPresent()).isFalse();
then(circuitBreakerConfig.getCircuitBreakerEventListener()).isNotNull();
}
}

0 comments on commit 8edfad4

Please sign in to comment.