Skip to content

Commit 0bde759

Browse files
RomehRobWin
authored andcommitted
Issue ReactiveX#411: Fix Spring Boot AutoConfiguration
1 parent 4074889 commit 0bde759

File tree

15 files changed

+130
-39
lines changed

15 files changed

+130
-39
lines changed

resilience4j-documentation/src/docs/asciidoc/addon_guides/springboot.adoc

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ dependencies {
1616
}
1717
```
1818

19+
==== Supported Types by Spring boot starter resilience aspects
20+
21+
Rate limiter , Retry , Circuit breaker and Bulkhead now support Synchronous return types ,CompletableFutures , RxJava2 types and Reactor types .
22+
1923
==== Monitoring
2024

2125
Spring Boot Actuator health information can be used to check the status of your running application.
@@ -184,29 +188,50 @@ The rules for Retry configuration :
184188

185189
The rules for Retry spring annotation usage :
186190

187-
- Retry aspect will detect the proper handling based into the method return type for synchronous and asynchronous execution
191+
- Retry aspect will detect the proper handling based into the method return type for synchronous , asynchronous execution(CompletableFuture) , RxJava2 and Reactor
188192

189193
Code example of retry and async retry annotation usage in Java Spring component :
190194
[source,java]
191195
----
192196
@Component
197+
@Retry(name = RetryDummyService.BACKEND)
193198
public class RetryDummyServiceImpl implements RetryDummyService {
194199
195-
@Retry(name = RetryDummyService.BACKEND)
200+
196201
@Override
197202
public void doSomething(boolean throwBackendTrouble) throws IOException {
198203
if (throwBackendTrouble) {
199204
throw new IOException("Test Message");
200205
}
201206
}
202207
203-
@Retry(name = RetryDummyService.BACKEND)
208+
204209
@Override
205210
public CompletionStage<String> doSomethingAsync(boolean throwException) throws IOException {
211+
if (throwException) {
212+
CompletableFuture<String> promise = new CompletableFuture<>();
213+
promise.completeExceptionally(new IOException("Test Message"));
214+
return promise;
215+
} else {
216+
return CompletableFuture.supplyAsync(() -> "test");
217+
}
218+
}
219+
220+
@Override
221+
public Flux<String> doSomethingFlux(boolean throwException) {
206222
if (throwException) {
207-
throw new IOException("Test Message");
208-
} else {
209-
return CompletableFuture.supplyAsync(() -> "test");
223+
return Flux.error(new IllegalArgumentException("FailedFlux"));
224+
}
225+
return Flux.fromArray(Arrays.array("test", "test2"));
226+
}
227+
}
228+
229+
@Override
230+
public Flowable<String> doSomethingFlowable(boolean throwException) {
231+
if (throwException) {
232+
return Flowable.error(new IllegalArgumentException("Failed"));
233+
}
234+
return Flowable.just("testMaybe");
210235
}
211236
}
212237

resilience4j-documentation/src/docs/asciidoc/addon_guides/springboot2.adoc

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
=== Spring Boot Starter
1+
=== Spring Boot 2 Starter
22

33
==== Gradle
44

@@ -15,6 +15,9 @@ dependencies {
1515
compile('io.github.resilience4j:resilience4j-spring-boot2:{release-version}')
1616
}
1717
```
18+
==== Supported Types by Spring boot starter resilience aspects
19+
20+
Rate limiter , Retry , Circuit breaker and Bulkhead now support Synchronous return types ,CompletableFutures , RxJava2 types and Reactor types .
1821

1922
==== Monitoring
2023

@@ -184,29 +187,50 @@ The rules for Retry configuration :
184187

185188
The rules for Retry spring annotation usage :
186189

187-
- Retry aspect will detect the proper handling based into the method return type for synchronous and asynchronous execution
190+
- Retry aspect will detect the proper handling based into the method return type for synchronous , asynchronous execution(CompletableFuture) , RxJava2 and Reactor
188191

189192
Code example of retry and async retry annotation usage in Java Spring component :
190193
[source,java]
191194
----
192-
@component
195+
@Component
196+
@Retry(name = RetryDummyService.BACKEND)
193197
public class RetryDummyServiceImpl implements RetryDummyService {
194198
195-
@Retry(name = RetryDummyService.BACKEND)
199+
196200
@Override
197201
public void doSomething(boolean throwBackendTrouble) throws IOException {
198202
if (throwBackendTrouble) {
199203
throw new IOException("Test Message");
200204
}
201205
}
202206
203-
@Retry(name = RetryDummyService.BACKEND)
207+
204208
@Override
205209
public CompletionStage<String> doSomethingAsync(boolean throwException) throws IOException {
210+
if (throwException) {
211+
CompletableFuture<String> promise = new CompletableFuture<>();
212+
promise.completeExceptionally(new IOException("Test Message"));
213+
return promise;
214+
} else {
215+
return CompletableFuture.supplyAsync(() -> "test");
216+
}
217+
}
218+
219+
@Override
220+
public Flux<String> doSomethingFlux(boolean throwException) {
206221
if (throwException) {
207-
throw new IOException("Test Message");
208-
} else {
209-
return CompletableFuture.supplyAsync(() -> "test");
222+
return Flux.error(new IllegalArgumentException("FailedFlux"));
223+
}
224+
return Flux.fromArray(Arrays.array("test", "test2"));
225+
}
226+
}
227+
228+
@Override
229+
public Flowable<String> doSomethingFlowable(boolean throwException) {
230+
if (throwException) {
231+
return Flowable.error(new IllegalArgumentException("Failed"));
232+
}
233+
return Flowable.just("testMaybe");
210234
}
211235
}
212236

resilience4j-documentation/src/docs/asciidoc/core_guides/retry.adoc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,17 @@ Observable.fromCallable(backendService::doSomething)
6363
.compose(RetryTransformer.of(retry))
6464
----
6565

66+
===== Retry and Reactor
67+
68+
The following example shows how to decorate a Flux or Mono by using the custom Reactor Operator.
69+
70+
[source,java]
71+
----
72+
Retry retry = Retry.ofDefaults("backendName");
73+
Mono.fromCallable(backendService::doSomething)
74+
.compose(RetryOperator.of(retry))
75+
----
76+
6677
===== Consume emitted RetryEvents
6778

6879
The Retry emits a stream of RetryEvents. An event can be a failure which signals that even all retries have failed or success if a retry was successful.

resilience4j-spring-boot/src/main/java/io/github/resilience4j/bulkhead/autoconfigure/BulkheadAutoConfiguration.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,6 @@
1515
*/
1616
package io.github.resilience4j.bulkhead.autoconfigure;
1717

18-
import io.github.resilience4j.bulkhead.Bulkhead;
19-
import io.github.resilience4j.bulkhead.BulkheadRegistry;
20-
import io.github.resilience4j.bulkhead.configure.BulkheadConfiguration;
21-
import io.github.resilience4j.bulkhead.monitoring.endpoint.BulkheadEndpoint;
2218
import org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration;
2319
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
2420
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@@ -27,6 +23,14 @@
2723
import org.springframework.context.annotation.Configuration;
2824
import org.springframework.context.annotation.Import;
2925

26+
import io.github.resilience4j.bulkhead.Bulkhead;
27+
import io.github.resilience4j.bulkhead.BulkheadRegistry;
28+
import io.github.resilience4j.bulkhead.configure.BulkheadConfiguration;
29+
import io.github.resilience4j.bulkhead.event.BulkheadEvent;
30+
import io.github.resilience4j.bulkhead.monitoring.endpoint.BulkheadEndpoint;
31+
import io.github.resilience4j.bulkhead.monitoring.endpoint.BulkheadEventsEndpoint;
32+
import io.github.resilience4j.consumer.EventConsumerRegistry;
33+
3034
/**
3135
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
3236
* Auto-configuration} for resilience4j-bulkhead.
@@ -42,4 +46,9 @@ public class BulkheadAutoConfiguration {
4246
public BulkheadEndpoint bulkheadEndpoint(BulkheadRegistry bulkheadRegistry) {
4347
return new BulkheadEndpoint(bulkheadRegistry);
4448
}
49+
50+
@Bean
51+
public BulkheadEventsEndpoint bulkheadEventsEndpoint(EventConsumerRegistry<BulkheadEvent> eventConsumerRegistry, BulkheadRegistry bulkheadRegistry) {
52+
return new BulkheadEventsEndpoint(eventConsumerRegistry, bulkheadRegistry);
53+
}
4554
}

resilience4j-spring-boot/src/main/java/io/github/resilience4j/bulkhead/autoconfigure/BulkheadMetricsAutoConfiguration.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
*/
3535
@Configuration
3636
@ConditionalOnClass(MetricRegistry.class)
37-
@AutoConfigureAfter(value = {BulkheadMetricsAutoConfiguration.class, MetricsDropwizardAutoConfiguration.class})
37+
@AutoConfigureAfter(value = {MetricsDropwizardAutoConfiguration.class})
3838
@AutoConfigureBefore(MetricRepositoryAutoConfiguration.class)
3939
public class BulkheadMetricsAutoConfiguration {
4040
@Bean

resilience4j-spring-boot/src/main/java/io/github/resilience4j/ratelimiter/autoconfigure/RateLimiterAutoConfiguration.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*/
1616
package io.github.resilience4j.ratelimiter.autoconfigure;
1717

18+
import javax.annotation.PostConstruct;
19+
1820
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
1921
import org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration;
2022
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
@@ -24,12 +26,13 @@
2426
import org.springframework.context.annotation.Configuration;
2527
import org.springframework.context.annotation.Import;
2628

27-
import javax.annotation.PostConstruct;
28-
29+
import io.github.resilience4j.consumer.EventConsumerRegistry;
2930
import io.github.resilience4j.ratelimiter.RateLimiter;
3031
import io.github.resilience4j.ratelimiter.RateLimiterRegistry;
3132
import io.github.resilience4j.ratelimiter.configure.RateLimiterConfiguration;
33+
import io.github.resilience4j.ratelimiter.event.RateLimiterEvent;
3234
import io.github.resilience4j.ratelimiter.monitoring.endpoint.RateLimiterEndpoint;
35+
import io.github.resilience4j.ratelimiter.monitoring.endpoint.RateLimiterEventsEndpoint;
3336
import io.github.resilience4j.ratelimiter.monitoring.health.RateLimiterHealthIndicator;
3437

3538
/**
@@ -57,6 +60,11 @@ public RateLimiterEndpoint rateLimiterEndpoint(RateLimiterRegistry rateLimiterRe
5760
return new RateLimiterEndpoint(rateLimiterRegistry);
5861
}
5962

63+
@Bean
64+
public RateLimiterEventsEndpoint rateLimiterEventsEndpoint(EventConsumerRegistry<RateLimiterEvent> eventsConsumerRegistry, RateLimiterRegistry rateLimiterRegistry) {
65+
return new RateLimiterEventsEndpoint(eventsConsumerRegistry, rateLimiterRegistry);
66+
}
67+
6068
@PostConstruct
6169
public void configureHealthIndicators() {
6270
rateLimiterProperties.getLimiters().forEach(

resilience4j-spring-boot/src/main/java/io/github/resilience4j/retry/autoconfigure/RetryAutoConfiguration.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,13 @@
2121
import org.springframework.context.annotation.Configuration;
2222
import org.springframework.context.annotation.Import;
2323

24+
import io.github.resilience4j.consumer.EventConsumerRegistry;
2425
import io.github.resilience4j.retry.Retry;
2526
import io.github.resilience4j.retry.RetryRegistry;
2627
import io.github.resilience4j.retry.configure.RetryConfiguration;
28+
import io.github.resilience4j.retry.event.RetryEvent;
2729
import io.github.resilience4j.retry.monitoring.endpoint.RetryEndpoint;
30+
import io.github.resilience4j.retry.monitoring.endpoint.RetryEventsEndpoint;
2831

2932

3033
/**
@@ -42,4 +45,9 @@ public RetryEndpoint retryEndpoint(RetryRegistry retryRegistry) {
4245
return new RetryEndpoint(retryRegistry);
4346
}
4447

48+
@Bean
49+
public RetryEventsEndpoint retryEventsEndpoint(EventConsumerRegistry<RetryEvent> eventConsumerRegistry) {
50+
return new RetryEventsEndpoint(eventConsumerRegistry);
51+
}
52+
4553
}

resilience4j-spring-boot/src/main/resources/META-INF/spring.factories

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
2+
io.github.resilience4j.bulkhead.autoconfigure.BulkheadAutoConfiguration,\
3+
io.github.resilience4j.bulkhead.autoconfigure.BulkheadMetricsAutoConfiguration,\
4+
io.github.resilience4j.bulkhead.autoconfigure.BulkheadPrometheusAutoConfiguration,\
25
io.github.resilience4j.retry.autoconfigure.RetryAutoConfiguration,\
36
io.github.resilience4j.retry.autoconfigure.RetryMetricsAutoConfiguration,\
47
io.github.resilience4j.circuitbreaker.autoconfigure.CircuitBreakerAutoConfiguration,\

resilience4j-spring-boot/src/test/java/io/github/resilience4j/service/test/TestApplication.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@
99
/**
1010
* @author bstorozhuk
1111
*/
12-
@SpringBootApplication(scanBasePackages = "io.github.resilience4j")
12+
@SpringBootApplication
1313
@EnableSpringBootMetricsCollector
1414
@EnablePrometheusEndpoint
1515
public class TestApplication {
16-
public static void main(String[] args) {
17-
SpringApplication.run(TestApplication.class, args);
18-
}
16+
public static void main(String[] args) {
17+
SpringApplication.run(TestApplication.class, args);
18+
}
1919
}

resilience4j-spring-boot2/src/main/java/io/github/resilience4j/bulkhead/autoconfigure/BulkheadMetricsAutoConfiguration.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,24 @@
1515
*/
1616
package io.github.resilience4j.bulkhead.autoconfigure;
1717

18-
import io.github.resilience4j.bulkhead.BulkheadRegistry;
19-
import io.github.resilience4j.micrometer.BulkheadMetrics;
20-
import io.github.resilience4j.micrometer.tagged.TaggedBulkheadMetrics;
2118
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
2219
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
2320
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2421
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
2522
import org.springframework.context.annotation.Bean;
2623
import org.springframework.context.annotation.Configuration;
2724

25+
import io.github.resilience4j.bulkhead.BulkheadRegistry;
26+
import io.github.resilience4j.micrometer.BulkheadMetrics;
27+
import io.github.resilience4j.micrometer.tagged.TaggedBulkheadMetrics;
28+
2829
/**
2930
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
3031
* Auto-configuration} for resilience4j-metrics.
3132
*/
3233
@Configuration
3334
@ConditionalOnClass(MetricsAutoConfiguration.class)
34-
@AutoConfigureAfter(value = {BulkheadMetricsAutoConfiguration.class, MetricsAutoConfiguration.class})
35+
@AutoConfigureAfter(value = {MetricsAutoConfiguration.class})
3536
@ConditionalOnProperty(value = "resilience4j.bulkhead.metrics.enabled", matchIfMissing = true)
3637
public class BulkheadMetricsAutoConfiguration {
3738

resilience4j-spring-boot2/src/main/resources/META-INF/spring.factories

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
2+
io.github.resilience4j.bulkhead.autoconfigure.BulkheadAutoConfiguration,\
3+
io.github.resilience4j.bulkhead.autoconfigure.BulkheadMetricsAutoConfiguration,\
24
io.github.resilience4j.retry.autoconfigure.RetryAutoConfiguration,\
35
io.github.resilience4j.retry.autoconfigure.RetryMetricsAutoConfiguration,\
46
io.github.resilience4j.circuitbreaker.autoconfigure.CircuitBreakerAutoConfiguration,\

resilience4j-spring-boot2/src/test/java/io/github/resilience4j/circuitbreaker/CircuitBreakerAutoConfigurationRxJava2Test.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,12 @@ public void testCircuitBreakerAutoConfigurationReactiveRxJava2() throws IOExcept
7272
assertThat(circuitBreakerProperties).isNotNull();
7373

7474
try {
75-
reactiveDummyService.doSomethingFlowable(true).blockingSubscribe(String::toUpperCase, Throwable::getCause);
75+
reactiveDummyService.doSomethingFlowable(true).blockingSubscribe(String::toUpperCase, throwable -> System.out.println("Exception received:" + throwable.getMessage()));
7676
} catch (Exception ex) {
7777
// Do nothing. The IOException is recorded by the CircuitBreaker as part of the recordFailurePredicate as a failure.
7878
}
7979
// The invocation is recorded by the CircuitBreaker as a success.
80-
reactiveDummyService.doSomethingFlowable(false).blockingSubscribe(String::toUpperCase, Throwable::getCause);
80+
reactiveDummyService.doSomethingFlowable(false).blockingSubscribe(String::toUpperCase, throwable -> System.out.println("Exception received:" + throwable.getMessage()));
8181

8282
CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker(ReactiveDummyService.BACKEND);
8383
assertThat(circuitBreaker).isNotNull();

resilience4j-spring-boot2/src/test/java/io/github/resilience4j/circuitbreaker/CircuitBreakerAutoConfigurationTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,12 +180,12 @@ public void testCircuitBreakerAutoConfigurationReactive() throws IOException {
180180
assertThat(circuitBreakerProperties).isNotNull();
181181

182182
try {
183-
reactiveDummyService.doSomethingFlux(true).subscribe(String::toUpperCase, Throwable::printStackTrace);
183+
reactiveDummyService.doSomethingFlux(true).subscribe(String::toUpperCase, throwable -> System.out.println("Exception received:" + throwable.getMessage()));
184184
} catch (IOException ex) {
185185
// Do nothing. The IOException is recorded by the CircuitBreaker as part of the recordFailurePredicate as a failure.
186186
}
187187
// The invocation is recorded by the CircuitBreaker as a success.
188-
reactiveDummyService.doSomethingFlux(false).subscribe(String::toUpperCase, Throwable::printStackTrace);
188+
reactiveDummyService.doSomethingFlux(false).subscribe(String::toUpperCase, throwable -> System.out.println("Exception received:" + throwable.getMessage()));
189189

190190
CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker(ReactiveDummyService.BACKEND);
191191
assertThat(circuitBreaker).isNotNull();

resilience4j-spring-boot2/src/test/java/io/github/resilience4j/retry/RetryAutoConfigurationRxJavaTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,13 @@ public void testRetryAutoConfigurationRxJava2() throws IOException {
6969
assertThat(retryProperties).isNotNull();
7070

7171
try {
72-
retryDummyService.doSomethingFlowable(true).subscribe(String::toUpperCase, throwable -> System.out.println("Exception received:" + throwable.getMessage()));
72+
retryDummyService.doSomethingFlowable(true).blockingSubscribe(String::toUpperCase, throwable -> System.out.println("Exception received:" + throwable.getMessage()));
7373
} catch (RetryExceptionWrapper ex) {
7474
assertThat(ex.getCause()).hasCauseInstanceOf(IllegalArgumentException.class);
7575
// Do nothing. The IOException is recorded by the retry as it is one of failure exceptions
7676
}
7777
// The invocation is recorded by the CircuitBreaker as a success.
78-
retryDummyService.doSomethingFlowable(false).subscribe(String::toUpperCase, throwable -> System.out.println("Exception received:" + throwable.getMessage()));
78+
retryDummyService.doSomethingFlowable(false).blockingSubscribe(String::toUpperCase, throwable -> System.out.println("Exception received:" + throwable.getMessage()));
7979
;
8080

8181
Retry retry = retryRegistry.retry(BACKEND_C);

resilience4j-spring-boot2/src/test/java/io/github/resilience4j/service/test/TestApplication.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
/**
77
* @author bstorozhuk
88
*/
9-
@SpringBootApplication(scanBasePackages = "io.github.resilience4j")
9+
@SpringBootApplication
1010
public class TestApplication {
11-
public static void main(String[] args) {
12-
SpringApplication.run(TestApplication.class, args);
13-
}
11+
public static void main(String[] args) {
12+
SpringApplication.run(TestApplication.class, args);
13+
}
1414
}

0 commit comments

Comments
 (0)