├── .editorconfig
├── .github
├── maven-cd-settings.xml
├── maven-ci-settings.xml
└── workflows
│ ├── ci-4.x.yml
│ ├── ci-5.x-stable.yml
│ ├── ci-5.x.yml
│ ├── ci-matrix-5.x.yml
│ ├── ci.yml
│ └── deploy.yml
├── .gitignore
├── LICENSE.txt
├── README.adoc
├── pom.xml
└── src
├── main
├── asciidoc
│ └── index.adoc
└── java
│ ├── examples
│ └── ReactiveStreamsExamples.java
│ ├── io
│ └── vertx
│ │ └── ext
│ │ └── reactivestreams
│ │ ├── ReactiveReadStream.java
│ │ ├── ReactiveWriteStream.java
│ │ ├── impl
│ │ ├── ReactiveReadStreamImpl.java
│ │ └── ReactiveWriteStreamImpl.java
│ │ └── package-info.java
│ └── module-info.java
└── test
└── java
├── io
└── vertx
│ └── ext
│ └── reactivestreams
│ ├── tck
│ ├── FiniteReactiveWriteStream.java
│ ├── PublisherVerificationTest.java
│ ├── SubscriberBlackboxVerificationTest.java
│ └── SubscriberWhiteboxVerificationTest.java
│ └── test
│ ├── ReactiveReadStreamTest.java
│ ├── ReactiveStreamTestBase.java
│ └── ReactiveWriteStreamTest.java
└── module-info.java
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | trim_trailing_whitespace = true
8 | end_of_line = lf
9 | insert_final_newline = true
10 |
--------------------------------------------------------------------------------
/.github/maven-cd-settings.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 | false
20 |
21 |
22 |
23 | vertx-snapshots-repository
24 | ${env.VERTX_NEXUS_USERNAME}
25 | ${env.VERTX_NEXUS_PASSWORD}
26 |
27 |
28 |
29 |
30 |
31 | google-mirror
32 |
33 | true
34 |
35 |
36 |
37 | google-maven-central
38 | GCS Maven Central mirror EU
39 | https://maven-central.storage-download.googleapis.com/maven2/
40 |
41 | true
42 |
43 |
44 | false
45 |
46 |
47 |
48 |
49 |
50 | google-maven-central
51 | GCS Maven Central mirror
52 | https://maven-central.storage-download.googleapis.com/maven2/
53 |
54 | true
55 |
56 |
57 | false
58 |
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/.github/maven-ci-settings.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 | false
20 |
21 |
22 |
23 | google-mirror
24 |
25 | true
26 |
27 |
28 |
29 | google-maven-central
30 | GCS Maven Central mirror EU
31 | https://maven-central.storage-download.googleapis.com/maven2/
32 |
33 | true
34 |
35 |
36 | false
37 |
38 |
39 |
40 |
41 |
42 | google-maven-central
43 | GCS Maven Central mirror
44 | https://maven-central.storage-download.googleapis.com/maven2/
45 |
46 | true
47 |
48 |
49 | false
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/.github/workflows/ci-4.x.yml:
--------------------------------------------------------------------------------
1 | name: vertx-reactive-streams (4.x)
2 | on:
3 | schedule:
4 | - cron: '0 4 * * *'
5 | jobs:
6 | CI:
7 | strategy:
8 | matrix:
9 | include:
10 | - os: ubuntu-latest
11 | jdk: 8
12 | - os: ubuntu-latest
13 | jdk: 17
14 | uses: ./.github/workflows/ci.yml
15 | with:
16 | branch: 4.x
17 | jdk: ${{ matrix.jdk }}
18 | os: ${{ matrix.os }}
19 | secrets: inherit
20 | Deploy:
21 | if: ${{ github.repository_owner == 'vert-x3' && (github.event_name == 'push' || github.event_name == 'schedule') }}
22 | needs: CI
23 | uses: ./.github/workflows/deploy.yml
24 | with:
25 | branch: 4.x
26 | jdk: 8
27 | secrets: inherit
28 |
--------------------------------------------------------------------------------
/.github/workflows/ci-5.x-stable.yml:
--------------------------------------------------------------------------------
1 | name: vertx-reactive-streams (5.x-stable)
2 | on:
3 | push:
4 | branches:
5 | - '5.[0-9]+'
6 | pull_request:
7 | branches:
8 | - '5.[0-9]+'
9 | schedule:
10 | - cron: '0 6 * * *'
11 | jobs:
12 | CI-CD:
13 | uses: ./.github/workflows/ci-matrix-5.x.yml
14 | secrets: inherit
15 | with:
16 | branch: ${{ github.event_name == 'schedule' && vars.VERTX_5_STABLE_BRANCH || github.event.pull_request.head.sha || github.ref_name }}
17 |
--------------------------------------------------------------------------------
/.github/workflows/ci-5.x.yml:
--------------------------------------------------------------------------------
1 | name: vertx-reactive-streams (5.x)
2 | on:
3 | push:
4 | branches:
5 | - master
6 | pull_request:
7 | branches:
8 | - master
9 | schedule:
10 | - cron: '0 5 * * *'
11 | jobs:
12 | CI-CD:
13 | uses: ./.github/workflows/ci-matrix-5.x.yml
14 | secrets: inherit
15 | with:
16 | branch: ${{ github.event.pull_request.head.sha || github.ref_name }}
17 |
--------------------------------------------------------------------------------
/.github/workflows/ci-matrix-5.x.yml:
--------------------------------------------------------------------------------
1 | name: CI matrix (5.x)
2 | on:
3 | workflow_call:
4 | inputs:
5 | branch:
6 | required: true
7 | type: string
8 | jobs:
9 | CI:
10 | strategy:
11 | matrix:
12 | include:
13 | - os: ubuntu-latest
14 | jdk: 11
15 | - os: ubuntu-latest
16 | jdk: 21
17 | uses: ./.github/workflows/ci.yml
18 | with:
19 | branch: ${{ inputs.branch }}
20 | jdk: ${{ matrix.jdk }}
21 | os: ${{ matrix.os }}
22 | secrets: inherit
23 | Deploy:
24 | if: ${{ github.repository_owner == 'vert-x3' && (github.event_name == 'push' || github.event_name == 'schedule') }}
25 | needs: CI
26 | uses: ./.github/workflows/deploy.yml
27 | with:
28 | branch: ${{ inputs.branch }}
29 | jdk: 11
30 | secrets: inherit
31 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 | on:
3 | workflow_call:
4 | inputs:
5 | branch:
6 | required: true
7 | type: string
8 | jdk:
9 | default: 8
10 | type: string
11 | os:
12 | default: ubuntu-latest
13 | type: string
14 | jobs:
15 | Test:
16 | name: Run tests
17 | runs-on: ${{ inputs.os }}
18 | steps:
19 | - name: Checkout
20 | uses: actions/checkout@v2
21 | with:
22 | ref: ${{ inputs.branch }}
23 | - name: Install JDK
24 | uses: actions/setup-java@v2
25 | with:
26 | java-version: ${{ inputs.jdk }}
27 | distribution: temurin
28 | - name: Run tests
29 | run: mvn -s .github/maven-ci-settings.xml -q clean verify -B
30 |
--------------------------------------------------------------------------------
/.github/workflows/deploy.yml:
--------------------------------------------------------------------------------
1 | name: Deploy
2 | on:
3 | workflow_call:
4 | inputs:
5 | branch:
6 | required: true
7 | type: string
8 | jdk:
9 | default: 8
10 | type: string
11 | jobs:
12 | Deploy:
13 | name: Deploy to OSSRH
14 | runs-on: ubuntu-latest
15 | env:
16 | VERTX_NEXUS_USERNAME: ${{ secrets.VERTX_NEXUS_USERNAME }}
17 | VERTX_NEXUS_PASSWORD: ${{ secrets.VERTX_NEXUS_PASSWORD }}
18 | steps:
19 | - name: Checkout
20 | uses: actions/checkout@v2
21 | with:
22 | ref: ${{ inputs.branch }}
23 | - name: Install JDK
24 | uses: actions/setup-java@v2
25 | with:
26 | java-version: ${{ inputs.jdk }}
27 | distribution: temurin
28 | - name: Get project version
29 | run: echo "PROJECT_VERSION=$(mvn org.apache.maven.plugins:maven-help-plugin:evaluate -Dexpression=project.version -q -DforceStdout | grep -v '\[')" >> $GITHUB_ENV
30 | - name: Maven deploy
31 | if: ${{ endsWith(env.PROJECT_VERSION, '-SNAPSHOT') }}
32 | run: mvn deploy -s .github/maven-cd-settings.xml -DskipTests -B
33 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .gradle
3 | .idea
4 | .classpath
5 | .project
6 | .settings
7 | .yardoc
8 | .yardopts
9 | build
10 | target
11 | out
12 | *.iml
13 | *.ipr
14 | *.iws
15 | test-output
16 | Scratch.java
17 | ScratchTest.java
18 | test-results
19 | test-tmp
20 | *.class
21 | ScratchPad.java
22 | src/main/resources/ext-js/*.js
23 | src/main/java/io/vertx/java/**/*.java
24 | src/main/groovy/io/vertx/groovy/**/*.groovy
25 | *.swp
26 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.adoc:
--------------------------------------------------------------------------------
1 | = Reactive Streams for Vert.x
2 |
3 | image:https://github.com/vert-x3/vertx-reactive-streams/actions/workflows/ci-5.x.yml/badge.svg["Build Status (5.x)",link="https://github.com/vert-x3/vertx-reactive-streams/actions/workflows/ci-5.x.yml"]
4 | image:https://github.com/vert-x3/vertx-reactive-streams/actions/workflows/ci-4.x.yml/badge.svg["Build Status (4.x)",link="https://github.com/vert-x3/vertx-reactive-streams/actions/workflows/ci-4.x.yml"]
5 |
6 | "link:http://www.reactive-streams.org/[Reactive Streams] is an initiative to provide a standard for asynchronous stream
7 | processing with non-blocking back pressure on the JVM."
8 |
9 | Please see the main documentation on the web-site for a full description:
10 |
11 | * https://vertx.io/docs/vertx-reactive-streams/java/[Web-site documentation]
12 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 | 4.0.0
19 |
20 |
21 | io.vertx
22 | vertx5-parent
23 | 12
24 |
25 |
26 | vertx-reactive-streams
27 | 5.1.0-SNAPSHOT
28 |
29 | Vert.x - Reactive Streams
30 |
31 |
32 | scm:git:git@github.com:vert-x3/vertx-reactive-streams.git
33 | scm:git:git@github.com:vert-x3/vertx-reactive-streams.git
34 | git@github.com:vert-x3/vertx-reactive-streams.git
35 |
36 |
37 |
38 | 1.0.3
39 |
40 |
41 |
42 |
43 |
44 | io.vertx
45 | vertx-dependencies
46 | ${project.version}
47 | pom
48 | import
49 |
50 |
51 |
52 |
53 |
54 |
55 | io.vertx
56 | vertx-core
57 |
58 |
59 | io.vertx
60 | vertx-codegen-api
61 | true
62 |
63 |
64 | io.vertx
65 | vertx-docgen-api
66 | true
67 |
68 |
69 | org.reactivestreams
70 | reactive-streams
71 | ${reactive.streams.version}
72 |
73 |
74 | org.reactivestreams
75 | reactive-streams-tck
76 | ${reactive.streams.version}
77 | test
78 |
79 |
80 | io.vertx
81 | vertx-core
82 | test-jar
83 | test
84 |
85 |
86 | junit
87 | junit
88 | test
89 | 4.13.1
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 | maven-compiler-plugin
98 |
99 |
100 | default-compile
101 |
102 |
103 |
104 | io.vertx
105 | vertx-codegen
106 | processor
107 |
108 |
109 | io.vertx
110 | vertx-docgen-processor
111 | processor
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 | org.apache.maven.plugins
123 | maven-surefire-plugin
124 |
125 |
126 | default-test
127 |
128 | test
129 |
130 |
131 |
132 | **/tck/*
133 |
134 | none:none
135 |
136 |
137 |
138 | tck
139 |
140 | test
141 |
142 |
143 |
144 | **/test/*
145 | **/tck/SubscriberWhitebox*
146 |
147 | none:none
148 |
149 |
150 |
151 |
152 |
153 | maven-assembly-plugin
154 |
155 |
156 | package-docs
157 |
158 | single
159 |
160 |
161 |
162 |
163 |
164 |
165 |
--------------------------------------------------------------------------------
/src/main/asciidoc/index.adoc:
--------------------------------------------------------------------------------
1 | = Vert.x Reactive Streams Integration
2 |
3 | link:http://www.reactive-streams.org/[Reactive Streams] is an initiative to provide a standard for asynchronous stream
4 | processing with non-blocking back pressure on the JVM.
5 |
6 | This library provides an implementation of reactive streams for Vert.x.
7 |
8 | Vert.x provides its own mechanisms for handling streams of data and pumping them with back pressure from one to another
9 | using the `io.vertx.core.streams.ReadStream`, `io.vertx.core.streams.WriteStream` and `io.vertx.core.streams.Pump`.
10 | Please see the Vert.x core manual for more information on Vert.x streams.
11 |
12 | This library provides implementations of read stream and write stream which also act as reactive streams publishers
13 | and subscribers. This allows us to treat any reactive streams publisher or subscriber and deal with it like any other
14 | Vert.x read or write stream.
15 |
16 | == Using Vert.x Reactive Streams
17 |
18 | To use Vert.x Reactive Streams, add the following dependency to the _dependencies_ section of your build descriptor:
19 |
20 | * Maven (in your `pom.xml`):
21 |
22 | [source,xml,subs="+attributes"]
23 | ----
24 |
25 | io.vertx
26 | vertx-reactive-streams
27 | ${maven.version}
28 |
29 | ----
30 |
31 | * Gradle (in your `build.gradle` file):
32 |
33 | [source,groovy,subs="+attributes"]
34 | ----
35 | compile io.vertx:vertx-reactive-streams:${maven.version}
36 | ----
37 |
38 | == Reactive Read Stream
39 |
40 | We provide an implementation of the Vert.x `ReadStream` interface with {@link io.vertx.ext.reactivestreams.ReactiveReadStream}
41 | which also implements a reactive streams `Subscriber`.
42 |
43 | You can pass an instance of this to any reactive streams `Publisher` (e.g. a Publisher from Akka) and then you will be
44 | able to read from that just like any other Vert.x `ReadStream` (e.g. use a `Pump` to pump it to a `WriteStream`.
45 |
46 | Here's an example of taking a publisher from some other reactive streams implementation (e.g. Akka) and pumping that
47 | stream to the body of a server side HTTP response. This will handle back pressure automatically.
48 |
49 | [source,java]
50 | ----
51 | {@link examples.ReactiveStreamsExamples#example1}
52 | ----
53 |
54 | == Reactive Write Stream
55 |
56 | We also provide an implementation of the Vert.x `WriteStream` interface with {@link io.vertx.ext.reactivestreams.ReactiveWriteStream}
57 | which also implements a reactive streams `Publisher`. You can take any reactive streams `Subscriber`
58 | (e.g. a Subscriber from Akka) and then you will be able* to write to it like any other Vert.x `WriteStream`.
59 | (e.g. use a `Pump` to pump it from a `ReadStream`).
60 |
61 | You use `pause`, `resume`, and `writeQueueFull`, as you would with any Vert.x read stream to handle your back pressure.
62 | This is automatically translated internally into the reactive streams method of propagating back pressure
63 | (requesting more items).
64 |
65 | Here's an example of taking a subscriber from some other reactive streams implementation and pumping the body of
66 | a server side HTTP request to that subscriber. This will handle back pressure automatically.
67 |
68 | [source,java]
69 | ----
70 | {@link examples.ReactiveStreamsExamples#example2}
71 | ----
72 |
--------------------------------------------------------------------------------
/src/main/java/examples/ReactiveStreamsExamples.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Red Hat, Inc.
3 | *
4 | * All rights reserved. This program and the accompanying materials
5 | * are made available under the terms of the Eclipse Public License v1.0
6 | * and Apache License v2.0 which accompanies this distribution.
7 | *
8 | * The Eclipse Public License is available at
9 | * http://www.eclipse.org/legal/epl-v10.html
10 | *
11 | * The Apache License v2.0 is available at
12 | * http://www.opensource.org/licenses/apache2.0.php
13 | *
14 | * You may elect to redistribute this code under either of these licenses.
15 | */
16 |
17 | package examples;
18 |
19 | import io.vertx.core.Vertx;
20 | import io.vertx.core.buffer.Buffer;
21 | import io.vertx.core.http.HttpServerRequest;
22 | import io.vertx.core.http.HttpServerResponse;
23 | import io.vertx.docgen.Source;
24 | import io.vertx.ext.reactivestreams.ReactiveReadStream;
25 | import io.vertx.ext.reactivestreams.ReactiveWriteStream;
26 | import org.reactivestreams.Publisher;
27 | import org.reactivestreams.Subscriber;
28 |
29 | /**
30 | * @author Tim Fox
31 | */
32 | @Source(translate = false)
33 | public class ReactiveStreamsExamples {
34 |
35 | public void example1(HttpServerResponse response, Publisher otherPublisher) {
36 |
37 | ReactiveReadStream rrs = ReactiveReadStream.readStream();
38 |
39 | // Subscribe the read stream to the publisher
40 | otherPublisher.subscribe(rrs);
41 |
42 | // Pipe from the read stream to the http response
43 | rrs.pipeTo(response);
44 |
45 | }
46 |
47 | public void example2(Vertx vertx, HttpServerRequest request, Subscriber otherSubscriber) {
48 |
49 | ReactiveWriteStream rws = ReactiveWriteStream.writeStream(vertx);
50 |
51 | // Subscribe the other subscriber to the write stream
52 | rws.subscribe(otherSubscriber);
53 |
54 | // Pipe the http request to the write stream
55 | request.pipeTo(rws);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/main/java/io/vertx/ext/reactivestreams/ReactiveReadStream.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Red Hat, Inc.
3 | *
4 | * All rights reserved. This program and the accompanying materials
5 | * are made available under the terms of the Eclipse Public License v1.0
6 | * and Apache License v2.0 which accompanies this distribution.
7 | *
8 | * The Eclipse Public License is available at
9 | * http://www.eclipse.org/legal/epl-v10.html
10 | *
11 | * The Apache License v2.0 is available at
12 | * http://www.opensource.org/licenses/apache2.0.php
13 | *
14 | * You may elect to redistribute this code under either of these licenses.
15 | */
16 |
17 | package io.vertx.ext.reactivestreams;
18 |
19 | import io.vertx.core.Handler;
20 | import io.vertx.core.streams.ReadStream;
21 | import io.vertx.ext.reactivestreams.impl.ReactiveReadStreamImpl;
22 | import org.reactivestreams.Subscriber;
23 |
24 | /**
25 | * A Vert.x read stream that also implements reactive streams subscriber interface.
26 | *
27 | * @author Tim Fox
28 | */
29 | public interface ReactiveReadStream extends ReadStream, Subscriber {
30 |
31 | /**
32 | * Default batch size
33 | */
34 | static final long DEFAULT_BATCH_SIZE = 4L;
35 |
36 | /**
37 | * Create a reactive read stream
38 | *
39 | * @return the stream
40 | */
41 | static ReactiveReadStream readStream() {
42 | return readStream(DEFAULT_BATCH_SIZE);
43 | }
44 |
45 | /**
46 | * Create a reactive read stream specifying batch size
47 | *
48 | * @param batchSize the batch size
49 | * @return the stream
50 | */
51 | static ReactiveReadStream readStream(long batchSize) {
52 | return new ReactiveReadStreamImpl<>(batchSize);
53 | }
54 |
55 | @Override
56 | ReactiveReadStream exceptionHandler(Handler handler);
57 |
58 | @Override
59 | ReactiveReadStream handler(Handler handler);
60 |
61 | @Override
62 | ReactiveReadStream pause();
63 |
64 | @Override
65 | ReactiveReadStream resume();
66 |
67 | @Override
68 | ReactiveReadStream endHandler(Handler endHandler);
69 | }
70 |
--------------------------------------------------------------------------------
/src/main/java/io/vertx/ext/reactivestreams/ReactiveWriteStream.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Red Hat, Inc.
3 | *
4 | * All rights reserved. This program and the accompanying materials
5 | * are made available under the terms of the Eclipse Public License v1.0
6 | * and Apache License v2.0 which accompanies this distribution.
7 | *
8 | * The Eclipse Public License is available at
9 | * http://www.eclipse.org/legal/epl-v10.html
10 | *
11 | * The Apache License v2.0 is available at
12 | * http://www.opensource.org/licenses/apache2.0.php
13 | *
14 | * You may elect to redistribute this code under either of these licenses.
15 | */
16 |
17 | package io.vertx.ext.reactivestreams;
18 |
19 | import io.vertx.core.AsyncResult;
20 | import io.vertx.core.Future;
21 | import io.vertx.core.Handler;
22 | import io.vertx.core.Vertx;
23 | import io.vertx.core.streams.WriteStream;
24 | import io.vertx.ext.reactivestreams.impl.ReactiveWriteStreamImpl;
25 | import org.reactivestreams.Publisher;
26 |
27 | /**
28 | * A Vert.x write stream that also implements reactive streams publisher interface.
29 | *
30 | *
31 | * @author Tim Fox
32 | */
33 | public interface ReactiveWriteStream extends WriteStream, Publisher {
34 |
35 | /**
36 | * Default write queue max size
37 | */
38 | static final int DEFAULT_WRITE_QUEUE_MAX_SIZE = 32;
39 |
40 | /**
41 | * Create a reactive write stream
42 | *
43 | * @param vertx the Vert.x instance
44 | * @return the stream
45 | */
46 | static ReactiveWriteStream writeStream(Vertx vertx) {
47 | return new ReactiveWriteStreamImpl<>(vertx);
48 | }
49 |
50 | @Override
51 | ReactiveWriteStream exceptionHandler(Handler handler);
52 |
53 | @Override
54 | Future write(T data);
55 |
56 | @Override
57 | ReactiveWriteStream setWriteQueueMaxSize(int maxSize);
58 |
59 | @Override
60 | ReactiveWriteStream drainHandler(Handler handler);
61 |
62 | /**
63 | * Close the stream
64 | *
65 | * @return a reference to this for a fluent API
66 | */
67 | ReactiveWriteStream close();
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/src/main/java/io/vertx/ext/reactivestreams/impl/ReactiveReadStreamImpl.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Red Hat, Inc.
3 | *
4 | * All rights reserved. This program and the accompanying materials
5 | * are made available under the terms of the Eclipse Public License v1.0
6 | * and Apache License v2.0 which accompanies this distribution.
7 | *
8 | * The Eclipse Public License is available at
9 | * http://www.eclipse.org/legal/epl-v10.html
10 | *
11 | * The Apache License v2.0 is available at
12 | * http://www.opensource.org/licenses/apache2.0.php
13 | *
14 | * You may elect to redistribute this code under either of these licenses.
15 | */
16 |
17 | package io.vertx.ext.reactivestreams.impl;
18 |
19 | import io.vertx.core.Handler;
20 | import io.vertx.ext.reactivestreams.ReactiveReadStream;
21 | import org.reactivestreams.Subscription;
22 |
23 | import java.util.ArrayDeque;
24 | import java.util.Queue;
25 |
26 | /**
27 | * @author Nick Scavelli
28 | * @author Tim Fox
29 | */
30 | public class ReactiveReadStreamImpl implements ReactiveReadStream {
31 |
32 | private final long batchSize;
33 | private Handler dataHandler;
34 | private Handler endHandler;
35 | private Handler exceptionHandler;
36 |
37 | private Subscription subscription;
38 | private final Queue pending = new ArrayDeque<>();
39 | private long demand = Long.MAX_VALUE;
40 | private long tokens;
41 |
42 | public ReactiveReadStreamImpl(long batchSize) {
43 | this.batchSize = batchSize;
44 | }
45 |
46 | public synchronized ReactiveReadStream handler(Handler handler) {
47 | this.dataHandler = handler;
48 | if (dataHandler != null && demand > 0L) {
49 | checkRequestTokens();
50 | }
51 | return this;
52 | }
53 |
54 | @Override
55 | public synchronized ReactiveReadStream pause() {
56 | this.demand = 0L;
57 | return this;
58 | }
59 |
60 | @Override
61 | public ReactiveReadStream fetch(long amount) {
62 | if (amount > 0L) {
63 | demand += amount;
64 | if (demand < 0L) {
65 | demand = Long.MAX_VALUE;
66 | }
67 | T data;
68 | while (demand > 0L && (data = pending.poll()) != null) {
69 | if (demand != Long.MAX_VALUE) {
70 | demand--;
71 | }
72 | handleData(data);
73 | }
74 | checkRequestTokens();
75 | }
76 | return this;
77 | }
78 |
79 | @Override
80 | public synchronized ReactiveReadStream resume() {
81 | return fetch(Long.MAX_VALUE);
82 | }
83 |
84 | @Override
85 | public synchronized ReactiveReadStream endHandler(Handler endHandler) {
86 | this.endHandler = endHandler;
87 | return this;
88 | }
89 |
90 | @Override
91 | public synchronized ReactiveReadStream exceptionHandler(Handler handler) {
92 | this.exceptionHandler = handler;
93 | return this;
94 | }
95 |
96 | @Override
97 | public synchronized void onSubscribe(Subscription subscription) {
98 | if (subscription == null) {
99 | throw new NullPointerException("subscription");
100 | }
101 | if (this.subscription != null) {
102 | subscription.cancel();
103 | } else {
104 | this.subscription = subscription;
105 | }
106 | }
107 |
108 | @Override
109 | public synchronized void onNext(T data) {
110 | if (data == null) {
111 | throw new NullPointerException("data");
112 | }
113 | checkUnsolicitedTokens();
114 | if (demand > 0L) {
115 | if (demand != Long.MAX_VALUE) {
116 | demand--;
117 | }
118 | if (pending.size() > 0) {
119 | pending.add(data);
120 | data = pending.poll();
121 | }
122 | handleData(data);
123 | } else {
124 | pending.add(data);
125 | }
126 | }
127 |
128 | @Override
129 | public synchronized void onError(Throwable throwable) {
130 | if (throwable == null) {
131 | throw new NullPointerException("throwable");
132 | }
133 | if (exceptionHandler != null) {
134 | exceptionHandler.handle(throwable);
135 | }
136 | }
137 |
138 | @Override
139 | public synchronized void onComplete() {
140 | if (endHandler != null) {
141 | endHandler.handle(null);
142 | }
143 | }
144 |
145 | protected void checkUnsolicitedTokens() {
146 | if (tokens == 0) {
147 | throw new IllegalStateException("Data received but wasn't requested");
148 | }
149 | }
150 |
151 | private synchronized void handleData(T data) {
152 | if (dataHandler != null) {
153 | dataHandler.handle(data);
154 | tokens--;
155 | checkRequestTokens();
156 | }
157 | }
158 |
159 | private void checkRequestTokens() {
160 | if (demand > 0L && subscription != null && tokens == 0) {
161 | tokens = batchSize;
162 | subscription.request(batchSize);
163 | }
164 | }
165 |
166 | }
167 |
--------------------------------------------------------------------------------
/src/main/java/io/vertx/ext/reactivestreams/impl/ReactiveWriteStreamImpl.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Red Hat, Inc.
3 | *
4 | * All rights reserved. This program and the accompanying materials
5 | * are made available under the terms of the Eclipse Public License v1.0
6 | * and Apache License v2.0 which accompanies this distribution.
7 | *
8 | * The Eclipse Public License is available at
9 | * http://www.eclipse.org/legal/epl-v10.html
10 | *
11 | * The Apache License v2.0 is available at
12 | * http://www.opensource.org/licenses/apache2.0.php
13 | *
14 | * You may elect to redistribute this code under either of these licenses.
15 | */
16 |
17 | package io.vertx.ext.reactivestreams.impl;
18 |
19 | import io.vertx.core.*;
20 | import io.vertx.core.internal.ContextInternal;
21 | import io.vertx.core.internal.net.NetSocketInternal;
22 | import io.vertx.ext.reactivestreams.ReactiveWriteStream;
23 | import org.reactivestreams.Subscriber;
24 | import org.reactivestreams.Subscription;
25 |
26 | import java.util.ArrayDeque;
27 | import java.util.Objects;
28 | import java.util.Queue;
29 | import java.util.Set;
30 | import java.util.concurrent.ConcurrentHashMap;
31 | import java.util.concurrent.atomic.AtomicLong;
32 |
33 | /**
34 | * @author Tim Fox
35 | */
36 | public class ReactiveWriteStreamImpl implements ReactiveWriteStream {
37 |
38 | private final Set subscriptions = ConcurrentHashMap.newKeySet();
39 | private final Queue- > pending = new ArrayDeque<>();
40 | private Handler drainHandler;
41 | private int writeQueueMaxSize = DEFAULT_WRITE_QUEUE_MAX_SIZE;
42 | protected final ContextInternal ctx;
43 | private boolean closed;
44 |
45 | public ReactiveWriteStreamImpl(Vertx vertx) {
46 | ctx = (ContextInternal) vertx.getOrCreateContext();
47 | }
48 |
49 | private void checkClosed() {
50 | if (closed) {
51 | throw new IllegalStateException("Closed");
52 | }
53 | }
54 |
55 | @Override
56 | public synchronized void subscribe(Subscriber super T> subscriber) {
57 | checkClosed();
58 | Objects.requireNonNull(subscriber);
59 |
60 | SubscriptionImpl sub = new SubscriptionImpl(subscriber);
61 | if (subscriptions.add(sub)) {
62 | ctx.runOnContext(v -> {
63 | try {
64 | subscriber.onSubscribe(sub);
65 | } catch (Throwable t) {
66 | signalError(sub.subscriber, t);
67 | }
68 | });
69 | } else {
70 | throw new IllegalStateException("1.10 Cannot subscribe multiple times with the same subscriber.");
71 | }
72 | }
73 |
74 | @Override
75 | public synchronized Future write(T data) {
76 | checkClosed();
77 | Promise promise = ctx.promise();
78 | pending.add(new Item<>(data, promise));
79 | checkSend();
80 | return promise.future();
81 | }
82 |
83 | @Override
84 | public synchronized ReactiveWriteStream setWriteQueueMaxSize(int maxSize) {
85 | checkClosed();
86 | if (writeQueueMaxSize < 1) {
87 | throw new IllegalArgumentException("writeQueueMaxSize must be >=1");
88 | }
89 | this.writeQueueMaxSize = maxSize;
90 | return this;
91 | }
92 |
93 | @Override
94 | public synchronized boolean writeQueueFull() {
95 | checkClosed();
96 | return pending.size() >= writeQueueMaxSize;
97 | }
98 |
99 | @Override
100 | public synchronized ReactiveWriteStream drainHandler(Handler handler) {
101 | checkClosed();
102 | this.drainHandler = handler;
103 | return this;
104 | }
105 |
106 | @Override
107 | public synchronized ReactiveWriteStream exceptionHandler(Handler handler) {
108 | return this;
109 | }
110 |
111 | @Override
112 | public Future end() {
113 | close();
114 | return ctx.succeededFuture();
115 | }
116 |
117 | @Override
118 | public ReactiveWriteStream close() {
119 | synchronized (this) {
120 | if (closed) {
121 | return this;
122 | }
123 | closed = true;
124 | complete();
125 | subscriptions.clear();
126 | Future closedFut = Future.failedFuture(NetSocketInternal.CLOSED_EXCEPTION);
127 | for (Item item: pending) {
128 | Completable handler = item.handler;
129 | if (handler != null) {
130 | ctx.runOnContext(v -> {
131 | if (closedFut.succeeded()) {
132 | handler.succeed(closedFut.result());
133 | } else {
134 | handler.fail(closedFut.cause());
135 | }
136 | });
137 | }
138 | }
139 | pending.clear();
140 | }
141 | return this;
142 | }
143 |
144 | private synchronized void checkSend() {
145 | if (!subscriptions.isEmpty()) {
146 | long availableTokens = getAvailable();
147 | long toSend = Math.min(availableTokens, pending.size());
148 | takeTokens(toSend);
149 | for (long i = 0; i < toSend; i++) {
150 | sendToSubscribers(pending.poll());
151 | }
152 | if (drainHandler != null && pending.size() < writeQueueMaxSize) {
153 | callDrainHandler();
154 | }
155 | }
156 | }
157 |
158 | private void callDrainHandler() {
159 | Handler dh = drainHandler;
160 | ctx.runOnContext(v -> dh.handle(null));
161 | }
162 |
163 | private long getAvailable() {
164 | long min = Long.MAX_VALUE;
165 | for (SubscriptionImpl subscription: subscriptions) {
166 | min = Math.min(subscription.tokens(), min);
167 | }
168 | return min;
169 | }
170 |
171 | private void takeTokens(long toSend) {
172 | for (SubscriptionImpl subscription: subscriptions) {
173 | subscription.takeTokens(toSend);
174 | }
175 | }
176 |
177 | private void complete() {
178 | for (SubscriptionImpl sub: subscriptions) {
179 | ctx.runOnContext(v -> sub.subscriber.onComplete());
180 | }
181 | }
182 |
183 | private void sendToSubscribers(Item item) {
184 | for (SubscriptionImpl sub: subscriptions) {
185 | onNext(ctx, sub.subscriber, item.value);
186 | }
187 | if (item.handler != null) {
188 | item.handler.succeed();
189 | }
190 | }
191 |
192 | protected void onNext(Context context, Subscriber super T> subscriber, T data) {
193 | context.runOnContext(v -> {
194 | try {
195 | subscriber.onNext(data);
196 | } catch (Throwable t) {
197 | signalError(subscriber, t);
198 | }
199 | });
200 | }
201 |
202 | public class SubscriptionImpl implements Subscription {
203 |
204 | private final Subscriber super T> subscriber;
205 | // We start at Long.MIN_VALUE so we know when we've requested more then Long.MAX_VALUE. See 3.17 of spec
206 | private final AtomicLong tokens = new AtomicLong(Long.MIN_VALUE);
207 |
208 | private SubscriptionImpl(Subscriber super T> subscriber) {
209 | this.subscriber = subscriber;
210 | }
211 |
212 | public long tokens() {
213 | return -(Long.MIN_VALUE - tokens.get());
214 | }
215 |
216 | public void takeTokens(long amount) {
217 | tokens.addAndGet(-amount);
218 | }
219 |
220 | @Override
221 | public void request(long n) {
222 | if (n > 0) {
223 | // More then Long.MAX_VALUE pending
224 | if (tokens.addAndGet(n) > 0) {
225 | signalError(subscriber, new IllegalStateException("3.17 Subscriber has more then Long.MAX_VALUE (2^63-1) currently pending."));
226 | } else {
227 | checkSend();
228 | }
229 | } else {
230 | signalError(subscriber, new IllegalArgumentException("3.9 Subscriber cannot request less then 1 for the number of elements."));
231 | }
232 | }
233 |
234 | @Override
235 | public void cancel() {
236 | subscriptions.remove(this);
237 | }
238 |
239 | @Override
240 | public boolean equals(Object o) {
241 | if (this == o) return true;
242 | if (o == null || getClass() != o.getClass()) return false;
243 |
244 | @SuppressWarnings("unchecked")
245 | SubscriptionImpl that = (SubscriptionImpl) o;
246 |
247 | return subscriber == that.subscriber;
248 | }
249 |
250 | @Override
251 | public int hashCode() {
252 | return subscriber.hashCode();
253 | }
254 | }
255 |
256 | private void signalError(Subscriber super T> subscriber, Throwable error) {
257 | subscriptions.removeIf(sub -> sub.subscriber == subscriber);
258 | subscriber.onError(error);
259 | }
260 |
261 | static class Item {
262 | final T value;
263 | final Completable handler;
264 | Item(T value, Completable handler) {
265 | this.value = value;
266 | this.handler = handler;
267 | }
268 | }
269 |
270 | }
271 |
--------------------------------------------------------------------------------
/src/main/java/io/vertx/ext/reactivestreams/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * = Vert.x Reactive Streams Integration
3 | *
4 | * link:http://www.reactive-streams.org/[Reactive Streams] is an initiative to provide a standard for asynchronous stream
5 | * processing with non-blocking back pressure on the JVM.
6 | *
7 | * This library provides an implementation of reactive streams for Vert.x.
8 | *
9 | * Vert.x provides its own mechanisms for handling streams of data and pumping them with back pressure from one to another
10 | * using the `io.vertx.core.streams.ReadStream`, `io.vertx.core.streams.WriteStream` and `io.vertx.core.streams.Pump`.
11 | * Please see the Vert.x core manual for more information on Vert.x streams.
12 | *
13 | * This library provides implementations of read stream and write stream which also act as reactive streams publishers
14 | * and subscribers. This allows us to treat any reactive streams publisher or subscriber and deal with it like any other
15 | * Vert.x read or write stream.
16 | *
17 | * == Using Vert.x Reactive Streams
18 | *
19 | * To use Vert.x Reactive Streams, add the following dependency to the _dependencies_ section of your build descriptor:
20 | *
21 | * * Maven (in your `pom.xml`):
22 | *
23 | * [source,xml,subs="+attributes"]
24 | * ----
25 | *
26 | * ${maven.groupId}
27 | * ${maven.artifactId}
28 | * ${maven.version}
29 | *
30 | * ----
31 | *
32 | * * Gradle (in your `build.gradle` file):
33 | *
34 | * [source,groovy,subs="+attributes"]
35 | * ----
36 | * compile ${maven.groupId}:${maven.artifactId}:${maven.version}
37 | * ----
38 | *
39 | * == Reactive Read Stream
40 | *
41 | * We provide an implementation of the Vert.x `ReadStream` interface with {@link io.vertx.ext.reactivestreams.ReactiveReadStream}
42 | * which also implements a reactive streams `Subscriber`.
43 | *
44 | * You can pass an instance of this to any reactive streams `Publisher` (e.g. a Publisher from Akka) and then you will be
45 | * able to read from that just like any other Vert.x `ReadStream` (e.g. use a `Pump` to pump it to a `WriteStream`.
46 | *
47 | * Here's an example of taking a publisher from some other reactive streams implementation (e.g. Akka) and pumping that
48 | * stream to the body of a server side HTTP response. This will handle back pressure automatically.
49 | *
50 | * [source,java]
51 | * ----
52 | * {@link examples.ReactiveStreamsExamples#example1}
53 | * ----
54 | *
55 | * == Reactive Write Stream
56 | *
57 | * We also provide an implementation of the Vert.x `WriteStream` interface with {@link io.vertx.ext.reactivestreams.ReactiveWriteStream}
58 | * which also implements a reactive streams `Publisher`. You can take any reactive streams `Subscriber`
59 | * (e.g. a Subscriber from Akka) and then you will be able* to write to it like any other Vert.x `WriteStream`.
60 | * (e.g. use a `Pump` to pump it from a `ReadStream`).
61 | *
62 | * You use `pause`, `resume`, and `writeQueueFull`, as you would with any Vert.x read stream to handle your back pressure.
63 | * This is automatically translated internally into the reactive streams method of propagating back pressure
64 | * (requesting more items).
65 | *
66 | * Here's an example of taking a subscriber from some other reactive streams implementation and pumping the body of
67 | * a server side HTTP request to that subscriber. This will handle back pressure automatically.
68 | *
69 | * [source,java]
70 | * ----
71 | * {@link examples.ReactiveStreamsExamples#example2}
72 | * ----
73 | *
74 | */
75 | @Document(fileName = "index.adoc")
76 | package io.vertx.ext.reactivestreams;
77 |
78 | import io.vertx.docgen.Document;
79 |
--------------------------------------------------------------------------------
/src/main/java/module-info.java:
--------------------------------------------------------------------------------
1 | open module io.vertx.reactivestreams {
2 |
3 | requires static io.vertx.docgen;
4 |
5 | requires io.vertx.core;
6 | requires org.reactivestreams;
7 |
8 | exports io.vertx.ext.reactivestreams;
9 | exports io.vertx.ext.reactivestreams.impl to io.vertx.reactivestreams.tests;
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/ext/reactivestreams/tck/FiniteReactiveWriteStream.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2014 The original author or authors
3 | * ------------------------------------------------------
4 | * All rights reserved. This program and the accompanying materials
5 | * are made available under the terms of the Eclipse Public License v1.0
6 | * and Apache License v2.0 which accompanies this distribution.
7 | *
8 | * The Eclipse Public License is available at
9 | * http://www.eclipse.org/legal/epl-v10.html
10 | *
11 | * The Apache License v2.0 is available at
12 | * http://www.opensource.org/licenses/apache2.0.php
13 | *
14 | * You may elect to redistribute this code under either of these licenses.
15 | */
16 |
17 | package io.vertx.ext.reactivestreams.tck;
18 |
19 | import io.vertx.core.Context;
20 | import io.vertx.core.Vertx;
21 | import io.vertx.ext.reactivestreams.impl.ReactiveWriteStreamImpl;
22 | import org.reactivestreams.Subscriber;
23 |
24 | import java.util.Map;
25 | import java.util.WeakHashMap;
26 | import java.util.concurrent.atomic.AtomicLong;
27 |
28 | /**
29 | * @author Nick Scavelli
30 | */
31 | public class FiniteReactiveWriteStream extends ReactiveWriteStreamImpl {
32 |
33 | private Map, AtomicLong> subs = new WeakHashMap<>();
34 | private final long elements;
35 |
36 | public FiniteReactiveWriteStream(Vertx vertx, long elements) {
37 | super(vertx);
38 | this.elements = elements;
39 | }
40 |
41 | @Override
42 | public void subscribe(Subscriber super T> subscriber) {
43 | subs.put(subscriber, new AtomicLong(elements));
44 | super.subscribe(subscriber);
45 | }
46 |
47 | @Override
48 | protected void onNext(Context context, Subscriber super T> subscriber, T data) {
49 | AtomicLong count = subs.get(subscriber);
50 | if (count == null) return; // Means we already completed it
51 |
52 | long remaining = count.decrementAndGet();
53 | super.onNext(context, subscriber, data);
54 | if (remaining == 0) {
55 | close();
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/ext/reactivestreams/tck/PublisherVerificationTest.java:
--------------------------------------------------------------------------------
1 | package io.vertx.ext.reactivestreams.tck;
2 |
3 | import io.vertx.core.Context;
4 | import io.vertx.core.Vertx;
5 | import io.vertx.core.buffer.Buffer;
6 | import io.vertx.ext.reactivestreams.ReactiveWriteStream;
7 | import io.vertx.ext.reactivestreams.impl.ReactiveWriteStreamImpl;
8 | import io.vertx.test.core.TestUtils;
9 | import org.reactivestreams.Publisher;
10 | import org.reactivestreams.Subscriber;
11 | import org.reactivestreams.tck.PublisherVerification;
12 | import org.reactivestreams.tck.TestEnvironment;
13 |
14 |
15 | /**
16 | * @author Nick Scavelli
17 | */
18 | public class PublisherVerificationTest extends PublisherVerification {
19 |
20 | private static final long DEFAULT_TIMEOUT = 300L;
21 | private static final long DEFAULT_GC_TIMEOUT = 1000L;
22 |
23 | private Vertx vertx;
24 |
25 | public PublisherVerificationTest() {
26 | super(new TestEnvironment(DEFAULT_TIMEOUT), DEFAULT_GC_TIMEOUT);
27 | this.vertx = Vertx.vertx();
28 | }
29 |
30 | @Override
31 | public Publisher createPublisher(long elements) {
32 | ReactiveWriteStream rws;
33 | rws = new FiniteReactiveWriteStream<>(vertx, elements);
34 | if (elements < Integer.MAX_VALUE) {
35 | for (long i = 0; i < elements; i++) {
36 | rws.write(TestUtils.randomBuffer(10));
37 | }
38 | }
39 | return rws;
40 | }
41 |
42 | @Override
43 | public Publisher createFailedPublisher() {
44 |
45 | return new ReactiveWriteStreamImpl(vertx) {
46 | @Override
47 | public void subscribe(Subscriber super Buffer> subscriber) {
48 | super.subscribe(subscriber);
49 | ctx.runOnContext(v -> {
50 | // Now signal an error
51 | subscriber.onError(new RuntimeException("Can't subscribe subscriber: " + subscriber + ", because of reasons."));
52 | });
53 | }
54 | };
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/ext/reactivestreams/tck/SubscriberBlackboxVerificationTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Red Hat, Inc.
3 | *
4 | * All rights reserved. This program and the accompanying materials
5 | * are made available under the terms of the Eclipse Public License v1.0
6 | * and Apache License v2.0 which accompanies this distribution.
7 | *
8 | * The Eclipse Public License is available at
9 | * http://www.eclipse.org/legal/epl-v10.html
10 | *
11 | * The Apache License v2.0 is available at
12 | * http://www.opensource.org/licenses/apache2.0.php
13 | *
14 | * You may elect to redistribute this code under either of these licenses.
15 | */
16 |
17 | package io.vertx.ext.reactivestreams.tck;
18 |
19 | import io.vertx.core.buffer.Buffer;
20 | import io.vertx.ext.reactivestreams.ReactiveReadStream;
21 | import org.reactivestreams.Subscriber;
22 | import org.reactivestreams.tck.SubscriberBlackboxVerification;
23 | import org.reactivestreams.tck.TestEnvironment;
24 |
25 | /*
26 | *
27 | * @author Tim Fox
28 | */
29 | public class SubscriberBlackboxVerificationTest extends SubscriberBlackboxVerification {
30 |
31 | public SubscriberBlackboxVerificationTest() {
32 | super(new TestEnvironment());
33 | }
34 |
35 | @Override
36 | public Buffer createElement(int i) {
37 | return Buffer.buffer("element" + i);
38 | }
39 |
40 | @Override
41 | public Subscriber createSubscriber() {
42 | return ReactiveReadStream.readStream();
43 | }
44 |
45 | @Override
46 | public void triggerRequest(Subscriber super Buffer> subscriber) {
47 | System.out.println("Calling triggerRequest on: " + subscriber);
48 | ReactiveReadStream rrs = (ReactiveReadStream)subscriber;
49 | rrs.handler(b -> {});
50 | }
51 |
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/ext/reactivestreams/tck/SubscriberWhiteboxVerificationTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Red Hat, Inc.
3 | *
4 | * All rights reserved. This program and the accompanying materials
5 | * are made available under the terms of the Eclipse Public License v1.0
6 | * and Apache License v2.0 which accompanies this distribution.
7 | *
8 | * The Eclipse Public License is available at
9 | * http://www.eclipse.org/legal/epl-v10.html
10 | *
11 | * The Apache License v2.0 is available at
12 | * http://www.opensource.org/licenses/apache2.0.php
13 | *
14 | * You may elect to redistribute this code under either of these licenses.
15 | */
16 |
17 | package io.vertx.ext.reactivestreams.tck;
18 |
19 | import io.vertx.core.buffer.Buffer;
20 | import io.vertx.ext.reactivestreams.ReactiveReadStream;
21 | import io.vertx.ext.reactivestreams.impl.ReactiveReadStreamImpl;
22 | import org.junit.Ignore;
23 | import org.reactivestreams.Subscriber;
24 | import org.reactivestreams.Subscription;
25 | import org.reactivestreams.tck.SubscriberWhiteboxVerification;
26 | import org.reactivestreams.tck.TestEnvironment;
27 |
28 | /*
29 | *
30 | * @author Tim Fox
31 | */
32 | @Ignore
33 | public class SubscriberWhiteboxVerificationTest extends SubscriberWhiteboxVerification {
34 |
35 |
36 | public SubscriberWhiteboxVerificationTest() {
37 | super(new TestEnvironment());
38 | }
39 |
40 | @Override
41 | public Subscriber createSubscriber(WhiteboxSubscriberProbe probe) {
42 |
43 | // return YOUR subscriber under-test, with additional WhiteboxSubscriberProbe instrumentation
44 | return new ReactiveReadStreamImpl(ReactiveReadStream.DEFAULT_BATCH_SIZE) {
45 |
46 | @Override
47 | public void onSubscribe(final Subscription s) {
48 |
49 | super.onSubscribe(s);
50 |
51 | // in addition to normal Subscriber work that you're testing,
52 | // register a SubscriberPuppet, to give the TCK control over demand generation and cancelling
53 | probe.registerOnSubscribe(new SubscriberPuppet() {
54 |
55 | @Override
56 | public void triggerRequest(long n) {
57 | s.request(n);
58 | }
59 |
60 | @Override
61 | public void signalCancel() {
62 | s.cancel();
63 | }
64 | });
65 |
66 |
67 | }
68 |
69 | @Override
70 | public void onNext(Buffer value) {
71 |
72 | // in addition to normal Subscriber work that you're testing, register onNext with the probe
73 | probe.registerOnNext(value);
74 | super.onNext(value);
75 | }
76 |
77 | @Override
78 | public void onError(Throwable cause) {
79 |
80 | // in addition to normal Subscriber work that you're testing, register onError with the probe
81 | probe.registerOnError(cause);
82 | super.onError(cause);
83 | }
84 |
85 | @Override
86 | public void onComplete() {
87 |
88 | // in addition to normal Subscriber work that you're testing, register onComplete with the probe
89 | probe.registerOnComplete();
90 | super.onComplete();
91 | }
92 |
93 | // Need to override this to drop the tokens check or the test won't pass
94 | @Override
95 | protected void checkUnsolicitedTokens() {
96 | // NO OP
97 | }
98 | };
99 | }
100 |
101 | @Override
102 | public Buffer createElement(int element) {
103 | return Buffer.buffer("element" + element);
104 | }
105 |
106 | }
107 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/ext/reactivestreams/test/ReactiveReadStreamTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Red Hat, Inc.
3 | *
4 | * All rights reserved. This program and the accompanying materials
5 | * are made available under the terms of the Eclipse Public License v1.0
6 | * and Apache License v2.0 which accompanies this distribution.
7 | *
8 | * The Eclipse Public License is available at
9 | * http://www.eclipse.org/legal/epl-v10.html
10 | *
11 | * The Apache License v2.0 is available at
12 | * http://www.opensource.org/licenses/apache2.0.php
13 | *
14 | * You may elect to redistribute this code under either of these licenses.
15 | */
16 |
17 | package io.vertx.ext.reactivestreams.test;
18 |
19 | import io.vertx.core.VertxException;
20 | import io.vertx.core.buffer.Buffer;
21 | import io.vertx.ext.reactivestreams.ReactiveReadStream;
22 | import org.junit.Test;
23 | import org.reactivestreams.Publisher;
24 | import org.reactivestreams.Subscriber;
25 | import org.reactivestreams.Subscription;
26 |
27 | import java.util.ArrayList;
28 | import java.util.List;
29 |
30 | /**
31 | * @author Tim Fox
32 | */
33 | public class ReactiveReadStreamTest extends ReactiveStreamTestBase {
34 |
35 |
36 | @Test
37 | public void testSubscribe() throws Exception {
38 | ReactiveReadStream rws = ReactiveReadStream.readStream();
39 | MyPublisher publisher = new MyPublisher();
40 | publisher.subscribe(rws);
41 | assertNotNull(publisher.subscription);
42 | assertEquals(0, publisher.subscription.requestedTimes);
43 | }
44 |
45 | @Test
46 | public void testDatahandler() throws Exception {
47 | ReactiveReadStream rws = ReactiveReadStream.readStream();
48 | MyPublisher publisher = new MyPublisher();
49 | publisher.subscribe(rws);
50 | assertNotNull(publisher.subscription);
51 | assertEquals(0, publisher.subscription.requestedTimes);
52 | List received = new ArrayList<>();
53 | rws.handler(received::add);
54 | assertEquals(1, publisher.subscription.requestedTimes);
55 | assertEquals(ReactiveReadStream.DEFAULT_BATCH_SIZE, publisher.subscription.requested);
56 | int numBuffers = 4;
57 | List buffers = createRandomBuffers(numBuffers);
58 | for (Buffer buffer: buffers) {
59 | publisher.subscriber.onNext(buffer);
60 | }
61 | assertEquals(numBuffers, received.size());
62 | for (int i = 0; i < numBuffers; i++) {
63 | assertEquals(buffers.get(i), received.get(i));
64 | }
65 | assertEquals(2, publisher.subscription.requestedTimes);
66 | assertEquals(ReactiveReadStream.DEFAULT_BATCH_SIZE * 2, publisher.subscription.requested);
67 | }
68 |
69 | @Test
70 | public void testSetPausedDataHandler() throws Exception {
71 | ReactiveReadStream rws = ReactiveReadStream.readStream();
72 | MyPublisher publisher = new MyPublisher();
73 | publisher.subscribe(rws);
74 | assertNotNull(publisher.subscription);
75 | assertEquals(0, publisher.subscription.requestedTimes);
76 | List received = new ArrayList<>();
77 | rws.pause();
78 | rws.handler(received::add);
79 | assertEquals(0, publisher.subscription.requestedTimes);
80 | assertEquals(0, publisher.subscription.requested);
81 |
82 | assertEquals(0, received.size());
83 | assertEquals(0, publisher.subscription.requestedTimes);
84 | assertEquals(0, publisher.subscription.requested);
85 | rws.resume();
86 | assertEquals(1, publisher.subscription.requestedTimes);
87 | assertEquals(ReactiveReadStream.DEFAULT_BATCH_SIZE, publisher.subscription.requested);
88 | int numBuffers = 4;
89 | List buffers = createRandomBuffers(numBuffers);
90 | for (Buffer buffer: buffers) {
91 | publisher.subscriber.onNext(buffer);
92 | }
93 | assertEquals(numBuffers, received.size());
94 | for (int i = 0; i < numBuffers; i++) {
95 | assertEquals(buffers.get(i), received.get(i));
96 | }
97 | }
98 |
99 | @Test
100 | public void testPauseInHandler() throws Exception {
101 | ReactiveReadStream rws = ReactiveReadStream.readStream();
102 | MyPublisher publisher = new MyPublisher();
103 | publisher.subscribe(rws);
104 | assertNotNull(publisher.subscription);
105 | assertEquals(0, publisher.subscription.requestedTimes);
106 | List received = new ArrayList<>();
107 | rws.handler(buff -> {
108 | received.add(buff);
109 | rws.pause();
110 | });
111 | assertEquals(1, publisher.subscription.requestedTimes);
112 | assertEquals(ReactiveReadStream.DEFAULT_BATCH_SIZE, publisher.subscription.requested);
113 | int numBuffers = 4;
114 | List buffers = createRandomBuffers(numBuffers);
115 | for (Buffer buffer: buffers) {
116 | publisher.subscriber.onNext(buffer);
117 | }
118 | assertEquals(1, received.size());
119 | assertEquals(buffers.get(0), received.get(0));
120 |
121 | assertEquals(1, publisher.subscription.requestedTimes);
122 | assertEquals(ReactiveReadStream.DEFAULT_BATCH_SIZE, publisher.subscription.requested);
123 | }
124 |
125 | @Test
126 | public void testPauseResume() throws Exception {
127 | ReactiveReadStream rws = ReactiveReadStream.readStream();
128 | MyPublisher publisher = new MyPublisher();
129 | publisher.subscribe(rws);
130 | assertNotNull(publisher.subscription);
131 | assertEquals(0, publisher.subscription.requestedTimes);
132 | List received = new ArrayList<>();
133 | rws.handler(received::add);
134 | assertEquals(1, publisher.subscription.requestedTimes);
135 | assertEquals(ReactiveReadStream.DEFAULT_BATCH_SIZE, publisher.subscription.requested);
136 | int numBuffers = 4;
137 | List buffers = createRandomBuffers(numBuffers);
138 | for (Buffer buffer: buffers) {
139 | publisher.subscriber.onNext(buffer);
140 | }
141 | assertEquals(numBuffers, received.size());
142 | for (int i = 0; i < numBuffers; i++) {
143 | assertEquals(buffers.get(i), received.get(i));
144 | }
145 | rws.pause();
146 | assertEquals(2, publisher.subscription.requestedTimes);
147 | assertEquals(ReactiveReadStream.DEFAULT_BATCH_SIZE * 2, publisher.subscription.requested);
148 | rws.resume();
149 | assertEquals(2, publisher.subscription.requestedTimes);
150 | assertEquals(ReactiveReadStream.DEFAULT_BATCH_SIZE * 2, publisher.subscription.requested);
151 | buffers.clear();
152 | received.clear();
153 | buffers = createRandomBuffers(numBuffers);
154 | for (Buffer buffer: buffers) {
155 | publisher.subscriber.onNext(buffer);
156 | }
157 | assertEquals(numBuffers, received.size());
158 | for (int i = 0; i < numBuffers; i++) {
159 | assertEquals(buffers.get(i), received.get(i));
160 | }
161 | }
162 |
163 | @Test
164 | public void testPauseResumeInHandler() throws Exception {
165 | ReactiveReadStream rws = ReactiveReadStream.readStream();
166 | MyPublisher publisher = new MyPublisher();
167 | publisher.subscribe(rws);
168 | assertNotNull(publisher.subscription);
169 | assertEquals(0, publisher.subscription.requestedTimes);
170 | List received = new ArrayList<>();
171 | rws.handler(buff -> {
172 | rws.pause();
173 | rws.resume();
174 | received.add(buff);
175 | });
176 | assertEquals(1, publisher.subscription.requestedTimes);
177 | assertEquals(ReactiveReadStream.DEFAULT_BATCH_SIZE, publisher.subscription.requested);
178 | int numBuffers = 4;
179 | List buffers = createRandomBuffers(numBuffers);
180 | for (Buffer buffer: buffers) {
181 | publisher.subscriber.onNext(buffer);
182 | }
183 | assertEquals(numBuffers, received.size());
184 | for (int i = 0; i < numBuffers; i++) {
185 | assertEquals(buffers.get(i), received.get(i));
186 | }
187 | assertEquals(2, publisher.subscription.requestedTimes);
188 | assertEquals(ReactiveReadStream.DEFAULT_BATCH_SIZE * 2, publisher.subscription.requested);
189 | }
190 |
191 | @Test
192 | public void testFetch() throws Exception {
193 | ReactiveReadStream rws = ReactiveReadStream.readStream();
194 | MyPublisher publisher = new MyPublisher();
195 | publisher.subscribe(rws);
196 | assertNotNull(publisher.subscription);
197 | assertEquals(0, publisher.subscription.requestedTimes);
198 | List received = new ArrayList<>();
199 | rws.handler(received::add);
200 | assertEquals(1, publisher.subscription.requestedTimes);
201 | assertEquals(ReactiveReadStream.DEFAULT_BATCH_SIZE, publisher.subscription.requested);
202 | int numBuffers = 4;
203 | List buffers = createRandomBuffers(numBuffers);
204 | for (Buffer buffer: buffers) {
205 | publisher.subscriber.onNext(buffer);
206 | }
207 | assertEquals(numBuffers, received.size());
208 | for (int i = 0; i < numBuffers; i++) {
209 | assertEquals(buffers.get(i), received.get(i));
210 | }
211 | rws.pause();
212 | assertEquals(2, publisher.subscription.requestedTimes);
213 | assertEquals(ReactiveReadStream.DEFAULT_BATCH_SIZE * 2, publisher.subscription.requested);
214 | assertEquals(2, publisher.subscription.requestedTimes);
215 | assertEquals(ReactiveReadStream.DEFAULT_BATCH_SIZE * 2, publisher.subscription.requested);
216 | buffers.clear();
217 | received.clear();
218 | buffers = createRandomBuffers(numBuffers);
219 | for (Buffer buffer: buffers) {
220 | publisher.subscriber.onNext(buffer);
221 | }
222 | for (int i = 0;i < numBuffers;i++) {
223 | rws.fetch(1);
224 | assertEquals(1 + i, received.size());
225 | }
226 | for (int i = 0; i < numBuffers; i++) {
227 | assertEquals(buffers.get(i), received.get(i));
228 | }
229 | }
230 |
231 | @Test
232 | public void testOnError() throws Exception {
233 | ReactiveReadStream rws = ReactiveReadStream.readStream();
234 | MyPublisher publisher = new MyPublisher();
235 | publisher.subscribe(rws);
236 | rws.exceptionHandler(t -> {
237 | assertTrue(t instanceof VertxException);
238 | assertEquals("foo", t.getMessage());
239 | testComplete();
240 | });
241 | publisher.subscriber.onError(new VertxException("foo"));
242 | await();
243 | }
244 |
245 | @Test
246 | public void testOnComplete() throws Exception {
247 | ReactiveReadStream rws = ReactiveReadStream.readStream();
248 | MyPublisher publisher = new MyPublisher();
249 | publisher.subscribe(rws);
250 | rws.endHandler(v -> {
251 | testComplete();
252 | });
253 | publisher.subscriber.onComplete();
254 | await();
255 | }
256 |
257 | class MySubscription implements Subscription {
258 |
259 | int requested;
260 | int requestedTimes;
261 |
262 | @Override
263 | public void request(long n) {
264 | requestedTimes++;
265 | requested += n;
266 | }
267 |
268 | @Override
269 | public void cancel() {
270 |
271 | }
272 | }
273 |
274 |
275 | class MyPublisher implements Publisher {
276 |
277 | MySubscription subscription;
278 | Subscriber super Buffer> subscriber;
279 |
280 | @Override
281 | public void subscribe(Subscriber super Buffer> subscriber) {
282 | this.subscriber = subscriber;
283 | subscription = new MySubscription();
284 | subscriber.onSubscribe(subscription);
285 | }
286 | }
287 |
288 | }
289 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/ext/reactivestreams/test/ReactiveStreamTestBase.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Red Hat, Inc.
3 | *
4 | * All rights reserved. This program and the accompanying materials
5 | * are made available under the terms of the Eclipse Public License v1.0
6 | * and Apache License v2.0 which accompanies this distribution.
7 | *
8 | * The Eclipse Public License is available at
9 | * http://www.eclipse.org/legal/epl-v10.html
10 | *
11 | * The Apache License v2.0 is available at
12 | * http://www.opensource.org/licenses/apache2.0.php
13 | *
14 | * You may elect to redistribute this code under either of these licenses.
15 | */
16 |
17 | package io.vertx.ext.reactivestreams.test;
18 |
19 | import io.vertx.core.buffer.Buffer;
20 | import io.vertx.test.core.TestUtils;
21 | import io.vertx.test.core.VertxTestBase;
22 |
23 | import java.util.ArrayList;
24 | import java.util.List;
25 |
26 | /**
27 | * @author Tim Fox
28 | */
29 | public class ReactiveStreamTestBase extends VertxTestBase {
30 |
31 | protected List createRandomBuffers(int number) {
32 | List buffers = new ArrayList<>();
33 | for (int i = 0; i < number; i++) {
34 | buffers.add(TestUtils.randomBuffer(100));
35 | }
36 | return buffers;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/ext/reactivestreams/test/ReactiveWriteStreamTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Red Hat, Inc.
3 | *
4 | * All rights reserved. This program and the accompanying materials
5 | * are made available under the terms of the Eclipse Public License v1.0
6 | * and Apache License v2.0 which accompanies this distribution.
7 | *
8 | * The Eclipse Public License is available at
9 | * http://www.eclipse.org/legal/epl-v10.html
10 | *
11 | * The Apache License v2.0 is available at
12 | * http://www.opensource.org/licenses/apache2.0.php
13 | *
14 | * You may elect to redistribute this code under either of these licenses.
15 | */
16 |
17 | package io.vertx.ext.reactivestreams.test;
18 |
19 | import io.vertx.core.Future;
20 | import io.vertx.core.Promise;
21 | import io.vertx.core.buffer.Buffer;
22 | import io.vertx.ext.reactivestreams.ReactiveWriteStream;
23 | import io.vertx.test.core.TestUtils;
24 | import org.junit.Test;
25 | import org.reactivestreams.Subscriber;
26 | import org.reactivestreams.Subscription;
27 |
28 | import java.util.List;
29 | import java.util.concurrent.CopyOnWriteArrayList;
30 | import java.util.concurrent.atomic.AtomicBoolean;
31 |
32 | /**
33 | * @author Tim Fox
34 | */
35 | public class ReactiveWriteStreamTest extends ReactiveStreamTestBase {
36 |
37 | @Test
38 | public void testWriteNoTokensInitially() throws Exception {
39 | ReactiveWriteStream rws = ReactiveWriteStream.writeStream(vertx);
40 |
41 | MySubscriber subscriber = new MySubscriber();
42 | rws.subscribe(subscriber);
43 |
44 | assertWaitUntil(() -> subscriber.subscription != null);
45 |
46 | List buffers = createRandomBuffers(4);
47 | for (Buffer buffer: buffers) {
48 | rws.write(buffer);
49 | }
50 |
51 | assertTrue(subscriber.buffers.isEmpty());
52 |
53 | subscriber.subscription.request(1);
54 |
55 | assertWaitUntil(() -> subscriber.buffers.size() == 1);
56 | assertEquals(1, subscriber.buffers.size());
57 | assertSame(buffers.get(0), subscriber.buffers.get(0));
58 |
59 | subscriber.subscription.request(2);
60 | assertWaitUntil(() -> subscriber.buffers.size() == 3);
61 | assertEquals(3, subscriber.buffers.size());
62 | assertSame(buffers.get(1), subscriber.buffers.get(1));
63 | assertSame(buffers.get(2), subscriber.buffers.get(2));
64 |
65 | }
66 |
67 | @Test
68 | public void testWriteInitialTokens() throws Exception {
69 | ReactiveWriteStream rws = ReactiveWriteStream.writeStream(vertx);
70 |
71 | MySubscriber subscriber = new MySubscriber();
72 | rws.subscribe(subscriber);
73 |
74 | assertWaitUntil(() -> subscriber.subscription != null);
75 | subscriber.subscription.request(3);
76 |
77 | List buffers = createRandomBuffers(4);
78 | for (Buffer buffer: buffers) {
79 | rws.write(buffer);
80 | }
81 |
82 | assertWaitUntil(() -> subscriber.buffers.size() == 3);
83 | assertEquals(3, subscriber.buffers.size());
84 | assertSame(buffers.get(0), subscriber.buffers.get(0));
85 | assertSame(buffers.get(1), subscriber.buffers.get(1));
86 | assertSame(buffers.get(2), subscriber.buffers.get(2));
87 |
88 | }
89 |
90 | // TODO test setters for max writestreamsize and buffer size and valid values
91 |
92 | // TODO test cancel subscription
93 |
94 | @Test
95 | public void testMultipleSubscribers() throws Exception {
96 | ReactiveWriteStream rws = ReactiveWriteStream.writeStream(vertx);
97 |
98 | MySubscriber subscriber1 = new MySubscriber();
99 | rws.subscribe(subscriber1);
100 | MySubscriber subscriber2 = new MySubscriber();
101 | rws.subscribe(subscriber2);
102 | MySubscriber subscriber3 = new MySubscriber();
103 | rws.subscribe(subscriber3);
104 |
105 | assertWaitUntil(() -> subscriber1.subscription != null);
106 | assertWaitUntil(() -> subscriber2.subscription != null);
107 | assertWaitUntil(() -> subscriber3.subscription != null);
108 |
109 | List buffers = createRandomBuffers(10);
110 | for (Buffer buffer: buffers) {
111 | rws.write(buffer);
112 | }
113 |
114 | assertEquals(0, subscriber1.buffers.size());
115 | assertEquals(0, subscriber2.buffers.size());
116 | assertEquals(0, subscriber3.buffers.size());
117 |
118 | // We go at the speed of the slowest consumer
119 | subscriber1.subscription.request(1);
120 | assertEquals(0, subscriber1.buffers.size());
121 | assertEquals(0, subscriber2.buffers.size());
122 | assertEquals(0, subscriber3.buffers.size());
123 |
124 | subscriber2.subscription.request(1);
125 | assertEquals(0, subscriber1.buffers.size());
126 | assertEquals(0, subscriber2.buffers.size());
127 | assertEquals(0, subscriber3.buffers.size());
128 |
129 | subscriber3.subscription.request(1);
130 | assertWaitUntil(() -> subscriber1.buffers.size() == 1);
131 | assertWaitUntil(() -> subscriber2.buffers.size() == 1);
132 | assertWaitUntil(() -> subscriber3.buffers.size() == 1);
133 | assertEquals(1, subscriber1.buffers.size());
134 | assertEquals(1, subscriber2.buffers.size());
135 | assertEquals(1, subscriber3.buffers.size());
136 | assertEquals(buffers.get(0), subscriber1.buffers.get(0));
137 | assertEquals(buffers.get(0), subscriber2.buffers.get(0));
138 | assertEquals(buffers.get(0), subscriber3.buffers.get(0));
139 |
140 | subscriber1.subscription.request(4);
141 | assertEquals(1, subscriber1.buffers.size());
142 | assertEquals(1, subscriber2.buffers.size());
143 | assertEquals(1, subscriber3.buffers.size());
144 | subscriber2.subscription.request(3);
145 | assertEquals(1, subscriber1.buffers.size());
146 | assertEquals(1, subscriber2.buffers.size());
147 | assertEquals(1, subscriber3.buffers.size());
148 | subscriber3.subscription.request(2);
149 | assertWaitUntil(() -> subscriber1.buffers.size() == 3);
150 | assertWaitUntil(() -> subscriber2.buffers.size() == 3);
151 | assertWaitUntil(() -> subscriber3.buffers.size() == 3);
152 | assertEquals(3, subscriber1.buffers.size());
153 | assertEquals(3, subscriber2.buffers.size());
154 | assertEquals(3, subscriber3.buffers.size());
155 | assertEquals(buffers.get(0), subscriber1.buffers.get(0));
156 | assertEquals(buffers.get(1), subscriber1.buffers.get(1));
157 | assertEquals(buffers.get(2), subscriber1.buffers.get(2));
158 | assertEquals(buffers.get(0), subscriber2.buffers.get(0));
159 | assertEquals(buffers.get(1), subscriber2.buffers.get(1));
160 | assertEquals(buffers.get(2), subscriber2.buffers.get(2));
161 | assertEquals(buffers.get(0), subscriber3.buffers.get(0));
162 | assertEquals(buffers.get(1), subscriber3.buffers.get(1));
163 | assertEquals(buffers.get(2), subscriber3.buffers.get(2));
164 |
165 | subscriber2.subscription.request(1);
166 | assertEquals(3, subscriber1.buffers.size());
167 | assertEquals(3, subscriber2.buffers.size());
168 | assertEquals(3, subscriber3.buffers.size());
169 | subscriber3.subscription.request(2);
170 | assertWaitUntil(() -> subscriber1.buffers.size() == 5);
171 | assertWaitUntil(() -> subscriber2.buffers.size() == 5);
172 | assertWaitUntil(() -> subscriber3.buffers.size() == 5);
173 | assertEquals(5, subscriber1.buffers.size());
174 | assertEquals(5, subscriber2.buffers.size());
175 | assertEquals(5, subscriber3.buffers.size());
176 | assertEquals(buffers.get(0), subscriber1.buffers.get(0));
177 | assertEquals(buffers.get(1), subscriber1.buffers.get(1));
178 | assertEquals(buffers.get(2), subscriber1.buffers.get(2));
179 | assertEquals(buffers.get(3), subscriber1.buffers.get(3));
180 | assertEquals(buffers.get(4), subscriber1.buffers.get(4));
181 | assertEquals(buffers.get(0), subscriber2.buffers.get(0));
182 | assertEquals(buffers.get(1), subscriber2.buffers.get(1));
183 | assertEquals(buffers.get(2), subscriber2.buffers.get(2));
184 | assertEquals(buffers.get(3), subscriber2.buffers.get(3));
185 | assertEquals(buffers.get(4), subscriber2.buffers.get(4));
186 | assertEquals(buffers.get(0), subscriber3.buffers.get(0));
187 | assertEquals(buffers.get(1), subscriber3.buffers.get(1));
188 | assertEquals(buffers.get(2), subscriber3.buffers.get(2));
189 | assertEquals(buffers.get(3), subscriber3.buffers.get(3));
190 | assertEquals(buffers.get(4), subscriber3.buffers.get(4));
191 | }
192 |
193 | @Test
194 | public void testWriteQueueFullAndDrainDefaultQueueSize() throws Exception {
195 | ReactiveWriteStream rws = ReactiveWriteStream.writeStream(vertx);
196 | testWriteQueueFullAndDrain(rws, 10);
197 | }
198 |
199 | private void testWriteQueueFullAndDrain(ReactiveWriteStream rws, int writeQueueMaxSize) throws Exception {
200 | rws.setWriteQueueMaxSize(writeQueueMaxSize);
201 | MySubscriber subscriber = new MySubscriber();
202 | rws.subscribe(subscriber);
203 | for (int i = 0; i < writeQueueMaxSize - 1; i++) {
204 | rws.write(TestUtils.randomBuffer(50));
205 | }
206 | assertFalse(rws.writeQueueFull());
207 | Buffer buff2 = TestUtils.randomBuffer(100);
208 | rws.write(buff2);
209 | assertTrue(rws.writeQueueFull());
210 | rws.drainHandler(v -> {
211 | assertFalse(rws.writeQueueFull());
212 | testComplete();
213 | });
214 | assertWaitUntil(() -> subscriber.subscription != null);
215 | subscriber.subscription.request(2);
216 | await();
217 | }
218 |
219 |
220 | class MySubscriber implements Subscriber {
221 |
222 | final List buffers = new CopyOnWriteArrayList<>();
223 | volatile Subscription subscription;
224 |
225 | @Override
226 | public void onSubscribe(Subscription subscription) {
227 | this.subscription = subscription;
228 | }
229 |
230 | @Override
231 | public void onNext(Buffer buffer) {
232 | buffers.add(buffer);
233 | }
234 |
235 | @Override
236 | public void onError(Throwable throwable) {
237 |
238 | }
239 |
240 | @Override
241 | public void onComplete() {
242 |
243 | }
244 | }
245 |
246 | @Test
247 | public void testCancelSubscriptionOnError1() {
248 | ReactiveWriteStream rws = ReactiveWriteStream.writeStream(vertx);
249 | AtomicBoolean failed = new AtomicBoolean();
250 | MySubscriber subscriber1 = new MySubscriber() {
251 | @Override
252 | public void onSubscribe(Subscription subscription) {
253 | super.onSubscribe(subscription);
254 | throw new RuntimeException();
255 | }
256 | @Override
257 | public void onNext(Buffer buffer) {
258 | fail();
259 | }
260 | @Override
261 | public void onError(Throwable throwable) {
262 | failed.set(true);
263 | }
264 | };
265 | rws.subscribe(subscriber1);
266 | MySubscriber subscriber2 = new MySubscriber() {
267 | @Override
268 | public void onSubscribe(Subscription subscription) {
269 | subscription.request(1);
270 | super.onSubscribe(subscription);
271 | }
272 | int count = 0;
273 | @Override
274 | public void onNext(Buffer buffer) {
275 | if (++count == 1) {
276 | testComplete();
277 | }
278 | }
279 | };
280 | rws.subscribe(subscriber2);
281 | assertWaitUntil(() -> subscriber1.subscription != null);
282 | assertWaitUntil(() -> subscriber2.subscription != null);
283 | assertWaitUntil(failed::get);
284 | rws.write(createRandomBuffers(1).get(0));
285 | await();
286 | }
287 |
288 | @Test
289 | public void testCancelSubscriptionOnError2() {
290 | ReactiveWriteStream rws = ReactiveWriteStream.writeStream(vertx);
291 | AtomicBoolean failed = new AtomicBoolean();
292 | MySubscriber subscriber1 = new MySubscriber() {
293 | @Override
294 | public void onSubscribe(Subscription subscription) {
295 | subscription.request(2);
296 | super.onSubscribe(subscription);
297 | }
298 | @Override
299 | public void onNext(Buffer buffer) {
300 | if (!failed.get()) {
301 | throw new RuntimeException();
302 | } else {
303 | fail();
304 | }
305 | }
306 | @Override
307 | public void onError(Throwable throwable) {
308 | failed.set(true);
309 | }
310 | };
311 | rws.subscribe(subscriber1);
312 | MySubscriber subscriber2 = new MySubscriber() {
313 | @Override
314 | public void onSubscribe(Subscription subscription) {
315 | subscription.request(3);
316 | super.onSubscribe(subscription);
317 | }
318 | int count = 0;
319 | @Override
320 | public void onNext(Buffer buffer) {
321 | if (++count == 3) {
322 | testComplete();
323 | }
324 | }
325 | };
326 | rws.subscribe(subscriber2);
327 | assertWaitUntil(() -> subscriber1.subscription != null);
328 | assertWaitUntil(() -> subscriber2.subscription != null);
329 | rws.write(createRandomBuffers(1).get(0));
330 | assertWaitUntil(failed::get);
331 | rws.write(createRandomBuffers(1).get(0));
332 | rws.write(createRandomBuffers(1).get(0));
333 | await();
334 | }
335 |
336 | @Test
337 | public void testWriteHandler() {
338 | ReactiveWriteStream rws = ReactiveWriteStream.writeStream(vertx);
339 | Promise f1 = Promise.promise();
340 | rws.write(createRandomBuffers(1).get(0)).onComplete(f1);
341 | assertFalse(f1.future().isComplete());
342 | rws.subscribe(new MySubscriber() {
343 | @Override
344 | public void onSubscribe(Subscription subscription) {
345 | subscription.request(1);
346 | }
347 | });
348 | waitUntil(f1.future()::succeeded);
349 | }
350 |
351 | @Test
352 | public void testWriteHandlerFailure() {
353 | ReactiveWriteStream rws = ReactiveWriteStream.writeStream(vertx);
354 | Promise f1 = Promise.promise();
355 | rws.write(createRandomBuffers(1).get(0)).onComplete(f1);
356 | assertFalse(f1.future().isComplete());
357 | Promise f2 = Promise.promise();
358 | rws.end().onComplete(f2);
359 | waitUntil(f1.future()::failed);
360 | waitUntil(f2.future()::succeeded);
361 | }
362 | }
363 |
--------------------------------------------------------------------------------
/src/test/java/module-info.java:
--------------------------------------------------------------------------------
1 | module io.vertx.reactivestreams.tests {
2 | requires io.vertx.core;
3 | requires io.vertx.core.tests;
4 | requires io.vertx.reactivestreams;
5 | requires junit;
6 | requires org.reactivestreams;
7 | requires org.reactivestreams.tck;
8 |
9 | exports io.vertx.ext.reactivestreams.test;
10 | exports io.vertx.ext.reactivestreams.tck;
11 | }
12 |
--------------------------------------------------------------------------------