├── .github ├── actions │ ├── build │ │ └── action.yml │ ├── create-github-release │ │ ├── action.yml │ │ └── changelog-generator.yml │ ├── prepare-maven-build │ │ └── action.yml │ ├── print-jvm-thread-dumps │ │ └── action.yml │ ├── send-notification │ │ └── action.yml │ └── sync-to-maven-central │ │ ├── action.yml │ │ └── artifacts.spec ├── dco.yml └── workflows │ ├── announce-milestone-planning.yml │ ├── build-and-deploy-snapshot.yml │ ├── build-pull-request.yml │ └── release.yml ├── .gitignore ├── .mvn ├── extensions.xml └── wrapper │ └── maven-wrapper.properties ├── CODE_OF_CONDUCT.adoc ├── LICENSE-2.0.txt ├── NOTICE.txt ├── README.md ├── jitpack.yml ├── mvnw ├── mvnw.cmd ├── pom.xml └── src ├── main └── java │ └── org │ └── springframework │ ├── classify │ ├── BackToBackPatternClassifier.java │ ├── BinaryExceptionClassifier.java │ ├── BinaryExceptionClassifierBuilder.java │ ├── Classifier.java │ ├── ClassifierAdapter.java │ ├── ClassifierSupport.java │ ├── PatternMatcher.java │ ├── PatternMatchingClassifier.java │ ├── SubclassClassifier.java │ ├── annotation │ │ └── Classifier.java │ └── util │ │ ├── AnnotationMethodResolver.java │ │ ├── MethodInvoker.java │ │ ├── MethodInvokerUtils.java │ │ ├── MethodResolver.java │ │ └── SimpleMethodInvoker.java │ └── retry │ ├── ExhaustedRetryException.java │ ├── RecoveryCallback.java │ ├── RetryCallback.java │ ├── RetryContext.java │ ├── RetryException.java │ ├── RetryListener.java │ ├── RetryOperations.java │ ├── RetryPolicy.java │ ├── RetryState.java │ ├── RetryStatistics.java │ ├── TerminatedRetryException.java │ ├── annotation │ ├── AnnotationAwareRetryOperationsInterceptor.java │ ├── Backoff.java │ ├── CircuitBreaker.java │ ├── EnableRetry.java │ ├── Recover.java │ ├── RecoverAnnotationRecoveryHandler.java │ ├── RetryConfiguration.java │ └── Retryable.java │ ├── backoff │ ├── BackOffContext.java │ ├── BackOffInterruptedException.java │ ├── BackOffPolicy.java │ ├── BackOffPolicyBuilder.java │ ├── ExponentialBackOffPolicy.java │ ├── ExponentialRandomBackOffPolicy.java │ ├── FixedBackOffPolicy.java │ ├── NoBackOffPolicy.java │ ├── ObjectWaitSleeper.java │ ├── Sleeper.java │ ├── SleepingBackOffPolicy.java │ ├── StatelessBackOffPolicy.java │ ├── ThreadWaitSleeper.java │ ├── UniformRandomBackOffPolicy.java │ └── package.html │ ├── context │ ├── RetryContextSupport.java │ └── package.html │ ├── interceptor │ ├── FixedKeyGenerator.java │ ├── MethodArgumentsKeyGenerator.java │ ├── MethodInvocationRecoverer.java │ ├── MethodInvocationRetryCallback.java │ ├── NewMethodArgumentsIdentifier.java │ ├── RetryInterceptorBuilder.java │ ├── RetryOperationsInterceptor.java │ ├── Retryable.java │ ├── StatefulRetryOperationsInterceptor.java │ └── package.html │ ├── listener │ ├── MethodInvocationRetryListenerSupport.java │ ├── RetryListenerSupport.java │ └── package.html │ ├── package.html │ ├── policy │ ├── AlwaysRetryPolicy.java │ ├── BinaryExceptionClassifierRetryPolicy.java │ ├── CircuitBreakerRetryPolicy.java │ ├── CompositeRetryPolicy.java │ ├── ExceptionClassifierRetryPolicy.java │ ├── ExpressionRetryPolicy.java │ ├── MapRetryContextCache.java │ ├── MaxAttemptsRetryPolicy.java │ ├── NeverRetryPolicy.java │ ├── PredicateRetryPolicy.java │ ├── RetryCacheCapacityExceededException.java │ ├── RetryContextCache.java │ ├── SimpleRetryPolicy.java │ ├── SoftReferenceMapRetryContextCache.java │ ├── TimeoutRetryPolicy.java │ └── package.html │ ├── stats │ ├── DefaultRetryStatistics.java │ ├── DefaultRetryStatisticsFactory.java │ ├── DefaultStatisticsRepository.java │ ├── ExponentialAverageRetryStatistics.java │ ├── MutableRetryStatistics.java │ ├── RetryStatisticsFactory.java │ ├── StatisticsListener.java │ └── StatisticsRepository.java │ └── support │ ├── Args.java │ ├── DefaultRetryState.java │ ├── MetricsRetryListener.java │ ├── RetrySimulation.java │ ├── RetrySimulator.java │ ├── RetrySynchronizationManager.java │ ├── RetryTemplate.java │ ├── RetryTemplateBuilder.java │ └── package.html └── test ├── java └── org │ └── springframework │ ├── classify │ ├── BackToBackPatternClassifierTests.java │ ├── BinaryExceptionClassifierBuilderTests.java │ ├── BinaryExceptionClassifierTests.java │ ├── ClassifierAdapterTests.java │ ├── ClassifierSupportTests.java │ ├── PatternMatchingClassifierTests.java │ ├── SubclassClassifierTests.java │ └── SubclassExceptionClassifierTests.java │ └── retry │ ├── AbstractExceptionTests.java │ ├── AnyThrowTests.java │ ├── BackOffInterruptedExceptionTests.java │ ├── ExhaustedRetryExceptionTests.java │ ├── ResourcelessTransactionManager.java │ ├── RetryExceptionTests.java │ ├── TerminatedRetryExceptionTests.java │ ├── annotation │ ├── CircuitBreakerResetTimeoutTests.java │ ├── CircuitBreakerTests.java │ ├── DontRetryRecovererTests.java │ ├── EnableRetryNoThreadLocalTests.java │ ├── EnableRetryTests.java │ ├── EnableRetryWithBackoffNoThreadLocalTests.java │ ├── EnableRetryWithBackoffTests.java │ ├── EnableRetryWithListenersTests.java │ ├── PrototypeBeanTests.java │ ├── ProxyApplicationTests.java │ ├── RecoverAnnotationRecoveryHandlerTests.java │ └── RetryableXmlConfigTests.java │ ├── backoff │ ├── BackOffPolicyBuilderTests.java │ ├── BackOffPolicySerializationTests.java │ ├── DummySleeper.java │ ├── ExponentialBackOffPolicyTests.java │ ├── ExponentialRandomBackOffPolicyTests.java │ ├── FixedBackOffPolicyTests.java │ ├── ThreadWaitSleeperTests.java │ └── UniformRandomBackOffPolicyTests.java │ ├── interceptor │ ├── RetryInterceptorBuilderTests.java │ ├── RetryOperationsInterceptorTests.java │ └── StatefulRetryOperationsInterceptorTests.java │ ├── listener │ ├── MethodInvocationRetryListenerSupportTests.java │ └── RetryListenerTests.java │ ├── policy │ ├── AlwaysRetryPolicyTests.java │ ├── CircuitBreakerRetryTemplateTests.java │ ├── CompositeRetryPolicyTests.java │ ├── ExceptionClassifierRetryPolicyTests.java │ ├── FatalExceptionRetryPolicyTests.java │ ├── MapRetryContextCacheTests.java │ ├── MockRetryPolicySupport.java │ ├── NeverRetryPolicyTests.java │ ├── RetryContextSerializationTests.java │ ├── SerializedMapRetryContextCache.java │ ├── SimpleRetryPolicyTests.java │ ├── SoftReferenceMapRetryContextCacheTests.java │ ├── StatefulRetryIntegrationTests.java │ └── TimeoutRetryPolicyTests.java │ ├── stats │ ├── CircuitBreakerInterceptorStatisticsTests.java │ ├── CircuitBreakerStatisticsTests.java │ ├── ExponentialAverageRetryStatisticsTests.java │ └── StatisticsListenerTests.java │ ├── support │ ├── DefaultRetryStateTests.java │ ├── RetryMetricsTests.java │ ├── RetrySimulationTests.java │ ├── RetrySynchronizationManagerNoThreadLocalTests.java │ ├── RetrySynchronizationManagerTests.java │ ├── RetryTemplateBuilderTests.java │ ├── RetryTemplateNoThreadLocalTests.java │ ├── RetryTemplateTests.java │ └── StatefulRecoveryRetryTests.java │ └── util │ └── test │ └── TestUtils.java └── resources ├── log4j2-test.xml └── org └── springframework ├── repeat └── support │ └── trades.csv └── retry ├── annotation └── RetryableXmlConfigTests-context.xml └── interceptor └── retry-transaction-test.xml /.github/actions/build/action.yml: -------------------------------------------------------------------------------- 1 | name: 'Build' 2 | description: 'Builds the project, optionally publishing it to a local deployment repository' 3 | inputs: 4 | java-version: 5 | required: false 6 | default: '17' 7 | description: 'The Java version to compile and test with' 8 | java-distribution: 9 | required: false 10 | default: 'liberica' 11 | description: 'The Java distribution to use for the build' 12 | publish: 13 | required: false 14 | default: 'false' 15 | description: 'Whether to publish artifacts ready for deployment to Artifactory' 16 | outputs: 17 | version: 18 | description: 'The version that was built' 19 | value: ${{ steps.read-version.outputs.version }} 20 | runs: 21 | using: composite 22 | steps: 23 | - name: Prepare Maven Build 24 | uses: ./.github/actions/prepare-maven-build 25 | with: 26 | java-version: ${{ inputs.java-version }} 27 | java-distribution: ${{ inputs.java-distribution }} 28 | - name: Build 29 | id: build 30 | if: ${{ inputs.publish == 'false' }} 31 | shell: bash 32 | run: ./mvnw --no-transfer-progress --batch-mode --update-snapshots verify 33 | - name: Publish 34 | id: publish 35 | if: ${{ inputs.publish == 'true' }} 36 | shell: bash 37 | run: ./mvnw --no-transfer-progress --batch-mode --update-snapshots -DaltDeploymentRepository=local::file:deployment-repository/ clean deploy -Pspring -Duser.name=spring-builds+github 38 | - name: Read version from pom.xml 39 | id: read-version 40 | shell: bash 41 | run: | 42 | version=$(sed -n 's/^.*\(.*\)<\/revision>.*$/\1/p' pom.xml) 43 | echo "Version is $version" 44 | echo "version=$version" >> $GITHUB_OUTPUT 45 | -------------------------------------------------------------------------------- /.github/actions/create-github-release/action.yml: -------------------------------------------------------------------------------- 1 | name: Create GitHub Release 2 | description: Create the release on GitHub with a changelog 3 | inputs: 4 | milestone: 5 | description: 'Name of the GitHub milestone for which a release will be created' 6 | required: true 7 | token: 8 | description: 'Token to use for authentication with GitHub' 9 | required: true 10 | prerelease: 11 | description: 'Whether the release is a pre-release (milestone or release candidate)' 12 | required: false 13 | default: 'false' 14 | runs: 15 | using: composite 16 | steps: 17 | - name: Generate Changelog 18 | uses: spring-io/github-changelog-generator@86958813a62af8fb223b3fd3b5152035504bcb83 #v0.0.12 19 | with: 20 | milestone: ${{ inputs.milestone }} 21 | token: ${{ inputs.token }} 22 | config-file: .github/actions/create-github-release/changelog-generator.yml 23 | - name: Create GitHub Release 24 | env: 25 | GITHUB_TOKEN: ${{ inputs.token }} 26 | shell: bash 27 | run: gh release create ${{ format('v{0}', inputs.milestone) }} --notes-file changelog.md ${{ inputs.prerelease == 'true' && '--prerelease' || '' }} 28 | -------------------------------------------------------------------------------- /.github/actions/create-github-release/changelog-generator.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | repository: spring-projects/spring-retry 3 | sections: 4 | - title: ":star: New Features" 5 | labels: 6 | - "enhancement" 7 | - title: ":lady_beetle: Bug Fixes" 8 | labels: 9 | - "bug" 10 | - title: ":notebook_with_decorative_cover: Documentation" 11 | labels: 12 | - "documentation" 13 | - title: ":hammer: Dependency Upgrades" 14 | sort: "title" 15 | labels: 16 | - "dependencies" 17 | -------------------------------------------------------------------------------- /.github/actions/prepare-maven-build/action.yml: -------------------------------------------------------------------------------- 1 | name: 'Prepare Gradle Build' 2 | description: 'Prepares a Maven build. Sets up Java' 3 | inputs: 4 | java-version: 5 | required: false 6 | default: '17' 7 | description: 'The Java version to use for the build' 8 | java-distribution: 9 | required: false 10 | default: 'liberica' 11 | description: 'The Java distribution to use for the build' 12 | runs: 13 | using: composite 14 | steps: 15 | - name: Set Up Java 16 | uses: actions/setup-java@v4 17 | with: 18 | distribution: ${{ inputs.java-distribution }} 19 | java-version: | 20 | ${{ inputs.java-version }} 21 | ${{ inputs.java-toolchain == 'true' && '17' || '' }} 22 | -------------------------------------------------------------------------------- /.github/actions/print-jvm-thread-dumps/action.yml: -------------------------------------------------------------------------------- 1 | name: Print JVM thread dumps 2 | description: Prints a thread dump for all running JVMs 3 | runs: 4 | using: composite 5 | steps: 6 | - if: ${{ runner.os == 'Linux' }} 7 | shell: bash 8 | run: | 9 | for jvm_pid in $(jps -q -J-XX:+PerfDisableSharedMem); do 10 | jcmd $jvm_pid Thread.print 11 | done 12 | - if: ${{ runner.os == 'Windows' }} 13 | shell: powershell 14 | run: | 15 | foreach ($jvm_pid in $(jps -q -J-XX:+PerfDisableSharedMem)) { 16 | jcmd $jvm_pid Thread.print 17 | } 18 | -------------------------------------------------------------------------------- /.github/actions/send-notification/action.yml: -------------------------------------------------------------------------------- 1 | name: Send Notification 2 | description: Sends a Google Chat message as a notification of the job's outcome 3 | inputs: 4 | webhook-url: 5 | description: 'Google Chat Webhook URL' 6 | required: true 7 | status: 8 | description: 'Status of the job' 9 | required: true 10 | run-name: 11 | description: 'Name of the run to include in the notification' 12 | default: ${{ format('{0} {1}', github.ref_name, github.job) }} 13 | runs: 14 | using: composite 15 | steps: 16 | - shell: bash 17 | run: | 18 | echo "RUN_URL=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" >> "$GITHUB_ENV" 19 | - shell: bash 20 | if: ${{ inputs.status == 'success' }} 21 | run: | 22 | curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<${{ env.RUN_URL }}|${{ inputs.run-name }}> was successful"}' || true 23 | - shell: bash 24 | if: ${{ inputs.status == 'failure' }} 25 | run: | 26 | curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: " *<${{ env.RUN_URL }}|${{ inputs.run-name }}> failed*"}' || true 27 | - shell: bash 28 | if: ${{ inputs.status == 'cancelled' }} 29 | run: | 30 | curl -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: "<${{ env.RUN_URL }}|${{ inputs.run-name }}> was cancelled"}' || true 31 | -------------------------------------------------------------------------------- /.github/actions/sync-to-maven-central/action.yml: -------------------------------------------------------------------------------- 1 | name: Sync to Maven Central 2 | description: Syncs a release to Maven Central and waits for it to be available for use 3 | inputs: 4 | jfrog-cli-config-token: 5 | description: 'Config token for the JFrog CLI' 6 | required: true 7 | spring-retry-version: 8 | description: 'The version of Spring Retry that is being synced to Central' 9 | required: true 10 | ossrh-s01-token-username: 11 | description: 'Username for authentication with s01.oss.sonatype.org' 12 | required: true 13 | ossrh-s01-token-password: 14 | description: 'Password for authentication with s01.oss.sonatype.org' 15 | required: true 16 | ossrh-s01-staging-profile: 17 | description: 'Staging profile to use when syncing to Central' 18 | required: true 19 | runs: 20 | using: composite 21 | steps: 22 | - name: Set Up JFrog CLI 23 | uses: jfrog/setup-jfrog-cli@7c95feb32008765e1b4e626b078dfd897c4340ad # v4.1.2 24 | env: 25 | JF_ENV_SPRING: ${{ inputs.jfrog-cli-config-token }} 26 | - name: Download Release Artifacts 27 | shell: bash 28 | run: jf rt download --spec ${{ format('{0}/artifacts.spec', github.action_path) }} --spec-vars 'buildName=${{ format('spring-retry-{0}', inputs.spring-retry-version) }};buildNumber=${{ github.run_number }}' 29 | - name: Sync 30 | uses: spring-io/nexus-sync-action@42477a2230a2f694f9eaa4643fa9e76b99b7ab84 # v0.0.1 31 | with: 32 | username: ${{ inputs.ossrh-s01-token-username }} 33 | password: ${{ inputs.ossrh-s01-token-password }} 34 | staging-profile-name: ${{ inputs.ossrh-s01-staging-profile }} 35 | create: true 36 | upload: true 37 | close: true 38 | release: true 39 | generate-checksums: true 40 | - name: Await 41 | shell: bash 42 | run: | 43 | url=${{ format('https://repo.maven.apache.org/maven2/org/springframework/retry/spring-retry/{0}/spring-retry-{0}.jar', inputs.spring-retry-version) }} 44 | echo "Waiting for $url" 45 | until curl --fail --head --silent $url > /dev/null 46 | do 47 | echo "." 48 | sleep 60 49 | done 50 | echo "$url is available" 51 | -------------------------------------------------------------------------------- /.github/actions/sync-to-maven-central/artifacts.spec: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | { 4 | "aql": { 5 | "items.find": { 6 | "$and": [ 7 | { 8 | "@build.name": "${buildName}", 9 | "@build.number": "${buildNumber}" 10 | } 11 | ] 12 | } 13 | }, 14 | "target": "nexus/" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /.github/dco.yml: -------------------------------------------------------------------------------- 1 | require: 2 | members: false 3 | -------------------------------------------------------------------------------- /.github/workflows/announce-milestone-planning.yml: -------------------------------------------------------------------------------- 1 | name: Announce Milestone Planning in Chat 2 | 3 | on: 4 | milestone: 5 | types: [ created, edited ] 6 | 7 | jobs: 8 | announce-milestone-planning: 9 | uses: spring-io/spring-github-workflows/.github/workflows/spring-announce-milestone-planning.yml@main 10 | secrets: 11 | SPRING_RELEASE_CHAT_WEBHOOK_URL: ${{ secrets.SPRING_RELEASE_GCHAT_WEBHOOK_URL }} 12 | -------------------------------------------------------------------------------- /.github/workflows/build-and-deploy-snapshot.yml: -------------------------------------------------------------------------------- 1 | name: Build and Deploy Snapshot 2 | on: 3 | push: 4 | branches: 5 | - main 6 | permissions: 7 | actions: write 8 | concurrency: 9 | group: ${{ github.workflow }}-${{ github.ref }} 10 | 11 | env: 12 | DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }} 13 | 14 | jobs: 15 | build-and-deploy-snapshot: 16 | if: ${{ github.repository == 'spring-projects/spring-retry' }} 17 | name: Build and Deploy Snapshot 18 | runs-on: ubuntu-latest 19 | steps: 20 | - name: Check Out Code 21 | uses: actions/checkout@v4 22 | - name: Build and Publish 23 | id: build-and-publish 24 | uses: ./.github/actions/build 25 | with: 26 | publish: true 27 | - name: Deploy 28 | uses: spring-io/artifactory-deploy-action@26bbe925a75f4f863e1e529e85be2d0093cac116 # v0.0.1 29 | with: 30 | uri: 'https://repo.spring.io' 31 | username: ${{ secrets.ARTIFACTORY_USERNAME }} 32 | password: ${{ secrets.ARTIFACTORY_PASSWORD }} 33 | build-name: 'spring-retry-2.0.x' 34 | repository: 'libs-snapshot-local' 35 | folder: 'deployment-repository' 36 | signing-key: ${{ secrets.GPG_PRIVATE_KEY }} 37 | signing-passphrase: ${{ secrets.GPG_PASSPHRASE }} 38 | artifact-properties: | 39 | /**/*.zip::zip.name=${{ github.event.repository.name }},zip.deployed=false 40 | /**/*docs.zip::zip.type=docs 41 | - name: Send Notification 42 | uses: ./.github/actions/send-notification 43 | if: always() 44 | with: 45 | webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }} 46 | status: ${{ job.status }} 47 | run-name: ${{ format('{0} | Linux | Java 17', github.ref_name) }} 48 | outputs: 49 | version: ${{ steps.build-and-publish.outputs.version }} 50 | -------------------------------------------------------------------------------- /.github/workflows/build-pull-request.yml: -------------------------------------------------------------------------------- 1 | name: Build Pull Request 2 | on: pull_request 3 | 4 | permissions: 5 | contents: read 6 | 7 | jobs: 8 | build: 9 | name: Build Pull Request 10 | runs-on: ubuntu-latest 11 | if: ${{ github.repository == 'spring-projects/spring-retry' }} 12 | steps: 13 | - name: Set Up JDK 17 14 | uses: actions/setup-java@v4 15 | with: 16 | java-version: '17' 17 | distribution: 'liberica' 18 | - name: Check Out 19 | uses: actions/checkout@v4 20 | - name: Build 21 | run: ./mvnw --batch-mode --update-snapshots verify 22 | - name: Print JVM Thread Dumps When Cancelled 23 | uses: ./.github/actions/print-jvm-thread-dumps 24 | if: cancelled() 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .#* 2 | *# 3 | *~ 4 | target 5 | .springBeans 6 | .sts4-cache 7 | .settings 8 | .classpath 9 | .project 10 | *.iml 11 | *.ipr 12 | *.iws 13 | .idea 14 | credentials.yml 15 | .DS_Store 16 | .vscode 17 | /.mvn/.develocity 18 | -------------------------------------------------------------------------------- /.mvn/extensions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | io.spring.develocity.conventions 5 | develocity-conventions-maven-extension 6 | 0.0.22 7 | 8 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | wrapperVersion=3.3.2 2 | distributionType=only-script 3 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.8/apache-maven-3.9.8-bin.zip 4 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.adoc: -------------------------------------------------------------------------------- 1 | = Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, and in the interest of fostering an open 4 | and welcoming community, we pledge to respect all people who contribute through reporting 5 | issues, posting feature requests, updating documentation, submitting pull requests or 6 | patches, and other activities. 7 | 8 | We are committed to making participation in this project a harassment-free experience for 9 | everyone, regardless of level of experience, gender, gender identity and expression, 10 | sexual orientation, disability, personal appearance, body size, race, ethnicity, age, 11 | religion, or nationality. 12 | 13 | Examples of unacceptable behavior by participants include: 14 | 15 | * The use of sexualized language or imagery 16 | * Personal attacks 17 | * Trolling or insulting/derogatory comments 18 | * Public or private harassment 19 | * Publishing other's private information, such as physical or electronic addresses, 20 | without explicit permission 21 | * Other unethical or unprofessional conduct 22 | 23 | Project maintainers have the right and responsibility to remove, edit, or reject comments, 24 | commits, code, wiki edits, issues, and other contributions that are not aligned to this 25 | Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors 26 | that they deem inappropriate, threatening, offensive, or harmful. 27 | 28 | By adopting this Code of Conduct, project maintainers commit themselves to fairly and 29 | consistently applying these principles to every aspect of managing this project. Project 30 | maintainers who do not follow or enforce the Code of Conduct may be permanently removed 31 | from the project team. 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an 34 | individual is representing the project or its community. 35 | 36 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by 37 | contacting a project maintainer at spring-code-of-conduct@pivotal.io . All complaints will 38 | be reviewed and investigated and will result in a response that is deemed necessary and 39 | appropriate to the circumstances. Maintainers are obligated to maintain confidentiality 40 | with regard to the reporter of an incident. 41 | 42 | This Code of Conduct is adapted from the 43 | https://contributor-covenant.org[Contributor Covenant], version 1.3.0, available at 44 | https://contributor-covenant.org/version/1/3/0/[contributor-covenant.org/version/1/3/0/] 45 | -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2006-2023 the original author or authors. 2 | 3 | This product is licensed to you under the Apache License, Version 2.0 4 | (the "License"). You may not use this product except in compliance with 5 | the License. 6 | -------------------------------------------------------------------------------- /jitpack.yml: -------------------------------------------------------------------------------- 1 | jdk: 2 | - oraclejdk8 3 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/classify/BackToBackPatternClassifier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-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 | * https://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 org.springframework.classify; 17 | 18 | import java.util.Map; 19 | 20 | /** 21 | * A special purpose {@link Classifier} with easy configuration options for mapping from 22 | * one arbitrary type of object to another via a pattern matcher. 23 | * 24 | * @author Dave Syer 25 | * @param the type of thing to classify 26 | * @param the output of the classifier 27 | * 28 | */ 29 | @SuppressWarnings("serial") 30 | public class BackToBackPatternClassifier implements Classifier { 31 | 32 | private Classifier router; 33 | 34 | private Classifier matcher; 35 | 36 | /** 37 | * Default constructor, provided as a convenience for people using setter injection. 38 | */ 39 | public BackToBackPatternClassifier() { 40 | } 41 | 42 | /** 43 | * Set up a classifier with input to the router and output from the matcher. 44 | * @param router see {@link #setRouterDelegate(Object)} 45 | * @param matcher see {@link #setMatcherMap(Map)} 46 | */ 47 | public BackToBackPatternClassifier(Classifier router, Classifier matcher) { 48 | super(); 49 | this.router = router; 50 | this.matcher = matcher; 51 | } 52 | 53 | /** 54 | * A convenience method for creating a pattern matching classifier for the matcher 55 | * component. 56 | * @param map maps pattern keys with wildcards to output values 57 | */ 58 | public void setMatcherMap(Map map) { 59 | this.matcher = new PatternMatchingClassifier<>(map); 60 | } 61 | 62 | /** 63 | * A convenience method of creating a router classifier based on a plain old Java 64 | * Object. The object provided must have precisely one public method that either has 65 | * the @Classifier annotation or accepts a single argument and outputs a 66 | * String. This will be used to create an input classifier for the router component. 67 | * @param delegate the delegate object used to create a router classifier 68 | */ 69 | public void setRouterDelegate(Object delegate) { 70 | this.router = new ClassifierAdapter<>(delegate); 71 | } 72 | 73 | /** 74 | * Classify the input and map to a String, then take that and put it into a pattern 75 | * matcher to match to an output value. 76 | */ 77 | @Override 78 | public T classify(C classifiable) { 79 | return this.matcher.classify(this.router.classify(classifiable)); 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/classify/BinaryExceptionClassifierBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-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 | * https://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 | 17 | package org.springframework.classify; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | import org.springframework.util.Assert; 23 | 24 | /** 25 | * Fluent API for BinaryExceptionClassifier configuration. 26 | *

27 | * Can be used in while list style:

{@code
 28 |  * BinaryExceptionClassifier.newBuilder()
 29 |  * 			.retryOn(IOException.class)
 30 |  * 			.retryOn(IllegalArgumentException.class)
 31 |  * 			.build();
 32 |  * } 
or in black list style:
{@code
 33 |  * BinaryExceptionClassifier.newBuilder()
 34 |  *            .notRetryOn(Error.class)
 35 |  *            .build();
 36 |  * } 
37 | *

38 | * Provides traverseCauses=false by default, and no default rules for exceptions. 39 | *

40 | * Not thread safe. Building should be performed in a single thread, publishing of newly 41 | * created instance should be safe. 42 | * 43 | * @author Aleksandr Shamukov 44 | */ 45 | public class BinaryExceptionClassifierBuilder { 46 | 47 | /** 48 | * Building notation type (white list or black list) - null: has not selected yet - 49 | * true: white list - false: black list 50 | */ 51 | private Boolean isWhiteList = null; 52 | 53 | private boolean traverseCauses = false; 54 | 55 | private final List> exceptionClasses = new ArrayList<>(); 56 | 57 | public BinaryExceptionClassifierBuilder retryOn(Class throwable) { 58 | Assert.isTrue(isWhiteList == null || isWhiteList, "Please use only retryOn() or only notRetryOn()"); 59 | Assert.notNull(throwable, "Exception class can not be null"); 60 | isWhiteList = true; 61 | exceptionClasses.add(throwable); 62 | return this; 63 | 64 | } 65 | 66 | public BinaryExceptionClassifierBuilder notRetryOn(Class throwable) { 67 | Assert.isTrue(isWhiteList == null || !isWhiteList, "Please use only retryOn() or only notRetryOn()"); 68 | Assert.notNull(throwable, "Exception class can not be null"); 69 | isWhiteList = false; 70 | exceptionClasses.add(throwable); 71 | return this; 72 | } 73 | 74 | public BinaryExceptionClassifierBuilder traversingCauses() { 75 | this.traverseCauses = true; 76 | return this; 77 | } 78 | 79 | public BinaryExceptionClassifier build() { 80 | Assert.isTrue(!exceptionClasses.isEmpty(), 81 | "Attempt to build classifier with empty rules. To build always true, or always false " 82 | + "instance, please use explicit rule for Throwable"); 83 | BinaryExceptionClassifier classifier = new BinaryExceptionClassifier(exceptionClasses, isWhiteList // using 84 | // white 85 | // list 86 | // means 87 | // classifying 88 | // provided 89 | // classes 90 | // as 91 | // "true" 92 | // (is 93 | // retryable) 94 | ); 95 | classifier.setTraverseCauses(traverseCauses); 96 | return classifier; 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/classify/Classifier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 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 | * https://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 | 17 | package org.springframework.classify; 18 | 19 | import java.io.Serializable; 20 | 21 | /** 22 | * Interface for a classifier. At its simplest a {@link Classifier} is just a map from 23 | * objects of one type to objects of another type. 24 | * 25 | * Note that implementations can only be serializable if the parameter types are 26 | * themselves serializable. 27 | * 28 | * @author Dave Syer 29 | * @param the type of the thing to classify 30 | * @param the output of the classifier 31 | * 32 | */ 33 | public interface Classifier extends Serializable { 34 | 35 | /** 36 | * Classify the given object and return an object of a different type, possibly an 37 | * enumerated type. 38 | * @param classifiable the input object. Can be null. 39 | * @return an object. Can be null, but implementations should declare if this is the 40 | * case. 41 | */ 42 | T classify(C classifiable); 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/classify/ClassifierAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2023 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 | * https://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 org.springframework.classify; 17 | 18 | import org.springframework.classify.util.MethodInvoker; 19 | import org.springframework.classify.util.MethodInvokerUtils; 20 | import org.springframework.util.Assert; 21 | 22 | /** 23 | * Wrapper for an object to adapt it to the {@link Classifier} interface. 24 | * 25 | * @author Dave Syer 26 | * @param the type of the thing to classify 27 | * @param the output of the classifier 28 | */ 29 | @SuppressWarnings("serial") 30 | public class ClassifierAdapter implements Classifier { 31 | 32 | private MethodInvoker invoker; 33 | 34 | private Classifier classifier; 35 | 36 | /** 37 | * Default constructor for use with setter injection. 38 | */ 39 | public ClassifierAdapter() { 40 | super(); 41 | } 42 | 43 | /** 44 | * Create a new {@link Classifier} from the delegate provided. Use the constructor as 45 | * an alternative to the {@link #setDelegate(Object)} method. 46 | * @param delegate the delegate 47 | */ 48 | public ClassifierAdapter(Object delegate) { 49 | setDelegate(delegate); 50 | } 51 | 52 | /** 53 | * Create a new {@link Classifier} from the delegate provided. Use the constructor as 54 | * an alternative to the {@link #setDelegate(Classifier)} method. 55 | * @param delegate the classifier to delegate to 56 | */ 57 | public ClassifierAdapter(Classifier delegate) { 58 | this.classifier = delegate; 59 | } 60 | 61 | public void setDelegate(Classifier delegate) { 62 | this.classifier = delegate; 63 | this.invoker = null; 64 | } 65 | 66 | /** 67 | * Search for the {@link org.springframework.classify.annotation.Classifier 68 | * Classifier} annotation on a method in the supplied delegate and use that to create 69 | * a {@link Classifier} from the parameter type to the return type. If the annotation 70 | * is not found a unique non-void method with a single parameter will be used, if it 71 | * exists. The signature of the method cannot be checked here, so might be a runtime 72 | * exception when the method is invoked if the signature doesn't match the classifier 73 | * types. 74 | * @param delegate an object with an annotated method 75 | */ 76 | public final void setDelegate(Object delegate) { 77 | this.classifier = null; 78 | this.invoker = MethodInvokerUtils 79 | .getMethodInvokerByAnnotation(org.springframework.classify.annotation.Classifier.class, delegate); 80 | if (this.invoker == null) { 81 | this.invoker = MethodInvokerUtils.getMethodInvokerForSingleArgument(delegate); 82 | } 83 | Assert.state(this.invoker != null, "No single argument public method with or without " 84 | + "@Classifier was found in delegate of type " + delegate.getClass()); 85 | } 86 | 87 | /** 88 | * {@inheritDoc} 89 | */ 90 | @Override 91 | @SuppressWarnings("unchecked") 92 | public T classify(C classifiable) { 93 | if (this.classifier != null) { 94 | return this.classifier.classify(classifiable); 95 | } 96 | return (T) this.invoker.invokeMethod(classifiable); 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/classify/ClassifierSupport.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 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 | * https://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 | 17 | package org.springframework.classify; 18 | 19 | /** 20 | * Base class for {@link Classifier} implementations. Provides default behaviour and some 21 | * convenience members, like constants. 22 | * 23 | * @author Dave Syer 24 | * @param the type of the thing to classify 25 | * @param the output of the classifier 26 | */ 27 | @SuppressWarnings("serial") 28 | public class ClassifierSupport implements Classifier { 29 | 30 | final private T defaultValue; 31 | 32 | /** 33 | * @param defaultValue the default value 34 | */ 35 | public ClassifierSupport(T defaultValue) { 36 | super(); 37 | this.defaultValue = defaultValue; 38 | } 39 | 40 | /** 41 | * Always returns the default value. This is the main extension point for subclasses, 42 | * so it must be able to classify null. 43 | * 44 | * @see org.springframework.classify.Classifier#classify(Object) 45 | */ 46 | @Override 47 | public T classify(C throwable) { 48 | return this.defaultValue; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/classify/PatternMatchingClassifier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-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 | * https://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 org.springframework.classify; 17 | 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | 21 | /** 22 | * A {@link Classifier} that maps from String patterns with wildcards to a set of values 23 | * of a given type. An input String is matched with the most specific pattern possible to 24 | * the corresponding value in an input map. A default value should be specified with a 25 | * pattern key of "*". 26 | * 27 | * @author Dave Syer 28 | * @param the output of the classifier 29 | */ 30 | @SuppressWarnings("serial") 31 | public class PatternMatchingClassifier implements Classifier { 32 | 33 | private PatternMatcher values; 34 | 35 | /** 36 | * Default constructor. Use the setter or the other constructor to create a sensible 37 | * classifier, otherwise all inputs will cause an exception. 38 | */ 39 | public PatternMatchingClassifier() { 40 | this(new HashMap<>()); 41 | } 42 | 43 | /** 44 | * Create a classifier from the provided map. The keys are patterns, using '?' as a 45 | * single character and '*' as multi-character wildcard. 46 | * @param values the values to use in the {@link PatternMatcher} 47 | */ 48 | public PatternMatchingClassifier(Map values) { 49 | super(); 50 | this.values = new PatternMatcher<>(values); 51 | } 52 | 53 | /** 54 | * A map from pattern to value 55 | * @param values the pattern map to set 56 | */ 57 | public void setPatternMap(Map values) { 58 | this.values = new PatternMatcher<>(values); 59 | } 60 | 61 | /** 62 | * Classify the input by matching it against the patterns provided in 63 | * {@link #setPatternMap(Map)}. The most specific pattern that matches will be used to 64 | * locate a value. 65 | * @return the value matching the most specific pattern possible 66 | * @throws IllegalStateException if no matching value is found. 67 | */ 68 | @Override 69 | public T classify(String classifiable) { 70 | T value = this.values.match(classifiable); 71 | return value; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/classify/annotation/Classifier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 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 | * https://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 org.springframework.classify.annotation; 17 | 18 | import java.lang.annotation.Documented; 19 | import java.lang.annotation.ElementType; 20 | import java.lang.annotation.Inherited; 21 | import java.lang.annotation.Retention; 22 | import java.lang.annotation.RetentionPolicy; 23 | import java.lang.annotation.Target; 24 | 25 | /** 26 | * Mark a method as capable of classifying its input to an instance of its output. Should 27 | * only be used on non-void methods with one parameter. 28 | * 29 | * @author Dave Syer 30 | * 31 | */ 32 | @Target(ElementType.METHOD) 33 | @Retention(RetentionPolicy.RUNTIME) 34 | @Inherited 35 | @Documented 36 | public @interface Classifier { 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/classify/util/MethodInvoker.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2002-2008 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 | * https://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 | 17 | package org.springframework.classify.util; 18 | 19 | /** 20 | * A strategy interface for invoking a method. Typically used by adapters. 21 | * 22 | * @author Mark Fisher 23 | */ 24 | public interface MethodInvoker { 25 | 26 | Object invokeMethod(Object... args); 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/classify/util/MethodResolver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2002-2008 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 | * https://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 | 17 | package org.springframework.classify.util; 18 | 19 | import java.lang.reflect.Method; 20 | 21 | /** 22 | * Strategy interface for detecting a single Method on a Class. 23 | * 24 | * @author Mark Fisher 25 | */ 26 | public interface MethodResolver { 27 | 28 | /** 29 | * Find a single Method on the provided Object that matches this resolver's criteria. 30 | * @param candidate the candidate Object whose Class should be searched for a Method 31 | * @return a single Method or null if no Method matching this resolver's 32 | * criteria can be found. 33 | * @throws IllegalArgumentException if more than one Method defined on the given 34 | * candidate's Class matches this resolver's criteria 35 | */ 36 | Method findMethod(Object candidate) throws IllegalArgumentException; 37 | 38 | /** 39 | * Find a single Method on the given Class that matches this resolver's 40 | * criteria. 41 | * @param clazz the Class instance on which to search for a Method 42 | * @return a single Method or null if no Method matching this resolver's 43 | * criteria can be found. 44 | * @throws IllegalArgumentException if more than one Method defined on the given Class 45 | * matches this resolver's criteria 46 | */ 47 | Method findMethod(Class clazz); 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/ExhaustedRetryException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 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 | * https://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 | 17 | package org.springframework.retry; 18 | 19 | @SuppressWarnings("serial") 20 | public class ExhaustedRetryException extends RetryException { 21 | 22 | public ExhaustedRetryException(String msg, Throwable cause) { 23 | super(msg, cause); 24 | } 25 | 26 | public ExhaustedRetryException(String msg) { 27 | super(msg); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/RecoveryCallback.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 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 | * https://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 org.springframework.retry; 17 | 18 | /** 19 | * Callback for stateful retry after all tries are exhausted. 20 | * 21 | * @author Dave Syer 22 | * @param the type that is returned from the recovery 23 | * @since 1.1 24 | */ 25 | public interface RecoveryCallback { 26 | 27 | /** 28 | * @param context the current retry context 29 | * @return an Object that can be used to replace the callback result that failed 30 | * @throws Exception when something goes wrong 31 | */ 32 | T recover(RetryContext context) throws Exception; 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/RetryCallback.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 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 | * https://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 | 17 | package org.springframework.retry; 18 | 19 | /** 20 | * Callback interface for an operation that can be retried using a 21 | * {@link RetryOperations}. 22 | * 23 | * @param the type of object returned by the callback 24 | * @param the type of exception it declares may be thrown 25 | * @author Rob Harrop 26 | * @author Dave Syer 27 | * @author Artem Bilan 28 | */ 29 | public interface RetryCallback { 30 | 31 | /** 32 | * Execute an operation with retry semantics. Operations should generally be 33 | * idempotent, but implementations may choose to implement compensation semantics when 34 | * an operation is retried. 35 | * @param context the current retry context. 36 | * @return the result of the successful operation. 37 | * @throws E of type E if processing fails 38 | */ 39 | T doWithRetry(RetryContext context) throws E; 40 | 41 | /** 42 | * A logical identifier for this callback to distinguish retries around business 43 | * operations. 44 | * @return the identifier for this callback. 45 | * @since 2.0.6 46 | */ 47 | default String getLabel() { 48 | return null; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/RetryException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 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 | * https://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 | 17 | package org.springframework.retry; 18 | 19 | import org.springframework.core.NestedRuntimeException; 20 | 21 | @SuppressWarnings("serial") 22 | public class RetryException extends NestedRuntimeException { 23 | 24 | public RetryException(String msg, Throwable cause) { 25 | super(msg, cause); 26 | } 27 | 28 | public RetryException(String msg) { 29 | super(msg); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/RetryListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-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 | * https://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 | 17 | package org.springframework.retry; 18 | 19 | /** 20 | * Interface for listener that can be used to add behaviour to a retry. Implementations of 21 | * {@link RetryOperations} can chose to issue callbacks to an interceptor during the retry 22 | * lifecycle. 23 | * 24 | * @author Dave Syer 25 | * @author Gary Russell 26 | * @author Henning Pöttker 27 | * 28 | */ 29 | public interface RetryListener { 30 | 31 | /** 32 | * Called before the first attempt in a retry. For instance, implementers can set up 33 | * state that is needed by the policies in the {@link RetryOperations}. The whole 34 | * retry can be vetoed by returning false from this method, in which case a 35 | * {@link TerminatedRetryException} will be thrown. 36 | * @param the type of object returned by the callback 37 | * @param the type of exception it declares may be thrown 38 | * @param context the current {@link RetryContext}. 39 | * @param callback the current {@link RetryCallback}. 40 | * @return true if the retry should proceed. 41 | */ 42 | default boolean open(RetryContext context, RetryCallback callback) { 43 | return true; 44 | } 45 | 46 | /** 47 | * Called after the final attempt (successful or not). Allow the listener to clean up 48 | * any resource it is holding before control returns to the retry caller. 49 | * @param context the current {@link RetryContext}. 50 | * @param callback the current {@link RetryCallback}. 51 | * @param throwable the last exception that was thrown by the callback. 52 | * @param the exception type 53 | * @param the return value 54 | */ 55 | default void close(RetryContext context, RetryCallback callback, 56 | Throwable throwable) { 57 | } 58 | 59 | /** 60 | * Called after a successful attempt; allow the listener to throw a new exception to 61 | * cause a retry (according to the retry policy), based on the result returned by the 62 | * {@link RetryCallback#doWithRetry(RetryContext)} 63 | * @param the return type. 64 | * @param context the current {@link RetryContext}. 65 | * @param callback the current {@link RetryCallback}. 66 | * @param result the result returned by the callback method. 67 | * @since 2.0 68 | */ 69 | default void onSuccess(RetryContext context, RetryCallback callback, T result) { 70 | } 71 | 72 | /** 73 | * Called after every unsuccessful attempt at a retry. 74 | * @param context the current {@link RetryContext}. 75 | * @param callback the current {@link RetryCallback}. 76 | * @param throwable the last exception that was thrown by the callback. 77 | * @param the return value 78 | * @param the exception to throw 79 | */ 80 | default void onError(RetryContext context, RetryCallback callback, 81 | Throwable throwable) { 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/RetryPolicy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 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 | * https://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 | 17 | package org.springframework.retry; 18 | 19 | import java.io.Serializable; 20 | 21 | /** 22 | * A {@link RetryPolicy} is responsible for allocating and managing resources needed by 23 | * {@link RetryOperations}. The {@link RetryPolicy} allows retry operations to be aware of 24 | * their context. Context can be internal to the retry framework, e.g. to support nested 25 | * retries. Context can also be external, and the {@link RetryPolicy} provides a uniform 26 | * API for a range of different platforms for the external context. 27 | * 28 | * @author Dave Syer 29 | * @author Emanuele Ivaldi 30 | * 31 | */ 32 | public interface RetryPolicy extends Serializable { 33 | 34 | /** 35 | * The value returned by {@link RetryPolicy#getMaxAttempts()} when the policy doesn't 36 | * provide a maximum number of attempts before failure 37 | */ 38 | int NO_MAXIMUM_ATTEMPTS_SET = -1; 39 | 40 | /** 41 | * @param context the current retry status 42 | * @return true if the operation can proceed 43 | */ 44 | boolean canRetry(RetryContext context); 45 | 46 | /** 47 | * Acquire resources needed for the retry operation. The callback is passed in so that 48 | * marker interfaces can be used and a manager can collaborate with the callback to 49 | * set up some state in the status token. 50 | * @param parent the parent context if we are in a nested retry. 51 | * @return a {@link RetryContext} object specific to this policy. 52 | * 53 | */ 54 | RetryContext open(RetryContext parent); 55 | 56 | /** 57 | * @param context a retry status created by the {@link #open(RetryContext)} method of 58 | * this policy. 59 | */ 60 | void close(RetryContext context); 61 | 62 | /** 63 | * Called once per retry attempt, after the callback fails. 64 | * @param context the current status object. 65 | * @param throwable the exception to throw 66 | */ 67 | void registerThrowable(RetryContext context, Throwable throwable); 68 | 69 | /** 70 | * Called to understand if the policy has a fixed number of maximum attempts before 71 | * failure 72 | * @return -1 if the policy doesn't provide a fixed number of maximum attempts before 73 | * failure, the number of maximum attempts before failure otherwise 74 | */ 75 | default int getMaxAttempts() { 76 | return NO_MAXIMUM_ATTEMPTS_SET; 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/RetryState.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 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 | * https://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 org.springframework.retry; 17 | 18 | /** 19 | * Stateful retry is characterised by having to recognise the items that are being 20 | * processed, so this interface is used primarily to provide a cache key in between failed 21 | * attempts. It also provides a hints to the {@link RetryOperations} for optimisations to 22 | * do with avoidable cache hits and switching to stateless retry if a rollback is not 23 | * needed. 24 | * 25 | * @author Dave Syer 26 | * 27 | */ 28 | public interface RetryState { 29 | 30 | /** 31 | * Key representing the state for a retry attempt. Stateful retry is characterised by 32 | * having to recognise the items that are being processed, so this value is used as a 33 | * cache key in between failed attempts. 34 | * @return the key that this state represents 35 | */ 36 | Object getKey(); 37 | 38 | /** 39 | * Indicate whether a cache lookup can be avoided. If the key is known ahead of the 40 | * retry attempt to be fresh (i.e. has never been seen before) then a cache lookup can 41 | * be avoided if this flag is true. 42 | * @return true if the state does not require an explicit check for the key 43 | */ 44 | boolean isForceRefresh(); 45 | 46 | /** 47 | * Check whether this exception requires a rollback. The default is always true, which 48 | * is conservative, so this method provides an optimisation for switching to stateless 49 | * retry if there is an exception for which rollback is unnecessary. Example usage 50 | * would be for a stateful retry to specify a validation exception as not for 51 | * rollback. 52 | * @param exception the exception that caused a retry attempt to fail 53 | * @return true if this exception should cause a rollback 54 | */ 55 | boolean rollbackFor(Throwable exception); 56 | 57 | } -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/RetryStatistics.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 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 | * https://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 | 17 | package org.springframework.retry; 18 | 19 | /** 20 | * Interface for statistics reporting of retry attempts. Counts the number of retry 21 | * attempts, successes, errors (including retries), and aborts. 22 | * 23 | * @author Dave Syer 24 | * 25 | */ 26 | public interface RetryStatistics { 27 | 28 | /** 29 | * @return the number of completed successful retry attempts. 30 | */ 31 | int getCompleteCount(); 32 | 33 | /** 34 | * Get the number of times a retry block has been entered, irrespective of how many 35 | * times the operation was retried. 36 | * @return the number of retry blocks started. 37 | */ 38 | int getStartedCount(); 39 | 40 | /** 41 | * Get the number of errors detected, whether or not they resulted in a retry. 42 | * @return the number of errors detected. 43 | */ 44 | int getErrorCount(); 45 | 46 | /** 47 | * Get the number of times a block failed to complete successfully, even after retry. 48 | * @return the number of retry attempts that failed overall. 49 | */ 50 | int getAbortCount(); 51 | 52 | /** 53 | * Get the number of times a recovery callback was applied. 54 | * @return the number of recovered attempts. 55 | */ 56 | int getRecoveryCount(); 57 | 58 | /** 59 | * Get an identifier for the retry block for reporting purposes. 60 | * @return an identifier for the block. 61 | */ 62 | String getName(); 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/TerminatedRetryException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 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 | * https://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 | 17 | package org.springframework.retry; 18 | 19 | @SuppressWarnings("serial") 20 | public class TerminatedRetryException extends RetryException { 21 | 22 | public TerminatedRetryException(String msg, Throwable cause) { 23 | super(msg, cause); 24 | } 25 | 26 | public TerminatedRetryException(String msg) { 27 | super(msg); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/annotation/EnableRetry.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2023 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 | * https://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 | 17 | package org.springframework.retry.annotation; 18 | 19 | import java.lang.annotation.Documented; 20 | import java.lang.annotation.ElementType; 21 | import java.lang.annotation.Retention; 22 | import java.lang.annotation.RetentionPolicy; 23 | import java.lang.annotation.Target; 24 | 25 | import org.springframework.context.annotation.EnableAspectJAutoProxy; 26 | import org.springframework.context.annotation.Import; 27 | import org.springframework.core.Ordered; 28 | import org.springframework.core.annotation.AliasFor; 29 | 30 | /** 31 | * Global enabler for @Retryable annotations in Spring beans. If this is 32 | * declared on any @Configuration in the context then beans that have 33 | * retryable methods will be proxied and the retry handled according to the metadata in 34 | * the annotations. 35 | * 36 | * @author Dave Syer 37 | * @author Yanming Zhou 38 | * @author Ruslan Stelmachenko 39 | * @since 1.1 40 | * 41 | */ 42 | @Target(ElementType.TYPE) 43 | @Retention(RetentionPolicy.RUNTIME) 44 | @EnableAspectJAutoProxy(proxyTargetClass = false) 45 | @Import(RetryConfiguration.class) 46 | @Documented 47 | public @interface EnableRetry { 48 | 49 | /** 50 | * Indicate whether subclass-based (CGLIB) proxies are to be created as opposed to 51 | * standard Java interface-based proxies. The default is {@code false}. 52 | * @return whether to proxy or not to proxy the class 53 | */ 54 | @AliasFor(annotation = EnableAspectJAutoProxy.class) 55 | boolean proxyTargetClass() default false; 56 | 57 | /** 58 | * Indicate the order in which the {@link RetryConfiguration} AOP advice should 59 | * be applied. 60 | *

61 | * The default is {@code Ordered.LOWEST_PRECEDENCE - 1} in order to make sure the 62 | * advice is applied before other advices with {@link Ordered#LOWEST_PRECEDENCE} order 63 | * (e.g. an advice responsible for {@code @Transactional} behavior). 64 | */ 65 | int order() default Ordered.LOWEST_PRECEDENCE - 1; 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/annotation/Recover.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2024 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 | * https://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 | 17 | package org.springframework.retry.annotation; 18 | 19 | import java.lang.annotation.Documented; 20 | import java.lang.annotation.ElementType; 21 | import java.lang.annotation.Retention; 22 | import java.lang.annotation.RetentionPolicy; 23 | import java.lang.annotation.Target; 24 | 25 | /** 26 | * Annotation for a method invocation that is a recovery handler. A suitable recovery 27 | * handler has a first parameter of type Throwable (or a subtype of Throwable) and a 28 | * return value of the same type as the @Retryable method to recover from. 29 | * The Throwable first argument is optional (but a method without it will only be called 30 | * if no others match). Subsequent arguments are populated from the argument list of the 31 | * failed method in order. 32 | * 33 | * @author Dave Syer 34 | * @since 2.0 35 | * 36 | */ 37 | @Target({ ElementType.METHOD, ElementType.TYPE }) 38 | @Retention(RetentionPolicy.RUNTIME) 39 | @Documented 40 | public @interface Recover { 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/backoff/BackOffContext.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 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 | * https://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 | 17 | package org.springframework.retry.backoff; 18 | 19 | import java.io.Serializable; 20 | 21 | /** 22 | * @author Rob Harrop 23 | * @since 2.1 24 | */ 25 | public interface BackOffContext extends Serializable { 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/backoff/BackOffInterruptedException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2025 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 | * https://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 | 17 | package org.springframework.retry.backoff; 18 | 19 | import org.springframework.retry.RetryException; 20 | 21 | /** 22 | * Exception class signifying that an attempt to back off using a {@link BackOffPolicy} 23 | * was interrupted, most likely by an {@link InterruptedException} during a call to 24 | * {@link Thread#sleep(long)}. 25 | * 26 | * @author Rob Harrop 27 | * @since 2.1 28 | */ 29 | @SuppressWarnings("serial") 30 | public class BackOffInterruptedException extends RetryException { 31 | 32 | public BackOffInterruptedException(String msg) { 33 | super(msg); 34 | } 35 | 36 | public BackOffInterruptedException(String msg, Throwable cause) { 37 | super(msg, cause); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/backoff/BackOffPolicy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 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 | * https://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 | 17 | package org.springframework.retry.backoff; 18 | 19 | import org.springframework.retry.RetryContext; 20 | 21 | /** 22 | * Strategy interface to control back off between attempts in a single 23 | * {@link org.springframework.retry.support.RetryTemplate retry operation}. 24 | * 25 | * Implementations are expected to be thread-safe and should be designed for concurrent 26 | * access. Configuration for each implementation is also expected to be thread-safe but 27 | * need not be suitable for high load concurrent access. 28 | * 29 | * For each block of retry operations the {@link #start} method is called and 30 | * implementations can return an implementation-specific 31 | * 32 | * {@link BackOffContext} that can be used to track state through subsequent back off 33 | * invocations. Each back off process is handled via a call to {@link #backOff}. 34 | * 35 | * The {@link org.springframework.retry.support.RetryTemplate} will pass in the 36 | * corresponding {@link BackOffContext} object created by the call to {@link #start}. 37 | * 38 | * @author Rob Harrop 39 | * @author Dave Syer 40 | */ 41 | public interface BackOffPolicy { 42 | 43 | /** 44 | * Start a new block of back off operations. Implementations can choose to pause when 45 | * this method is called, but normally it returns immediately. 46 | * @param context the {@link RetryContext} context, which might contain information 47 | * that we can use to decide how to proceed. 48 | * @return the implementation-specific {@link BackOffContext} or 'null'. 49 | */ 50 | BackOffContext start(RetryContext context); 51 | 52 | /** 53 | * Back off/pause in an implementation-specific fashion. The passed in 54 | * {@link BackOffContext} corresponds to the one created by the call to {@link #start} 55 | * for a given retry operation set. 56 | * @param backOffContext the {@link BackOffContext} 57 | * @throws BackOffInterruptedException if the attempt at back off is interrupted. 58 | */ 59 | void backOff(BackOffContext backOffContext) throws BackOffInterruptedException; 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/backoff/ExponentialRandomBackOffPolicy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-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 | * https://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 | 17 | package org.springframework.retry.backoff; 18 | 19 | import java.util.Random; 20 | import java.util.function.Supplier; 21 | 22 | import org.springframework.retry.RetryContext; 23 | 24 | /** 25 | * Implementation of {@link org.springframework.retry.backoff.ExponentialBackOffPolicy} 26 | * that chooses a random multiple of the interval that would come from a simple 27 | * deterministic exponential. The random multiple is uniformly distributed between 1 and 28 | * the deterministic multiplier (so in practice the interval is somewhere between the next 29 | * and next but one intervals in the deterministic case). This is often referred to as 30 | * jitter. 31 | * 32 | * This has shown to at least be useful in testing scenarios where excessive contention is 33 | * generated by the test needing many retries. In test, usually threads are started at the 34 | * same time, and thus stomp together onto the next interval. Using this 35 | * {@link BackOffPolicy} can help avoid that scenario. 36 | * 37 | * Example: initialInterval = 50 multiplier = 2.0 maxInterval = 3000 numRetries = 5 38 | * 39 | * {@link ExponentialBackOffPolicy} yields: [50, 100, 200, 400, 800] 40 | * 41 | * {@link ExponentialRandomBackOffPolicy} may yield [76, 151, 304, 580, 901] or [53, 190, 42 | * 267, 451, 815] (random distributed values within the ranges of [50-100, 100-200, 43 | * 200-400, 400-800, 800-1600]) 44 | * 45 | * @author Jon Travis 46 | * @author Dave Syer 47 | * @author Chase Diem 48 | */ 49 | @SuppressWarnings("serial") 50 | public class ExponentialRandomBackOffPolicy extends ExponentialBackOffPolicy { 51 | 52 | /** 53 | * Returns a new instance of {@link org.springframework.retry.backoff.BackOffContext}, 54 | * seeded with this policies settings. 55 | */ 56 | public BackOffContext start(RetryContext context) { 57 | return new ExponentialRandomBackOffContext(getInitialInterval(), getMultiplier(), getMaxInterval(), 58 | getInitialIntervalSupplier(), getMultiplierSupplier(), getMaxIntervalSupplier()); 59 | } 60 | 61 | protected ExponentialBackOffPolicy newInstance() { 62 | return new ExponentialRandomBackOffPolicy(); 63 | } 64 | 65 | static class ExponentialRandomBackOffContext extends ExponentialBackOffPolicy.ExponentialBackOffContext { 66 | 67 | private final Random r = new Random(); 68 | 69 | public ExponentialRandomBackOffContext(long expSeed, double multiplier, long maxInterval, 70 | Supplier expSeedSupplier, Supplier multiplierSupplier, 71 | Supplier maxIntervalSupplier) { 72 | 73 | super(expSeed, multiplier, maxInterval, expSeedSupplier, multiplierSupplier, maxIntervalSupplier); 74 | } 75 | 76 | @Override 77 | public synchronized long getSleepAndIncrement() { 78 | long next = super.getSleepAndIncrement(); 79 | next = (long) (next * (1 + r.nextFloat() * (getMultiplier() - 1))); 80 | if (next > super.getMaxInterval()) { 81 | next = super.getMaxInterval(); 82 | } 83 | return next; 84 | } 85 | 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/backoff/NoBackOffPolicy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 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 | * https://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 | 17 | package org.springframework.retry.backoff; 18 | 19 | /** 20 | * Implementation of {@link BackOffPolicy} that performs a no-op and as such all retry 21 | * operation in a given set proceed one after the other with no pause. 22 | * 23 | * @author Rob Harrop 24 | * @since 2.1 25 | */ 26 | public class NoBackOffPolicy extends StatelessBackOffPolicy { 27 | 28 | protected void doBackOff() throws BackOffInterruptedException { 29 | } 30 | 31 | @Override 32 | public String toString() { 33 | return "NoBackOffPolicy []"; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/backoff/ObjectWaitSleeper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 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 | * https://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 org.springframework.retry.backoff; 17 | 18 | /** 19 | * Simple {@link Sleeper} implementation that just waits on a local Object. 20 | * 21 | * @deprecated in favor of {@link org.springframework.retry.backoff.ThreadWaitSleeper} 22 | * @author Dave Syer 23 | * 24 | */ 25 | @SuppressWarnings("serial") 26 | @Deprecated 27 | public class ObjectWaitSleeper implements Sleeper { 28 | 29 | /* 30 | * (non-Javadoc) 31 | * 32 | * @see org.springframework.batch.retry.backoff.Sleeper#sleep(long) 33 | */ 34 | public void sleep(long backOffPeriod) throws InterruptedException { 35 | Object mutex = new Object(); 36 | synchronized (mutex) { 37 | mutex.wait(backOffPeriod); 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/backoff/Sleeper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 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 | * https://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 org.springframework.retry.backoff; 17 | 18 | import java.io.Serializable; 19 | 20 | /** 21 | * Strategy interface for backoff policies to delegate the pausing of execution. 22 | * 23 | * @author Dave Syer 24 | * 25 | */ 26 | public interface Sleeper extends Serializable { 27 | 28 | /** 29 | * Pause for the specified period using whatever means available. 30 | * @param backOffPeriod the backoff period 31 | * @throws InterruptedException the exception when interrupted 32 | */ 33 | void sleep(long backOffPeriod) throws InterruptedException; 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/backoff/SleepingBackOffPolicy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 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 | * https://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 | 17 | package org.springframework.retry.backoff; 18 | 19 | /** 20 | * A interface which can be mixed in by {@link BackOffPolicy}s indicating that they sleep 21 | * when backing off. 22 | * 23 | * @param the type of the policy itself 24 | */ 25 | public interface SleepingBackOffPolicy> extends BackOffPolicy { 26 | 27 | /** 28 | * Clone the policy and return a new policy which uses the passed sleeper. 29 | * @param sleeper Target to be invoked any time the backoff policy sleeps 30 | * @return a clone of this policy which will have all of its backoff sleeps routed 31 | * into the passed sleeper 32 | */ 33 | T withSleeper(Sleeper sleeper); 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/backoff/StatelessBackOffPolicy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 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 | * https://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 | 17 | package org.springframework.retry.backoff; 18 | 19 | import org.springframework.retry.RetryContext; 20 | 21 | /** 22 | * Simple base class for {@link BackOffPolicy} implementations that maintain no state 23 | * across invocations. 24 | * 25 | * @author Rob Harrop 26 | * @author Dave Syer 27 | */ 28 | public abstract class StatelessBackOffPolicy implements BackOffPolicy { 29 | 30 | /** 31 | * Delegates directly to the {@link #doBackOff()} method without passing on the 32 | * {@link BackOffContext} argument which is not needed for stateless implementations. 33 | */ 34 | @Override 35 | public final void backOff(BackOffContext backOffContext) throws BackOffInterruptedException { 36 | doBackOff(); 37 | } 38 | 39 | /** 40 | * Returns 'null'. Subclasses can add behaviour, e.g. initial sleep 41 | * before first attempt. 42 | */ 43 | @Override 44 | public BackOffContext start(RetryContext status) { 45 | return null; 46 | } 47 | 48 | /** 49 | * Sub-classes should implement this method to perform the actual back off. 50 | * @throws BackOffInterruptedException if the backoff is interrupted 51 | */ 52 | protected abstract void doBackOff() throws BackOffInterruptedException; 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/backoff/ThreadWaitSleeper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 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 | * https://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 | 17 | package org.springframework.retry.backoff; 18 | 19 | /** 20 | * Simple {@link Sleeper} implementation that just blocks the current Thread with sleep 21 | * period. 22 | * 23 | * @author Artem Bilan 24 | * @since 1.1 25 | */ 26 | @SuppressWarnings("serial") 27 | public class ThreadWaitSleeper implements Sleeper { 28 | 29 | @Override 30 | public void sleep(long backOffPeriod) throws InterruptedException { 31 | Thread.sleep(backOffPeriod); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/backoff/package.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

4 | Infrastructure implementations of retry backoff concerns. 5 |

6 | 7 | 8 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/context/RetryContextSupport.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2014 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 | * https://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 | 17 | package org.springframework.retry.context; 18 | 19 | import org.springframework.core.AttributeAccessorSupport; 20 | import org.springframework.retry.RetryContext; 21 | import org.springframework.retry.RetryPolicy; 22 | 23 | /** 24 | * @author Dave Syer 25 | */ 26 | @SuppressWarnings("serial") 27 | public class RetryContextSupport extends AttributeAccessorSupport implements RetryContext { 28 | 29 | private final RetryContext parent; 30 | 31 | private volatile boolean terminate = false; 32 | 33 | private volatile int count; 34 | 35 | private volatile Throwable lastException; 36 | 37 | public RetryContextSupport(RetryContext parent) { 38 | super(); 39 | this.parent = parent; 40 | } 41 | 42 | public RetryContext getParent() { 43 | return this.parent; 44 | } 45 | 46 | public boolean isExhaustedOnly() { 47 | return terminate; 48 | } 49 | 50 | public void setExhaustedOnly() { 51 | terminate = true; 52 | } 53 | 54 | public int getRetryCount() { 55 | return count; 56 | } 57 | 58 | public Throwable getLastThrowable() { 59 | return lastException; 60 | } 61 | 62 | /** 63 | * Set the exception for the public interface {@link RetryContext}, and also increment 64 | * the retry count if the throwable is non-null. 65 | * 66 | * All {@link RetryPolicy} implementations should use this method when they register 67 | * the throwable. It should only be called once per retry attempt because it 68 | * increments a counter. 69 | * 70 | * Use of this method is not enforced by the framework - it is a service provider 71 | * contract for authors of policies. 72 | * @param throwable the exception that caused the current retry attempt to fail. 73 | */ 74 | public void registerThrowable(Throwable throwable) { 75 | this.lastException = throwable; 76 | if (throwable != null) 77 | count++; 78 | } 79 | 80 | @Override 81 | public String toString() { 82 | return String.format("[RetryContext: count=%d, lastException=%s, exhausted=%b]", count, lastException, 83 | terminate); 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/context/package.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

4 | Infrastructure implementations of retry context concerns. 5 |

6 | 7 | 8 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/interceptor/FixedKeyGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-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 | * https://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 | 17 | package org.springframework.retry.interceptor; 18 | 19 | /** 20 | * @author Dave Syer 21 | * 22 | */ 23 | public class FixedKeyGenerator implements MethodArgumentsKeyGenerator { 24 | 25 | private final String label; 26 | 27 | public FixedKeyGenerator(String label) { 28 | this.label = label; 29 | } 30 | 31 | @Override 32 | public Object getKey(Object[] item) { 33 | return this.label; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/interceptor/MethodArgumentsKeyGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 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 | * https://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 org.springframework.retry.interceptor; 17 | 18 | /** 19 | * Interface that allows method parameters to be identified and tagged by a unique key. 20 | * 21 | * @author Dave Syer 22 | * 23 | */ 24 | public interface MethodArgumentsKeyGenerator { 25 | 26 | /** 27 | * Get a unique identifier for the item that can be used to cache it between calls if 28 | * necessary, and then identify it later. 29 | * @param item the current method arguments (may be null if there are none). 30 | * @return a unique identifier. 31 | */ 32 | Object getKey(Object[] item); 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/interceptor/MethodInvocationRecoverer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 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 | * https://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 | 17 | package org.springframework.retry.interceptor; 18 | 19 | /** 20 | * Strategy interface for recovery action when processing of an item fails. 21 | * 22 | * @author Dave Syer 23 | * @param the return type 24 | */ 25 | public interface MethodInvocationRecoverer { 26 | 27 | /** 28 | * Recover gracefully from an error. Clients can call this if processing of the item 29 | * throws an unexpected exception. Caller can use the return value to decide whether 30 | * to try more corrective action or perhaps throw an exception. 31 | * @param args the arguments for the method invocation that failed. 32 | * @param cause the cause of the failure that led to this recovery. 33 | * @return the value to be returned to the caller 34 | */ 35 | T recover(Object[] args, Throwable cause); 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/interceptor/MethodInvocationRetryCallback.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2024 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 | * https://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 | 17 | package org.springframework.retry.interceptor; 18 | 19 | import org.aopalliance.intercept.MethodInvocation; 20 | 21 | import org.springframework.lang.Nullable; 22 | import org.springframework.retry.RetryCallback; 23 | import org.springframework.retry.RetryOperations; 24 | import org.springframework.util.ClassUtils; 25 | import org.springframework.util.StringUtils; 26 | 27 | /** 28 | * Callback class for a Spring AOP reflective `MethodInvocation` that can be retried using 29 | * a {@link RetryOperations}. 30 | *

31 | * In a concrete {@link org.springframework.retry.RetryListener} implementation, the 32 | * `MethodInvocation` can be analysed for providing insights on the method called as well 33 | * as its parameter values which could then be used for monitoring purposes. 34 | * 35 | * @param the type of object returned by the callback 36 | * @param the type of exception it declares may be thrown 37 | * @see StatefulRetryOperationsInterceptor 38 | * @see RetryOperationsInterceptor 39 | * @see org.springframework.retry.listener.MethodInvocationRetryListenerSupport 40 | * @author Marius Grama 41 | * @author Artem Bilan 42 | * @since 1.3 43 | */ 44 | public abstract class MethodInvocationRetryCallback implements RetryCallback { 45 | 46 | protected final MethodInvocation invocation; 47 | 48 | protected final String label; 49 | 50 | /** 51 | * Constructor for the class. 52 | * @param invocation the method invocation 53 | * @param label a unique label for statistics reporting. 54 | */ 55 | public MethodInvocationRetryCallback(MethodInvocation invocation, @Nullable String label) { 56 | this.invocation = invocation; 57 | if (StringUtils.hasText(label)) { 58 | this.label = label; 59 | } 60 | else { 61 | this.label = ClassUtils.getQualifiedMethodName(invocation.getMethod()); 62 | } 63 | } 64 | 65 | public MethodInvocation getInvocation() { 66 | return this.invocation; 67 | } 68 | 69 | @Override 70 | public String getLabel() { 71 | return this.label; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/interceptor/NewMethodArgumentsIdentifier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 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 | * https://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 | 17 | package org.springframework.retry.interceptor; 18 | 19 | /** 20 | * Strategy interface to distinguish new arguments from ones that have been processed 21 | * before, e.g. by examining a message flag. 22 | * 23 | * @author Dave Syer 24 | * 25 | */ 26 | public interface NewMethodArgumentsIdentifier { 27 | 28 | /** 29 | * Inspect the arguments and determine if they have never been processed before. The 30 | * safest choice when the answer is indeterminate is 'false'. 31 | * @param args the current method arguments. 32 | * @return true if the item is known to have never been processed before. 33 | */ 34 | boolean isNew(Object[] args); 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/interceptor/Retryable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 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 | * https://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 | 17 | package org.springframework.retry.interceptor; 18 | 19 | /** 20 | * Marker interface for proxies that are providing retryable behaviour. Can be added by 21 | * proxy creators that use the {@link RetryOperationsInterceptor} and 22 | * {@link StatefulRetryOperationsInterceptor}. 23 | * 24 | * @author Dave Syer 25 | * @since 1.1 26 | */ 27 | public interface Retryable { 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/interceptor/package.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

4 | Infrastructure implementations of retry aop concerns. 5 |

6 | 7 | 8 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/listener/RetryListenerSupport.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-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 | * https://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 | 17 | package org.springframework.retry.listener; 18 | 19 | import org.springframework.retry.RetryCallback; 20 | import org.springframework.retry.RetryContext; 21 | import org.springframework.retry.RetryListener; 22 | 23 | /** 24 | * Empty method implementation of {@link RetryListener}. 25 | * 26 | * @author Dave Syer 27 | * @author Henning Pöttker 28 | * @deprecated in favor of the default implementations in {@link RetryListener} 29 | */ 30 | @Deprecated(since = "2.0.1", forRemoval = true) 31 | public class RetryListenerSupport implements RetryListener { 32 | 33 | public void close(RetryContext context, RetryCallback callback, 34 | Throwable throwable) { 35 | } 36 | 37 | public void onError(RetryContext context, RetryCallback callback, 38 | Throwable throwable) { 39 | } 40 | 41 | public boolean open(RetryContext context, RetryCallback callback) { 42 | return true; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/listener/package.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

4 | Infrastructure implementations of retry interceptor concerns. 5 |

6 | 7 | 8 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/package.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

4 | Infrastructure implementations of retry concerns. 5 |

6 | 7 | 8 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/policy/AlwaysRetryPolicy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 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 | * https://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 | 17 | package org.springframework.retry.policy; 18 | 19 | import org.springframework.retry.RetryContext; 20 | import org.springframework.retry.RetryPolicy; 21 | 22 | /** 23 | * A {@link RetryPolicy} that always permits a retry. Can also be used as a base class for 24 | * other policies, e.g. for test purposes as a stub. 25 | * 26 | * @author Dave Syer 27 | * 28 | */ 29 | @SuppressWarnings("serial") 30 | public class AlwaysRetryPolicy extends NeverRetryPolicy { 31 | 32 | /** 33 | * Always returns true. 34 | * 35 | * @see org.springframework.retry.RetryPolicy#canRetry(org.springframework.retry.RetryContext) 36 | */ 37 | public boolean canRetry(RetryContext context) { 38 | return true; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/policy/BinaryExceptionClassifierRetryPolicy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-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 | * https://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 | 17 | package org.springframework.retry.policy; 18 | 19 | import org.springframework.classify.BinaryExceptionClassifier; 20 | import org.springframework.retry.RetryContext; 21 | import org.springframework.retry.RetryPolicy; 22 | import org.springframework.retry.context.RetryContextSupport; 23 | 24 | /** 25 | * A policy, that is based on {@link BinaryExceptionClassifier}. Usually, binary 26 | * classification is enough for retry purposes. If you need more flexible classification, 27 | * use {@link ExceptionClassifierRetryPolicy}. 28 | * 29 | * @author Aleksandr Shamukov 30 | */ 31 | @SuppressWarnings("serial") 32 | public class BinaryExceptionClassifierRetryPolicy implements RetryPolicy { 33 | 34 | private final BinaryExceptionClassifier exceptionClassifier; 35 | 36 | public BinaryExceptionClassifierRetryPolicy(BinaryExceptionClassifier exceptionClassifier) { 37 | this.exceptionClassifier = exceptionClassifier; 38 | } 39 | 40 | public BinaryExceptionClassifier getExceptionClassifier() { 41 | return exceptionClassifier; 42 | } 43 | 44 | @Override 45 | public boolean canRetry(RetryContext context) { 46 | Throwable t = context.getLastThrowable(); 47 | return t == null || exceptionClassifier.classify(t); 48 | } 49 | 50 | @Override 51 | public void close(RetryContext status) { 52 | } 53 | 54 | @Override 55 | public void registerThrowable(RetryContext context, Throwable throwable) { 56 | RetryContextSupport simpleContext = ((RetryContextSupport) context); 57 | simpleContext.registerThrowable(throwable); 58 | } 59 | 60 | @Override 61 | public RetryContext open(RetryContext parent) { 62 | return new RetryContextSupport(parent); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/policy/MapRetryContextCache.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-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 | * https://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 org.springframework.retry.policy; 17 | 18 | import java.util.Collections; 19 | import java.util.HashMap; 20 | import java.util.Map; 21 | 22 | import org.springframework.retry.RetryContext; 23 | 24 | /** 25 | * Map-based implementation of {@link RetryContextCache}. The map backing the cache of 26 | * contexts is synchronized. 27 | * 28 | * @author Dave Syer 29 | */ 30 | public class MapRetryContextCache implements RetryContextCache { 31 | 32 | /** 33 | * Default value for maximum capacity of the cache. This is set to a reasonably low 34 | * value (4096) to avoid users inadvertently filling the cache with item keys that are 35 | * inconsistent. 36 | */ 37 | public static final int DEFAULT_CAPACITY = 4096; 38 | 39 | private final Map map = Collections.synchronizedMap(new HashMap<>()); 40 | 41 | private int capacity; 42 | 43 | /** 44 | * Create a {@link MapRetryContextCache} with default capacity. 45 | */ 46 | public MapRetryContextCache() { 47 | this(DEFAULT_CAPACITY); 48 | } 49 | 50 | /** 51 | * @param defaultCapacity the default capacity 52 | */ 53 | public MapRetryContextCache(int defaultCapacity) { 54 | super(); 55 | this.capacity = defaultCapacity; 56 | } 57 | 58 | /** 59 | * Public setter for the capacity. Prevents the cache from growing unboundedly if 60 | * items that fail are misidentified and two references to an identical item actually 61 | * do not have the same key. This can happen when users implement equals and hashCode 62 | * based on mutable fields, for instance. 63 | * @param capacity the capacity to set 64 | */ 65 | public void setCapacity(int capacity) { 66 | this.capacity = capacity; 67 | } 68 | 69 | public boolean containsKey(Object key) { 70 | return map.containsKey(key); 71 | } 72 | 73 | public RetryContext get(Object key) { 74 | return map.get(key); 75 | } 76 | 77 | public void put(Object key, RetryContext context) { 78 | if (map.size() >= capacity) { 79 | throw new RetryCacheCapacityExceededException("Retry cache capacity limit breached. " 80 | + "Do you need to re-consider the implementation of the key generator, " 81 | + "or the equals and hashCode of the items that failed?"); 82 | } 83 | map.put(key, context); 84 | } 85 | 86 | public void remove(Object key) { 87 | map.remove(key); 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/policy/NeverRetryPolicy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 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 | * https://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 | 17 | package org.springframework.retry.policy; 18 | 19 | import org.springframework.retry.RetryContext; 20 | import org.springframework.retry.RetryPolicy; 21 | import org.springframework.retry.context.RetryContextSupport; 22 | 23 | /** 24 | * A {@link RetryPolicy} that allows the first attempt but never permits a retry. Also be 25 | * used as a base class for other policies, e.g. for test purposes as a stub. 26 | * 27 | * @author Dave Syer 28 | * 29 | */ 30 | @SuppressWarnings("serial") 31 | public class NeverRetryPolicy implements RetryPolicy { 32 | 33 | /** 34 | * Returns false after the first exception. So there is always one try, and then the 35 | * retry is prevented. 36 | * 37 | * @see org.springframework.retry.RetryPolicy#canRetry(org.springframework.retry.RetryContext) 38 | */ 39 | public boolean canRetry(RetryContext context) { 40 | return !((NeverRetryContext) context).isFinished(); 41 | } 42 | 43 | /** 44 | * Do nothing. 45 | * 46 | * @see org.springframework.retry.RetryPolicy#close(org.springframework.retry.RetryContext) 47 | */ 48 | public void close(RetryContext context) { 49 | // no-op 50 | } 51 | 52 | /** 53 | * Return a context that can respond to early termination requests, but does nothing 54 | * else. 55 | * 56 | * @see org.springframework.retry.RetryPolicy#open(RetryContext) 57 | */ 58 | public RetryContext open(RetryContext parent) { 59 | return new NeverRetryContext(parent); 60 | } 61 | 62 | /** 63 | * Make the throwable available for downstream use through the context. 64 | * @see org.springframework.retry.RetryPolicy#registerThrowable(org.springframework.retry.RetryContext, 65 | * Throwable) 66 | */ 67 | public void registerThrowable(RetryContext context, Throwable throwable) { 68 | ((NeverRetryContext) context).setFinished(); 69 | ((RetryContextSupport) context).registerThrowable(throwable); 70 | } 71 | 72 | /** 73 | * Special context object for {@link NeverRetryPolicy}. Implements a flag with a 74 | * similar function to {@link RetryContext#isExhaustedOnly()}, but kept separate so 75 | * that if subclasses of {@link NeverRetryPolicy} need to they can modify the 76 | * behaviour of {@link NeverRetryPolicy#canRetry(RetryContext)} without affecting 77 | * {@link RetryContext#isExhaustedOnly()}. 78 | * 79 | * @author Dave Syer 80 | * 81 | */ 82 | private static class NeverRetryContext extends RetryContextSupport { 83 | 84 | private boolean finished = false; 85 | 86 | public NeverRetryContext(RetryContext parent) { 87 | super(parent); 88 | } 89 | 90 | public boolean isFinished() { 91 | return finished; 92 | } 93 | 94 | public void setFinished() { 95 | this.finished = true; 96 | } 97 | 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/policy/PredicateRetryPolicy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024-2024 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 | * https://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 | 17 | package org.springframework.retry.policy; 18 | 19 | import java.util.function.Predicate; 20 | 21 | import org.springframework.retry.RetryContext; 22 | import org.springframework.retry.RetryPolicy; 23 | import org.springframework.retry.context.RetryContextSupport; 24 | import org.springframework.util.Assert; 25 | 26 | /** 27 | * A policy, that is based on {@link Predicate}. Usually, binary classification 28 | * is enough for retry purposes. If you need more flexible classification, use 29 | * {@link ExceptionClassifierRetryPolicy}. 30 | * 31 | * @author Morulai Planinski 32 | * @since 2.0.7 33 | */ 34 | public class PredicateRetryPolicy implements RetryPolicy { 35 | 36 | private final Predicate predicate; 37 | 38 | public PredicateRetryPolicy(Predicate predicate) { 39 | Assert.notNull(predicate, "'predicate' must not be null"); 40 | this.predicate = predicate; 41 | } 42 | 43 | @Override 44 | public boolean canRetry(RetryContext context) { 45 | Throwable t = context.getLastThrowable(); 46 | return t == null || predicate.test(t); 47 | } 48 | 49 | @Override 50 | public void close(RetryContext status) { 51 | } 52 | 53 | @Override 54 | public void registerThrowable(RetryContext context, Throwable throwable) { 55 | ((RetryContextSupport) context).registerThrowable(throwable); 56 | } 57 | 58 | @Override 59 | public RetryContext open(RetryContext parent) { 60 | return new RetryContextSupport(parent); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/policy/RetryCacheCapacityExceededException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 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 | * https://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 org.springframework.retry.policy; 17 | 18 | import org.springframework.retry.RetryException; 19 | 20 | /** 21 | * Exception that indicates that a cache limit was exceeded. This is often a sign of badly 22 | * or inconsistently implemented hashCode, equals in failed items. Items can then fail 23 | * repeatedly and appear different to the cache, so they get added over and over again 24 | * until a limit is reached and this exception is thrown. Consult the documentation of the 25 | * {@link RetryContextCache} in use to determine how to increase the limit if appropriate. 26 | * 27 | * @author Dave Syer 28 | * 29 | */ 30 | @SuppressWarnings("serial") 31 | public class RetryCacheCapacityExceededException extends RetryException { 32 | 33 | /** 34 | * Constructs a new instance with a message. 35 | * @param message the message sent when creating the exception 36 | */ 37 | public RetryCacheCapacityExceededException(String message) { 38 | super(message); 39 | } 40 | 41 | /** 42 | * Constructs a new instance with a message and nested exception. 43 | * @param msg the exception message. 44 | * @param nested the nested exception 45 | */ 46 | public RetryCacheCapacityExceededException(String msg, Throwable nested) { 47 | super(msg, nested); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/policy/RetryContextCache.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 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 | * https://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 | 17 | package org.springframework.retry.policy; 18 | 19 | import org.springframework.retry.RetryContext; 20 | 21 | /** 22 | * Simple map-like abstraction for stateful retry policies to use when storing and 23 | * retrieving {@link RetryContext} instances. A null key should never be passed in by the 24 | * caller, but if it is then implementations are free to discard the context instead of 25 | * saving it (null key means "no information"). 26 | * 27 | * @author Dave Syer 28 | * @see MapRetryContextCache 29 | * 30 | */ 31 | public interface RetryContextCache { 32 | 33 | RetryContext get(Object key); 34 | 35 | void put(Object key, RetryContext context) throws RetryCacheCapacityExceededException; 36 | 37 | void remove(Object key); 38 | 39 | boolean containsKey(Object key); 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/policy/SoftReferenceMapRetryContextCache.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-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 | * https://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 org.springframework.retry.policy; 17 | 18 | import java.lang.ref.SoftReference; 19 | import java.util.Collections; 20 | import java.util.HashMap; 21 | import java.util.Map; 22 | 23 | import org.springframework.retry.RetryContext; 24 | 25 | /** 26 | * Map-based implementation of {@link RetryContextCache}. The map backing the cache of 27 | * contexts is synchronized and its entries are soft-referenced, so may be garbage 28 | * collected under pressure. 29 | * 30 | * @see MapRetryContextCache for non-soft referenced version 31 | * @author Dave Syer 32 | */ 33 | public class SoftReferenceMapRetryContextCache implements RetryContextCache { 34 | 35 | /** 36 | * Default value for maximum capacity of the cache. This is set to a reasonably low 37 | * value (4096) to avoid users inadvertently filling the cache with item keys that are 38 | * inconsistent. 39 | */ 40 | public static final int DEFAULT_CAPACITY = 4096; 41 | 42 | private final Map> map = Collections.synchronizedMap(new HashMap<>()); 43 | 44 | private int capacity; 45 | 46 | /** 47 | * Create a {@link SoftReferenceMapRetryContextCache} with default capacity. 48 | */ 49 | public SoftReferenceMapRetryContextCache() { 50 | this(DEFAULT_CAPACITY); 51 | } 52 | 53 | /** 54 | * @param defaultCapacity the default capacity 55 | */ 56 | public SoftReferenceMapRetryContextCache(int defaultCapacity) { 57 | super(); 58 | this.capacity = defaultCapacity; 59 | } 60 | 61 | /** 62 | * Public setter for the capacity. Prevents the cache from growing unboundedly if 63 | * items that fail are misidentified and two references to an identical item actually 64 | * do not have the same key. This can happen when users implement equals and hashCode 65 | * based on mutable fields, for instance. 66 | * @param capacity the capacity to set 67 | */ 68 | public void setCapacity(int capacity) { 69 | this.capacity = capacity; 70 | } 71 | 72 | public boolean containsKey(Object key) { 73 | if (!map.containsKey(key)) { 74 | return false; 75 | } 76 | if (map.get(key).get() == null) { 77 | // our reference was garbage collected 78 | map.remove(key); 79 | } 80 | return map.containsKey(key); 81 | } 82 | 83 | public RetryContext get(Object key) { 84 | return map.get(key).get(); 85 | } 86 | 87 | public void put(Object key, RetryContext context) { 88 | if (map.size() >= capacity) { 89 | throw new RetryCacheCapacityExceededException("Retry cache capacity limit breached. " 90 | + "Do you need to re-consider the implementation of the key generator, " 91 | + "or the equals and hashCode of the items that failed?"); 92 | } 93 | map.put(key, new SoftReference<>(context)); 94 | } 95 | 96 | public void remove(Object key) { 97 | map.remove(key); 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/policy/TimeoutRetryPolicy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-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 | * https://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 | 17 | package org.springframework.retry.policy; 18 | 19 | import org.springframework.retry.RetryContext; 20 | import org.springframework.retry.RetryPolicy; 21 | import org.springframework.retry.context.RetryContextSupport; 22 | 23 | /** 24 | * A {@link RetryPolicy} that allows a retry only if it hasn't timed out. The clock is 25 | * started on a call to {@link #open(RetryContext)}. 26 | * 27 | * @author Dave Syer 28 | * 29 | */ 30 | @SuppressWarnings("serial") 31 | public class TimeoutRetryPolicy implements RetryPolicy { 32 | 33 | /** 34 | * Default value for timeout (milliseconds). 35 | */ 36 | public static final long DEFAULT_TIMEOUT = 1000; 37 | 38 | private long timeout; 39 | 40 | /** 41 | * Create a new instance with the timeout set to {@link #DEFAULT_TIMEOUT}. 42 | */ 43 | public TimeoutRetryPolicy() { 44 | this(DEFAULT_TIMEOUT); 45 | } 46 | 47 | /** 48 | * Create a new instance with a configurable timeout. 49 | * @param timeout timeout in milliseconds 50 | * @since 2.0.2 51 | */ 52 | public TimeoutRetryPolicy(long timeout) { 53 | this.timeout = timeout; 54 | } 55 | 56 | /** 57 | * Setter for timeout in milliseconds. Default is {@link #DEFAULT_TIMEOUT}. 58 | * @param timeout how long to wait until a timeout 59 | */ 60 | public void setTimeout(long timeout) { 61 | this.timeout = timeout; 62 | } 63 | 64 | /** 65 | * The value of the timeout. 66 | * @return the timeout in milliseconds 67 | */ 68 | public long getTimeout() { 69 | return timeout; 70 | } 71 | 72 | /** 73 | * Only permits a retry if the timeout has not expired. Does not check the exception 74 | * at all. 75 | * 76 | * @see org.springframework.retry.RetryPolicy#canRetry(org.springframework.retry.RetryContext) 77 | */ 78 | public boolean canRetry(RetryContext context) { 79 | return ((TimeoutRetryContext) context).isAlive(); 80 | } 81 | 82 | public void close(RetryContext context) { 83 | } 84 | 85 | public RetryContext open(RetryContext parent) { 86 | return new TimeoutRetryContext(parent, timeout); 87 | } 88 | 89 | public void registerThrowable(RetryContext context, Throwable throwable) { 90 | ((RetryContextSupport) context).registerThrowable(throwable); 91 | // otherwise no-op - we only time out, otherwise retry everything... 92 | } 93 | 94 | private static class TimeoutRetryContext extends RetryContextSupport { 95 | 96 | private final long timeout; 97 | 98 | private final long start; 99 | 100 | public TimeoutRetryContext(RetryContext parent, long timeout) { 101 | super(parent); 102 | this.start = System.currentTimeMillis(); 103 | this.timeout = timeout; 104 | } 105 | 106 | public boolean isAlive() { 107 | return (System.currentTimeMillis() - start) <= timeout; 108 | } 109 | 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/policy/package.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

4 | Infrastructure implementations of retry policy concerns. 5 |

6 | 7 | 8 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/stats/DefaultRetryStatistics.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-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 | * https://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 | 17 | package org.springframework.retry.stats; 18 | 19 | import java.util.concurrent.atomic.AtomicInteger; 20 | 21 | import org.springframework.core.AttributeAccessorSupport; 22 | import org.springframework.retry.RetryStatistics; 23 | 24 | /** 25 | * @author Dave Syer 26 | * 27 | */ 28 | @SuppressWarnings("serial") 29 | public class DefaultRetryStatistics extends AttributeAccessorSupport 30 | implements RetryStatistics, MutableRetryStatistics { 31 | 32 | private String name; 33 | 34 | private final AtomicInteger startedCount = new AtomicInteger(); 35 | 36 | private final AtomicInteger completeCount = new AtomicInteger(); 37 | 38 | private final AtomicInteger recoveryCount = new AtomicInteger(); 39 | 40 | private final AtomicInteger errorCount = new AtomicInteger(); 41 | 42 | private final AtomicInteger abortCount = new AtomicInteger(); 43 | 44 | DefaultRetryStatistics() { 45 | } 46 | 47 | public DefaultRetryStatistics(String name) { 48 | this.name = name; 49 | } 50 | 51 | @Override 52 | public int getCompleteCount() { 53 | return completeCount.get(); 54 | } 55 | 56 | @Override 57 | public int getStartedCount() { 58 | return startedCount.get(); 59 | } 60 | 61 | @Override 62 | public int getErrorCount() { 63 | return errorCount.get(); 64 | } 65 | 66 | @Override 67 | public int getAbortCount() { 68 | return abortCount.get(); 69 | } 70 | 71 | @Override 72 | public String getName() { 73 | return name; 74 | } 75 | 76 | @Override 77 | public int getRecoveryCount() { 78 | return recoveryCount.get(); 79 | } 80 | 81 | public void setName(String name) { 82 | this.name = name; 83 | } 84 | 85 | @Override 86 | public void incrementStartedCount() { 87 | this.startedCount.incrementAndGet(); 88 | } 89 | 90 | @Override 91 | public void incrementCompleteCount() { 92 | this.completeCount.incrementAndGet(); 93 | } 94 | 95 | @Override 96 | public void incrementRecoveryCount() { 97 | this.recoveryCount.incrementAndGet(); 98 | } 99 | 100 | @Override 101 | public void incrementErrorCount() { 102 | this.errorCount.incrementAndGet(); 103 | } 104 | 105 | @Override 106 | public void incrementAbortCount() { 107 | this.abortCount.incrementAndGet(); 108 | } 109 | 110 | @Override 111 | public String toString() { 112 | return "DefaultRetryStatistics [name=" + name + ", startedCount=" + startedCount + ", completeCount=" 113 | + completeCount + ", recoveryCount=" + recoveryCount + ", errorCount=" + errorCount + ", abortCount=" 114 | + abortCount + "]"; 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/stats/DefaultRetryStatisticsFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 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 | * https://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 | 17 | package org.springframework.retry.stats; 18 | 19 | /** 20 | * @author Dave Syer 21 | * 22 | */ 23 | public class DefaultRetryStatisticsFactory implements RetryStatisticsFactory { 24 | 25 | private long window = 15000; 26 | 27 | /** 28 | * Window in milliseconds for exponential decay factor in rolling averages. 29 | * @param window the window to set 30 | */ 31 | public void setWindow(long window) { 32 | this.window = window; 33 | } 34 | 35 | @Override 36 | public MutableRetryStatistics create(String name) { 37 | ExponentialAverageRetryStatistics stats = new ExponentialAverageRetryStatistics(name); 38 | stats.setWindow(window); 39 | return stats; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/stats/DefaultStatisticsRepository.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-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 | * https://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 | 17 | package org.springframework.retry.stats; 18 | 19 | import java.util.ArrayList; 20 | import java.util.concurrent.ConcurrentHashMap; 21 | import java.util.concurrent.ConcurrentMap; 22 | 23 | import org.springframework.retry.RetryStatistics; 24 | 25 | /** 26 | * @author Dave Syer 27 | * 28 | */ 29 | public class DefaultStatisticsRepository implements StatisticsRepository { 30 | 31 | private final ConcurrentMap map = new ConcurrentHashMap<>(); 32 | 33 | private RetryStatisticsFactory factory = new DefaultRetryStatisticsFactory(); 34 | 35 | public void setRetryStatisticsFactory(RetryStatisticsFactory factory) { 36 | this.factory = factory; 37 | } 38 | 39 | @Override 40 | public RetryStatistics findOne(String name) { 41 | return map.get(name); 42 | } 43 | 44 | @Override 45 | public Iterable findAll() { 46 | return new ArrayList<>(map.values()); 47 | } 48 | 49 | @Override 50 | public void addStarted(String name) { 51 | getStatistics(name).incrementStartedCount(); 52 | } 53 | 54 | @Override 55 | public void addError(String name) { 56 | getStatistics(name).incrementErrorCount(); 57 | } 58 | 59 | @Override 60 | public void addRecovery(String name) { 61 | getStatistics(name).incrementRecoveryCount(); 62 | } 63 | 64 | @Override 65 | public void addComplete(String name) { 66 | getStatistics(name).incrementCompleteCount(); 67 | } 68 | 69 | @Override 70 | public void addAbort(String name) { 71 | getStatistics(name).incrementAbortCount(); 72 | } 73 | 74 | private MutableRetryStatistics getStatistics(String name) { 75 | MutableRetryStatistics stats; 76 | if (!map.containsKey(name)) { 77 | map.putIfAbsent(name, factory.create(name)); 78 | } 79 | stats = map.get(name); 80 | return stats; 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/stats/MutableRetryStatistics.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 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 | * https://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 | 17 | package org.springframework.retry.stats; 18 | 19 | import org.springframework.core.AttributeAccessor; 20 | import org.springframework.retry.RetryStatistics; 21 | 22 | /** 23 | * @author Dave Syer 24 | * 25 | */ 26 | public interface MutableRetryStatistics extends RetryStatistics, AttributeAccessor { 27 | 28 | void incrementStartedCount(); 29 | 30 | void incrementCompleteCount(); 31 | 32 | void incrementRecoveryCount(); 33 | 34 | void incrementErrorCount(); 35 | 36 | void incrementAbortCount(); 37 | 38 | } -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/stats/RetryStatisticsFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 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 | * https://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 | 17 | package org.springframework.retry.stats; 18 | 19 | /** 20 | * @author Dave Syer 21 | * 22 | */ 23 | public interface RetryStatisticsFactory { 24 | 25 | MutableRetryStatistics create(String name); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/stats/StatisticsRepository.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 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 | * https://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 | 17 | package org.springframework.retry.stats; 18 | 19 | import org.springframework.retry.RetryStatistics; 20 | 21 | /** 22 | * @author Dave Syer 23 | * 24 | */ 25 | public interface StatisticsRepository { 26 | 27 | RetryStatistics findOne(String name); 28 | 29 | Iterable findAll(); 30 | 31 | void addStarted(String name); 32 | 33 | void addError(String name); 34 | 35 | void addRecovery(String name); 36 | 37 | void addComplete(String name); 38 | 39 | void addAbort(String name); 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/support/Args.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022-2025 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 | * https://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 | 17 | package org.springframework.retry.support; 18 | 19 | /** 20 | * A root object containing the method arguments to use in expression evaluation. 21 | * IMPORTANT; the arguments are not available (will contain nulls) until after the first 22 | * call to the retryable method; this is generally only an issue for the 23 | * {@code maxAttempts}, meaning the arguments cannot be used to indicate 24 | * {@code maxAttempts = 0}. 25 | * 26 | * @author Gary Russell 27 | * @since 2.0 28 | */ 29 | public class Args { 30 | 31 | /** 32 | * An empty {@link Args} with 100 null arguments. 33 | */ 34 | public static final Args NO_ARGS = new Args(new Object[100]); 35 | 36 | private final Object[] args; 37 | 38 | public Args(Object[] args) { 39 | this.args = args; 40 | } 41 | 42 | public Object[] getArgs() { 43 | return args; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/support/RetrySimulation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-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 | * https://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 | 17 | package org.springframework.retry.support; 18 | 19 | import java.util.ArrayList; 20 | import java.util.Collections; 21 | import java.util.List; 22 | 23 | /** 24 | * The results of a simulation. 25 | */ 26 | public class RetrySimulation { 27 | 28 | private final List sleepSequences = new ArrayList<>(); 29 | 30 | private final List sleepHistogram = new ArrayList<>(); 31 | 32 | public RetrySimulation() { 33 | } 34 | 35 | /** 36 | * Add a sequence of sleeps to the simulation. 37 | * @param sleeps the times to be created as a {@link SleepSequence} 38 | */ 39 | public void addSequence(List sleeps) { 40 | sleepHistogram.addAll(sleeps); 41 | sleepSequences.add(new SleepSequence(sleeps)); 42 | } 43 | 44 | /** 45 | * @return Returns a list of all the unique sleep values which were executed within 46 | * all simulations. 47 | */ 48 | public List getPercentiles() { 49 | List res = new ArrayList<>(); 50 | for (double percentile : new double[] { 10, 20, 30, 40, 50, 60, 70, 80, 90 }) { 51 | res.add(getPercentile(percentile / 100)); 52 | } 53 | return res; 54 | } 55 | 56 | public double getPercentile(double p) { 57 | Collections.sort(sleepHistogram); 58 | int size = sleepHistogram.size(); 59 | double pos = p * (size - 1); 60 | int i0 = (int) pos; 61 | int i1 = i0 + 1; 62 | double weight = pos - i0; 63 | return sleepHistogram.get(i0) * (1 - weight) + sleepHistogram.get(i1) * weight; 64 | 65 | } 66 | 67 | /** 68 | * @return the longest total time slept by a retry sequence. 69 | */ 70 | public SleepSequence getLongestTotalSleepSequence() { 71 | SleepSequence longest = null; 72 | for (SleepSequence sequence : sleepSequences) { 73 | if (longest == null || sequence.getTotalSleep() > longest.getTotalSleep()) { 74 | longest = sequence; 75 | } 76 | } 77 | return longest; 78 | } 79 | 80 | public static class SleepSequence { 81 | 82 | private final List sleeps; 83 | 84 | private final long longestSleep; 85 | 86 | private final long totalSleep; 87 | 88 | public SleepSequence(List sleeps) { 89 | this.sleeps = sleeps; 90 | this.longestSleep = Collections.max(sleeps); 91 | long totalSleep = 0; 92 | for (Long sleep : sleeps) { 93 | totalSleep += sleep; 94 | } 95 | this.totalSleep = totalSleep; 96 | } 97 | 98 | public List getSleeps() { 99 | return sleeps; 100 | } 101 | 102 | /** 103 | * @return the longest individual sleep within this sequence 104 | */ 105 | public long getLongestSleep() { 106 | return longestSleep; 107 | } 108 | 109 | public long getTotalSleep() { 110 | return totalSleep; 111 | } 112 | 113 | public String toString() { 114 | return "totalSleep=" + totalSleep + ": " + sleeps.toString(); 115 | } 116 | 117 | } 118 | 119 | } 120 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/retry/support/package.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

4 | Infrastructure implementations of retry support concerns. 5 |

6 | 7 | 8 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/classify/BackToBackPatternClassifierTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-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 | * https://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 org.springframework.classify; 17 | 18 | import java.util.Collections; 19 | import java.util.HashMap; 20 | import java.util.Map; 21 | 22 | import org.junit.jupiter.api.BeforeEach; 23 | import org.junit.jupiter.api.Test; 24 | 25 | import org.springframework.classify.annotation.Classifier; 26 | 27 | import static org.assertj.core.api.Assertions.assertThat; 28 | import static org.assertj.core.api.Assertions.assertThatExceptionOfType; 29 | 30 | /** 31 | * @author Dave Syer 32 | * 33 | */ 34 | public class BackToBackPatternClassifierTests { 35 | 36 | private BackToBackPatternClassifier classifier = new BackToBackPatternClassifier<>(); 37 | 38 | private Map map; 39 | 40 | @BeforeEach 41 | public void createMap() { 42 | map = new HashMap<>(); 43 | map.put("foo", "bar"); 44 | map.put("*", "spam"); 45 | } 46 | 47 | @Test 48 | public void testNoClassifiers() { 49 | assertThatExceptionOfType(NullPointerException.class).isThrownBy(() -> classifier.classify("foo")); 50 | } 51 | 52 | @Test 53 | public void testCreateFromConstructor() { 54 | classifier = new BackToBackPatternClassifier<>( 55 | new PatternMatchingClassifier<>(Collections.singletonMap("oof", "bucket")), 56 | new PatternMatchingClassifier<>(map)); 57 | assertThat(classifier.classify("oof")).isEqualTo("spam"); 58 | } 59 | 60 | @Test 61 | public void testSetRouterDelegate() { 62 | classifier.setRouterDelegate(new Object() { 63 | @Classifier 64 | public String convert(String value) { 65 | return "bucket"; 66 | } 67 | }); 68 | classifier.setMatcherMap(map); 69 | assertThat(classifier.classify("oof")).isEqualTo("spam"); 70 | } 71 | 72 | @Test 73 | public void testSingleMethodWithNoAnnotation() { 74 | classifier = new BackToBackPatternClassifier<>(); 75 | classifier.setRouterDelegate(new RouterDelegate()); 76 | classifier.setMatcherMap(map); 77 | assertThat(classifier.classify("oof")).isEqualTo("spam"); 78 | } 79 | 80 | @SuppressWarnings("serial") 81 | private class RouterDelegate implements org.springframework.classify.Classifier { 82 | 83 | @Override 84 | public String classify(Object classifiable) { 85 | return "bucket"; 86 | } 87 | 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/classify/BinaryExceptionClassifierBuilderTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2023 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 | * https://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 | 17 | package org.springframework.classify; 18 | 19 | import java.io.FileNotFoundException; 20 | import java.io.IOException; 21 | import java.io.StreamCorruptedException; 22 | import java.util.concurrent.TimeoutException; 23 | 24 | import org.junit.jupiter.api.Test; 25 | 26 | import org.springframework.retry.support.RetryTemplate; 27 | 28 | import static org.assertj.core.api.Assertions.assertThat; 29 | import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; 30 | 31 | /** 32 | * @author Aleksandr Shamukov 33 | */ 34 | public class BinaryExceptionClassifierBuilderTests { 35 | 36 | @Test 37 | public void testWhiteList() { 38 | RetryTemplate.builder().infiniteRetry().retryOn(IOException.class).uniformRandomBackoff(1000, 3000).build(); 39 | 40 | BinaryExceptionClassifier classifier = BinaryExceptionClassifier.builder() 41 | .retryOn(IOException.class) 42 | .retryOn(TimeoutException.class) 43 | .build(); 44 | 45 | assertThat(classifier.classify(new IOException())).isTrue(); 46 | // should not retry due to traverseCauses=fasle 47 | assertThat(classifier.classify(new RuntimeException(new IOException()))).isFalse(); 48 | assertThat(classifier.classify(new StreamCorruptedException())).isTrue(); 49 | assertThat(classifier.classify(new OutOfMemoryError())).isFalse(); 50 | } 51 | 52 | @Test 53 | public void testWhiteListWithTraverseCauses() { 54 | BinaryExceptionClassifier classifier = BinaryExceptionClassifier.builder() 55 | .retryOn(IOException.class) 56 | .retryOn(TimeoutException.class) 57 | .traversingCauses() 58 | .build(); 59 | 60 | assertThat(classifier.classify(new IOException())).isTrue(); 61 | // should retry due to traverseCauses=true 62 | assertThat(classifier.classify(new RuntimeException(new IOException()))).isTrue(); 63 | assertThat(classifier.classify(new StreamCorruptedException())).isTrue(); 64 | // should retry due to FileNotFoundException is a subclass of TimeoutException 65 | assertThat(classifier.classify(new FileNotFoundException())).isTrue(); 66 | assertThat(classifier.classify(new RuntimeException())).isFalse(); 67 | } 68 | 69 | @Test 70 | public void testBlackList() { 71 | BinaryExceptionClassifier classifier = BinaryExceptionClassifier.builder() 72 | .notRetryOn(Error.class) 73 | .notRetryOn(InterruptedException.class) 74 | .traversingCauses() 75 | .build(); 76 | 77 | // should not retry due to OutOfMemoryError is a subclass of Error 78 | assertThat(classifier.classify(new OutOfMemoryError())).isFalse(); 79 | assertThat(classifier.classify(new InterruptedException())).isFalse(); 80 | assertThat(classifier.classify(new Throwable())).isTrue(); 81 | // should retry due to traverseCauses=true 82 | assertThat(classifier.classify(new RuntimeException(new InterruptedException()))).isFalse(); 83 | } 84 | 85 | @Test 86 | public void testFailOnNotationMix() { 87 | assertThatIllegalArgumentException().isThrownBy(() -> BinaryExceptionClassifier.builder() 88 | .retryOn(IOException.class) 89 | .notRetryOn(OutOfMemoryError.class)); 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/classify/ClassifierSupportTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-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 | * https://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 | 17 | package org.springframework.classify; 18 | 19 | import org.junit.jupiter.api.Test; 20 | 21 | import static org.assertj.core.api.Assertions.assertThat; 22 | 23 | public class ClassifierSupportTests { 24 | 25 | @Test 26 | public void testClassifyNullIsDefault() { 27 | ClassifierSupport classifier = new ClassifierSupport<>("foo"); 28 | assertThat(classifier.classify(null)).isEqualTo("foo"); 29 | } 30 | 31 | @Test 32 | public void testClassifyRandomException() { 33 | ClassifierSupport classifier = new ClassifierSupport<>("foo"); 34 | assertThat(classifier.classify(new IllegalStateException("Foo"))).isEqualTo(classifier.classify(null)); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/classify/PatternMatchingClassifierTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-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 | * https://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 org.springframework.classify; 17 | 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | 21 | import org.junit.jupiter.api.BeforeEach; 22 | import org.junit.jupiter.api.Test; 23 | 24 | import static org.assertj.core.api.Assertions.assertThat; 25 | 26 | /** 27 | * @author Dave Syer 28 | * 29 | */ 30 | public class PatternMatchingClassifierTests { 31 | 32 | private PatternMatchingClassifier classifier = new PatternMatchingClassifier<>(); 33 | 34 | private Map map; 35 | 36 | @BeforeEach 37 | public void createMap() { 38 | map = new HashMap<>(); 39 | map.put("foo", "bar"); 40 | map.put("*", "spam"); 41 | } 42 | 43 | @Test 44 | public void testSetPatternMap() { 45 | classifier.setPatternMap(map); 46 | assertThat(classifier.classify("foo")).isEqualTo("bar"); 47 | assertThat(classifier.classify("bucket")).isEqualTo("spam"); 48 | } 49 | 50 | @Test 51 | public void testCreateFromMap() { 52 | classifier = new PatternMatchingClassifier<>(map); 53 | assertThat(classifier.classify("foo")).isEqualTo("bar"); 54 | assertThat(classifier.classify("bucket")).isEqualTo("spam"); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/classify/SubclassClassifierTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-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 | * https://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 | 17 | package org.springframework.classify; 18 | 19 | import java.util.Collections; 20 | import java.util.function.Supplier; 21 | 22 | import org.junit.jupiter.api.Test; 23 | 24 | import static org.assertj.core.api.Assertions.assertThat; 25 | 26 | public class SubclassClassifierTests { 27 | 28 | @Test 29 | public void testClassifyInterface() { 30 | SubclassClassifier classifier = new SubclassClassifier<>(); 31 | classifier.setTypeMap(Collections., String>singletonMap(Supplier.class, "foo")); 32 | assertThat(classifier.classify(new Foo())).isEqualTo("foo"); 33 | } 34 | 35 | @Test 36 | public void testClassifyInterfaceOfParent() { 37 | SubclassClassifier classifier = new SubclassClassifier<>(); 38 | classifier.setTypeMap(Collections., String>singletonMap(Supplier.class, "foo")); 39 | assertThat(classifier.classify(new Bar())).isEqualTo("foo"); 40 | } 41 | 42 | public class Bar extends Foo { 43 | 44 | } 45 | 46 | public static class Foo implements Supplier { 47 | 48 | @Override 49 | public String get() { 50 | return "foo"; 51 | } 52 | 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/retry/AbstractExceptionTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-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 | * https://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 | 17 | package org.springframework.retry; 18 | 19 | import org.junit.jupiter.api.Test; 20 | 21 | import static org.assertj.core.api.Assertions.assertThat; 22 | 23 | public abstract class AbstractExceptionTests { 24 | 25 | @Test 26 | public void testExceptionString() throws Exception { 27 | Exception exception = getException("foo"); 28 | assertThat(exception.getMessage()).isEqualTo("foo"); 29 | } 30 | 31 | @Test 32 | public void testExceptionStringThrowable() throws Exception { 33 | Exception exception = getException("foo", new IllegalStateException()); 34 | assertThat(exception.getMessage().substring(0, 3)).isEqualTo("foo"); 35 | } 36 | 37 | public abstract Exception getException(String msg); 38 | 39 | public abstract Exception getException(String msg, Throwable t); 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/retry/AnyThrowTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2023 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 | * https://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 | 17 | package org.springframework.retry; 18 | 19 | import org.junit.jupiter.api.Test; 20 | 21 | import static org.assertj.core.api.Assertions.assertThatExceptionOfType; 22 | 23 | /** 24 | * @author Dave Syer 25 | * @author Gary Russell 26 | * 27 | */ 28 | public class AnyThrowTests { 29 | 30 | @Test 31 | public void testRuntimeException() { 32 | assertThatExceptionOfType(RuntimeException.class) 33 | .isThrownBy(() -> AnyThrow.throwAny(new RuntimeException("planned"))); 34 | } 35 | 36 | @Test 37 | public void testUncheckedRuntimeException() { 38 | assertThatExceptionOfType(RuntimeException.class) 39 | .isThrownBy(() -> AnyThrow.throwUnchecked(new RuntimeException("planned"))); 40 | } 41 | 42 | @Test 43 | public void testCheckedException() { 44 | assertThatExceptionOfType(Exception.class).isThrownBy(() -> AnyThrow.throwAny(new Exception("planned"))); 45 | } 46 | 47 | private static class AnyThrow { 48 | 49 | private static void throwUnchecked(Throwable e) { 50 | AnyThrow.throwAny(e); 51 | } 52 | 53 | @SuppressWarnings("unchecked") 54 | private static void throwAny(Throwable e) throws E { 55 | throw (E) e; 56 | } 57 | 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/retry/BackOffInterruptedExceptionTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-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 | * https://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 | 17 | package org.springframework.retry; 18 | 19 | import org.springframework.retry.backoff.BackOffInterruptedException; 20 | 21 | public class BackOffInterruptedExceptionTests extends AbstractExceptionTests { 22 | 23 | public Exception getException(String msg) { 24 | return new BackOffInterruptedException(msg); 25 | } 26 | 27 | public Exception getException(String msg, Throwable t) { 28 | return new BackOffInterruptedException(msg, t); 29 | } 30 | 31 | public void testNothing() { 32 | // fool coverage tools... 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/retry/ExhaustedRetryExceptionTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-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 | * https://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 | 17 | package org.springframework.retry; 18 | 19 | import org.springframework.retry.ExhaustedRetryException; 20 | 21 | public class ExhaustedRetryExceptionTests extends AbstractExceptionTests { 22 | 23 | public Exception getException(String msg) { 24 | return new ExhaustedRetryException(msg); 25 | } 26 | 27 | public Exception getException(String msg, Throwable t) { 28 | return new ExhaustedRetryException(msg, t); 29 | } 30 | 31 | public void testNothing() { 32 | // fool coverage tools... 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/retry/ResourcelessTransactionManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-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 | * https://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 | 17 | package org.springframework.retry; 18 | 19 | import java.util.Stack; 20 | 21 | import org.springframework.transaction.TransactionDefinition; 22 | import org.springframework.transaction.TransactionException; 23 | import org.springframework.transaction.support.AbstractPlatformTransactionManager; 24 | import org.springframework.transaction.support.DefaultTransactionStatus; 25 | import org.springframework.transaction.support.TransactionSynchronizationManager; 26 | 27 | @SuppressWarnings("serial") 28 | public class ResourcelessTransactionManager extends AbstractPlatformTransactionManager { 29 | 30 | protected void doBegin(Object transaction, TransactionDefinition definition) throws TransactionException { 31 | ((ResourcelessTransaction) transaction).begin(); 32 | } 33 | 34 | protected void doCommit(DefaultTransactionStatus status) throws TransactionException { 35 | logger.debug("Committing resourceless transaction on [" + status.getTransaction() + "]"); 36 | } 37 | 38 | protected Object doGetTransaction() throws TransactionException { 39 | Object transaction = new ResourcelessTransaction(); 40 | Stack resources; 41 | if (!TransactionSynchronizationManager.hasResource(this)) { 42 | resources = new Stack<>(); 43 | TransactionSynchronizationManager.bindResource(this, resources); 44 | } 45 | else { 46 | @SuppressWarnings("unchecked") 47 | Stack stack = (Stack) TransactionSynchronizationManager.getResource(this); 48 | resources = stack; 49 | } 50 | resources.push(transaction); 51 | return transaction; 52 | } 53 | 54 | protected void doRollback(DefaultTransactionStatus status) throws TransactionException { 55 | logger.debug("Rolling back resourceless transaction on [" + status.getTransaction() + "]"); 56 | } 57 | 58 | protected boolean isExistingTransaction(Object transaction) throws TransactionException { 59 | if (TransactionSynchronizationManager.hasResource(this)) { 60 | @SuppressWarnings("unchecked") 61 | Stack stack = (Stack) TransactionSynchronizationManager.getResource(this); 62 | return stack.size() > 1; 63 | } 64 | return ((ResourcelessTransaction) transaction).isActive(); 65 | } 66 | 67 | protected void doSetRollbackOnly(DefaultTransactionStatus status) throws TransactionException { 68 | } 69 | 70 | protected void doCleanupAfterCompletion(Object transaction) { 71 | @SuppressWarnings("unchecked") 72 | Stack list = (Stack) TransactionSynchronizationManager.getResource(this); 73 | Stack resources = list; 74 | resources.clear(); 75 | TransactionSynchronizationManager.unbindResource(this); 76 | ((ResourcelessTransaction) transaction).clear(); 77 | } 78 | 79 | private static class ResourcelessTransaction { 80 | 81 | private boolean active = false; 82 | 83 | public boolean isActive() { 84 | return active; 85 | } 86 | 87 | public void begin() { 88 | active = true; 89 | } 90 | 91 | public void clear() { 92 | active = false; 93 | } 94 | 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/retry/RetryExceptionTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-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 | * https://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 | 17 | package org.springframework.retry; 18 | 19 | import org.springframework.retry.RetryException; 20 | 21 | public class RetryExceptionTests extends AbstractExceptionTests { 22 | 23 | public Exception getException(String msg) { 24 | return new RetryException(msg); 25 | } 26 | 27 | public Exception getException(String msg, Throwable t) { 28 | return new RetryException(msg, t); 29 | } 30 | 31 | public void testNothing() { 32 | // fool coverage tools... 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/retry/TerminatedRetryExceptionTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-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 | * https://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 | 17 | package org.springframework.retry; 18 | 19 | import org.springframework.retry.TerminatedRetryException; 20 | 21 | public class TerminatedRetryExceptionTests extends AbstractExceptionTests { 22 | 23 | public Exception getException(String msg) { 24 | return new TerminatedRetryException(msg); 25 | } 26 | 27 | public Exception getException(String msg, Throwable t) { 28 | return new TerminatedRetryException(msg, t); 29 | } 30 | 31 | public void testNothing() { 32 | // fool coverage tools... 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/retry/annotation/CircuitBreakerResetTimeoutTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-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 | * https://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 | 17 | package org.springframework.retry.annotation; 18 | 19 | import org.junit.jupiter.api.Test; 20 | 21 | import org.springframework.context.annotation.AnnotationConfigApplicationContext; 22 | import org.springframework.context.annotation.Bean; 23 | import org.springframework.context.annotation.Configuration; 24 | import org.springframework.retry.RetryContext; 25 | import org.springframework.retry.policy.CircuitBreakerRetryPolicy; 26 | import org.springframework.retry.support.RetrySynchronizationManager; 27 | 28 | import static org.assertj.core.api.Assertions.assertThat; 29 | 30 | public class CircuitBreakerResetTimeoutTests { 31 | 32 | private final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( 33 | CircuitBreakerResetTimeoutTests.TestConfiguration.class); 34 | 35 | private final TestService serviceInTest = context.getBean(TestService.class); 36 | 37 | @Test 38 | public void circuitBreakerShouldBeClosedAfterResetTimeout() throws InterruptedException { 39 | incorrectStep(); 40 | incorrectStep(); 41 | incorrectStep(); 42 | incorrectStep(); 43 | 44 | final long timeOfLastFailure = System.currentTimeMillis(); 45 | correctStep(timeOfLastFailure); 46 | correctStep(timeOfLastFailure); 47 | correctStep(timeOfLastFailure); 48 | assertThat((Boolean) serviceInTest.getContext().getAttribute(CircuitBreakerRetryPolicy.CIRCUIT_OPEN)).isFalse(); 49 | } 50 | 51 | private void incorrectStep() { 52 | doFailedUpload(serviceInTest); 53 | System.out.println(); 54 | } 55 | 56 | private void correctStep(final long timeOfLastFailure) throws InterruptedException { 57 | Thread.sleep(6000L); 58 | printTime(timeOfLastFailure); 59 | doCorrectUpload(serviceInTest); 60 | System.out.println(); 61 | } 62 | 63 | private void printTime(final long timeOfLastFailure) { 64 | System.out.println(String.format("%d ms after last failure", (System.currentTimeMillis() - timeOfLastFailure))); 65 | } 66 | 67 | private void doFailedUpload(TestService externalService) { 68 | externalService.service("FAIL"); 69 | } 70 | 71 | private void doCorrectUpload(TestService externalService) { 72 | externalService.service(""); 73 | } 74 | 75 | @Configuration 76 | @EnableRetry 77 | protected static class TestConfiguration { 78 | 79 | @Bean 80 | public TestService externalService() { 81 | return new TestService(); 82 | } 83 | 84 | } 85 | 86 | static class TestService { 87 | 88 | private RetryContext context; 89 | 90 | @CircuitBreaker(retryFor = { RuntimeException.class }, openTimeout = 10000, resetTimeout = 15000) 91 | String service(String payload) { 92 | this.context = RetrySynchronizationManager.getContext(); 93 | System.out.println("real service called"); 94 | if (payload.contentEquals("FAIL")) { 95 | throw new RuntimeException(""); 96 | } 97 | return payload; 98 | } 99 | 100 | @Recover 101 | public String recover() { 102 | System.out.println("recovery action"); 103 | return ""; 104 | } 105 | 106 | public RetryContext getContext() { 107 | return this.context; 108 | } 109 | 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/retry/annotation/DontRetryRecovererTests.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 | * https://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 | 17 | package org.springframework.retry.annotation; 18 | 19 | import org.junit.jupiter.api.Test; 20 | 21 | import org.springframework.beans.factory.annotation.Autowired; 22 | import org.springframework.context.annotation.Bean; 23 | import org.springframework.context.annotation.Configuration; 24 | import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; 25 | 26 | import static org.assertj.core.api.Assertions.assertThat; 27 | import static org.assertj.core.api.Assertions.assertThatExceptionOfType; 28 | 29 | /** 30 | * @author Gary Russell 31 | * @since 1.3.4 32 | */ 33 | @SpringJUnitConfig 34 | public class DontRetryRecovererTests { 35 | 36 | @Test 37 | void dontRetry(@Autowired Service service) { 38 | assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> service.foo("x")).withMessage("test"); 39 | assertThat(service.getCallCount()).isEqualTo(3); 40 | assertThat(service.getRecoverCount()).isEqualTo(1); 41 | } 42 | 43 | @Configuration 44 | @EnableRetry 45 | public static class Config { 46 | 47 | @Bean 48 | Service service() { 49 | return new Service(); 50 | } 51 | 52 | } 53 | 54 | @Retryable 55 | public static class Service { 56 | 57 | int callCount; 58 | 59 | int recoverCount; 60 | 61 | public void foo(String in) { 62 | callCount++; 63 | throw new RuntimeException(); 64 | } 65 | 66 | @Recover 67 | public void recover(Exception ex, String in) { 68 | this.recoverCount++; 69 | throw new RuntimeException("test"); 70 | } 71 | 72 | public int getCallCount() { 73 | return callCount; 74 | } 75 | 76 | public int getRecoverCount() { 77 | return recoverCount; 78 | } 79 | 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/retry/annotation/EnableRetryNoThreadLocalTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 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 | * https://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 | 17 | package org.springframework.retry.annotation; 18 | 19 | import org.junit.jupiter.api.AfterAll; 20 | import org.junit.jupiter.api.BeforeAll; 21 | 22 | import org.springframework.retry.support.RetrySynchronizationManager; 23 | 24 | public class EnableRetryNoThreadLocalTests extends EnableRetryTests { 25 | 26 | @BeforeAll 27 | static void before() { 28 | RetrySynchronizationManager.setUseThreadLocal(false); 29 | } 30 | 31 | @AfterAll 32 | static void after() { 33 | RetrySynchronizationManager.setUseThreadLocal(true); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/retry/annotation/EnableRetryWithBackoffNoThreadLocalTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 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 | * https://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 | 17 | package org.springframework.retry.annotation; 18 | 19 | import org.junit.jupiter.api.AfterAll; 20 | import org.junit.jupiter.api.BeforeAll; 21 | 22 | import org.springframework.retry.support.RetrySynchronizationManager; 23 | 24 | /** 25 | * @author Gary Russell 26 | */ 27 | public class EnableRetryWithBackoffNoThreadLocalTests extends EnableRetryWithBackoffTests { 28 | 29 | @BeforeAll 30 | static void before() { 31 | RetrySynchronizationManager.setUseThreadLocal(false); 32 | } 33 | 34 | @AfterAll 35 | static void after() { 36 | RetrySynchronizationManager.setUseThreadLocal(true); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/retry/annotation/PrototypeBeanTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017-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 | * https://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 | 17 | package org.springframework.retry.annotation; 18 | 19 | import org.junit.jupiter.api.Test; 20 | 21 | import org.springframework.beans.factory.annotation.Autowired; 22 | import org.springframework.beans.factory.config.ConfigurableBeanFactory; 23 | import org.springframework.context.annotation.Bean; 24 | import org.springframework.context.annotation.Configuration; 25 | import org.springframework.context.annotation.Scope; 26 | import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; 27 | 28 | import static org.assertj.core.api.Assertions.assertThat; 29 | 30 | /** 31 | * @author Gary Russell 32 | * @since 1.2.2 33 | * 34 | */ 35 | @SpringJUnitConfig 36 | public class PrototypeBeanTests { 37 | 38 | @Autowired 39 | private Bar bar1; 40 | 41 | @Autowired 42 | private Bar bar2; 43 | 44 | @Autowired 45 | private Foo foo; 46 | 47 | @Test 48 | public void testProtoBean() { 49 | this.bar1.foo("one"); 50 | this.bar2.foo("two"); 51 | assertThat(this.foo.recovered).isEqualTo("two"); 52 | } 53 | 54 | @Configuration 55 | @EnableRetry 56 | public static class Config { 57 | 58 | @Bean 59 | public Foo foo() { 60 | return new Foo(); 61 | } 62 | 63 | @Bean 64 | @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) 65 | public Baz baz() { 66 | return new Baz(); 67 | } 68 | 69 | } 70 | 71 | public static class Foo { 72 | 73 | private String recovered; 74 | 75 | void demoRun(Bar bar) { 76 | throw new RuntimeException(); 77 | } 78 | 79 | void demoRecover(String instance) { 80 | this.recovered = instance; 81 | } 82 | 83 | } 84 | 85 | public interface Bar { 86 | 87 | @Retryable(backoff = @Backoff(0)) 88 | void foo(String instance); 89 | 90 | @Recover 91 | void bar(); 92 | 93 | } 94 | 95 | public static class Baz implements Bar { 96 | 97 | private String instance; 98 | 99 | @Autowired 100 | private Foo foo; 101 | 102 | @Override 103 | public void foo(String instance) { 104 | this.instance = instance; 105 | foo.demoRun(this); 106 | } 107 | 108 | @Override 109 | public void bar() { 110 | foo.demoRecover(this.instance); 111 | } 112 | 113 | @Override 114 | public String toString() { 115 | return "Baz [instance=" + this.instance + "]"; 116 | } 117 | 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/retry/annotation/ProxyApplicationTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-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 | * https://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 | 17 | package org.springframework.retry.annotation; 18 | 19 | import java.net.URL; 20 | import java.net.URLClassLoader; 21 | import java.util.HashSet; 22 | import java.util.Set; 23 | 24 | import org.junit.jupiter.api.Test; 25 | 26 | import org.springframework.context.ConfigurableApplicationContext; 27 | import org.springframework.context.annotation.AnnotationConfigApplicationContext; 28 | import org.springframework.context.annotation.Bean; 29 | import org.springframework.context.annotation.Configuration; 30 | import org.springframework.stereotype.Component; 31 | 32 | import static org.assertj.core.api.Assertions.assertThat; 33 | 34 | public class ProxyApplicationTests { 35 | 36 | private final CountClassesClassLoader classLoader = new CountClassesClassLoader(); 37 | 38 | @Test 39 | // See gh-53 40 | public void contextLoads() throws Exception { 41 | int count = count(); 42 | runAndClose(); 43 | runAndClose(); 44 | // Let the JVM catch up 45 | Thread.sleep(500L); 46 | runAndClose(); 47 | int base = count(); 48 | runAndClose(); 49 | count = count(); 50 | assertThat(count).describedAs("Class leak").isEqualTo(base); 51 | runAndClose(); 52 | count = count(); 53 | assertThat(count).describedAs("Class leak").isEqualTo(base); 54 | runAndClose(); 55 | count = count(); 56 | assertThat(count).describedAs("Class leak").isEqualTo(base); 57 | } 58 | 59 | @SuppressWarnings("resource") 60 | private void runAndClose() { 61 | AnnotationConfigApplicationContext run = new AnnotationConfigApplicationContext(); 62 | run.setClassLoader(this.classLoader); 63 | run.register(Empty.class); 64 | run.close(); 65 | while (run.getParent() != null) { 66 | ((ConfigurableApplicationContext) run.getParent()).close(); 67 | run = (AnnotationConfigApplicationContext) run.getParent(); 68 | } 69 | } 70 | 71 | private int count() { 72 | return this.classLoader.classes.size(); 73 | } 74 | 75 | private static class CountClassesClassLoader extends URLClassLoader { 76 | 77 | private final Set> classes = new HashSet<>(); 78 | 79 | public CountClassesClassLoader() { 80 | super(new URL[0], ProxyApplicationTests.class.getClassLoader()); 81 | } 82 | 83 | @Override 84 | public Class loadClass(String name) throws ClassNotFoundException { 85 | Class type = super.loadClass(name); 86 | classes.add(type); 87 | return type; 88 | } 89 | 90 | @Override 91 | protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { 92 | Class type = super.loadClass(name, resolve); 93 | classes.add(type); 94 | return type; 95 | } 96 | 97 | } 98 | 99 | @Configuration 100 | @EnableRetry(proxyTargetClass = true) 101 | protected static class Empty { 102 | 103 | @Bean 104 | public Service service() { 105 | return new Service(); 106 | } 107 | 108 | } 109 | 110 | @Component 111 | static class Service { 112 | 113 | @Retryable 114 | public void handle() { 115 | System.err.println("Handling"); 116 | } 117 | 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/retry/annotation/RetryableXmlConfigTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024-2024 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 | * https://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 | 17 | package org.springframework.retry.annotation; 18 | 19 | import org.junit.jupiter.api.Test; 20 | 21 | import org.springframework.beans.factory.annotation.Autowired; 22 | import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; 23 | 24 | import static org.assertj.core.api.Assertions.assertThat; 25 | 26 | /** 27 | * @author Artem Bilan 28 | * @since 2.0.9 29 | */ 30 | @SpringJUnitConfig 31 | public class RetryableXmlConfigTests { 32 | 33 | @Autowired 34 | Service service; 35 | 36 | @Test 37 | void serviceCallIsRetied() { 38 | this.service.service(); 39 | assertThat(service.getCount()).isEqualTo(3); 40 | } 41 | 42 | public static class Service { 43 | 44 | private int count = 0; 45 | 46 | @Retryable 47 | public void service() { 48 | if (this.count++ < 2) { 49 | throw new RuntimeException("Planned"); 50 | } 51 | } 52 | 53 | public int getCount() { 54 | return this.count; 55 | } 56 | 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/retry/backoff/BackOffPolicySerializationTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-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 | * https://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 | 17 | package org.springframework.retry.backoff; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | import java.util.Set; 22 | import java.util.regex.Pattern; 23 | import java.util.stream.Stream; 24 | 25 | import org.apache.commons.logging.Log; 26 | import org.apache.commons.logging.LogFactory; 27 | import org.junit.jupiter.params.ParameterizedTest; 28 | import org.junit.jupiter.params.provider.MethodSource; 29 | 30 | import org.springframework.beans.BeanUtils; 31 | import org.springframework.beans.factory.config.BeanDefinition; 32 | import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; 33 | import org.springframework.core.type.filter.AssignableTypeFilter; 34 | import org.springframework.core.type.filter.RegexPatternTypeFilter; 35 | import org.springframework.retry.context.RetryContextSupport; 36 | import org.springframework.util.ClassUtils; 37 | import org.springframework.util.SerializationUtils; 38 | 39 | import static org.junit.jupiter.api.Assertions.assertTrue; 40 | 41 | /** 42 | * @author Dave Syer 43 | * @author Gary Russell 44 | * 45 | */ 46 | public class BackOffPolicySerializationTests { 47 | 48 | private static final Log logger = LogFactory.getLog(BackOffPolicySerializationTests.class); 49 | 50 | @SuppressWarnings("deprecation") 51 | public static Stream policies() { 52 | List result = new ArrayList<>(); 53 | ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(true); 54 | scanner.addIncludeFilter(new AssignableTypeFilter(BackOffPolicy.class)); 55 | scanner.addExcludeFilter(new RegexPatternTypeFilter(Pattern.compile(".*Test.*"))); 56 | scanner.addExcludeFilter(new RegexPatternTypeFilter(Pattern.compile(".*Mock.*"))); 57 | scanner.addExcludeFilter(new RegexPatternTypeFilter(Pattern.compile(".*Configuration.*"))); 58 | Set candidates = scanner.findCandidateComponents("org.springframework.retry"); 59 | for (BeanDefinition beanDefinition : candidates) { 60 | try { 61 | result.add(new Object[] { 62 | BeanUtils.instantiate(ClassUtils.resolveClassName(beanDefinition.getBeanClassName(), null)) }); 63 | } 64 | catch (Exception e) { 65 | logger.warn("Cannot create instance of " + beanDefinition.getBeanClassName()); 66 | } 67 | } 68 | return result.stream(); 69 | } 70 | 71 | @ParameterizedTest 72 | @MethodSource("policies") 73 | @SuppressWarnings("deprecation") 74 | public void testSerializationCycleForContext(BackOffPolicy policy) { 75 | BackOffContext context = policy.start(new RetryContextSupport(null)); 76 | if (context != null) { 77 | assertTrue(SerializationUtils.deserialize(SerializationUtils.serialize(context)) instanceof BackOffContext); 78 | } 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/retry/backoff/DummySleeper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2025 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 | * https://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 org.springframework.retry.backoff; 17 | 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | /** 22 | * Simple {@link Sleeper} implementation that just waits on a local Object. 23 | * 24 | * @author Dave Syer 25 | * 26 | */ 27 | @SuppressWarnings("serial") 28 | public class DummySleeper implements Sleeper { 29 | 30 | private final List backOffs = new ArrayList<>(); 31 | 32 | /** 33 | * Public getter for the long. 34 | * @return the lastBackOff 35 | */ 36 | public long getLastBackOff() { 37 | return backOffs.get(backOffs.size() - 1); 38 | } 39 | 40 | public long[] getBackOffs() { 41 | long[] result = new long[backOffs.size()]; 42 | int i = 0; 43 | for (Long value : backOffs) { 44 | result[i++] = value; 45 | } 46 | return result; 47 | } 48 | 49 | /* 50 | * (non-Javadoc) 51 | * 52 | * @see org.springframework.batch.retry.backoff.Sleeper#sleep(long) 53 | */ 54 | public void sleep(long backOffPeriod) throws InterruptedException { 55 | this.backOffs.add(backOffPeriod); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/retry/backoff/FixedBackOffPolicyTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2023 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 | * https://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 | 17 | package org.springframework.retry.backoff; 18 | 19 | import org.junit.jupiter.api.Test; 20 | 21 | import static org.assertj.core.api.Assertions.assertThat; 22 | import static org.assertj.core.api.Assertions.assertThatExceptionOfType; 23 | 24 | /** 25 | * @author Rob Harrop 26 | * @author Dave Syer 27 | * @author Gary Russell 28 | * @author Marius Lichtblau 29 | * @since 2.1 30 | */ 31 | public class FixedBackOffPolicyTests { 32 | 33 | private final DummySleeper sleeper = new DummySleeper(); 34 | 35 | @Test 36 | public void testSetBackoffPeriodNegative() { 37 | FixedBackOffPolicy strategy = new FixedBackOffPolicy(); 38 | strategy.setBackOffPeriod(-1000L); 39 | strategy.setSleeper(sleeper); 40 | strategy.backOff(null); 41 | // We should see a zero backoff if we try to set it negative 42 | assertThat(sleeper.getBackOffs().length).isEqualTo(1); 43 | assertThat(sleeper.getLastBackOff()).isEqualTo(1); 44 | } 45 | 46 | @Test 47 | public void testSingleBackOff() { 48 | int backOffPeriod = 50; 49 | FixedBackOffPolicy strategy = new FixedBackOffPolicy(); 50 | strategy.setBackOffPeriod(backOffPeriod); 51 | strategy.setSleeper(sleeper); 52 | strategy.backOff(null); 53 | assertThat(sleeper.getBackOffs().length).isEqualTo(1); 54 | assertThat(sleeper.getLastBackOff()).isEqualTo(backOffPeriod); 55 | } 56 | 57 | @Test 58 | public void testManyBackOffCalls() { 59 | int backOffPeriod = 50; 60 | FixedBackOffPolicy strategy = new FixedBackOffPolicy(); 61 | strategy.setBackOffPeriod(backOffPeriod); 62 | strategy.setSleeper(sleeper); 63 | for (int x = 0; x < 10; x++) { 64 | strategy.backOff(null); 65 | assertThat(sleeper.getLastBackOff()).isEqualTo(backOffPeriod); 66 | } 67 | assertThat(sleeper.getBackOffs().length).isEqualTo(10); 68 | } 69 | 70 | @Test 71 | public void testInterruptedStatusIsRestored() { 72 | int backOffPeriod = 50; 73 | FixedBackOffPolicy strategy = new FixedBackOffPolicy(); 74 | strategy.setBackOffPeriod(backOffPeriod); 75 | strategy.setSleeper(new Sleeper() { 76 | @Override 77 | public void sleep(long backOffPeriod) throws InterruptedException { 78 | throw new InterruptedException("foo"); 79 | } 80 | }); 81 | assertThatExceptionOfType(BackOffInterruptedException.class).isThrownBy(() -> strategy.backOff(null)); 82 | assertThat(Thread.interrupted()).isTrue(); 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/retry/backoff/ThreadWaitSleeperTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2023 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 | * https://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 | 17 | package org.springframework.retry.backoff; 18 | 19 | import org.junit.jupiter.api.Test; 20 | 21 | import static org.assertj.core.api.Assertions.assertThat; 22 | 23 | /** 24 | * @author Dave Syer 25 | * @author Artem Bilan 26 | * @author Gary Russell 27 | */ 28 | public class ThreadWaitSleeperTests { 29 | 30 | @Test 31 | public void testSingleBackOff() throws Exception { 32 | long backOffPeriod = 50; 33 | ThreadWaitSleeper strategy = new ThreadWaitSleeper(); 34 | long before = System.currentTimeMillis(); 35 | strategy.sleep(backOffPeriod); 36 | long after = System.currentTimeMillis(); 37 | assertEqualsApprox(backOffPeriod, after - before, 25); 38 | } 39 | 40 | private void assertEqualsApprox(long desired, long actual, long variance) { 41 | long lower = desired - variance; 42 | long upper = desired + 2 * variance; 43 | assertThat(lower).describedAs("Expected value to be between '%d' and '%d' but was '%d'", lower, upper, actual) 44 | .isLessThanOrEqualTo(actual); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/retry/backoff/UniformRandomBackOffPolicyTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2023 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 | * https://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 | 17 | package org.springframework.retry.backoff; 18 | 19 | import org.junit.jupiter.api.Test; 20 | 21 | import static org.assertj.core.api.Assertions.assertThat; 22 | import static org.assertj.core.api.Assertions.assertThatExceptionOfType; 23 | 24 | /** 25 | * @author Tomaz Fernandes 26 | * @author Gary Russell 27 | * @author Marius Lichtblau 28 | * @since 1.3.2 29 | */ 30 | public class UniformRandomBackOffPolicyTests { 31 | 32 | @Test 33 | public void testSetSleeper() { 34 | UniformRandomBackOffPolicy backOffPolicy = new UniformRandomBackOffPolicy(); 35 | int minBackOff = 1000; 36 | int maxBackOff = 10000; 37 | backOffPolicy.setMinBackOffPeriod(minBackOff); 38 | backOffPolicy.setMaxBackOffPeriod(maxBackOff); 39 | 40 | DummySleeper dummySleeper = new DummySleeper(); 41 | UniformRandomBackOffPolicy withSleeper = backOffPolicy.withSleeper(dummySleeper); 42 | 43 | assertThat(withSleeper.getMinBackOffPeriod()).isEqualTo(minBackOff); 44 | assertThat(withSleeper.getMaxBackOffPeriod()).isEqualTo(maxBackOff); 45 | 46 | assertThat(dummySleeper.getBackOffs()).isEmpty(); 47 | withSleeper.backOff(null); 48 | 49 | assertThat(dummySleeper.getBackOffs()).hasSize(1); 50 | assertThat(dummySleeper.getBackOffs()[0]).isLessThan(maxBackOff); 51 | } 52 | 53 | @Test 54 | public void testInterruptedStatusIsRestored() { 55 | UniformRandomBackOffPolicy backOffPolicy = new UniformRandomBackOffPolicy(); 56 | int minBackOff = 1000; 57 | int maxBackOff = 10000; 58 | backOffPolicy.setMinBackOffPeriod(minBackOff); 59 | backOffPolicy.setMaxBackOffPeriod(maxBackOff); 60 | UniformRandomBackOffPolicy withSleeper = backOffPolicy.withSleeper(new Sleeper() { 61 | @Override 62 | public void sleep(long backOffPeriod) throws InterruptedException { 63 | throw new InterruptedException("foo"); 64 | } 65 | }); 66 | 67 | assertThatExceptionOfType(BackOffInterruptedException.class).isThrownBy(() -> withSleeper.backOff(null)); 68 | assertThat(Thread.interrupted()).isTrue(); 69 | } 70 | 71 | @Test 72 | public void testMaxBackOffLessThanMinBackOff() { 73 | UniformRandomBackOffPolicy backOffPolicy = new UniformRandomBackOffPolicy(); 74 | int minBackOff = 1000; 75 | int maxBackOff = 10; 76 | backOffPolicy.setMinBackOffPeriod(minBackOff); 77 | backOffPolicy.setMaxBackOffPeriod(maxBackOff); 78 | 79 | DummySleeper dummySleeper = new DummySleeper(); 80 | UniformRandomBackOffPolicy withSleeper = backOffPolicy.withSleeper(dummySleeper); 81 | assertThat(dummySleeper.getBackOffs()).isEmpty(); 82 | withSleeper.backOff(null); 83 | assertThat(dummySleeper.getBackOffs()).hasSize(1); 84 | assertThat(dummySleeper.getBackOffs()[0]).isEqualTo(minBackOff); 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/retry/policy/AlwaysRetryPolicyTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-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 | * https://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 | 17 | package org.springframework.retry.policy; 18 | 19 | import org.junit.jupiter.api.Test; 20 | 21 | import org.springframework.retry.RetryContext; 22 | 23 | import static org.assertj.core.api.Assertions.assertThat; 24 | 25 | public class AlwaysRetryPolicyTests { 26 | 27 | @Test 28 | public void testSimpleOperations() { 29 | AlwaysRetryPolicy policy = new AlwaysRetryPolicy(); 30 | RetryContext context = policy.open(null); 31 | assertThat(context).isNotNull(); 32 | assertThat(policy.canRetry(context)).isTrue(); 33 | policy.registerThrowable(context, null); 34 | assertThat(policy.canRetry(context)).isTrue(); 35 | policy.close(context); 36 | assertThat(policy.canRetry(context)).isTrue(); 37 | } 38 | 39 | @Test 40 | public void testRetryCount() { 41 | AlwaysRetryPolicy policy = new AlwaysRetryPolicy(); 42 | RetryContext context = policy.open(null); 43 | assertThat(context).isNotNull(); 44 | policy.registerThrowable(context, null); 45 | assertThat(context.getRetryCount()).isEqualTo(0); 46 | policy.registerThrowable(context, new RuntimeException("foo")); 47 | assertThat(context.getRetryCount()).isEqualTo(1); 48 | assertThat(context.getLastThrowable().getMessage()).isEqualTo("foo"); 49 | } 50 | 51 | @Test 52 | public void testParent() { 53 | AlwaysRetryPolicy policy = new AlwaysRetryPolicy(); 54 | RetryContext context = policy.open(null); 55 | RetryContext child = policy.open(context); 56 | assertThat(context).isNotSameAs(child); 57 | assertThat(child.getParent()).isSameAs(context); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/retry/policy/MapRetryContextCacheTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2023 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 | * https://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 | 17 | package org.springframework.retry.policy; 18 | 19 | import org.junit.jupiter.api.Test; 20 | 21 | import org.springframework.retry.context.RetryContextSupport; 22 | 23 | import static org.assertj.core.api.Assertions.assertThat; 24 | import static org.assertj.core.api.Assertions.assertThatExceptionOfType; 25 | 26 | public class MapRetryContextCacheTests { 27 | 28 | MapRetryContextCache cache = new MapRetryContextCache(); 29 | 30 | @Test 31 | public void testPut() { 32 | RetryContextSupport context = new RetryContextSupport(null); 33 | cache.put("foo", context); 34 | assertThat(cache.get("foo")).isEqualTo(context); 35 | } 36 | 37 | @Test 38 | public void testPutOverLimit() { 39 | RetryContextSupport context = new RetryContextSupport(null); 40 | cache.setCapacity(1); 41 | cache.put("foo", context); 42 | assertThatExceptionOfType(RetryCacheCapacityExceededException.class) 43 | .isThrownBy(() -> cache.put("foo", context)); 44 | } 45 | 46 | @Test 47 | public void testRemove() { 48 | assertThat(cache.containsKey("foo")).isFalse(); 49 | RetryContextSupport context = new RetryContextSupport(null); 50 | cache.put("foo", context); 51 | assertThat(cache.containsKey("foo")).isTrue(); 52 | cache.remove("foo"); 53 | assertThat(cache.containsKey("foo")).isFalse(); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/retry/policy/MockRetryPolicySupport.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 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 | * https://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 | 17 | package org.springframework.retry.policy; 18 | 19 | @SuppressWarnings("serial") 20 | public class MockRetryPolicySupport extends AlwaysRetryPolicy { 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/retry/policy/NeverRetryPolicyTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-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 | * https://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 | 17 | package org.springframework.retry.policy; 18 | 19 | import org.junit.jupiter.api.Test; 20 | 21 | import org.springframework.retry.RetryContext; 22 | 23 | import static org.assertj.core.api.Assertions.assertThat; 24 | 25 | public class NeverRetryPolicyTests { 26 | 27 | @Test 28 | public void testSimpleOperations() { 29 | NeverRetryPolicy policy = new NeverRetryPolicy(); 30 | RetryContext context = policy.open(null); 31 | assertThat(context).isNotNull(); 32 | // We can retry until the first exception is registered... 33 | assertThat(policy.canRetry(context)).isTrue(); 34 | assertThat(policy.canRetry(context)).isTrue(); 35 | policy.registerThrowable(context, null); 36 | assertThat(policy.canRetry(context)).isFalse(); 37 | policy.close(context); 38 | assertThat(policy.canRetry(context)).isFalse(); 39 | } 40 | 41 | @Test 42 | public void testRetryCount() { 43 | NeverRetryPolicy policy = new NeverRetryPolicy(); 44 | RetryContext context = policy.open(null); 45 | assertThat(context).isNotNull(); 46 | policy.registerThrowable(context, null); 47 | assertThat(context.getRetryCount()).isEqualTo(0); 48 | policy.registerThrowable(context, new RuntimeException("foo")); 49 | assertThat(context.getRetryCount()).isEqualTo(1); 50 | assertThat(context.getLastThrowable().getMessage()).isEqualTo("foo"); 51 | } 52 | 53 | @Test 54 | public void testParent() { 55 | NeverRetryPolicy policy = new NeverRetryPolicy(); 56 | RetryContext context = policy.open(null); 57 | RetryContext child = policy.open(context); 58 | assertThat(context).isNotSameAs(child); 59 | assertThat(child.getParent()).isSameAs(context); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/retry/policy/SerializedMapRetryContextCache.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-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 | * https://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 org.springframework.retry.policy; 17 | 18 | import java.io.ByteArrayInputStream; 19 | import java.io.IOException; 20 | import java.io.ObjectInputStream; 21 | import java.util.Collections; 22 | import java.util.HashMap; 23 | import java.util.Map; 24 | 25 | import org.springframework.retry.RetryContext; 26 | import org.springframework.util.SerializationUtils; 27 | 28 | public class SerializedMapRetryContextCache implements RetryContextCache { 29 | 30 | private static final int DEFAULT_CAPACITY = 4096; 31 | 32 | private final Map map = Collections.synchronizedMap(new HashMap<>()); 33 | 34 | @Override 35 | public boolean containsKey(Object key) { 36 | return map.containsKey(key); 37 | } 38 | 39 | @Override 40 | public RetryContext get(Object key) { 41 | byte[] bytes = map.get(key); 42 | try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes))) { 43 | return (RetryContext) ois.readObject(); 44 | } 45 | catch (IOException ex) { 46 | throw new IllegalArgumentException("Failed to deserialize object", ex); 47 | } 48 | catch (ClassNotFoundException ex) { 49 | throw new IllegalStateException("Failed to deserialize object type", ex); 50 | } 51 | } 52 | 53 | @Override 54 | public void put(Object key, RetryContext context) { 55 | if (map.size() >= DEFAULT_CAPACITY) { 56 | throw new RetryCacheCapacityExceededException("Retry cache capacity " 57 | + "limit breached. Do you need to re-consider the implementation of the key generator, " 58 | + "or the equals and hashCode of the items that failed?"); 59 | } 60 | byte[] serialized = SerializationUtils.serialize(context); 61 | map.put(key, serialized); 62 | } 63 | 64 | @Override 65 | public void remove(Object key) { 66 | map.remove(key); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/retry/policy/SoftReferenceMapRetryContextCacheTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2023 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 | * https://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 | 17 | package org.springframework.retry.policy; 18 | 19 | import org.junit.jupiter.api.Test; 20 | 21 | import org.springframework.retry.context.RetryContextSupport; 22 | 23 | import static org.assertj.core.api.Assertions.assertThat; 24 | import static org.assertj.core.api.Assertions.assertThatExceptionOfType; 25 | 26 | public class SoftReferenceMapRetryContextCacheTests { 27 | 28 | SoftReferenceMapRetryContextCache cache = new SoftReferenceMapRetryContextCache(); 29 | 30 | @Test 31 | public void testPut() { 32 | RetryContextSupport context = new RetryContextSupport(null); 33 | cache.put("foo", context); 34 | assertThat(cache.get("foo")).isEqualTo(context); 35 | } 36 | 37 | @Test 38 | public void testPutOverLimit() { 39 | RetryContextSupport context = new RetryContextSupport(null); 40 | cache.setCapacity(1); 41 | cache.put("foo", context); 42 | assertThatExceptionOfType(RetryCacheCapacityExceededException.class) 43 | .isThrownBy(() -> cache.put("foo", context)); 44 | } 45 | 46 | @Test 47 | public void testRemove() { 48 | assertThat(cache.containsKey("foo")).isFalse(); 49 | RetryContextSupport context = new RetryContextSupport(null); 50 | cache.put("foo", context); 51 | assertThat(cache.containsKey("foo")).isTrue(); 52 | cache.remove("foo"); 53 | assertThat(cache.containsKey("foo")).isFalse(); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/retry/policy/TimeoutRetryPolicyTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-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 | * https://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 | 17 | package org.springframework.retry.policy; 18 | 19 | import org.junit.jupiter.api.Test; 20 | 21 | import org.springframework.retry.RetryContext; 22 | 23 | import static org.assertj.core.api.Assertions.assertThat; 24 | 25 | public class TimeoutRetryPolicyTests { 26 | 27 | @Test 28 | public void testTimeoutPreventsRetry() throws Exception { 29 | TimeoutRetryPolicy policy = new TimeoutRetryPolicy(); 30 | policy.setTimeout(100); 31 | RetryContext context = policy.open(null); 32 | policy.registerThrowable(context, new Exception()); 33 | assertThat(policy.canRetry(context)).isTrue(); 34 | Thread.sleep(200); 35 | assertThat(policy.canRetry(context)).isFalse(); 36 | policy.close(context); 37 | } 38 | 39 | @Test 40 | public void testRetryCount() { 41 | TimeoutRetryPolicy policy = new TimeoutRetryPolicy(); 42 | RetryContext context = policy.open(null); 43 | assertThat(context).isNotNull(); 44 | policy.registerThrowable(context, null); 45 | assertThat(context.getRetryCount()).isEqualTo(0); 46 | policy.registerThrowable(context, new RuntimeException("foo")); 47 | assertThat(context.getRetryCount()).isEqualTo(1); 48 | assertThat(context.getLastThrowable().getMessage()).isEqualTo("foo"); 49 | } 50 | 51 | @Test 52 | public void testParent() { 53 | TimeoutRetryPolicy policy = new TimeoutRetryPolicy(); 54 | RetryContext context = policy.open(null); 55 | RetryContext child = policy.open(context); 56 | assertThat(context).isNotSameAs(child); 57 | assertThat(child.getParent()).isSameAs(context); 58 | } 59 | 60 | @Test 61 | public void testConstructorWithCustomTimeout() throws Exception { 62 | TimeoutRetryPolicy policy = new TimeoutRetryPolicy(100); 63 | RetryContext context = policy.open(null); 64 | policy.registerThrowable(context, new Exception()); 65 | assertThat(policy.canRetry(context)).isTrue(); 66 | Thread.sleep(200); 67 | assertThat(policy.canRetry(context)).isFalse(); 68 | policy.close(context); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/retry/stats/ExponentialAverageRetryStatisticsTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-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 | * https://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 | 17 | package org.springframework.retry.stats; 18 | 19 | import java.util.Arrays; 20 | 21 | import org.junit.jupiter.api.Test; 22 | 23 | import org.springframework.test.util.ReflectionTestUtils; 24 | 25 | import static org.assertj.core.api.Assertions.assertThat; 26 | 27 | /** 28 | * @author Dave Syer 29 | * @author Gary Russell 30 | * 31 | */ 32 | public class ExponentialAverageRetryStatisticsTests { 33 | 34 | private final ExponentialAverageRetryStatistics stats = new ExponentialAverageRetryStatistics("test"); 35 | 36 | @Test 37 | public void pointless() { 38 | stats.setName("spam"); 39 | assertThat(stats.getName()).isEqualTo("spam"); 40 | } 41 | 42 | @Test 43 | public void attributes() { 44 | stats.setAttribute("foo", "bar"); 45 | assertThat(stats.getAttribute("foo")).isEqualTo("bar"); 46 | assertThat(Arrays.asList(stats.attributeNames()).contains("foo")).isTrue(); 47 | } 48 | 49 | @Test 50 | public void abortCount() { 51 | stats.incrementAbortCount(); 52 | assertThat(stats.getAbortCount()).isEqualTo(1); 53 | // rounds up to 1 54 | assertThat(stats.getRollingAbortCount()).isEqualTo(1); 55 | } 56 | 57 | @Test 58 | public void errorCount() { 59 | stats.incrementErrorCount(); 60 | assertThat(stats.getErrorCount()).isEqualTo(1); 61 | // rounds up to 1 62 | assertThat(stats.getRollingErrorCount()).isEqualTo(1); 63 | } 64 | 65 | @Test 66 | public void startedCount() { 67 | stats.incrementStartedCount(); 68 | assertThat(stats.getStartedCount()).isEqualTo(1); 69 | // rounds up to 1 70 | assertThat(stats.getRollingStartedCount()).isEqualTo(1); 71 | } 72 | 73 | @Test 74 | public void completeCount() { 75 | stats.incrementCompleteCount(); 76 | assertThat(stats.getCompleteCount()).isEqualTo(1); 77 | // rounds up to 1 78 | assertThat(stats.getRollingCompleteCount()).isEqualTo(1); 79 | } 80 | 81 | @Test 82 | public void recoveryCount() { 83 | stats.incrementRecoveryCount(); 84 | assertThat(stats.getRecoveryCount()).isEqualTo(1); 85 | // rounds up to 1 86 | assertThat(stats.getRollingRecoveryCount()).isEqualTo(1); 87 | } 88 | 89 | @Test 90 | public void oldValuesDecay() { 91 | stats.incrementAbortCount(); 92 | assertThat(stats.getAbortCount()).isEqualTo(1); 93 | // Wind back time to epoch 0 94 | ReflectionTestUtils.setField(ReflectionTestUtils.getField(stats, "abort"), "lastTime", 0); 95 | // rounds down to 1 96 | assertThat(stats.getRollingAbortCount()).isEqualTo(0); 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/retry/support/DefaultRetryStateTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-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 | * https://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 org.springframework.retry.support; 17 | 18 | import org.junit.jupiter.api.Test; 19 | 20 | import static org.assertj.core.api.Assertions.assertThat; 21 | 22 | /** 23 | * @author Dave Syer 24 | * @author Gary Russell 25 | * 26 | */ 27 | public class DefaultRetryStateTests { 28 | 29 | /** 30 | * Test method for 31 | * {@link org.springframework.retry.support.DefaultRetryState#DefaultRetryState(java.lang.Object, boolean, org.springframework.classify.Classifier)}. 32 | */ 33 | @SuppressWarnings("serial") 34 | @Test 35 | public void testDefaultRetryStateObjectBooleanClassifierOfQsuperThrowableBoolean() { 36 | DefaultRetryState state = new DefaultRetryState("foo", true, classifiable -> false); 37 | assertThat(state.getKey()).isEqualTo("foo"); 38 | assertThat(state.isForceRefresh()).isTrue(); 39 | assertThat(state.rollbackFor(null)).isFalse(); 40 | } 41 | 42 | /** 43 | * Test method for 44 | * {@link org.springframework.retry.support.DefaultRetryState#DefaultRetryState(java.lang.Object, org.springframework.classify.Classifier)}. 45 | */ 46 | @SuppressWarnings("serial") 47 | @Test 48 | public void testDefaultRetryStateObjectClassifierOfQsuperThrowableBoolean() { 49 | DefaultRetryState state = new DefaultRetryState("foo", classifiable -> false); 50 | assertThat(state.getKey()).isEqualTo("foo"); 51 | assertThat(state.isForceRefresh()).isFalse(); 52 | assertThat(state.rollbackFor(null)).isFalse(); 53 | } 54 | 55 | /** 56 | * Test method for 57 | * {@link org.springframework.retry.support.DefaultRetryState#DefaultRetryState(java.lang.Object, boolean)}. 58 | */ 59 | @Test 60 | public void testDefaultRetryStateObjectBoolean() { 61 | DefaultRetryState state = new DefaultRetryState("foo", true); 62 | assertThat(state.getKey()).isEqualTo("foo"); 63 | assertThat(state.isForceRefresh()).isTrue(); 64 | assertThat(state.rollbackFor(null)).isTrue(); 65 | } 66 | 67 | /** 68 | * Test method for 69 | * {@link org.springframework.retry.support.DefaultRetryState#DefaultRetryState(java.lang.Object)}. 70 | */ 71 | @Test 72 | public void testDefaultRetryStateObject() { 73 | DefaultRetryState state = new DefaultRetryState("foo"); 74 | assertThat(state.getKey()).isEqualTo("foo"); 75 | assertThat(state.isForceRefresh()).isFalse(); 76 | assertThat(state.rollbackFor(null)).isTrue(); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/retry/support/RetrySynchronizationManagerNoThreadLocalTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 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 | * https://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 | 17 | package org.springframework.retry.support; 18 | 19 | import org.junit.jupiter.api.AfterAll; 20 | import org.junit.jupiter.api.BeforeAll; 21 | import org.junit.jupiter.api.BeforeEach; 22 | 23 | import org.springframework.retry.RetryContext; 24 | 25 | import static org.assertj.core.api.Assertions.assertThat; 26 | 27 | public class RetrySynchronizationManagerNoThreadLocalTests extends RetrySynchronizationManagerTests { 28 | 29 | @BeforeAll 30 | static void before() { 31 | RetrySynchronizationManager.setUseThreadLocal(false); 32 | } 33 | 34 | @AfterAll 35 | static void after() { 36 | RetrySynchronizationManager.setUseThreadLocal(true); 37 | } 38 | 39 | @Override 40 | @BeforeEach 41 | public void setUp() { 42 | RetrySynchronizationManagerTests.clearAll(); 43 | RetryContext status = RetrySynchronizationManager.getContext(); 44 | assertThat(status).isNull(); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/retry/support/RetrySynchronizationManagerTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-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 | * https://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 | 17 | package org.springframework.retry.support; 18 | 19 | import org.junit.jupiter.api.BeforeEach; 20 | import org.junit.jupiter.api.Test; 21 | 22 | import org.springframework.retry.RetryContext; 23 | import org.springframework.retry.context.RetryContextSupport; 24 | 25 | import static org.assertj.core.api.Assertions.assertThat; 26 | 27 | /** 28 | * @author Dave Syer 29 | * @author Gary Russell 30 | */ 31 | public class RetrySynchronizationManagerTests { 32 | 33 | RetryTemplate template = new RetryTemplate(); 34 | 35 | @BeforeEach 36 | public void setUp() { 37 | RetrySynchronizationManagerTests.clearAll(); 38 | RetryContext status = RetrySynchronizationManager.getContext(); 39 | assertThat(status).isNull(); 40 | } 41 | 42 | @Test 43 | public void testStatusIsStoredByTemplate() { 44 | 45 | RetryContext status = RetrySynchronizationManager.getContext(); 46 | assertThat(status).isNull(); 47 | 48 | this.template.execute(retryContext -> { 49 | RetryContext global = RetrySynchronizationManager.getContext(); 50 | assertThat(retryContext).isNotNull(); 51 | assertThat(retryContext).isEqualTo(global); 52 | return null; 53 | }); 54 | 55 | status = RetrySynchronizationManager.getContext(); 56 | assertThat(status).isNull(); 57 | } 58 | 59 | @Test 60 | public void testStatusRegistration() { 61 | RetryContext status = new RetryContextSupport(null); 62 | RetryContext value = RetrySynchronizationManager.register(status); 63 | assertThat(value).isNull(); 64 | value = RetrySynchronizationManager.register(status); 65 | assertThat(value).isEqualTo(status); 66 | } 67 | 68 | @Test 69 | public void testClear() { 70 | RetryContext status = new RetryContextSupport(null); 71 | RetryContext value = RetrySynchronizationManager.register(status); 72 | assertThat(value).isNull(); 73 | RetrySynchronizationManager.clear(); 74 | value = RetrySynchronizationManager.register(status); 75 | assertThat(value).isNull(); 76 | } 77 | 78 | @Test 79 | public void testParent() { 80 | RetryContext parent = new RetryContextSupport(null); 81 | RetryContext child = new RetryContextSupport(parent); 82 | assertThat(child.getParent()).isSameAs(parent); 83 | } 84 | 85 | /** 86 | * Clear all contexts starting with the current one and continuing until 87 | * {@link RetrySynchronizationManager#clear()} returns null. 88 | * @return a retry context 89 | */ 90 | public static RetryContext clearAll() { 91 | RetryContext result = null; 92 | RetryContext context = RetrySynchronizationManager.clear(); 93 | while (context != null) { 94 | result = context; 95 | context = RetrySynchronizationManager.clear(); 96 | } 97 | return result; 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/retry/support/RetryTemplateNoThreadLocalTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 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 | * https://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 | 17 | package org.springframework.retry.support; 18 | 19 | import org.junit.jupiter.api.AfterAll; 20 | import org.junit.jupiter.api.BeforeAll; 21 | 22 | /** 23 | * @author Gary Russell 24 | */ 25 | public class RetryTemplateNoThreadLocalTests extends RetryTemplateTests { 26 | 27 | @BeforeAll 28 | static void before() { 29 | RetrySynchronizationManager.setUseThreadLocal(false); 30 | } 31 | 32 | @AfterAll 33 | static void after() { 34 | RetrySynchronizationManager.setUseThreadLocal(true); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/retry/util/test/TestUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 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 | * https://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 org.springframework.retry.util.test; 17 | 18 | import org.springframework.beans.DirectFieldAccessor; 19 | import org.springframework.util.Assert; 20 | 21 | /** 22 | * See Spring Integration TestUtils. 23 | * 24 | * @author Mark Fisher 25 | * @author Iwein Fuld 26 | * @author Oleg Zhurakousky 27 | * @author Gary Russell 28 | * @since 1.2 29 | */ 30 | public class TestUtils { 31 | 32 | /** 33 | * Uses nested {@link org.springframework.beans.DirectFieldAccessor}s to obtain a 34 | * property using dotted notation to traverse fields; e.g. "foo.bar.baz" will obtain a 35 | * reference to the baz field of the bar field of foo. Adopted from Spring 36 | * Integration. 37 | * @param root The object. 38 | * @param propertyPath The path. 39 | * @return The field. 40 | */ 41 | public static Object getPropertyValue(Object root, String propertyPath) { 42 | Object value = null; 43 | DirectFieldAccessor accessor = new DirectFieldAccessor(root); 44 | String[] tokens = propertyPath.split("\\."); 45 | for (int i = 0; i < tokens.length; i++) { 46 | value = accessor.getPropertyValue(tokens[i]); 47 | if (value != null) { 48 | accessor = new DirectFieldAccessor(value); 49 | } 50 | else if (i == tokens.length - 1) { 51 | return null; 52 | } 53 | else { 54 | throw new IllegalArgumentException("intermediate property '" + tokens[i] + "' is null"); 55 | } 56 | } 57 | return value; 58 | } 59 | 60 | @SuppressWarnings("unchecked") 61 | public static T getPropertyValue(Object root, String propertyPath, Class type) { 62 | Object value = getPropertyValue(root, propertyPath); 63 | if (value != null) { 64 | Assert.isAssignable(type, value.getClass()); 65 | } 66 | return (T) value; 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/test/resources/log4j2-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/test/resources/org/springframework/repeat/support/trades.csv: -------------------------------------------------------------------------------- 1 | UK21341EAH45,978,98.34 2 | UK21341EAH46,112,18.12 3 | UK21341EAH47,245,12.78 4 | UK21341EAH48,108,109.25 5 | UK21341EAH49,854,123.39 -------------------------------------------------------------------------------- /src/test/resources/org/springframework/retry/annotation/RetryableXmlConfigTests-context.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/test/resources/org/springframework/retry/interceptor/retry-transaction-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 14 | 16 | 17 | 18 | 19 | 21 | 22 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 33 | 34 | 35 | --------------------------------------------------------------------------------