├── .github ├── dco.yml ├── dependabot.yml ├── release.yml └── workflows │ ├── pr-checks.yml │ └── publish.yml ├── .gitignore ├── README.md ├── build.gradle ├── docs └── api │ ├── overview.html │ └── stylesheet.css ├── gradle.properties ├── gradle ├── javadoc.gradle ├── libs.versions.toml ├── releaser.gradle ├── setup.gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── reactor-adapter └── src │ ├── main │ └── java │ │ └── reactor │ │ └── adapter │ │ ├── akka │ │ └── ActorScheduler.java │ │ └── rxjava │ │ ├── RxJava2Adapter.java │ │ ├── RxJava2Scheduler.java │ │ ├── RxJava2Schedulers.java │ │ ├── RxJava3Adapter.java │ │ ├── RxJava3Scheduler.java │ │ └── package-info.java │ └── test │ ├── java │ └── reactor │ │ └── adapter │ │ ├── akka │ │ └── ActorAdapterTest.java │ │ └── rxjava │ │ ├── RxJava2AdapterTest.java │ │ ├── RxJava2SchedulersTest.java │ │ └── RxJava3AdapterTest.java │ └── resources │ └── akka-streams.conf ├── reactor-extra └── src │ ├── main │ └── java │ │ └── reactor │ │ ├── bool │ │ └── BooleanUtils.java │ │ ├── cache │ │ ├── CacheFlux.java │ │ ├── CacheMono.java │ │ └── package-info.java │ │ ├── function │ │ ├── Consumer3.java │ │ ├── Consumer4.java │ │ ├── Consumer5.java │ │ ├── Consumer6.java │ │ ├── Consumer7.java │ │ ├── Consumer8.java │ │ ├── Function3.java │ │ ├── Function4.java │ │ ├── Function5.java │ │ ├── Function6.java │ │ ├── Function7.java │ │ ├── Function8.java │ │ ├── Predicate3.java │ │ ├── Predicate4.java │ │ ├── Predicate5.java │ │ ├── Predicate6.java │ │ ├── Predicate7.java │ │ ├── Predicate8.java │ │ └── TupleUtils.java │ │ ├── math │ │ ├── MathFlux.java │ │ ├── MathSubscriber.java │ │ ├── MonoAverageBigDecimal.java │ │ ├── MonoAverageBigInteger.java │ │ ├── MonoAverageDouble.java │ │ ├── MonoAverageFloat.java │ │ ├── MonoFromFluxOperator.java │ │ ├── MonoMinMax.java │ │ ├── MonoSumBigDecimal.java │ │ ├── MonoSumBigInteger.java │ │ ├── MonoSumDouble.java │ │ ├── MonoSumFloat.java │ │ ├── MonoSumInt.java │ │ └── MonoSumLong.java │ │ ├── retry │ │ ├── AbstractRetry.java │ │ ├── Backoff.java │ │ ├── BackoffDelay.java │ │ ├── DefaultContext.java │ │ ├── DefaultRepeat.java │ │ ├── DefaultRetry.java │ │ ├── IterationContext.java │ │ ├── Jitter.java │ │ ├── RandomJitter.java │ │ ├── Repeat.java │ │ ├── RepeatContext.java │ │ ├── Retry.java │ │ ├── RetryContext.java │ │ └── RetryExhaustedException.java │ │ ├── scheduler │ │ ├── clock │ │ │ └── SchedulerClock.java │ │ └── forkjoin │ │ │ └── ForkJoinPoolScheduler.java │ │ └── swing │ │ ├── SwingScheduler.java │ │ └── SwtScheduler.java │ └── test │ └── java │ └── reactor │ ├── bool │ └── BooleanUtilsTest.java │ ├── cache │ ├── CacheFluxTest.java │ └── CacheMonoTest.java │ ├── function │ └── TupleUtilsTest.java │ ├── math │ └── ReactorMathTests.java │ ├── retry │ ├── AbstractRetryTest.java │ ├── BackoffDelayTest.java │ ├── BackoffTest.java │ ├── DefaultRepeatTest.java │ ├── DefaultRetryTest.java │ ├── RandomJitterTest.java │ ├── RepeatTests.java │ ├── RetryTestUtils.java │ └── RetryTests.java │ ├── scheduler │ ├── clock │ │ └── SchedulerClockTest.java │ └── forkjoin │ │ ├── AbstractSchedulerTest.java │ │ └── ForkJoinPoolSchedulerTest.java │ └── swing │ ├── SwingAdapterTest.java │ └── SwtAdapterTest.java └── settings.gradle /.github/dco.yml: -------------------------------------------------------------------------------- 1 | require: 2 | members: false 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gradle 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | assignees: 9 | - violetagg 10 | target-branch: "3.5.x" 11 | labels: 12 | - type/dependency-upgrade 13 | ignore: 14 | # Don't update Reactor projects 15 | - dependency-name: io.projectreactor:* 16 | # JSR305 backport is fixed to last version with annotations (3.0.1) 17 | - dependency-name: "com.google.code.findbugs:jsr305" 18 | # Versions > 4 require JDK11+ 19 | - dependency-name: org.mockito:mockito-core 20 | versions: 21 | - "[5.a, 6]" 22 | # Versions > 6 require JDK17+ 23 | - dependency-name: biz.aQute.bnd.builder 24 | versions: 25 | - "[7.a, 8]" 26 | - dependency-name: org.slf4j:* 27 | versions: 28 | - "[2.a, 3]" 29 | - dependency-name: ch.qos.logback:logback-classic 30 | versions: 31 | - "[1.3.a, 1.6]" 32 | # artifactory: don't upgrade to v5 33 | - dependency-name: "com.jfrog.artifactory" 34 | versions: 35 | - ">= 5.0.a" 36 | # TestNG: 7.6+ doesn't support JDK8 anymore 37 | - dependency-name: "org.testng:testng" 38 | versions: 39 | - ">= 7.6.a" 40 | rebase-strategy: disabled 41 | - package-ecosystem: github-actions 42 | directory: "/" 43 | schedule: 44 | interval: daily 45 | open-pull-requests-limit: 10 46 | assignees: 47 | - violetagg 48 | target-branch: "3.5.x" 49 | labels: 50 | - type/dependency-upgrade 51 | rebase-strategy: disabled 52 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | exclude: 3 | labels: 4 | - ignore-changelog 5 | - type/chore 6 | - type/dependency-upgrade 7 | categories: 8 | - title: ":warning: Update considerations and deprecations" 9 | labels: 10 | - "warn/api-change" 11 | - "warn/behavior-change" 12 | - "warn/blocker" 13 | - "warn/deprecation" 14 | - "warn/regression" 15 | - title: ":sparkles: New features and improvements" 16 | labels: 17 | - "type/enhancement" 18 | - title: ":lady_beetle: Bug fixes" 19 | labels: 20 | - "type/bug" 21 | - title: ":book: Documentation" 22 | labels: 23 | - "type/documentation" 24 | - title: ":question: Other Changes" 25 | labels: 26 | - "*" 27 | -------------------------------------------------------------------------------- /.github/workflows/pr-checks.yml: -------------------------------------------------------------------------------- 1 | name: PR Checks 2 | on: 3 | pull_request: {} 4 | permissions: read-all 5 | jobs: 6 | checks: 7 | name: checks 8 | runs-on: ubuntu-22.04 9 | steps: 10 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 11 | - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 12 | with: 13 | distribution: 'temurin' 14 | java-version: 8 15 | - name: Setup Gradle 16 | uses: gradle/actions/setup-gradle@8379f6a1328ee0e06e2bb424dadb7b159856a326 17 | - name: gradle 18 | run: ./gradlew check javadoc 19 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: publish 2 | on: 3 | push: 4 | branches: # For branches, better to list them explicitly than regexp include 5 | - main 6 | - 3.5.x 7 | - 3.4.x 8 | permissions: read-all 9 | jobs: 10 | # General job notes: we DON'T want to cancel any previous runs, especially in the case of a "back to snapshots" build right after a release push 11 | # We specify the ubuntu version to minimize the chances we have to deal with a migration during a release 12 | prepare: 13 | # Notes on prepare: this job has no access to secrets, only github token. As a result, all non-core actions are centralized here 14 | # This includes the tagging and drafting of release notes. Still, when possible we favor plain run of gradle tasks 15 | name: prepare 16 | runs-on: ubuntu-22.04 17 | outputs: 18 | versionType: ${{ steps.version.outputs.versionType }} 19 | fullVersion: ${{ steps.version.outputs.fullVersion }} 20 | steps: 21 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 22 | - name: setup java 23 | uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # tag=v3 24 | with: 25 | distribution: 'temurin' 26 | java-version: 8 27 | - name: interpret version 28 | id: version 29 | #we only run the qualifyVersionGha task so that no other console printing can hijack this step's output 30 | #output: versionType, fullVersion 31 | #fails if versionType is BAD, which interrupts the workflow 32 | run: ./gradlew qualifyVersionGha 33 | - name: run checks 34 | id: checks 35 | run: ./gradlew check 36 | 37 | #deploy the snapshot artifacts to Artifactory 38 | deploySnapshot: 39 | name: deploySnapshot 40 | runs-on: ubuntu-22.04 41 | needs: prepare 42 | if: needs.prepare.outputs.versionType == 'SNAPSHOT' 43 | environment: snapshots 44 | steps: 45 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 46 | - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # tag=v3 47 | with: 48 | distribution: 'temurin' 49 | java-version: 8 50 | - name: deploy 51 | env: 52 | ORG_GRADLE_PROJECT_artifactory_publish_username: ${{secrets.ARTIFACTORY_SNAPSHOT_USERNAME}} 53 | ORG_GRADLE_PROJECT_artifactory_publish_password: ${{secrets.ARTIFACTORY_PASSWORD}} 54 | run: | 55 | ./gradlew assemble artifactoryPublish -Partifactory_publish_contextUrl=https://repo.spring.io -Partifactory_publish_repoKey=libs-snapshot-local 56 | 57 | #sign the milestone artifacts and deploy them to Artifactory 58 | deployMilestone: 59 | name: deployMilestone 60 | runs-on: ubuntu-22.04 61 | needs: prepare 62 | if: needs.prepare.outputs.versionType == 'MILESTONE' 63 | environment: releases 64 | steps: 65 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 66 | - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # tag=v3 67 | with: 68 | distribution: 'temurin' 69 | java-version: 8 70 | - name: deploy 71 | env: 72 | ORG_GRADLE_PROJECT_artifactory_publish_username: ${{secrets.ARTIFACTORY_USERNAME}} 73 | ORG_GRADLE_PROJECT_artifactory_publish_password: ${{secrets.ARTIFACTORY_PASSWORD}} 74 | ORG_GRADLE_PROJECT_signingKey: ${{secrets.SIGNING_KEY}} 75 | ORG_GRADLE_PROJECT_signingPassword: ${{secrets.SIGNING_PASSPHRASE}} 76 | run: | 77 | ./gradlew assemble sign artifactoryPublish -Partifactory_publish_contextUrl=https://repo.spring.io -Partifactory_publish_repoKey=libs-milestone-local 78 | 79 | #sign the release artifacts and deploy them to Artifactory 80 | deployRelease: 81 | name: deployRelease 82 | runs-on: ubuntu-22.04 83 | needs: prepare 84 | if: needs.prepare.outputs.versionType == 'RELEASE' 85 | environment: releases 86 | steps: 87 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 88 | - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # tag=v3 89 | with: 90 | distribution: 'temurin' 91 | java-version: 8 92 | - name: deploy 93 | env: 94 | ORG_GRADLE_PROJECT_artifactory_publish_username: ${{secrets.ARTIFACTORY_USERNAME}} 95 | ORG_GRADLE_PROJECT_artifactory_publish_password: ${{secrets.ARTIFACTORY_PASSWORD}} 96 | ORG_GRADLE_PROJECT_signingKey: ${{secrets.SIGNING_KEY}} 97 | ORG_GRADLE_PROJECT_signingPassword: ${{secrets.SIGNING_PASSPHRASE}} 98 | ORG_GRADLE_PROJECT_sonatypeUsername: ${{secrets.SONATYPE_USERNAME}} 99 | ORG_GRADLE_PROJECT_sonatypePassword: ${{secrets.SONATYPE_PASSWORD}} 100 | run: | 101 | ./gradlew assemble sign artifactoryPublish -Partifactory_publish_contextUrl=https://repo.spring.io -Partifactory_publish_repoKey=libs-release-local publishMavenJavaPublicationToSonatypeRepository 102 | - name: Stage the release 103 | env: 104 | ORG_GRADLE_PROJECT_sonatypeUsername: ${{secrets.SONATYPE_USERNAME}} 105 | ORG_GRADLE_PROJECT_sonatypePassword: ${{secrets.SONATYPE_PASSWORD}} 106 | run: | 107 | GRADLE_PUBLISH_MAVEN_AUTHORIZATION=$(echo "${ORG_GRADLE_PROJECT_sonatypeUsername}:${ORG_GRADLE_PROJECT_sonatypePassword}" | base64) 108 | echo "Searching for opened repository..." 109 | REPOSITORY_RESPONSE=$(curl -s -X GET \ 110 | -H "Authorization: Bearer ${GRADLE_PUBLISH_MAVEN_AUTHORIZATION}" \ 111 | "https://ossrh-staging-api.central.sonatype.com/manual/search/repositories?state=open") 112 | REPOSITORY_KEY=$(echo "${REPOSITORY_RESPONSE}" | grep -o '"key":"[^"]*"' | head -1 | cut -d':' -f2 | tr -d '"') 113 | echo "Triggering uploading..." 114 | curl -s -X POST \ 115 | -H "Authorization: Bearer ${GRADLE_PUBLISH_MAVEN_AUTHORIZATION}" \ 116 | "https://ossrh-staging-api.central.sonatype.com/manual/upload/repository/${REPOSITORY_KEY}?publishing_type=user_managed" 117 | 118 | tagMilestone: 119 | name: Tag milestone 120 | needs: [ prepare, deployMilestone ] 121 | runs-on: ubuntu-22.04 122 | permissions: 123 | contents: write 124 | steps: 125 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 126 | - name: tag 127 | run: | 128 | git config --local user.name 'reactorbot' 129 | git config --local user.email '32325210+reactorbot@users.noreply.github.com' 130 | git tag -m "Release milestone ${{ needs.prepare.outputs.fullVersion }}" v${{ needs.prepare.outputs.fullVersion }} ${{ github.sha }} 131 | git push --tags 132 | 133 | tagRelease: 134 | name: Tag release 135 | needs: [ prepare, deployRelease ] 136 | runs-on: ubuntu-22.04 137 | permissions: 138 | contents: write 139 | steps: 140 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 141 | - name: tag 142 | run: | 143 | git config --local user.name 'reactorbot' 144 | git config --local user.email '32325210+reactorbot@users.noreply.github.com' 145 | git tag -m "Release version ${{ needs.prepare.outputs.fullVersion }}" v${{ needs.prepare.outputs.fullVersion }} ${{ github.sha }} 146 | git push --tags 147 | 148 | # For Gradle configuration of signing, see https://docs.gradle.org/current/userguide/signing_plugin.html#sec:in-memory-keys 149 | # publishMavenJavaPublicationToSonatypeRepository only sends to a staging repository -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | reactor-pylon/src/main/resources/public 3 | .DS_Store 4 | bin 5 | build 6 | .classpath 7 | .eclipse 8 | .gradle 9 | .project 10 | .settings 11 | out 12 | *.log 13 | .sass-cache 14 | *.i* 15 | *.java.hsp 16 | deps.txt 17 | line_feed.txt 18 | *.index 19 | *.data 20 | classes 21 | exportToHtml 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Reactor Addons 2 | 3 | [![CI on GHA](https://github.com/reactor/reactor-addons/actions/workflows/publish.yml/badge.svg)](https://github.com/reactor/reactor-addons/actions/workflows/publish.yml) 4 | 5 | [![Join the chat at https://gitter.im/reactor/reactor](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/reactor/reactor?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 6 | 7 | [![Latest addons](https://maven-badges.herokuapp.com/maven-central/io.projectreactor.addons/reactor-extra/badge.svg?style=plastic)](https://mvnrepository.com/artifact/io.projectreactor.addons/reactor-extra) 8 | 9 | # Addons List 10 | 11 | # reactor-adapter 12 | 13 | Bridge to RxJava 2 Observable, Completable, Flowable, Single, Maybe, Scheduler, and also SWT Scheduler, Akka Scheduler ... 14 | 15 | # reactor-extra 16 | 17 | Extra operations and processors for Reactor Core including mathematical operations to compute sum, average, min or max from numerical sources. 18 | 19 | # Contributing an Add-on 20 | 21 | ### Build instructions 22 | 23 | `Reactor` uses a Gradle-based build system. Building the code yourself should be a straightforward case of: 24 | 25 | git clone git@github.com:reactor/reactor-addons.git 26 | cd reactor-addons 27 | ./gradlew test 28 | 29 | This should cause the submodules to be compiled and the tests to be run. To install these artifacts to your local Maven repo, use the handly Gradle Maven plugin: 30 | 31 | ./gradlew install 32 | 33 | ### Maven Artifacts 34 | 35 | Snapshot and pre-release Maven artifacts are provided in the SpringSource snapshot repositories. 36 | To add this repo to your Gradle build, specify the URL like the following: 37 | 38 | ext { 39 | reactorAddonsVersion = '3.6.0-RC1' 40 | } 41 | 42 | repositories { 43 | //maven { url 'https://repo.spring.io/release' } 44 | maven { url 'https://repo.spring.io/milestone' } 45 | //maven { url 'https://repo.spring.io/snapshot' } 46 | mavenCentral() 47 | } 48 | 49 | dependencies { 50 | // Reactor Adapter (RxJava2, Akka Actors scheduler and more) 51 | compile "io.projectreactor.addons:reactor-adapter:$reactorAddonsVersion" 52 | } 53 | 54 | 55 | ## Documentation 56 | 57 | * [Guides](https://projectreactor.io/docs) (Notably `reactor-core` reference guide which 58 | contains a section [about testing](https://projectreactor.io/docs/core/release/reference/docs/index.html#testing)) 59 | * [Reactive Streams](https://www.reactive-streams.org/) 60 | 61 | ## Community / Support 62 | 63 | * [GitHub Issues](https://github.com/reactor/reactor-addons/issues) 64 | 65 | ## License 66 | 67 | Reactor is [Apache 2.0 licensed](https://www.apache.org/licenses/LICENSE-2.0.html). 68 | -------------------------------------------------------------------------------- /docs/api/overview.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | This document is the API specification for the Reactor Addons libraries, a set of 8 | artifacts that complement the Reactor-Core library. 9 | 10 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | version=3.6.0-SNAPSHOT 2 | -------------------------------------------------------------------------------- /gradle/javadoc.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 | javadoc { 18 | dependsOn jar 19 | group = "documentation" 20 | description = "Generates Javadoc API documentation." 21 | 22 | title = "$project.name $project.version" 23 | options.addStringOption('charSet', 'UTF-8') 24 | options.author = true 25 | options.header = "Reactor Addons: $project.name" 26 | options.overview = "$rootDir/docs/api/overview.html" 27 | options.stylesheetFile = file("$rootDir/docs/api/stylesheet.css") 28 | 29 | options.memberLevel = JavadocMemberLevel.PROTECTED 30 | 31 | options.links(rootProject.ext.javadocLinks) 32 | 33 | options.tags = [ "apiNote:a:API Note:", "implSpec:a:Implementation Requirements:", 34 | "implNote:a:Implementation Note:" ] 35 | 36 | maxMemory = "1024m" 37 | destinationDir = new File(project.buildDir, "docs/javadoc") 38 | 39 | 40 | if (JavaVersion.current().isJava8Compatible()) { 41 | options.addStringOption('Xdoclint:none', '-quiet') 42 | } 43 | 44 | source project.sourceSets.main.allJava 45 | doFirst { 46 | classpath = files(project.sourceSets.main.compileClasspath) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | reactorCore = "3.7.0-SNAPSHOT" 3 | # Other shared versions 4 | reactiveStreams = "1.0.4" 5 | swt = "4.5.2" 6 | 7 | [libraries] 8 | reactorCore = { module = "io.projectreactor:reactor-core", version.ref = "reactorCore" } 9 | reactorTest = { module = "io.projectreactor:reactor-test", version.ref = "reactorCore" } 10 | akkaActor = "com.typesafe.akka:akka-actor_2.11:2.4.10" 11 | assertj = "org.assertj:assertj-core:3.27.3" 12 | hamcrest = "org.hamcrest:hamcrest-library:3.0" 13 | jsr305 = "com.google.code.findbugs:jsr305:3.0.1" 14 | junit4 = "junit:junit:4.13.2" 15 | logback = "ch.qos.logback:logback-classic:1.2.13" 16 | mockito = "org.mockito:mockito-core:4.11.0" 17 | quickTheories = "org.quicktheories:quicktheories:0.25" 18 | reactiveStreams-tck = { module = "org.reactivestreams:reactive-streams-tck", version.ref = "reactiveStreams" } 19 | rxJava2 = "io.reactivex.rxjava2:rxjava:2.2.21" 20 | rxJava3 = "io.reactivex.rxjava3:rxjava:3.1.10" 21 | slf4j = "org.slf4j:slf4j-api:1.7.36" 22 | swt-windows = { module = "org.eclipse.swt:org.eclipse.swt.win32.win32.x86", version.ref = "swt" } 23 | swt-windows64 = { module = "org.eclipse.swt:org.eclipse.swt.win32.win32.x86_64", version.ref = "swt" } 24 | swt-linux = { module = "org.eclipse.swt:org.eclipse.swt.gtk.linux.x86", version.ref = "swt" } 25 | swt-linux64 = { module = "org.eclipse.swt:org.eclipse.swt.gtk.linux.x86_64", version.ref = "swt" } 26 | swt-mac = { module = "org.eclipse.swt:org.eclipse.swt.cocoa.macosx.x86_64", version.ref = "swt" } 27 | testNg = "org.testng:testng:7.5.1" 28 | 29 | [plugins] 30 | artifactory = { id = "com.jfrog.artifactory", version = "4.33.1" } 31 | bnd = { id = "biz.aQute.bnd.builder", version = "6.4.0" } 32 | -------------------------------------------------------------------------------- /gradle/releaser.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 | import java.time.ZoneOffset 18 | import java.time.ZonedDateTime 19 | import java.time.temporal.ChronoField 20 | 21 | if (rootProject.hasProperty("releaserDryRun") && rootProject.findProperty("releaserDryRun") != "false") { 22 | println "Adding MavenLocal() for benefit of releaser dry run" 23 | rootProject.repositories { 24 | mavenLocal() 25 | } 26 | } 27 | 28 | /** 29 | * return a specific property value, assuming all the provided projects have it with the 30 | * same value, or throw if multiple values are found 31 | */ 32 | private static def getUniquePropertyPerProject(Set projects, String property) { 33 | //"multimap": dictionary of lists 34 | def props = [:].withDefault {[]} 35 | projects.each { props.get(it.findProperty(property)).add(it.name) } 36 | 37 | if (props.size() != 1) { 38 | throw new InvalidUserDataException("build defines multiple values for property `${property}`: ${props}") 39 | } 40 | return props.keySet().find() 41 | } 42 | 43 | //NOTE: this task is intended for rootProject with submodules 44 | task groupId(group: "releaser helpers", description: "output the group id of submodules, checking there is only one") { 45 | doLast { 46 | println getUniquePropertyPerProject(rootProject.subprojects, "group") 47 | } 48 | } 49 | 50 | 51 | task copyReadme(type: Copy, group: "releaser helpers", description: "copies the README in preparation for search and replace") { 52 | from(rootProject.rootDir) { 53 | include "README.md" 54 | } 55 | into rootProject.buildDir 56 | } 57 | 58 | task bumpVersionsInReadme(type: Copy, group: "releaser helpers", description: "replaces versions in README") { 59 | def oldVersion = rootProject.findProperty("oldVersion") 60 | def currentVersion = rootProject.findProperty("currentVersion") 61 | def nextVersion = rootProject.findProperty("nextVersion") 62 | def oldSnapshot = currentVersion + "-SNAPSHOT" 63 | 64 | onlyIf { oldVersion != null && currentVersion != null && nextVersion != null } 65 | dependsOn copyReadme 66 | 67 | doLast { 68 | println "Will replace $oldVersion with $currentVersion and $oldSnapshot with $nextVersion" 69 | } 70 | from(rootProject.buildDir) { 71 | include 'README.md' 72 | } 73 | into rootProject.rootDir 74 | filter { line -> line 75 | .replace(oldVersion, currentVersion) 76 | .replace(oldSnapshot, nextVersion) 77 | } 78 | } 79 | 80 | String getOrGenerateBuildNumber() { 81 | if (project.hasProperty("buildNumber")) { 82 | return project.findProperty("buildNumber") 83 | } 84 | def ciNumber = System.getenv("GITHUB_RUN_NUMBER") 85 | if (ciNumber != null) { 86 | return ciNumber 87 | } 88 | ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC) 89 | long secondsInDay = now.toEpochSecond() - ZonedDateTime.now(ZoneOffset.UTC).withSecond(0).withHour(0).withMinute(0).toEpochSecond() 90 | String buildNumber = "$version-${now.get(ChronoField.YEAR)}${now.get(ChronoField.MONTH_OF_YEAR).toString().padLeft(2,'0')}${now.get(ChronoField.DAY_OF_MONTH).toString().padLeft(2,'0')}@${secondsInDay}" 91 | println "No buildNumber set, generated: $buildNumber" 92 | return buildNumber 93 | } 94 | 95 | task printBuildNumber() { 96 | doLast { 97 | println getOrGenerateBuildNumber() 98 | } 99 | } 100 | 101 | if (rootProject.hasProperty("artifactory_publish_password")) { 102 | configure(rootProject) { p -> 103 | apply plugin: "com.jfrog.artifactory" 104 | def buildNumber = getOrGenerateBuildNumber() 105 | artifactory { 106 | contextUrl = "${artifactory_publish_contextUrl}" 107 | publish { 108 | repository { 109 | repoKey = "${artifactory_publish_repoKey}" 110 | username = "${artifactory_publish_username}" 111 | password = "${artifactory_publish_password}" 112 | } 113 | } 114 | clientConfig.setIncludeEnvVars(false) 115 | clientConfig.info.setBuildName('Reactor - Addons') 116 | clientConfig.info.setBuildNumber(buildNumber) 117 | if (System.getenv("GITHUB_ACTIONS") == "true") { 118 | clientConfig.info.setBuildUrl(System.getenv("GITHUB_SERVER_URL") + "/" + System.getenv("GITHUB_REPOSITORY") + "/actions/runs/" + System.getenv("GITHUB_RUN_ID")) 119 | clientConfig.info.setVcsRevision(System.getenv("GITHUB_SHA")) 120 | } 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /gradle/setup.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2025 VMware Inc. or its affiliates, All Rights Reserved. 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 | import org.gradle.util.VersionNumber 18 | 19 | //NOTE: this file is intended to be applied per subproject 20 | 21 | apply plugin: 'maven-publish' 22 | //we also conditionally apply artifactory and signing plugins below 23 | 24 | jar { 25 | manifest.attributes["Created-By"] = "${System.getProperty("java.version")} (${System.getProperty("java.specification.vendor")})" 26 | manifest.attributes["Implementation-Title"] = project.name 27 | manifest.attributes["Implementation-Version"] = project.version 28 | } 29 | 30 | task sourcesJar(type: Jar) { 31 | archiveClassifier.set('sources') 32 | from sourceSets.main.allSource 33 | } 34 | 35 | task javadocJar(type: Jar) { 36 | archiveClassifier.set('javadoc') 37 | from javadoc 38 | } 39 | 40 | static def qualifyVersion(String v) { 41 | def versionNumber = VersionNumber.parse(v) 42 | 43 | if (versionNumber == VersionNumber.UNKNOWN) return "BAD"; 44 | 45 | if (versionNumber.qualifier == null || versionNumber.qualifier.size() == 0) return "RELEASE" //new scheme 46 | if (versionNumber.qualifier == "RELEASE") return "RELEASE" //old scheme 47 | if (versionNumber.qualifier.matches("(?:M|RC)\\d+")) return "MILESTONE" 48 | if (versionNumber.qualifier == "SNAPSHOT" || versionNumber.qualifier == "BUILD-SNAPSHOT") return "SNAPSHOT" 49 | 50 | return "BAD" 51 | } 52 | 53 | static def outputToGha(String versionType, String fullVersion) { 54 | def ghaFilename = System.getenv("GITHUB_OUTPUT") 55 | if (ghaFilename == null) { 56 | println "::set-output name=versionType::$versionType" 57 | println "::set-output name=fullVersion::$fullVersion" 58 | } 59 | else { 60 | println "using GITHUB_OUTPUT file" 61 | def ghaFile = new File(ghaFilename) 62 | ghaFile.withWriterAppend { 63 | it.newLine() 64 | it.append("versionType=$versionType") 65 | it.newLine() 66 | it.append("fullVersion=$fullVersion") 67 | } 68 | } 69 | } 70 | 71 | task qualifyVersionGha() { 72 | doLast { 73 | def versionType = qualifyVersion("$version") 74 | //we ensure that if at least _one_ submodule version is BAD, we only output versionType=BAD + job fails 75 | if (versionType == "BAD") { 76 | outputToGha(versionType, version) 77 | println "::error ::Unable to parse $version to a VersionNumber with recognizable qualifier" 78 | throw new TaskExecutionException(tasks.getByName("qualifyVersionGha"), new IllegalArgumentException("Unable to parse $version to a VersionNumber with recognizable qualifier")) 79 | } 80 | println "Recognized $version as $versionType" 81 | 82 | //only output the versionType and fullVersion for the main artifact 83 | if (project.name == 'reactor-adapter') { 84 | outputToGha(versionType, version) 85 | } 86 | } 87 | } 88 | 89 | publishing { 90 | repositories { 91 | maven { 92 | name = "mock" 93 | url = "${rootProject.buildDir}/mockRepo" 94 | } 95 | if (qualifyVersion("$version") == "RELEASE") { 96 | maven { 97 | name = "sonatype" 98 | url = "https://ossrh-staging-api.central.sonatype.com/service/local/staging/deploy/maven2/" 99 | credentials { 100 | username findProperty("sonatypeUsername") 101 | password findProperty("sonatypePassword") 102 | } 103 | } 104 | } 105 | } 106 | 107 | publications { 108 | mavenJava(MavenPublication) { 109 | from components.java 110 | artifact sourcesJar 111 | artifact javadocJar 112 | //consider adding extra artifacts here, conditionally on submodule's name and perhaps in an afterEvaluate block 113 | 114 | pom { 115 | afterEvaluate { 116 | name = project.shortDescription 117 | description = project.description 118 | } 119 | packaging = 'jar' //if not explicitly set, end up as `pom` in output. omitted in output if set as `jar`... 120 | url = 'https://github.com/reactor/reactor-addons' 121 | organization { 122 | name = 'reactor' 123 | url = 'https://github.com/reactor' 124 | } 125 | licenses { 126 | license { 127 | name = 'Apache License, Version 2.0' 128 | url = 'https://www.apache.org/licenses/LICENSE-2.0.txt' 129 | distribution = 'repo' 130 | } 131 | } 132 | scm { 133 | url = 'https://github.com/reactor/reactor-addons' 134 | connection = 'scm:git:git://github.com/reactor/reactor-addons' 135 | developerConnection = 'scm:git:git://github.com/reactor/reactor-addons' 136 | } 137 | developers { 138 | developer { 139 | id = 'simonbasle' 140 | name = 'Simon Baslé' 141 | email = 'sbasle@vmware.com' 142 | } 143 | } 144 | issueManagement { 145 | system = "GitHub Issues" 146 | url = "https://github.com/reactor/reactor-addons/issues" 147 | } 148 | //NB: only the direct dependencies are published in the pom (ie. only reactive-streams) 149 | //optional dependencies are not published anymore, see https://github.com/reactor/reactor-core/issues/2262 150 | } 151 | } 152 | } 153 | } 154 | 155 | if (rootProject.hasProperty("artifactory_publish_password")) { 156 | apply plugin: "com.jfrog.artifactory" 157 | 158 | artifactoryPublish { 159 | publications(publishing.publications.mavenJava) 160 | } 161 | } 162 | 163 | if (qualifyVersion("$version") in ["RELEASE", "MILESTONE"] || rootProject.hasProperty("forceSigning")) { 164 | apply plugin: 'signing' 165 | 166 | signing { 167 | //requiring signature if there is a publish task that is not to MavenLocal 168 | required { gradle.taskGraph.allTasks.any { it.name.toLowerCase().contains("publish") && !it.name.contains("MavenLocal") } } 169 | def signingKey = findProperty("signingKey") 170 | def signingPassword = findProperty("signingPassword") 171 | 172 | useInMemoryPgpKeys(signingKey, signingPassword) 173 | 174 | afterEvaluate { 175 | sign publishing.publications.mavenJava 176 | } 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactor/reactor-addons/1321ee76f2b7e2916adfacd3b514e6080f19d865/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | # Determine the Java command to use to start the JVM. 86 | if [ -n "$JAVA_HOME" ] ; then 87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 88 | # IBM's JDK on AIX uses strange locations for the executables 89 | JAVACMD="$JAVA_HOME/jre/sh/java" 90 | else 91 | JAVACMD="$JAVA_HOME/bin/java" 92 | fi 93 | if [ ! -x "$JAVACMD" ] ; then 94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 95 | 96 | Please set the JAVA_HOME variable in your environment to match the 97 | location of your Java installation." 98 | fi 99 | else 100 | JAVACMD="java" 101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 102 | 103 | Please set the JAVA_HOME variable in your environment to match the 104 | location of your Java installation." 105 | fi 106 | 107 | # Increase the maximum file descriptors if we can. 108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 109 | MAX_FD_LIMIT=`ulimit -H -n` 110 | if [ $? -eq 0 ] ; then 111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 112 | MAX_FD="$MAX_FD_LIMIT" 113 | fi 114 | ulimit -n $MAX_FD 115 | if [ $? -ne 0 ] ; then 116 | warn "Could not set maximum file descriptor limit: $MAX_FD" 117 | fi 118 | else 119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 120 | fi 121 | fi 122 | 123 | # For Darwin, add options to specify how the application appears in the dock 124 | if $darwin; then 125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 126 | fi 127 | 128 | # For Cygwin or MSYS, switch paths to Windows format before running java 129 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 132 | JAVACMD=`cygpath --unix "$JAVACMD"` 133 | 134 | # We build the pattern for arguments to be converted via cygpath 135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 136 | SEP="" 137 | for dir in $ROOTDIRSRAW ; do 138 | ROOTDIRS="$ROOTDIRS$SEP$dir" 139 | SEP="|" 140 | done 141 | OURCYGPATTERN="(^($ROOTDIRS))" 142 | # Add a user-defined pattern to the cygpath arguments 143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 145 | fi 146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 147 | i=0 148 | for arg in "$@" ; do 149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 151 | 152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 154 | else 155 | eval `echo args$i`="\"$arg\"" 156 | fi 157 | i=$((i+1)) 158 | done 159 | case $i in 160 | (0) set -- ;; 161 | (1) set -- "$args0" ;; 162 | (2) set -- "$args0" "$args1" ;; 163 | (3) set -- "$args0" "$args1" "$args2" ;; 164 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 165 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 166 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 167 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 168 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 169 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 170 | esac 171 | fi 172 | 173 | # Escape application args 174 | save () { 175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 176 | echo " " 177 | } 178 | APP_ARGS=$(save "$@") 179 | 180 | # Collect all arguments for the java command, following the shell quoting and substitution rules 181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 182 | 183 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 184 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 185 | cd "$(dirname "$0")" 186 | fi 187 | 188 | exec "$JAVACMD" "$@" 189 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 33 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 34 | 35 | @rem Find java.exe 36 | if defined JAVA_HOME goto findJavaFromJavaHome 37 | 38 | set JAVA_EXE=java.exe 39 | %JAVA_EXE% -version >NUL 2>&1 40 | if "%ERRORLEVEL%" == "0" goto init 41 | 42 | echo. 43 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 44 | echo. 45 | echo Please set the JAVA_HOME variable in your environment to match the 46 | echo location of your Java installation. 47 | 48 | goto fail 49 | 50 | :findJavaFromJavaHome 51 | set JAVA_HOME=%JAVA_HOME:"=% 52 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 53 | 54 | if exist "%JAVA_EXE%" goto init 55 | 56 | echo. 57 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 58 | echo. 59 | echo Please set the JAVA_HOME variable in your environment to match the 60 | echo location of your Java installation. 61 | 62 | goto fail 63 | 64 | :init 65 | @rem Get command-line arguments, handling Windows variants 66 | 67 | if not "%OS%" == "Windows_NT" goto win9xME_args 68 | 69 | :win9xME_args 70 | @rem Slurp the command line arguments. 71 | set CMD_LINE_ARGS= 72 | set _SKIP=2 73 | 74 | :win9xME_args_slurp 75 | if "x%~1" == "x" goto execute 76 | 77 | set CMD_LINE_ARGS=%* 78 | 79 | :execute 80 | @rem Setup the command line 81 | 82 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 83 | 84 | @rem Execute Gradle 85 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 86 | 87 | :end 88 | @rem End local scope for the variables with windows NT shell 89 | if "%ERRORLEVEL%"=="0" goto mainEnd 90 | 91 | :fail 92 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 93 | rem the _cmd.exe /c_ return code! 94 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 95 | exit /b 1 96 | 97 | :mainEnd 98 | if "%OS%"=="Windows_NT" endlocal 99 | 100 | :omega 101 | -------------------------------------------------------------------------------- /reactor-adapter/src/main/java/reactor/adapter/akka/ActorScheduler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.adapter.akka; 18 | 19 | import java.util.HashSet; 20 | import java.util.Objects; 21 | import java.util.concurrent.atomic.AtomicBoolean; 22 | 23 | import akka.actor.ActorRef; 24 | import akka.actor.ActorSystem; 25 | import akka.actor.Props; 26 | import akka.actor.UntypedActor; 27 | import reactor.core.Disposable; 28 | import reactor.core.Exceptions; 29 | import reactor.core.publisher.Operators; 30 | import reactor.util.context.Context; 31 | 32 | /** 33 | * A Scheduler implementation that given an ActorSystem, creates a single Actor and 34 | * routes all Scheduler/Worker calls to it. 35 | */ 36 | public class ActorScheduler implements reactor.core.scheduler.Scheduler { 37 | 38 | /** 39 | * 40 | * @param system an {@link ActorSystem} 41 | * @return a new {@link reactor.core.scheduler.Scheduler} 42 | */ 43 | public static reactor.core.scheduler.Scheduler from(ActorSystem system) { 44 | Objects.requireNonNull(system, "system"); 45 | return new ActorScheduler(system.actorOf(Props.create(ActorExecutor.class))); 46 | } 47 | 48 | /** 49 | * 50 | * @param actorRef an {@link ActorRef} 51 | * @return a new {@link reactor.core.scheduler.Scheduler} 52 | */ 53 | public static reactor.core.scheduler.Scheduler from(ActorRef actorRef) { 54 | Objects.requireNonNull(actorRef, "actorRef"); 55 | return new ActorScheduler(actorRef); 56 | } 57 | 58 | final ActorRef actor; 59 | 60 | ActorScheduler(ActorRef actor) { 61 | this.actor = actor; 62 | } 63 | 64 | @Override 65 | public Disposable schedule(Runnable task) { 66 | DirectRunnable dr = new DirectRunnable(task); 67 | actor.tell(dr, ActorRef.noSender()); 68 | return dr; 69 | } 70 | 71 | @Override 72 | public Worker createWorker() { 73 | return new ActorWorker(actor); 74 | } 75 | 76 | 77 | static final class ActorWorker implements Worker { 78 | 79 | final ActorRef actor; 80 | 81 | HashSet tasks; 82 | 83 | public ActorWorker(ActorRef actor) { 84 | this.actor = actor; 85 | this.tasks = new HashSet<>(); 86 | } 87 | 88 | @Override 89 | public Disposable schedule(Runnable task) { 90 | WorkerRunnable wr = new WorkerRunnable(task, this); 91 | 92 | synchronized (this) { 93 | HashSet set = tasks; 94 | if (set == null) { 95 | throw Exceptions.failWithRejected(); 96 | } 97 | set.add(wr); 98 | } 99 | 100 | actor.tell(wr, ActorRef.noSender()); 101 | 102 | return wr; 103 | } 104 | 105 | @Override 106 | public void dispose() { 107 | HashSet set; 108 | 109 | synchronized (this) { 110 | set = tasks; 111 | tasks = null; 112 | } 113 | 114 | if (set != null) { 115 | for (WorkerRunnable wr : set) { 116 | wr.delete(); 117 | } 118 | } 119 | } 120 | 121 | void delete(WorkerRunnable run) { 122 | synchronized (this) { 123 | HashSet set = tasks; 124 | if (set == null) { 125 | return; 126 | } 127 | set.remove(run); 128 | } 129 | } 130 | } 131 | 132 | static final class DirectRunnable 133 | extends AtomicBoolean implements Runnable, Disposable { 134 | /** */ 135 | private static final long serialVersionUID = -8208677295345126172L; 136 | 137 | final Runnable run; 138 | 139 | public DirectRunnable(Runnable run) { 140 | this.run = run; 141 | } 142 | 143 | @Override 144 | public void run() { 145 | if (!get()) { 146 | run.run(); 147 | } 148 | } 149 | 150 | @Override 151 | public void dispose() { 152 | set(true); 153 | } 154 | } 155 | 156 | static final class WorkerRunnable 157 | extends AtomicBoolean implements Runnable, Disposable { 158 | /** */ 159 | private static final long serialVersionUID = -1760219254778525714L; 160 | 161 | final Runnable run; 162 | 163 | final ActorWorker parent; 164 | 165 | public WorkerRunnable(Runnable run, ActorWorker parent) { 166 | this.run = run; 167 | this.parent = parent; 168 | } 169 | 170 | @Override 171 | public void run() { 172 | if (!get()) { 173 | try { 174 | run.run(); 175 | } finally { 176 | if (compareAndSet(false, true)) { 177 | parent.delete(this); 178 | } 179 | } 180 | } 181 | } 182 | 183 | @Override 184 | public void dispose() { 185 | if (compareAndSet(false, true)) { 186 | parent.delete(this); 187 | } 188 | } 189 | 190 | public void delete() { 191 | set(true); 192 | } 193 | } 194 | 195 | static final class ActorExecutor extends UntypedActor { 196 | 197 | @Override 198 | public void onReceive(Object message) throws Exception { 199 | Runnable r = (Runnable)message; 200 | 201 | try { 202 | r.run(); 203 | } catch (Throwable ex) { 204 | Exceptions.throwIfFatal(ex); 205 | Operators.onErrorDropped(ex, Context.empty()); 206 | } 207 | } 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /reactor-adapter/src/main/java/reactor/adapter/rxjava/RxJava2Scheduler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.adapter.rxjava; 18 | 19 | import java.util.Objects; 20 | import java.util.concurrent.TimeUnit; 21 | 22 | import io.reactivex.disposables.Disposable; 23 | import reactor.core.scheduler.Scheduler; 24 | 25 | /** 26 | * Wraps an RxJava scheduler and exposes it as a Reactor-Core Scheduler. 27 | */ 28 | public final class RxJava2Scheduler implements Scheduler { 29 | 30 | /** 31 | * 32 | * @param scheduler an rxjava 2 {@link io.reactivex.Scheduler} 33 | * @return a new {@link Scheduler} 34 | */ 35 | public static Scheduler from(io.reactivex.Scheduler scheduler) { 36 | return new RxJava2Scheduler(scheduler); 37 | } 38 | 39 | final io.reactivex.Scheduler scheduler; 40 | 41 | RxJava2Scheduler(io.reactivex.Scheduler scheduler) { 42 | this.scheduler = Objects.requireNonNull(scheduler, "scheduler"); 43 | } 44 | 45 | @Override 46 | public reactor.core.Disposable schedule(Runnable task) { 47 | Disposable s = scheduler.scheduleDirect(task); 48 | return s::dispose; 49 | } 50 | 51 | @Override 52 | public reactor.core.Disposable schedule(Runnable task, long delay, TimeUnit unit) { 53 | Disposable s = scheduler.scheduleDirect(task, delay, unit); 54 | return s::dispose; 55 | } 56 | 57 | @Override 58 | public reactor.core.Disposable schedulePeriodically(Runnable task, long initialDelay, long period, TimeUnit unit) { 59 | Disposable s = 60 | scheduler.schedulePeriodicallyDirect(task, initialDelay, period, unit); 61 | return s::dispose; 62 | } 63 | 64 | @Override 65 | public Worker createWorker() { 66 | return new RxSchedulerWorker(scheduler.createWorker()); 67 | } 68 | 69 | static final class RxSchedulerWorker implements Worker { 70 | final io.reactivex.Scheduler.Worker w; 71 | 72 | RxSchedulerWorker(io.reactivex.Scheduler.Worker w) { 73 | this.w = w; 74 | } 75 | 76 | @Override 77 | public reactor.core.Disposable schedule(Runnable task) { 78 | Disposable s = w.schedule(task); 79 | return s::dispose; 80 | } 81 | 82 | @Override 83 | public void dispose() { 84 | w.dispose(); 85 | } 86 | 87 | @Override 88 | public reactor.core.Disposable schedule(Runnable task, long delay, TimeUnit unit) { 89 | Disposable s = w.schedule(task, delay, unit); 90 | return s::dispose; 91 | } 92 | 93 | @Override 94 | public reactor.core.Disposable schedulePeriodically(Runnable task, long initialDelay, long period, TimeUnit unit) { 95 | Disposable s = w.schedulePeriodically(task, initialDelay, period, unit); 96 | return s::dispose; 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /reactor-adapter/src/main/java/reactor/adapter/rxjava/RxJava3Scheduler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.adapter.rxjava; 18 | 19 | import java.util.Objects; 20 | import java.util.concurrent.TimeUnit; 21 | 22 | import reactor.adapter.rxjava.RxJava3Adapter.DisposableFromRxJava3Disposable; 23 | import reactor.core.scheduler.Scheduler; 24 | 25 | /** 26 | * Wraps an RxJava 3 Scheduler and exposes it as a Reactor-Core {@link Scheduler}. 27 | */ 28 | public final class RxJava3Scheduler implements Scheduler { 29 | 30 | boolean isDisposed = false; 31 | 32 | /** 33 | * Adapt an RxJava 3 {@link io.reactivex.rxjava3.core.Scheduler} into a Reactor {@link Scheduler}. 34 | * 35 | * @param scheduler an RxJava3 {@link io.reactivex.rxjava3.core.Scheduler} 36 | * 37 | * @return a wrapping {@link Scheduler} 38 | */ 39 | public static Scheduler from(io.reactivex.rxjava3.core.Scheduler scheduler) { 40 | return new RxJava3Scheduler(scheduler); 41 | } 42 | 43 | final io.reactivex.rxjava3.core.Scheduler scheduler; 44 | 45 | RxJava3Scheduler(io.reactivex.rxjava3.core.Scheduler scheduler) { 46 | this.scheduler = Objects.requireNonNull(scheduler, "scheduler"); 47 | } 48 | 49 | @Override 50 | public Worker createWorker() { 51 | return new RxSchedulerWorker(scheduler.createWorker()); 52 | } 53 | 54 | @Override 55 | public void dispose() { 56 | if (!isDisposed) { 57 | this.isDisposed = true; 58 | scheduler.shutdown(); 59 | } 60 | } 61 | 62 | @Override 63 | public boolean isDisposed() { 64 | return isDisposed; 65 | } 66 | 67 | @Override 68 | public void start() { 69 | scheduler.start(); 70 | } 71 | 72 | @Override 73 | public reactor.core.Disposable schedule(Runnable task) { 74 | return new DisposableFromRxJava3Disposable(scheduler.scheduleDirect(task)); 75 | } 76 | 77 | @Override 78 | public reactor.core.Disposable schedule(Runnable task, long delay, TimeUnit unit) { 79 | return new DisposableFromRxJava3Disposable(scheduler.scheduleDirect(task, 80 | delay, 81 | unit)); 82 | } 83 | 84 | @Override 85 | public reactor.core.Disposable schedulePeriodically(Runnable task, long initialDelay, 86 | long period, TimeUnit unit) { 87 | return new DisposableFromRxJava3Disposable(scheduler.schedulePeriodicallyDirect( 88 | task, 89 | initialDelay, 90 | period, 91 | unit)); 92 | } 93 | 94 | static final class RxSchedulerWorker implements Worker { 95 | 96 | final io.reactivex.rxjava3.core.Scheduler.Worker w; 97 | 98 | RxSchedulerWorker(io.reactivex.rxjava3.core.Scheduler.Worker w) { 99 | this.w = w; 100 | } 101 | 102 | @Override 103 | public void dispose() { 104 | w.dispose(); 105 | } 106 | 107 | @Override 108 | public boolean isDisposed() { 109 | return w.isDisposed(); 110 | } 111 | 112 | @Override 113 | public reactor.core.Disposable schedule(Runnable task) { 114 | return new DisposableFromRxJava3Disposable(w.schedule(task)); 115 | } 116 | 117 | @Override 118 | public reactor.core.Disposable schedule(Runnable task, long delay, 119 | TimeUnit unit) { 120 | return new DisposableFromRxJava3Disposable(w.schedule(task, delay, unit)); 121 | } 122 | 123 | @Override 124 | public reactor.core.Disposable schedulePeriodically(Runnable task, 125 | long initialDelay, long period, TimeUnit unit) { 126 | return new DisposableFromRxJava3Disposable(w.schedulePeriodically(task, initialDelay, period, unit)); 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /reactor-adapter/src/main/java/reactor/adapter/rxjava/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Adapters between RxJava classes (both 2.x and 3.x) and Reactor classes. 3 | */ 4 | @NonNullApi 5 | package reactor.adapter.rxjava; 6 | 7 | import reactor.util.annotation.NonNullApi; -------------------------------------------------------------------------------- /reactor-adapter/src/test/java/reactor/adapter/akka/ActorAdapterTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.adapter.akka; 18 | 19 | import akka.actor.ActorSystem; 20 | import com.typesafe.config.Config; 21 | import com.typesafe.config.ConfigFactory; 22 | import org.junit.Test; 23 | import reactor.core.publisher.Flux; 24 | import reactor.core.scheduler.Scheduler; 25 | import reactor.test.StepVerifier; 26 | 27 | /** 28 | * @author Stephane Maldini 29 | */ 30 | public class ActorAdapterTest { 31 | 32 | @Test 33 | public void normal() { 34 | Config cfg = 35 | ConfigFactory.parseResources(ActorAdapterTest.class, "/akka-streams.conf") 36 | .resolve(); 37 | 38 | Scheduler actorScheduler = ActorScheduler.from(ActorSystem.create("sys", cfg)); 39 | 40 | Flux actorFlux = Flux.range(0, 1_000_000) 41 | .publishOn(actorScheduler); 42 | 43 | StepVerifier.create(actorFlux) 44 | .expectNextCount(1_000_000) 45 | .expectComplete() 46 | .verify(); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /reactor-adapter/src/test/java/reactor/adapter/rxjava/RxJava2AdapterTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.adapter.rxjava; 18 | 19 | import java.io.IOException; 20 | import java.lang.reflect.Field; 21 | import java.util.NoSuchElementException; 22 | 23 | import io.reactivex.Completable; 24 | import io.reactivex.Flowable; 25 | import io.reactivex.Maybe; 26 | import io.reactivex.Single; 27 | import io.reactivex.internal.fuseable.QueueSubscription; 28 | import io.reactivex.observers.BaseTestConsumer; 29 | import io.reactivex.schedulers.Schedulers; 30 | import org.junit.Test; 31 | import reactor.core.Fuseable; 32 | import reactor.core.publisher.Flux; 33 | import reactor.core.publisher.Mono; 34 | import reactor.test.StepVerifier; 35 | 36 | import static org.junit.Assert.assertEquals; 37 | 38 | public class RxJava2AdapterTest { 39 | @Test 40 | public void flowableToFlux() { 41 | Flux f = Flowable.range(1, 10) 42 | .hide() 43 | .to(RxJava2Adapter::flowableToFlux); 44 | 45 | StepVerifier.create(f) 46 | .expectNext(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) 47 | .expectComplete() 48 | .verify(); 49 | } 50 | 51 | @Test 52 | public void scheduler() { 53 | Flux f = Flux.range(1, 10) 54 | .publishOn(RxJava2Scheduler.from(Schedulers.computation())); 55 | 56 | StepVerifier.create(f) 57 | .expectNext(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) 58 | .expectComplete() 59 | .verify(); 60 | } 61 | 62 | @Test 63 | public void flowableToFluxFused() { 64 | Flux f = Flowable.range(1, 10) 65 | .to(RxJava2Adapter::flowableToFlux); 66 | 67 | StepVerifier.create(f) 68 | .expectFusion() 69 | .expectNext(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) 70 | .expectComplete() 71 | .verify(); 72 | } 73 | 74 | @Test 75 | public void fluxToFlowable() { 76 | Flowable f = Flux.range(1, 10) 77 | .hide() 78 | .as(RxJava2Adapter::fluxToFlowable); 79 | 80 | StepVerifier.create(f) 81 | .expectNext(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) 82 | .expectComplete() 83 | .verify(); 84 | } 85 | 86 | @Test 87 | public void fluxToFlowableFused() throws Exception { 88 | io.reactivex.subscribers.TestSubscriber ts = new io.reactivex.subscribers.TestSubscriber<>(); 89 | 90 | // Testing for fusion is an RxJava 2 internal affair unfortunately 91 | Field f = BaseTestConsumer.class.getDeclaredField("initialFusionMode"); 92 | f.setAccessible(true); 93 | f.setInt(ts, QueueSubscription.ANY); 94 | 95 | Flux.range(1, 10) 96 | .as(RxJava2Adapter::fluxToFlowable) 97 | .subscribe(ts); 98 | 99 | ts 100 | .assertValues(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) 101 | .assertNoErrors() 102 | .assertComplete(); 103 | 104 | // Testing for fusion is an RxJava 2 internal affair unfortunately 105 | f = BaseTestConsumer.class.getDeclaredField("establishedFusionMode"); 106 | f.setAccessible(true); 107 | assertEquals(QueueSubscription.SYNC, f.getInt(ts)); 108 | } 109 | 110 | @Test 111 | public void singleToMono() { 112 | Mono m = Single.just(1) 113 | .to(RxJava2Adapter::singleToMono); 114 | 115 | StepVerifier.create(m) 116 | .expectNext(1) 117 | .expectComplete() 118 | .verify(); 119 | } 120 | //NB: MonoSubscribers like SingleToMonoSubscriber are not fuseable anymore 121 | 122 | @Test 123 | public void monoToSingle() { 124 | Mono.just(1) 125 | .as(RxJava2Adapter::monoToSingle) 126 | .test() 127 | .assertValues(1) 128 | .assertNoErrors() 129 | .assertComplete(); 130 | } 131 | 132 | @Test 133 | public void monoEmptyToSingle() { 134 | Mono.empty() 135 | .as(RxJava2Adapter::monoToSingle) 136 | .test() 137 | .assertNoValues() 138 | .assertError(NoSuchElementException.class) 139 | .assertNotComplete(); 140 | } 141 | 142 | @Test 143 | public void completableToMono() { 144 | Mono m = Completable.complete() 145 | .to(RxJava2Adapter::completableToMono); 146 | 147 | StepVerifier.create(m) 148 | .expectComplete() 149 | .verify(); 150 | } 151 | 152 | @Test 153 | public void completableToMonoFused() { 154 | Mono m = Completable.complete() 155 | .to(RxJava2Adapter::completableToMono); 156 | 157 | StepVerifier.create(m) 158 | .expectFusion(Fuseable.ANY, Fuseable.ASYNC) 159 | .expectComplete() 160 | .verify(); 161 | } 162 | 163 | @Test 164 | public void monoToCompletable() { 165 | Mono.empty() 166 | .as(RxJava2Adapter::monoToCompletable) 167 | .test() 168 | .assertNoValues() 169 | .assertNoErrors() 170 | .assertComplete(); 171 | } 172 | 173 | @Test 174 | public void monoToMaybeJust() { 175 | Mono.just(1) 176 | .as(RxJava2Adapter::monoToMaybe) 177 | .test() 178 | .assertResult(1); 179 | } 180 | 181 | @Test 182 | public void monoToMaybeEmpty() { 183 | Mono.empty() 184 | .as(RxJava2Adapter::monoToMaybe) 185 | .test() 186 | .assertResult(); 187 | } 188 | 189 | @Test 190 | public void monoToMaybeError() { 191 | Mono.error(new IOException("Forced failure")) 192 | .as(RxJava2Adapter::monoToMaybe) 193 | .test() 194 | .assertFailureAndMessage(IOException.class, "Forced failure"); 195 | } 196 | 197 | @Test 198 | public void maybeToMonoJust() { 199 | Mono m = Maybe.just(1) 200 | .to(RxJava2Adapter::maybeToMono); 201 | 202 | StepVerifier.create(m) 203 | .expectNext(1) 204 | .expectComplete() 205 | .verify(); 206 | } 207 | 208 | @Test 209 | public void maybeToMonoEmpty() { 210 | Mono m = Maybe.empty().to(RxJava2Adapter::maybeToMono); 211 | StepVerifier.create(m) 212 | .expectComplete() 213 | .verify(); 214 | } 215 | 216 | @Test 217 | public void maybeToMonoError() { 218 | Mono m = 219 | Maybe.error(new IOException("Forced failure")).to(RxJava2Adapter::maybeToMono); 220 | 221 | StepVerifier.create(m) 222 | .expectErrorMessage("Forced failure") 223 | .verify(); 224 | } 225 | //NB: MonoSubscribers like MaybeToMonoSubscriber are not fuseable anymore 226 | 227 | } 228 | -------------------------------------------------------------------------------- /reactor-adapter/src/test/java/reactor/adapter/rxjava/RxJava2SchedulersTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.adapter.rxjava; 18 | 19 | import java.util.Collections; 20 | import java.util.concurrent.TimeUnit; 21 | import java.util.function.Supplier; 22 | 23 | import org.junit.Test; 24 | 25 | import io.reactivex.*; 26 | import io.reactivex.schedulers.Schedulers; 27 | 28 | public class RxJava2SchedulersTest { 29 | 30 | @SuppressWarnings("unchecked") 31 | static void runRxJavaTest(boolean expected, String name, Supplier supplier) { 32 | Flowable.fromCallable(() -> Thread.currentThread().getName().contains(name)) 33 | .subscribeOn(supplier.get()) 34 | .test() 35 | .awaitDone(5, TimeUnit.SECONDS) 36 | .assertResult(expected); 37 | 38 | Single.fromCallable(() -> Thread.currentThread().getName().contains(name)) 39 | .subscribeOn(supplier.get()) 40 | .test() 41 | .awaitDone(5, TimeUnit.SECONDS) 42 | .assertResult(expected); 43 | 44 | Flowable.timer(100, TimeUnit.MILLISECONDS, supplier.get()) 45 | .map(v -> Thread.currentThread().getName().contains(name)) 46 | .test() 47 | .awaitDone(5, TimeUnit.SECONDS) 48 | .assertResult(expected); 49 | 50 | Flowable.fromCallable(() -> Thread.currentThread().getName().contains(name)) 51 | .subscribeOn(supplier.get()) 52 | .delay(20, TimeUnit.MILLISECONDS, Schedulers.computation()) 53 | .test() 54 | .awaitDone(5, TimeUnit.SECONDS) 55 | .assertResult(expected); 56 | 57 | Flowable.interval(20, TimeUnit.MILLISECONDS, supplier.get()) 58 | .map(v -> Thread.currentThread().getName().contains(name)) 59 | .take(5) 60 | .test() 61 | .awaitDone(5, TimeUnit.SECONDS) 62 | .assertResult(expected, expected, expected, expected, expected); 63 | 64 | Flowable.fromCallable(() -> Thread.currentThread().getName().contains(name)) 65 | .subscribeOn(supplier.get()) 66 | .buffer(10, 20, TimeUnit.MILLISECONDS, supplier.get()) 67 | .test() 68 | .awaitDone(5, TimeUnit.SECONDS) 69 | .assertResult(Collections.singletonList(expected)); 70 | } 71 | 72 | @Test 73 | public void useAndThenDisable() { 74 | 75 | // ----------------------------------------------------------------------------------------- 76 | 77 | runRxJavaTest(true, "RxComputation", () -> Schedulers.computation()); 78 | runRxJavaTest(true, "RxSingle", () -> Schedulers.single()); 79 | runRxJavaTest(true, "RxCached", () -> Schedulers.io()); 80 | runRxJavaTest(true, "RxNewThread", () -> Schedulers.newThread()); 81 | 82 | // ----------------------------------------------------------------------------------------- 83 | 84 | RxJava2Schedulers.useReactorCoreSchedulers(); 85 | 86 | runRxJavaTest(false, "RxComputation", () -> Schedulers.computation()); 87 | runRxJavaTest(false, "RxSingle", () -> Schedulers.single()); 88 | runRxJavaTest(false, "RxCached", () -> Schedulers.io()); 89 | runRxJavaTest(false, "RxNewThread", () -> Schedulers.newThread()); 90 | 91 | // ----------------------------------------------------------------------------------------- 92 | 93 | RxJava2Schedulers.resetReactorCoreSchedulers(); 94 | 95 | runRxJavaTest(true, "RxComputation", () -> Schedulers.computation()); 96 | runRxJavaTest(true, "RxSingle", () -> Schedulers.single()); 97 | runRxJavaTest(true, "RxCached", () -> Schedulers.io()); 98 | runRxJavaTest(true, "RxNewThread", () -> Schedulers.newThread()); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/bool/BooleanUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.bool; 18 | 19 | import reactor.core.publisher.Mono; 20 | 21 | /** 22 | * Functions to help compose results with {@link Mono}-wrapped {@literal boolean} values. 23 | * 24 | * @author Greg Turnquist 25 | */ 26 | public final class BooleanUtils { 27 | 28 | private BooleanUtils() { 29 | } 30 | 31 | /** 32 | * A boolean, wrapped in a {@link Mono}, inverted by the NOT operator. 33 | * 34 | * @param value boolean 35 | * @return {@literal !value} wrapped in a {@link Mono} 36 | */ 37 | public static Mono not(Mono value) { 38 | return value.map(bool -> !bool); 39 | } 40 | 41 | /** 42 | * Two booleans, wrapped in {@link Mono}, combined with the AND operator. 43 | * 44 | * @param value1 first boolean 45 | * @param value2 second boolean 46 | * @return {@literal value1 && value2} wrapped in a {@link Mono} 47 | */ 48 | public static Mono and(Mono value1, Mono value2) { 49 | return Mono.zip(value1, value2, (bool1, bool2) -> bool1 && bool2); 50 | } 51 | 52 | 53 | /** 54 | * Two booleans, wrapped in {@link Mono}, combined with the exclusive-OR operator. 55 | * 56 | * @param value1 first boolean 57 | * @param value2 second boolean 58 | * @return {@literal value1 XOR value2} wrapped in a {@link Mono} 59 | */ 60 | public static Mono xor(Mono value1, Mono value2) { 61 | return Mono.zip(value1, value2, (bool1, bool2) -> (bool1 != bool2) && (bool1 || bool2)); 62 | } 63 | 64 | /** 65 | * Two booleans, wrapped in {@link Mono}, combined with the OR operator. 66 | * 67 | * @param value1 first boolean 68 | * @param value2 second boolean 69 | * @return {@literal value1 || value2} wrapped in a {@link Mono} 70 | */ 71 | public static Mono or(Mono value1, Mono value2) { 72 | return Mono.zip(value1, value2, (bool1, bool2) -> bool1 || bool2); 73 | } 74 | 75 | /** 76 | * Two booleans, wrapped in {@link Mono}, combined with the NOT-AND (NAND) operator. 77 | * 78 | * @param value1 first boolean 79 | * @param value2 second boolean 80 | * @return {@literal !(value1 && value2)} wrapped in a {@link Mono} 81 | */ 82 | public static Mono nand(Mono value1, Mono value2) { 83 | return not(and(value1, value2)); 84 | } 85 | 86 | /** 87 | * Two booleans, wrapped in {@link Mono}, combined with the NOT-OR (NOR) operator. 88 | * 89 | * @param value1 first boolean 90 | * @param value2 second boolean 91 | * @return {@literal !(value1 || value2)} wrapped in a {@link Mono} 92 | */ 93 | public static Mono nor(Mono value1, Mono value2) { 94 | return not(or(value1, value2)); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/cache/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Extra utilities around caching, providing a default go-to strategy on what to store 3 | * in a cache. 4 | */ 5 | @NonNullApi 6 | package reactor.cache; 7 | 8 | import reactor.util.annotation.NonNullApi; -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/function/Consumer3.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.function; 18 | 19 | /** 20 | * An operation that accepts three input arguments and returns no result. 21 | * 22 | * @param The type of the first input to the operation 23 | * @param The type of the second input to the operation 24 | * @param The type of the third input to the operation 25 | * @author Ben Hale 26 | */ 27 | @FunctionalInterface 28 | public interface Consumer3 { 29 | 30 | /** 31 | * Performs this operation on the given arguments. 32 | * 33 | * @param t1 the first input argument 34 | * @param t2 the second input argument 35 | * @param t3 the third input argument 36 | */ 37 | void accept(T1 t1, T2 t2, T3 t3); 38 | 39 | } 40 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/function/Consumer4.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.function; 18 | 19 | /** 20 | * An operation that accepts four input arguments and returns no result. 21 | * 22 | * @param The type of the first input to the operation 23 | * @param The type of the second input to the operation 24 | * @param The type of the third input to the operation 25 | * @param The type of the fourth input to the operation 26 | * @author Ben Hale 27 | */ 28 | @FunctionalInterface 29 | public interface Consumer4 { 30 | 31 | /** 32 | * Performs this operation on the given arguments. 33 | * 34 | * @param t1 the first input argument 35 | * @param t2 the second input argument 36 | * @param t3 the third input argument 37 | * @param t4 the fourth input argument 38 | */ 39 | void accept(T1 t1, T2 t2, T3 t3, T4 t4); 40 | 41 | } 42 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/function/Consumer5.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.function; 18 | 19 | /** 20 | * An operation that accepts five input arguments and returns no result. 21 | * 22 | * @param The type of the first input to the operation 23 | * @param The type of the second input to the operation 24 | * @param The type of the third input to the operation 25 | * @param The type of the fourth input to the operation 26 | * @param The type of the fifth input to the operation 27 | * @author Ben Hale 28 | */ 29 | @FunctionalInterface 30 | public interface Consumer5 { 31 | 32 | /** 33 | * Performs this operation on the given arguments. 34 | * 35 | * @param t1 the first input argument 36 | * @param t2 the second input argument 37 | * @param t3 the third input argument 38 | * @param t4 the fourth input argument 39 | * @param t5 the fifth input argument 40 | */ 41 | void accept(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5); 42 | 43 | } 44 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/function/Consumer6.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.function; 18 | 19 | /** 20 | * An operation that accepts six input arguments and returns no result. 21 | * 22 | * @param The type of the first input to the operation 23 | * @param The type of the second input to the operation 24 | * @param The type of the third input to the operation 25 | * @param The type of the fourth input to the operation 26 | * @param The type of the fifth input to the operation 27 | * @param The type of the sixth input to the operation 28 | * @author Ben Hale 29 | */ 30 | @FunctionalInterface 31 | public interface Consumer6 { 32 | 33 | /** 34 | * Performs this operation on the given arguments. 35 | * 36 | * @param t1 the first input argument 37 | * @param t2 the second input argument 38 | * @param t3 the third input argument 39 | * @param t4 the fourth input argument 40 | * @param t5 the fifth input argument 41 | * @param t6 the sixth input argument 42 | */ 43 | void accept(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6); 44 | 45 | } 46 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/function/Consumer7.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.function; 18 | 19 | /** 20 | * An operation that accepts seven input arguments and returns no result. 21 | * 22 | * @param The type of the first input to the operation 23 | * @param The type of the second input to the operation 24 | * @param The type of the third input to the operation 25 | * @param The type of the fourth input to the operation 26 | * @param The type of the fifth input to the operation 27 | * @param The type of the sixth input to the operation 28 | * @param The type of the seventh input to the operation 29 | * @author Ben Hale 30 | */ 31 | @FunctionalInterface 32 | public interface Consumer7 { 33 | 34 | /** 35 | * Performs this operation on the given arguments. 36 | * 37 | * @param t1 the first input argument 38 | * @param t2 the second input argument 39 | * @param t3 the third input argument 40 | * @param t4 the fourth input argument 41 | * @param t5 the fifth input argument 42 | * @param t6 the sixth input argument 43 | * @param t7 the seventh input argument 44 | */ 45 | void accept(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7); 46 | 47 | } 48 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/function/Consumer8.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.function; 18 | 19 | /** 20 | * An operation that accepts eight input arguments and returns no result. 21 | * 22 | * @param The type of the first input to the operation 23 | * @param The type of the second input to the operation 24 | * @param The type of the third input to the operation 25 | * @param The type of the fourth input to the operation 26 | * @param The type of the fifth input to the operation 27 | * @param The type of the sixth input to the operation 28 | * @param The type of the seventh input to the operation 29 | * @param The type of the eighth input to the operation 30 | * @author Ben Hale 31 | */ 32 | @FunctionalInterface 33 | public interface Consumer8 { 34 | 35 | /** 36 | * Performs this operation on the given arguments. 37 | * 38 | * @param t1 the first input argument 39 | * @param t2 the second input argument 40 | * @param t3 the third input argument 41 | * @param t4 the fourth input argument 42 | * @param t5 the fifth input argument 43 | * @param t6 the sixth input argument 44 | * @param t7 the seventh input argument 45 | * @param t8 the eighth input argument 46 | */ 47 | void accept(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8); 48 | 49 | } 50 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/function/Function3.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.function; 18 | 19 | /** 20 | * Represents a function that accepts three arguments and produces a result. 21 | * 22 | * @param The type of the first input to the function 23 | * @param The type of the second input to the function 24 | * @param The type of the third input to the function 25 | * @param the type of the result of the function 26 | * @author Ben Hale 27 | */ 28 | @FunctionalInterface 29 | public interface Function3 { 30 | 31 | /** 32 | * Applies this function to the given arguments. 33 | * 34 | * @param t1 the first input argument 35 | * @param t2 the second input argument 36 | * @param t3 the third input argument 37 | * @return the function result 38 | */ 39 | R apply(T1 t1, T2 t2, T3 t3); 40 | 41 | } 42 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/function/Function4.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.function; 18 | 19 | /** 20 | * Represents a function that accepts four arguments and produces a result. 21 | * 22 | * @param The type of the first input to the function 23 | * @param The type of the second input to the function 24 | * @param The type of the third input to the function 25 | * @param The type of the fourth input to the function 26 | * @param the type of the result of the function 27 | * @author Ben Hale 28 | */ 29 | @FunctionalInterface 30 | public interface Function4 { 31 | 32 | /** 33 | * Applies this function to the given arguments. 34 | * 35 | * @param t1 the first input argument 36 | * @param t2 the second input argument 37 | * @param t3 the third input argument 38 | * @param t4 the fourth input argument 39 | * @return the function result 40 | */ 41 | R apply(T1 t1, T2 t2, T3 t3, T4 t4); 42 | 43 | } 44 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/function/Function5.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.function; 18 | 19 | /** 20 | * Represents a function that accepts five arguments and produces a result. 21 | * 22 | * @param The type of the first input to the function 23 | * @param The type of the second input to the function 24 | * @param The type of the third input to the function 25 | * @param The type of the fourth input to the function 26 | * @param The type of the fifth input to the function 27 | * @param the type of the result of the function 28 | * @author Ben Hale 29 | */ 30 | @FunctionalInterface 31 | public interface Function5 { 32 | 33 | /** 34 | * Applies this function to the given arguments. 35 | * 36 | * @param t1 the first input argument 37 | * @param t2 the second input argument 38 | * @param t3 the third input argument 39 | * @param t4 the fourth input argument 40 | * @param t5 the fifth input argument 41 | * @return the function result 42 | */ 43 | R apply(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5); 44 | 45 | } 46 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/function/Function6.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.function; 18 | 19 | /** 20 | * Represents a function that accepts six arguments and produces a result. 21 | * 22 | * @param The type of the first input to the function 23 | * @param The type of the second input to the function 24 | * @param The type of the third input to the function 25 | * @param The type of the fourth input to the function 26 | * @param The type of the fifth input to the function 27 | * @param The type of the sixth input to the function 28 | * @param the type of the result of the function 29 | * @author Ben Hale 30 | */ 31 | @FunctionalInterface 32 | public interface Function6 { 33 | 34 | /** 35 | * Applies this function to the given arguments. 36 | * 37 | * @param t1 the first input argument 38 | * @param t2 the second input argument 39 | * @param t3 the third input argument 40 | * @param t4 the fourth input argument 41 | * @param t5 the fifth input argument 42 | * @param t6 the sixth input argument 43 | * @return the function result 44 | */ 45 | R apply(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6); 46 | 47 | } 48 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/function/Function7.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.function; 18 | 19 | /** 20 | * Represents a function that accepts seven arguments and produces a result. 21 | * 22 | * @param The type of the first input to the function 23 | * @param The type of the second input to the function 24 | * @param The type of the third input to the function 25 | * @param The type of the fourth input to the function 26 | * @param The type of the fifth input to the function 27 | * @param The type of the sixth input to the function 28 | * @param The type of the seventh input to the function 29 | * @param the type of the result of the function 30 | * @author Ben Hale 31 | */ 32 | @FunctionalInterface 33 | public interface Function7 { 34 | 35 | /** 36 | * Applies this function to the given arguments. 37 | * 38 | * @param t1 the first input argument 39 | * @param t2 the second input argument 40 | * @param t3 the third input argument 41 | * @param t4 the fourth input argument 42 | * @param t5 the fifth input argument 43 | * @param t6 the sixth input argument 44 | * @param t7 the seventh input argument 45 | * @return the function result 46 | */ 47 | R apply(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7); 48 | 49 | } 50 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/function/Function8.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.function; 18 | 19 | /** 20 | * Represents a function that accepts eight arguments and produces a result. 21 | * 22 | * @param The type of the first input to the function 23 | * @param The type of the second input to the function 24 | * @param The type of the third input to the function 25 | * @param The type of the fourth input to the function 26 | * @param The type of the fifth input to the function 27 | * @param The type of the sixth input to the function 28 | * @param The type of the seventh input to the function 29 | * @param The type of the eighth input to the function 30 | * @param the type of the result of the function 31 | * @author Ben Hale 32 | */ 33 | @FunctionalInterface 34 | public interface Function8 { 35 | 36 | /** 37 | * Applies this function to the given arguments. 38 | * 39 | * @param t1 the first input argument 40 | * @param t2 the second input argument 41 | * @param t3 the third input argument 42 | * @param t4 the fourth input argument 43 | * @param t5 the fifth input argument 44 | * @param t6 the sixth input argument 45 | * @param t7 the seventh input argument 46 | * @param t8 the eighth input argument 47 | * @return the function result 48 | */ 49 | R apply(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8); 50 | 51 | } 52 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/function/Predicate3.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.function; 18 | 19 | /** 20 | * Represents a predicate (boolean-valued function) of three arguments. 21 | * 22 | * @param The type of the first input to the predicate 23 | * @param The type of the second input to the predicate 24 | * @param The type of the third input to the predicate 25 | * @author Ben Hale 26 | */ 27 | @FunctionalInterface 28 | public interface Predicate3 { 29 | 30 | /** 31 | * Evaluates this predicate on the given arguments. 32 | * 33 | * @param t1 the first input argument 34 | * @param t2 the second input argument 35 | * @param t3 the third input argument 36 | * @return {@code true} if the input arguments match the predicate, otherwise {@code false} 37 | */ 38 | boolean test(T1 t1, T2 t2, T3 t3); 39 | 40 | } 41 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/function/Predicate4.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.function; 18 | 19 | /** 20 | * Represents a predicate (boolean-valued function) of four arguments. 21 | * 22 | * @param The type of the first input to the predicate 23 | * @param The type of the second input to the predicate 24 | * @param The type of the third input to the predicate 25 | * @param The type of the fourth input to the predicate 26 | * @author Ben Hale 27 | */ 28 | @FunctionalInterface 29 | public interface Predicate4 { 30 | 31 | /** 32 | * Evaluates this predicate on the given arguments. 33 | * 34 | * @param t1 the first input argument 35 | * @param t2 the second input argument 36 | * @param t3 the third input argument 37 | * @param t4 the fourth input argument 38 | * @return {@code true} if the input arguments match the predicate, otherwise {@code false} 39 | */ 40 | boolean test(T1 t1, T2 t2, T3 t3, T4 t4); 41 | 42 | } 43 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/function/Predicate5.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.function; 18 | 19 | /** 20 | * Represents a predicate (boolean-valued function) of five arguments. 21 | * 22 | * @param The type of the first input to the predicate 23 | * @param The type of the second input to the predicate 24 | * @param The type of the third input to the predicate 25 | * @param The type of the fourth input to the predicate 26 | * @param The type of the fifth input to the predicate 27 | * @author Ben Hale 28 | */ 29 | @FunctionalInterface 30 | public interface Predicate5 { 31 | 32 | /** 33 | * Evaluates this predicate on the given arguments. 34 | * 35 | * @param t1 the first input argument 36 | * @param t2 the second input argument 37 | * @param t3 the third input argument 38 | * @param t4 the fourth input argument 39 | * @param t5 the fifth input argument 40 | * @return {@code true} if the input arguments match the predicate, otherwise {@code false} 41 | */ 42 | boolean test(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5); 43 | 44 | } 45 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/function/Predicate6.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.function; 18 | 19 | /** 20 | * Represents a predicate (boolean-valued function) of six arguments. 21 | * 22 | * @param The type of the first input to the predicate 23 | * @param The type of the second input to the predicate 24 | * @param The type of the third input to the predicate 25 | * @param The type of the fourth input to the predicate 26 | * @param The type of the fifth input to the predicate 27 | * @param The type of the sixth input to the predicate 28 | * @author Ben Hale 29 | */ 30 | @FunctionalInterface 31 | public interface Predicate6 { 32 | 33 | /** 34 | * Evaluates this predicate on the given arguments. 35 | * 36 | * @param t1 the first input argument 37 | * @param t2 the second input argument 38 | * @param t3 the third input argument 39 | * @param t4 the fourth input argument 40 | * @param t5 the fifth input argument 41 | * @param t6 the sixth input argument 42 | * @return {@code true} if the input arguments match the predicate, otherwise {@code false} 43 | */ 44 | boolean test(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6); 45 | 46 | } 47 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/function/Predicate7.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.function; 18 | 19 | /** 20 | * Represents a predicate (boolean-valued function) of seven arguments. 21 | * 22 | * @param The type of the first input to the predicate 23 | * @param The type of the second input to the predicate 24 | * @param The type of the third input to the predicate 25 | * @param The type of the fourth input to the predicate 26 | * @param The type of the fifth input to the predicate 27 | * @param The type of the sixth input to the predicate 28 | * @param The type of the seventh input to the predicate 29 | * @author Ben Hale 30 | */ 31 | @FunctionalInterface 32 | public interface Predicate7 { 33 | 34 | /** 35 | * Evaluates this predicate on the given arguments. 36 | * 37 | * @param t1 the first input argument 38 | * @param t2 the second input argument 39 | * @param t3 the third input argument 40 | * @param t4 the fourth input argument 41 | * @param t5 the fifth input argument 42 | * @param t6 the sixth input argument 43 | * @param t7 the seventh input argument 44 | * @return {@code true} if the input arguments match the predicate, otherwise {@code false} 45 | */ 46 | boolean test(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7); 47 | 48 | } 49 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/function/Predicate8.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.function; 18 | 19 | /** 20 | * Represents a predicate (boolean-valued function) of eight arguments. 21 | * 22 | * @param The type of the first input to the predicate 23 | * @param The type of the second input to the predicate 24 | * @param The type of the third input to the predicate 25 | * @param The type of the fourth input to the predicate 26 | * @param The type of the fifth input to the predicate 27 | * @param The type of the sixth input to the predicate 28 | * @param The type of the seventh input to the predicate 29 | * @param The type of the eighth input to the predicate 30 | * @author Ben Hale 31 | */ 32 | @FunctionalInterface 33 | public interface Predicate8 { 34 | 35 | /** 36 | * Evaluates this predicate on the given arguments. 37 | * 38 | * @param t1 the first input argument 39 | * @param t2 the second input argument 40 | * @param t3 the third input argument 41 | * @param t4 the fourth input argument 42 | * @param t5 the fifth input argument 43 | * @param t6 the sixth input argument 44 | * @param t7 the seventh input argument 45 | * @param t8 the eighth input argument 46 | * @return {@code true} if the input arguments match the predicate, otherwise {@code false} 47 | */ 48 | boolean test(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8); 49 | 50 | } 51 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/math/MathSubscriber.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.math; 18 | 19 | import org.reactivestreams.Subscription; 20 | import reactor.core.CoreSubscriber; 21 | import reactor.core.publisher.Operators; 22 | 23 | abstract class MathSubscriber extends Operators.MonoSubscriber { 24 | 25 | Subscription s; 26 | 27 | boolean done; 28 | 29 | MathSubscriber(CoreSubscriber actual) { 30 | super(actual); 31 | } 32 | 33 | @Override 34 | public void onSubscribe(Subscription s) { 35 | if (Operators.validate(this.s, s)) { 36 | this.s = s; 37 | actual.onSubscribe(this); 38 | s.request(Long.MAX_VALUE); 39 | } 40 | } 41 | 42 | @Override 43 | public void onNext(T t) { 44 | if (done) { 45 | Operators.onNextDropped(t, actual.currentContext()); 46 | return; 47 | } 48 | 49 | try { 50 | updateResult(t); 51 | } 52 | catch (Throwable ex) { 53 | reset(); 54 | done = true; 55 | actual.onError(Operators.onOperatorError(s, ex, t, actual.currentContext())); 56 | return; 57 | } 58 | } 59 | 60 | @Override 61 | public void onError(Throwable t) { 62 | if (done) { 63 | Operators.onErrorDropped(t, actual.currentContext()); 64 | return; 65 | } 66 | done = true; 67 | reset(); 68 | actual.onError(t); 69 | } 70 | 71 | @Override 72 | public void onComplete() { 73 | if (done) { 74 | return; 75 | } 76 | done = true; 77 | R r = result(); 78 | if (r != null) { 79 | complete(r); 80 | } 81 | else { 82 | actual.onComplete(); 83 | } 84 | } 85 | 86 | @Override 87 | public void cancel() { 88 | super.cancel(); 89 | s.cancel(); 90 | } 91 | 92 | protected abstract void reset(); 93 | 94 | protected abstract R result(); 95 | 96 | protected abstract void updateResult(T newValue); 97 | } 98 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/math/MonoAverageBigDecimal.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.math; 18 | 19 | import java.math.BigDecimal; 20 | import java.math.BigInteger; 21 | import java.util.function.Function; 22 | 23 | import org.reactivestreams.Publisher; 24 | import reactor.core.CoreSubscriber; 25 | import reactor.core.Fuseable; 26 | import reactor.core.publisher.Flux; 27 | 28 | /** 29 | * Computes the average of source numbers and returns the result as a {@link BigDecimal}. 30 | * 31 | * @param the input value type 32 | */ 33 | public class MonoAverageBigDecimal extends MonoFromFluxOperator 34 | implements Fuseable { 35 | 36 | private final Function mapping; 37 | 38 | MonoAverageBigDecimal(Publisher source, 39 | Function mapping) { 40 | super(Flux.from(source)); 41 | this.mapping = mapping; 42 | } 43 | 44 | @Override 45 | public void subscribe(CoreSubscriber actual) { 46 | source.subscribe(new AverageBigDecimalSubscriber(actual, mapping)); 47 | } 48 | 49 | private static final class AverageBigDecimalSubscriber 50 | extends MathSubscriber { 51 | 52 | private final Function mapping; 53 | 54 | private int count; 55 | 56 | private BigDecimal sum = BigDecimal.ZERO; 57 | 58 | AverageBigDecimalSubscriber(CoreSubscriber actual, 59 | Function mapping) { 60 | super(actual); 61 | this.mapping = mapping; 62 | } 63 | 64 | @Override 65 | protected void reset() { 66 | count = 0; 67 | sum = BigDecimal.ZERO; 68 | } 69 | 70 | @Override 71 | protected BigDecimal result() { 72 | return (count == 0 ? null : sum.divide(BigDecimal.valueOf(count))); 73 | } 74 | 75 | @Override 76 | protected void updateResult(T newValue) { 77 | Number number = mapping.apply(newValue); 78 | BigDecimal bigDecimalValue = (number instanceof BigDecimal) ? 79 | (BigDecimal) number : new BigDecimal(number.toString()); 80 | sum = sum.add(bigDecimalValue); 81 | count++; 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/math/MonoAverageBigInteger.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.math; 18 | 19 | import java.math.BigDecimal; 20 | import java.math.BigInteger; 21 | import java.math.RoundingMode; 22 | import java.util.function.Function; 23 | 24 | import org.reactivestreams.Publisher; 25 | import reactor.core.CoreSubscriber; 26 | import reactor.core.Fuseable; 27 | import reactor.core.publisher.Flux; 28 | 29 | /** 30 | * Computes the average of source numbers and returns the result as a {@link BigInteger}. 31 | * 32 | * @param the input value type 33 | */ 34 | public class MonoAverageBigInteger extends MonoFromFluxOperator 35 | implements Fuseable { 36 | 37 | private final Function mapping; 38 | 39 | MonoAverageBigInteger(Publisher source, 40 | Function mapping) { 41 | super(Flux.from(source)); 42 | this.mapping = mapping; 43 | } 44 | 45 | @Override 46 | public void subscribe(CoreSubscriber actual) { 47 | source.subscribe(new AverageBigIntegerSubscriber(actual, mapping)); 48 | } 49 | 50 | private static final class AverageBigIntegerSubscriber 51 | extends MathSubscriber { 52 | 53 | private final Function mapping; 54 | 55 | private int count; 56 | 57 | private BigDecimal sum = BigDecimal.ZERO; 58 | 59 | AverageBigIntegerSubscriber(CoreSubscriber actual, 60 | Function mapping) { 61 | super(actual); 62 | this.mapping = mapping; 63 | } 64 | 65 | @Override 66 | protected void reset() { 67 | count = 0; 68 | sum = BigDecimal.ZERO; 69 | } 70 | 71 | @Override 72 | protected BigInteger result() { 73 | return (count == 0 ? null : sum.divide(BigDecimal.valueOf(count), RoundingMode.FLOOR).toBigInteger()); 74 | } 75 | 76 | @Override 77 | protected void updateResult(T newValue) { 78 | Number number = mapping.apply(newValue); 79 | BigDecimal bigDecimalValue; 80 | if (number instanceof BigDecimal) { 81 | bigDecimalValue = (BigDecimal) number; 82 | } 83 | else if (number instanceof BigInteger) { 84 | bigDecimalValue = new BigDecimal((BigInteger) number); 85 | } 86 | else { 87 | bigDecimalValue = new BigDecimal(number.toString()); 88 | } 89 | sum = sum.add(bigDecimalValue); 90 | count++; 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/math/MonoAverageDouble.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.math; 18 | 19 | import java.util.function.Function; 20 | 21 | import org.reactivestreams.Publisher; 22 | import reactor.core.CoreSubscriber; 23 | import reactor.core.Fuseable; 24 | import reactor.core.publisher.Flux; 25 | 26 | /** 27 | * Computes the average of source numbers and returns the result as a double. 28 | * 29 | * @param the input value type 30 | */ 31 | final class MonoAverageDouble extends MonoFromFluxOperator implements Fuseable { 32 | 33 | final Function mapping; 34 | 35 | MonoAverageDouble(Publisher source, Function mapping) { 36 | super(Flux.from(source)); 37 | this.mapping = mapping; 38 | } 39 | 40 | @Override 41 | public void subscribe(CoreSubscriber s) { 42 | source.subscribe(new AverageDoubleSubscriber(s, mapping)); 43 | } 44 | 45 | static final class AverageDoubleSubscriber extends MathSubscriber { 46 | 47 | final Function mapping; 48 | 49 | int count; 50 | 51 | double sum; 52 | 53 | AverageDoubleSubscriber(CoreSubscriber actual, Function mapping) { 54 | super(actual); 55 | this.mapping = mapping; 56 | } 57 | 58 | @Override 59 | protected void updateResult(T newValue) { 60 | count++; 61 | double doubleValue = mapping.apply(newValue).doubleValue(); 62 | sum += doubleValue; 63 | } 64 | 65 | @Override 66 | protected Double result() { 67 | return count == 0 ? null : sum / count; 68 | } 69 | 70 | @Override 71 | protected void reset() { 72 | count = 0; 73 | sum = 0.0; 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/math/MonoAverageFloat.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.math; 18 | 19 | import java.util.function.Function; 20 | 21 | import org.reactivestreams.Publisher; 22 | import reactor.core.CoreSubscriber; 23 | import reactor.core.Fuseable; 24 | import reactor.core.publisher.Flux; 25 | 26 | /** 27 | * Computes the average of source numbers and returns the result as a float. 28 | * 29 | * @param the input value type 30 | */ 31 | final class MonoAverageFloat extends MonoFromFluxOperator implements Fuseable { 32 | 33 | final Function mapping; 34 | 35 | MonoAverageFloat(Publisher source, Function mapping) { 36 | super(Flux.from(source)); 37 | this.mapping = mapping; 38 | } 39 | 40 | @Override 41 | public void subscribe(CoreSubscriber s) { 42 | source.subscribe(new AverageFloatSubscriber(s, mapping)); 43 | } 44 | 45 | static final class AverageFloatSubscriber extends MathSubscriber { 46 | 47 | final Function mapping; 48 | 49 | int count; 50 | 51 | float sum; 52 | 53 | AverageFloatSubscriber(CoreSubscriber actual, Function mapping) { 54 | super(actual); 55 | this.mapping = mapping; 56 | } 57 | 58 | @Override 59 | protected void updateResult(T newValue) { 60 | count++; 61 | float floatValue = mapping.apply(newValue).floatValue(); 62 | sum += floatValue; 63 | } 64 | 65 | @Override 66 | protected Float result() { 67 | return count == 0 ? null : sum / count; 68 | } 69 | 70 | @Override 71 | protected void reset() { 72 | count = 0; 73 | sum = 0.0F; 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/math/MonoFromFluxOperator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.math; 18 | 19 | import java.util.Objects; 20 | import javax.annotation.Nullable; 21 | 22 | import org.reactivestreams.Publisher; 23 | import reactor.core.Scannable; 24 | import reactor.core.publisher.Flux; 25 | import reactor.core.publisher.Mono; 26 | 27 | /** 28 | * A decorating {@link Mono} {@link Publisher} that exposes {@link Mono} API over an 29 | * arbitrary {@link Publisher} Useful to create operators which return a {@link Mono}. 30 | * 31 | * @param delegate {@link Publisher} type 32 | * @param produced type 33 | */ 34 | abstract class MonoFromFluxOperator extends Mono implements Scannable { 35 | 36 | protected final Flux source; 37 | 38 | /** 39 | * Build a {@link reactor.core.publisher.MonoFromFluxOperator} wrapper around the passed parent {@link Publisher} 40 | * 41 | * @param source the {@link Publisher} to decorate 42 | */ 43 | protected MonoFromFluxOperator(Flux source) { 44 | this.source = Objects.requireNonNull(source); 45 | } 46 | 47 | @Override 48 | @Nullable 49 | public Object scanUnsafe(Attr key) { 50 | if (key == Attr.PREFETCH) return Integer.MAX_VALUE; 51 | if (key == Attr.PARENT) return source; 52 | return null; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/math/MonoMinMax.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.math; 18 | 19 | import java.util.Comparator; 20 | 21 | import org.reactivestreams.Publisher; 22 | import reactor.core.CoreSubscriber; 23 | import reactor.core.Fuseable; 24 | import reactor.core.publisher.Flux; 25 | 26 | /** 27 | * Computes the maximum or minimum of source items and returns the result. 28 | * 29 | * @param the input value type 30 | */ 31 | final class MonoMinMax extends MonoFromFluxOperator implements Fuseable { 32 | 33 | final Comparator comparator; 34 | 35 | final int comparisonMultiplier; 36 | 37 | MonoMinMax(Publisher source, Comparator comparator, int comparisonMultiplier) { 38 | super(Flux.from(source)); 39 | this.comparator = comparator; 40 | this.comparisonMultiplier = comparisonMultiplier; 41 | } 42 | 43 | @Override 44 | public void subscribe(CoreSubscriber s) { 45 | source.subscribe(new MinMaxSubscriber(s, comparator, comparisonMultiplier)); 46 | } 47 | 48 | static final class MinMaxSubscriber extends MathSubscriber { 49 | 50 | final Comparator comparator; 51 | 52 | final int comparisonMultiplier; 53 | 54 | T result; 55 | 56 | MinMaxSubscriber(CoreSubscriber actual, Comparator comparator, int comparisonMultiplier) { 57 | super(actual); 58 | this.comparator = comparator; 59 | this.comparisonMultiplier = comparisonMultiplier; 60 | } 61 | 62 | @Override 63 | protected void updateResult(T newValue) { 64 | T r = result; 65 | if (r == null || comparator.compare(newValue, result) * comparisonMultiplier > 0) { 66 | r = newValue; 67 | } 68 | result = r; 69 | } 70 | 71 | @Override 72 | protected T result() { 73 | return result; 74 | } 75 | 76 | @Override 77 | protected void reset() { 78 | result = null; 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/math/MonoSumBigDecimal.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.math; 18 | 19 | import java.math.BigDecimal; 20 | import java.util.function.Function; 21 | 22 | import org.reactivestreams.Publisher; 23 | import reactor.core.CoreSubscriber; 24 | import reactor.core.Fuseable; 25 | import reactor.core.publisher.Flux; 26 | 27 | /** 28 | * Computes the sun of source numbers and returns the result as {@link BigDecimal} 29 | * 30 | * @param the input value type 31 | */ 32 | public class MonoSumBigDecimal extends MonoFromFluxOperator 33 | implements Fuseable { 34 | 35 | private final Function mapping; 36 | 37 | MonoSumBigDecimal(Publisher source, 38 | Function mapping) { 39 | super(Flux.from(source)); 40 | this.mapping = mapping; 41 | } 42 | 43 | @Override 44 | public void subscribe(CoreSubscriber actual) { 45 | source.subscribe(new SumBigDecimalSubscriber(actual, mapping)); 46 | } 47 | 48 | static final private class SumBigDecimalSubscriber 49 | extends MathSubscriber { 50 | 51 | private final Function mapping; 52 | 53 | BigDecimal sum; 54 | 55 | boolean hasValue; 56 | 57 | SumBigDecimalSubscriber(CoreSubscriber actual, 58 | Function mapping) { 59 | super(actual); 60 | this.mapping = mapping; 61 | } 62 | 63 | @Override 64 | protected void reset() { 65 | sum = BigDecimal.ZERO; 66 | hasValue = false; 67 | } 68 | 69 | @Override 70 | protected BigDecimal result() { 71 | return (hasValue ? sum : null); 72 | } 73 | 74 | @Override 75 | protected void updateResult(T newValue) { 76 | Number number = mapping.apply(newValue); 77 | BigDecimal bigDecimalValue = (number instanceof BigDecimal) ? 78 | (BigDecimal) number : new BigDecimal(number.toString()); 79 | sum = hasValue ? sum.add(bigDecimalValue) : bigDecimalValue; 80 | hasValue = true; 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/math/MonoSumBigInteger.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.math; 18 | 19 | import java.math.BigDecimal; 20 | import java.math.BigInteger; 21 | import java.util.function.Function; 22 | 23 | import org.reactivestreams.Publisher; 24 | import reactor.core.CoreSubscriber; 25 | import reactor.core.Fuseable; 26 | import reactor.core.publisher.Flux; 27 | 28 | /** 29 | * Computes the sun of source numbers and returns the result as {@link BigInteger} 30 | * 31 | * @param the input value type 32 | */ 33 | final class MonoSumBigInteger extends MonoFromFluxOperator 34 | implements Fuseable { 35 | 36 | private final Function mapping; 37 | 38 | MonoSumBigInteger(Publisher source, 39 | Function mapping) { 40 | super(Flux.from(source)); 41 | this.mapping = mapping; 42 | } 43 | 44 | @Override 45 | public void subscribe(CoreSubscriber actual) { 46 | source.subscribe(new SumBigIntegerSubscriber(actual, mapping)); 47 | } 48 | 49 | static final private class SumBigIntegerSubscriber 50 | extends MathSubscriber { 51 | 52 | private final Function mapping; 53 | 54 | BigDecimal sum; 55 | 56 | boolean hasValue; 57 | 58 | SumBigIntegerSubscriber(CoreSubscriber actual, 59 | Function mapping) { 60 | super(actual); 61 | this.mapping = mapping; 62 | } 63 | 64 | @Override 65 | protected void reset() { 66 | sum = BigDecimal.ZERO; 67 | hasValue = false; 68 | } 69 | 70 | @Override 71 | protected BigInteger result() { 72 | return (hasValue ? sum.toBigInteger() : null); 73 | } 74 | 75 | @Override 76 | protected void updateResult(T newValue) { 77 | Number number = mapping.apply(newValue); 78 | BigDecimal bigDecimalValue; 79 | if (number instanceof BigDecimal) { 80 | bigDecimalValue = (BigDecimal) number; 81 | } 82 | else if (number instanceof BigInteger) { 83 | bigDecimalValue = new BigDecimal((BigInteger) number); 84 | } 85 | else { 86 | bigDecimalValue = new BigDecimal(number.toString()); 87 | } 88 | sum = hasValue ? sum.add(bigDecimalValue) : bigDecimalValue; 89 | hasValue = true; 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/math/MonoSumDouble.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.math; 18 | 19 | import java.util.function.Function; 20 | 21 | import org.reactivestreams.Publisher; 22 | import reactor.core.CoreSubscriber; 23 | import reactor.core.Fuseable; 24 | import reactor.core.publisher.Flux; 25 | 26 | /** 27 | * Computes the sum of source numbers and returns the result as a double. 28 | * 29 | * @param the input value type 30 | */ 31 | final class MonoSumDouble extends MonoFromFluxOperator implements Fuseable { 32 | 33 | final Function mapping; 34 | 35 | MonoSumDouble(Publisher source, Function mapping) { 36 | super(Flux.from(source)); 37 | this.mapping = mapping; 38 | } 39 | 40 | @Override 41 | public void subscribe(CoreSubscriber s) { 42 | source.subscribe(new SumLongSubscriber(s, mapping)); 43 | } 44 | 45 | static final class SumLongSubscriber extends MathSubscriber { 46 | 47 | final Function mapping; 48 | 49 | double sum; 50 | 51 | boolean hasValue; 52 | 53 | SumLongSubscriber(CoreSubscriber actual, Function mapping) { 54 | super(actual); 55 | this.mapping = mapping; 56 | } 57 | 58 | @Override 59 | protected void updateResult(T newValue) { 60 | double doubleValue = mapping.apply(newValue).doubleValue(); 61 | sum = hasValue ? sum + doubleValue : doubleValue; 62 | hasValue = true; 63 | } 64 | 65 | @Override 66 | protected Double result() { 67 | return hasValue ? sum : null; 68 | } 69 | 70 | @Override 71 | protected void reset() { 72 | sum = 0.0; 73 | hasValue = false; 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/math/MonoSumFloat.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.math; 18 | 19 | import java.util.function.Function; 20 | 21 | import org.reactivestreams.Publisher; 22 | import reactor.core.CoreSubscriber; 23 | import reactor.core.Fuseable; 24 | import reactor.core.publisher.Flux; 25 | 26 | /** 27 | * Computes the sum of source numbers and returns the result as a float. 28 | * 29 | * @param the input value type 30 | */ 31 | final class MonoSumFloat extends MonoFromFluxOperator implements Fuseable { 32 | 33 | final Function mapping; 34 | 35 | MonoSumFloat(Publisher source, Function mapping) { 36 | super(Flux.from(source)); 37 | this.mapping = mapping; 38 | } 39 | 40 | @Override 41 | public void subscribe(CoreSubscriber s) { 42 | source.subscribe(new SumLongSubscriber(s, mapping)); 43 | } 44 | 45 | static final class SumLongSubscriber extends MathSubscriber { 46 | 47 | final Function mapping; 48 | 49 | float sum; 50 | 51 | boolean hasValue; 52 | 53 | SumLongSubscriber(CoreSubscriber actual, Function mapping) { 54 | super(actual); 55 | this.mapping = mapping; 56 | } 57 | 58 | @Override 59 | protected void updateResult(T newValue) { 60 | float floatValue = mapping.apply(newValue).floatValue(); 61 | sum = hasValue ? sum + floatValue : floatValue; 62 | hasValue = true; 63 | } 64 | 65 | @Override 66 | protected Float result() { 67 | return hasValue ? sum : null; 68 | } 69 | 70 | @Override 71 | protected void reset() { 72 | sum = 0.0F; 73 | hasValue = false; 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/math/MonoSumInt.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.math; 18 | 19 | import java.util.function.Function; 20 | 21 | import org.reactivestreams.Publisher; 22 | import reactor.core.CoreSubscriber; 23 | import reactor.core.Fuseable; 24 | import reactor.core.publisher.Flux; 25 | 26 | /** 27 | * Computes the sum of source numbers and returns the result as an integer. 28 | * 29 | * @param the input value type 30 | */ 31 | final class MonoSumInt extends MonoFromFluxOperator implements Fuseable { 32 | 33 | final Function mapping; 34 | 35 | MonoSumInt(Publisher source, Function mapping) { 36 | super(Flux.from(source)); 37 | this.mapping = mapping; 38 | } 39 | 40 | @Override 41 | public void subscribe(CoreSubscriber s) { 42 | source.subscribe(new SumIntSubscriber(s, mapping)); 43 | } 44 | 45 | static final class SumIntSubscriber extends MathSubscriber { 46 | 47 | final Function mapping; 48 | 49 | int sum; 50 | 51 | boolean hasValue; 52 | 53 | SumIntSubscriber(CoreSubscriber actual, Function mapping) { 54 | super(actual); 55 | this.mapping = mapping; 56 | } 57 | 58 | @Override 59 | protected void updateResult(T newValue) { 60 | int intValue = mapping.apply(newValue).intValue(); 61 | if (hasValue) { 62 | boolean sumPositive = sum >= 0; 63 | sum = sum + intValue; 64 | //overflow 65 | if (sumPositive && intValue >= 0 && sum < 0) { 66 | sum = Integer.MAX_VALUE; 67 | } 68 | //underflow 69 | else if (!sumPositive && intValue < 0 && sum > 0) { 70 | sum = Integer.MIN_VALUE; 71 | } 72 | } 73 | else { 74 | sum = intValue; 75 | hasValue = true; 76 | } 77 | } 78 | 79 | @Override 80 | protected Integer result() { 81 | return hasValue ? sum : null; 82 | } 83 | 84 | @Override 85 | protected void reset() { 86 | sum = 0; 87 | hasValue = false; 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/math/MonoSumLong.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.math; 18 | 19 | import java.util.function.Function; 20 | 21 | import org.reactivestreams.Publisher; 22 | import reactor.core.CoreSubscriber; 23 | import reactor.core.Fuseable; 24 | import reactor.core.publisher.Flux; 25 | 26 | /** 27 | * Computes the sum of source numbers and returns the result as a long. 28 | * 29 | * @param the input value type 30 | */ 31 | final class MonoSumLong extends MonoFromFluxOperator implements Fuseable { 32 | 33 | final Function mapping; 34 | 35 | MonoSumLong(Publisher source, Function mapping) { 36 | super(Flux.from(source)); 37 | this.mapping = mapping; 38 | } 39 | 40 | @Override 41 | public void subscribe(CoreSubscriber s) { 42 | source.subscribe(new SumLongSubscriber(s, mapping)); 43 | } 44 | 45 | static final class SumLongSubscriber extends MathSubscriber { 46 | 47 | final Function mapping; 48 | 49 | long sum; 50 | 51 | boolean hasValue; 52 | 53 | SumLongSubscriber(CoreSubscriber actual, Function mapping) { 54 | super(actual); 55 | this.mapping = mapping; 56 | } 57 | 58 | @Override 59 | protected void updateResult(T newValue) { 60 | long longValue = mapping.apply(newValue).longValue(); 61 | if (hasValue) { 62 | boolean sumPositive = sum >= 0; 63 | sum = sum + longValue; 64 | //overflow 65 | if (sumPositive && longValue >= 0 && sum < 0) { 66 | sum = Long.MAX_VALUE; 67 | } 68 | //underflow 69 | else if (!sumPositive && longValue < 0 && sum > 0) { 70 | sum = Long.MIN_VALUE; 71 | } 72 | } 73 | else { 74 | sum = longValue; 75 | hasValue = true; 76 | } 77 | } 78 | 79 | @Override 80 | protected Long result() { 81 | return hasValue ? sum : null; 82 | } 83 | 84 | @Override 85 | protected void reset() { 86 | sum = 0L; 87 | hasValue = false; 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/retry/AbstractRetry.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.retry; 18 | 19 | import java.time.Duration; 20 | import java.time.Instant; 21 | import java.util.function.Function; 22 | 23 | import org.reactivestreams.Publisher; 24 | 25 | import reactor.core.publisher.Flux; 26 | import reactor.core.publisher.Mono; 27 | import reactor.core.scheduler.Scheduler; 28 | import reactor.core.scheduler.Schedulers; 29 | import reactor.scheduler.clock.SchedulerClock; 30 | import reactor.util.Logger; 31 | import reactor.util.Loggers; 32 | import reactor.util.annotation.Nullable; 33 | 34 | public abstract class AbstractRetry implements Function, Publisher> { 35 | 36 | static final Logger log = Loggers.getLogger(AbstractRetry.class); 37 | 38 | static final BackoffDelay RETRY_EXHAUSTED = new BackoffDelay(Duration.ofSeconds(-1)) { 39 | @Override 40 | public String toString() { 41 | return "{EXHAUSTED}"; 42 | } 43 | }; 44 | 45 | final long maxIterations; 46 | final Duration timeout; 47 | final Backoff backoff; 48 | final Jitter jitter; 49 | @Nullable 50 | final Scheduler backoffScheduler; 51 | final SchedulerClock clock; 52 | final T applicationContext; 53 | 54 | AbstractRetry(long maxIterations, 55 | Duration timeout, 56 | Backoff backoff, 57 | Jitter jitter, 58 | @Nullable 59 | Scheduler backoffScheduler, 60 | T applicationContext) { 61 | this.maxIterations = maxIterations; 62 | this.timeout = timeout; 63 | this.backoff = backoff; 64 | this.jitter = jitter; 65 | this.backoffScheduler = backoffScheduler; 66 | this.clock = SchedulerClock.of(backoffScheduler == null ? Schedulers.parallel() : backoffScheduler); 67 | this.applicationContext = applicationContext; 68 | } 69 | 70 | Instant calculateTimeout() { 71 | return timeout != null ? Instant.now(clock).plus(timeout) : Instant.MAX; 72 | } 73 | 74 | BackoffDelay calculateBackoff(IterationContext retryContext, Instant timeoutInstant) { 75 | if (retryContext.iteration() > maxIterations) 76 | return RETRY_EXHAUSTED; 77 | 78 | BackoffDelay nextBackoff = backoff.apply(retryContext); 79 | Duration minBackoff = nextBackoff.min; 80 | Duration maxBackoff = nextBackoff.max; 81 | Duration backoff = nextBackoff.delay; 82 | if (maxBackoff != null) 83 | backoff = backoff.compareTo(maxBackoff) < 0 ? backoff : maxBackoff; 84 | if (minBackoff != null) 85 | backoff = backoff.compareTo(minBackoff) > 0 ? backoff : minBackoff; 86 | 87 | BackoffDelay sanitizedBackoff = new BackoffDelay(minBackoff, maxBackoff, backoff); 88 | Duration jitteredBackoff = jitter.apply(sanitizedBackoff); 89 | 90 | if (Instant.now(clock).plus(jitteredBackoff).isAfter(timeoutInstant)) 91 | return RETRY_EXHAUSTED; 92 | else 93 | return new BackoffDelay(minBackoff, maxBackoff, jitteredBackoff); 94 | } 95 | 96 | Publisher retryMono(Duration delay) { 97 | if (delay == Duration.ZERO) 98 | return Mono.just(0L); 99 | else if (backoffScheduler == null) 100 | return Mono.delay(delay); 101 | else 102 | return Mono.delay(delay, backoffScheduler); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/retry/Backoff.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2022 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.retry; 18 | 19 | import reactor.util.annotation.Nullable; 20 | 21 | import java.time.Duration; 22 | import java.util.function.Function; 23 | 24 | /** 25 | * Backoff function 26 | * 27 | */ 28 | public interface Backoff extends Function, BackoffDelay> { 29 | 30 | Backoff ZERO_BACKOFF = new Backoff() { 31 | @Override 32 | public BackoffDelay apply(IterationContext context) { 33 | return BackoffDelay.ZERO; 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return "Backoff{ZERO}"; 39 | } 40 | }; 41 | 42 | /** 43 | * Backoff function with no backoff delay 44 | * @return Backoff function for zero backoff delay 45 | */ 46 | static Backoff zero() { 47 | return ZERO_BACKOFF; 48 | } 49 | 50 | /** 51 | * Backoff function with fixed backoff delay 52 | * @param backoffInterval backoff interval 53 | * @return Backoff function with fixed backoff delay 54 | */ 55 | static Backoff fixed(final Duration backoffInterval) { 56 | return new Backoff() { 57 | @Override 58 | public BackoffDelay apply(IterationContext context) { 59 | return new BackoffDelay(backoffInterval); 60 | } 61 | 62 | @Override 63 | public String toString() { 64 | return "Backoff{fixed=" + backoffInterval.toMillis() + "ms}"; 65 | } 66 | }; 67 | } 68 | 69 | /** 70 | * Backoff function with exponential backoff delay. Retries are performed after a backoff 71 | * interval of firstBackoff * (factor ** n) where n is the iteration. If 72 | * maxBackoff is not null, the maximum backoff applied will be limited to 73 | * maxBackoff. 74 | *

75 | * If basedOnPreviousValue is true, backoff will be calculated using 76 | * prevBackoff * factor. When backoffs are combined with {@link Jitter}, this 77 | * value will be different from the actual exponential value for the iteration. 78 | * 79 | * @param firstBackoff First backoff duration 80 | * @param maxBackoff Maximum backoff duration, capped to {@link Long#MAX_VALUE} milliseconds 81 | * @param factor The multiplicand for calculating backoff 82 | * @param basedOnPreviousValue If true, calculation is based on previous value which may 83 | * be a backoff with jitter applied 84 | * @return Backoff function with exponential delay 85 | */ 86 | static Backoff exponential(Duration firstBackoff, @Nullable Duration maxBackoff, int factor, boolean basedOnPreviousValue) { 87 | if (firstBackoff == null || firstBackoff.isNegative() || firstBackoff.isZero()) 88 | throw new IllegalArgumentException("firstBackoff must be > 0"); 89 | Duration cap = Duration.ofMillis(Long.MAX_VALUE); 90 | if (maxBackoff != null && maxBackoff.compareTo(cap) > 0) { 91 | throw new IllegalArgumentException("maxBackoff must be less than Long.MAX_VALUE milliseconds"); 92 | } 93 | Duration maxBackoffInterval = maxBackoff != null && maxBackoff.compareTo(cap) < 0 ? maxBackoff : cap; 94 | if (maxBackoffInterval.compareTo(firstBackoff) < 0) 95 | throw new IllegalArgumentException("maxBackoff must be >= firstBackoff"); 96 | if (!basedOnPreviousValue) { 97 | return new Backoff() { 98 | @Override 99 | public BackoffDelay apply(IterationContext context) { 100 | Duration nextBackoff; 101 | if (context.backoff() != null && context.backoff().compareTo(maxBackoffInterval) >= 0) { 102 | nextBackoff = maxBackoffInterval; 103 | } 104 | else { 105 | try { 106 | nextBackoff = firstBackoff.multipliedBy((long) Math.pow(factor, (context.iteration() - 1))); 107 | if (nextBackoff.compareTo(maxBackoffInterval) >= 0) { 108 | nextBackoff = maxBackoffInterval; 109 | } 110 | } 111 | catch (ArithmeticException e) { 112 | nextBackoff = maxBackoffInterval; 113 | } 114 | } 115 | return new BackoffDelay(firstBackoff, maxBackoffInterval, nextBackoff); 116 | } 117 | 118 | @Override 119 | public String toString() { 120 | return String.format("Backoff{exponential,min=%sms,max=%s,factor=%s,basedOnPreviousValue=false}", 121 | firstBackoff.toMillis(), 122 | maxBackoff == null ? "NONE" : maxBackoff.toMillis() + "ms", 123 | factor); 124 | } 125 | }; 126 | } 127 | else { 128 | return new Backoff() { 129 | @Override 130 | public BackoffDelay apply(IterationContext context) { 131 | Duration prevBackoff = context.backoff() == null ? Duration.ZERO : context.backoff(); 132 | Duration nextBackoff; 133 | if (prevBackoff.compareTo(maxBackoffInterval) >= 0) { 134 | nextBackoff = maxBackoffInterval; 135 | } 136 | else try { 137 | nextBackoff = prevBackoff.multipliedBy(factor); 138 | if (nextBackoff.compareTo(maxBackoffInterval) >= 0) { 139 | nextBackoff = maxBackoffInterval; 140 | } 141 | } 142 | catch (ArithmeticException e) { 143 | nextBackoff = maxBackoffInterval; 144 | } 145 | nextBackoff = nextBackoff.compareTo(firstBackoff) < 0 ? firstBackoff : nextBackoff; 146 | return new BackoffDelay(firstBackoff, maxBackoffInterval, nextBackoff); 147 | } 148 | 149 | @Override 150 | public String toString() { 151 | return String.format("Backoff{exponential,min=%sms,max=%s,factor=%s,basedOnPreviousValue=true}", 152 | firstBackoff.toMillis(), 153 | maxBackoff == null ? "NONE" : maxBackoff.toMillis() + "ms", 154 | factor); 155 | } 156 | }; 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/retry/BackoffDelay.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.retry; 18 | 19 | import java.time.Duration; 20 | 21 | public class BackoffDelay { 22 | 23 | static final BackoffDelay ZERO = new BackoffDelay(Duration.ZERO) { 24 | @Override 25 | public String toString() { 26 | return "{ZERO}"; 27 | } 28 | }; 29 | 30 | final Duration min; 31 | final Duration max; 32 | final Duration delay; 33 | 34 | public BackoffDelay(Duration fixedBackoff) { 35 | this(fixedBackoff, fixedBackoff, fixedBackoff); 36 | } 37 | 38 | public BackoffDelay(Duration min, Duration max, Duration delay) { 39 | this.min = min; 40 | this.max = max; 41 | this.delay = delay; 42 | } 43 | 44 | public Duration minDelay() { 45 | return min; 46 | } 47 | 48 | public Duration maxDelay() { 49 | return max; 50 | } 51 | 52 | public Duration delay() { 53 | return delay; 54 | } 55 | 56 | @Override 57 | public String toString() { 58 | if (min == max && max == delay) { 59 | return "{" + delay.toMillis() + "ms}"; 60 | } 61 | else { 62 | return "{" + delay.toMillis() + "ms/" + max.toMillis() + "ms}"; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/retry/DefaultContext.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.retry; 18 | 19 | import java.time.Duration; 20 | 21 | public class DefaultContext implements RetryContext, RepeatContext { 22 | 23 | final T applicationContext; 24 | final long iteration; 25 | final Long repeatCompanionValue; 26 | final Throwable exception; 27 | final BackoffDelay backoff; 28 | BackoffDelay lastBackoff; 29 | 30 | public DefaultContext(T applicationContext, 31 | long iteration, 32 | BackoffDelay backoff, 33 | long repeatCompanionValue) { 34 | this(applicationContext, iteration, backoff, repeatCompanionValue, null); 35 | } 36 | 37 | public DefaultContext(T applicationContext, 38 | long iteration, 39 | BackoffDelay backoff, 40 | Throwable exception) { 41 | this(applicationContext, iteration, backoff, null, exception); 42 | } 43 | 44 | private DefaultContext(T applicationContext, 45 | long iteration, 46 | BackoffDelay backoff, 47 | Long repeatCompanionValue, 48 | Throwable exception) { 49 | this.applicationContext = applicationContext; 50 | this.iteration = iteration; 51 | this.backoff = backoff; 52 | this.repeatCompanionValue = repeatCompanionValue; 53 | this.exception = exception; 54 | } 55 | 56 | @Override 57 | public T applicationContext() { 58 | return applicationContext; 59 | } 60 | 61 | @Override 62 | public long iteration() { 63 | return iteration; 64 | } 65 | 66 | @Override 67 | public Long companionValue() { 68 | return repeatCompanionValue; 69 | } 70 | 71 | @Override 72 | public Throwable exception() { 73 | return exception; 74 | } 75 | 76 | @Override 77 | public Duration backoff() { 78 | return backoff == null ? null : backoff.delay; 79 | } 80 | 81 | @Override 82 | public String toString() { 83 | if (exception != null) 84 | return String.format("iteration=%d exception=%s backoff=%s", iteration, exception, backoff); 85 | else 86 | return String.format("iteration=%d repeatCompanionValue=%s backoff=%s", iteration, repeatCompanionValue, backoff); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/retry/DefaultRepeat.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.retry; 18 | 19 | import java.time.Duration; 20 | import java.time.Instant; 21 | import java.util.function.Consumer; 22 | import java.util.function.Predicate; 23 | 24 | import org.reactivestreams.Publisher; 25 | 26 | import reactor.core.publisher.Flux; 27 | import reactor.core.scheduler.Scheduler; 28 | import reactor.util.Logger; 29 | import reactor.util.Loggers; 30 | 31 | public class DefaultRepeat extends AbstractRetry implements Repeat { 32 | 33 | static final Logger log = Loggers.getLogger(DefaultRepeat.class); 34 | static final Consumer> NOOP_ON_REPEAT = r -> {}; 35 | 36 | final Predicate> repeatPredicate; 37 | final Consumer> onRepeat; 38 | 39 | DefaultRepeat(Predicate> repeatPredicate, 40 | long maxRepeats, 41 | Duration timeout, 42 | Backoff backoff, 43 | Jitter jitter, 44 | Scheduler backoffScheduler, 45 | final Consumer> onRepeat, 46 | T applicationContext) { 47 | super(maxRepeats, timeout, backoff, jitter, backoffScheduler, applicationContext); 48 | this.repeatPredicate = repeatPredicate; 49 | this.onRepeat = onRepeat; 50 | } 51 | 52 | public static DefaultRepeat create(Predicate> repeatPredicate, long n) { 53 | return new DefaultRepeat(repeatPredicate, 54 | n, 55 | null, 56 | Backoff.zero(), 57 | Jitter.noJitter(), 58 | null, 59 | NOOP_ON_REPEAT, 60 | (T) null); 61 | } 62 | 63 | @Override 64 | public Repeat withApplicationContext(T applicationContext) { 65 | return new DefaultRepeat<>(repeatPredicate, maxIterations, timeout, 66 | backoff, jitter, backoffScheduler, onRepeat, applicationContext); 67 | } 68 | 69 | @Override 70 | public Repeat doOnRepeat(Consumer> onRepeat) { 71 | return new DefaultRepeat<>(repeatPredicate, maxIterations, timeout, 72 | backoff, jitter, backoffScheduler, onRepeat, applicationContext); 73 | } 74 | 75 | @Override 76 | public Repeat timeout(Duration timeout) { 77 | if (timeout.isNegative()) 78 | throw new IllegalArgumentException("timeout should be >= 0"); 79 | return new DefaultRepeat<>(repeatPredicate, maxIterations, timeout, 80 | backoff, jitter, backoffScheduler, onRepeat, applicationContext); 81 | } 82 | 83 | @Override 84 | public Repeat repeatMax(long maxRepeats) { 85 | if (maxRepeats < 1) 86 | throw new IllegalArgumentException("maxRepeats should be > 0"); 87 | return new DefaultRepeat<>(repeatPredicate, maxRepeats, timeout, backoff, jitter, 88 | backoffScheduler, onRepeat, applicationContext); 89 | } 90 | 91 | @Override 92 | public Repeat backoff(Backoff backoff) { 93 | return new DefaultRepeat<>(repeatPredicate, maxIterations, timeout, 94 | backoff, jitter, backoffScheduler, onRepeat, applicationContext); 95 | } 96 | 97 | @Override 98 | public Repeat jitter(Jitter jitter) { 99 | return new DefaultRepeat<>(repeatPredicate, maxIterations, timeout, 100 | backoff, jitter, backoffScheduler, onRepeat, applicationContext); 101 | } 102 | 103 | @Override 104 | public Repeat withBackoffScheduler(Scheduler scheduler) { 105 | return new DefaultRepeat<>(repeatPredicate, maxIterations, timeout, 106 | backoff, jitter, scheduler, onRepeat, applicationContext); 107 | } 108 | 109 | @Override 110 | public Publisher apply(Flux companionValues) { 111 | Instant timeoutInstant = calculateTimeout(); 112 | DefaultContext context = new DefaultContext<>(applicationContext, 0, null, -1L); 113 | return companionValues 114 | .index() 115 | .map(tuple -> repeatBackoff(tuple.getT2(), tuple.getT1() + 1L, timeoutInstant, context)) 116 | .takeWhile(backoff -> backoff != RETRY_EXHAUSTED) 117 | .concatMap(backoff -> retryMono(backoff.delay)); 118 | } 119 | 120 | BackoffDelay repeatBackoff(Long companionValue, Long iteration, Instant timeoutInstant, DefaultContext context) { 121 | DefaultContext tmpContext = new DefaultContext<>(applicationContext, iteration, context.lastBackoff, companionValue); 122 | BackoffDelay nextBackoff = calculateBackoff(tmpContext, timeoutInstant); 123 | DefaultContext repeatContext = new DefaultContext<>(applicationContext, iteration, nextBackoff, companionValue); 124 | context.lastBackoff = nextBackoff; 125 | 126 | if (!repeatPredicate.test(repeatContext)) { 127 | log.debug("Stopping repeats since predicate returned false, retry context: {}", repeatContext); 128 | return RETRY_EXHAUSTED; 129 | } 130 | else if (nextBackoff == RETRY_EXHAUSTED) { 131 | log.debug("Repeats exhausted, retry context: {}", repeatContext); 132 | return RETRY_EXHAUSTED; 133 | } 134 | else { 135 | log.debug("Scheduling repeat attempt, retry context: {}", repeatContext); 136 | onRepeat.accept(repeatContext); 137 | return nextBackoff; 138 | } 139 | } 140 | 141 | @Override 142 | public String toString() { 143 | return "Repeat{times=" + this.maxIterations + ",backoff=" + backoff + ",jitter=" + 144 | jitter + "}"; 145 | } 146 | } 147 | 148 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/retry/DefaultRetry.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2022 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.retry; 18 | 19 | import java.time.Duration; 20 | import java.time.Instant; 21 | import java.util.function.Consumer; 22 | import java.util.function.Predicate; 23 | 24 | import org.reactivestreams.Publisher; 25 | 26 | import reactor.core.publisher.Flux; 27 | import reactor.core.publisher.Mono; 28 | import reactor.core.scheduler.Scheduler; 29 | import reactor.util.Logger; 30 | import reactor.util.Loggers; 31 | 32 | /** 33 | * @deprecated To be removed in 3.6.0 at the earliest. Use equivalent features of reactor-core like 34 | * {@link reactor.util.retry.RetrySpec} and {@link reactor.util.retry.RetryBackoffSpec} instead. 35 | */ 36 | @Deprecated 37 | public class DefaultRetry extends AbstractRetry implements Retry { 38 | 39 | static final Logger log = Loggers.getLogger(DefaultRetry.class); 40 | static final Consumer> NOOP_ON_RETRY = r -> {}; 41 | 42 | final Predicate> retryPredicate; 43 | final Consumer> onRetry; 44 | 45 | DefaultRetry(Predicate> retryPredicate, 46 | long maxIterations, 47 | Duration timeout, 48 | Backoff backoff, 49 | Jitter jitter, 50 | Scheduler backoffScheduler, 51 | final Consumer> onRetry, 52 | T applicationContext) { 53 | super(maxIterations, timeout, backoff, jitter, backoffScheduler, applicationContext); 54 | this.retryPredicate = retryPredicate; 55 | this.onRetry = onRetry; 56 | } 57 | 58 | public static DefaultRetry create(Predicate> retryPredicate) { 59 | return new DefaultRetry(retryPredicate, 60 | Long.MAX_VALUE, 61 | null, 62 | Backoff.zero(), 63 | Jitter.noJitter(), 64 | null, 65 | NOOP_ON_RETRY, 66 | (T) null); 67 | } 68 | 69 | @Override 70 | public Retry withApplicationContext(T applicationContext) { 71 | return new DefaultRetry<>(retryPredicate, maxIterations, timeout, 72 | backoff, jitter, backoffScheduler, onRetry, applicationContext); 73 | } 74 | 75 | @Override 76 | public Retry doOnRetry(Consumer> onRetry) { 77 | return new DefaultRetry<>(retryPredicate, maxIterations, timeout, 78 | backoff, jitter, backoffScheduler, onRetry, applicationContext); 79 | } 80 | 81 | @Override 82 | public Retry retryMax(long maxIterations) { 83 | if (maxIterations < 0) 84 | throw new IllegalArgumentException("maxIterations should be >= 0"); 85 | return new DefaultRetry<>(retryPredicate, maxIterations, timeout, 86 | backoff, jitter, backoffScheduler, onRetry, applicationContext); 87 | } 88 | 89 | @Override 90 | public Retry timeout(Duration timeout) { 91 | if (timeout.isNegative()) 92 | throw new IllegalArgumentException("timeout should be >= 0"); 93 | return new DefaultRetry<>(retryPredicate, maxIterations, timeout, 94 | backoff, jitter, backoffScheduler, onRetry, applicationContext); 95 | } 96 | 97 | @Override 98 | public Retry backoff(Backoff backoff) { 99 | return new DefaultRetry<>(retryPredicate, maxIterations, timeout, 100 | backoff, jitter, backoffScheduler, onRetry, applicationContext); 101 | } 102 | 103 | @Override 104 | public Retry jitter(Jitter jitter) { 105 | return new DefaultRetry<>(retryPredicate, maxIterations, timeout, 106 | backoff, jitter, backoffScheduler, onRetry, applicationContext); 107 | } 108 | 109 | @Override 110 | public Retry withBackoffScheduler(Scheduler scheduler) { 111 | return new DefaultRetry<>(retryPredicate, maxIterations, timeout, 112 | backoff, jitter, scheduler, onRetry, applicationContext); 113 | } 114 | 115 | @Override 116 | public Publisher apply(Flux errors) { 117 | Instant timeoutInstant = calculateTimeout(); 118 | DefaultContext context = new DefaultContext<>(applicationContext, 0L, null, null); 119 | return errors.index() 120 | .concatMap(tuple -> retry(tuple.getT2(), tuple.getT1() + 1L, timeoutInstant, context)); 121 | } 122 | 123 | Publisher retry(Throwable e, long iteration, Instant timeoutInstant, DefaultContext context) { 124 | DefaultContext tmpContext = new DefaultContext<>(applicationContext, iteration, context.lastBackoff, e); 125 | BackoffDelay nextBackoff = calculateBackoff(tmpContext, timeoutInstant); 126 | DefaultContext retryContext = new DefaultContext(applicationContext, iteration, nextBackoff, e); 127 | context.lastBackoff = nextBackoff; 128 | 129 | if (!retryPredicate.test(retryContext)) { 130 | log.debug("Stopping retries since predicate returned false, retry context: {}", retryContext); 131 | return Mono.error(e); 132 | } 133 | else if (nextBackoff == RETRY_EXHAUSTED) { 134 | log.debug("Retries exhausted, retry context: {}", retryContext); 135 | return Mono.error(new RetryExhaustedException(e)); 136 | } 137 | else { 138 | log.debug("Scheduling retry attempt, retry context: {}", retryContext); 139 | onRetry.accept(retryContext); 140 | return retryMono(nextBackoff.delay()); 141 | } 142 | } 143 | 144 | @Override 145 | public String toString() { 146 | return "Retry{max=" + this.maxIterations + ",backoff=" + backoff + ",jitter=" + 147 | jitter + "}"; 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/retry/IterationContext.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.retry; 18 | 19 | import java.time.Duration; 20 | 21 | /** 22 | * Context provided to retry or repeat callbacks. 23 | * 24 | * @param Application context type 25 | */ 26 | public interface IterationContext { 27 | 28 | /** 29 | * Application context that may be used to perform any rollbacks before 30 | * a retry. Application context can be configured using {@link Retry#withApplicationContext(Object)} 31 | * or {@link Repeat#withApplicationContext(Object)}. 32 | * 33 | * @return application context 34 | */ 35 | public T applicationContext(); 36 | 37 | /** 38 | * The next iteration number. This is a zero-based incrementing number with 39 | * the first attempt prior to any retries as iteration zero. 40 | * @return the current iteration number 41 | */ 42 | public long iteration(); 43 | 44 | /** 45 | * The backoff delay. When {@link Backoff} function is invoked, the previous 46 | * backoff is provided in the context. The context provided for the retry 47 | * predicates {@link Retry#onlyIf(java.util.function.Predicate)} and 48 | * {@link Repeat#onlyIf(java.util.function.Predicate)} as well as the retry 49 | * callbacks {@link Retry#doOnRetry(java.util.function.Consumer)} and 50 | * {@link Repeat#doOnRepeat(java.util.function.Consumer)} provide the 51 | * backoff delay for the next retry. 52 | * 53 | * @return Backoff delay 54 | */ 55 | public Duration backoff(); 56 | } 57 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/retry/Jitter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.retry; 18 | 19 | import java.time.Duration; 20 | import java.util.concurrent.ThreadLocalRandom; 21 | import java.util.function.Function; 22 | 23 | /** 24 | * Jitter function that is applied to the backoff delay. 25 | * 26 | */ 27 | public interface Jitter extends Function { 28 | 29 | Jitter NO_JITTER = new Jitter() { 30 | @Override 31 | public Duration apply(BackoffDelay delay) { 32 | return delay.delay(); 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | return "Jitter{NONE}"; 38 | } 39 | }; 40 | 41 | Jitter RANDOM_JITTER = new RandomJitter(0.5); 42 | 43 | /** 44 | * Jitter function that is a no-op. 45 | * @return Jitter function that does not apply any jitter 46 | */ 47 | static Jitter noJitter() { 48 | return NO_JITTER; 49 | } 50 | 51 | /** 52 | * Jitter function that applies a random jitter with a factor of 0.5, generating a 53 | * backoff between {@code [d - d*0.5; d + d*0.5]} (but still within the limits of 54 | * [{@link BackoffDelay#minDelay()}; {@link BackoffDelay#maxDelay()}]. 55 | * @return Jitter function to randomize backoff delay 56 | */ 57 | static Jitter random() { 58 | return RANDOM_JITTER; 59 | } 60 | 61 | /** 62 | * Jitter function that applies a random jitter with a provided [0; 1] factor (default 0.5), 63 | * generating a backoff between {@code [d - d*factor; d + d*factor]} (but still within 64 | * the limits of [{@link BackoffDelay#minDelay()}; {@link BackoffDelay#maxDelay()}]. 65 | * @return Jitter function to randomize backoff delay 66 | */ 67 | static Jitter random(double randomFactor) { 68 | return new RandomJitter(randomFactor); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/retry/RandomJitter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.retry; 18 | 19 | import java.time.Duration; 20 | import java.util.concurrent.ThreadLocalRandom; 21 | 22 | /** 23 | * Randomized Jitter with a factor, that works on BackoffDelay with min <= d <= max and 24 | * maintain that invariant on the randomized {@literal d}. 25 | * 26 | * @author Simon Baslé 27 | */ 28 | class RandomJitter implements Jitter { 29 | 30 | private final double randomFactor; 31 | 32 | public RandomJitter(double randomFactor) { 33 | if (randomFactor < 0 || randomFactor > 1) throw new IllegalArgumentException("random factor must be between 0 and 1 (default 0.5)"); 34 | this.randomFactor = randomFactor; 35 | } 36 | 37 | @Override 38 | public Duration apply(BackoffDelay backoff) { 39 | //check the invariant 40 | if (backoff.delay.compareTo(backoff.min) < 0) { 41 | throw new IllegalArgumentException("jitter can only be applied on a delay that is >= to min backoff"); 42 | } 43 | if (backoff.delay.compareTo(backoff.max) > 0) { 44 | throw new IllegalArgumentException("jitter can only be applied on a delay that is <= to max backoff"); 45 | } 46 | 47 | //short-circuit delay == 0 case 48 | if (backoff.delay.isZero()) { 49 | return backoff.delay; 50 | } 51 | 52 | ThreadLocalRandom random = ThreadLocalRandom.current(); 53 | 54 | long jitterOffset = jitterOffsetCapped(backoff); 55 | long lowBound = lowJitterBound(backoff, jitterOffset); 56 | long highBound = highJitterBound(backoff, jitterOffset); 57 | 58 | long jitter; 59 | if (highBound == lowBound) { 60 | if (highBound == 0) jitter = 0; 61 | else jitter = random.nextLong(highBound); 62 | } 63 | else { 64 | jitter = random.nextLong(lowBound, highBound); 65 | } 66 | return backoff.delay.plusMillis(jitter); 67 | } 68 | 69 | /** 70 | * Compute the jitter offset that will be used for the bounds of the random jitter, 71 | * in a way that is safe for large {@link Duration} that go over Long.MAX_VALUE ms. 72 | */ 73 | long jitterOffsetCapped(BackoffDelay backoff) { 74 | try { 75 | return backoff.delay.multipliedBy((long) (100 * randomFactor)) 76 | .dividedBy(100) 77 | .toMillis(); 78 | } 79 | catch (ArithmeticException ae) { 80 | return Math.round(Long.MAX_VALUE * randomFactor); 81 | } 82 | } 83 | 84 | /** 85 | * Compute a lower bound for the random jitter that won't let the final delay go 86 | * below {@link BackoffDelay#minDelay()}. 87 | * 88 | * @param backoff the original backoff constraints to work with 89 | * @param jitterOffset the jitter offset 90 | * @return a lower bound for the random generation function, so that delay + jitter >= min 91 | */ 92 | long lowJitterBound(BackoffDelay backoff, long jitterOffset) { 93 | return Math.max( 94 | backoff.min.minus(backoff.delay).toMillis(), 95 | -jitterOffset); 96 | } 97 | 98 | /** 99 | * Compute a higher bound for the random jitter that won't let the final delay go 100 | * over {@link BackoffDelay#maxDelay()}. 101 | * 102 | * @param backoff the original backoff constraints to work with 103 | * @param jitterOffset the jitter offset 104 | * @return a higher bound for the random generation function, so that delay + jitter <= max 105 | */ 106 | long highJitterBound(BackoffDelay backoff, long jitterOffset) { 107 | return Math.min( 108 | backoff.max.minus(backoff.delay).toMillis(), 109 | jitterOffset); 110 | } 111 | 112 | @Override 113 | public String toString() { 114 | return "Jitter{RANDOM-" + randomFactor + "}"; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/retry/RepeatContext.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.retry; 18 | 19 | import reactor.core.publisher.Flux; 20 | import reactor.core.publisher.Mono; 21 | 22 | /** 23 | * Context provided to repeat predicate {@link Repeat#onlyIf(java.util.function.Predicate)} and 24 | * the repeat callback {@link Repeat#doOnRepeat(java.util.function.Consumer)}. 25 | * 26 | * @param Application context type 27 | */ 28 | public interface RepeatContext extends IterationContext { 29 | 30 | /** 31 | * Returns the value provided in the companion Flux for repeats. 32 | *

    33 | *
  • For {@link Flux#repeatWhen(java.util.function.Function)} and {@link Mono#repeatWhen(java.util.function.Function)}, 34 | * value is the number of items emitted in the last attempt. 35 | *
  • For {@link Mono#repeatWhenEmpty(java.util.function.Function)} and {@link Mono#repeatWhenEmpty(int, java.util.function.Function)}, 36 | * value is a zero-based incrementing Long, which is number of attempts - 1. 37 | *
38 | * @return value the value emitted on the companion Flux for repeats. 39 | */ 40 | public Long companionValue(); 41 | } 42 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/retry/RetryContext.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2022 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.retry; 18 | 19 | /** 20 | * Context provided to retry predicate {@link Retry#onlyIf(java.util.function.Predicate)} and 21 | * the retry callback {@link Retry#doOnRetry(java.util.function.Consumer)}. 22 | * 23 | * @param Application context type 24 | * @deprecated To be removed in 3.6.0 at the earliest. Use equivalent features of reactor-core like 25 | * {@link reactor.util.retry.RetrySpec} and {@link reactor.util.retry.RetryBackoffSpec} instead. 26 | */ 27 | @Deprecated 28 | public interface RetryContext extends IterationContext { 29 | 30 | /** 31 | * Returns the exception from the last iteration. 32 | * @return exception that resulted in retry 33 | */ 34 | public Throwable exception(); 35 | } 36 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/retry/RetryExhaustedException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2022 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.retry; 18 | 19 | /** 20 | * Exception indicating that retries have been exhausted after 21 | * {@link Retry#timeout(java.time.Duration)} or {@link Retry#retryMax(long)}. 22 | * For retries, {@link #getCause()} returns the original exception from the 23 | * last retry attempt that generated this exception. 24 | * @deprecated To be removed in 3.6.0 at the earliest. Use equivalent features of reactor-core like 25 | * {@link reactor.core.Exceptions#retryExhausted(String, Throwable)} and {@link reactor.core.Exceptions#isRetryExhausted(Throwable)} instead. 26 | */ 27 | @Deprecated 28 | public class RetryExhaustedException extends RuntimeException { 29 | 30 | private static final long serialVersionUID = 6961442923363481283L; 31 | 32 | public RetryExhaustedException() { 33 | super(); 34 | } 35 | 36 | public RetryExhaustedException(String message, Throwable cause, boolean enableSuppression, 37 | boolean writableStackTrace) { 38 | super(message, cause, enableSuppression, writableStackTrace); 39 | } 40 | 41 | public RetryExhaustedException(String message, Throwable cause) { 42 | super(message, cause); 43 | } 44 | 45 | public RetryExhaustedException(String message) { 46 | super(message); 47 | } 48 | 49 | public RetryExhaustedException(Throwable cause) { 50 | super(cause); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /reactor-extra/src/main/java/reactor/scheduler/clock/SchedulerClock.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.scheduler.clock; 18 | 19 | import java.time.Clock; 20 | import java.time.Instant; 21 | import java.time.ZoneId; 22 | import java.util.concurrent.TimeUnit; 23 | 24 | import reactor.core.scheduler.Scheduler; 25 | import reactor.util.annotation.NonNull; 26 | 27 | /** 28 | * Clock adapter around {@link Scheduler}. That adoption gives better integration with 29 | * Java 8 DateTime API. 30 | *

31 | * This feature may be useful for testing purpose, especially for integration with {@code 32 | * VirtualTimeScheduler}. For example: 33 | *

34 | *

 35 |  *     
 36 |  *         VirtualTimeScheduler scheduler = VirtualTimeScheduler.create();
 37 |  *         SchedulerClock clock = SchedulerClock.of(scheduler);
 38 |  *
 39 |  *         ZonedDateTime beforeAdvance = ZonedDateTime.now(clock);
 40 |  *         scheduler.advanceTimeBy(Duration.ofSeconds(1));
 41 |  *         ZonedDateTime afterAdvance = ZonedDateTime.now(clock);
 42 |  *
 43 |  *         Assert.assertTrue(beforeAdvance.isBefore(afterAdvance));
 44 |  *     
 45 |  * 
46 | *

47 | * 48 | * @author Oleh Dokuka 49 | * @author Peter Royal 50 | * @since 3.1.4 51 | */ 52 | public class SchedulerClock extends Clock { 53 | 54 | /** 55 | * Nanos per second. 56 | */ 57 | private static final long NANOS_PER_SECOND = 1000_000_000L; 58 | 59 | private final Scheduler scheduler; 60 | private final ZoneId zone; 61 | 62 | private SchedulerClock(Scheduler scheduler, ZoneId zone) { 63 | this.scheduler = scheduler; 64 | this.zone = zone; 65 | } 66 | 67 | @Override 68 | @NonNull 69 | public ZoneId getZone() { 70 | return zone; 71 | } 72 | 73 | @Override 74 | @NonNull 75 | public SchedulerClock withZone(ZoneId zone) { 76 | return new SchedulerClock(scheduler, zone); 77 | } 78 | 79 | /** 80 | * Return wrapped Scheduler instance 81 | * 82 | * @return {@link Scheduler} instance 83 | */ 84 | @NonNull 85 | public Scheduler getScheduler() { 86 | return scheduler; 87 | } 88 | 89 | /** 90 | * Builder method that returns new instance of {@link SchedulerClock} which is 91 | * constructed from old {@link ZoneId} and passed {@link Scheduler} instance 92 | * 93 | * @return {@link SchedulerClock} instance 94 | */ 95 | @NonNull 96 | public SchedulerClock withScheduler(Scheduler scheduler) { 97 | return new SchedulerClock(scheduler, zone); 98 | } 99 | 100 | @Override 101 | public long millis() { 102 | return scheduler.now(TimeUnit.MILLISECONDS); 103 | } 104 | 105 | /** 106 | * Gets the current instant of the clock. 107 | *

108 | * This returns an instant representing the current instant as defined by the clock. 109 | *

110 | * Note: If scheduler does not support time in nanoseconds the returned {@link 111 | * Instant} will be limited by milliseconds 112 | * 113 | * @return the current instant from this clock, not null 114 | */ 115 | @Override 116 | @NonNull 117 | public Instant instant() { 118 | long nano = scheduler.now(TimeUnit.NANOSECONDS); 119 | long secs = Math.floorDiv(nano, NANOS_PER_SECOND); 120 | long nos = Math.floorMod(nano, NANOS_PER_SECOND); 121 | 122 | return Instant.ofEpochSecond(secs, nos); 123 | } 124 | 125 | @Override 126 | public boolean equals(Object o) { 127 | if (this == o) { 128 | return true; 129 | } 130 | if (o == null || getClass() != o.getClass()) { 131 | return false; 132 | } 133 | if (!super.equals(o)) { 134 | return false; 135 | } 136 | 137 | SchedulerClock that = (SchedulerClock) o; 138 | 139 | return scheduler.equals(that.scheduler) && zone.equals(that.zone); 140 | 141 | } 142 | 143 | @Override 144 | public int hashCode() { 145 | int result = super.hashCode(); 146 | result = 31 * result + scheduler.hashCode(); 147 | result = 31 * result + zone.hashCode(); 148 | return result; 149 | } 150 | 151 | @Override 152 | public String toString() { 153 | return "SchedulerClock{" + "scheduler=" + scheduler + ", zone=" + zone + '}'; 154 | } 155 | 156 | /** 157 | * Create instance of {@link SchedulerClock} from given {@link Scheduler} and system 158 | * default {@link ZoneId#systemDefault()} 159 | * 160 | * @param scheduler {@link Scheduler} instance 161 | * 162 | * @return new {@link SchedulerClock} 163 | */ 164 | @NonNull 165 | public static SchedulerClock of(@NonNull Scheduler scheduler) { 166 | return new SchedulerClock(scheduler, ZoneId.systemDefault()); 167 | } 168 | 169 | /** 170 | * Create instance of {@link SchedulerClock} from given {@link Scheduler} and {@link 171 | * ZoneId} 172 | * 173 | * @param scheduler {@link Scheduler} instance 174 | * 175 | * @return new {@link SchedulerClock} 176 | */ 177 | @NonNull 178 | public static SchedulerClock of(@NonNull Scheduler scheduler, 179 | @NonNull ZoneId zoneId) { 180 | return new SchedulerClock(scheduler, zoneId); 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /reactor-extra/src/test/java/reactor/bool/BooleanUtilsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.bool; 18 | 19 | import static org.assertj.core.api.Assertions.assertThat; 20 | import static reactor.bool.BooleanUtils.*; 21 | import static reactor.bool.BooleanUtils.not; 22 | 23 | import org.junit.Test; 24 | import reactor.core.publisher.Mono; 25 | import reactor.test.StepVerifier; 26 | 27 | /** 28 | * @author Greg Turnquist 29 | */ 30 | public class BooleanUtilsTest { 31 | 32 | private static final Mono TRUE = Mono.just(true); 33 | private static final Mono FALSE = Mono.just(false); 34 | 35 | @Test 36 | public void testNot() { 37 | StepVerifier.create(not(TRUE)) 38 | .assertNext(bool -> assertThat(bool).as("not(TRUE)").isFalse()) 39 | .verifyComplete(); 40 | 41 | StepVerifier.create(not(FALSE)) 42 | .assertNext(bool -> assertThat(bool).as("not(FALSE)").isTrue()) 43 | .verifyComplete(); 44 | } 45 | 46 | @Test 47 | public void testAnd() { 48 | StepVerifier.create(and(TRUE, TRUE)) 49 | .assertNext(bool -> assertThat(bool).as("and(TRUE, TRUE)").isTrue()) 50 | .verifyComplete(); 51 | 52 | StepVerifier.create(and(TRUE, FALSE)) 53 | .assertNext(bool -> assertThat(bool).as("and(TRUE, FALSE)").isFalse()) 54 | .verifyComplete(); 55 | 56 | StepVerifier.create(and(FALSE, TRUE)) 57 | .assertNext(bool -> assertThat(bool).as("and(FALSE, TRUE)").isFalse()) 58 | .verifyComplete(); 59 | 60 | StepVerifier.create(and(FALSE, FALSE)) 61 | .assertNext(bool -> assertThat(bool).as("and(FALSE, FALSE)").isFalse()) 62 | .verifyComplete(); 63 | } 64 | 65 | @Test 66 | public void testOr() { 67 | StepVerifier.create(or(TRUE, TRUE)) 68 | .assertNext(bool -> assertThat(bool).as("or(TRUE, TRUE)").isTrue()) 69 | .verifyComplete(); 70 | 71 | StepVerifier.create(or(TRUE, FALSE)) 72 | .assertNext(bool -> assertThat(bool).as("or(TRUE, FALSE)").isTrue()) 73 | .verifyComplete(); 74 | 75 | StepVerifier.create(or(FALSE, TRUE)) 76 | .assertNext(bool -> assertThat(bool).as("or(FALSE, TRUE)").isTrue()) 77 | .verifyComplete(); 78 | 79 | StepVerifier.create(or(FALSE, FALSE)) 80 | .assertNext(bool -> assertThat(bool).as("or(FALSE, FALSE)").isFalse()) 81 | .verifyComplete(); 82 | } 83 | 84 | @Test 85 | public void testNand() { 86 | StepVerifier.create(nand(TRUE, TRUE)) 87 | .assertNext(bool -> assertThat(bool).as("nand(TRUE, TRUE)").isFalse()) 88 | .verifyComplete(); 89 | 90 | StepVerifier.create(nand(TRUE, FALSE)) 91 | .assertNext(bool -> assertThat(bool).as("nand(TRUE, FALSE)").isTrue()) 92 | .verifyComplete(); 93 | 94 | StepVerifier.create(nand(FALSE, TRUE)) 95 | .assertNext(bool -> assertThat(bool).as("nand(FALSE, TRUE)").isTrue()) 96 | .verifyComplete(); 97 | 98 | StepVerifier.create(nand(FALSE, FALSE)) 99 | .assertNext(bool -> assertThat(bool).as("nand(FALSE, FALSE)").isTrue()) 100 | .verifyComplete(); 101 | } 102 | 103 | @Test 104 | public void testNor() { 105 | StepVerifier.create(nor(TRUE, TRUE)) 106 | .assertNext(bool -> assertThat(bool).as("nor(TRUE, TRUE)").isFalse()) 107 | .verifyComplete(); 108 | 109 | StepVerifier.create(nor(TRUE, FALSE)) 110 | .assertNext(bool -> assertThat(bool).as("nor(TRUE, FALSE)").isFalse()) 111 | .verifyComplete(); 112 | 113 | StepVerifier.create(nor(FALSE, TRUE)) 114 | .assertNext(bool -> assertThat(bool).as("nor(FALSE, TRUE)").isFalse()) 115 | .verifyComplete(); 116 | 117 | StepVerifier.create(nor(FALSE, FALSE)) 118 | .assertNext(bool -> assertThat(bool).as("nor(FALSE, FALSE)").isTrue()) 119 | .verifyComplete(); 120 | } 121 | 122 | @Test 123 | public void testXor() { 124 | StepVerifier.create(xor(TRUE, TRUE)) 125 | .assertNext(bool -> assertThat(bool).as("xor(TRUE, TRUE)").isFalse()) 126 | .verifyComplete(); 127 | 128 | StepVerifier.create(xor(TRUE, FALSE)) 129 | .assertNext(bool -> assertThat(bool).as("xor(TRUE, FALSE)").isTrue()) 130 | .verifyComplete(); 131 | 132 | StepVerifier.create(xor(FALSE, TRUE)) 133 | .assertNext(bool -> assertThat(bool).as("xor(FALSE, TRUE)").isTrue()) 134 | .verifyComplete(); 135 | 136 | StepVerifier.create(xor(FALSE, FALSE)) 137 | .assertNext(bool -> assertThat(bool).as("xor(FALSE, FALSE)").isFalse()) 138 | .verifyComplete(); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /reactor-extra/src/test/java/reactor/retry/AbstractRetryTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2022 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.retry; 18 | 19 | import org.junit.Test; 20 | import org.reactivestreams.Publisher; 21 | import reactor.core.publisher.Flux; 22 | import reactor.test.scheduler.VirtualTimeScheduler; 23 | 24 | import java.time.Duration; 25 | import java.time.Instant; 26 | 27 | import static org.assertj.core.api.Assertions.assertThat; 28 | 29 | public class AbstractRetryTest { 30 | 31 | @Test 32 | public void calculateTimeoutUsesScheduler() { 33 | VirtualTimeScheduler scheduler = VirtualTimeScheduler.create(); 34 | 35 | AbstractRetry abstractRetry = new AbstractRetry(2, Duration.ofSeconds(1), 36 | Backoff.ZERO_BACKOFF, Jitter.NO_JITTER, scheduler, null) { 37 | @Override 38 | public Publisher apply(Flux integerFlux) { 39 | return null; 40 | } 41 | }; 42 | 43 | assertThat(abstractRetry.calculateTimeout().toEpochMilli()) 44 | .as("at clock 0") 45 | .isEqualTo(1000L); 46 | 47 | scheduler.advanceTimeBy(Duration.ofSeconds(3)); 48 | 49 | assertThat(abstractRetry.calculateTimeout().toEpochMilli()) 50 | .as("after clock move") 51 | .isEqualTo(4000L); 52 | } 53 | 54 | @Test 55 | public void calculateBackoffUsesScheduler() { 56 | VirtualTimeScheduler scheduler = VirtualTimeScheduler.create(); 57 | Instant timeoutInstant = Instant.ofEpochMilli(1000); 58 | 59 | AbstractRetry abstractRetry = new AbstractRetry(2, Duration.ofSeconds(1), 60 | Backoff.fixed(Duration.ofMillis(600)), Jitter.NO_JITTER, scheduler, null) { 61 | @Override 62 | public Publisher apply(Flux integerFlux) { 63 | return null; 64 | } 65 | }; 66 | 67 | RepeatContext retryContext = new DefaultContext<>(null, 1, BackoffDelay.ZERO, null); 68 | 69 | BackoffDelay backoff = abstractRetry.calculateBackoff(retryContext, timeoutInstant); 70 | assertThat(backoff) 71 | .as("at clock 0") 72 | .isNotSameAs(AbstractRetry.RETRY_EXHAUSTED) 73 | .satisfies(b -> assertThat(b.delay).isEqualTo(Duration.ofMillis(600))); 74 | 75 | scheduler.advanceTimeBy(Duration.ofMillis(500)); 76 | 77 | backoff = abstractRetry.calculateBackoff(retryContext, timeoutInstant); 78 | assertThat(backoff) 79 | .as("at clock 500") 80 | .isSameAs(AbstractRetry.RETRY_EXHAUSTED); 81 | } 82 | 83 | @Test 84 | public void calculateTimeoutUsesDefaultClockWhenNoScheduler() { 85 | AbstractRetry abstractRetry = new AbstractRetry(2, Duration.ofSeconds(1), 86 | Backoff.ZERO_BACKOFF, Jitter.NO_JITTER, null, null) { 87 | @Override 88 | public Publisher apply(Flux integerFlux) { 89 | return null; 90 | } 91 | }; 92 | 93 | assertThat(abstractRetry.calculateTimeout().toEpochMilli()) 94 | .isGreaterThanOrEqualTo(1000L + Instant.now().toEpochMilli()); 95 | } 96 | 97 | @Test 98 | public void calculateBackoffUsesDefaultClockWhenNoScheduler() { 99 | Instant timeoutInstant = Instant.now().plusSeconds(3); 100 | 101 | AbstractRetry abstractRetry = new AbstractRetry(2, Duration.ofSeconds(1), 102 | Backoff.fixed(Duration.ofMillis(600)), Jitter.NO_JITTER, null, null) { 103 | @Override 104 | public Publisher apply(Flux integerFlux) { 105 | return null; 106 | } 107 | }; 108 | 109 | RepeatContext retryContext = new DefaultContext<>(null, 1, BackoffDelay.ZERO, null); 110 | 111 | BackoffDelay backoff = abstractRetry.calculateBackoff(retryContext, timeoutInstant); 112 | assertThat(backoff) 113 | .isNotSameAs(AbstractRetry.RETRY_EXHAUSTED) 114 | .satisfies(b -> assertThat(b.delay).isEqualTo(Duration.ofMillis(600))); 115 | } 116 | 117 | } -------------------------------------------------------------------------------- /reactor-extra/src/test/java/reactor/retry/BackoffDelayTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.retry; 18 | 19 | import java.time.Duration; 20 | 21 | import org.junit.Test; 22 | 23 | import static org.assertj.core.api.Assertions.assertThat; 24 | 25 | /** 26 | * @author Simon Baslé 27 | */ 28 | public class BackoffDelayTest { 29 | 30 | @Test 31 | public void toStringZero() { 32 | assertThat(BackoffDelay.ZERO.toString()) 33 | .isEqualTo("{ZERO}"); 34 | } 35 | 36 | @Test 37 | public void toStringExhausted() { 38 | assertThat(AbstractRetry.RETRY_EXHAUSTED.toString()) 39 | .isEqualTo("{EXHAUSTED}"); 40 | } 41 | 42 | @Test 43 | public void toStringSimple() { 44 | assertThat(new BackoffDelay(Duration.ofSeconds(3)).toString()) 45 | .isEqualTo("{3000ms}"); 46 | } 47 | 48 | @Test 49 | public void toStringMinMaxDelay() { 50 | assertThat(new BackoffDelay( 51 | Duration.ofSeconds(3), 52 | Duration.ofSeconds(4), 53 | Duration.ofMillis(123) 54 | ).toString()) 55 | .isEqualTo("{123ms/4000ms}"); 56 | } 57 | 58 | } -------------------------------------------------------------------------------- /reactor-extra/src/test/java/reactor/retry/DefaultRepeatTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.retry; 18 | 19 | import java.time.Duration; 20 | 21 | import org.junit.Test; 22 | 23 | import static org.assertj.core.api.Assertions.assertThat; 24 | 25 | public class DefaultRepeatTest { 26 | 27 | @Test 28 | public void stringJustOnce() { 29 | Repeat test = Repeat.once(); 30 | 31 | assertThat(test.toString()) 32 | .isEqualTo("Repeat{times=1,backoff=Backoff{ZERO},jitter=Jitter{NONE}}"); 33 | } 34 | 35 | @Test 36 | public void stringTwiceFixedNoJitter() { 37 | Repeat test = Repeat.times(2) 38 | .backoff(Backoff.fixed(Duration.ofHours(2))); 39 | 40 | assertThat(test.toString()) 41 | .isEqualTo("Repeat{times=2,backoff=Backoff{fixed=7200000ms},jitter=Jitter{NONE}}"); 42 | } 43 | 44 | @Test 45 | public void stringThreeTimesExponentialRandomJitter() { 46 | Backoff backoff = Backoff.exponential( 47 | Duration.ofMillis(12), 48 | Duration.ofMinutes(2), 49 | 3, 50 | true); 51 | Repeat test = Repeat.times(3) 52 | .backoff(backoff) 53 | .jitter(Jitter.random()); 54 | 55 | assertThat(test.toString()) 56 | .isEqualTo("Repeat{times=3,backoff=" + backoff + ",jitter=Jitter{RANDOM-0.5}}"); 57 | } 58 | 59 | } -------------------------------------------------------------------------------- /reactor-extra/src/test/java/reactor/retry/DefaultRetryTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2022 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.retry; 18 | 19 | import java.time.Duration; 20 | 21 | import org.junit.Test; 22 | 23 | import static org.assertj.core.api.Assertions.assertThat; 24 | 25 | @Deprecated 26 | public class DefaultRetryTest { 27 | 28 | @Test 29 | public void stringJustOnce() { 30 | Retry test = Retry.any().retryOnce(); 31 | 32 | assertThat(test.toString()) 33 | .isEqualTo("Retry{max=1,backoff=Backoff{ZERO},jitter=Jitter{NONE}}"); 34 | } 35 | 36 | @Test 37 | public void stringTwiceFixedNoJitter() { 38 | Retry test = Retry.any() 39 | .retryMax(2) 40 | .backoff(Backoff.fixed(Duration.ofHours(2))); 41 | 42 | assertThat(test.toString()) 43 | .isEqualTo("Retry{max=2,backoff=Backoff{fixed=7200000ms},jitter=Jitter{NONE}}"); 44 | } 45 | 46 | @Test 47 | public void stringThreeTimesExponentialRandomJitter() { 48 | Backoff backoff = Backoff.exponential( 49 | Duration.ofMillis(12), 50 | Duration.ofMinutes(2), 51 | 3, 52 | true); 53 | Retry test = Retry.any() 54 | .retryMax(3) 55 | .backoff(backoff) 56 | .jitter(Jitter.random()); 57 | 58 | assertThat(test.toString()) 59 | .isEqualTo("Retry{max=3,backoff=" + backoff + ",jitter=Jitter{RANDOM-0.5}}"); 60 | } 61 | 62 | @Test 63 | public void timeoutDoesntChangeMaxIterations() { 64 | final DefaultRetry retry1 = (DefaultRetry) Retry.any() 65 | .retryMax(3); 66 | 67 | assertThat(retry1.maxIterations).isEqualTo(3); 68 | 69 | final DefaultRetry retry2 = 70 | (DefaultRetry) retry1.timeout(Duration.ofMillis(200)); 71 | 72 | assertThat(retry2.maxIterations).as("not changed by timeout") 73 | .isEqualTo(3); 74 | 75 | final DefaultRetry retry3 = 76 | (DefaultRetry) retry2.retryMax(4); 77 | 78 | assertThat(retry3.maxIterations).as("changed by retryMax") 79 | .isEqualTo(4); 80 | } 81 | 82 | } -------------------------------------------------------------------------------- /reactor-extra/src/test/java/reactor/retry/RandomJitterTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.retry; 18 | 19 | import java.time.Duration; 20 | 21 | import org.junit.Test; 22 | import org.quicktheories.QuickTheory; 23 | import org.quicktheories.core.Gen; 24 | import org.quicktheories.generators.SourceDSL; 25 | 26 | import static org.assertj.core.api.Assertions.assertThat; 27 | import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; 28 | 29 | public class RandomJitterTest { 30 | 31 | @Test 32 | public void negativeFactorRejected() { 33 | assertThatIllegalArgumentException().isThrownBy(() -> new RandomJitter(-0.1)) 34 | .withMessage("random factor must be between 0 and 1 (default 0.5)"); 35 | } 36 | 37 | @Test 38 | public void overOneFactorRejected() { 39 | assertThatIllegalArgumentException().isThrownBy(() -> new RandomJitter(1.1)) 40 | .withMessage("random factor must be between 0 and 1 (default 0.5)"); 41 | } 42 | 43 | @Test 44 | public void applyToDelayUnderMinRejected() { 45 | //the BackoffDelay must comply to the invariant of min <= delay <= max 46 | Duration min = Duration.ofMillis(100); 47 | Duration max = Duration.ofMillis(100); 48 | Duration delay = Duration.ofMillis(99); 49 | BackoffDelay bd = new BackoffDelay(min, max, delay); 50 | RandomJitter jitter = new RandomJitter(0.5); 51 | 52 | assertThatIllegalArgumentException().isThrownBy(() -> jitter.apply(bd)) 53 | .withMessage("jitter can only be applied on a delay that is >= to min backoff"); 54 | } 55 | 56 | @Test 57 | public void applyToDelayOverMaxRejected() { 58 | //the BackoffDelay must comply to the invariant of min <= delay <= max 59 | Duration min = Duration.ofMillis(100); 60 | Duration max = Duration.ofMillis(200); 61 | Duration delay = Duration.ofMillis(201); 62 | BackoffDelay bd = new BackoffDelay(min, max, delay); 63 | RandomJitter jitter = new RandomJitter(0.5); 64 | 65 | assertThatIllegalArgumentException().isThrownBy(() -> jitter.apply(bd)) 66 | .withMessage("jitter can only be applied on a delay that is <= to max backoff"); 67 | } 68 | 69 | @Test 70 | public void jitterOffsetCappedForSuperLargeMaxDuration() { 71 | double factor = 0.5; 72 | Duration min = Duration.ofMillis(100); 73 | Duration max = Duration.ofMillis(100); 74 | Duration delay = Duration.ofSeconds(Long.MAX_VALUE); 75 | BackoffDelay bd = new BackoffDelay(min, max, delay); 76 | 77 | RandomJitter jitter = new RandomJitter(factor); 78 | 79 | assertThat(jitter.jitterOffsetCapped(bd)) 80 | .isEqualTo(Math.round(Long.MAX_VALUE * factor)); 81 | } 82 | 83 | @Test 84 | public void jitterOffsetForMaximumMillisDuration() { 85 | double factor = 0.5; 86 | Duration min = Duration.ofMillis(100); 87 | Duration max = Duration.ofMillis(100); 88 | Duration delay = Duration.ofMillis(Long.MAX_VALUE); 89 | BackoffDelay bd = new BackoffDelay(min, max, delay); 90 | 91 | RandomJitter jitter = new RandomJitter(factor); 92 | 93 | long expected = delay.multipliedBy(50) 94 | .dividedBy(100) 95 | .toMillis(); 96 | 97 | assertThat(jitter.jitterOffsetCapped(bd)) 98 | .isEqualTo(expected); 99 | } 100 | 101 | @Test 102 | public void jitterOffsetForReasonableDuration() { 103 | double factor = 0.5; 104 | Duration min = Duration.ofMillis(100); 105 | Duration max = Duration.ofMillis(100); 106 | Duration delay = Duration.ofMillis(500); 107 | BackoffDelay bd = new BackoffDelay(min, max, delay); 108 | 109 | RandomJitter jitter = new RandomJitter(factor); 110 | 111 | assertThat(jitter.jitterOffsetCapped(bd)) 112 | .isEqualTo(250); 113 | } 114 | 115 | @Test 116 | public void lowBoundNormal() { 117 | double factor = 0.5; 118 | Duration min = Duration.ofMillis(100); //not over half the duration 119 | Duration max = Duration.ofMillis(-123); //should be ignored 120 | Duration delay = Duration.ofMillis(500); 121 | BackoffDelay bd = new BackoffDelay(min, max, delay); 122 | 123 | RandomJitter jitter = new RandomJitter(factor); 124 | 125 | assertThat(jitter.lowJitterBound(bd, 250)) 126 | .isEqualTo(-250); 127 | } 128 | 129 | @Test 130 | public void lowBoundFlooredAtMin() { 131 | double factor = 0.5; 132 | Duration min = Duration.ofMillis(300); //over half the duration 133 | Duration max = Duration.ofMillis(-123); //should be ignored 134 | Duration delay = Duration.ofMillis(500); 135 | BackoffDelay bd = new BackoffDelay(min, max, delay); 136 | 137 | RandomJitter jitter = new RandomJitter(factor); 138 | 139 | assertThat(jitter.lowJitterBound(bd, 250)) 140 | .as("offset over min") 141 | .isEqualTo(-200); 142 | 143 | assertThat(jitter.lowJitterBound(bd, Long.MAX_VALUE / 2)) 144 | .as("offset half long max") 145 | .isEqualTo(-200); 146 | 147 | assertThat(jitter.lowJitterBound(bd, Long.MAX_VALUE)) 148 | .as("offset long max") 149 | .isEqualTo(-200); 150 | } 151 | 152 | @Test 153 | public void highBoundNormal() { 154 | double factor = 0.5; 155 | Duration min = Duration.ofMillis(-123); //should be ignored 156 | Duration max = Duration.ofMillis(1000); //not under half the duration 157 | Duration delay = Duration.ofMillis(500); 158 | BackoffDelay bd = new BackoffDelay(min, max, delay); 159 | 160 | RandomJitter jitter = new RandomJitter(factor); 161 | 162 | assertThat(jitter.highJitterBound(bd, 250)) 163 | .isEqualTo(250); 164 | } 165 | 166 | @Test 167 | public void highBoundCappedAtMax() { 168 | double factor = 0.5; 169 | Duration min = Duration.ofMillis(-123); //should be ignored 170 | Duration max = Duration.ofMillis(550); //under half the duration 171 | Duration delay = Duration.ofMillis(500); 172 | BackoffDelay bd = new BackoffDelay(min, max, delay); 173 | 174 | RandomJitter jitter = new RandomJitter(factor); 175 | 176 | assertThat(jitter.highJitterBound(bd, 250)) 177 | .as("offset over min") 178 | .isEqualTo(50); 179 | 180 | assertThat(jitter.highJitterBound(bd, Long.MAX_VALUE / 2)) 181 | .as("offset half long max") 182 | .isEqualTo(50); 183 | 184 | assertThat(jitter.highJitterBound(bd, Long.MAX_VALUE)) 185 | .as("offset long max") 186 | .isEqualTo(50); 187 | } 188 | 189 | @Test 190 | public void propertyJitterDoesntCrossMinMax() { 191 | Gen minGen = SourceDSL.longs() 192 | .between(0, Long.MAX_VALUE - 2); 193 | 194 | Gen delayGen = minGen.flatMap(d -> SourceDSL.longs().between(d, Long.MAX_VALUE - 1)); 195 | Gen maxGen = delayGen.flatMap(d -> SourceDSL.longs().between(d, Long.MAX_VALUE)); 196 | 197 | QuickTheory.qt() 198 | .withGenerateAttempts(1000) 199 | .forAll(minGen, delayGen, maxGen) 200 | .assuming((min, d, max) -> min <= d && d <= max) 201 | .as((min, d, max) -> new BackoffDelay(Duration.ofMillis(min), Duration.ofMillis(max), Duration.ofMillis(d))) 202 | .checkAssert(bd -> assertThat(Jitter.random().apply(bd)) 203 | .isGreaterThanOrEqualTo(bd.min) 204 | .isLessThanOrEqualTo(bd.max)); 205 | } 206 | 207 | } -------------------------------------------------------------------------------- /reactor-extra/src/test/java/reactor/retry/RetryTestUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.retry; 18 | 19 | import java.time.Duration; 20 | import java.util.ArrayList; 21 | import java.util.Iterator; 22 | import java.util.List; 23 | import java.util.Queue; 24 | import java.util.concurrent.ConcurrentHashMap; 25 | import java.util.concurrent.CountDownLatch; 26 | import java.util.concurrent.ExecutorService; 27 | import java.util.concurrent.Executors; 28 | import java.util.concurrent.Future; 29 | import java.util.concurrent.TimeUnit; 30 | import java.util.concurrent.atomic.AtomicInteger; 31 | import java.util.function.Consumer; 32 | import java.util.function.Function; 33 | 34 | import org.reactivestreams.Publisher; 35 | 36 | import reactor.core.publisher.Flux; 37 | 38 | import static org.junit.Assert.assertEquals; 39 | import static org.junit.Assert.assertNotNull; 40 | import static org.junit.Assert.assertNull; 41 | import static org.junit.Assert.assertTrue; 42 | 43 | public class RetryTestUtils { 44 | 45 | static void assertDelays(Queue> retries, Long... delayMs) { 46 | assertEquals(delayMs.length, retries.size()); 47 | int index = 0; 48 | for (Iterator> it = retries.iterator(); it.hasNext(); ) { 49 | IterationContext repeatContext = it.next(); 50 | assertEquals(delayMs[index].longValue(), repeatContext.backoff().toMillis()); 51 | index++; 52 | } 53 | } 54 | 55 | static void assertRandomDelays(Queue> retries, int firstMs, int maxMs) { 56 | long prevMs = 0; 57 | int randomValues = 0; 58 | for (IterationContext context : retries) { 59 | long backoffMs = context.backoff().toMillis(); 60 | assertTrue("Unexpected delay " + backoffMs, backoffMs >= firstMs && backoffMs <= maxMs); 61 | if (backoffMs != firstMs && backoffMs != prevMs) 62 | randomValues++; 63 | prevMs = backoffMs; 64 | } 65 | assertTrue("Delays not random", randomValues >= 2); // Allow for at most one edge case. 66 | } 67 | 68 | static void testReuseInParallel(int threads, int iterations, 69 | Function, Publisher>> retryOrRepeat, 70 | Consumer, Publisher>> testTask) throws Exception { 71 | int repeatCount = iterations - 1; 72 | AtomicInteger nextBackoff = new AtomicInteger(); 73 | // Keep track of the number of backoff invocations per instance 74 | ConcurrentHashMap backoffCounts = new ConcurrentHashMap<>(); 75 | // Use a countdown latch to get all instances to stop in the first backoff callback 76 | CountDownLatch latch = new CountDownLatch(threads); 77 | Backoff customBackoff = new Backoff() { 78 | @Override 79 | public BackoffDelay apply(IterationContext context) { 80 | Duration backoff = context.backoff(); 81 | if (latch.getCount() > 0) { 82 | assertNull("Wrong context, backoff must be null", backoff); 83 | backoff = Duration.ofMillis(nextBackoff.incrementAndGet()); 84 | backoffCounts.put(backoff.toMillis(), 1); 85 | latch.countDown(); 86 | try { 87 | latch.await(10, TimeUnit.SECONDS); 88 | } 89 | catch (Exception e) { 90 | // ignore, errors are handled later 91 | } 92 | } else { 93 | assertNotNull("Wrong context, backoff must not be null", backoff); 94 | long index = backoff.toMillis(); 95 | backoffCounts.put(index, backoffCounts.get(index) + 1); 96 | } 97 | return new BackoffDelay(backoff); 98 | } 99 | }; 100 | Function, Publisher> retryFunc = retryOrRepeat.apply(customBackoff); 101 | ExecutorService executor = Executors.newFixedThreadPool(threads); 102 | List> futures = new ArrayList<>(); 103 | try { 104 | for (int i = 0; i < threads; i++) { 105 | Runnable runnable = () -> testTask.accept(retryFunc); 106 | futures.add(executor.submit(runnable)); 107 | } 108 | for (Future future : futures) 109 | future.get(5, TimeUnit.SECONDS); 110 | } 111 | finally { 112 | executor.shutdownNow(); 113 | } 114 | 115 | assertEquals(0, latch.getCount()); 116 | assertEquals(threads, backoffCounts.size()); 117 | for (Integer count : backoffCounts.values()) { 118 | //backoff not invoked anymore when maxIteration reached 119 | assertEquals(repeatCount, count.intValue()); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /reactor-extra/src/test/java/reactor/scheduler/clock/SchedulerClockTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.scheduler.clock; 18 | 19 | import java.time.Duration; 20 | import java.time.ZonedDateTime; 21 | 22 | import org.junit.Assert; 23 | import org.junit.Test; 24 | import reactor.test.scheduler.VirtualTimeScheduler; 25 | 26 | public class SchedulerClockTest { 27 | 28 | @Test 29 | public void shouldReturnExpectedTime() { 30 | VirtualTimeScheduler scheduler = VirtualTimeScheduler.create(); 31 | SchedulerClock clock = SchedulerClock.of(scheduler); 32 | ZonedDateTime beforeAdvance = ZonedDateTime.now(clock); 33 | Assert.assertEquals(0, clock.millis()); 34 | Assert.assertEquals(0, 35 | clock.instant() 36 | .toEpochMilli()); 37 | 38 | scheduler.advanceTimeBy(Duration.ofSeconds(1)); 39 | ZonedDateTime afterAdvance = ZonedDateTime.now(clock); 40 | 41 | Assert.assertTrue(beforeAdvance.isBefore(afterAdvance)); 42 | Assert.assertEquals(1000, clock.millis()); 43 | Assert.assertEquals(1000, 44 | clock.instant() 45 | .toEpochMilli()); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /reactor-extra/src/test/java/reactor/scheduler/forkjoin/ForkJoinPoolSchedulerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.scheduler.forkjoin; 18 | 19 | import java.time.Duration; 20 | import java.util.concurrent.CountDownLatch; 21 | import java.util.concurrent.TimeUnit; 22 | import java.util.concurrent.atomic.AtomicLong; 23 | 24 | import org.junit.Test; 25 | import reactor.core.publisher.Flux; 26 | import reactor.core.publisher.Mono; 27 | import reactor.core.scheduler.Scheduler; 28 | import reactor.test.StepVerifier; 29 | 30 | import static org.assertj.core.api.Assertions.assertThat; 31 | 32 | public class ForkJoinPoolSchedulerTest extends AbstractSchedulerTest { 33 | 34 | @Test(timeout = 500) 35 | public void scheduleThenDisposeOfScheduler() throws Exception { 36 | Scheduler s = ForkJoinPoolScheduler.create("test", 1); 37 | 38 | CountDownLatch latch = new CountDownLatch(1); 39 | 40 | s.schedule(latch::countDown, 50, TimeUnit.MILLISECONDS); 41 | 42 | assertThat(s.isDisposed()).isFalse(); 43 | s.dispose(); 44 | assertThat(s.isDisposed()).isTrue(); 45 | assertThat(latch.await(100, TimeUnit.MILLISECONDS)).isFalse(); 46 | } 47 | 48 | @Test(timeout = 500) 49 | public void scheduleThenDisposeOfWorker() throws Exception { 50 | Scheduler s = scheduler(); 51 | 52 | CountDownLatch latch = new CountDownLatch(1); 53 | 54 | Scheduler.Worker worker = s.createWorker(); 55 | 56 | worker.schedule(latch::countDown, 50, TimeUnit.MILLISECONDS); 57 | 58 | assertThat(worker.isDisposed()).isFalse(); 59 | worker.dispose(); 60 | assertThat(worker.isDisposed()).isTrue(); 61 | assertThat(latch.await(100, TimeUnit.MILLISECONDS)).isFalse(); 62 | } 63 | 64 | @Test 65 | public void scheduledDoesntReject() { 66 | Scheduler s = scheduler(); 67 | 68 | assertThat(s.schedule(() -> {}, 100, TimeUnit.MILLISECONDS)) 69 | .describedAs("direct delayed scheduling") 70 | .isNotNull(); 71 | assertThat(s.schedulePeriodically(() -> {}, 100, 100, TimeUnit.MILLISECONDS)) 72 | .describedAs("direct periodic scheduling") 73 | .isNotNull(); 74 | 75 | Scheduler.Worker w = s.createWorker(); 76 | assertThat(w.schedule(() -> {}, 100, TimeUnit.MILLISECONDS)) 77 | .describedAs("worker delayed scheduling") 78 | .isNotNull(); 79 | assertThat(w.schedulePeriodically(() -> {}, 100, 100, TimeUnit.MILLISECONDS)) 80 | .describedAs("worker periodic scheduling") 81 | .isNotNull(); 82 | } 83 | 84 | @Test 85 | public void smokeTestDelay() { 86 | for (int i = 0; i < 20; i++) { 87 | Scheduler s = ForkJoinPoolScheduler.create("test"); 88 | AtomicLong start = new AtomicLong(); 89 | AtomicLong end = new AtomicLong(); 90 | 91 | try { 92 | StepVerifier.create(Mono.delay(Duration.ofMillis(100), s) 93 | .log() 94 | .doOnSubscribe(sub -> start.set(System.nanoTime())) 95 | .doOnSuccess(v -> end.set(System.nanoTime())) 96 | .doOnError(e -> end.set(System.nanoTime()))) 97 | .expectSubscription() 98 | .expectNext(0L) 99 | .verifyComplete(); 100 | 101 | long endValue = end.longValue(); 102 | long startValue = start.longValue(); 103 | long measuredDelay = endValue - startValue; 104 | long measuredDelayMs = TimeUnit.NANOSECONDS.toMillis(measuredDelay); 105 | assertThat(measuredDelayMs).as( 106 | "iteration %s, measured delay %s nanos, start at %s nanos, end at %s nanos", 107 | i, 108 | measuredDelay, 109 | startValue, 110 | endValue) 111 | .isGreaterThanOrEqualTo(100L) 112 | .isLessThan(200L); 113 | } 114 | finally { 115 | s.dispose(); 116 | } 117 | } 118 | } 119 | 120 | @Test 121 | public void smokeTestInterval() { 122 | Scheduler s = scheduler(); 123 | 124 | try { 125 | StepVerifier.create(Flux.interval(Duration.ofMillis(100), 126 | Duration.ofMillis(200), 127 | s)) 128 | .expectSubscription() 129 | .expectNoEvent(Duration.ofMillis(100)) 130 | .expectNext(0L) 131 | .expectNoEvent(Duration.ofMillis(200)) 132 | .expectNext(1L) 133 | .expectNoEvent(Duration.ofMillis(200)) 134 | .expectNext(2L) 135 | .thenCancel(); 136 | } 137 | finally { 138 | s.dispose(); 139 | } 140 | } 141 | 142 | @Override 143 | protected Scheduler scheduler() { 144 | return ForkJoinPoolScheduler.create("test"); 145 | } 146 | 147 | @Override 148 | protected boolean shouldCheckInterrupted() { 149 | return true; 150 | } 151 | 152 | } 153 | -------------------------------------------------------------------------------- /reactor-extra/src/test/java/reactor/swing/SwingAdapterTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.swing; 18 | 19 | import java.time.Duration; 20 | 21 | import org.junit.Test; 22 | import reactor.core.publisher.Flux; 23 | import reactor.core.scheduler.Scheduler; 24 | import reactor.test.StepVerifier; 25 | 26 | /** 27 | * @author Stephane Maldini 28 | * @deprecated To be removed in 3.6.0. See https://github.com/reactor/reactor-addons/issues/273 29 | */ 30 | @Deprecated 31 | public class SwingAdapterTest { 32 | 33 | @Test 34 | public void normal() { 35 | Scheduler swingScheduler = SwingScheduler.create(); 36 | 37 | Flux swingFlux = Flux.range(0, 1_000_000) 38 | .publishOn(swingScheduler); 39 | 40 | StepVerifier.create(swingFlux) 41 | .expectNextCount(1_000_000) 42 | .expectComplete() 43 | .verify(); 44 | } 45 | 46 | @Test 47 | public void timed() { 48 | Scheduler swingScheduler = SwingScheduler.create(); 49 | 50 | Flux swingFlux = Flux.interval(Duration.ofMillis(100), swingScheduler) 51 | .take(3); 52 | 53 | StepVerifier.create(swingFlux) 54 | .expectNext(0L, 1L, 2L) 55 | .expectComplete() 56 | .verify(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /reactor-extra/src/test/java/reactor/swing/SwtAdapterTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 reactor.swing; 18 | 19 | import org.eclipse.swt.widgets.Display; 20 | import org.junit.Ignore; 21 | import org.junit.Test; 22 | import reactor.core.publisher.Flux; 23 | import reactor.core.scheduler.Scheduler; 24 | import reactor.test.StepVerifier; 25 | 26 | /** 27 | * @author Stephane Maldini 28 | * @deprecated To be removed in 3.6.0. See https://github.com/reactor/reactor-addons/issues/273 29 | */ 30 | @Deprecated 31 | public class SwtAdapterTest { 32 | 33 | @Test 34 | @Ignore("cannot test without display") 35 | public void normal() { 36 | Scheduler swtScheduler = SwtScheduler.from(new Display()); 37 | 38 | Flux swtFlux = Flux.range(0, 1_000_000) 39 | .publishOn(swtScheduler); 40 | 41 | StepVerifier.create(swtFlux) 42 | .expectNextCount(1_000_000) 43 | .expectComplete() 44 | .verify(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2021 VMware Inc. or its affiliates, All Rights Reserved. 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 | rootProject.name = 'reactor-addons' 18 | 19 | include 'reactor-adapter', 20 | 'reactor-extra' 21 | --------------------------------------------------------------------------------