├── .github └── workflows │ └── maven.yml ├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── VERSIONING.md ├── bin └── push-javadoc.sh ├── core ├── pom.xml └── src │ ├── main │ ├── java │ │ └── dev │ │ │ └── failsafe │ │ │ ├── AsyncExecution.java │ │ │ ├── AsyncExecutionImpl.java │ │ │ ├── Bulkhead.java │ │ │ ├── BulkheadBuilder.java │ │ │ ├── BulkheadConfig.java │ │ │ ├── BulkheadFullException.java │ │ │ ├── Call.java │ │ │ ├── CallImpl.java │ │ │ ├── CircuitBreaker.java │ │ │ ├── CircuitBreakerBuilder.java │ │ │ ├── CircuitBreakerConfig.java │ │ │ ├── CircuitBreakerOpenException.java │ │ │ ├── DelayablePolicyBuilder.java │ │ │ ├── DelayablePolicyConfig.java │ │ │ ├── Execution.java │ │ │ ├── ExecutionContext.java │ │ │ ├── ExecutionImpl.java │ │ │ ├── Failsafe.java │ │ │ ├── FailsafeException.java │ │ │ ├── FailsafeExecutor.java │ │ │ ├── FailurePolicyBuilder.java │ │ │ ├── FailurePolicyConfig.java │ │ │ ├── Fallback.java │ │ │ ├── FallbackBuilder.java │ │ │ ├── FallbackConfig.java │ │ │ ├── Functions.java │ │ │ ├── Policy.java │ │ │ ├── PolicyBuilder.java │ │ │ ├── PolicyConfig.java │ │ │ ├── PolicyListeners.java │ │ │ ├── RateLimitExceededException.java │ │ │ ├── RateLimiter.java │ │ │ ├── RateLimiterBuilder.java │ │ │ ├── RateLimiterConfig.java │ │ │ ├── RetryPolicy.java │ │ │ ├── RetryPolicyBuilder.java │ │ │ ├── RetryPolicyConfig.java │ │ │ ├── SyncExecutionImpl.java │ │ │ ├── Timeout.java │ │ │ ├── TimeoutBuilder.java │ │ │ ├── TimeoutConfig.java │ │ │ ├── TimeoutExceededException.java │ │ │ ├── event │ │ │ ├── CircuitBreakerStateChangedEvent.java │ │ │ ├── EventListener.java │ │ │ ├── ExecutionAttemptedEvent.java │ │ │ ├── ExecutionCompletedEvent.java │ │ │ ├── ExecutionEvent.java │ │ │ ├── ExecutionScheduledEvent.java │ │ │ └── package-info.java │ │ │ ├── function │ │ │ ├── AsyncRunnable.java │ │ │ ├── AsyncSupplier.java │ │ │ ├── CheckedBiPredicate.java │ │ │ ├── CheckedConsumer.java │ │ │ ├── CheckedFunction.java │ │ │ ├── CheckedPredicate.java │ │ │ ├── CheckedRunnable.java │ │ │ ├── CheckedSupplier.java │ │ │ ├── ContextualRunnable.java │ │ │ ├── ContextualSupplier.java │ │ │ └── package-info.java │ │ │ ├── internal │ │ │ ├── BulkheadExecutor.java │ │ │ ├── BulkheadImpl.java │ │ │ ├── BurstyRateLimiterStats.java │ │ │ ├── CircuitBreakerExecutor.java │ │ │ ├── CircuitBreakerImpl.java │ │ │ ├── CircuitState.java │ │ │ ├── CircuitStats.java │ │ │ ├── ClosedState.java │ │ │ ├── CountingCircuitStats.java │ │ │ ├── DefaultCircuitStats.java │ │ │ ├── EventHandler.java │ │ │ ├── FallbackExecutor.java │ │ │ ├── FallbackImpl.java │ │ │ ├── HalfOpenState.java │ │ │ ├── OpenState.java │ │ │ ├── RateLimiterExecutor.java │ │ │ ├── RateLimiterImpl.java │ │ │ ├── RateLimiterStats.java │ │ │ ├── RetryPolicyExecutor.java │ │ │ ├── RetryPolicyImpl.java │ │ │ ├── SmoothRateLimiterStats.java │ │ │ ├── TimedCircuitStats.java │ │ │ ├── TimeoutExecutor.java │ │ │ ├── TimeoutImpl.java │ │ │ └── util │ │ │ │ ├── Assert.java │ │ │ │ ├── DelegatingScheduler.java │ │ │ │ ├── Durations.java │ │ │ │ ├── FutureLinkedList.java │ │ │ │ ├── Lists.java │ │ │ │ ├── Maths.java │ │ │ │ └── RandomDelay.java │ │ │ ├── package-info.java │ │ │ └── spi │ │ │ ├── AsyncExecutionInternal.java │ │ │ ├── DefaultScheduledFuture.java │ │ │ ├── DelayablePolicy.java │ │ │ ├── ExecutionInternal.java │ │ │ ├── ExecutionResult.java │ │ │ ├── FailsafeFuture.java │ │ │ ├── FailurePolicy.java │ │ │ ├── PolicyExecutor.java │ │ │ ├── Scheduler.java │ │ │ ├── SyncExecutionInternal.java │ │ │ └── package-info.java │ └── javadoc │ │ └── overview.html │ └── test │ └── java │ └── dev │ └── failsafe │ ├── AsyncExecutionTest.java │ ├── AsyncFailsafeTest.java │ ├── BulkheadBuilderTest.java │ ├── CircuitBreakerBuilderTest.java │ ├── CircuitBreakerTest.java │ ├── DelayablePolicyTest.java │ ├── ExecutionTest.java │ ├── FailsafeFutureTest.java │ ├── FailsafeTest.java │ ├── FailurePolicyBuilderTest.java │ ├── FailurePolicyTest.java │ ├── FallbackBuilderTest.java │ ├── ListenersTest.java │ ├── RateLimiterBuilderTest.java │ ├── RetryPolicyBuilderTest.java │ ├── TimeoutBuilderTest.java │ ├── functional │ ├── BlockedExecutionTest.java │ ├── BulkheadTest.java │ ├── CallCancellationTest.java │ ├── CircuitBreakerTest.java │ ├── DelayableCircuitBreakerTest.java │ ├── DelayableRetryPolicyTest.java │ ├── ExecutorConfigurationTest.java │ ├── ExecutorTest.java │ ├── FallbackTest.java │ ├── FutureCancellationTest.java │ ├── FutureCompletionTest.java │ ├── InterruptionTest.java │ ├── NestedCircuitBreakerTest.java │ ├── NestedRetryPolicyTest.java │ ├── NestedTimeoutTest.java │ ├── NoPolicyTest.java │ ├── PolicyCompositionExecutionTest.java │ ├── PolicyCompositionTest.java │ ├── RateLimiterTest.java │ ├── RetryPolicyTest.java │ ├── ShutdownExecutorTest.java │ ├── TimeoutTest.java │ └── package-info.java │ ├── internal │ ├── BurstyRateLimiterStatsTest.java │ ├── CircuitStatsTest.java │ ├── ClosedStateTest.java │ ├── CountingCircuitStatsTest.java │ ├── HalfOpenStateTest.java │ ├── InternalTesting.java │ ├── OpenStateTest.java │ ├── RateLimiterImplTest.java │ ├── RateLimiterStatsTest.java │ ├── RetryPolicyImplTest.java │ ├── SmoothRateLimiterStatsTest.java │ ├── TimedCircuitStatsTest.java │ └── util │ │ ├── DelegatingSchedulerTest.java │ │ ├── DurationsTest.java │ │ ├── FutureLinkedListTest.java │ │ ├── ListsTest.java │ │ ├── MathsTest.java │ │ └── RandomDelayTest.java │ ├── issues │ ├── Issue115Test.java │ ├── Issue131Test.java │ ├── Issue146Test.java │ ├── Issue165Test.java │ ├── Issue177Test.java │ ├── Issue190Test.java │ ├── Issue192Test.java │ ├── Issue206Test.java │ ├── Issue215Test.java │ ├── Issue218Test.java │ ├── Issue224Test.java │ ├── Issue231Test.java │ ├── Issue240Test.java │ ├── Issue242Test.java │ ├── Issue260Test.java │ ├── Issue267Test.java │ ├── Issue284Test.java │ ├── Issue298Test.java │ ├── Issue311Test.java │ ├── Issue36Test.java │ ├── Issue52Test.java │ ├── Issue55Test.java │ ├── Issue5Test.java │ ├── Issue75Test.java │ ├── Issue76Test.java │ ├── Issue84Test.java │ └── Issue9Test.java │ └── testing │ ├── Asserts.java │ ├── Logging.java │ ├── Mocking.java │ ├── TestCaseLogger.java │ ├── Testing.java │ └── package-info.java ├── examples ├── pom.xml └── src │ └── main │ └── java │ └── dev │ └── failsafe │ └── examples │ ├── AsyncExample.java │ ├── Java8Example.java │ ├── NettyExample.java │ ├── RetryLoopExample.java │ ├── RxJavaExample.java │ └── VertxExample.java ├── modules ├── okhttp │ ├── pom.xml │ └── src │ │ ├── main │ │ └── java │ │ │ └── dev │ │ │ └── failsafe │ │ │ └── okhttp │ │ │ └── FailsafeCall.java │ │ └── test │ │ └── java │ │ └── dev │ │ └── failsafe │ │ └── okhttp │ │ ├── FailsafeCallTest.java │ │ └── testing │ │ └── OkHttpTesting.java └── retrofit │ ├── pom.xml │ └── src │ ├── main │ └── java │ │ └── dev │ │ └── failsafe │ │ └── retrofit │ │ └── FailsafeCall.java │ └── test │ └── java │ └── dev │ └── retrofit │ ├── FailsafeCallTest.java │ ├── TestService.java │ └── testing │ └── RetrofitTesting.java └── pom.xml /.github/workflows/maven.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Maven 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven 3 | 4 | name: build 5 | 6 | on: [push, pull_request] 7 | 8 | jobs: 9 | compile: 10 | runs-on: ubuntu-latest 11 | 12 | strategy: 13 | matrix: 14 | java: [ 8, 11, 17 ] 15 | jdk: ['temurin', 'zulu'] 16 | 17 | name: Java ${{ matrix.java }} ${{ matrix.jdk }} 18 | steps: 19 | - name: Checkout Source Code 20 | uses: actions/checkout@v4 21 | 22 | - name: Setup Java 23 | uses: actions/setup-java@v4 24 | with: 25 | distribution: ${{ matrix.jdk }} 26 | java-package: jdk 27 | java-version: ${{ matrix.java }} 28 | cache: 'maven' 29 | 30 | - name: Build with maven 31 | run: mvn -B test 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | *~ 3 | .project 4 | .classpath 5 | .settings/ 6 | test-output/ 7 | docs/ 8 | .idea 9 | *.iml 10 | _site 11 | .java-version -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ### Reporting Bugs 2 | 3 | Bug reports are welcome and appreciated. When filing an issue, please include a small code snippet that demonstrates the bug if you can, else include a good description of how to reproduce the bug. 4 | 5 | ### Contributing Bug Fixes 6 | 7 | Pull requests for bugs related to existing features are always welcome. 8 | 9 | ### Requesting Features 10 | 11 | Feature requests are welcome by filing an issue. In general we try to make sure that new features fit well with the existing ones and that they're broadly useful. If your feature will require new APIs or API changes, feel free to share an example of how you think the API should look. 12 | 13 | ### Contributing Features 14 | 15 | If you have an idea for a new feature, the best place to start is not with a pull request but rather by opening an issue describing how the feature or API change should work and why you think it is necessary. The reason we suggest starting with an issue rather than a pull request is that we like to make sure every feature and API change is widely useful and a good fit for the library, and would hate to reject a PR that someone puts a lot of time into if it's not a good fit. 16 | 17 | If your feature idea sounds good, you can then submit a PR, else we'll schedule the feature for implementation. 18 | 19 | ### Contributing Documentation or Website Fixes 20 | 21 | Fixes to the Failsafe documentation or website are welcome. Just clone the [website repo](https://github.com/failsafe-lib/failsafe-lib.github.io) and feel free to submit a pull request. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Failsafe 2 | 3 | [![Build Status](https://github.com/failsafe-lib/failsafe/workflows/build/badge.svg)](https://github.com/failsafe-lib/failsafe/actions) 4 | [![Maven Central](https://img.shields.io/maven-central/v/dev.failsafe/failsafe.svg?maxAge=60&colorB=53C92E)](https://maven-badges.herokuapp.com/maven-central/dev.failsafe/failsafe) 5 | [![License](http://img.shields.io/:license-apache-brightgreen.svg)](http://www.apache.org/licenses/LICENSE-2.0.html) 6 | [![Slack](https://img.shields.io/badge/slack-failsafe-brightgreen.svg?logo=slack)](https://failsafe-lib.slack.com) 7 | [![JavaDoc](https://img.shields.io/maven-central/v/dev.failsafe/failsafe.svg?maxAge=60&label=javadoc)](https://failsafe.dev/javadoc/core) 8 | 9 | Failsafe is a lightweight, zero-dependency library for handling failures in Java 8+, with a concise API for handling everyday use cases and the flexibility to handle everything else. It works by wrapping executable logic with one or more resilience policies, which can be combined and composed as needed. 10 | 11 | Policies include [Retry](https://failsafe.dev/retry/), [CircuitBreaker](https://failsafe.dev/circuit-breaker/), [RateLimiter](https://failsafe.dev/rate-limiter/), [Timeout](https://failsafe.dev/timeout/), [Bulkhead](https://failsafe.dev/bulkhead/), and [Fallback](https://failsafe.dev/fallback/). Additional modules include [OkHttp](https://failsafe.dev/okhttp/) and [Retrofit](https://failsafe.dev/retrofit/). 12 | 13 | ## Usage 14 | 15 | Visit [failsafe.dev](https://failsafe.dev) for usage info, docs, and additional resources. 16 | 17 | ## Contributing 18 | 19 | Check out the [contributing guidelines](https://github.com/failsafe-lib/failsafe/blob/master/CONTRIBUTING.md). 20 | 21 | ## License 22 | 23 | Copyright Jonathan Halterman and friends. Released under the [Apache 2.0 license](https://github.com/failsafe-lib/failsafe/blob/master/LICENSE). -------------------------------------------------------------------------------- /VERSIONING.md: -------------------------------------------------------------------------------- 1 | ### Versioning 2 | 3 | Failsafe follows MAJOR.MINOR.PATCH versioning where: 4 | 5 | - MAJOR versions contain significant new features and potentially significant incompatible API changes. 6 | - MINOR versions contain new features and potentially minor yet incompatible API changes. 7 | - PATCH versions contain bug fixes and minor new features that are fully backwards compatible. 8 | 9 | All versions, new features, and API changes are described in the [CHANGELOG](CHANGELOG.md). -------------------------------------------------------------------------------- /bin/push-javadoc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # run from top level dir 3 | 4 | ORG=failsafe-lib 5 | REPO=failsafe.dev 6 | 7 | pwd=`pwd` 8 | 9 | build () { 10 | echo "Building Javadocs for $1" 11 | cd $pwd 12 | if [ "$1" != "core" ]; then 13 | cd modules 14 | fi 15 | cd $1 16 | mvn javadoc:javadoc -Djv=$apiVersion 17 | rm -rf target/docs 18 | git clone git@github.com:$ORG/$REPO.git target/docs 19 | cd target/docs 20 | git rm -rf javadoc/$1 21 | mkdir -p javadoc/$1 22 | mv -v ../site/apidocs/* javadoc/$1 23 | 24 | patchFavIcon "javadoc" "../assets/images/favicon.png" 25 | commit && echo "Published Javadocs for $1" 26 | } 27 | 28 | patchFavIcon () { 29 | echo "Patching favicons" 30 | for f in $1/*.html ; do 31 | if [ -f "$f" ]; then # if no .html files exist, f is literal "*.html" 32 | tmpfile=`mktemp patch_favicon_XXXXX` 33 | # This creates tmpfile, with the same permissions as $f. 34 | # The next command will overwrite it but preserve the permissions. 35 | # Hat tip to http://superuser.com/questions/170226/standard-way-to-duplicate-a-files-permissions for this trick. 36 | \cp -p $f $tmpfile 37 | sed -e " s%\$%%" $f > $tmpfile 38 | DIFF=$(diff $f $tmpfile) 39 | if [ "$DIFF" != "" ] 40 | then 41 | echo "$f modified with favicon" 42 | fi 43 | mv -f $tmpfile $f 44 | fi; 45 | done ; 46 | for d in $1/* ; do 47 | if [ -d $d ]; then echo "descending to "$d ; patchFavIcon $d ../$2 ; fi ; 48 | done 49 | } 50 | 51 | commit() { 52 | echo "Committing javadocs" 53 | git add -A -f javadoc 54 | git commit -m "Updated JavaDocs" 55 | git push -fq > /dev/null 56 | } 57 | 58 | # Install parent and core 59 | echo "Installing parent and core artifacts" 60 | mvn install -N 61 | cd core 62 | mvn install -DskipTests=true 63 | cd ../ 64 | 65 | build "core" 66 | build "okhttp" 67 | build "retrofit" -------------------------------------------------------------------------------- /core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | dev.failsafe 7 | failsafe-parent 8 | 3.3.3-SNAPSHOT 9 | 10 | 11 | failsafe 12 | Failsafe 13 | 14 | 15 | ${project.groupId}.core 16 | 17 | 18 | 19 | 20 | 21 | org.moditect 22 | moditect-maven-plugin 23 | 24 | 25 | maven-jar-plugin 26 | 27 | 28 | 29 | test-jar 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/AsyncExecution.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe; 17 | 18 | import java.util.concurrent.CompletableFuture; 19 | 20 | /** 21 | * Allows asynchronous executions to record their results or complete an execution. 22 | * 23 | * @param result type 24 | * @author Jonathan Halterman 25 | */ 26 | public interface AsyncExecution extends ExecutionContext { 27 | /** 28 | * Completes the execution and the associated {@code CompletableFuture}. 29 | * 30 | * @throws IllegalStateException if the execution is already recorded or complete 31 | */ 32 | void complete(); 33 | 34 | /** 35 | * Returns whether the execution is complete or if it can be retried. An execution is considered complete only when 36 | * all configured policies consider the execution complete. 37 | */ 38 | boolean isComplete(); 39 | 40 | /** 41 | * Records an execution {@code result} or {@code exception} which triggers failure handling, if needed, by the 42 | * configured policies. If policy handling is not possible or already complete, the resulting {@link 43 | * CompletableFuture} is completed. 44 | * 45 | * @throws IllegalStateException if the most recent execution was already recorded or the execution is complete 46 | */ 47 | void record(R result, Throwable exception); 48 | 49 | /** 50 | * Records an execution {@code result} which triggers failure handling, if needed, by the configured policies. If 51 | * policy handling is not possible or already complete, the resulting {@link CompletableFuture} is completed. 52 | * 53 | * @throws IllegalStateException if the most recent execution was already recorded or the execution is complete 54 | */ 55 | void recordResult(R result); 56 | 57 | /** 58 | * Records an {@code exception} which triggers failure handling, if needed, by the configured policies. If policy 59 | * handling is not possible or already complete, the resulting {@link CompletableFuture} is completed. 60 | * 61 | * @throws IllegalStateException if the most recent execution was already recorded or the execution is complete 62 | */ 63 | void recordException(Throwable exception); 64 | } -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/BulkheadBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe; 17 | 18 | import dev.failsafe.internal.BulkheadImpl; 19 | import dev.failsafe.internal.util.Assert; 20 | 21 | import java.time.Duration; 22 | 23 | /** 24 | * Builds {@link Bulkhead} instances. 25 | *

26 | * This class is not threadsafe. 27 | *

28 | * 29 | * @param result type 30 | * @author Jonathan Halterman 31 | * @see BulkheadConfig 32 | * @see BulkheadFullException 33 | */ 34 | public class BulkheadBuilder extends PolicyBuilder, BulkheadConfig, R> { 35 | BulkheadBuilder(int maxConcurrency) { 36 | super(new BulkheadConfig<>(maxConcurrency)); 37 | } 38 | 39 | BulkheadBuilder(BulkheadConfig config) { 40 | super(new BulkheadConfig<>(config)); 41 | } 42 | 43 | /** 44 | * Builds a new {@link Bulkhead} using the builder's configuration. 45 | */ 46 | public Bulkhead build() { 47 | return new BulkheadImpl<>(new BulkheadConfig<>(config)); 48 | } 49 | 50 | /** 51 | * Configures the {@code maxWaitTime} to wait for permits to be available. If permits cannot be acquired before the 52 | * {@code maxWaitTime} is exceeded, then the bulkhead will throw {@link BulkheadFullException}. 53 | *

54 | * This setting only applies when the resulting Bulkhead is used with the {@link Failsafe} class. It does not apply 55 | * when the Bulkhead is used in a standalone way. 56 | *

57 | * 58 | * @throws NullPointerException if {@code maxWaitTime} is null 59 | */ 60 | public BulkheadBuilder withMaxWaitTime(Duration maxWaitTime) { 61 | config.maxWaitTime = Assert.notNull(maxWaitTime, "maxWaitTime"); 62 | return this; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/BulkheadConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe; 17 | 18 | import java.time.Duration; 19 | 20 | /** 21 | * Configuration for a {@link Bulkhead}. 22 | * 23 | * @param result type 24 | * @author Jonathan Halterman 25 | */ 26 | public class BulkheadConfig extends PolicyConfig { 27 | int maxConcurrency; 28 | Duration maxWaitTime; 29 | 30 | BulkheadConfig(int maxConcurrency) { 31 | this.maxConcurrency = maxConcurrency; 32 | maxWaitTime = Duration.ZERO; 33 | } 34 | 35 | BulkheadConfig(BulkheadConfig config) { 36 | super(config); 37 | maxConcurrency = config.maxConcurrency; 38 | maxWaitTime = config.maxWaitTime; 39 | } 40 | 41 | /** 42 | * Returns that max concurrent executions that are permitted within the bulkhead. 43 | * 44 | * @see Bulkhead#builder(int) 45 | */ 46 | public int getMaxConcurrency() { 47 | return maxConcurrency; 48 | } 49 | 50 | /** 51 | * Returns the max time to wait for permits to be available. If permits cannot be acquired before the max wait time is 52 | * exceeded, then the bulkhead will throw {@link BulkheadFullException}. 53 | *

54 | * This setting only applies when the Bulkhead is used with the {@link Failsafe} class. It does not apply when the 55 | * Bulkhead is used in a standalone way. 56 | *

57 | * 58 | * @see BulkheadBuilder#withMaxWaitTime(Duration) 59 | */ 60 | public Duration getMaxWaitTime() { 61 | return maxWaitTime; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/BulkheadFullException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe; 17 | 18 | /** 19 | * Thrown when an execution is attempted against a {@link Bulkhead} that is full. 20 | * 21 | * @author Jonathan Halterman 22 | */ 23 | public class BulkheadFullException extends FailsafeException { 24 | private static final long serialVersionUID = 1L; 25 | 26 | private final Bulkhead bulkhead; 27 | 28 | public BulkheadFullException(Bulkhead bulkhead) { 29 | this.bulkhead = bulkhead; 30 | } 31 | 32 | /** Returns the {@link Bulkhead} that caused the exception. */ 33 | public Bulkhead getBulkhead() { 34 | return bulkhead; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/Call.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe; 17 | 18 | import dev.failsafe.function.CheckedRunnable; 19 | 20 | /** 21 | * A call that can perform Failsafe executions and can be cancelled. Cancellations are propagated to any {@link 22 | * ExecutionContext#onCancel(CheckedRunnable) cancelCallback} that is registered. Useful for integrating with libraries 23 | * that support cancellation. 24 | *

25 | * To perform cancellable async executions, use the {@link FailsafeExecutor} async methods. 26 | *

27 | * 28 | * @param result type 29 | * @author Jonathan Halterman 30 | */ 31 | public interface Call { 32 | /** 33 | * Executes the call until a successful result is returned or the configured policies are exceeded. 34 | * 35 | * @throws FailsafeException if the execution fails with a checked Exception. {@link FailsafeException#getCause()} can 36 | * be used to learn the underlying checked exception. 37 | */ 38 | R execute(); 39 | 40 | /** 41 | * Cancels a synchronous execution and calls the most recent {@link ExecutionContext#onCancel(CheckedRunnable) 42 | * cancelCallback} that was registered. The execution is still allowed to complete and return a result. In addition to 43 | * using a {@link ExecutionContext#onCancel(CheckedRunnable) cancelCallback}, executions can cooperate with 44 | * cancellation by checking {@link ExecutionContext#isCancelled()}. 45 | * 46 | * @param mayInterruptIfRunning whether the execution should be interrupted 47 | * @return whether cancellation was successful or not. Returns {@code false} if the execution was already cancelled or 48 | * completed. 49 | */ 50 | boolean cancel(boolean mayInterruptIfRunning); 51 | 52 | /** 53 | * Returns whether the call has been cancelled. 54 | */ 55 | boolean isCancelled(); 56 | } 57 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/CallImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe; 17 | 18 | /** 19 | * A call implementation that delegates to an execution. 20 | * 21 | * @param result type 22 | * @author Jonathan Halterman 23 | */ 24 | class CallImpl implements Call { 25 | private volatile SyncExecutionImpl execution; 26 | 27 | void setExecution(SyncExecutionImpl execution) { 28 | this.execution = execution; 29 | } 30 | 31 | @Override 32 | public R execute() { 33 | return execution.executeSync(); 34 | } 35 | 36 | @Override 37 | public boolean cancel(boolean mayInterruptIfRunning) { 38 | boolean result = execution.cancel(); 39 | if (mayInterruptIfRunning) 40 | execution.interrupt(); 41 | return result; 42 | } 43 | 44 | @Override 45 | public boolean isCancelled() { 46 | return execution.isCancelled(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/CircuitBreakerOpenException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe; 17 | 18 | /** 19 | * Thrown when an execution is attempted against a {@link CircuitBreaker} that is open. 20 | * 21 | * @author Jonathan Halterman 22 | */ 23 | public class CircuitBreakerOpenException extends FailsafeException { 24 | private static final long serialVersionUID = 1L; 25 | 26 | private final CircuitBreaker circuitBreaker; 27 | 28 | public CircuitBreakerOpenException(CircuitBreaker circuitBreaker) { 29 | this.circuitBreaker = circuitBreaker; 30 | } 31 | 32 | /** Returns the {@link CircuitBreaker} that caused the exception. */ 33 | public CircuitBreaker getCircuitBreaker() { 34 | return circuitBreaker; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/DelayablePolicyConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe; 17 | 18 | import dev.failsafe.function.ContextualSupplier; 19 | 20 | import java.time.Duration; 21 | 22 | /** 23 | * Configuration for policies that can delay between executions. 24 | * 25 | * @param result type 26 | * @author Jonathan Halterman 27 | */ 28 | public abstract class DelayablePolicyConfig extends FailurePolicyConfig { 29 | Duration delay; 30 | R delayResult; 31 | Class delayException; 32 | ContextualSupplier delayFn; 33 | 34 | protected DelayablePolicyConfig() { 35 | } 36 | 37 | protected DelayablePolicyConfig(DelayablePolicyConfig config) { 38 | super(config); 39 | delay = config.delay; 40 | delayResult = config.delayResult; 41 | delayException = config.delayException; 42 | delayFn = config.delayFn; 43 | } 44 | 45 | /** 46 | * Returns the delay until the next execution attempt can be performed. 47 | * 48 | * @see DelayablePolicyBuilder#withDelay(Duration) 49 | */ 50 | public Duration getDelay() { 51 | return delay; 52 | } 53 | 54 | /** 55 | * Returns the function that determines the next delay before another execution can be performed. 56 | * 57 | * @see DelayablePolicyBuilder#withDelayFn(ContextualSupplier) 58 | * @see DelayablePolicyBuilder#withDelayFnOn(ContextualSupplier, Class) 59 | * @see DelayablePolicyBuilder#withDelayFnWhen(ContextualSupplier, Object) 60 | */ 61 | public ContextualSupplier getDelayFn() { 62 | return delayFn; 63 | } 64 | 65 | /** 66 | * Returns the Throwable that must be matched in order to delay using the {@link #getDelayFn()}. 67 | * 68 | * @see DelayablePolicyBuilder#withDelayFnOn(ContextualSupplier, Class) 69 | */ 70 | public Class getDelayException() { 71 | return delayException; 72 | } 73 | 74 | /** 75 | * Returns the result that must be matched in order to delay using the {@link #getDelayFn()}. 76 | * 77 | * @see DelayablePolicyBuilder#withDelayFnWhen(ContextualSupplier, Object) 78 | */ 79 | public R getDelayResult() { 80 | return delayResult; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/FailsafeException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe; 17 | 18 | /** 19 | * Thrown when a synchronous Failsafe execution fails with an {@link Exception}, wrapping the underlying exception. Use 20 | * {@link Throwable#getCause()} to learn the cause of the failure. 21 | * 22 | * @author Jonathan Halterman 23 | */ 24 | public class FailsafeException extends RuntimeException { 25 | private static final long serialVersionUID = 1L; 26 | 27 | public FailsafeException() { 28 | } 29 | 30 | public FailsafeException(Throwable t) { 31 | super(t); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/FailurePolicyConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe; 17 | 18 | import dev.failsafe.function.CheckedBiPredicate; 19 | import dev.failsafe.function.CheckedPredicate; 20 | 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | 24 | /** 25 | * Configuration for policies that handle specific failures and conditions. 26 | * 27 | * @param result type 28 | * @author Jonathan Halterman 29 | */ 30 | public abstract class FailurePolicyConfig extends PolicyConfig { 31 | /** Indicates whether exceptions are checked by a configured failure condition */ 32 | boolean exceptionsChecked; 33 | /** Conditions that determine whether an execution is a failure */ 34 | List> failureConditions; 35 | 36 | protected FailurePolicyConfig() { 37 | failureConditions = new ArrayList<>(); 38 | } 39 | 40 | protected FailurePolicyConfig(FailurePolicyConfig config) { 41 | super(config); 42 | exceptionsChecked = config.exceptionsChecked; 43 | failureConditions = new ArrayList<>(config.failureConditions); 44 | } 45 | 46 | /** 47 | * Returns whether exceptions are checked by a configured failure condition. 48 | */ 49 | public boolean isExceptionsChecked() { 50 | return exceptionsChecked; 51 | } 52 | 53 | /** 54 | * Returns the conditions under which a result or Throwable should be treated as a failure and handled. 55 | * 56 | * @see FailurePolicyBuilder#handle(Class...) 57 | * @see FailurePolicyBuilder#handle(List) 58 | * @see FailurePolicyBuilder#handleIf(CheckedBiPredicate) 59 | * @see FailurePolicyBuilder#handleIf(CheckedPredicate) 60 | * @see FailurePolicyBuilder#handleResult(Object) 61 | * @see FailurePolicyBuilder#handleResultIf(CheckedPredicate) 62 | */ 63 | public List> getFailureConditions() { 64 | return failureConditions; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/Policy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe; 17 | 18 | import dev.failsafe.spi.PolicyExecutor; 19 | 20 | /** 21 | * A policy for handling executions. 22 | * 23 | * @param result type 24 | * @author Jonathan Halterman 25 | */ 26 | public interface Policy { 27 | /** 28 | * Returns the policy config. 29 | */ 30 | PolicyConfig getConfig(); 31 | 32 | /** 33 | * Returns a {@link PolicyExecutor} capable of handling an execution for the Policy. 34 | */ 35 | PolicyExecutor toExecutor(int policyIndex); 36 | } 37 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/PolicyBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe; 17 | 18 | import dev.failsafe.event.EventListener; 19 | import dev.failsafe.internal.util.Assert; 20 | import dev.failsafe.event.ExecutionCompletedEvent; 21 | 22 | /** 23 | * Builds policies. 24 | * 25 | * @param self type 26 | * @param config type 27 | * @param result type 28 | * @author Jonathan Halterman 29 | */ 30 | @SuppressWarnings("unchecked") 31 | public abstract class PolicyBuilder, R> implements PolicyListeners { 32 | protected C config; 33 | 34 | protected PolicyBuilder(C config) { 35 | this.config = config; 36 | } 37 | 38 | public S onFailure(EventListener> listener) { 39 | config.failureListener = Assert.notNull(listener, "listener"); 40 | return (S) this; 41 | } 42 | 43 | @Override 44 | public S onSuccess(EventListener> listener) { 45 | config.successListener = Assert.notNull(listener, "listener"); 46 | return (S) this; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/PolicyConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe; 17 | 18 | import dev.failsafe.event.EventListener; 19 | import dev.failsafe.event.ExecutionCompletedEvent; 20 | 21 | /** 22 | * Configuration for a {@link Policy}. 23 | * 24 | * @param result type 25 | * @author Jonathan Halterman 26 | */ 27 | public abstract class PolicyConfig { 28 | volatile EventListener> successListener; 29 | volatile EventListener> failureListener; 30 | 31 | protected PolicyConfig() { 32 | } 33 | 34 | protected PolicyConfig(PolicyConfig config) { 35 | successListener = config.successListener; 36 | failureListener = config.failureListener; 37 | } 38 | 39 | /** 40 | * Returns the success listener. 41 | * 42 | * @see PolicyListeners#onSuccess(EventListener) 43 | */ 44 | public EventListener> getSuccessListener() { 45 | return successListener; 46 | } 47 | 48 | /** 49 | * Returns the failure listener. 50 | * 51 | * @see PolicyListeners#onFailure(EventListener) 52 | */ 53 | public EventListener> getFailureListener() { 54 | return failureListener; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/PolicyListeners.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe; 17 | 18 | import dev.failsafe.event.EventListener; 19 | import dev.failsafe.event.ExecutionCompletedEvent; 20 | 21 | /** 22 | * Configures listeners for a policy execution result. 23 | * 24 | * @param self type 25 | * @param result type 26 | * @author Jonathan Halterman 27 | */ 28 | public interface PolicyListeners { 29 | /** 30 | * Registers the {@code listener} to be called when the policy fails to handle an execution. This means that not only 31 | * was the supplied execution considered a failure by the policy, but that the policy was unable to produce a 32 | * successful result. 33 | *

Note: Any exceptions that are thrown from within the {@code listener} are ignored. To provide an alternative 34 | * result for a failed execution, use a {@link Fallback}.

35 | */ 36 | S onFailure(EventListener> listener); 37 | 38 | /** 39 | * Registers the {@code listener} to be called when the policy succeeds in handling an execution. This means that the 40 | * supplied execution either succeeded, or if it failed, the policy was able to produce a successful result. 41 | *

Note: Any exceptions that are thrown from within the {@code listener} are ignored.

42 | */ 43 | S onSuccess(EventListener> listener); 44 | } 45 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/RateLimitExceededException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe; 17 | 18 | /** 19 | * Thrown when an execution exceeds or would exceed a {@link RateLimiter}. 20 | * 21 | * @author Jonathan Halterman 22 | */ 23 | public class RateLimitExceededException extends FailsafeException { 24 | private static final long serialVersionUID = 1L; 25 | 26 | private final RateLimiter rateLimiter; 27 | 28 | public RateLimitExceededException(RateLimiter rateLimiter) { 29 | this.rateLimiter = rateLimiter; 30 | } 31 | 32 | /** Returns the {@link RateLimiter} that caused the exception. */ 33 | public RateLimiter getRateLimiter() { 34 | return rateLimiter; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/RateLimiterBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe; 17 | 18 | import dev.failsafe.internal.RateLimiterImpl; 19 | import dev.failsafe.internal.util.Assert; 20 | 21 | import java.time.Duration; 22 | 23 | /** 24 | * Builds {@link RateLimiter} instances. 25 | *

26 | * This class is not threadsafe. 27 | *

28 | * 29 | * @param result type 30 | * @author Jonathan Halterman 31 | * @see RateLimiterConfig 32 | * @see RateLimitExceededException 33 | */ 34 | public class RateLimiterBuilder extends PolicyBuilder, RateLimiterConfig, R> { 35 | RateLimiterBuilder(Duration executionRate) { 36 | super(new RateLimiterConfig<>(executionRate)); 37 | config.maxWaitTime = Duration.ZERO; 38 | } 39 | 40 | RateLimiterBuilder(long maxPermits, Duration period) { 41 | super(new RateLimiterConfig<>(maxPermits, period)); 42 | config.maxWaitTime = Duration.ZERO; 43 | } 44 | 45 | RateLimiterBuilder(RateLimiterConfig config) { 46 | super(new RateLimiterConfig<>(config)); 47 | } 48 | 49 | /** 50 | * Builds a new {@link RateLimiter} using the builder's configuration. 51 | */ 52 | public RateLimiter build() { 53 | return new RateLimiterImpl<>(new RateLimiterConfig<>(config)); 54 | } 55 | 56 | /** 57 | * Configures the {@code maxWaitTime} to wait for permits to be available. If permits cannot be acquired before the 58 | * {@code maxWaitTime} is exceeded, then the rate limiter will throw {@link RateLimitExceededException}. 59 | *

60 | * This setting only applies when the resulting RateLimiter is used with the {@link Failsafe} class. It does not apply 61 | * when the RateLimiter is used in a standalone way. 62 | *

63 | * 64 | * @throws NullPointerException if {@code maxWaitTime} is null 65 | */ 66 | public RateLimiterBuilder withMaxWaitTime(Duration maxWaitTime) { 67 | config.maxWaitTime = Assert.notNull(maxWaitTime, "maxWaitTime"); 68 | return this; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/RetryPolicy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe; 17 | 18 | /** 19 | * A policy that defines when retries should be performed. See {@link RetryPolicyBuilder} for configuration options. 20 | *

21 | * This class is threadsafe. 22 | *

23 | * 24 | * @param result type 25 | * @author Jonathan Halterman 26 | * @see RetryPolicyConfig 27 | * @see RetryPolicyBuilder 28 | */ 29 | public interface RetryPolicy extends Policy { 30 | /** 31 | * Creates a RetryPolicyBuilder that by default will build a RetryPolicy that allows 3 execution attempts max with no 32 | * delay, unless configured otherwise. 33 | * 34 | * @see #ofDefaults() 35 | */ 36 | static RetryPolicyBuilder builder() { 37 | return new RetryPolicyBuilder<>(); 38 | } 39 | 40 | /** 41 | * Creates a new RetryPolicyBuilder that will be based on the {@code config}. 42 | */ 43 | static RetryPolicyBuilder builder(RetryPolicyConfig config) { 44 | return new RetryPolicyBuilder<>(config); 45 | } 46 | 47 | /** 48 | * Creates a RetryPolicy that allows 3 execution attempts max with no delay. To configure additional options on a 49 | * RetryPolicy, use {@link #builder()} instead. 50 | * 51 | * @see #builder() 52 | */ 53 | static RetryPolicy ofDefaults() { 54 | return RetryPolicy.builder().build(); 55 | } 56 | 57 | /** 58 | * Returns the {@link RetryPolicyConfig} that the RetryPolicy was built with. 59 | */ 60 | @Override 61 | RetryPolicyConfig getConfig(); 62 | } 63 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/TimeoutBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe; 17 | 18 | import dev.failsafe.function.AsyncRunnable; 19 | import dev.failsafe.internal.TimeoutImpl; 20 | 21 | import java.time.Duration; 22 | 23 | /** 24 | * Builds {@link Timeout} instances. 25 | *

26 | * This class is not threadsafe. 27 | *

28 | * 29 | * @param result type 30 | * @author Jonathan Halterman 31 | * @see TimeoutConfig 32 | * @see TimeoutExceededException 33 | */ 34 | public class TimeoutBuilder extends PolicyBuilder, TimeoutConfig, R> { 35 | TimeoutBuilder(Duration timeout) { 36 | super(new TimeoutConfig<>(timeout, false)); 37 | } 38 | 39 | TimeoutBuilder(TimeoutConfig config) { 40 | super(new TimeoutConfig<>(config)); 41 | } 42 | 43 | /** 44 | * Builds a new {@link Timeout} using the builder's configuration. 45 | */ 46 | public Timeout build() { 47 | return new TimeoutImpl<>(new TimeoutConfig<>(config)); 48 | } 49 | 50 | /** 51 | * Configures the policy to interrupt an execution in addition to cancelling it when the timeout is exceeded. For 52 | * synchronous executions this is done by calling {@link Thread#interrupt()} on the execution's thread. For 53 | * asynchronous executions this is done by calling {@link java.util.concurrent.Future#cancel(boolean) 54 | * Future.cancel(true)}. Executions can internally cooperate with interruption by checking {@link 55 | * Thread#isInterrupted()} or by handling {@link InterruptedException} where available. 56 | *

57 | * Note: Only configure interrupts if the code being executed is designed to be interrupted. 58 | *

59 | *

Note: interruption will have no effect when performing an {@link 60 | * FailsafeExecutor#getAsyncExecution(AsyncRunnable) async execution} since the async thread is unknown to 61 | * Failsafe.

62 | */ 63 | public TimeoutBuilder withInterrupt() { 64 | config.canInterrupt = true; 65 | return this; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/TimeoutConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe; 17 | 18 | import java.time.Duration; 19 | 20 | /** 21 | * Configuration for a {@link Timeout}. 22 | *

23 | * This class is threadsafe. 24 | *

25 | * 26 | * @param result type 27 | * @author Jonathan Halterman 28 | * @see TimeoutBuilder 29 | */ 30 | public class TimeoutConfig extends PolicyConfig { 31 | Duration timeout; 32 | boolean canInterrupt; 33 | 34 | TimeoutConfig(Duration timeout, boolean canInterrupt) { 35 | this.timeout = timeout; 36 | this.canInterrupt = canInterrupt; 37 | } 38 | 39 | TimeoutConfig(TimeoutConfig config) { 40 | super(config); 41 | timeout = config.timeout; 42 | canInterrupt = config.canInterrupt; 43 | } 44 | 45 | /** 46 | * Returns the timeout duration. 47 | */ 48 | public Duration getTimeout() { 49 | return timeout; 50 | } 51 | 52 | /** 53 | * Returns whether the policy can interrupt an execution if the timeout is exceeded. 54 | * 55 | * @see TimeoutBuilder#withInterrupt() 56 | */ 57 | public boolean canInterrupt() { 58 | return canInterrupt; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/TimeoutExceededException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe; 17 | 18 | /** 19 | * Thrown when an execution exceeds a configured {@link Timeout}. 20 | * 21 | * @author Jonathan Halterman 22 | */ 23 | public class TimeoutExceededException extends FailsafeException { 24 | private static final long serialVersionUID = 1L; 25 | 26 | private final Timeout timeout; 27 | 28 | public TimeoutExceededException(Timeout timeout) { 29 | this.timeout = timeout; 30 | } 31 | 32 | /** Returns the {@link Timeout} that caused the exception. */ 33 | public Timeout getTimeout() { 34 | return timeout; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/event/CircuitBreakerStateChangedEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.event; 17 | 18 | import dev.failsafe.CircuitBreaker.State; 19 | 20 | /** 21 | * Indicates a circuit breaker's state changed. 22 | * 23 | * @author Jonathan Halterman 24 | */ 25 | public class CircuitBreakerStateChangedEvent { 26 | private final State previousState; 27 | 28 | public CircuitBreakerStateChangedEvent(State previousState) { 29 | this.previousState = previousState; 30 | } 31 | 32 | /** 33 | * Returns the previous state of the circuit breaker. 34 | */ 35 | public State getPreviousState() { 36 | return previousState; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/event/EventListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.event; 17 | 18 | /** 19 | * Listens for events. 20 | * 21 | * @param event type 22 | */ 23 | @FunctionalInterface 24 | public interface EventListener { 25 | void accept(E event) throws Throwable; 26 | 27 | /** 28 | * Accepts an {@code event} and ignores any exceptions that result. 29 | */ 30 | default void acceptUnchecked(E event) { 31 | try { 32 | accept(event); 33 | } catch (Throwable ignore) { 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/event/ExecutionAttemptedEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.event; 17 | 18 | import dev.failsafe.ExecutionContext; 19 | 20 | /** 21 | * Indicates an execution was attempted. 22 | * 23 | * @param result type 24 | * @author Jonathan Halterman 25 | */ 26 | public class ExecutionAttemptedEvent extends ExecutionEvent { 27 | private final R result; 28 | private final Throwable exception; 29 | 30 | public ExecutionAttemptedEvent(R result, Throwable exception, ExecutionContext context) { 31 | super(context); 32 | this.result = result; 33 | this.exception = exception; 34 | } 35 | 36 | /** 37 | * Returns the failure that preceded the event, else {@code null} if there was none. 38 | */ 39 | public Throwable getLastException() { 40 | return exception; 41 | } 42 | 43 | /** 44 | * Returns the result that preceded the event, else {@code null} if there was none. 45 | */ 46 | public R getLastResult() { 47 | return result; 48 | } 49 | 50 | @Override 51 | public String toString() { 52 | return "ExecutionAttemptedEvent[" + "result=" + result + ", exception=" + exception + ']'; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/event/ExecutionCompletedEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.event; 17 | 18 | import dev.failsafe.ExecutionContext; 19 | 20 | /** 21 | * Indicates an execution was completed or cancelled. 22 | * 23 | * @param result type 24 | * @author Jonathan Halterman 25 | */ 26 | public class ExecutionCompletedEvent extends ExecutionEvent { 27 | private final R result; 28 | private final Throwable exception; 29 | 30 | public ExecutionCompletedEvent(R result, Throwable exception, ExecutionContext context) { 31 | super(context); 32 | this.result = result; 33 | this.exception = exception; 34 | } 35 | 36 | /** 37 | * Returns the failure that preceded the event, else {@code null} if there was none. 38 | */ 39 | public Throwable getException() { 40 | return exception; 41 | } 42 | 43 | /** 44 | * Returns the result that preceded the event, else {@code null} if there was none. 45 | */ 46 | public R getResult() { 47 | return result; 48 | } 49 | 50 | @Override 51 | public String toString() { 52 | return "ExecutionCompletedEvent[" + "result=" + result + ", exception=" + exception + ']'; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/event/ExecutionEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.event; 17 | 18 | import dev.failsafe.CircuitBreaker; 19 | import dev.failsafe.ExecutionContext; 20 | 21 | import java.time.Duration; 22 | import java.time.Instant; 23 | import java.util.Optional; 24 | 25 | /** 26 | * Encapsulates information about a Failsafe execution. 27 | * 28 | * @author Jonathan Halterman 29 | */ 30 | public abstract class ExecutionEvent { 31 | private final ExecutionContext context; 32 | 33 | ExecutionEvent(ExecutionContext context) { 34 | this.context = context; 35 | } 36 | 37 | /** 38 | * Returns the elapsed time since initial execution began. 39 | */ 40 | public Duration getElapsedTime() { 41 | return context.getElapsedTime(); 42 | } 43 | 44 | /** 45 | * Gets the number of execution attempts so far, including attempts that are blocked before being executed, such as 46 | * when a {@link CircuitBreaker CircuitBreaker} is open. Will return {@code 0} when the first 47 | * attempt is in progress or has yet to begin. 48 | */ 49 | public int getAttemptCount() { 50 | return context.getAttemptCount(); 51 | } 52 | 53 | /** 54 | * Gets the number of completed executions so far. Executions that are blocked, such as when a {@link 55 | * CircuitBreaker CircuitBreaker} is open, are not counted. Will return {@code 0} when the first 56 | * attempt is in progress or has yet to begin. 57 | */ 58 | public int getExecutionCount() { 59 | return context.getExecutionCount(); 60 | } 61 | 62 | /** 63 | * Returns the time that the initial execution started, else {@link Optional#empty()} if an execution has not started yet. 64 | */ 65 | public Optional getStartTime() { 66 | return Optional.ofNullable(context.getStartTime()); 67 | } 68 | 69 | /** 70 | * Returns the elapsed time since the last execution attempt began. 71 | */ 72 | public Duration getElapsedAttemptTime() { 73 | return context.getElapsedAttemptTime(); 74 | } 75 | 76 | /** 77 | * Returns {@code true} when {@link #getAttemptCount()} is {@code 0} meaning this is the first execution attempt. 78 | */ 79 | public boolean isFirstAttempt() { 80 | return context.isFirstAttempt(); 81 | } 82 | 83 | /** 84 | * Returns {@code true} when {@link #getAttemptCount()} is {@code > 0} meaning the execution is being retried. 85 | */ 86 | public boolean isRetry() { 87 | return context.isRetry(); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/event/ExecutionScheduledEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.event; 17 | 18 | import dev.failsafe.ExecutionContext; 19 | import dev.failsafe.Timeout; 20 | import dev.failsafe.spi.Scheduler; 21 | 22 | import java.time.Duration; 23 | 24 | /** 25 | * Indicates an execution was scheduled. A scheduled execution will be executed after the {@link #getDelay() delay} 26 | * unless it is cancelled, either explicitly or via {@link java.util.concurrent.Future#cancel(boolean) 27 | * Future.cancel(boolean)}, a {@link Timeout Timeout}, or if the underlying {@link Scheduler Scheduler} or {@link 28 | * java.util.concurrent.ExecutorService ExecutorService} is shutdown. 29 | * 30 | * @param result type 31 | * @author Jonathan Halterman 32 | */ 33 | public class ExecutionScheduledEvent extends ExecutionEvent { 34 | private final R result; 35 | private final Throwable exception; 36 | private final Duration delay; 37 | 38 | public ExecutionScheduledEvent(R result, Throwable exception, Duration delay, ExecutionContext context) { 39 | super(context); 40 | this.result = result; 41 | this.exception = exception; 42 | this.delay = delay; 43 | } 44 | 45 | /** 46 | * Returns the failure that preceded the event, else {@code null} if there was none. 47 | */ 48 | public Throwable getLastException() { 49 | return exception; 50 | } 51 | 52 | /** 53 | * Returns the result that preceded the event, else {@code null} if there was none. 54 | */ 55 | public R getLastResult() { 56 | return result; 57 | } 58 | 59 | /** 60 | * Returns the delay before the next execution attempt. 61 | */ 62 | public Duration getDelay() { 63 | return delay; 64 | } 65 | 66 | @Override 67 | public String toString() { 68 | return "ExecutionScheduledEvent[" + "result=" + result + ", exception=" + exception + ", delay=" + delay + ']'; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/event/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Event listener types. 3 | */ 4 | package dev.failsafe.event; 5 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/function/AsyncRunnable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.function; 17 | 18 | import dev.failsafe.AsyncExecution; 19 | 20 | /** 21 | * A Runnable that manually triggers asynchronous retries or completion via an asynchronous execution. 22 | * 23 | * @param result type 24 | * @author Jonathan Halterman 25 | */ 26 | @FunctionalInterface 27 | public interface AsyncRunnable { 28 | void run(AsyncExecution execution) throws Exception; 29 | } 30 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/function/AsyncSupplier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.function; 17 | 18 | import dev.failsafe.AsyncExecution; 19 | 20 | /** 21 | * A Supplier that manually triggers asynchronous retries or completion via an asynchronous execution. 22 | * 23 | * @param result type 24 | * @param supplied type 25 | * @author Jonathan Halterman 26 | */ 27 | @FunctionalInterface 28 | public interface AsyncSupplier { 29 | T get(AsyncExecution execution) throws Exception; 30 | } 31 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/function/CheckedBiPredicate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.function; 17 | 18 | /** 19 | * A BiPredicate that throws checked exceptions. 20 | * 21 | * @param first input type 22 | * @param second input type 23 | * @author Jonathan Halterman 24 | */ 25 | @FunctionalInterface 26 | public interface CheckedBiPredicate { 27 | boolean test(T t, U u) throws Throwable; 28 | } 29 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/function/CheckedConsumer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.function; 17 | 18 | /** 19 | * A Consumer that throws checked exceptions. 20 | * 21 | * @author Jonathan Halterman 22 | * @param input type 23 | */ 24 | @FunctionalInterface 25 | public interface CheckedConsumer { 26 | void accept(T t) throws Throwable; 27 | } -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/function/CheckedFunction.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.function; 17 | 18 | /** 19 | * A Function that throws checked exceptions. 20 | * 21 | * @author Jonathan Halterman 22 | * @param input type 23 | * @param result type 24 | */ 25 | @FunctionalInterface 26 | public interface CheckedFunction { 27 | R apply(T t) throws Throwable; 28 | } 29 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/function/CheckedPredicate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.function; 17 | 18 | /** 19 | * A Predicate that throws checked exceptions. 20 | * 21 | * @param input type 22 | * @author Jonathan Halterman 23 | */ 24 | @FunctionalInterface 25 | public interface CheckedPredicate { 26 | boolean test(T t) throws Throwable; 27 | } 28 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/function/CheckedRunnable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.function; 17 | 18 | /** 19 | * A Runnable that throws checked exceptions. 20 | * 21 | * @author Jonathan Halterman 22 | */ 23 | @FunctionalInterface 24 | public interface CheckedRunnable { 25 | void run() throws Throwable; 26 | } 27 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/function/CheckedSupplier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.function; 17 | 18 | /** 19 | * A Supplier that throws checked exceptions. 20 | * 21 | * @param supplied type 22 | * @author Jonathan Halterman 23 | */ 24 | @FunctionalInterface 25 | public interface CheckedSupplier { 26 | T get() throws Throwable; 27 | } 28 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/function/ContextualRunnable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.function; 17 | 18 | import dev.failsafe.ExecutionContext; 19 | 20 | /** 21 | * A Runnable that provides execution context. 22 | * 23 | * @param result type 24 | * @author Jonathan Halterman 25 | */ 26 | @FunctionalInterface 27 | public interface ContextualRunnable { 28 | void run(ExecutionContext context) throws Throwable; 29 | } 30 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/function/ContextualSupplier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.function; 17 | 18 | import dev.failsafe.ExecutionContext; 19 | 20 | /** 21 | * A Supplier that provides execution context. 22 | * 23 | * @param result type 24 | * @param supplied type 25 | * @author Jonathan Halterman 26 | */ 27 | @FunctionalInterface 28 | public interface ContextualSupplier { 29 | T get(ExecutionContext context) throws Throwable; 30 | } 31 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/function/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Functional interface types. 3 | */ 4 | package dev.failsafe.function; 5 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/internal/CircuitBreakerExecutor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.internal; 17 | 18 | import dev.failsafe.CircuitBreakerOpenException; 19 | import dev.failsafe.ExecutionContext; 20 | import dev.failsafe.spi.ExecutionResult; 21 | import dev.failsafe.spi.PolicyExecutor; 22 | import dev.failsafe.CircuitBreaker; 23 | 24 | /** 25 | * A PolicyExecutor that handles failures according to a {@link CircuitBreaker}. 26 | * 27 | * @param result type 28 | * @author Jonathan Halterman 29 | */ 30 | public class CircuitBreakerExecutor extends PolicyExecutor { 31 | private final CircuitBreakerImpl circuitBreaker; 32 | 33 | public CircuitBreakerExecutor(CircuitBreakerImpl circuitBreaker, int policyIndex) { 34 | super(circuitBreaker, policyIndex); 35 | this.circuitBreaker = circuitBreaker; 36 | } 37 | 38 | @Override 39 | protected ExecutionResult preExecute() { 40 | return circuitBreaker.tryAcquirePermit() ? 41 | null : 42 | ExecutionResult.exception(new CircuitBreakerOpenException(circuitBreaker)); 43 | } 44 | 45 | @Override 46 | public void onSuccess(ExecutionResult result) { 47 | circuitBreaker.recordSuccess(); 48 | } 49 | 50 | @Override 51 | protected ExecutionResult onFailure(ExecutionContext context, ExecutionResult result) { 52 | circuitBreaker.recordExecutionFailure(context); 53 | return result; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/internal/CircuitState.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.internal; 17 | 18 | import dev.failsafe.CircuitBreaker.State; 19 | import dev.failsafe.CircuitBreakerConfig; 20 | import dev.failsafe.CircuitBreakerOpenException; 21 | import dev.failsafe.ExecutionContext; 22 | 23 | import java.time.Duration; 24 | 25 | /** 26 | * The state of a circuit. 27 | * 28 | * @param result type 29 | * @author Jonathan Halterman 30 | */ 31 | abstract class CircuitState { 32 | final CircuitBreakerImpl breaker; 33 | final CircuitBreakerConfig config; 34 | volatile CircuitStats stats; 35 | 36 | CircuitState(CircuitBreakerImpl breaker, CircuitStats stats) { 37 | this.breaker = breaker; 38 | this.config = breaker.getConfig(); 39 | this.stats = stats; 40 | } 41 | 42 | public Duration getRemainingDelay() { 43 | return Duration.ZERO; 44 | } 45 | 46 | public CircuitStats getStats() { 47 | return stats; 48 | } 49 | 50 | public abstract State getState(); 51 | 52 | public synchronized void recordFailure(ExecutionContext context) { 53 | stats.recordFailure(); 54 | checkThreshold(context); 55 | releasePermit(); 56 | } 57 | 58 | public synchronized void recordSuccess() { 59 | stats.recordSuccess(); 60 | checkThreshold(null); 61 | releasePermit(); 62 | } 63 | 64 | public void handleConfigChange() { 65 | } 66 | 67 | void checkThreshold(ExecutionContext context) { 68 | } 69 | 70 | abstract boolean tryAcquirePermit(); 71 | 72 | void releasePermit() { 73 | } 74 | } -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/internal/CircuitStats.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.internal; 17 | 18 | import dev.failsafe.CircuitBreaker; 19 | import dev.failsafe.internal.TimedCircuitStats.Clock; 20 | 21 | /** 22 | * Stats for a circuit breaker. 23 | */ 24 | interface CircuitStats { 25 | static CircuitStats create(CircuitBreaker breaker, int capacity, boolean supportsTimeBased, 26 | CircuitStats oldStats) { 27 | if (supportsTimeBased && breaker.getConfig().getFailureThresholdingPeriod() != null) 28 | return new TimedCircuitStats(TimedCircuitStats.DEFAULT_BUCKET_COUNT, 29 | breaker.getConfig().getFailureThresholdingPeriod(), new Clock(), oldStats); 30 | else if (capacity > 1) { 31 | return new CountingCircuitStats(capacity, oldStats); 32 | } else { 33 | return new DefaultCircuitStats(); 34 | } 35 | } 36 | 37 | default void copyExecutions(CircuitStats oldStats) { 38 | for (int i = 0; i < oldStats.getSuccessCount(); i++) 39 | recordSuccess(); 40 | for (int i = 0; i < oldStats.getFailureCount(); i++) 41 | recordFailure(); 42 | } 43 | 44 | int getFailureCount(); 45 | 46 | int getExecutionCount(); 47 | 48 | int getSuccessCount(); 49 | 50 | int getFailureRate(); 51 | 52 | int getSuccessRate(); 53 | 54 | void recordFailure(); 55 | 56 | void recordSuccess(); 57 | 58 | void reset(); 59 | } 60 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/internal/ClosedState.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.internal; 17 | 18 | import dev.failsafe.CircuitBreaker; 19 | import dev.failsafe.CircuitBreaker.State; 20 | import dev.failsafe.ExecutionContext; 21 | 22 | class ClosedState extends CircuitState { 23 | public ClosedState(CircuitBreakerImpl breaker) { 24 | super(breaker, CircuitStats.create(breaker, capacityFor(breaker), true, null)); 25 | } 26 | 27 | @Override 28 | public boolean tryAcquirePermit() { 29 | return true; 30 | } 31 | 32 | @Override 33 | public State getState() { 34 | return State.CLOSED; 35 | } 36 | 37 | @Override 38 | public synchronized void handleConfigChange() { 39 | stats = CircuitStats.create(breaker, capacityFor(breaker), true, stats); 40 | } 41 | 42 | /** 43 | * Checks to see if the executions and failure thresholds have been exceeded, opening the circuit if so. 44 | */ 45 | @Override 46 | synchronized void checkThreshold(ExecutionContext context) { 47 | // Execution threshold can only be set for time based thresholding 48 | if (stats.getExecutionCount() >= config.getFailureExecutionThreshold()) { 49 | // Failure rate threshold can only be set for time based thresholding 50 | double failureRateThreshold = config.getFailureRateThreshold(); 51 | if ((failureRateThreshold != 0 && stats.getFailureRate() >= failureRateThreshold) || (failureRateThreshold == 0 52 | && stats.getFailureCount() >= config.getFailureThreshold())) 53 | breaker.open(context); 54 | } 55 | } 56 | 57 | /** 58 | * Returns the capacity of the breaker in the closed state. 59 | */ 60 | private static int capacityFor(CircuitBreaker breaker) { 61 | if (breaker.getConfig().getFailureExecutionThreshold() != 0) 62 | return breaker.getConfig().getFailureExecutionThreshold(); 63 | else 64 | return breaker.getConfig().getFailureThresholdingCapacity(); 65 | } 66 | } -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/internal/DefaultCircuitStats.java: -------------------------------------------------------------------------------- 1 | package dev.failsafe.internal; 2 | 3 | /** 4 | * A default CircuitStats implementation that tracks a single execution result. 5 | */ 6 | class DefaultCircuitStats implements CircuitStats { 7 | volatile int result = -1; 8 | 9 | @Override 10 | public int getFailureCount() { 11 | return result == 0 ? 1 : 0; 12 | } 13 | 14 | @Override 15 | public int getExecutionCount() { 16 | return result == -1 ? 0 : 1; 17 | } 18 | 19 | @Override 20 | public int getSuccessCount() { 21 | return result == 1 ? 1 : 0; 22 | } 23 | 24 | @Override 25 | public int getFailureRate() { 26 | return getFailureCount() * 100; 27 | } 28 | 29 | @Override 30 | public int getSuccessRate() { 31 | return getSuccessCount() * 100; 32 | } 33 | 34 | @Override 35 | public void recordFailure() { 36 | result = 0; 37 | } 38 | 39 | @Override 40 | public void recordSuccess() { 41 | result = 1; 42 | } 43 | 44 | @Override 45 | public void reset() { 46 | result = -1; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/internal/EventHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.internal; 17 | 18 | import dev.failsafe.event.EventListener; 19 | import dev.failsafe.ExecutionContext; 20 | import dev.failsafe.event.ExecutionAttemptedEvent; 21 | import dev.failsafe.event.ExecutionCompletedEvent; 22 | import dev.failsafe.event.ExecutionScheduledEvent; 23 | import dev.failsafe.spi.ExecutionResult; 24 | 25 | import java.time.Duration; 26 | 27 | /** 28 | * Internal handling of events. 29 | * 30 | * @param result type 31 | */ 32 | public interface EventHandler { 33 | void handle(ExecutionResult result, ExecutionContext context); 34 | 35 | static EventHandler ofExecutionCompleted(EventListener> handler) { 36 | return handler == null ? 37 | null : 38 | (result, context) -> handler.acceptUnchecked( 39 | new ExecutionCompletedEvent<>(result.getResult(), result.getException(), context)); 40 | } 41 | 42 | static EventHandler ofExecutionAttempted(EventListener> handler) { 43 | return handler == null ? 44 | null : 45 | (result, context) -> handler.acceptUnchecked( 46 | new ExecutionAttemptedEvent<>(result.getResult(), result.getException(), context)); 47 | } 48 | 49 | static EventHandler ofExecutionScheduled(EventListener> handler) { 50 | return handler == null ? 51 | null : 52 | (result, context) -> handler.acceptUnchecked( 53 | new ExecutionScheduledEvent<>(result.getResult(), result.getException(), Duration.ofNanos(result.getDelay()), 54 | context)); 55 | } 56 | } -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/internal/FallbackImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.internal; 17 | 18 | import dev.failsafe.Fallback; 19 | import dev.failsafe.event.ExecutionAttemptedEvent; 20 | import dev.failsafe.spi.FailurePolicy; 21 | import dev.failsafe.ExecutionContext; 22 | import dev.failsafe.FallbackBuilder; 23 | import dev.failsafe.FallbackConfig; 24 | import dev.failsafe.spi.PolicyExecutor; 25 | 26 | import java.util.concurrent.CompletableFuture; 27 | 28 | /** 29 | * A {@link Fallback} implementation. 30 | * 31 | * @param result type 32 | * @author Jonathan Halterman 33 | * @see FallbackBuilder 34 | */ 35 | public class FallbackImpl implements Fallback, FailurePolicy { 36 | /** 37 | * A fallback that will return null if execution fails. 38 | */ 39 | public static Fallback NONE = Fallback.builder(() -> null).build(); 40 | 41 | private final FallbackConfig config; 42 | 43 | public FallbackImpl(FallbackConfig config) { 44 | this.config = config; 45 | } 46 | 47 | @Override 48 | public FallbackConfig getConfig() { 49 | return config; 50 | } 51 | 52 | /** 53 | * Returns the applied fallback result. 54 | */ 55 | protected R apply(R result, Throwable exception, ExecutionContext context) throws Throwable { 56 | ExecutionAttemptedEvent event = new ExecutionAttemptedEvent<>(result, exception, context); 57 | return config.getFallback() != null ? 58 | config.getFallback().apply(event) : 59 | config.getFallbackStage().apply(event).get(); 60 | } 61 | 62 | /** 63 | * Returns a future applied fallback result. 64 | */ 65 | protected CompletableFuture applyStage(R result, Throwable exception, ExecutionContext context) throws Throwable { 66 | ExecutionAttemptedEvent event = new ExecutionAttemptedEvent<>(result, exception, context); 67 | return config.getFallback() != null ? 68 | CompletableFuture.completedFuture(config.getFallback().apply(event)) : 69 | config.getFallbackStage().apply(event); 70 | } 71 | 72 | @Override 73 | public PolicyExecutor toExecutor(int policyIndex) { 74 | return new FallbackExecutor<>(this, policyIndex); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/internal/OpenState.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.internal; 17 | 18 | import dev.failsafe.CircuitBreaker.State; 19 | 20 | import java.time.Duration; 21 | 22 | class OpenState extends CircuitState { 23 | private final long startTime = System.nanoTime(); 24 | private final long delayNanos; 25 | 26 | public OpenState(CircuitBreakerImpl breaker, CircuitState previousState, Duration delay) { 27 | super(breaker, previousState.stats); 28 | this.delayNanos = delay.toNanos(); 29 | } 30 | 31 | @Override 32 | public boolean tryAcquirePermit() { 33 | if (System.nanoTime() - startTime >= delayNanos) { 34 | breaker.halfOpen(); 35 | return breaker.tryAcquirePermit(); 36 | } 37 | 38 | return false; 39 | } 40 | 41 | @Override 42 | public Duration getRemainingDelay() { 43 | long elapsedTime = System.nanoTime() - startTime; 44 | long remainingDelay = delayNanos - elapsedTime; 45 | return Duration.ofNanos(Math.max(remainingDelay, 0)); 46 | } 47 | 48 | @Override 49 | public State getState() { 50 | return State.OPEN; 51 | } 52 | } -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/internal/RateLimiterStats.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.internal; 17 | 18 | import java.time.Duration; 19 | 20 | abstract class RateLimiterStats { 21 | final Stopwatch stopwatch; 22 | 23 | RateLimiterStats(Stopwatch stopwatch) { 24 | this.stopwatch = stopwatch; 25 | } 26 | 27 | static class Stopwatch { 28 | private long startTime = System.nanoTime(); 29 | 30 | long elapsedNanos() { 31 | return System.nanoTime() - startTime; 32 | } 33 | 34 | void reset() { 35 | startTime = System.nanoTime(); 36 | } 37 | } 38 | 39 | /** 40 | * Eagerly acquires permits and returns the time in nanos that must be waited in order to use the permits, else 41 | * returns {@code -1} if the wait time would exceed the {@code maxWaitTime}. 42 | * 43 | * @param permits the number of requested permits 44 | * @param maxWaitTime the max time to wait for the requested permits, else {@code null} to wait indefinitely 45 | */ 46 | abstract long acquirePermits(long permits, Duration maxWaitTime); 47 | 48 | /** 49 | * Returns whether the {@code waitNanos} would exceed the {@code maxWaitTime}, else {@code false} if {@code 50 | * maxWaitTime} is null. 51 | */ 52 | boolean exceedsMaxWaitTime(long waitNanos, Duration maxWaitTime) { 53 | return maxWaitTime != null && waitNanos > maxWaitTime.toNanos(); 54 | } 55 | 56 | /** 57 | * Returns the elapsed time since the rate limiter began. 58 | */ 59 | Duration getElapsed() { 60 | return Duration.ofNanos(stopwatch.elapsedNanos()); 61 | } 62 | 63 | /** 64 | * Resets the rate limiter's internal stats. 65 | */ 66 | abstract void reset(); 67 | } -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/internal/RetryPolicyImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.internal; 17 | 18 | import dev.failsafe.RetryPolicy; 19 | import dev.failsafe.RetryPolicyBuilder; 20 | import dev.failsafe.RetryPolicyConfig; 21 | import dev.failsafe.function.CheckedBiPredicate; 22 | import dev.failsafe.function.CheckedPredicate; 23 | import dev.failsafe.spi.DelayablePolicy; 24 | import dev.failsafe.spi.FailurePolicy; 25 | import dev.failsafe.spi.PolicyExecutor; 26 | 27 | import java.util.List; 28 | 29 | /** 30 | * A {@link RetryPolicy} implementation. 31 | * 32 | * @param result type 33 | * @author Jonathan Halterman 34 | * @see RetryPolicyBuilder 35 | */ 36 | public class RetryPolicyImpl implements RetryPolicy, FailurePolicy, DelayablePolicy { 37 | private final RetryPolicyConfig config; 38 | 39 | public RetryPolicyImpl(RetryPolicyConfig config) { 40 | this.config = config; 41 | } 42 | 43 | @Override 44 | public RetryPolicyConfig getConfig() { 45 | return config; 46 | } 47 | 48 | /** 49 | * Returns whether an execution result can be aborted given the configured abort conditions. 50 | * 51 | * @see RetryPolicyBuilder#abortOn(Class...) 52 | * @see RetryPolicyBuilder#abortOn(List) 53 | * @see RetryPolicyBuilder#abortOn(CheckedPredicate) 54 | * @see RetryPolicyBuilder#abortIf(CheckedBiPredicate) 55 | * @see RetryPolicyBuilder#abortIf(CheckedPredicate) 56 | * @see RetryPolicyBuilder#abortWhen(R) 57 | */ 58 | public boolean isAbortable(R result, Throwable failure) { 59 | for (CheckedBiPredicate predicate : config.getAbortConditions()) { 60 | try { 61 | if (predicate.test(result, failure)) 62 | return true; 63 | } catch (Throwable ignore) { 64 | } 65 | } 66 | return false; 67 | } 68 | 69 | @Override 70 | public PolicyExecutor toExecutor(int policyIndex) { 71 | return new RetryPolicyExecutor<>(this, policyIndex); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/internal/SmoothRateLimiterStats.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.internal; 17 | 18 | import dev.failsafe.RateLimiterConfig; 19 | import dev.failsafe.internal.util.Maths; 20 | 21 | import java.time.Duration; 22 | 23 | /** 24 | * A rate limiter implementation that evenly distributes permits over time, based on the max permits per period. This 25 | * implementation focuses on the interval between permits, and tracks the next interval in which a permit is free. 26 | */ 27 | class SmoothRateLimiterStats extends RateLimiterStats { 28 | /* The nanos per interval between permits */ 29 | final long intervalNanos; 30 | 31 | // The amount of time, relative to the start time, that the next permit will be free. 32 | // Will be a multiple of intervalNanos. 33 | private long nextFreePermitNanos; 34 | 35 | SmoothRateLimiterStats(RateLimiterConfig config, Stopwatch stopwatch) { 36 | super(stopwatch); 37 | intervalNanos = config.getMaxRate().toNanos(); 38 | } 39 | 40 | @Override 41 | public synchronized long acquirePermits(long requestedPermits, Duration maxWaitTime) { 42 | long currentNanos = stopwatch.elapsedNanos(); 43 | long requestedPermitNanos = requestedPermits * intervalNanos; 44 | long waitNanos; 45 | long newNextFreePermitNanos; 46 | 47 | // If a permit is currently available 48 | if (currentNanos >= nextFreePermitNanos) { 49 | // Nanos at the start of the current interval 50 | long currentIntervalNanos = Maths.roundDown(currentNanos, intervalNanos); 51 | newNextFreePermitNanos = Maths.add(currentIntervalNanos, requestedPermitNanos); 52 | } else { 53 | newNextFreePermitNanos = Maths.add(nextFreePermitNanos, requestedPermitNanos); 54 | } 55 | 56 | waitNanos = Math.max(newNextFreePermitNanos - currentNanos - intervalNanos, 0); 57 | 58 | if (exceedsMaxWaitTime(waitNanos, maxWaitTime)) 59 | return -1; 60 | 61 | nextFreePermitNanos = newNextFreePermitNanos; 62 | return waitNanos; 63 | } 64 | 65 | synchronized long getNextFreePermitNanos() { 66 | return nextFreePermitNanos; 67 | } 68 | 69 | @Override 70 | synchronized void reset() { 71 | stopwatch.reset(); 72 | nextFreePermitNanos = 0; 73 | } 74 | } -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/internal/TimeoutImpl.java: -------------------------------------------------------------------------------- 1 | package dev.failsafe.internal; 2 | 3 | import dev.failsafe.Timeout; 4 | import dev.failsafe.TimeoutBuilder; 5 | import dev.failsafe.TimeoutConfig; 6 | import dev.failsafe.TimeoutExceededException; 7 | import dev.failsafe.spi.PolicyExecutor; 8 | 9 | /** 10 | * A {@link Timeout} implementation. 11 | * 12 | * @param result type 13 | * @author Jonathan Halterman 14 | * @see TimeoutBuilder 15 | * @see TimeoutExceededException 16 | */ 17 | public class TimeoutImpl implements Timeout { 18 | private final TimeoutConfig config; 19 | 20 | public TimeoutImpl(TimeoutConfig config) { 21 | this.config = config; 22 | } 23 | 24 | @Override 25 | public TimeoutConfig getConfig() { 26 | return config; 27 | } 28 | 29 | @Override 30 | public PolicyExecutor toExecutor(int policyIndex) { 31 | return new TimeoutExecutor<>(this, policyIndex); 32 | } 33 | 34 | @Override 35 | public String toString() { 36 | return "Timeout[timeout=" + config.getTimeout() + ", interruptable=" + config.canInterrupt() + ']'; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/internal/util/Assert.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.internal.util; 17 | 18 | /** 19 | * Assertion utilities. 20 | * 21 | * @author Jonathan Halterman 22 | */ 23 | public final class Assert { 24 | private Assert() { 25 | } 26 | 27 | public static void isTrue(boolean expression, String errorMessageFormat, Object... args) { 28 | if (!expression) 29 | throw new IllegalArgumentException(String.format(errorMessageFormat, args)); 30 | } 31 | 32 | public static T notNull(T reference, String parameterName) { 33 | if (reference == null) 34 | throw new NullPointerException(parameterName + " cannot be null"); 35 | return reference; 36 | } 37 | 38 | public static void state(boolean expression, String errorMessageFormat, Object... args) { 39 | if (!expression) 40 | throw new IllegalStateException(String.format(errorMessageFormat, args)); 41 | } 42 | } -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/internal/util/Durations.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011-2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.internal.util; 17 | 18 | import java.time.Duration; 19 | 20 | /** 21 | * Duration and long utilities. 22 | */ 23 | public final class Durations { 24 | static long MAX_SECONDS_PER_LONG = Long.MAX_VALUE / 1000_000_000L; 25 | static Duration MAX_SAFE_NANOS_DURATION = Duration.ofSeconds(MAX_SECONDS_PER_LONG); 26 | 27 | private Durations() { 28 | } 29 | 30 | /** 31 | * Returns either the {@code duration} else a Duration containing the max seconds that can safely be converted to 32 | * nanos without overflowing. 33 | */ 34 | public static Duration ofSafeNanos(Duration duration) { 35 | return duration.getSeconds() < MAX_SECONDS_PER_LONG ? duration : MAX_SAFE_NANOS_DURATION; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/internal/util/FutureLinkedList.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.internal.util; 17 | 18 | import java.util.concurrent.CompletableFuture; 19 | 20 | /** 21 | * A LinkedList of CompletableFutures that removes a future from the list when it's completed. 22 | *

23 | * This class is threadsafe. 24 | *

25 | * 26 | * @author Jonathan Halterman 27 | */ 28 | public final class FutureLinkedList { 29 | Node head; 30 | Node tail; 31 | 32 | static class Node { 33 | Node previous; 34 | Node next; 35 | CompletableFuture future; 36 | } 37 | 38 | /** 39 | * Adds a new CompletableFuture to the list and returns it. The returned future will be removed from the list when 40 | * it's completed. 41 | */ 42 | public synchronized CompletableFuture add() { 43 | Node node = new Node(); 44 | node.future = new CompletableFuture<>(); 45 | node.future.whenComplete((result, error) -> remove(node)); 46 | 47 | if (head == null) 48 | head = tail = node; 49 | else { 50 | tail.next = node; 51 | node.previous = tail; 52 | tail = node; 53 | } 54 | return node.future; 55 | } 56 | 57 | /** 58 | * Returns and removes the first future in the list, else returns {@code null} if the list is empty. 59 | */ 60 | public synchronized CompletableFuture pollFirst() { 61 | Node previousHead = head; 62 | if (head != null) { 63 | head = head.next; 64 | if (head != null) 65 | head.previous = null; 66 | } 67 | return previousHead == null ? null : previousHead.future; 68 | } 69 | 70 | private synchronized void remove(Node node) { 71 | if (node.previous != null) 72 | node.previous.next = node.next; 73 | if (node.next != null) 74 | node.next.previous = node.previous; 75 | if (head == node) 76 | head = node.next; 77 | if (tail == node) 78 | tail = node.previous; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/internal/util/Lists.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011-2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.internal.util; 17 | 18 | import java.util.ArrayList; 19 | import java.util.Collections; 20 | import java.util.List; 21 | 22 | /** 23 | * List utilities. 24 | * 25 | * @author Jonathan Halterman 26 | */ 27 | public final class Lists { 28 | private Lists() { 29 | } 30 | 31 | /** 32 | * Returns a list containing the {@code first} element followed by the {@code rest}. 33 | */ 34 | public static List of(T first, T[] rest) { 35 | List result = new ArrayList<>(rest.length + 1); 36 | result.add(first); 37 | Collections.addAll(result, rest); 38 | return result; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/internal/util/Maths.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011-2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.internal.util; 17 | 18 | /** 19 | * Misc math utilities. 20 | */ 21 | public final class Maths { 22 | private Maths() { 23 | } 24 | 25 | /** 26 | * Returns the sum of {@code a} and {@code b} else {@code Long.MAX_VALUE} if the sum would otherwise overflow. 27 | */ 28 | public static long add(long a, long b) { 29 | long naiveSum = a + b; 30 | return (a ^ b) < 0L | (a ^ naiveSum) >= 0L ? naiveSum : 9223372036854775807L + (naiveSum >>> 63 ^ 1L); 31 | } 32 | 33 | /** 34 | * Returns the {@code input} rounded down to the nearest {@code interval}. 35 | */ 36 | public static long roundDown(long input, long interval) { 37 | return (input / interval) * interval; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/internal/util/RandomDelay.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.internal.util; 17 | 18 | /** 19 | * Utilities for computing random delays. 20 | * 21 | * @author Jonathan Halterman 22 | */ 23 | public final class RandomDelay { 24 | private RandomDelay() { 25 | } 26 | 27 | public static long randomDelayInRange(long delayMin, long delayMax, double random) { 28 | return (long) (random * (delayMax - delayMin)) + delayMin; 29 | } 30 | 31 | public static long randomDelay(long delay, long jitter, double random) { 32 | double randomAddend = (1 - random * 2) * jitter; 33 | return (long) (delay + randomAddend); 34 | } 35 | 36 | public static long randomDelay(long delay, double jitterFactor, double random) { 37 | double randomFactor = 1 + (1 - random * 2) * jitterFactor; 38 | return (long) (delay * randomFactor); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * APIs for performing failsafe executions. 3 | *

4 | * {@link dev.failsafe.Failsafe} is the entry point for the library. See {@link dev.failsafe.FailsafeExecutor} 5 | * for execution options. 6 | */ 7 | package dev.failsafe; 8 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/spi/AsyncExecutionInternal.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.spi; 17 | 18 | import dev.failsafe.AsyncExecution; 19 | 20 | /** 21 | * Internal async execution APIs. 22 | * 23 | * @param result type 24 | * @author Jonathan Halterman 25 | */ 26 | public interface AsyncExecutionInternal extends ExecutionInternal, AsyncExecution { 27 | /** 28 | * Returns whether the execution is an async integration execution. 29 | */ 30 | boolean isAsyncExecution(); 31 | 32 | /** 33 | * Returns whether one of the public {@link AsyncExecution} record or complete methods have been called. 34 | */ 35 | boolean isRecorded(); 36 | 37 | /** 38 | * Sets the PolicyExecutor corresponding to the {@code policyIndex} as having post-executed. 39 | */ 40 | void setPostExecuted(int policyIndex); 41 | 42 | /** 43 | * Returns whether the PolicyExecutor corresponding to the {@code policyIndex} has already post-executed. 44 | */ 45 | boolean isPostExecuted(int policyIndex); 46 | 47 | /** 48 | * Returns a new copy of the AsyncExecutionInternal. 49 | */ 50 | AsyncExecutionInternal copy(); 51 | } 52 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/spi/DefaultScheduledFuture.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.spi; 17 | 18 | import java.util.concurrent.Delayed; 19 | import java.util.concurrent.ExecutionException; 20 | import java.util.concurrent.ScheduledFuture; 21 | import java.util.concurrent.TimeUnit; 22 | import java.util.concurrent.TimeoutException; 23 | 24 | /** 25 | * A default ScheduledFuture implementation. Useful for {@link Scheduler} implementations. 26 | * 27 | * @param result type 28 | * @author Jonathan Halterman 29 | */ 30 | public class DefaultScheduledFuture implements ScheduledFuture { 31 | /** 32 | * @return {@code 0} 33 | */ 34 | @Override 35 | public long getDelay(TimeUnit unit) { 36 | return 0; 37 | } 38 | 39 | /** 40 | * @return {@code 0} 41 | */ 42 | @Override 43 | public int compareTo(Delayed o) { 44 | return 0; 45 | } 46 | 47 | /** 48 | * @return {@code false} 49 | */ 50 | @Override 51 | public boolean cancel(boolean mayInterruptIfRunning) { 52 | return false; 53 | } 54 | 55 | /** 56 | * @return {@code false} 57 | */ 58 | @Override 59 | public boolean isCancelled() { 60 | return false; 61 | } 62 | 63 | /** 64 | * @return {@code false} 65 | */ 66 | @Override 67 | public boolean isDone() { 68 | return false; 69 | } 70 | 71 | /** 72 | * @return {@code null} 73 | */ 74 | @Override 75 | public R get() throws InterruptedException, ExecutionException { 76 | return null; 77 | } 78 | 79 | /** 80 | * @return {@code null} 81 | */ 82 | @Override 83 | public R get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { 84 | return null; 85 | } 86 | } -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/spi/DelayablePolicy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.spi; 17 | 18 | import dev.failsafe.DelayablePolicyConfig; 19 | import dev.failsafe.ExecutionContext; 20 | import dev.failsafe.Policy; 21 | import dev.failsafe.internal.util.Durations; 22 | 23 | import java.time.Duration; 24 | 25 | /** 26 | * A policy that can be delayed between executions. 27 | * 28 | * @param result type 29 | * @author Jonathan Halterman 30 | */ 31 | public interface DelayablePolicy extends Policy { 32 | DelayablePolicyConfig getConfig(); 33 | 34 | /** 35 | * Returns a computed delay for the {@code result} and {@code context} else {@code null} if no delay function is 36 | * configured or the computed delay is invalid. 37 | */ 38 | default Duration computeDelay(ExecutionContext context) { 39 | DelayablePolicyConfig config = getConfig(); 40 | Duration computed = null; 41 | if (context != null && config.getDelayFn() != null) { 42 | R result = context.getLastResult(); 43 | Throwable exception = context.getLastException(); 44 | 45 | R delayResult = config.getDelayResult(); 46 | Class delayFailure = config.getDelayException(); 47 | boolean delayResultMatched = delayResult == null || delayResult.equals(result); 48 | boolean delayExceptionMatched = 49 | delayFailure == null || (exception != null && delayFailure.isAssignableFrom(exception.getClass())); 50 | if (delayResultMatched && delayExceptionMatched) { 51 | try { 52 | computed = Durations.ofSafeNanos(config.getDelayFn().get(context)); 53 | } catch (Throwable e) { 54 | if (e instanceof RuntimeException) 55 | throw (RuntimeException) e; 56 | else 57 | throw new RuntimeException(e); 58 | } 59 | } 60 | } 61 | 62 | return computed != null && !computed.isNegative() ? computed : null; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/spi/ExecutionInternal.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.spi; 17 | 18 | import dev.failsafe.ExecutionContext; 19 | 20 | /** 21 | * Internal execution APIs. 22 | * 23 | * @param result type 24 | * @author Jonathan Halterman 25 | */ 26 | public interface ExecutionInternal extends ExecutionContext { 27 | /** 28 | * Returns the recorded result for an execution attempt. 29 | */ 30 | ExecutionResult getResult(); 31 | 32 | /** 33 | * Called when execution of the user's supplier is about to begin. 34 | */ 35 | void preExecute(); 36 | 37 | /** 38 | * Returns whether the execution has been pre-executed, indicating the attempt has started. 39 | */ 40 | boolean isPreExecuted(); 41 | 42 | /** 43 | * Records an execution attempt which may correspond with an execution result. Async executions will have results 44 | * recorded separately. 45 | */ 46 | void recordAttempt(); 47 | 48 | /** 49 | * Records the {@code result} if the execution has been {@link #preExecute() pre-executed} and a result has not 50 | * already been recorded. 51 | */ 52 | void record(ExecutionResult result); 53 | 54 | /** 55 | * Marks the execution as having been cancelled externally, which will cancel pending executions of all policies. 56 | * 57 | * @return whether cancellation was successful or not. Returns {@code false} if the execution was already cancelled or 58 | * completed. 59 | */ 60 | boolean cancel(); 61 | 62 | /** 63 | * Marks the execution as having been cancelled by the {@code policyExecutor}, which will also cancel pending 64 | * executions of any inner policies of the {@code policyExecutor}. Outer policies of the {@code policyExecutor} will 65 | * be unaffected. 66 | */ 67 | void cancel(PolicyExecutor policyExecutor); 68 | 69 | /** 70 | * Returns whether the execution is considered cancelled for the {@code policyExecutor}. 71 | */ 72 | boolean isCancelled(PolicyExecutor policyExecutor); 73 | 74 | /** 75 | * Returns a lock object that is common across all execution attempts. Useful for guarding against races when mutating 76 | * an execution. 77 | */ 78 | Object getLock(); 79 | 80 | /** 81 | * Returns the most recent execution to be attempted. 82 | */ 83 | ExecutionInternal getLatest(); 84 | } 85 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/spi/FailurePolicy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.spi; 17 | 18 | import dev.failsafe.FailurePolicyBuilder; 19 | import dev.failsafe.FailurePolicyConfig; 20 | import dev.failsafe.Policy; 21 | import dev.failsafe.function.CheckedBiPredicate; 22 | import dev.failsafe.function.CheckedPredicate; 23 | 24 | import java.util.List; 25 | 26 | /** 27 | * A policy that can handle specifically configured failures. 28 | * 29 | * @param result type 30 | * @author Jonathan Halterman 31 | */ 32 | public interface FailurePolicy extends Policy { 33 | FailurePolicyConfig getConfig(); 34 | 35 | /** 36 | * Returns whether an execution {@code result} or {@code exception} are considered a failure according to the policy 37 | * configuration. 38 | * 39 | * @see FailurePolicyBuilder#handle(Class...) 40 | * @see FailurePolicyBuilder#handle(List) 41 | * @see FailurePolicyBuilder#handleIf(CheckedBiPredicate) 42 | * @see FailurePolicyBuilder#handleIf(CheckedPredicate) 43 | * @see FailurePolicyBuilder#handleResult(Object) 44 | * @see FailurePolicyBuilder#handleResultIf(CheckedPredicate) 45 | */ 46 | default boolean isFailure(R result, Throwable exception) { 47 | FailurePolicyConfig config = getConfig(); 48 | if (config.getFailureConditions().isEmpty()) 49 | return exception != null; 50 | 51 | for (CheckedBiPredicate predicate : config.getFailureConditions()) { 52 | try { 53 | if (predicate.test(result, exception)) 54 | return true; 55 | } catch (Throwable ignore) { 56 | } 57 | } 58 | 59 | // Fail by default if an exception is not checked by a condition 60 | return exception != null && !config.isExceptionsChecked(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/spi/Scheduler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.spi; 17 | 18 | import dev.failsafe.internal.util.DelegatingScheduler; 19 | 20 | import java.util.concurrent.*; 21 | 22 | /** 23 | * Schedules executions. 24 | * 25 | * @author Jonathan Halterman 26 | * @see DefaultScheduledFuture 27 | */ 28 | public interface Scheduler { 29 | /** 30 | * The default scheduler used by Failsafe if no other scheduler or {@link ScheduledExecutorService} is configured for 31 | * an execution. 32 | */ 33 | Scheduler DEFAULT = DelegatingScheduler.INSTANCE; 34 | 35 | /** 36 | * Schedules the {@code callable} to be called after the {@code delay} for the {@code unit}. 37 | */ 38 | ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit); 39 | 40 | /** 41 | * Returns a Scheduler adapted from the {@code scheduledExecutorService}. 42 | */ 43 | static Scheduler of(ScheduledExecutorService scheduledExecutorService) { 44 | return scheduledExecutorService::schedule; 45 | } 46 | 47 | /** 48 | * Returns a Scheduler adapted from the {@code executorService}. 49 | */ 50 | static Scheduler of(ExecutorService executorService) { 51 | return executorService instanceof ScheduledExecutorService ? 52 | of((ScheduledExecutorService) executorService) : 53 | new DelegatingScheduler(executorService); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/spi/SyncExecutionInternal.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.spi; 17 | 18 | import dev.failsafe.Execution; 19 | 20 | /** 21 | * Internal execution APIs. 22 | * 23 | * @param result type 24 | * @author Jonathan Halterman 25 | */ 26 | public interface SyncExecutionInternal extends ExecutionInternal, Execution { 27 | /** 28 | * Returns whether the execution is currently interrupted. 29 | */ 30 | boolean isInterrupted(); 31 | 32 | /** 33 | * Sets whether the execution is currently {@code interruptable}. 34 | */ 35 | void setInterruptable(boolean interruptable); 36 | 37 | /** 38 | * Interrupts the execution. 39 | */ 40 | void interrupt(); 41 | 42 | /** 43 | * Returns a new copy of the SyncExecutionInternal if it is not standalone, else returns {@code this} since standalone 44 | * executions are referenced externally and cannot be replaced. 45 | */ 46 | SyncExecutionInternal copy(); 47 | } 48 | -------------------------------------------------------------------------------- /core/src/main/java/dev/failsafe/spi/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * The Failsafe Service Provider Interface (SPI). These classes and interfaces allow Failsafe to be extended with new 3 | * policies. 4 | */ 5 | package dev.failsafe.spi; 6 | -------------------------------------------------------------------------------- /core/src/main/javadoc/overview.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Failsafe - fault tolerance and resilience patterns for the JVM. 4 |

5 | See the project homepage for usage instructions. 6 |

7 | 8 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/BulkheadBuilderTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe; 17 | 18 | import org.testng.annotations.Test; 19 | 20 | import java.time.Duration; 21 | 22 | import static org.testng.Assert.*; 23 | 24 | @Test 25 | public class BulkheadBuilderTest { 26 | public void shouldCreateBuilderFromExistingConfig() { 27 | BulkheadConfig initialConfig = Bulkhead.builder(5) 28 | .withMaxWaitTime(Duration.ofSeconds(10)) 29 | .onSuccess(e -> { 30 | }).config; 31 | BulkheadConfig newConfig = Bulkhead.builder(initialConfig).config; 32 | assertEquals(newConfig.maxConcurrency, 5); 33 | assertEquals(newConfig.maxWaitTime, Duration.ofSeconds(10)); 34 | assertNotNull(newConfig.successListener); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/CircuitBreakerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe; 17 | 18 | import org.testng.annotations.Test; 19 | 20 | import static org.testng.Assert.assertEquals; 21 | import static org.testng.Assert.assertTrue; 22 | 23 | @Test 24 | public class CircuitBreakerTest { 25 | public void shouldDefaultDelay() throws Throwable { 26 | CircuitBreaker breaker = CircuitBreaker.ofDefaults(); 27 | breaker.recordFailure(); 28 | Thread.sleep(100); 29 | assertTrue(breaker.isOpen()); 30 | } 31 | 32 | public void shouldGetSuccessAndFailureStats() { 33 | // Given 34 | CircuitBreaker breaker = CircuitBreaker.builder() 35 | .withFailureThreshold(5, 10) 36 | .withSuccessThreshold(15, 20) 37 | .build(); 38 | 39 | // When 40 | for (int i = 0; i < 7; i++) 41 | if (i % 2 == 0) 42 | breaker.recordSuccess(); 43 | else 44 | breaker.recordFailure(); 45 | 46 | // Then 47 | assertEquals(breaker.getFailureCount(), 3); 48 | assertEquals(breaker.getFailureRate(), 43); 49 | assertEquals(breaker.getSuccessCount(), 4); 50 | assertEquals(breaker.getSuccessRate(), 57); 51 | 52 | // When 53 | for (int i = 0; i < 15; i++) 54 | if (i % 4 == 0) 55 | breaker.recordFailure(); 56 | else 57 | breaker.recordSuccess(); 58 | 59 | // Then 60 | assertEquals(breaker.getFailureCount(), 2); 61 | assertEquals(breaker.getFailureRate(), 20); 62 | assertEquals(breaker.getSuccessCount(), 8); 63 | assertEquals(breaker.getSuccessRate(), 80); 64 | 65 | // When 66 | breaker.halfOpen(); 67 | for (int i = 0; i < 15; i++) 68 | if (i % 3 == 0) 69 | breaker.recordFailure(); 70 | else 71 | breaker.recordSuccess(); 72 | 73 | // Then 74 | assertEquals(breaker.getFailureCount(), 5); 75 | assertEquals(breaker.getFailureRate(), 33); 76 | assertEquals(breaker.getSuccessCount(), 10); 77 | assertEquals(breaker.getSuccessRate(), 67); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/FailsafeTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe; 17 | 18 | import dev.failsafe.testing.Testing; 19 | import org.testng.annotations.Test; 20 | 21 | import java.util.concurrent.TimeoutException; 22 | 23 | import static org.testng.Assert.assertEquals; 24 | 25 | /** 26 | * Tests general Failsafe behaviors. 27 | */ 28 | @Test 29 | public class FailsafeTest extends Testing { 30 | /** 31 | * Asserts that errors can be reported through Failsafe. 32 | */ 33 | public void shouldSupportErrors() { 34 | // Given 35 | RetryPolicy retryPolicy = RetryPolicy.ofDefaults(); 36 | 37 | // When / Then 38 | testRunFailure(Failsafe.with(retryPolicy), ctx -> { 39 | throw new InternalError(); 40 | }, (f, e) -> { 41 | assertEquals(e.getAttemptCount(), 3); 42 | }, InternalError.class); 43 | } 44 | 45 | /** 46 | * Asserts that checked exeptions are wrapped with FailsafeException for sync executions. 47 | */ 48 | public void shouldWrapCheckedExceptionsForSyncExecutions() { 49 | RetryPolicy retryPolicy = RetryPolicy.builder().withMaxRetries(0).build(); 50 | 51 | assertThrows(() -> Failsafe.with(retryPolicy).run(() -> { 52 | throw new TimeoutException(); 53 | }), FailsafeException.class, TimeoutException.class); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/FailurePolicyBuilderTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe; 17 | 18 | import dev.failsafe.function.CheckedBiPredicate; 19 | import org.testng.annotations.Test; 20 | 21 | import static dev.failsafe.testing.Testing.unwrapExceptions; 22 | import static org.testng.Assert.assertEquals; 23 | import static org.testng.Assert.assertNotEquals; 24 | 25 | @Test 26 | public class FailurePolicyBuilderTest { 27 | public void testResultPredicateOnlyHandlesResults() { 28 | CheckedBiPredicate resultPredicate = FailurePolicyBuilder.resultPredicateFor(result -> true); 29 | assertEquals(unwrapExceptions(() -> resultPredicate.test("result", null)), Boolean.TRUE); 30 | assertNotEquals(unwrapExceptions(() -> resultPredicate.test(null, new RuntimeException())), Boolean.TRUE); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/FallbackBuilderTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe; 17 | 18 | import org.testng.annotations.Test; 19 | 20 | import java.time.Duration; 21 | 22 | import static org.testng.Assert.*; 23 | 24 | @Test 25 | public class FallbackBuilderTest { 26 | public void shouldCreateBuilderFromExistingConfig() throws Throwable { 27 | FallbackConfig initialConfig = Fallback.builder(10).withAsync().onFailedAttempt(e -> { 28 | }).config; 29 | FallbackConfig newConfig = Fallback.builder(initialConfig).config; 30 | assertEquals(newConfig.fallback.apply(null), Integer.valueOf(10)); 31 | assertTrue(newConfig.async); 32 | assertNotNull(newConfig.failedAttemptListener); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/RateLimiterBuilderTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe; 17 | 18 | import org.testng.annotations.Test; 19 | 20 | import java.time.Duration; 21 | 22 | import static org.testng.Assert.assertEquals; 23 | import static org.testng.Assert.assertNotNull; 24 | 25 | @Test 26 | public class RateLimiterBuilderTest { 27 | public void shouldCreateBuilderFromExistingConfig() { 28 | RateLimiterConfig initialConfig = RateLimiter.smoothBuilder(Duration.ofMillis(10)) 29 | .withMaxWaitTime(Duration.ofSeconds(10)) 30 | .onSuccess(e -> { 31 | }).config; 32 | RateLimiterConfig newConfig = RateLimiter.builder(initialConfig).config; 33 | assertEquals(newConfig.maxRate, Duration.ofMillis(10)); 34 | assertEquals(newConfig.maxWaitTime, Duration.ofSeconds(10)); 35 | assertNotNull(newConfig.successListener); 36 | } 37 | 38 | /** 39 | * Asserts that the smooth rate limiter factory methods are equal. 40 | */ 41 | public void shouldBuildEqualSmoothLimiters() { 42 | Duration maxRate1 = RateLimiter.smoothBuilder(100, Duration.ofSeconds(1)).config.getMaxRate(); 43 | Duration maxRate2 = RateLimiter.smoothBuilder(Duration.ofMillis(10)).config.getMaxRate(); 44 | assertEquals(maxRate1, maxRate2); 45 | 46 | maxRate1 = RateLimiter.smoothBuilder(20, Duration.ofMillis(300)).config.getMaxRate(); 47 | maxRate2 = RateLimiter.smoothBuilder(Duration.ofMillis(15)).config.getMaxRate(); 48 | assertEquals(maxRate1, maxRate2); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/TimeoutBuilderTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe; 17 | 18 | import org.testng.annotations.Test; 19 | 20 | import java.time.Duration; 21 | 22 | import static org.testng.Assert.*; 23 | 24 | @Test 25 | public class TimeoutBuilderTest { 26 | public void shouldCreateBuilderFromExistingConfig() { 27 | TimeoutConfig initialConfig = Timeout.builder(Duration.ofMillis(50)).withInterrupt().onFailure(e -> { 28 | }).config; 29 | TimeoutConfig newConfig = Timeout.builder(initialConfig).config; 30 | assertEquals(newConfig.timeout, Duration.ofMillis(50)); 31 | assertTrue(newConfig.canInterrupt); 32 | assertNotNull(newConfig.failureListener); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/functional/ExecutorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.functional; 17 | 18 | import dev.failsafe.testing.Testing; 19 | import dev.failsafe.Failsafe; 20 | import dev.failsafe.RetryPolicy; 21 | import org.testng.annotations.BeforeMethod; 22 | import org.testng.annotations.Test; 23 | 24 | import java.util.concurrent.ExecutionException; 25 | import java.util.concurrent.Executor; 26 | import java.util.concurrent.atomic.AtomicInteger; 27 | 28 | import static org.testng.Assert.assertEquals; 29 | 30 | /** 31 | * Tests failsafe with an executor. 32 | */ 33 | @Test 34 | public class ExecutorTest extends Testing { 35 | AtomicInteger executions = new AtomicInteger(); 36 | Executor executor = runnable -> { 37 | executions.incrementAndGet(); 38 | runnable.run(); 39 | }; 40 | 41 | @BeforeMethod 42 | protected void beforeMethod() { 43 | executions.set(0); 44 | } 45 | 46 | public void testExecutorWithSyncExecution() { 47 | assertThrows(() -> Failsafe.with(RetryPolicy.ofDefaults()).with(executor).run(() -> { 48 | throw new IllegalStateException(); 49 | }), IllegalStateException.class); 50 | assertEquals(executions.get(), 3); 51 | } 52 | 53 | public void testExecutorWithAsyncExecution() { 54 | assertThrows(() -> Failsafe.with(RetryPolicy.ofDefaults()).with(executor).runAsync(() -> { 55 | throw new IllegalStateException(); 56 | }).get(), ExecutionException.class, IllegalStateException.class); 57 | assertEquals(executions.get(), 3); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/functional/FallbackTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.functional; 17 | 18 | import dev.failsafe.Fallback; 19 | import dev.failsafe.testing.Testing; 20 | import dev.failsafe.Failsafe; 21 | import dev.failsafe.RetryPolicy; 22 | import org.testng.annotations.Test; 23 | 24 | import static org.testng.Assert.assertEquals; 25 | 26 | /** 27 | * Tests various Fallback scenarios. 28 | */ 29 | @Test 30 | public class FallbackTest extends Testing { 31 | /** 32 | * Tests a simple execution that does not fallback. 33 | */ 34 | public void shouldNotFallback() { 35 | testGetSuccess(Failsafe.with(Fallback.of(true)), ctx -> { 36 | return false; 37 | }, (f, e) -> { 38 | assertEquals(e.getAttemptCount(), 1); 39 | assertEquals(e.getExecutionCount(), 1); 40 | }, false); 41 | } 42 | 43 | /** 44 | * Tests the handling of a fallback with no conditions. 45 | */ 46 | public void testFallbackWithoutConditions() { 47 | // Given 48 | Fallback fallback = Fallback.of(true); 49 | 50 | // When / Then 51 | testRunSuccess(Failsafe.with(fallback), ctx -> { 52 | throw new IllegalArgumentException(); 53 | }, true); 54 | 55 | // Given 56 | RetryPolicy retryPolicy = RetryPolicy.ofDefaults(); 57 | 58 | // When / Then 59 | testRunSuccess(Failsafe.with(fallback, retryPolicy), ctx -> { 60 | throw new IllegalStateException(); 61 | }, true); 62 | } 63 | 64 | /** 65 | * Tests the handling of a fallback with conditions. 66 | */ 67 | public void testFallbackWithConditions() { 68 | // Given 69 | Fallback fallback = Fallback.builder(true).handle(IllegalArgumentException.class).build(); 70 | 71 | // When / Then 72 | testRunFailure(Failsafe.with(fallback), ctx -> { 73 | throw new IllegalStateException(); 74 | }, IllegalStateException.class); 75 | 76 | testRunSuccess(Failsafe.with(fallback), ctx -> { 77 | throw new IllegalArgumentException(); 78 | }, true); 79 | } 80 | 81 | /** 82 | * Tests Fallback.ofException. 83 | */ 84 | public void shouldFallbackOfException() { 85 | // Given 86 | Fallback fallback = Fallback.ofException(e -> new IllegalStateException(e.getLastException())); 87 | 88 | // When / Then 89 | testRunFailure(Failsafe.with(fallback), ctx -> { 90 | throw new IllegalArgumentException(); 91 | }, IllegalStateException.class); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/functional/FutureCompletionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.functional; 17 | 18 | import dev.failsafe.testing.Testing; 19 | import net.jodah.concurrentunit.Waiter; 20 | import dev.failsafe.Failsafe; 21 | import org.testng.annotations.Test; 22 | 23 | import java.util.concurrent.CompletableFuture; 24 | 25 | import static org.testng.Assert.assertFalse; 26 | 27 | /** 28 | * Tests behavior when a FailsafeFuture is explicitly completed. 29 | */ 30 | @Test 31 | public class FutureCompletionTest extends Testing { 32 | /** 33 | * Asserts that an externally completed FailsafeFuture works as expected. 34 | */ 35 | public void shouldCompleteFutureExternally() throws Throwable { 36 | // Given 37 | Waiter waiter = new Waiter(); 38 | CompletableFuture future1 = Failsafe.with(retryNever).onSuccess(e -> { 39 | waiter.assertFalse(e.getResult()); 40 | waiter.resume(); 41 | }).getAsync(() -> { 42 | waiter.resume(); 43 | Thread.sleep(500); 44 | return true; 45 | }); 46 | waiter.await(1000); 47 | 48 | // When completed 49 | future1.complete(false); 50 | 51 | // Then 52 | assertFalse(future1.get()); 53 | waiter.await(1000); 54 | 55 | // Given 56 | CompletableFuture future2 = Failsafe.with(retryNever).onFailure(e -> { 57 | waiter.assertTrue(e.getException() instanceof IllegalArgumentException); 58 | waiter.resume(); 59 | }).getAsync(() -> { 60 | waiter.resume(); 61 | Thread.sleep(500); 62 | return true; 63 | }); 64 | waiter.await(1000); 65 | 66 | // When completed exceptionally 67 | future2.completeExceptionally(new IllegalArgumentException()); 68 | 69 | // Then 70 | assertThrows(() -> unwrapExceptions(future2::get), IllegalArgumentException.class); 71 | waiter.await(1000); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/functional/NoPolicyTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.functional; 17 | 18 | import dev.failsafe.Failsafe; 19 | import dev.failsafe.FailsafeExecutor; 20 | import dev.failsafe.testing.Testing; 21 | import org.testng.annotations.Test; 22 | 23 | import java.util.concurrent.atomic.AtomicInteger; 24 | 25 | import static org.testng.Assert.assertEquals; 26 | 27 | /** 28 | * Tests the use of Failsafe.none(). 29 | */ 30 | @Test 31 | public class NoPolicyTest extends Testing { 32 | public void testWithNoPolicy() { 33 | AtomicInteger successCounter = new AtomicInteger(); 34 | AtomicInteger failureCounter = new AtomicInteger(); 35 | FailsafeExecutor failsafe = Failsafe.none().onFailure(e -> { 36 | System.out.println("Failure"); 37 | failureCounter.incrementAndGet(); 38 | }).onSuccess(e -> { 39 | System.out.println("Success"); 40 | successCounter.incrementAndGet(); 41 | }); 42 | 43 | // Test success 44 | testGetSuccess(() -> { 45 | successCounter.set(0); 46 | failureCounter.set(0); 47 | }, failsafe, ctx -> { 48 | return "success"; 49 | }, (f, e) -> { 50 | assertEquals(e.getAttemptCount(), 1); 51 | assertEquals(e.getExecutionCount(), 1); 52 | assertEquals(successCounter.get(), 1); 53 | assertEquals(failureCounter.get(), 0); 54 | }, "success"); 55 | 56 | // Test failure 57 | testRunFailure(() -> { 58 | successCounter.set(0); 59 | failureCounter.set(0); 60 | }, failsafe, ctx -> { 61 | throw new IllegalStateException(); 62 | }, (f, e) -> { 63 | assertEquals(e.getAttemptCount(), 1); 64 | assertEquals(e.getExecutionCount(), 1); 65 | assertEquals(successCounter.get(), 0); 66 | assertEquals(failureCounter.get(), 1); 67 | }, IllegalStateException.class); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/functional/PolicyCompositionExecutionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.functional; 17 | 18 | import dev.failsafe.CircuitBreaker; 19 | import dev.failsafe.Execution; 20 | import dev.failsafe.RetryPolicy; 21 | import org.testng.annotations.Test; 22 | 23 | import static org.testng.Assert.assertFalse; 24 | import static org.testng.Assert.assertTrue; 25 | 26 | /** 27 | * Tests the handling of ordered policy execution via an Execution or AsyncExecution. 28 | */ 29 | @Test 30 | public class PolicyCompositionExecutionTest { 31 | public void testRetryPolicyCircuitBreaker() { 32 | RetryPolicy rp = RetryPolicy.ofDefaults(); 33 | CircuitBreaker cb = CircuitBreaker.builder().withFailureThreshold(5).build(); 34 | 35 | Execution execution = Execution.of(rp, cb); 36 | execution.recordException(new Exception()); 37 | execution.recordException(new Exception()); 38 | assertFalse(execution.isComplete()); 39 | execution.recordException(new Exception()); 40 | assertTrue(execution.isComplete()); 41 | 42 | assertTrue(cb.isClosed()); 43 | } 44 | 45 | public void testCircuitBreakerRetryPolicy() { 46 | RetryPolicy rp = RetryPolicy.builder().withMaxRetries(1).build(); 47 | CircuitBreaker cb = CircuitBreaker.builder().withFailureThreshold(5).build(); 48 | 49 | Execution execution = Execution.of(cb, rp); 50 | execution.recordException(new Exception()); 51 | assertFalse(execution.isComplete()); 52 | execution.recordException(new Exception()); 53 | assertTrue(execution.isComplete()); 54 | 55 | assertTrue(cb.isClosed()); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/functional/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Functional tests for sets of behavior. When possible, each behavior will be tested against sync and various async 3 | * executions with the same assertions. 4 | */ 5 | package dev.failsafe.functional; 6 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/internal/CircuitStatsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.internal; 17 | 18 | import org.testng.annotations.Test; 19 | 20 | import java.util.function.Predicate; 21 | 22 | @Test 23 | public abstract class CircuitStatsTest { 24 | protected static void recordSuccesses(T stats, int count) { 25 | for (int i = 0; i < count; i++) 26 | stats.recordSuccess(); 27 | } 28 | 29 | protected static void recordFailures(T stats, int count) { 30 | for (int i = 0; i < count; i++) 31 | stats.recordFailure(); 32 | } 33 | 34 | protected static void recordExecutions(T stats, int count, 35 | Predicate successPredicate) { 36 | for (int i = 0; i < count; i++) { 37 | if (successPredicate.test(i)) 38 | stats.recordSuccess(); 39 | else 40 | stats.recordFailure(); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/internal/InternalTesting.java: -------------------------------------------------------------------------------- 1 | package dev.failsafe.internal; 2 | 3 | import dev.failsafe.CircuitBreaker; 4 | import dev.failsafe.RateLimiter; 5 | 6 | import java.lang.reflect.Field; 7 | import java.util.concurrent.atomic.AtomicReference; 8 | 9 | public final class InternalTesting { 10 | private InternalTesting() { 11 | } 12 | 13 | @SuppressWarnings("unchecked") 14 | public static > T stateFor(CircuitBreaker breaker) { 15 | Field stateField; 16 | try { 17 | stateField = CircuitBreakerImpl.class.getDeclaredField("state"); 18 | stateField.setAccessible(true); 19 | return ((AtomicReference) stateField.get(breaker)).get(); 20 | } catch (Exception e) { 21 | throw new IllegalStateException("Could not get circuit breaker state"); 22 | } 23 | } 24 | 25 | public static void resetBreaker(CircuitBreaker breaker) { 26 | breaker.close(); 27 | CircuitState state = stateFor(breaker); 28 | state.getStats().reset(); 29 | } 30 | 31 | public static void resetLimiter(RateLimiter limiter) { 32 | try { 33 | RateLimiterImpl impl = (RateLimiterImpl) limiter; 34 | Field statsField = RateLimiterImpl.class.getDeclaredField("stats"); 35 | statsField.setAccessible(true); 36 | RateLimiterStats stats = (RateLimiterStats) statsField.get(impl); 37 | stats.reset(); 38 | } catch (Exception e) { 39 | throw new IllegalStateException("Could not reset rate limiter"); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/internal/OpenStateTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.internal; 17 | 18 | import dev.failsafe.CircuitBreaker; 19 | import dev.failsafe.CircuitBreaker.State; 20 | import org.testng.annotations.Test; 21 | 22 | import java.time.Duration; 23 | 24 | import static org.testng.Assert.*; 25 | 26 | @Test 27 | public class OpenStateTest { 28 | public void testTryAcquirePermit() throws Throwable { 29 | // Given 30 | CircuitBreakerImpl breaker = (CircuitBreakerImpl) CircuitBreaker.builder() 31 | .withDelay(Duration.ofMillis(100)) 32 | .build(); 33 | breaker.open(); 34 | OpenState state = new OpenState<>(breaker, new ClosedState<>(breaker), breaker.getConfig().getDelay()); 35 | assertTrue(breaker.isOpen()); 36 | assertFalse(state.tryAcquirePermit()); 37 | 38 | // When 39 | Thread.sleep(110); 40 | 41 | // Then 42 | assertTrue(state.tryAcquirePermit()); 43 | assertEquals(breaker.getState(), State.HALF_OPEN); 44 | } 45 | 46 | public void testRemainingDelay() throws Throwable { 47 | // Given 48 | CircuitBreakerImpl breaker = (CircuitBreakerImpl) CircuitBreaker.builder() 49 | .withDelay(Duration.ofSeconds(1)) 50 | .build(); 51 | OpenState state = new OpenState<>(breaker, new ClosedState<>(breaker), breaker.getConfig().getDelay()); 52 | 53 | // When / Then 54 | long remainingDelayMillis = state.getRemainingDelay().toMillis(); 55 | assertTrue(remainingDelayMillis < 1000); 56 | assertTrue(remainingDelayMillis > 0); 57 | 58 | Thread.sleep(110); 59 | remainingDelayMillis = state.getRemainingDelay().toMillis(); 60 | assertTrue(remainingDelayMillis < 900); 61 | assertTrue(remainingDelayMillis > 0); 62 | } 63 | 64 | public void testNoRemainingDelay() throws Throwable { 65 | // Given 66 | CircuitBreakerImpl breaker = (CircuitBreakerImpl) CircuitBreaker.builder() 67 | .withDelay(Duration.ofMillis(10)) 68 | .build(); 69 | assertEquals(breaker.getRemainingDelay(), Duration.ZERO); 70 | 71 | // When 72 | OpenState state = new OpenState<>(breaker, new ClosedState<>(breaker), breaker.getConfig().getDelay()); 73 | Thread.sleep(50); 74 | 75 | // Then 76 | assertEquals(state.getRemainingDelay().toMillis(), 0); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/internal/util/DurationsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.internal.util; 17 | 18 | import org.testng.annotations.Test; 19 | 20 | import java.time.Duration; 21 | 22 | import static dev.failsafe.internal.util.Durations.*; 23 | import static org.testng.Assert.assertEquals; 24 | 25 | @Test 26 | public class DurationsTest { 27 | public void testOfSafeNanos() { 28 | assertEquals(ofSafeNanos(Duration.ofSeconds(MAX_SECONDS_PER_LONG)), MAX_SAFE_NANOS_DURATION); 29 | assertEquals(ofSafeNanos(Duration.ofSeconds(MAX_SECONDS_PER_LONG + 1)), MAX_SAFE_NANOS_DURATION); 30 | assertEquals(ofSafeNanos(Duration.ofSeconds(Long.MAX_VALUE)), MAX_SAFE_NANOS_DURATION); 31 | Duration safeDuration = Duration.ofSeconds(MAX_SECONDS_PER_LONG - 1000); 32 | assertEquals(ofSafeNanos(safeDuration), safeDuration); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/internal/util/FutureLinkedListTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.internal.util; 17 | 18 | import org.testng.annotations.Test; 19 | 20 | import java.util.concurrent.CompletableFuture; 21 | 22 | import static org.testng.Assert.assertEquals; 23 | import static org.testng.Assert.assertNull; 24 | 25 | @Test 26 | public class FutureLinkedListTest { 27 | public void testAdd() { 28 | // Given 29 | FutureLinkedList list = new FutureLinkedList(); 30 | 31 | // When 32 | CompletableFuture f1 = list.add(); 33 | CompletableFuture f2 = list.add(); 34 | CompletableFuture f3 = list.add(); 35 | 36 | // Then 37 | assertNull(list.head.previous); 38 | assertEquals(list.head.future, f1); 39 | assertEquals(list.tail.future, f3); 40 | assertEquals(list.head.next.future, f2); 41 | assertEquals(list.tail.previous.future, f2); 42 | } 43 | 44 | public void testPollFirst() { 45 | // Given 46 | FutureLinkedList list = new FutureLinkedList(); 47 | 48 | // When / Then 49 | assertNull(list.pollFirst()); 50 | 51 | // Given 52 | CompletableFuture f1 = list.add(); 53 | CompletableFuture f2 = list.add(); 54 | CompletableFuture f3 = list.add(); 55 | 56 | // When / Then 57 | assertEquals(list.pollFirst(), f1); 58 | assertEquals(list.head.future, f2); 59 | assertNull(list.head.previous); 60 | assertEquals(list.pollFirst(), f2); 61 | assertEquals(list.head.future, f3); 62 | assertNull(list.head.previous); 63 | assertEquals(list.pollFirst(), f3); 64 | assertNull(list.head); 65 | assertNull(list.pollFirst()); 66 | } 67 | 68 | public void testRemove() { 69 | // Given 70 | FutureLinkedList list = new FutureLinkedList(); 71 | CompletableFuture f1 = list.add(); 72 | CompletableFuture f2 = list.add(); 73 | CompletableFuture f3 = list.add(); 74 | 75 | // When / Then 76 | f1.complete(null); 77 | assertEquals(list.head.future, f2); 78 | assertNull(list.head.previous); 79 | assertEquals(list.tail.previous.future, f2); 80 | 81 | f2.complete(null); 82 | assertEquals(list.head.future, f3); 83 | assertEquals(list.tail.future, f3); 84 | assertNull(list.head.previous); 85 | assertNull(list.head.next); 86 | 87 | f3.complete(null); 88 | assertNull(list.head); 89 | assertNull(list.tail); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/internal/util/ListsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.internal.util; 17 | 18 | import org.testng.annotations.Test; 19 | 20 | import java.util.Arrays; 21 | 22 | import static org.testng.Assert.assertEquals; 23 | 24 | @Test 25 | public class ListsTest { 26 | public void testOf() { 27 | assertEquals(Lists.of("a", new String[] { "b", "c" }), Arrays.asList("a", "b", "c")); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/internal/util/MathsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.internal.util; 17 | 18 | import org.testng.annotations.Test; 19 | 20 | import static org.testng.Assert.assertEquals; 21 | 22 | @Test 23 | public class MathsTest { 24 | public void testAdd() { 25 | assertEquals(Maths.add(100000, 100000), 100000 * 2); 26 | assertEquals(Maths.add(Long.MAX_VALUE - 1000, 1000000), Long.MAX_VALUE); 27 | } 28 | 29 | public void testRoundDown() { 30 | assertEquals(Maths.roundDown(0, 20), 0); 31 | assertEquals(Maths.roundDown(40, 20), 40); 32 | assertEquals(Maths.roundDown(57, 20), 40); 33 | assertEquals(Maths.roundDown(57, 15), 45); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/internal/util/RandomDelayTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.internal.util; 17 | 18 | import org.testng.annotations.Test; 19 | 20 | import static org.testng.Assert.assertEquals; 21 | 22 | @Test 23 | public class RandomDelayTest { 24 | public void testRandomDelayInRange() { 25 | assertEquals(RandomDelay.randomDelayInRange(10, 100, 0), 10); 26 | assertEquals(RandomDelay.randomDelayInRange(10, 100, .25), 32); 27 | assertEquals(RandomDelay.randomDelayInRange(10, 100, .5), 55); 28 | assertEquals(RandomDelay.randomDelayInRange(10, 100, .75), 77); 29 | assertEquals(RandomDelay.randomDelayInRange(10, 100, 1), 100); 30 | 31 | assertEquals(RandomDelay.randomDelayInRange(50, 500, .25), 162); 32 | assertEquals(RandomDelay.randomDelayInRange(5000, 50000, .25), 16250); 33 | } 34 | 35 | public void testRandomDelayForFactor() { 36 | assertEquals(RandomDelay.randomDelay(100, .5, 0), 150); 37 | assertEquals(RandomDelay.randomDelay(100, .5, .25), 125); 38 | assertEquals(RandomDelay.randomDelay(100, .5, .5), 100); 39 | assertEquals(RandomDelay.randomDelay(100, .5, .75), 75); 40 | assertEquals(RandomDelay.randomDelay(100, .5, .9999), 50); 41 | 42 | assertEquals(RandomDelay.randomDelay(500, .5, .25), 625); 43 | assertEquals(RandomDelay.randomDelay(500, .5, .75), 375); 44 | assertEquals(RandomDelay.randomDelay(50000, .5, .25), 62500); 45 | } 46 | 47 | public void testRandomDelayForDuration() { 48 | assertEquals(RandomDelay.randomDelay(100, 50, 0), 150); 49 | assertEquals(RandomDelay.randomDelay(100, 50, .25), 125); 50 | assertEquals(RandomDelay.randomDelay(100, 50, .5), 100); 51 | assertEquals(RandomDelay.randomDelay(100, 50, .75), 75); 52 | assertEquals(RandomDelay.randomDelay(100, 50, .9999), 50); 53 | 54 | assertEquals(RandomDelay.randomDelay(500, 50, .25), 525); 55 | assertEquals(RandomDelay.randomDelay(50000, 5000, .25), 52500); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/issues/Issue115Test.java: -------------------------------------------------------------------------------- 1 | package dev.failsafe.issues; 2 | 3 | import dev.failsafe.RetryPolicy; 4 | import org.testng.annotations.Test; 5 | 6 | import java.time.Duration; 7 | 8 | /** 9 | * Tests https://github.com/jhalterman/failsafe/issues/115 and https://github.com/jhalterman/failsafe/issues/116 10 | */ 11 | @Test 12 | public class Issue115Test { 13 | @Test(expectedExceptions = IllegalStateException.class) 14 | public void shouldFailWithJitterLargerThanDelay() { 15 | RetryPolicy.builder() 16 | .handle(IllegalArgumentException.class) 17 | .withDelay(Duration.ofMillis(100)) 18 | .withJitter(Duration.ofMillis(200)) 19 | .build(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/issues/Issue146Test.java: -------------------------------------------------------------------------------- 1 | package dev.failsafe.issues; 2 | 3 | import dev.failsafe.Failsafe; 4 | import dev.failsafe.RetryPolicy; 5 | import org.testng.annotations.Test; 6 | 7 | import java.util.Objects; 8 | import java.util.concurrent.atomic.AtomicInteger; 9 | 10 | import static org.testng.Assert.assertEquals; 11 | 12 | /** 13 | * Tests https://github.com/jhalterman/failsafe/issues/146 14 | */ 15 | @Test 16 | public class Issue146Test { 17 | public void shouldRespectFailureCondition() { 18 | AtomicInteger successCounter = new AtomicInteger(); 19 | AtomicInteger failureCounter = new AtomicInteger(); 20 | AtomicInteger failedAttemptCounter = new AtomicInteger(); 21 | RetryPolicy retryPolicy = RetryPolicy.builder() 22 | .handleResultIf(Objects::isNull) 23 | .withMaxRetries(2) 24 | .onSuccess(e -> successCounter.incrementAndGet()) 25 | .onFailure(e -> failureCounter.incrementAndGet()) 26 | .onFailedAttempt(e -> failedAttemptCounter.incrementAndGet()) 27 | .build(); 28 | 29 | Failsafe.with(retryPolicy).get(() -> null); 30 | 31 | assertEquals(3, failedAttemptCounter.get()); 32 | assertEquals(0, successCounter.get()); 33 | assertEquals(1, failureCounter.get()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/issues/Issue165Test.java: -------------------------------------------------------------------------------- 1 | package dev.failsafe.issues; 2 | 3 | import dev.failsafe.Fallback; 4 | import dev.failsafe.Failsafe; 5 | import org.testng.annotations.Test; 6 | 7 | import java.util.concurrent.CompletableFuture; 8 | 9 | import static org.testng.Assert.assertEquals; 10 | import static org.testng.Assert.assertTrue; 11 | 12 | @Test 13 | public class Issue165Test { 14 | public void testOfStage() { 15 | Fallback fallback = Fallback.ofStage(e -> { 16 | assertTrue(e.getLastException() instanceof IllegalStateException); 17 | return CompletableFuture.supplyAsync(() -> "test"); 18 | }); 19 | Object result = Failsafe.with(fallback).get(() -> { 20 | throw new IllegalStateException(); 21 | }); 22 | assertEquals(result, "test"); 23 | } 24 | 25 | public void testOfStageAsync() throws Throwable { 26 | Fallback fallback = Fallback.builderOfStage(e -> CompletableFuture.completedFuture("test")).withAsync().build(); 27 | Object result = Failsafe.with(fallback).getAsync(() -> { 28 | throw new IllegalStateException(); 29 | }).get(); 30 | assertEquals(result, "test"); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/issues/Issue177Test.java: -------------------------------------------------------------------------------- 1 | package dev.failsafe.issues; 2 | 3 | import dev.failsafe.Fallback; 4 | import dev.failsafe.Failsafe; 5 | import org.testng.annotations.Test; 6 | 7 | import static org.testng.Assert.assertNull; 8 | 9 | @Test 10 | public class Issue177Test { 11 | public void shouldSupportNullFallback() { 12 | Fallback fallback = Fallback.builder((Boolean) null).handleResult(false).build(); 13 | assertNull(Failsafe.with(fallback).get(() -> false)); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/issues/Issue190Test.java: -------------------------------------------------------------------------------- 1 | package dev.failsafe.issues; 2 | 3 | import dev.failsafe.RetryPolicy; 4 | import dev.failsafe.testing.Testing; 5 | import net.jodah.concurrentunit.Waiter; 6 | import dev.failsafe.Failsafe; 7 | import org.testng.Assert; 8 | import org.testng.annotations.AfterClass; 9 | import org.testng.annotations.BeforeClass; 10 | import org.testng.annotations.Test; 11 | 12 | import java.util.concurrent.Executors; 13 | import java.util.concurrent.ScheduledExecutorService; 14 | import java.util.concurrent.atomic.AtomicInteger; 15 | 16 | @Test 17 | public class Issue190Test { 18 | ScheduledExecutorService executor; 19 | 20 | @BeforeClass 21 | protected void beforeClass() { 22 | executor = Executors.newSingleThreadScheduledExecutor(); 23 | } 24 | 25 | @AfterClass 26 | protected void afterClass() { 27 | executor.shutdownNow(); 28 | } 29 | 30 | public void test() throws Throwable { 31 | RetryPolicy policy = RetryPolicy.builder().withMaxRetries(5).build(); 32 | AtomicInteger failureEvents = new AtomicInteger(); 33 | AtomicInteger successEvents = new AtomicInteger(); 34 | Waiter waiter = new Waiter(); 35 | 36 | Failsafe.with(policy).onFailure(e -> { 37 | failureEvents.incrementAndGet(); 38 | waiter.resume(); 39 | }).onSuccess(e -> { 40 | successEvents.incrementAndGet(); 41 | waiter.resume(); 42 | }).getAsyncExecution(execution -> Testing.futureResult(executor, true).whenComplete((result, failure) -> { 43 | execution.recordResult(result); 44 | })).get(); 45 | 46 | waiter.await(1000); 47 | Assert.assertEquals(failureEvents.get(), 0); 48 | Assert.assertEquals(successEvents.get(), 1); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/issues/Issue192Test.java: -------------------------------------------------------------------------------- 1 | package dev.failsafe.issues; 2 | 3 | import dev.failsafe.testing.Asserts; 4 | import dev.failsafe.testing.Testing; 5 | import dev.failsafe.Failsafe; 6 | import dev.failsafe.RetryPolicy; 7 | import org.testng.Assert; 8 | import org.testng.annotations.AfterClass; 9 | import org.testng.annotations.BeforeClass; 10 | import org.testng.annotations.Test; 11 | 12 | import java.util.concurrent.ExecutionException; 13 | import java.util.concurrent.Executors; 14 | import java.util.concurrent.ScheduledExecutorService; 15 | import java.util.concurrent.atomic.AtomicInteger; 16 | 17 | @Test 18 | public class Issue192Test { 19 | ScheduledExecutorService executor; 20 | 21 | static class ExceptionA extends Exception { 22 | } 23 | 24 | static class ExceptionB extends Exception { 25 | } 26 | 27 | static class ExceptionC extends Exception { 28 | } 29 | 30 | @BeforeClass 31 | protected void beforeClass() { 32 | executor = Executors.newSingleThreadScheduledExecutor(); 33 | } 34 | 35 | @AfterClass 36 | protected void afterClass() { 37 | executor.shutdownNow(); 38 | } 39 | 40 | /** 41 | * Asserts the handling of multiple retry policies with an async execution. 42 | */ 43 | public void testAsync() { 44 | AtomicInteger exceptionA = new AtomicInteger(); 45 | AtomicInteger exceptionB = new AtomicInteger(); 46 | AtomicInteger exceptionC = new AtomicInteger(); 47 | RetryPolicy policyA = RetryPolicy.builder() 48 | .handle(ExceptionA.class) 49 | .withMaxRetries(5) 50 | .onRetry(evt -> exceptionA.incrementAndGet()) 51 | .build(); 52 | RetryPolicy policyB = RetryPolicy.builder() 53 | .handle(ExceptionB.class) 54 | .withMaxRetries(3) 55 | .onRetry(evt -> exceptionB.incrementAndGet()) 56 | .build(); 57 | RetryPolicy policyC = RetryPolicy.builder() 58 | .handle(ExceptionC.class) 59 | .withMaxRetries(2) 60 | .onRetry(evt -> exceptionC.incrementAndGet()) 61 | .build(); 62 | 63 | Asserts.assertThrows(() -> Failsafe.with(policyA, policyB, policyC) 64 | .getAsyncExecution( 65 | execution -> Testing.futureException(executor, new ExceptionB()).whenComplete((result, failure) -> { 66 | //System.out.println("Result = " + result + "; failure = " + failure); 67 | execution.record(result, failure); 68 | })) 69 | .get(), ExecutionException.class, ExceptionB.class); 70 | 71 | Assert.assertEquals(exceptionA.get(), 0); 72 | Assert.assertEquals(exceptionB.get(), 3); 73 | Assert.assertEquals(exceptionC.get(), 0); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/issues/Issue206Test.java: -------------------------------------------------------------------------------- 1 | package dev.failsafe.issues; 2 | 3 | import dev.failsafe.Fallback; 4 | import dev.failsafe.Failsafe; 5 | import org.testng.annotations.Test; 6 | 7 | import static org.testng.Assert.fail; 8 | 9 | @Test 10 | public class Issue206Test { 11 | public void test() { 12 | try { 13 | Failsafe.with(Fallback.builder(e -> true).handleResultIf(r -> true).build()) 14 | .onFailure(e -> fail("Unexpected execution failure")) 15 | .get(() -> true); 16 | } catch (RuntimeException e) { 17 | fail("Unexpected exception"); 18 | } 19 | 20 | try { 21 | Failsafe.with(Fallback.of(() -> { 22 | })).onFailure(e -> fail("Unexpected execution failure")).run(() -> { 23 | throw new RuntimeException(); 24 | }); 25 | } catch (RuntimeException e) { 26 | fail("Unexpected exception"); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/issues/Issue215Test.java: -------------------------------------------------------------------------------- 1 | package dev.failsafe.issues; 2 | 3 | import dev.failsafe.RetryPolicy; 4 | import org.testng.annotations.Test; 5 | 6 | import java.time.Duration; 7 | 8 | @Test 9 | public class Issue215Test { 10 | public void test() { 11 | RetryPolicy.builder() 12 | .withBackoff(Duration.ofNanos(Long.MAX_VALUE).minusSeconds(1), Duration.ofSeconds(Long.MAX_VALUE), 1.50); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/issues/Issue218Test.java: -------------------------------------------------------------------------------- 1 | package dev.failsafe.issues; 2 | 3 | import dev.failsafe.Failsafe; 4 | import dev.failsafe.Fallback; 5 | import dev.failsafe.RetryPolicy; 6 | import org.testng.annotations.Test; 7 | 8 | @Test 9 | public class Issue218Test { 10 | public void test() { 11 | RetryPolicy retryPolicy = RetryPolicy.builder().withMaxAttempts(2).build(); 12 | Fallback fallback = Fallback.none(); 13 | Failsafe.with(fallback, retryPolicy).run(() -> { 14 | throw new Exception(); 15 | }); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/issues/Issue224Test.java: -------------------------------------------------------------------------------- 1 | package dev.failsafe.issues; 2 | 3 | import dev.failsafe.RetryPolicy; 4 | import org.testng.annotations.Test; 5 | 6 | import java.time.Duration; 7 | import java.time.temporal.ChronoUnit; 8 | 9 | @Test 10 | public class Issue224Test { 11 | public void test() { 12 | RetryPolicy.builder().withDelay(10, 100, ChronoUnit.MILLIS).withJitter(Duration.ofMillis(5)).build(); 13 | RetryPolicy.builder().withDelay(1, 100, ChronoUnit.MILLIS).withJitter(.5).build(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/issues/Issue231Test.java: -------------------------------------------------------------------------------- 1 | package dev.failsafe.issues; 2 | 3 | import dev.failsafe.Timeout; 4 | import dev.failsafe.testing.Asserts; 5 | import dev.failsafe.Failsafe; 6 | import dev.failsafe.TimeoutExceededException; 7 | import org.testng.annotations.Test; 8 | 9 | import java.time.Duration; 10 | import java.util.concurrent.ExecutionException; 11 | import java.util.concurrent.ExecutorService; 12 | import java.util.concurrent.Executors; 13 | import java.util.concurrent.atomic.AtomicBoolean; 14 | 15 | import static org.testng.Assert.assertTrue; 16 | 17 | @Test 18 | public class Issue231Test { 19 | /** 20 | * Timeout, even with interruption, should wait for the execution to complete before completing the future. 21 | */ 22 | public void shouldWaitForExecutionCompletion() { 23 | // Use a separate executorService for this test in case the common pool is full 24 | ExecutorService executorService = Executors.newFixedThreadPool(2); 25 | Timeout timeout = Timeout.builder(Duration.ofMillis(100)).withInterrupt().build(); 26 | AtomicBoolean executionCompleted = new AtomicBoolean(); 27 | Asserts.assertThrows(() -> Failsafe.with(timeout).with(executorService).runAsync(() -> { 28 | try { 29 | Thread.sleep(1000); 30 | } catch (InterruptedException ignore) { 31 | Thread.sleep(200); 32 | executionCompleted.set(true); 33 | } 34 | }).get(), ExecutionException.class, TimeoutExceededException.class); 35 | assertTrue(executionCompleted.get()); 36 | executorService.shutdownNow(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/issues/Issue240Test.java: -------------------------------------------------------------------------------- 1 | package dev.failsafe.issues; 2 | 3 | import dev.failsafe.Failsafe; 4 | import dev.failsafe.RetryPolicy; 5 | import dev.failsafe.testing.Testing; 6 | import org.testng.annotations.Test; 7 | 8 | import java.util.concurrent.atomic.AtomicInteger; 9 | 10 | import static org.testng.Assert.assertEquals; 11 | 12 | @Test 13 | public class Issue240Test { 14 | public void testHandleResult() { 15 | AtomicInteger counter = new AtomicInteger(); 16 | RetryPolicy rp = RetryPolicy.builder() 17 | .handle(IllegalArgumentException.class) 18 | .withMaxRetries(2) 19 | .handleResult(null) 20 | .build(); 21 | 22 | Testing.ignoreExceptions(() -> { 23 | Failsafe.with(rp).get(() -> { 24 | counter.incrementAndGet(); 25 | throw new IllegalStateException(); 26 | }); 27 | }); 28 | 29 | assertEquals(counter.get(), 1); 30 | } 31 | 32 | public void testAbortWhen() { 33 | AtomicInteger counter = new AtomicInteger(); 34 | RetryPolicy rp = RetryPolicy.builder() 35 | .handle(IllegalArgumentException.class) 36 | .withMaxRetries(2) 37 | .abortWhen(null) 38 | .build(); 39 | 40 | Testing.ignoreExceptions(() -> { 41 | Failsafe.with(rp).get(() -> { 42 | counter.incrementAndGet(); 43 | throw new IllegalArgumentException(); 44 | }); 45 | }); 46 | 47 | assertEquals(counter.get(), 3); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/issues/Issue242Test.java: -------------------------------------------------------------------------------- 1 | package dev.failsafe.issues; 2 | 3 | import dev.failsafe.Failsafe; 4 | import dev.failsafe.RetryPolicy; 5 | import dev.failsafe.testing.Testing; 6 | import org.testng.annotations.Test; 7 | 8 | import java.time.Duration; 9 | 10 | import static dev.failsafe.testing.Testing.withLogs; 11 | import static org.testng.Assert.assertTrue; 12 | 13 | @Test 14 | public class Issue242Test extends Testing { 15 | public void shouldDelayOnExplicitRetry() throws Throwable { 16 | RetryPolicy retryPolicy = withLogs( 17 | RetryPolicy.builder().handleResult(null).withDelay(Duration.ofMillis(110))).build(); 18 | 19 | long elapsed = timed(() -> Failsafe.with(retryPolicy).runAsyncExecution(exec -> { 20 | exec.record(null, null); 21 | }).get()); 22 | 23 | assertTrue(elapsed > 200, "Expected delay between retries"); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/issues/Issue260Test.java: -------------------------------------------------------------------------------- 1 | package dev.failsafe.issues; 2 | 3 | import dev.failsafe.Failsafe; 4 | import dev.failsafe.RetryPolicy; 5 | import dev.failsafe.Timeout; 6 | import dev.failsafe.function.ContextualRunnable; 7 | import org.testng.annotations.Test; 8 | 9 | import java.time.Duration; 10 | import java.util.concurrent.ExecutorService; 11 | import java.util.concurrent.Executors; 12 | import java.util.concurrent.Future; 13 | import java.util.concurrent.TimeUnit; 14 | import java.util.function.Function; 15 | 16 | @Test 17 | public class Issue260Test { 18 | public void test() throws Throwable { 19 | ExecutorService executor = Executors.newSingleThreadExecutor(); 20 | Timeout timeout = Timeout.builder(Duration.ofMillis(300)) 21 | .withInterrupt() 22 | .onFailure(e -> System.out.println("Interrupted")) 23 | .build(); 24 | RetryPolicy rp = RetryPolicy.builder() 25 | .onRetry(e -> System.out.println("Retrying")) 26 | .onSuccess(e -> System.out.println("Success")) 27 | .build(); 28 | 29 | Function task = (taskId) -> ctx -> { 30 | System.out.println("Starting execution of task " + taskId); 31 | try { 32 | Thread.sleep(200); 33 | } catch (InterruptedException e) { 34 | System.out.println("Interrupted task " + taskId); 35 | throw e; 36 | } 37 | }; 38 | 39 | Future f1 = Failsafe.with(rp, timeout).with(executor).runAsync(task.apply(1)); 40 | Future f2 = Failsafe.with(rp, timeout).with(executor).runAsync(task.apply(2)); 41 | Future f3 = Failsafe.with(rp, timeout).with(executor).runAsync(task.apply(3)); 42 | f1.get(1, TimeUnit.SECONDS); 43 | f2.get(1, TimeUnit.SECONDS); 44 | f3.get(1, TimeUnit.SECONDS); 45 | executor.shutdownNow(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/issues/Issue267Test.java: -------------------------------------------------------------------------------- 1 | package dev.failsafe.issues; 2 | 3 | import dev.failsafe.ExecutionContext; 4 | import dev.failsafe.Fallback; 5 | import dev.failsafe.Timeout; 6 | import dev.failsafe.event.ExecutionAttemptedEvent; 7 | import dev.failsafe.Failsafe; 8 | import org.testng.annotations.Test; 9 | 10 | import java.net.ConnectException; 11 | import java.time.Duration; 12 | 13 | import static org.testng.Assert.assertNull; 14 | 15 | @Test 16 | public class Issue267Test { 17 | public void test() { 18 | Timeout timeout = Timeout.of(Duration.ofMillis(1000L)); 19 | Fallback notFoundFallback = Fallback.builder(this::handleNotFound).handleIf(this::causedBy404).build(); 20 | Fallback failureHandling = Fallback.ofException(this::handleException); 21 | 22 | Integer result = Failsafe.with(failureHandling, notFoundFallback, timeout).get(this::connect); 23 | assertNull(result); 24 | } 25 | 26 | private Integer connect(ExecutionContext context) throws ConnectException { 27 | throw new ConnectException(); 28 | } 29 | 30 | private boolean causedBy404(Object o, Throwable throwable) { 31 | return throwable instanceof ConnectException; 32 | } 33 | 34 | private Object handleNotFound(ExecutionAttemptedEvent event) { 35 | return null; 36 | } 37 | 38 | private Exception handleException(ExecutionAttemptedEvent event) { 39 | return new IllegalArgumentException(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/issues/Issue284Test.java: -------------------------------------------------------------------------------- 1 | package dev.failsafe.issues; 2 | 3 | import dev.failsafe.Fallback; 4 | import dev.failsafe.RetryPolicy; 5 | import dev.failsafe.Failsafe; 6 | import org.testng.annotations.BeforeMethod; 7 | import org.testng.annotations.Test; 8 | 9 | import java.util.concurrent.atomic.AtomicBoolean; 10 | import java.util.concurrent.atomic.AtomicInteger; 11 | 12 | import static org.testng.Assert.*; 13 | 14 | @Test 15 | public class Issue284Test { 16 | AtomicInteger failedAttempt; 17 | AtomicBoolean success; 18 | AtomicBoolean failure; 19 | AtomicBoolean executed; 20 | Fallback fallback; 21 | RetryPolicy retryPolicy = RetryPolicy.builder() 22 | .handleResult(null) 23 | .onFailedAttempt(e -> failedAttempt.incrementAndGet()) 24 | .onSuccess(e -> success.set(true)) 25 | .onFailure(e -> failure.set(true)) 26 | .build(); 27 | 28 | @BeforeMethod 29 | protected void beforeMethod() { 30 | failedAttempt = new AtomicInteger(); 31 | success = new AtomicBoolean(); 32 | failure = new AtomicBoolean(); 33 | executed = new AtomicBoolean(); 34 | } 35 | 36 | private Fallback fallbackFor(String result) { 37 | return Fallback.builder(result) 38 | .handleResult(null) 39 | .onFailedAttempt(e -> failedAttempt.incrementAndGet()) 40 | .onSuccess(e -> success.set(true)) 41 | .onFailure(e -> failure.set(true)) 42 | .build(); 43 | } 44 | 45 | public void testFallbackSuccess() { 46 | fallback = fallbackFor("hello"); 47 | String result = Failsafe.with(fallback).get(() -> null); 48 | 49 | assertEquals(result, "hello"); 50 | assertEquals(failedAttempt.get(), 1); 51 | assertTrue(success.get(), "Fallback should have been successful"); 52 | } 53 | 54 | public void testFallbackFailure() { 55 | fallback = fallbackFor(null); 56 | String result = Failsafe.with(fallback).get(() -> null); 57 | 58 | assertNull(result); 59 | assertEquals(failedAttempt.get(), 1); 60 | assertTrue(failure.get(), "Fallback should have failed"); 61 | } 62 | 63 | public void testRetryPolicySuccess() { 64 | String result = Failsafe.with(retryPolicy).get(() -> !executed.getAndSet(true) ? null : "hello"); 65 | 66 | assertEquals(result, "hello"); 67 | assertEquals(failedAttempt.get(), 1); 68 | assertTrue(success.get(), "RetryPolicy should have been successful"); 69 | } 70 | 71 | public void testRetryPolicyFailure() { 72 | String result = Failsafe.with(retryPolicy).get(() -> null); 73 | 74 | assertNull(result); 75 | assertEquals(failedAttempt.get(), 3); 76 | assertTrue(failure.get(), "RetryPolicy should have failed"); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/issues/Issue298Test.java: -------------------------------------------------------------------------------- 1 | package dev.failsafe.issues; 2 | 3 | import dev.failsafe.Failsafe; 4 | import dev.failsafe.Fallback; 5 | import org.testng.annotations.BeforeMethod; 6 | import org.testng.annotations.Test; 7 | 8 | import java.util.concurrent.atomic.AtomicBoolean; 9 | 10 | import static org.testng.Assert.assertFalse; 11 | import static org.testng.Assert.assertTrue; 12 | 13 | @Test 14 | public class Issue298Test { 15 | AtomicBoolean failedAttemptCalled = new AtomicBoolean(); 16 | AtomicBoolean failureCalled = new AtomicBoolean(); 17 | 18 | Fallback fallback = Fallback.builder(e -> "success") 19 | .onFailedAttempt(e -> failedAttemptCalled.set(true)) 20 | .onFailure(e -> failureCalled.set(true)) 21 | .build(); 22 | 23 | @BeforeMethod 24 | protected void beforeMethod() { 25 | failedAttemptCalled.set(false); 26 | failureCalled.set(false); 27 | } 28 | 29 | public void testSync() { 30 | Failsafe.with(fallback).get(() -> { 31 | throw new Exception(); 32 | }); 33 | 34 | assertTrue(failedAttemptCalled.get()); 35 | assertFalse(failureCalled.get()); 36 | } 37 | 38 | public void testAsync() throws Throwable { 39 | Failsafe.with(fallback).getAsync(() -> { 40 | throw new Exception(); 41 | }).get(); 42 | 43 | assertTrue(failedAttemptCalled.get()); 44 | assertFalse(failureCalled.get()); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/issues/Issue311Test.java: -------------------------------------------------------------------------------- 1 | package dev.failsafe.issues; 2 | 3 | import dev.failsafe.Failsafe; 4 | import dev.failsafe.RetryPolicy; 5 | import net.jodah.concurrentunit.Waiter; 6 | import org.testng.annotations.Test; 7 | 8 | import java.util.concurrent.CompletableFuture; 9 | import java.util.concurrent.Executor; 10 | import java.util.concurrent.ExecutorService; 11 | import java.util.concurrent.Executors; 12 | import java.util.concurrent.atomic.AtomicInteger; 13 | 14 | import static org.testng.Assert.*; 15 | 16 | @Test 17 | public class Issue311Test { 18 | public void failsafeFail() throws Throwable { 19 | AtomicInteger counter = new AtomicInteger(0); 20 | Executor executor = Executors.newSingleThreadExecutor(); 21 | Failsafe.with(RetryPolicy.builder().handle(RuntimeException.class).withMaxAttempts(2).build()) 22 | .with(executor) 23 | .runAsync(() -> { 24 | if (counter.incrementAndGet() == 1) 25 | throw new RuntimeException(); 26 | }) 27 | .get(); 28 | assertEquals(counter.get(), 2); 29 | ((ExecutorService) executor).shutdownNow(); 30 | } 31 | 32 | public void testNullCompletionStage() throws Throwable { 33 | assertNull(Failsafe.none().getStageAsync(() -> { 34 | return null; 35 | }).get()); 36 | } 37 | 38 | public void testRunAsyncWithThreadLocalInExecutor() throws Throwable { 39 | ThreadLocal threadLocal = new ThreadLocal<>(); 40 | Executor executor = runnable -> { 41 | threadLocal.set(true); 42 | runnable.run(); 43 | }; 44 | Failsafe.none().with(executor).runAsync(() -> { 45 | assertTrue(threadLocal.get()); 46 | }).get(); 47 | } 48 | 49 | public void testGetStageAsyncWithThreadLocalInExecutor() throws Throwable { 50 | ThreadLocal threadLocal = new ThreadLocal<>(); 51 | Executor executor = runnable -> { 52 | threadLocal.set(true); 53 | runnable.run(); 54 | }; 55 | assertNull(Failsafe.none().with(executor).getStageAsync(() -> { 56 | assertTrue(threadLocal.get()); 57 | return CompletableFuture.completedFuture("ignored"); 58 | }).get()); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/issues/Issue52Test.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.issues; 17 | 18 | import dev.failsafe.RetryPolicy; 19 | import dev.failsafe.testing.Asserts; 20 | import dev.failsafe.Failsafe; 21 | import org.testng.annotations.AfterClass; 22 | import org.testng.annotations.Test; 23 | 24 | import java.time.Duration; 25 | import java.util.concurrent.*; 26 | import java.util.concurrent.atomic.AtomicInteger; 27 | 28 | import static org.testng.Assert.assertEquals; 29 | import static org.testng.Assert.assertTrue; 30 | 31 | @Test 32 | public class Issue52Test { 33 | ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2); 34 | 35 | @AfterClass 36 | protected void afterClass() { 37 | scheduler.shutdownNow(); 38 | } 39 | 40 | @Test(expectedExceptions = CancellationException.class) 41 | public void shouldCancelExecutionViaFuture() throws Throwable { 42 | Future proxyFuture = Failsafe.with(RetryPolicy.builder().withDelay(Duration.ofMillis(10)).build()) 43 | .with(scheduler) 44 | .getAsync(exec -> { 45 | throw new IllegalStateException(); 46 | }); 47 | 48 | assertTrue(proxyFuture.cancel(true)); 49 | proxyFuture.get(); // should throw CancellationException per .getAsync() javadoc. 50 | } 51 | 52 | public void shouldCancelExecutionViaCompletableFuture() throws Throwable { 53 | AtomicInteger counter = new AtomicInteger(); 54 | CompletableFuture proxyFuture = Failsafe.with( 55 | RetryPolicy.builder().withDelay(Duration.ofMillis(10)).build()).with(scheduler).getStageAsync(exec -> { 56 | Thread.sleep(100); 57 | counter.incrementAndGet(); 58 | CompletableFuture result = new CompletableFuture<>(); 59 | result.completeExceptionally(new RuntimeException()); 60 | return result; 61 | }); 62 | 63 | assertTrue(proxyFuture.cancel(true)); 64 | int count = counter.get(); 65 | 66 | assertTrue(proxyFuture.isCancelled()); 67 | Asserts.assertThrows(proxyFuture::get, CancellationException.class); 68 | 69 | // Assert that execution has actually stopped 70 | Thread.sleep(20); 71 | assertEquals(count, counter.get()); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/issues/Issue55Test.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.issues; 17 | 18 | import dev.failsafe.Fallback; 19 | import dev.failsafe.RetryPolicy; 20 | import dev.failsafe.Failsafe; 21 | import org.testng.annotations.Test; 22 | 23 | import java.util.concurrent.Executors; 24 | import java.util.concurrent.ScheduledExecutorService; 25 | import java.util.concurrent.atomic.AtomicInteger; 26 | 27 | import static org.testng.Assert.assertEquals; 28 | 29 | @Test 30 | public class Issue55Test { 31 | public void shouldOnlyFallbackOnFailure() throws Throwable { 32 | ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); 33 | 34 | AtomicInteger counter = new AtomicInteger(); 35 | Failsafe.with(Fallback.of(counter::incrementAndGet), RetryPolicy.ofDefaults()).with(executor).getAsync(() -> null); 36 | 37 | Thread.sleep(100); 38 | assertEquals(counter.get(), 0); 39 | 40 | Failsafe.with(Fallback.of(counter::incrementAndGet), RetryPolicy.builder().withMaxRetries(1).build()) 41 | .with(executor) 42 | .runAsync(() -> { 43 | throw new RuntimeException(); 44 | }); 45 | 46 | Thread.sleep(100); 47 | assertEquals(counter.get(), 1); 48 | executor.shutdownNow(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/issues/Issue5Test.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.issues; 17 | 18 | import dev.failsafe.Failsafe; 19 | import dev.failsafe.RetryPolicy; 20 | import net.jodah.concurrentunit.Waiter; 21 | import org.testng.annotations.Test; 22 | 23 | import java.time.Duration; 24 | import java.util.concurrent.*; 25 | 26 | @Test 27 | public class Issue5Test { 28 | /** 29 | * Asserts that a failure is handled as expected by a listener registered via whenFailure. 30 | */ 31 | public void test() throws Throwable { 32 | Waiter waiter = new Waiter(); 33 | RetryPolicy retryPolicy = RetryPolicy.builder() 34 | .withDelay(Duration.ofMillis(100)) 35 | .withMaxDuration(Duration.ofSeconds(2)) 36 | .withMaxRetries(3) 37 | .handleResult(null) 38 | .build(); 39 | 40 | ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); 41 | Failsafe.with(retryPolicy).with(executor).onFailure(e -> { 42 | waiter.assertNull(e.getResult()); 43 | waiter.assertNull(e.getException()); 44 | waiter.resume(); 45 | }).getAsync(() -> null); 46 | 47 | waiter.await(1000); 48 | executor.shutdownNow(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/issues/Issue75Test.java: -------------------------------------------------------------------------------- 1 | package dev.failsafe.issues; 2 | 3 | import dev.failsafe.Fallback; 4 | import dev.failsafe.CircuitBreaker; 5 | import dev.failsafe.Failsafe; 6 | import org.testng.Assert; 7 | import org.testng.annotations.Test; 8 | 9 | import java.time.Duration; 10 | import java.util.concurrent.CompletableFuture; 11 | import java.util.concurrent.Executors; 12 | import java.util.concurrent.ScheduledExecutorService; 13 | 14 | public class Issue75Test { 15 | @Test 16 | public void testThatFailSafeIsBrokenWithFallback() throws Exception { 17 | CircuitBreaker breaker = CircuitBreaker.builder() 18 | .withFailureThreshold(10, 100) 19 | .withSuccessThreshold(2) 20 | .withDelay(Duration.ofMillis(100)) 21 | .build(); 22 | ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); 23 | int result = Failsafe.with(Fallback.of(e -> 999), breaker) 24 | .with(executor) 25 | .getStageAsync(() -> CompletableFuture.completedFuture(223)) 26 | .get(); 27 | 28 | Assert.assertEquals(result, 223); 29 | executor.shutdownNow(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/issues/Issue76Test.java: -------------------------------------------------------------------------------- 1 | package dev.failsafe.issues; 2 | 3 | import net.jodah.concurrentunit.Waiter; 4 | import dev.failsafe.Failsafe; 5 | import dev.failsafe.RetryPolicy; 6 | import org.testng.annotations.Test; 7 | 8 | import java.util.concurrent.ExecutionException; 9 | import java.util.concurrent.Executors; 10 | import java.util.concurrent.Future; 11 | import java.util.concurrent.ScheduledExecutorService; 12 | 13 | import static org.testng.Assert.assertEquals; 14 | import static org.testng.Assert.fail; 15 | 16 | @Test 17 | public class Issue76Test { 18 | public void shouldAbortOnSyncError() { 19 | AssertionError error = new AssertionError(); 20 | try { 21 | Failsafe.with(RetryPolicy.builder().abortOn(AssertionError.class).build()).run(() -> { 22 | throw error; 23 | }); 24 | fail(); 25 | } catch (AssertionError e) { 26 | assertEquals(e, error); 27 | } 28 | } 29 | 30 | public void shouldAbortOnAsyncError() throws Exception { 31 | final AssertionError error = new AssertionError(); 32 | Waiter waiter = new Waiter(); 33 | ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); 34 | Future future = Failsafe.with(RetryPolicy.builder().abortOn(AssertionError.class).onAbort(e -> { 35 | waiter.assertEquals(e.getException(), error); 36 | waiter.resume(); 37 | }).build()).with(executor).runAsync(() -> { 38 | throw error; 39 | }); 40 | waiter.await(1000); 41 | 42 | try { 43 | future.get(); 44 | fail(); 45 | } catch (ExecutionException e) { 46 | assertEquals(e.getCause(), error); 47 | } finally { 48 | executor.shutdownNow(); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/issues/Issue84Test.java: -------------------------------------------------------------------------------- 1 | package dev.failsafe.issues; 2 | 3 | import dev.failsafe.CircuitBreaker; 4 | import dev.failsafe.CircuitBreakerOpenException; 5 | import dev.failsafe.Failsafe; 6 | import dev.failsafe.Fallback; 7 | import dev.failsafe.testing.Asserts; 8 | import org.testng.annotations.Test; 9 | 10 | import java.time.Duration; 11 | import java.util.concurrent.*; 12 | 13 | import static org.testng.Assert.assertFalse; 14 | 15 | @Test 16 | public class Issue84Test { 17 | public void shouldHandleCircuitBreakerOpenException() throws Throwable { 18 | ScheduledExecutorService executor = Executors.newScheduledThreadPool(2); 19 | CircuitBreaker circuitBreaker = CircuitBreaker.builder() 20 | .withDelay(Duration.ofMinutes(10)) 21 | .handleResult(false) 22 | .build(); 23 | circuitBreaker.open(); 24 | 25 | // Synchronous 26 | Asserts.assertThrows(() -> Failsafe.with(circuitBreaker).get(() -> true), CircuitBreakerOpenException.class); 27 | 28 | // Synchronous with fallback 29 | assertFalse(Failsafe.with(Fallback.of(false), circuitBreaker).get(() -> true)); 30 | 31 | // Asynchronous 32 | Future future1 = Failsafe.with(circuitBreaker).with(executor).getAsync(() -> true); 33 | Asserts.assertThrows(future1::get, ExecutionException.class, CircuitBreakerOpenException.class); 34 | 35 | // Asynchronous with fallback 36 | Future future2 = Failsafe.with(Fallback.of(false), circuitBreaker).with(executor).getAsync(() -> true); 37 | assertFalse(future2.get()); 38 | 39 | // Future 40 | Future future3 = Failsafe.with(circuitBreaker) 41 | .with(executor) 42 | .getStageAsync(() -> CompletableFuture.completedFuture(false)); 43 | Asserts.assertThrows(future3::get, ExecutionException.class, CircuitBreakerOpenException.class); 44 | 45 | // Future with fallback 46 | Future future4 = Failsafe.with(Fallback.of(false), circuitBreaker) 47 | .getStageAsync(() -> CompletableFuture.completedFuture(false)); 48 | assertFalse(future4.get()); 49 | 50 | executor.shutdownNow(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/issues/Issue9Test.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.issues; 17 | 18 | import dev.failsafe.Failsafe; 19 | import dev.failsafe.RetryPolicy; 20 | import net.jodah.concurrentunit.Waiter; 21 | import org.testng.annotations.Test; 22 | 23 | import java.util.concurrent.Executors; 24 | import java.util.concurrent.Future; 25 | import java.util.concurrent.ScheduledExecutorService; 26 | import java.util.concurrent.atomic.AtomicInteger; 27 | 28 | import static dev.failsafe.testing.Mocking.failures; 29 | import static org.mockito.Mockito.*; 30 | import static org.testng.Assert.assertEquals; 31 | import static org.testng.Assert.assertTrue; 32 | 33 | @Test 34 | public class Issue9Test { 35 | public interface Service { 36 | boolean connect(); 37 | } 38 | 39 | public void test() throws Throwable { 40 | // Given - Fail twice then succeed 41 | AtomicInteger retryCounter = new AtomicInteger(); 42 | Service service = mock(Service.class); 43 | when(service.connect()).thenThrow(failures(2, new IllegalStateException())).thenReturn(true); 44 | ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); 45 | Waiter waiter = new Waiter(); 46 | 47 | // When 48 | AtomicInteger successCounter = new AtomicInteger(); 49 | Future future = Failsafe.with( 50 | RetryPolicy.builder().withMaxRetries(2).onRetry(e -> retryCounter.incrementAndGet()).build()) 51 | .with(executor) 52 | .onSuccess(p -> { 53 | successCounter.incrementAndGet(); 54 | waiter.resume(); 55 | }) 56 | .getAsync(service::connect); 57 | 58 | // Then 59 | waiter.await(1000); 60 | verify(service, times(3)).connect(); 61 | assertTrue(future.get()); 62 | assertEquals(retryCounter.get(), 2); 63 | assertEquals(successCounter.get(), 1); 64 | executor.shutdownNow(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/testing/TestCaseLogger.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.testing; 17 | 18 | import org.testng.*; 19 | 20 | import java.util.Map; 21 | import java.util.concurrent.ConcurrentHashMap; 22 | 23 | /** 24 | * Logs test invocations. 25 | */ 26 | public class TestCaseLogger implements IInvokedMethodListener { 27 | private static final Map START_TIMES = new ConcurrentHashMap<>(); 28 | 29 | public void beforeInvocation(IInvokedMethod method, ITestResult testResult) { 30 | if (!method.isTestMethod()) 31 | return; 32 | 33 | ITestNGMethod testMethod = method.getTestMethod(); 34 | IClass clazz = testMethod.getTestClass(); 35 | 36 | START_TIMES.put(testMethod, System.currentTimeMillis()); 37 | System.out.printf("BEFORE %s#%s%n", clazz.getRealClass().getName(), testMethod.getMethodName()); 38 | } 39 | 40 | public void afterInvocation(IInvokedMethod method, ITestResult testResult) { 41 | if (!method.isTestMethod()) 42 | return; 43 | 44 | ITestNGMethod testMethod = method.getTestMethod(); 45 | IClass clazz = testMethod.getTestClass(); 46 | double elapsed = (System.currentTimeMillis() - START_TIMES.remove(testMethod)) / 1000.0; 47 | 48 | if (elapsed > 1) 49 | System.out.printf("AFTER %s#%s Ran for %s seconds%n", clazz.getRealClass().getName(), testMethod.getMethodName(), elapsed); 50 | } 51 | } -------------------------------------------------------------------------------- /core/src/test/java/dev/failsafe/testing/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Utilities to assist with testing. 3 | */ 4 | package dev.failsafe.testing; 5 | -------------------------------------------------------------------------------- /examples/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | dev.failsafe 8 | failsafe-examples 9 | 1.0-SNAPSHOT 10 | Failsafe Examples 11 | 12 | 13 | 3.2.1 14 | 1.8 15 | 1.8 16 | 17 | 18 | 19 | 20 | ${project.groupId} 21 | failsafe 22 | ${failsafe.version} 23 | 24 | 25 | 26 | 27 | io.reactivex 28 | rxjava 29 | 1.0.12 30 | 31 | 32 | io.netty 33 | netty-all 34 | 4.1.51.Final 35 | 36 | 37 | io.vertx 38 | vertx-core 39 | 3.9.8 40 | 41 | 42 | 43 | 44 | org.testng 45 | testng 46 | 6.9.10 47 | 48 | 49 | org.mockito 50 | mockito-core 51 | 4.2.0 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /examples/src/main/java/dev/failsafe/examples/AsyncExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.examples; 17 | 18 | import dev.failsafe.Failsafe; 19 | import dev.failsafe.RetryPolicy; 20 | 21 | import java.time.Duration; 22 | import java.util.concurrent.CompletableFuture; 23 | import java.util.concurrent.Executors; 24 | import java.util.concurrent.ScheduledExecutorService; 25 | import java.util.concurrent.atomic.AtomicInteger; 26 | 27 | public class AsyncExample { 28 | static ScheduledExecutorService executor = Executors.newScheduledThreadPool(2); 29 | static RetryPolicy retryPolicy = RetryPolicy.builder() 30 | .withDelay(Duration.ofMillis(100)) 31 | .withJitter(.25) 32 | .onRetry(e -> System.out.println("Connection attempt failed. Retrying.")) 33 | .onSuccess(e -> System.out.println("Success")) 34 | .onFailure(e -> System.out.println("Connection attempts failed")) 35 | .build(); 36 | static Service service = new Service(); 37 | 38 | public static class Service { 39 | AtomicInteger failures = new AtomicInteger(); 40 | 41 | // Fail 2 times then succeed 42 | CompletableFuture connect() { 43 | CompletableFuture future = new CompletableFuture<>(); 44 | executor.submit(() -> { 45 | if (failures.getAndIncrement() < 2) 46 | future.completeExceptionally(new RuntimeException()); 47 | else 48 | future.complete(true); 49 | }); 50 | return future; 51 | } 52 | } 53 | 54 | public static void main(String... args) throws Throwable { 55 | Failsafe.with(retryPolicy) 56 | .with(executor) 57 | .getAsyncExecution(execution -> service.connect().whenComplete(execution::record)); 58 | 59 | Thread.sleep(3000); 60 | System.exit(0); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /examples/src/main/java/dev/failsafe/examples/Java8Example.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.examples; 17 | 18 | import dev.failsafe.Failsafe; 19 | import dev.failsafe.RetryPolicy; 20 | 21 | import java.util.concurrent.CompletableFuture; 22 | import java.util.concurrent.Executors; 23 | import java.util.concurrent.ScheduledExecutorService; 24 | import java.util.function.Function; 25 | import java.util.stream.Collectors; 26 | import java.util.stream.Stream; 27 | 28 | public class Java8Example { 29 | @SuppressWarnings("unused") 30 | public static void main(String... args) { 31 | ScheduledExecutorService executor = Executors.newScheduledThreadPool(2); 32 | RetryPolicy retryPolicy = RetryPolicy.ofDefaults(); 33 | 34 | // Create a retryable functional interface 35 | Function bar = value -> Failsafe.with(retryPolicy).get(() -> value + "bar"); 36 | 37 | // Create a retryable Stream operation 38 | Failsafe.with(retryPolicy).get(() -> Stream.of("foo") 39 | .map(value -> Failsafe.with(retryPolicy).get(() -> value + "bar")) 40 | .collect(Collectors.toList())); 41 | 42 | // Create an individual retryable Stream operation 43 | Stream.of("foo").map(value -> Failsafe.with(retryPolicy).get(() -> value + "bar")).forEach(System.out::println); 44 | 45 | // Create a retryable CompletableFuture 46 | Failsafe.with(retryPolicy).with(executor).getStageAsync(() -> CompletableFuture.supplyAsync(() -> "foo") 47 | .thenApplyAsync(value -> value + "bar") 48 | .thenAccept(System.out::println)); 49 | 50 | // Create an individual retryable CompletableFuture stages 51 | CompletableFuture.supplyAsync(() -> Failsafe.with(retryPolicy).get(() -> "foo")) 52 | .thenApplyAsync(value -> Failsafe.with(retryPolicy).get(() -> value + "bar")) 53 | .thenAccept(System.out::println); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /examples/src/main/java/dev/failsafe/examples/RetryLoopExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.examples; 17 | 18 | import dev.failsafe.Execution; 19 | import dev.failsafe.RetryPolicy; 20 | 21 | import java.time.temporal.ChronoUnit; 22 | import java.util.List; 23 | 24 | import static org.mockito.Mockito.mock; 25 | import static org.mockito.Mockito.when; 26 | import static org.testng.Assert.assertEquals; 27 | 28 | @SuppressWarnings("unchecked") 29 | public class RetryLoopExample { 30 | static List list; 31 | 32 | static { 33 | list = mock(List.class); 34 | when(list.size()).thenThrow(IllegalStateException.class, IllegalStateException.class).thenReturn(5); 35 | } 36 | 37 | public static void main(String... args) throws Throwable { 38 | RetryPolicy retryPolicy = RetryPolicy.builder() 39 | .handle(IllegalStateException.class) 40 | .withBackoff(10, 40, ChronoUnit.MILLIS) 41 | .build(); 42 | Execution execution = Execution.of(retryPolicy); 43 | 44 | while (!execution.isComplete()) { 45 | try { 46 | execution.recordResult(list.size()); 47 | } catch (IllegalStateException e) { 48 | execution.recordFailure(e); 49 | 50 | // Wait before retrying 51 | Thread.sleep(execution.getDelay().toMillis()); 52 | } 53 | } 54 | 55 | assertEquals(execution.getLastResult(), 5); 56 | assertEquals(execution.getAttemptCount(), 3); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /examples/src/main/java/dev/failsafe/examples/RxJavaExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package dev.failsafe.examples; 17 | 18 | import dev.failsafe.Execution; 19 | import dev.failsafe.RetryPolicy; 20 | import rx.Observable; 21 | import rx.Subscriber; 22 | 23 | import java.time.Duration; 24 | import java.util.concurrent.TimeUnit; 25 | import java.util.concurrent.atomic.AtomicInteger; 26 | 27 | public class RxJavaExample { 28 | public static void main(String... args) { 29 | AtomicInteger failures = new AtomicInteger(); 30 | RetryPolicy retryPolicy = RetryPolicy.builder().withDelay(Duration.ofSeconds(1)).build(); 31 | 32 | Observable.create((Subscriber s) -> { 33 | // Fail 2 times then succeed 34 | if (failures.getAndIncrement() < 2) 35 | s.onError(new RuntimeException()); 36 | else 37 | System.out.println("Subscriber completed successfully"); 38 | }).retryWhen(attempts -> { 39 | Execution execution = Execution.of(retryPolicy); 40 | return attempts.flatMap(failure -> { 41 | System.out.println("Failure detected"); 42 | execution.recordFailure(failure); 43 | if (!execution.isComplete()) 44 | return Observable.timer(execution.getDelay().toNanos(), TimeUnit.NANOSECONDS); 45 | else 46 | return Observable.error(failure); 47 | }); 48 | }).toBlocking().forEach(System.out::println); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /modules/okhttp/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | dev.failsafe 7 | failsafe-parent 8 | 3.3.3-SNAPSHOT 9 | ../../pom.xml 10 | 11 | 12 | failsafe-okhttp 13 | Failsafe OkHttp 14 | 15 | 16 | ${project.groupId}.okhttp 17 | 18 | 19 | 20 | 21 | ${project.groupId} 22 | failsafe 23 | ${project.version} 24 | 25 | 26 | com.squareup.okhttp3 27 | okhttp 28 | 4.9.3 29 | 30 | 31 | 32 | 33 | ${project.groupId} 34 | failsafe 35 | ${project.version} 36 | test-jar 37 | test 38 | 39 | 40 | com.github.tomakehurst 41 | wiremock-jre8 42 | 2.32.0 43 | test 44 | 45 | 46 | 47 | 48 | 49 | 50 | org.moditect 51 | moditect-maven-plugin 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /modules/retrofit/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | dev.failsafe 7 | failsafe-parent 8 | 3.3.3-SNAPSHOT 9 | ../../pom.xml 10 | 11 | 12 | failsafe-retrofit 13 | Failsafe Retrofit 14 | 15 | 16 | ${project.groupId}.retrofit 17 | 18 | 19 | 20 | 21 | ${project.groupId} 22 | failsafe 23 | ${project.version} 24 | 25 | 26 | com.squareup.retrofit2 27 | retrofit 28 | 2.9.0 29 | 30 | 31 | 32 | 33 | ${project.groupId} 34 | failsafe 35 | ${project.version} 36 | test-jar 37 | test 38 | 39 | 40 | com.google.code.gson 41 | gson 42 | 2.9.0 43 | test 44 | 45 | 46 | com.squareup.retrofit2 47 | converter-gson 48 | 2.3.0 49 | test 50 | 51 | 52 | com.github.tomakehurst 53 | wiremock-jre8 54 | 2.32.0 55 | test 56 | 57 | 58 | 59 | 60 | 61 | 62 | org.moditect 63 | moditect-maven-plugin 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /modules/retrofit/src/test/java/dev/retrofit/TestService.java: -------------------------------------------------------------------------------- 1 | package dev.retrofit; 2 | 3 | import retrofit2.Call; 4 | import retrofit2.http.GET; 5 | 6 | import java.util.Objects; 7 | 8 | public interface TestService { 9 | @GET("/test") 10 | Call testUser(); 11 | 12 | public static class User { 13 | public String getName() { 14 | return name; 15 | } 16 | 17 | public void setName(String name) { 18 | this.name = name; 19 | } 20 | 21 | private String name; 22 | 23 | public User(String name) { 24 | this.name = name; 25 | } 26 | 27 | @Override 28 | public boolean equals(Object o) { 29 | if (this == o) 30 | return true; 31 | if (o == null || getClass() != o.getClass()) 32 | return false; 33 | User user = (User) o; 34 | return Objects.equals(name, user.name); 35 | } 36 | } 37 | } 38 | --------------------------------------------------------------------------------