Skip to content

Commit 8edfad4

Browse files
author
Robert Winkler
committed
Final implementation of issue ReactiveX#3
1 parent 8ec1dad commit 8edfad4

11 files changed

+106
-45
lines changed

src/main/java/io/github/robwin/circuitbreaker/CircuitBreaker.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,36 @@ enum State {
8383
HALF_CLOSED
8484
}
8585

86+
enum StateTransition {
87+
CLOSED_TO_OPEN(State.CLOSED, State.OPEN),
88+
HALF_CLOSED_TO_CLOSED(State.HALF_CLOSED, State.CLOSED),
89+
HALF_CLOSED_TO_OPEN(State.HALF_CLOSED, State.OPEN),
90+
OPEN_TO_CLOSED(State.OPEN, State.CLOSED),
91+
OPEN_TO_HALF_CLOSED(State.OPEN, State.HALF_CLOSED),
92+
CLOSED_TO_CLOSED(State.CLOSED, State.CLOSED);
93+
94+
State fromState;
95+
State toState;
96+
97+
StateTransition(State fromState, State toState) {
98+
this.fromState = fromState;
99+
this.toState = toState;
100+
}
101+
102+
public State getFromState() {
103+
return fromState;
104+
}
105+
106+
public State getToState() {
107+
return toState;
108+
}
109+
110+
@Override
111+
public String toString(){
112+
return String.format("State transition from %s to %s", fromState, toState);
113+
}
114+
}
115+
86116
static <T> Try.CheckedSupplier<T> decorateCheckedSupplier(Try.CheckedSupplier<T> supplier, CircuitBreaker circuitBreaker){
87117
return () -> {
88118
CircuitBreakerUtils.isCallPermitted(circuitBreaker);

src/main/java/io/github/robwin/circuitbreaker/CircuitBreakerConfig.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020

2121
import java.util.ArrayList;
2222
import java.util.List;
23-
import java.util.Optional;
2423

2524

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

4039
private CircuitBreakerConfig(int maxFailures,
4140
int waitInterval,
4241
List<Class<? extends Throwable>> ignoredExceptions,
43-
Optional<CircuitBreakerEventListener> circuitBreakerEventListener){
42+
CircuitBreakerEventListener circuitBreakerEventListener){
4443
this.maxFailures = maxFailures;
4544
this.waitInterval = waitInterval;
4645
this.ignoredExceptions = ignoredExceptions;
@@ -59,7 +58,7 @@ public List<Class<? extends Throwable>> getIgnoredExceptions() {
5958
return ignoredExceptions;
6059
}
6160

62-
public Optional<CircuitBreakerEventListener> getCircuitBreakerEventListener() {
61+
public CircuitBreakerEventListener getCircuitBreakerEventListener() {
6362
return circuitBreakerEventListener;
6463
}
6564

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

8180
/**
@@ -144,7 +143,7 @@ public Builder onCircuitBreakerEvent(CircuitBreakerEventListener circuitBreakerE
144143
if (circuitBreakerEventListener == null) {
145144
throw new IllegalArgumentException("circuitBreakerEventListener must not be null");
146145
}
147-
this.circuitBreakerEventListener = Optional.of(circuitBreakerEventListener);
146+
this.circuitBreakerEventListener = circuitBreakerEventListener;
148147
return this;
149148
}
150149

src/main/java/io/github/robwin/circuitbreaker/CircuitBreakerEvent.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@
1919
package io.github.robwin.circuitbreaker;
2020

2121
public interface CircuitBreakerEvent {
22+
String getCircuitBreakerName();
2223
}

src/main/java/io/github/robwin/circuitbreaker/CircuitBreakerRegistry.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,15 @@
2525
public interface CircuitBreakerRegistry {
2626

2727
/**
28-
* Returns the managed {@link CircuitBreaker} or creates a new one with the default configuration.
28+
* Returns a managed {@link CircuitBreaker} or creates a new one with the default configuration.
2929
*
3030
* @param name the name of the CircuitBreaker
3131
* @return The {@link CircuitBreaker}
3232
*/
3333
CircuitBreaker circuitBreaker(String name);
3434

3535
/**
36-
* Returns the managed {@link CircuitBreaker} or creates a new one with a custom configuration.
36+
* Returns a managed {@link CircuitBreaker} or creates a new one with a custom configuration.
3737
*
3838
* @param name the name of the CircuitBreaker
3939
* @param circuitBreakerConfig the CircuitBreaker configuration

src/main/java/io/github/robwin/circuitbreaker/CircuitBreakerStateMachine.java

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -105,24 +105,18 @@ public String toString() {
105105
return String.format("CircuitBreaker '%s'", this.name);
106106
}
107107

108-
void resetState(CircuitBreakerState currentState) {
108+
void resetState(StateTransition stateTransition) {
109109
stateReference.set(new ClosedState(this));
110-
circuitBreakerConfig.getCircuitBreakerEventListener()
111-
.ifPresent(listener ->
112-
listener.onCircuitBreakerEvent(new CircuitBreakerStateChangeEvent(currentState.getState(), State.CLOSED)));
110+
circuitBreakerConfig.getCircuitBreakerEventListener().onCircuitBreakerEvent(new CircuitBreakerStateTransitionEvent(getName(), stateTransition));
113111
}
114112

115-
void transitionToOpenState(CircuitBreakerState currentState) {
113+
void transitionToOpenState(CircuitBreakerState currentState, StateTransition stateTransition) {
116114
stateReference.set(new OpenState(this, currentState));
117-
circuitBreakerConfig.getCircuitBreakerEventListener()
118-
.ifPresent(listener ->
119-
listener.onCircuitBreakerEvent(new CircuitBreakerStateChangeEvent(currentState.getState(), State.OPEN)));
115+
circuitBreakerConfig.getCircuitBreakerEventListener().onCircuitBreakerEvent(new CircuitBreakerStateTransitionEvent(getName(), stateTransition));
120116
}
121117

122-
void transitionToHalfClosedState(CircuitBreakerState currentState) {
118+
void transitionToHalfClosedState(CircuitBreakerState currentState, StateTransition stateTransition) {
123119
stateReference.set(new HalfClosedState(this, currentState));
124-
circuitBreakerConfig.getCircuitBreakerEventListener()
125-
.ifPresent(listener ->
126-
listener.onCircuitBreakerEvent(new CircuitBreakerStateChangeEvent(currentState.getState(), State.HALF_CLOSED)));
120+
circuitBreakerConfig.getCircuitBreakerEventListener().onCircuitBreakerEvent(new CircuitBreakerStateTransitionEvent(getName(), stateTransition));
127121
}
128122
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
*
3+
* Copyright 2015 Robert Winkler
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*
18+
*/
19+
package io.github.robwin.circuitbreaker;
20+
21+
public class CircuitBreakerStateTransitionEvent implements CircuitBreakerEvent{
22+
23+
private String circuitBreakerName;
24+
private CircuitBreaker.StateTransition stateTransition;
25+
26+
public CircuitBreakerStateTransitionEvent(String circuitBreakerName, CircuitBreaker.StateTransition stateTransition) {
27+
this.circuitBreakerName = circuitBreakerName;
28+
this.stateTransition = stateTransition;
29+
}
30+
31+
public CircuitBreaker.StateTransition getStateTransition() {
32+
return stateTransition;
33+
}
34+
35+
@Override
36+
public String getCircuitBreakerName() {
37+
return circuitBreakerName;
38+
}
39+
40+
@Override
41+
public String toString(){
42+
return String.format("CircuitBreaker '%s' changes state from %s to %s", getCircuitBreakerName(), getStateTransition().getFromState(), getStateTransition().getToState());
43+
44+
}
45+
}

src/main/java/io/github/robwin/circuitbreaker/ClosedState.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public void recordFailure() {
4545
if (currentNumOfFailures > this.maxFailures) {
4646
// Too many failures, set new retryAfter to current time + wait interval
4747
retryAfter.set(System.currentTimeMillis() + this.waitInterval);
48-
stateMachine.transitionToOpenState(this);
48+
stateMachine.transitionToOpenState(this, CircuitBreaker.StateTransition.CLOSED_TO_OPEN);
4949
}
5050
}
5151

@@ -55,7 +55,7 @@ public void recordFailure() {
5555
*/
5656
@Override
5757
public void recordSuccess() {
58-
stateMachine.resetState(this);
58+
stateMachine.resetState(CircuitBreaker.StateTransition.CLOSED_TO_CLOSED);
5959
}
6060

6161
/**

src/main/java/io/github/robwin/circuitbreaker/CircuitBreakerStateChangeEvent.java renamed to src/main/java/io/github/robwin/circuitbreaker/DefaultCircuitBreakerEventListener.java

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,16 @@
1818
*/
1919
package io.github.robwin.circuitbreaker;
2020

21-
public class CircuitBreakerStateChangeEvent implements CircuitBreakerEvent{
2221

23-
private CircuitBreaker.State previousState, newState;
22+
import org.slf4j.Logger;
23+
import org.slf4j.LoggerFactory;
2424

25-
public CircuitBreakerStateChangeEvent(CircuitBreaker.State previousState, CircuitBreaker.State newState) {
26-
this.previousState = previousState;
27-
this.newState = newState;
28-
}
25+
public class DefaultCircuitBreakerEventListener implements CircuitBreakerEventListener {
2926

30-
public CircuitBreaker.State getPreviousState() {
31-
return previousState;
32-
}
27+
private static final Logger LOG = LoggerFactory.getLogger(DefaultCircuitBreakerEventListener.class);
3328

34-
public CircuitBreaker.State getNewState() {
35-
return newState;
29+
@Override
30+
public void onCircuitBreakerEvent(CircuitBreakerEvent circuitBreakerEvent) {
31+
LOG.info(circuitBreakerEvent.toString());
3632
}
3733
}

src/main/java/io/github/robwin/circuitbreaker/HalfClosedState.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public boolean isCallPermitted() {
4242
public void recordFailure() {
4343
numOfFailures.incrementAndGet();
4444
retryAfter.set(System.currentTimeMillis() + this.waitInterval);
45-
stateMachine.transitionToOpenState(this);
45+
stateMachine.transitionToOpenState(this, CircuitBreaker.StateTransition.HALF_CLOSED_TO_OPEN);
4646
}
4747

4848
/**
@@ -51,7 +51,7 @@ public void recordFailure() {
5151
*/
5252
@Override
5353
public void recordSuccess() {
54-
stateMachine.resetState(this);
54+
stateMachine.resetState(CircuitBreaker.StateTransition.HALF_CLOSED_TO_CLOSED);
5555
}
5656

5757
/**

src/main/java/io/github/robwin/circuitbreaker/OpenState.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ final public class OpenState extends CircuitBreakerState {
3232
@Override
3333
public boolean isCallPermitted() {
3434
if (System.currentTimeMillis() >= retryAfter.get()) {
35-
stateMachine.transitionToHalfClosedState(this);
35+
stateMachine.transitionToHalfClosedState(this, CircuitBreaker.StateTransition.OPEN_TO_HALF_CLOSED);
3636
return true;
3737
}
3838
return false;
@@ -53,7 +53,7 @@ public void recordFailure() {
5353
*/
5454
@Override
5555
public void recordSuccess() {
56-
stateMachine.resetState(this);
56+
stateMachine.resetState(CircuitBreaker.StateTransition.OPEN_TO_CLOSED);
5757
}
5858

5959
/**

src/test/java/io/github/robwin/circuitbreaker/CircuitBreakerConfigTest.java

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
*/
1919
package io.github.robwin.circuitbreaker;
2020

21-
import javaslang.control.Match;
2221
import org.junit.Test;
2322
import org.slf4j.Logger;
2423
import org.slf4j.LoggerFactory;
@@ -54,18 +53,15 @@ public void shouldSetWaitInterval() {
5453
@Test
5554
public void shouldAddACircuitBreakerEventListener() {
5655
CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
57-
.onCircuitBreakerEvent((CircuitBreakerEvent circuitBreakerEvent)
58-
-> Match.of(circuitBreakerEvent)
59-
.whenType(CircuitBreakerStateChangeEvent.class)
60-
.then((event) -> event.getNewState().toString()))
56+
.onCircuitBreakerEvent((event) -> LOG.info(event.toString()))
6157
.build();
62-
then(circuitBreakerConfig.getCircuitBreakerEventListener().isPresent()).isTrue();
58+
then(circuitBreakerConfig.getCircuitBreakerEventListener()).isNotNull();
6359
}
6460

6561
@Test
66-
public void circuitBreakerEventListenerShouldBeEmpty() {
62+
public void shouldUseTheDefaultEventListener() {
6763
CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
6864
.build();
69-
then(circuitBreakerConfig.getCircuitBreakerEventListener().isPresent()).isFalse();
65+
then(circuitBreakerConfig.getCircuitBreakerEventListener()).isNotNull();
7066
}
7167
}

0 commit comments

Comments
 (0)