├── .build ├── deploy-snapshot.sh ├── perform-release.sh ├── release.sh └── settings.xml ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── pom.xml └── src ├── main └── java │ └── me │ └── escoffier │ └── vertx │ └── completablefuture │ └── VertxCompletableFuture.java └── test └── java └── me └── escoffier └── vertx └── completablefuture ├── CompletableFutureOperatorTest.java ├── SupplyAndRunAsyncTest.java ├── VertxCompletableFutureTest.java └── examples └── HttpClientTest.java /.build/deploy-snapshot.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Deploy a jar, source jar, and javadoc jar to Sonatype's snapshot repo. 4 | # 5 | 6 | REPO="cescoffier/vertx-completable-future" 7 | BRANCH="master" 8 | 9 | set -e 10 | echo "Configuring deployment using ${SONATYPE_USERNAME} for repository ${TRAVIS_REPO_SLUG}" 11 | if [ "$TRAVIS_REPO_SLUG" != "$REPO" ]; then 12 | echo "Skipping snapshot deployment: wrong repository. Expected '$REPO' but was '$TRAVIS_REPO_SLUG'." 13 | elif [ "$TRAVIS_PULL_REQUEST" != "false" ]; then 14 | echo "Skipping snapshot deployment: was pull request." 15 | elif [ "$TRAVIS_BRANCH" != "$BRANCH" ]; then 16 | echo "Skipping snapshot deployment: wrong branch. Expected '$BRANCH' but was '$TRAVIS_BRANCH'." 17 | else 18 | echo "Deploying snapshot..." 19 | mvn clean source:jar javadoc:jar deploy --settings=".build/settings.xml" -Dmaven.test.skip=true -Psonatype 20 | echo "Snapshot deployed!" 21 | fi 22 | -------------------------------------------------------------------------------- /.build/perform-release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | RED='\033[0;31m' 5 | NC='\033[0m' # No Color 6 | YELLOW='\033[0;33m' 7 | BLUE='\033[0;34m' 8 | 9 | # Extract versions 10 | LAST_TAG=$(git tag -l --sort=-v:refname | head -n 1) 11 | 12 | echo -e "${BLUE}Last tag:${YELLOW} ${LAST_TAG} ${NC}" 13 | 14 | cd target || exit 15 | export GPG_TTY=$(tty) 16 | echo -e "${BLUE}Cloning repo${NC}" 17 | git clone git@github.com:cescoffier/vertx-completable-future release-work 18 | cd release-work 19 | echo -e "${BLUE}Switching to tag${NC}" 20 | git checkout ${LAST_TAG} 21 | echo -e "${BLUE}Running deployment${NC}" 22 | mvn deploy -Psonatype,release 23 | 24 | #echo -e "${BLUE}Build doc${NC}" 25 | #mvn -Pdoc-html 26 | #echo -e "${BLUE}Deploying doc${NC}" 27 | #git clone -b gh-pages git@github.com:cescoffier/vertx-completable-future gh-pages 28 | #cp -rv target/generated-docs/* gh-pages 29 | #cd gh-pages 30 | #git add --ignore-errors * 31 | #git commit -m "generated documentation" 32 | #git push origin gh-pages 33 | 34 | cd ../.. || exit 35 | 36 | 37 | -------------------------------------------------------------------------------- /.build/release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | RED='\033[0;31m' 5 | NC='\033[0m' # No Color 6 | YELLOW='\033[0;33m' 7 | BLUE='\033[0;34m' 8 | 9 | # Extract versions 10 | LAST_TAG=$(git tag -l --sort=-v:refname | head -n 1) 11 | VERSION="" 12 | NEXT_DEV_VERSION="" 13 | TAG="" 14 | 15 | echo "Last tag: ${LAST_TAG}" 16 | regex="v([0-9]+).([0-9]+).([0-9]+)" 17 | if [[ ${LAST_TAG} =~ $regex ]] 18 | then 19 | major="${BASH_REMATCH[1]}" 20 | minor="${BASH_REMATCH[2]}" 21 | micro="${BASH_REMATCH[3]}" 22 | VERSION=${major}.${minor}.$(($micro +1)) 23 | TAG="v${VERSION}" 24 | NEXT_DEV_VERSION="${major}.${minor}-SNAPSHOT" 25 | echo -e "${BLUE}Release version: ${YELLOW}${VERSION} ${NC}" 26 | echo -e "${BLUE}Next development version: ${YELLOW}${NEXT_DEV_VERSION} ${NC}" 27 | else 28 | echo -e "${RED}Invalid last tag ${NC}" 29 | exit -1 30 | fi 31 | 32 | # Update version in pom.xml file 33 | echo -e "${BLUE}Updating project version to: ${YELLOW} ${VERSION} ${NC}" 34 | mvn versions:set -DnewVersion=${VERSION} >& bump-version-dev.log 35 | echo -e "${BLUE}Issuing a verification build${NC}" 36 | mvn clean install -DskipTests >& fast-build.log 37 | 38 | echo -e "${BLUE}Committing changes${NC}" 39 | git commit -am "Releasing version ${VERSION}" 40 | 41 | echo -e "${BLUE}Creating the tag ${YELLOW}${TAG}${NC}" 42 | git tag -a ${TAG} -m "Releasing ${TAG}" 43 | 44 | echo -e "${BLUE}Updating project version to: ${YELLOW}${NEXT_DEV_VERSION}${NC}" 45 | mvn versions:set -DnewVersion=${NEXT_DEV_VERSION} >& bump-version-dev.log 46 | 47 | mvn clean install -DskipTests >& fast-build-dev.log 48 | 49 | echo -e "${BLUE}Committing changes${NC}" 50 | git commit -am "Bumping version to ${NEXT_DEV_VERSION}" 51 | 52 | echo -e "${BLUE}Pushing changes${NC}" 53 | git push origin master --tags 54 | 55 | rm -Rf *.log pom.xml.versionsBackup 56 | 57 | 58 | -------------------------------------------------------------------------------- /.build/settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | sonatype-nexus-snapshots 5 | ${env.SONATYPE_USERNAME} 6 | ${env.SONATYPE_PASSWORD} 7 | 8 | 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | ### Java template 3 | *.class 4 | 5 | # Mobile Tools for Java (J2ME) 6 | .mtj.tmp/ 7 | 8 | # Package Files # 9 | *.jar 10 | *.war 11 | *.ear 12 | 13 | .project 14 | .classpath 15 | .settings 16 | 17 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 18 | hs_err_pid* 19 | ### Maven template 20 | target/ 21 | pom.xml.tag 22 | pom.xml.releaseBackup 23 | pom.xml.versionsBackup 24 | pom.xml.next 25 | release.properties 26 | dependency-reduced-pom.xml 27 | buildNumber.properties 28 | .mvn/timing.properties 29 | 30 | 31 | ### Vert.x 32 | .vertx 33 | 34 | ### IntelliJ 35 | .idea/ 36 | *.iml 37 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - openjdk8 4 | addons: 5 | sonarcloud: 6 | organization: reactiverse-vertx-maven-plugin 7 | token: 8 | secure: "s/a5VFbYyomNVymcgpHdX74vHscmmJedwVwOFajYsiArfhvjlpY45s/0ATuvFzWc98qyoQlztcNmIHEtFD3gdJMYoTgz2sEDNUZ4OC5K95SPIMLeYmw/IxXP7IsLqeBwrZX+yjwXzZgkevqMNkdAtKtsSTN/B9XOXuG3zn397WFVA6X4m6psG8YwPOyZafCDEqgauCX19jmaEXwD3e4mzptay3dfzwVV8rghAgrCpc3tSK076nhzYmZZ7LG1L8l1Pk0pvvLLO6YRRDassre07nOQlZcv1GlSVn4Ce/ZcSz7O2q0usyJ3iTU/atovjbdOTB56oybe0kSWosXWFki8r4Az3vkr+IAvAkDf7R56ly7s/CLs7Ka874mLBvVdGWwNXfBV5/3E8TJYRasukNwJOqGRc3AVytcJp7p7TwpQce8kk8MnVoH6k1h4qUoLPSTjt6K12Yee64bukGAG0OrY3vxDU86EtXpnopMhty1qzN/IYAJ14hHKTuqp5USLRpfPTzE21SrV/ZxUBuT4ySxPwAu7SCvwaLPJTj95sDnZnoL7S1I/fHvwG2IwwOT5YyP91l7FTvggXmWvC7X5spviri/wIEwr0loCfJU+FfwKrFHx6tw7g+XoSD80agU3NooAeGkVVfEm5/rKHxPFfBXm0NfkI8WSz2ooqrwxXzBjbTo=" 9 | env: 10 | global: 11 | - SONATYPE_USERNAME="clement.escoffier" 12 | - secure: h887vbdiBD8HPV0Ltssu2JIUWVHKHkshxiXu/7uh9ZFQyyzqVWLTjcSeXWqWwKgTVgUtDBtF0OICw7RqKAKzSA2EX+v0A9EzkkR2cqa5EKlii1GJRec7qbteRUIln0ejNHYKo66wzU3p/1AGMGK7b8nAu3jdAViTFgQ2b/1RHSA6lCWsOFMPkQVxGwsexXifWIIps4wCKmFmCN5yzFLOPDfv9bV76jcC5MyUy4KuLW8igm2ygJ8b+O71t3WJNPf12s/mRWcQ5AaRU772l8affbDvDjeud+IHpG7vmWCnCrVd2pLogfLnL/4Uj6uXhLScuHzynqAWRTW2GOrwbBEV5Qz01fcVTE2J1KfEkeQtGhpCWcB/6YQOgilttK0k+nwENxehg2fJgR5ZvxcfO+qkOMWsDNCGHRYKvQlJFHiY3DHsE4DuAKxE8WK5fVMNWA4eslAPJW/NOY5PQ0p8jWo2JSyR/ZvKPtR2z4CpwQcMgrtFIZWe4DhZbf73MedQ7n8qtx+WuBW1iL5fSUMye63VYuTILUTmNotrBSARqonlbZ5iglviZUwB/rm8dNklCNufuuEOeBkCLLuygF2/6mIlYN5X+UL1RZWsy4nx0oMNE6/TAGSoKPjt0DMYSABssPCJ7MRniIDzwhoJ6/CSlAnii29OGzcLEGQ2CXsLeE4NiRs= 13 | script: 14 | - mvn clean install sonar:sonar -Pcoverage -Dsonar.host.url=https://sonarcloud.io/ 15 | after_success: 16 | - ".build/deploy-snapshot.sh" 17 | notifications: 18 | email: 19 | - clement@apache.org 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 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.md: -------------------------------------------------------------------------------- 1 | # Vert.x Completable Future 2 | 3 | This project provides a way to use the Completable Future API with Vert.x. 4 | 5 | ## Why can't you use Completable Future with Vert.x 6 | 7 | Using Completable Future with Vert.x may lead to some thread issues. 8 | 9 | When you execute an asynchronous operation with Vert.x, the `Handler>` is called on on the same thread as the method having enqueued the asynchronous operation. Thanks to this _single-threaded_ aspect, Vert.x removes the need of synchronization and also it improves performances. 10 | 11 | Completable Future uses a fork-join thread pool. So callbacks (dependent stages) are called in a thread of this pool, so it's not the caller thread. 12 | 13 | This project provides the Completable Future API but enforces the Vert.x threading model: 14 | 15 | * When using `xAsync` methods (without `executor`), the callbacks are called on the Vert.x context 16 | * When using non-async, it uses the caller thread. If it's a Vert.x thread the same thread is used. If not called from a Vert.x thread, 17 | it still uses the caller thread 18 | * When using `xAsync` methods with an `Executor` parameter, this executor is used to execute the callback (does not enforce the Vert.x thread system) 19 | 20 | ## Examples 21 | 22 | All methods taking a `Vertx` instance as parameter exists with a `Context` parameter. 23 | 24 | ### Creating a VertxCompletableFuture 25 | 26 | ``` 27 | // From a Vert.x instance 28 | CompletableFuture future = new VertxCompletableFuture(vertx); 29 | 30 | // From a specific context 31 | Context context = ... 32 | CompletableFuture future = new VertxCompletableFuture(context); 33 | 34 | // From a completable future 35 | CompletableFuture cf = ... 36 | CompletableFuture future = VertxCompletableFuture.from(context, cf); 37 | 38 | // From a Vert.x future 39 | Future fut = ... 40 | CompletableFuture future = VertxCompletableFuture.from(context, fut); 41 | ``` 42 | 43 | You can also pass a `Supplier` or a `Runnable`: 44 | 45 | ``` 46 | // Run in context 47 | CompletableFuture future = VertxCompletableFuture.supplyAsync(vertx, () -> return "foo";); 48 | // Run in Vert.x worker thread 49 | CompletableFuture future = VertxCompletableFuture.supplyBlockingAsync(vertx, () -> return "foo"); 50 | 51 | CompletableFuture future = VertxCompletableFuture.runAsync(vertx, () -> System.out.println(foo")); 52 | CompletableFuture future = VertxCompletableFuture.runBlockingAsync(vertx, () -> System.out.println(foo")); 53 | ``` 54 | 55 | _*BlockingAsync_ method uses a Vert.x worker thread and do not block the Vert.x Event Loop. 56 | 57 | ### Stages 58 | 59 | Once you have the `VertxCompletableFuture` instance, you can use the `CompletableFuture` API: 60 | 61 | ``` 62 | VertxCompletableFuture future = new VertxCompletableFuture<>(vertx); 63 | future 64 | .thenApply(this::square) 65 | .thenAccept(System.out::print) 66 | .thenRun(System.out::println); 67 | 68 | // Somewhere in your code, later... 69 | future.complete(42); 70 | ``` 71 | 72 | You can compose, combine, join completable futures with: 73 | 74 | * combine 75 | * runAfterEither 76 | * acceptEither 77 | * runAfterBoth 78 | * ... 79 | 80 | ### All and Any operations 81 | 82 | The `VertxCompletableFuture` class offers two composition operators: 83 | 84 | * `allOf` - execute all the passed `CompletableFuture` (not necessarily `VertxCompletableFuture`) and calls dependant stages when all of the futures have been completed or one has failed 85 | * `anyOf` - execute all the passed `CompletableFuture` (not necessarily `VertxCompletableFuture`) and calls dependant stages when one of them has been completed 86 | 87 | Unlike `CompletableFuture`, the dependent stages are called in the Vert.x context. 88 | 89 | ``` 90 | HttpClientOptions options = new HttpClientOptions().setDefaultPort(8080).setDefaultHost("localhost"); 91 | HttpClient client1 = vertx.createHttpClient(options); 92 | HttpClient client2 = vertx.createHttpClient(options); 93 | 94 | VertxCompletableFuture requestA = new VertxCompletableFuture<>(vertx); 95 | client1.get("/A").handler(resp -> { 96 | resp.exceptionHandler(requestA::completeExceptionally) 97 | .bodyHandler(buffer -> { 98 | requestA.complete(Integer.parseInt(buffer.toString())); 99 | }); 100 | }).exceptionHandler(requestA::completeExceptionally).end(); 101 | 102 | VertxCompletableFuture requestB = new VertxCompletableFuture<>(vertx); 103 | client2.get("/B").handler(resp -> { 104 | resp.exceptionHandler(requestB::completeExceptionally) 105 | .bodyHandler(buffer -> { 106 | requestB.complete(Integer.parseInt(buffer.toString())); 107 | }); 108 | }).exceptionHandler(requestB::completeExceptionally).end(); 109 | 110 | 111 | VertxCompletableFuture.allOf(requestA, requestB).thenApply(v -> requestA.join() + requestB.join()) 112 | .thenAccept(i -> { 113 | tc.assertEquals(65, i); 114 | async.complete(); 115 | }); 116 | } 117 | ``` 118 | 119 | ### From / To Vert.x Futures 120 | 121 | You can transform a `VertxCompletableFuture` to a Vert.x `Future` with the `toFuture` method. 122 | 123 | You can also creates a new `VertxCompletableFuture` from a Vert.x `Future` using: 124 | 125 | ``` 126 | Future vertxFuture = ... 127 | VertxCompletableFuture vcf = VertxCompletableFuture.from(vertx, vertxFuture); 128 | 129 | vcf.thenAccept(i -> {...}).whenComplete((res, err) -> {...}) 130 | ``` 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 4.0.0 5 | 6 | me.escoffier.vertx 7 | vertx-completable-future 8 | 0.1.3-SNAPSHOT 9 | 10 | Vert.x Completable Future 11 | An implementation of CompletableFuture for Vert.x 3.x 12 | https://github.com/cescoffier/vertx-completable-future 13 | 14 | 15 | 16 | Apache License 2.0 17 | http://www.apache.org/licenses/LICENSE-2.0 18 | 19 | 20 | 21 | 22 | 23 | UTF-8 24 | 3.7.1 25 | 26 | 27 | 28 | 29 | io.vertx 30 | vertx-core 31 | 32 | provided 33 | 34 | 35 | junit 36 | junit 37 | 4.13.1 38 | test 39 | 40 | 41 | io.vertx 42 | vertx-unit 43 | test 44 | 45 | 46 | com.google.guava 47 | guava 48 | 28.0-jre 49 | test 50 | 51 | 52 | 53 | 54 | 55 | io.vertx 56 | vertx-dependencies 57 | ${vertx.version} 58 | pom 59 | import 60 | 61 | 62 | 63 | 64 | 65 | 66 | ossrh 67 | https://oss.sonatype.org/content/repositories/snapshots 68 | 69 | 70 | 71 | 72 | 73 | 74 | org.sonatype.plugins 75 | nexus-staging-maven-plugin 76 | 1.6.8 77 | true 78 | 79 | ossrh 80 | https://oss.sonatype.org/ 81 | true 82 | 83 | 84 | 85 | maven-compiler-plugin 86 | 3.8.1 87 | 88 | 1.8 89 | 1.8 90 | 91 | 92 | 93 | org.apache.maven.plugins 94 | maven-release-plugin 95 | 2.5.3 96 | 97 | true 98 | false 99 | release 100 | deploy 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | release 109 | 110 | 111 | 112 | org.apache.maven.plugins 113 | maven-source-plugin 114 | 3.1.0 115 | 116 | 117 | attach-sources 118 | 119 | jar-no-fork 120 | 121 | 122 | 123 | 124 | 125 | org.apache.maven.plugins 126 | maven-javadoc-plugin 127 | 3.1.1 128 | 129 | 130 | attach-javadocs 131 | 132 | jar 133 | 134 | 135 | -Xdoclint:none 136 | 137 | 138 | 139 | 140 | 141 | org.apache.maven.plugins 142 | maven-gpg-plugin 143 | 1.6 144 | 145 | 146 | sign-artifacts 147 | verify 148 | 149 | sign 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | sonatype 160 | 161 | 162 | sonatype-nexus-snapshots 163 | https://oss.sonatype.org/content/repositories/snapshots 164 | 165 | 166 | sonatype-nexus-snapshots 167 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 168 | 169 | 170 | 171 | 172 | 1.6.8 173 | 174 | 175 | 176 | 177 | 178 | org.sonatype.plugins 179 | nexus-staging-maven-plugin 180 | ${nexus-staging-maven-plugin.version} 181 | true 182 | 183 | sonatype-nexus-snapshots 184 | https://oss.sonatype.org/ 185 | true 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | coverage 194 | 195 | 196 | 197 | org.jacoco 198 | jacoco-maven-plugin 199 | 0.8.4 200 | 201 | 202 | 203 | prepare-agent 204 | 205 | 206 | 207 | report 208 | test 209 | 210 | report 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | https://github.com/cescoffier/vertx-completable-future 222 | https://github.com/cescoffier/vertx-completable-future.git 223 | scm:git:git@github.com:cescoffier/vertx-completable-future.git 224 | HEAD 225 | 226 | 227 | 2016 228 | 229 | 230 | github 231 | https://github.com/cescoffier/vertx-completable-future/issues 232 | 233 | 234 | 235 | escoffier.me 236 | http://escoffier.me 237 | 238 | 239 | 240 | 241 | cescoffier 242 | Clement Escoffier 243 | clement[AT]apache[DOT]org 244 | https://github.com/cescoffier 245 | 246 | 247 | 248 | 249 | -------------------------------------------------------------------------------- /src/main/java/me/escoffier/vertx/completablefuture/VertxCompletableFuture.java: -------------------------------------------------------------------------------- 1 | package me.escoffier.vertx.completablefuture; 2 | 3 | import io.vertx.core.AsyncResult; 4 | import io.vertx.core.Context; 5 | import io.vertx.core.Future; 6 | import io.vertx.core.Vertx; 7 | import io.vertx.core.WorkerExecutor; 8 | 9 | import java.util.Objects; 10 | import java.util.concurrent.CompletableFuture; 11 | import java.util.concurrent.CompletionStage; 12 | import java.util.concurrent.Executor; 13 | import java.util.function.*; 14 | 15 | /** 16 | * An implementation of {@link CompletableFuture} for Vert.x. It differs in the way to handle async calls: 17 | *

18 | * * {@link VertxCompletableFuture} are attached to a Vert.x {@link Context} 19 | * * All operator methods returns {@link VertxCompletableFuture} 20 | * * method not passing an {@link Executor} are executed on the attached {@link Context} 21 | * * All non async method are executed on the current Thread (so not necessary on the attached {@link Context} 22 | *

23 | * The class also offer bridges methods with Vert.x {@link Future}, and regular {@link CompletableFuture}. 24 | * 25 | * @param the expected type of result 26 | */ 27 | @SuppressWarnings("WeakerAccess") 28 | public class VertxCompletableFuture extends CompletableFuture implements CompletionStage { 29 | private final Executor executor; 30 | 31 | /** 32 | * The {@link Context} used by the future. 33 | */ 34 | private final Context context; 35 | 36 | // ============= Constructors ============= 37 | 38 | /** 39 | * Creates an instance of {@link VertxCompletableFuture}, using the current Vert.x context or create a new one. 40 | * 41 | * @param vertx the Vert.x instance 42 | */ 43 | public VertxCompletableFuture(Vertx vertx) { 44 | this(Objects.requireNonNull(vertx).getOrCreateContext()); 45 | } 46 | 47 | /** 48 | * Creates an instance of {@link VertxCompletableFuture}, using the given {@link Context}. 49 | * 50 | * @param context the context 51 | */ 52 | public VertxCompletableFuture(Context context) { 53 | this.context = Objects.requireNonNull(context); 54 | this.executor = command -> context.runOnContext(v -> command.run()); 55 | } 56 | 57 | /** 58 | * Creates a new {@link VertxCompletableFuture} from the given context and given {@link CompletableFuture}. 59 | * The created {@link VertxCompletableFuture} is completed successfully or not when the given completable future 60 | * completes successfully or not. 61 | * 62 | * @param context the context 63 | * @param future the completable future 64 | */ 65 | private VertxCompletableFuture(Context context, CompletableFuture future) { 66 | this(context); 67 | Objects.requireNonNull(future).whenComplete((res, err) -> { 68 | if (err != null) { 69 | completeExceptionally(err); 70 | } else { 71 | complete(res); 72 | } 73 | }); 74 | } 75 | 76 | /** 77 | * Creates a new {@link VertxCompletableFuture} using the current {@link Context}. This method 78 | * must be used from a Vert.x thread, or fails. 79 | */ 80 | public VertxCompletableFuture() { 81 | this(Vertx.currentContext()); 82 | } 83 | 84 | // ============= Factory methods (from) ============= 85 | 86 | /** 87 | * Creates a new {@link VertxCompletableFuture} with the current {@link Context} and given 88 | * {@link CompletableFuture}. 89 | *

90 | * The created {@link VertxCompletableFuture} is completed successfully or not when the given completable future 91 | * completes successfully or not. 92 | * 93 | * @param future the future 94 | * @param the type of the result 95 | * @return the new {@link VertxCompletableFuture} 96 | */ 97 | public static VertxCompletableFuture from(CompletableFuture future) { 98 | return from(Vertx.currentContext(), future); 99 | } 100 | 101 | /** 102 | * Creates a new {@link VertxCompletableFuture} with the current {@link Context} and given 103 | * {@link CompletionStage}. 104 | *

105 | * The created {@link VertxCompletableFuture} is completed successfully or not when the given completable future 106 | * completes successfully or not. 107 | * 108 | * @param stage the completion stage 109 | * @param the type of the result 110 | * @return the new {@link VertxCompletableFuture} 111 | */ 112 | public static VertxCompletableFuture from(CompletionStage stage) { 113 | return from(Vertx.currentContext(), stage); 114 | } 115 | 116 | /** 117 | * Creates a new {@link VertxCompletableFuture} with the current {@link Context} and given 118 | * {@link Future}. 119 | *

120 | * The created {@link VertxCompletableFuture} is completed successfully or not when the given completable future 121 | * completes successfully or not. 122 | * 123 | * @param future the future 124 | * @param the type of the result 125 | * @return the new {@link VertxCompletableFuture} 126 | */ 127 | public static VertxCompletableFuture from(Future future) { 128 | return from(Vertx.currentContext(), future); 129 | } 130 | 131 | /** 132 | * Creates a new {@link VertxCompletableFuture} from the given {@link Vertx} instance and given 133 | * {@link CompletableFuture}. The returned future uses the current Vert.x context, or creates a new one. 134 | *

135 | * The created {@link VertxCompletableFuture} is completed successfully or not when the given completable future 136 | * completes successfully or not. 137 | * 138 | * @param vertx the Vert.x instance 139 | * @param future the future 140 | * @param the type of the result 141 | * @return the new {@link VertxCompletableFuture} 142 | */ 143 | public static VertxCompletableFuture from(Vertx vertx, CompletableFuture future) { 144 | return from(vertx.getOrCreateContext(), future); 145 | } 146 | 147 | /** 148 | * Creates a new {@link VertxCompletableFuture} from the given {@link Vertx} instance and given 149 | * {@link CompletionStage}. The returned future uses the current Vert.x context, or creates a new one. 150 | *

151 | * The created {@link VertxCompletableFuture} is completed successfully or not when the given completable future 152 | * completes successfully or not. 153 | * 154 | * @param vertx the Vert.x instance 155 | * @param stage the completion stage 156 | * @param the type of the result 157 | * @return the new {@link VertxCompletableFuture} 158 | */ 159 | public static VertxCompletableFuture from(Vertx vertx, CompletionStage stage) { 160 | return from(vertx.getOrCreateContext(), stage); 161 | } 162 | 163 | /** 164 | * Creates a new {@link VertxCompletableFuture} from the given {@link Context} instance and given 165 | * {@link Future}. The returned future uses the current Vert.x context, or creates a new one. 166 | *

167 | * The created {@link VertxCompletableFuture} is completed successfully or not when the given future 168 | * completes successfully or not. 169 | * 170 | * @param vertx the Vert.x instance 171 | * @param future the Vert.x future 172 | * @param the type of the result 173 | * @return the new {@link VertxCompletableFuture} 174 | */ 175 | public static VertxCompletableFuture from(Vertx vertx, Future future) { 176 | return from(vertx.getOrCreateContext(), future); 177 | } 178 | 179 | /** 180 | * Creates a {@link VertxCompletableFuture} from the given {@link Context} and {@link CompletionStage}. 181 | *

182 | * The created {@link VertxCompletableFuture} is completed successfully or not when the given future 183 | * completes successfully or not. The completion is called on the given {@link Context}, immediately if it is 184 | * already executing on the right context, asynchronously if not. 185 | * 186 | * @param context the context 187 | * @param stage the completion stage 188 | * @param the type of result 189 | * @return the creation {@link VertxCompletableFuture} 190 | */ 191 | public static VertxCompletableFuture from(Context context, CompletionStage stage) { 192 | return from(context, stage.toCompletableFuture()); 193 | } 194 | 195 | /** 196 | * Creates a {@link VertxCompletableFuture} from the given {@link Context} and {@link CompletableFuture}. 197 | *

198 | * The created {@link VertxCompletableFuture} is completed successfully or not when the given future 199 | * completes successfully or not. The completion is called on the given {@link Context}, immediately if it is 200 | * already executing on the right context, asynchronously if not. 201 | * 202 | * @param context the context 203 | * @param future the future 204 | * @param the type of result 205 | * @return the creation {@link VertxCompletableFuture} 206 | */ 207 | public static VertxCompletableFuture from(Context context, CompletableFuture future) { 208 | VertxCompletableFuture res = new VertxCompletableFuture<>(Objects.requireNonNull(context)); 209 | Objects.requireNonNull(future).whenComplete((result, error) -> { 210 | if (context == Vertx.currentContext()) { 211 | res.complete(result, error); 212 | } else { 213 | res.context.runOnContext(v -> res.complete(result, error)); 214 | } 215 | }); 216 | return res; 217 | } 218 | 219 | /** 220 | * Creates a new {@link VertxCompletableFuture} from the given {@link Context} instance and given 221 | * {@link Future}. The returned future uses the current Vert.x context, or creates a new one. 222 | *

223 | * The created {@link VertxCompletableFuture} is completed successfully or not when the given future 224 | * completes successfully or not. The created {@link VertxCompletableFuture} is completed successfully or not 225 | * when the given future completes successfully or not. The completion is called on the given {@link Context}, 226 | * immediately if it is already executing on the right context, asynchronously if not. 227 | * 228 | * @param context the context 229 | * @param future the Vert.x future 230 | * @param the type of the result 231 | * @return the new {@link VertxCompletableFuture} 232 | */ 233 | public static VertxCompletableFuture from(Context context, Future future) { 234 | VertxCompletableFuture res = new VertxCompletableFuture<>(Objects.requireNonNull(context)); 235 | Objects.requireNonNull(future).setHandler(ar -> { 236 | if (context == Vertx.currentContext()) { 237 | res.completeFromAsyncResult(ar); 238 | } else { 239 | res.context.runOnContext(v -> res.completeFromAsyncResult(ar)); 240 | } 241 | }); 242 | return res; 243 | } 244 | 245 | /** 246 | * Returns a new CompletableFuture that is asynchronously completed by a task running in the current Vert.x 247 | * {@link Context} with the value obtained by calling the given Supplier. 248 | *

249 | * This method is different from {@link CompletableFuture#supplyAsync(Supplier)} as it does not use a fork join 250 | * executor, but use the Vert.x context. 251 | * 252 | * @param vertx the Vert.x instance 253 | * @param supplier a function returning the value to be used to complete the returned CompletableFuture 254 | * @param the function's return type 255 | * @return the new CompletableFuture 256 | */ 257 | public static VertxCompletableFuture supplyAsync(Vertx vertx, Supplier supplier) { 258 | return supplyAsync(Objects.requireNonNull(vertx).getOrCreateContext(), supplier); 259 | } 260 | 261 | /** 262 | * Returns a new CompletableFuture that is asynchronously completed by a task running in the 263 | * current Vert.x {@link Context} after it runs the given action. 264 | *

265 | * This method is different from {@link CompletableFuture#supplyAsync(Supplier)} as it does not use a fork join 266 | * executor, but use the Vert.x context. 267 | * 268 | * @param vertx the Vert.x instance 269 | * @param runnable the action to run before completing the returned CompletableFuture 270 | * @return the new CompletableFuture 271 | */ 272 | public static VertxCompletableFuture runAsync(Vertx vertx, Runnable runnable) { 273 | return runAsync(Objects.requireNonNull(vertx).getOrCreateContext(), runnable); 274 | } 275 | 276 | /** 277 | * Returns a new CompletableFuture that is asynchronously completed by a task running in the current Vert.x 278 | * {@link Context} with the value obtained by calling the given Supplier. 279 | *

280 | * This method is different from {@link CompletableFuture#supplyAsync(Supplier)} as it does not use a fork join 281 | * executor, but use the Vert.x context. 282 | * 283 | * @param context the context in which the supplier is executed. 284 | * @param supplier a function returning the value to be used to complete the returned CompletableFuture 285 | * @param the function's return type 286 | * @return the new CompletableFuture 287 | */ 288 | public static VertxCompletableFuture supplyAsync(Context context, Supplier supplier) { 289 | Objects.requireNonNull(supplier); 290 | VertxCompletableFuture future = new VertxCompletableFuture<>(Objects.requireNonNull(context)); 291 | context.runOnContext(v -> { 292 | try { 293 | future.complete(supplier.get()); 294 | } catch (Throwable e) { 295 | future.completeExceptionally(e); 296 | } 297 | }); 298 | return future; 299 | } 300 | 301 | /** 302 | * Returns a new CompletableFuture that is asynchronously completed by a task running in the 303 | * current Vert.x {@link Context} after it runs the given action. 304 | *

305 | * This method is different from {@link CompletableFuture#runAsync(Runnable)} as it does not use a fork join 306 | * executor, but use the Vert.x context. 307 | * 308 | * @param context the context 309 | * @param runnable the action to run before completing the returned CompletableFuture 310 | * @return the new CompletableFuture 311 | */ 312 | public static VertxCompletableFuture runAsync(Context context, Runnable runnable) { 313 | Objects.requireNonNull(runnable); 314 | VertxCompletableFuture future = new VertxCompletableFuture<>(context); 315 | context.runOnContext(v -> { 316 | try { 317 | runnable.run(); 318 | future.complete(null); 319 | } catch (Throwable e) { 320 | future.completeExceptionally(e); 321 | } 322 | }); 323 | return future; 324 | } 325 | 326 | /** 327 | * Returns a new CompletableFuture that is asynchronously completed by a task running in the worker thread pool of 328 | * Vert.x 329 | *

330 | * This method is different from {@link CompletableFuture#supplyAsync(Supplier)} as it does not use a fork join 331 | * executor, but the worker thread pool. 332 | * 333 | * @param vertx the Vert.x instance 334 | * @param supplier a function returning the value to be used to complete the returned CompletableFuture 335 | * @param the function's return type 336 | * @return the new CompletableFuture 337 | */ 338 | public static VertxCompletableFuture supplyBlockingAsync(Vertx vertx, Supplier supplier) { 339 | return supplyBlockingAsync(Objects.requireNonNull(vertx).getOrCreateContext(), supplier); 340 | } 341 | 342 | /** 343 | * Returns a new CompletableFuture that is asynchronously completed by a task running in the worker thread pool of 344 | * Vert.x 345 | *

346 | * This method is different from {@link CompletableFuture#supplyAsync(Supplier)} as it does not use a fork join 347 | * executor, but the worker thread pool. 348 | * 349 | * @param vertx the Vert.x instance 350 | * @param worker the WorkerExecution on which the supplier is to be executed 351 | * @param supplier a function returning the value to be used to complete the returned CompletableFuture 352 | * @param the function's return type 353 | * @return the new CompletableFuture 354 | */ 355 | public static VertxCompletableFuture supplyBlockingAsyncOn(Vertx vertx, WorkerExecutor worker, Supplier supplier) { 356 | return supplyBlockingAsyncOn(Objects.requireNonNull(vertx).getOrCreateContext(), worker, supplier); 357 | } 358 | 359 | /** 360 | * Returns a new CompletableFuture that is asynchronously completed by a action running in the worker thread pool of 361 | * Vert.x 362 | *

363 | * This method is different from {@link CompletableFuture#runAsync(Runnable)} as it does not use a fork join 364 | * executor, but the worker thread pool. 365 | * 366 | * @param vertx the Vert.x instance 367 | * @param runnable the action, when its execution completes, it completes the returned CompletableFuture. If the 368 | * execution throws an exception, the returned CompletableFuture is completed exceptionally. 369 | * @return the new CompletableFuture 370 | */ 371 | public static VertxCompletableFuture runBlockingAsync(Vertx vertx, Runnable runnable) { 372 | return runBlockingAsync(Objects.requireNonNull(vertx).getOrCreateContext(), runnable); 373 | } 374 | 375 | /** 376 | * Returns a new CompletableFuture that is asynchronously completed by a action running in the worker thread pool of 377 | * Vert.x 378 | *

379 | * This method is different from {@link CompletableFuture#runAsync(Runnable)} as it does not use a fork join 380 | * executor, but the worker thread pool. 381 | * 382 | * @param context the Vert.x context 383 | * @param runnable the action, when its execution completes, it completes the returned CompletableFuture. If the 384 | * execution throws an exception, the returned CompletableFuture is completed exceptionally. 385 | * @return the new CompletableFuture 386 | */ 387 | public static VertxCompletableFuture runBlockingAsync(Context context, Runnable runnable) { 388 | Objects.requireNonNull(runnable); 389 | VertxCompletableFuture future = new VertxCompletableFuture<>(Objects.requireNonNull(context)); 390 | context.executeBlocking( 391 | fut -> { 392 | try { 393 | runnable.run(); 394 | fut.complete(null); 395 | } catch (Throwable e) { 396 | fut.fail(e); 397 | } 398 | }, 399 | false, 400 | ar -> { 401 | if (ar.failed()) { 402 | future.completeExceptionally(ar.cause()); 403 | } else { 404 | future.complete(ar.result()); 405 | } 406 | } 407 | ); 408 | return future; 409 | } 410 | 411 | /** 412 | * Returns a new CompletableFuture that is asynchronously completed by a action running in the provided 413 | * Vert.x worker thread pool. 414 | *

415 | * This method is different from {@link CompletableFuture#runAsync(Runnable)} as it does not use a fork join 416 | * executor, but the worker thread pool. 417 | * 418 | * @param vertx the Vert.x instance 419 | * @param worker the WorkerExecution on which the runnable is to be executed 420 | * @param runnable the action, when its execution completes, it completes the returned CompletableFuture. If the 421 | * execution throws an exception, the returned CompletableFuture is completed exceptionally. 422 | * @return the new CompletableFuture 423 | */ 424 | public static VertxCompletableFuture runBlockingAsyncOn(Vertx vertx, WorkerExecutor worker, Runnable runnable) { 425 | return runBlockingAsyncOn(Objects.requireNonNull(vertx).getOrCreateContext(), worker, runnable); 426 | } 427 | 428 | /** 429 | * Returns a new CompletableFuture that is asynchronously completed by a action running in the provided 430 | * Vert.x worker thread pool. 431 | *

432 | * This method is different from {@link CompletableFuture#runAsync(Runnable)} as it does not use a fork join 433 | * executor, but the provided worker thread pool. 434 | * 435 | * @param context the Vert.x context 436 | * @param worker the WorkerExecution on which the runnable is to be executed 437 | * @param runnable the action, when its execution completes, it completes the returned CompletableFuture. If the 438 | * execution throws an exception, the returned CompletableFuture is completed exceptionally. 439 | * @return the new CompletableFuture 440 | */ 441 | public static VertxCompletableFuture runBlockingAsyncOn(Context context, WorkerExecutor worker, Runnable runnable) { 442 | Objects.requireNonNull(runnable); 443 | VertxCompletableFuture future = new VertxCompletableFuture<>(Objects.requireNonNull(context)); 444 | Objects.requireNonNull(worker).executeBlocking( 445 | fut -> { 446 | try { 447 | runnable.run(); 448 | fut.complete(null); 449 | } catch (Throwable e) { 450 | fut.fail(e); 451 | } 452 | }, 453 | false, 454 | ar -> { 455 | if (ar.failed()) { 456 | future.completeExceptionally(ar.cause()); 457 | } else { 458 | future.complete(ar.result()); 459 | } 460 | } 461 | ); 462 | return future; 463 | } 464 | 465 | /** 466 | * Returns a new CompletableFuture that is asynchronously completed by a task running in the worker thread pool of 467 | * Vert.x 468 | *

469 | * This method is different from {@link CompletableFuture#supplyAsync(Supplier)} as it does not use a fork join 470 | * executor, but the worker thread pool. 471 | * 472 | * @param context the context in which the supplier is executed. 473 | * @param supplier a function returning the value to be used to complete the returned CompletableFuture 474 | * @param the function's return type 475 | * @return the new CompletableFuture 476 | */ 477 | public static VertxCompletableFuture supplyBlockingAsync(Context context, Supplier supplier) { 478 | Objects.requireNonNull(supplier); 479 | VertxCompletableFuture future = new VertxCompletableFuture<>(context); 480 | context.executeBlocking( 481 | fut -> { 482 | try { 483 | fut.complete(supplier.get()); 484 | } catch (Throwable e) { 485 | fut.fail(e); 486 | } 487 | }, 488 | false, 489 | ar -> { 490 | if (ar.failed()) { 491 | future.completeExceptionally(ar.cause()); 492 | } else { 493 | future.complete(ar.result()); 494 | } 495 | } 496 | ); 497 | return future; 498 | } 499 | 500 | /** 501 | * Returns a new CompletableFuture that is asynchronously completed by a task running in the provided 502 | * Vert.x worker thread pool. 503 | *

504 | * This method is different from {@link CompletableFuture#supplyAsync(Supplier)} as it does not use a fork join 505 | * executor, but the worker thread pool. 506 | * 507 | * @param context the context in which the supplier is executed. 508 | * @param worker the WorkerExecution on which the supplier is to be executed 509 | * @param supplier a function returning the value to be used to complete the returned CompletableFuture 510 | * @param the function's return type 511 | * @return the new CompletableFuture 512 | */ 513 | public static VertxCompletableFuture supplyBlockingAsyncOn(Context context, WorkerExecutor worker, Supplier supplier) { 514 | Objects.requireNonNull(supplier); 515 | VertxCompletableFuture future = new VertxCompletableFuture<>(context); 516 | Objects.requireNonNull(worker).executeBlocking( 517 | fut -> { 518 | try { 519 | fut.complete(supplier.get()); 520 | } catch (Throwable e) { 521 | fut.fail(e); 522 | } 523 | }, 524 | false, 525 | ar -> { 526 | if (ar.failed()) { 527 | future.completeExceptionally(ar.cause()); 528 | } else { 529 | future.complete(ar.result()); 530 | } 531 | } 532 | ); 533 | return future; 534 | } 535 | 536 | // ============= Wrapping methods ============= 537 | 538 | /** 539 | * Creates a Vert.x {@link Future} from the given {@link CompletableFuture} (that can be a 540 | * {@link VertxCompletableFuture}). 541 | * 542 | * @param future the future 543 | * @param the type of the result 544 | * @return the Vert.x future completed or failed when the given {@link CompletableFuture} completes or fails. 545 | */ 546 | public static Future toFuture(CompletableFuture future) { 547 | Future fut = Future.future(); 548 | Objects.requireNonNull(future).whenComplete((res, err) -> { 549 | if (err != null) { 550 | fut.fail(err); 551 | } else { 552 | fut.complete(res); 553 | } 554 | }); 555 | // this can't recurse because the Future API guarantees repeated completions should do nothing 556 | fut.setHandler(res -> { 557 | if (res.succeeded()) { 558 | future.complete(res.result()); 559 | } else { 560 | future.completeExceptionally(res.cause()); 561 | } 562 | }); 563 | return fut; 564 | } 565 | 566 | // ============= Parallel composition methods ============= 567 | 568 | /** 569 | * Returns a new CompletableFuture that is completed when all of the given CompletableFutures complete. If any of 570 | * the given CompletableFutures complete exceptionally, then the returned CompletableFuture also does so, with a 571 | * CompletionException holding this exception as its cause. Otherwise, the results, if any, of the given 572 | * CompletableFutures are not reflected in the returned CompletableFuture, but may be obtained by inspecting them 573 | * individually. If no CompletableFutures are provided, returns a CompletableFuture completed with the value 574 | * {@code null}. 575 | *

576 | *

Among the applications of this method is to await completion 577 | * of a set of independent CompletableFutures before continuing a 578 | * program, as in: {@code CompletableFuture.allOf(c1, c2, c3).join();}. 579 | *

580 | * Unlike the original {@link CompletableFuture#allOf(CompletableFuture[])} this method invokes the dependent 581 | * stages into the Vert.x context. 582 | * 583 | * @param vertx the Vert.x instance to retrieve the context 584 | * @param futures the CompletableFutures 585 | * @return a new CompletableFuture that is completed when all of the given CompletableFutures complete 586 | * @throws NullPointerException if the array or any of its elements are {@code null} 587 | */ 588 | public static VertxCompletableFuture allOf(Vertx vertx, CompletableFuture... futures) { 589 | CompletableFuture all = CompletableFuture.allOf(futures); 590 | return VertxCompletableFuture.from(vertx, all); 591 | } 592 | 593 | /** 594 | * Returns a new CompletableFuture that is completed when all of the given CompletableFutures complete. If any of 595 | * the given CompletableFutures complete exceptionally, then the returned CompletableFuture also does so, with a 596 | * CompletionException holding this exception as its cause. Otherwise, the results, if any, of the given 597 | * CompletableFutures are not reflected in the returned CompletableFuture, but may be obtained by inspecting them 598 | * individually. If no CompletableFutures are provided, returns a CompletableFuture completed with the value 599 | * {@code null}. 600 | *

601 | *

Among the applications of this method is to await completion 602 | * of a set of independent CompletableFutures before continuing a 603 | * program, as in: {@code CompletableFuture.allOf(c1, c2, c3).join();}. 604 | *

605 | * Unlike the original {@link CompletableFuture#allOf(CompletableFuture[])} this method invokes the dependent 606 | * stages into the Vert.x context. 607 | * 608 | * @param context the context 609 | * @param futures the CompletableFutures 610 | * @return a new CompletableFuture that is completed when all of the given CompletableFutures complete 611 | * @throws NullPointerException if the array or any of its elements are {@code null} 612 | */ 613 | public static VertxCompletableFuture allOf(Context context, CompletableFuture... futures) { 614 | CompletableFuture all = CompletableFuture.allOf(futures); 615 | return VertxCompletableFuture.from(context, all); 616 | } 617 | 618 | /** 619 | * Returns a new CompletableFuture that is completed when any of the given CompletableFutures complete, with the 620 | * same result. Otherwise, if it completed exceptionally, the returned CompletableFuture also does so, with a 621 | * CompletionException holding this exception as its cause. If no CompletableFutures are provided, returns an 622 | * incomplete CompletableFuture. 623 | *

624 | * Unlike the original {@link CompletableFuture#allOf(CompletableFuture[])} this method invokes the dependent 625 | * stages into the Vert.x context. 626 | * 627 | * @param vertx the Vert.x instance to retrieve the context 628 | * @param futures the CompletableFutures 629 | * @return a new CompletableFuture that is completed with the result or exception of any of the given 630 | * CompletableFutures when one completes 631 | * @throws NullPointerException if the array or any of its elements are {@code null} 632 | */ 633 | public static VertxCompletableFuture anyOf(Vertx vertx, CompletableFuture... futures) { 634 | CompletableFuture all = CompletableFuture.anyOf(futures); 635 | return VertxCompletableFuture.from(vertx, all); 636 | } 637 | 638 | /** 639 | * Returns a new CompletableFuture that is completed when any of the given CompletableFutures complete, with the 640 | * same result. Otherwise, if it completed exceptionally, the returned CompletableFuture also does so, with a 641 | * CompletionException holding this exception as its cause. If no CompletableFutures are provided, returns an 642 | * incomplete CompletableFuture. 643 | *

644 | * Unlike the original {@link CompletableFuture#allOf(CompletableFuture[])} this method invokes the dependent 645 | * stages into the Vert.x context. 646 | * 647 | * @param context the context 648 | * @param futures the CompletableFutures 649 | * @return a new CompletableFuture that is completed with the result or exception of any of the given 650 | * CompletableFutures when one completes 651 | * @throws NullPointerException if the array or any of its elements are {@code null} 652 | */ 653 | public static VertxCompletableFuture anyOf(Context context, CompletableFuture... futures) { 654 | CompletableFuture all = CompletableFuture.anyOf(futures); 655 | return VertxCompletableFuture.from(context, all); 656 | } 657 | 658 | // ============= with context methods ============= 659 | 660 | /** 661 | * Creates a new {@link VertxCompletableFuture} using the current context. This method is used to switch between 662 | * Vert.x contexts. 663 | * 664 | * @return the created {@link VertxCompletableFuture} 665 | */ 666 | public VertxCompletableFuture withContext() { 667 | Context context = Objects.requireNonNull(Vertx.currentContext()); 668 | return withContext(context); 669 | } 670 | 671 | /** 672 | * Creates a new {@link VertxCompletableFuture} using the current context or creates a new one. This method is used 673 | * to switch between Vert.x contexts. 674 | * 675 | * @return the created {@link VertxCompletableFuture} 676 | */ 677 | public VertxCompletableFuture withContext(Vertx vertx) { 678 | return withContext(Objects.requireNonNull(vertx).getOrCreateContext()); 679 | } 680 | 681 | /** 682 | * Creates a new {@link VertxCompletableFuture} using the given context. This method is used to switch between 683 | * Vert.x contexts. 684 | * 685 | * @return the created {@link VertxCompletableFuture} 686 | */ 687 | public VertxCompletableFuture withContext(Context context) { 688 | VertxCompletableFuture future = new VertxCompletableFuture<>(Objects.requireNonNull(context)); 689 | whenComplete((res, err) -> { 690 | if (err != null) { 691 | future.completeExceptionally(err); 692 | } else { 693 | future.complete(res); 694 | } 695 | }); 696 | return future; 697 | } 698 | 699 | /** 700 | * @return the context associated with the current {@link VertxCompletableFuture}. 701 | */ 702 | public Context context() { 703 | return context; 704 | } 705 | 706 | // ============= Composite Future implementation ============= 707 | 708 | @Override 709 | public VertxCompletableFuture thenApply(Function fn) { 710 | return new VertxCompletableFuture<>(context, super.thenApply(fn)); 711 | } 712 | 713 | @Override 714 | public VertxCompletableFuture thenApplyAsync(Function fn, Executor executor) { 715 | return new VertxCompletableFuture<>(context, super.thenApplyAsync(fn, executor)); 716 | } 717 | 718 | @Override 719 | public VertxCompletableFuture thenAcceptAsync(Consumer action, Executor executor) { 720 | return new VertxCompletableFuture<>(context, super.thenAcceptAsync(action, executor)); 721 | } 722 | 723 | @Override 724 | public VertxCompletableFuture thenRun(Runnable action) { 725 | return new VertxCompletableFuture<>(context, super.thenRun(action)); 726 | } 727 | 728 | @Override 729 | public VertxCompletableFuture thenRunAsync(Runnable action, Executor executor) { 730 | return new VertxCompletableFuture<>(context, super.thenRunAsync(action, executor)); 731 | } 732 | 733 | @Override 734 | public VertxCompletableFuture thenCombine(CompletionStage other, BiFunction fn) { 735 | return new VertxCompletableFuture<>(context, super.thenCombine(other, fn)); 736 | } 737 | 738 | @Override 739 | public VertxCompletableFuture thenAcceptBoth(CompletionStage other, BiConsumer action) { 740 | return new VertxCompletableFuture<>(context, super.thenAcceptBoth(other, action)); 741 | } 742 | 743 | @Override 744 | public VertxCompletableFuture thenAcceptBothAsync(CompletionStage other, BiConsumer action, Executor executor) { 745 | return new VertxCompletableFuture<>(context, super.thenAcceptBothAsync(other, action, executor)); 746 | } 747 | 748 | @Override 749 | public VertxCompletableFuture runAfterBoth(CompletionStage other, Runnable action) { 750 | return new VertxCompletableFuture<>(context, super.runAfterBoth(other, action)); 751 | } 752 | 753 | @Override 754 | public VertxCompletableFuture runAfterBothAsync(CompletionStage other, Runnable action, Executor executor) { 755 | return new VertxCompletableFuture<>(context, super.runAfterBothAsync(other, action, executor)); 756 | } 757 | 758 | @Override 759 | public VertxCompletableFuture applyToEither(CompletionStage other, Function fn) { 760 | return new VertxCompletableFuture<>(context, super.applyToEither(other, fn)); 761 | } 762 | 763 | @Override 764 | public VertxCompletableFuture applyToEitherAsync(CompletionStage other, Function fn, Executor executor) { 765 | return new VertxCompletableFuture<>(context, super.applyToEitherAsync(other, fn, executor)); 766 | } 767 | 768 | @Override 769 | public VertxCompletableFuture acceptEither(CompletionStage other, Consumer action) { 770 | return new VertxCompletableFuture<>(context, super.acceptEither(other, action)); 771 | } 772 | 773 | @Override 774 | public VertxCompletableFuture acceptEitherAsync(CompletionStage other, Consumer action, Executor executor) { 775 | return new VertxCompletableFuture<>(context, super.acceptEitherAsync(other, action, executor)); 776 | } 777 | 778 | @Override 779 | public VertxCompletableFuture runAfterEither(CompletionStage other, Runnable action) { 780 | return new VertxCompletableFuture<>(context, super.runAfterEither(other, action)); 781 | } 782 | 783 | @Override 784 | public VertxCompletableFuture runAfterEitherAsync(CompletionStage other, Runnable action, Executor executor) { 785 | return new VertxCompletableFuture<>(context, super.runAfterEitherAsync(other, action, executor)); 786 | } 787 | 788 | @Override 789 | public VertxCompletableFuture thenCompose(Function> fn) { 790 | return new VertxCompletableFuture<>(context, super.thenCompose(fn)); 791 | } 792 | 793 | @Override 794 | public VertxCompletableFuture whenComplete(BiConsumer action) { 795 | return new VertxCompletableFuture<>(context, super.whenComplete(action)); 796 | } 797 | 798 | @Override 799 | public VertxCompletableFuture whenCompleteAsync(BiConsumer action, Executor executor) { 800 | return new VertxCompletableFuture<>(context, super.whenCompleteAsync(action, executor)); 801 | } 802 | 803 | @Override 804 | public VertxCompletableFuture handle(BiFunction fn) { 805 | return new VertxCompletableFuture<>(context, super.handle(fn)); 806 | } 807 | 808 | @Override 809 | public VertxCompletableFuture handleAsync(BiFunction fn, Executor executor) { 810 | return new VertxCompletableFuture<>(context, super.handleAsync(fn, executor)); 811 | } 812 | 813 | @Override 814 | public VertxCompletableFuture thenApplyAsync(Function fn) { 815 | return new VertxCompletableFuture<>(context, super.thenApplyAsync(fn, executor)); 816 | } 817 | 818 | @Override 819 | public VertxCompletableFuture thenAccept(Consumer action) { 820 | return new VertxCompletableFuture<>(context, super.thenAccept(action)); 821 | } 822 | 823 | @Override 824 | public VertxCompletableFuture thenAcceptAsync(Consumer action) { 825 | return new VertxCompletableFuture<>(context, super.thenAcceptAsync(action, executor)); 826 | } 827 | 828 | @Override 829 | public VertxCompletableFuture thenRunAsync(Runnable action) { 830 | return new VertxCompletableFuture<>(context, super.thenRunAsync(action, executor)); 831 | } 832 | 833 | @Override 834 | public VertxCompletableFuture thenCombineAsync(CompletionStage other, 835 | BiFunction fn) { 836 | return new VertxCompletableFuture<>(context, super.thenCombineAsync(other, fn, executor)); 837 | } 838 | 839 | @Override 840 | public VertxCompletableFuture thenAcceptBothAsync(CompletionStage other, 841 | BiConsumer action) { 842 | return new VertxCompletableFuture<>(context, super.thenAcceptBothAsync(other, action, executor)); 843 | } 844 | 845 | @Override 846 | public VertxCompletableFuture runAfterBothAsync(CompletionStage other, Runnable action) { 847 | return new VertxCompletableFuture<>(context, super.runAfterBothAsync(other, action, executor)); 848 | } 849 | 850 | 851 | @Override 852 | public VertxCompletableFuture applyToEitherAsync(CompletionStage other, Function fn) { 853 | return new VertxCompletableFuture<>(context, super.applyToEitherAsync(other, fn, executor)); 854 | } 855 | 856 | @Override 857 | public VertxCompletableFuture acceptEitherAsync(CompletionStage other, Consumer action) { 858 | return new VertxCompletableFuture<>(context, super.acceptEitherAsync(other, action, executor)); 859 | } 860 | 861 | 862 | @Override 863 | public VertxCompletableFuture runAfterEitherAsync(CompletionStage other, Runnable action) { 864 | return new VertxCompletableFuture<>(context, super.runAfterEitherAsync(other, action, executor)); 865 | } 866 | 867 | @Override 868 | public VertxCompletableFuture thenComposeAsync(Function> fn) { 869 | return new VertxCompletableFuture<>(context, super.thenComposeAsync(fn, executor)); 870 | } 871 | 872 | @Override 873 | public VertxCompletableFuture thenComposeAsync(Function> fn, Executor executor) { 874 | return new VertxCompletableFuture<>(context, super.thenComposeAsync(fn, executor)); 875 | } 876 | 877 | public VertxCompletableFuture thenCombineAsync( 878 | CompletionStage other, 879 | BiFunction fn, Executor executor) { 880 | return new VertxCompletableFuture<>(context, super.thenCombineAsync(other, fn, executor)); 881 | } 882 | 883 | @Override 884 | public VertxCompletableFuture whenCompleteAsync(BiConsumer action) { 885 | return new VertxCompletableFuture<>(context, super.whenCompleteAsync(action, executor)); 886 | } 887 | 888 | @Override 889 | public VertxCompletableFuture handleAsync(BiFunction fn) { 890 | return new VertxCompletableFuture<>(context, super.handleAsync(fn, executor)); 891 | } 892 | 893 | @Override 894 | public VertxCompletableFuture exceptionally(Function fn) { 895 | return new VertxCompletableFuture<>(context, super.exceptionally(fn)); 896 | } 897 | 898 | @Override 899 | public VertxCompletableFuture toCompletableFuture() { 900 | return this; 901 | } 902 | 903 | // ============= other instance methods ============= 904 | 905 | 906 | /** 907 | * Creates a new {@link Future} object completed / failed when the current {@link VertxCompletableFuture} is 908 | * completed successfully or not. 909 | * 910 | * @return the {@link Future}. 911 | */ 912 | public Future toFuture() { 913 | return VertxCompletableFuture.toFuture(this); 914 | } 915 | 916 | private void complete(T result, Throwable error) { 917 | if (error == null) { 918 | super.complete(result); 919 | } else { 920 | super.completeExceptionally(error); 921 | } 922 | } 923 | 924 | private void completeFromAsyncResult(AsyncResult ar) { 925 | if (ar.succeeded()) { 926 | super.complete(ar.result()); 927 | } else { 928 | super.completeExceptionally(ar.cause()); 929 | } 930 | } 931 | 932 | } 933 | -------------------------------------------------------------------------------- /src/test/java/me/escoffier/vertx/completablefuture/CompletableFutureOperatorTest.java: -------------------------------------------------------------------------------- 1 | package me.escoffier.vertx.completablefuture; 2 | 3 | import io.vertx.core.Vertx; 4 | import io.vertx.ext.unit.Async; 5 | import io.vertx.ext.unit.TestContext; 6 | import io.vertx.ext.unit.junit.VertxUnitRunner; 7 | import org.junit.After; 8 | import org.junit.Before; 9 | import org.junit.Test; 10 | import org.junit.runner.RunWith; 11 | 12 | import java.util.Arrays; 13 | import java.util.List; 14 | import java.util.concurrent.CompletableFuture; 15 | import java.util.concurrent.Executor; 16 | import java.util.concurrent.Executors; 17 | 18 | /** 19 | * @author Clement Escoffier 20 | */ 21 | @RunWith(VertxUnitRunner.class) 22 | public class CompletableFutureOperatorTest { 23 | 24 | private Vertx vertx; 25 | 26 | @Before 27 | public void setUp() { 28 | vertx = Vertx.vertx(); 29 | } 30 | 31 | @After 32 | public void tearDown() { 33 | vertx.close(); 34 | } 35 | 36 | @Test 37 | public void testAllOf(TestContext tc) { 38 | Async async1 = tc.async(); 39 | Async async2 = tc.async(); 40 | 41 | 42 | CompletableFuture one = new CompletableFuture<>(); 43 | CompletableFuture two = new VertxCompletableFuture<>(vertx); 44 | CompletableFuture three = new VertxCompletableFuture<>(vertx); 45 | CompletableFuture four = new CompletableFuture<>(); 46 | CompletableFuture failure = new CompletableFuture<>(); 47 | CompletableFuture failure2 = new CompletableFuture<>(); 48 | 49 | 50 | List> list = Arrays.asList(one, two, three, four); 51 | 52 | vertx.runOnContext(v -> { 53 | String thread = Thread.currentThread().getName(); 54 | VertxCompletableFuture.allOf(vertx, one, two, three, four).thenApply(x -> { 55 | tc.assertEquals(thread, Thread.currentThread().getName()); 56 | return list.stream() 57 | .map(CompletableFuture::join) 58 | .mapToInt(i -> i) 59 | .sum(); 60 | }).whenCompleteAsync((res, err) -> { 61 | tc.assertEquals(10, res); 62 | tc.assertNull(err); 63 | tc.assertEquals(thread, Thread.currentThread().getName()); 64 | async1.complete(); 65 | }); 66 | 67 | VertxCompletableFuture.allOf(vertx, one, two, failure, four, failure2).whenComplete((res, err) -> { 68 | tc.assertNotNull(err); 69 | tc.assertTrue(err.getMessage().contains("A")); 70 | tc.assertEquals(thread, Thread.currentThread().getName()); 71 | async2.complete(); 72 | }); 73 | }); 74 | 75 | one.complete(1); 76 | vertx.setTimer(100, l -> { 77 | two.complete(2); 78 | four.complete(4); 79 | failure.completeExceptionally(new Exception("A")); 80 | }); 81 | failure2.completeExceptionally(new Exception("B")); 82 | three.complete(3); 83 | 84 | } 85 | 86 | @Test 87 | public void testAnyOf(TestContext tc) { 88 | Async async1 = tc.async(); 89 | 90 | CompletableFuture one = new CompletableFuture<>(); 91 | CompletableFuture two = new VertxCompletableFuture<>(vertx); 92 | CompletableFuture three = new VertxCompletableFuture<>(vertx); 93 | CompletableFuture four = new CompletableFuture<>(); 94 | CompletableFuture failure = new CompletableFuture<>(); 95 | CompletableFuture failure2 = new CompletableFuture<>(); 96 | 97 | 98 | vertx.runOnContext(v -> { 99 | String thread = Thread.currentThread().getName(); 100 | VertxCompletableFuture.anyOf(vertx, one, failure, two, failure2, three, four).thenApply(x -> { 101 | tc.assertEquals(thread, Thread.currentThread().getName()); 102 | return (int) x * (int) x; 103 | }).whenCompleteAsync((res, err) -> { 104 | tc.assertNotNull(res); 105 | tc.assertNull(err); 106 | tc.assertEquals(thread, Thread.currentThread().getName()); 107 | async1.complete(); 108 | }); 109 | 110 | }); 111 | 112 | one.complete(1); 113 | vertx.setTimer(100, l -> { 114 | two.complete(2); 115 | four.complete(4); 116 | failure.completeExceptionally(new Exception("A")); 117 | }); 118 | failure2.completeExceptionally(new Exception("B")); 119 | three.complete(3); 120 | 121 | } 122 | 123 | 124 | 125 | } 126 | -------------------------------------------------------------------------------- /src/test/java/me/escoffier/vertx/completablefuture/SupplyAndRunAsyncTest.java: -------------------------------------------------------------------------------- 1 | package me.escoffier.vertx.completablefuture; 2 | 3 | import io.vertx.core.Vertx; 4 | import io.vertx.core.WorkerExecutor; 5 | import io.vertx.ext.unit.junit.VertxUnitRunner; 6 | import org.junit.After; 7 | import org.junit.Assert; 8 | import org.junit.Before; 9 | import org.junit.Test; 10 | import org.junit.runner.RunWith; 11 | 12 | import java.util.concurrent.CompletableFuture; 13 | import java.util.concurrent.ExecutionException; 14 | import java.util.concurrent.Executor; 15 | import java.util.concurrent.Executors; 16 | import java.util.concurrent.atomic.AtomicReference; 17 | 18 | @RunWith(VertxUnitRunner.class) 19 | public class SupplyAndRunAsyncTest { 20 | 21 | private Vertx vertx; 22 | private Executor executor; 23 | 24 | @Before 25 | public void setUp() { 26 | vertx = Vertx.vertx(); 27 | executor = Executors.newSingleThreadExecutor(); 28 | } 29 | 30 | @After 31 | public void tearDown() { 32 | vertx.close(); 33 | } 34 | 35 | @Test 36 | public void testSupplyAsync() throws ExecutionException, InterruptedException { 37 | CompletableFuture future = VertxCompletableFuture.supplyAsync(vertx, () -> Thread.currentThread().getName()); 38 | String s = future.get(); 39 | Assert.assertTrue(s.contains("eventloop")); 40 | } 41 | 42 | @Test 43 | public void testSupplyBlockingAsync() throws ExecutionException, InterruptedException { 44 | CompletableFuture future = VertxCompletableFuture.supplyBlockingAsync(vertx, () -> 45 | Thread.currentThread().getName()); 46 | String s = future.get(); 47 | Assert.assertTrue(s.contains("worker")); 48 | } 49 | 50 | @Test 51 | public void testSupplyAsyncWithExecutor() throws ExecutionException, InterruptedException { 52 | CompletableFuture future = VertxCompletableFuture.supplyAsync( 53 | () -> Thread.currentThread().getName(), 54 | executor); 55 | String s = future.get(); 56 | Assert.assertTrue(s.startsWith("pool")); 57 | } 58 | 59 | @Test 60 | public void testRunAsync() throws ExecutionException, InterruptedException { 61 | AtomicReference reference = new AtomicReference<>(); 62 | CompletableFuture future = VertxCompletableFuture.runAsync(vertx, () -> 63 | reference.set(Thread.currentThread().getName())); 64 | future.get(); 65 | Assert.assertTrue(reference.get().contains("eventloop")); 66 | } 67 | 68 | @Test 69 | public void testRunBlockingAsync() throws ExecutionException, InterruptedException { 70 | AtomicReference reference = new AtomicReference<>(); 71 | CompletableFuture future = VertxCompletableFuture.runBlockingAsync(vertx, () -> 72 | reference.set(Thread.currentThread().getName())); 73 | future.get(); 74 | Assert.assertTrue(reference.get().contains("worker")); 75 | } 76 | 77 | @Test 78 | public void testSupplyBlockingAsyncOn() throws ExecutionException, InterruptedException { 79 | String threadPoolName = "testing-thread-pool"; 80 | WorkerExecutor worker = vertx.createSharedWorkerExecutor(threadPoolName); 81 | CompletableFuture future = VertxCompletableFuture.supplyBlockingAsyncOn(vertx, worker, () -> 82 | Thread.currentThread().getName()); 83 | String s = future.get(); 84 | Assert.assertTrue(s.contains(threadPoolName)); 85 | } 86 | 87 | @Test 88 | public void testRunBlockingAsyncOn() throws ExecutionException, InterruptedException { 89 | String threadPoolName = "testing-thread-pool"; 90 | WorkerExecutor worker = vertx.createSharedWorkerExecutor(threadPoolName); 91 | AtomicReference reference = new AtomicReference<>(); 92 | CompletableFuture future = VertxCompletableFuture.runBlockingAsyncOn(vertx, worker, () -> 93 | reference.set(Thread.currentThread().getName())); 94 | future.get(); 95 | Assert.assertTrue(reference.get().contains(threadPoolName)); 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /src/test/java/me/escoffier/vertx/completablefuture/VertxCompletableFutureTest.java: -------------------------------------------------------------------------------- 1 | package me.escoffier.vertx.completablefuture; 2 | 3 | import com.google.common.util.concurrent.ThreadFactoryBuilder; 4 | import io.vertx.core.Context; 5 | import io.vertx.core.Future; 6 | import io.vertx.core.Vertx; 7 | import io.vertx.ext.unit.Async; 8 | import io.vertx.ext.unit.TestContext; 9 | import io.vertx.ext.unit.junit.Repeat; 10 | import io.vertx.ext.unit.junit.RepeatRule; 11 | import io.vertx.ext.unit.junit.VertxUnitRunner; 12 | import org.junit.*; 13 | import org.junit.runner.RunWith; 14 | 15 | import java.util.concurrent.*; 16 | 17 | import static org.hamcrest.CoreMatchers.*; 18 | import static org.junit.Assert.assertThat; 19 | 20 | @RunWith(VertxUnitRunner.class) 21 | public class VertxCompletableFutureTest { 22 | 23 | private Vertx vertx; 24 | private Executor executor; 25 | 26 | @Rule 27 | public RepeatRule rule = new RepeatRule(); 28 | 29 | @Before 30 | public void setUp(TestContext tc) { 31 | vertx = Vertx.vertx(); 32 | ThreadFactory namedThreadFactory = 33 | new ThreadFactoryBuilder().setNameFormat("my-sad-thread-%d").build(); 34 | executor = Executors.newSingleThreadExecutor(namedThreadFactory); 35 | vertx.exceptionHandler(tc.exceptionHandler()); 36 | } 37 | 38 | @After 39 | public void tearDown() { 40 | vertx.close(); 41 | } 42 | 43 | @Test 44 | public void testApply(TestContext tc) { 45 | Async async = tc.async(); 46 | 47 | VertxCompletableFuture future = new VertxCompletableFuture<>(vertx); 48 | future.complete(42); 49 | 50 | vertx.runOnContext(x -> { 51 | String thread = Thread.currentThread().getName(); 52 | 53 | future.thenApply(i -> { 54 | tc.assertEquals(thread, Thread.currentThread().getName()); 55 | tc.assertEquals(42, i); 56 | async.complete(); 57 | return null; 58 | }); 59 | }); 60 | 61 | } 62 | 63 | @Test 64 | public void testApplyAsync(TestContext tc) { 65 | Async async = tc.async(); 66 | 67 | vertx.runOnContext(x -> { 68 | VertxCompletableFuture future = new VertxCompletableFuture<>(vertx); 69 | future.complete(42); 70 | 71 | String thread = Thread.currentThread().getName(); 72 | 73 | future.thenApplyAsync(i -> { 74 | tc.assertEquals(thread, Thread.currentThread().getName()); 75 | tc.assertEquals(42, i); 76 | async.complete(); 77 | return null; 78 | }); 79 | }); 80 | 81 | } 82 | 83 | @Test 84 | public void testApplyAsyncWithExecutor(TestContext tc) { 85 | Async async = tc.async(); 86 | 87 | vertx.runOnContext(x -> { 88 | VertxCompletableFuture future = new VertxCompletableFuture<>(vertx); 89 | future.complete(42); 90 | 91 | String thread = Thread.currentThread().getName(); 92 | 93 | future.thenApplyAsync(i -> { 94 | tc.assertNotEquals(thread, Thread.currentThread().getName()); 95 | tc.assertEquals(42, i); 96 | async.complete(); 97 | return null; 98 | }, executor); 99 | }); 100 | 101 | } 102 | 103 | @Test 104 | public void testAccept(TestContext tc) { 105 | Async async = tc.async(); 106 | 107 | VertxCompletableFuture future = new VertxCompletableFuture<>(vertx); 108 | future.complete(42); 109 | 110 | vertx.runOnContext(x -> { 111 | String thread = Thread.currentThread().getName(); 112 | 113 | future.thenAccept(i -> { 114 | tc.assertEquals(thread, Thread.currentThread().getName()); 115 | tc.assertEquals(42, i); 116 | async.complete(); 117 | }); 118 | }); 119 | 120 | } 121 | 122 | @Test 123 | public void testAcceptAsync(TestContext tc) { 124 | Async async = tc.async(); 125 | 126 | vertx.runOnContext(x -> { 127 | VertxCompletableFuture future = new VertxCompletableFuture<>(vertx); 128 | future.complete(42); 129 | 130 | String thread = Thread.currentThread().getName(); 131 | 132 | future.thenAcceptAsync(i -> { 133 | tc.assertEquals(thread, Thread.currentThread().getName()); 134 | tc.assertEquals(42, i); 135 | async.complete(); 136 | }); 137 | }); 138 | 139 | } 140 | 141 | @Test 142 | public void testAcceptAsyncWithExecutor(TestContext tc) { 143 | Async async = tc.async(); 144 | 145 | vertx.runOnContext(x -> { 146 | VertxCompletableFuture future = new VertxCompletableFuture<>(vertx); 147 | future.complete(42); 148 | 149 | String thread = Thread.currentThread().getName(); 150 | 151 | future.thenAcceptAsync(i -> { 152 | tc.assertNotEquals(thread, Thread.currentThread().getName()); 153 | tc.assertEquals(42, i); 154 | async.complete(); 155 | }, executor); 156 | }); 157 | 158 | } 159 | 160 | @Test 161 | public void testRun(TestContext tc) { 162 | Async async = tc.async(); 163 | 164 | VertxCompletableFuture future = new VertxCompletableFuture<>(vertx); 165 | future.complete(42); 166 | 167 | vertx.runOnContext(x -> { 168 | String thread = Thread.currentThread().getName(); 169 | 170 | future.thenRun(() -> { 171 | tc.assertEquals(thread, Thread.currentThread().getName()); 172 | async.complete(); 173 | }); 174 | }); 175 | 176 | } 177 | 178 | @Test 179 | public void testRunAsync(TestContext tc) { 180 | Async async = tc.async(); 181 | 182 | vertx.runOnContext(x -> { 183 | VertxCompletableFuture future = new VertxCompletableFuture<>(vertx); 184 | future.complete(42); 185 | 186 | String thread = Thread.currentThread().getName(); 187 | 188 | future.thenRunAsync(() -> { 189 | tc.assertEquals(thread, Thread.currentThread().getName()); 190 | async.complete(); 191 | }); 192 | }); 193 | 194 | } 195 | 196 | @Test 197 | public void testRunAsyncWithExecutor(TestContext tc) { 198 | Async async = tc.async(); 199 | 200 | vertx.runOnContext(x -> { 201 | VertxCompletableFuture future = new VertxCompletableFuture<>(vertx); 202 | future.complete(42); 203 | 204 | String thread = Thread.currentThread().getName(); 205 | 206 | future.thenRunAsync(() -> { 207 | tc.assertNotEquals(thread, Thread.currentThread().getName()); 208 | async.complete(); 209 | }, executor); 210 | }); 211 | 212 | } 213 | 214 | @Test 215 | public void testRunAfterEither(TestContext tc) { 216 | // Success and success 217 | Async async1 = tc.async(); 218 | // Failure and success 219 | Async async2 = tc.async(); 220 | // Success and Failure 221 | Async async3 = tc.async(); 222 | // Failure and failure 223 | Async async4 = tc.async(); 224 | 225 | VertxCompletableFuture success1 = new VertxCompletableFuture<>(vertx); 226 | VertxCompletableFuture success2 = new VertxCompletableFuture<>(vertx); 227 | VertxCompletableFuture failure1 = new VertxCompletableFuture<>(vertx); 228 | VertxCompletableFuture failure2 = new VertxCompletableFuture<>(vertx); 229 | 230 | vertx.runOnContext(x -> { 231 | String thread = Thread.currentThread().getName(); 232 | 233 | success1.runAfterEither(success2, () -> { 234 | tc.assertEquals(thread, Thread.currentThread().getName()); 235 | async1.complete(); 236 | }); 237 | 238 | failure1.runAfterEither(success2, () -> { 239 | tc.assertEquals(thread, Thread.currentThread().getName()); 240 | async2.complete(); 241 | }); 242 | 243 | success1.runAfterEither(failure2, () -> { 244 | tc.assertEquals(thread, Thread.currentThread().getName()); 245 | async3.complete(); 246 | }); 247 | 248 | failure1.runAfterEither(failure2, () -> { 249 | tc.fail("Should not be called"); 250 | }).whenComplete((res, err) -> { 251 | tc.assertNotNull(err); 252 | tc.assertNull(res); 253 | tc.assertEquals(thread, Thread.currentThread().getName()); 254 | async4.complete(); 255 | }); 256 | 257 | success1.complete(42); 258 | failure2.completeExceptionally(new Exception("2")); 259 | vertx.setTimer(100, v -> { 260 | success2.complete(1); 261 | failure1.completeExceptionally(new Exception("1")); 262 | }); 263 | }); 264 | } 265 | 266 | @Test 267 | public void testRunAfterEitherAsync(TestContext tc) { 268 | // Success and success 269 | Async async1 = tc.async(); 270 | // Failure and success 271 | Async async2 = tc.async(); 272 | // Success and Failure 273 | Async async3 = tc.async(); 274 | // Failure and failure 275 | Async async4 = tc.async(); 276 | 277 | vertx.runOnContext(x -> { 278 | VertxCompletableFuture success1 = new VertxCompletableFuture<>(vertx); 279 | VertxCompletableFuture success2 = new VertxCompletableFuture<>(vertx); 280 | VertxCompletableFuture failure1 = new VertxCompletableFuture<>(vertx); 281 | VertxCompletableFuture failure2 = new VertxCompletableFuture<>(vertx); 282 | 283 | String thread = Thread.currentThread().getName(); 284 | 285 | success1.runAfterEitherAsync(success2, () -> { 286 | tc.assertEquals(thread, Thread.currentThread().getName()); 287 | async1.complete(); 288 | }); 289 | 290 | failure1 291 | .runAfterEitherAsync(success2, () -> tc.fail("Should not be called")) 292 | .whenComplete((res, err) -> { 293 | tc.assertNotNull(err); 294 | tc.assertNull(res); 295 | tc.assertEquals(thread, Thread.currentThread().getName()); 296 | async2.complete(); 297 | }); 298 | 299 | success1.runAfterEitherAsync(failure2, () -> { 300 | tc.assertEquals(thread, Thread.currentThread().getName()); 301 | async3.complete(); 302 | }); 303 | 304 | failure1.runAfterEitherAsync(failure2, () -> { 305 | tc.fail("Should not be called"); 306 | }).whenComplete((res, err) -> { 307 | tc.assertNotNull(err); 308 | tc.assertNull(res); 309 | tc.assertEquals(thread, Thread.currentThread().getName()); 310 | async4.complete(); 311 | }); 312 | 313 | success1.complete(42); 314 | failure2.completeExceptionally(new Exception("2")); 315 | vertx.setTimer(100, v -> { 316 | success2.complete(1); 317 | failure1.completeExceptionally(new Exception("1")); 318 | }); 319 | }); 320 | } 321 | 322 | @Test 323 | public void testRunAfterEitherAsyncWithExecutor(TestContext tc) { 324 | // Success and success 325 | Async async1 = tc.async(); 326 | // Failure and success 327 | Async async2 = tc.async(); 328 | // Success and Failure 329 | Async async3 = tc.async(); 330 | // Failure and failure 331 | Async async4 = tc.async(); 332 | 333 | VertxCompletableFuture success1 = new VertxCompletableFuture<>(vertx); 334 | VertxCompletableFuture success2 = new VertxCompletableFuture<>(vertx); 335 | VertxCompletableFuture failure1 = new VertxCompletableFuture<>(vertx); 336 | VertxCompletableFuture failure2 = new VertxCompletableFuture<>(vertx); 337 | 338 | vertx.runOnContext(x -> { 339 | String thread = Thread.currentThread().getName(); 340 | 341 | success1.runAfterEitherAsync(success2, () -> { 342 | tc.assertNotEquals(thread, Thread.currentThread().getName()); 343 | async1.complete(); 344 | }, executor); 345 | 346 | failure2 347 | .runAfterEitherAsync(success2, () -> tc.fail("Should not be called"), executor) 348 | .whenComplete((res, err) -> { 349 | tc.assertNotNull(err); 350 | tc.assertNull(res); 351 | tc.assertNotEquals(thread, Thread.currentThread().getName()); 352 | async2.complete(); 353 | }); 354 | 355 | success1.runAfterEitherAsync(failure2, () -> { 356 | tc.assertNotEquals(thread, Thread.currentThread().getName()); 357 | async3.complete(); 358 | }, executor); 359 | 360 | failure1.runAfterEitherAsync(failure2, () -> { 361 | tc.fail("Should not be called"); 362 | }, executor).whenComplete((res, err) -> { 363 | tc.assertNotNull(err); 364 | tc.assertNull(res); 365 | tc.assertNotEquals(thread, Thread.currentThread().getName()); 366 | async4.complete(); 367 | }); 368 | 369 | success1.complete(42); 370 | failure2.completeExceptionally(new Exception("2")); 371 | vertx.setTimer(100, v -> { 372 | success2.complete(1); 373 | failure1.completeExceptionally(new Exception("1")); 374 | }); 375 | }); 376 | } 377 | 378 | @Test 379 | public void testCombine(TestContext tc) { 380 | Async async = tc.async(); 381 | 382 | VertxCompletableFuture future1 = new VertxCompletableFuture<>(vertx); 383 | future1.complete(42); 384 | 385 | VertxCompletableFuture future2 = new VertxCompletableFuture<>(vertx); 386 | future2.complete(1); 387 | 388 | vertx.runOnContext(v -> { 389 | String thread = Thread.currentThread().getName(); 390 | 391 | future1.thenCombine(future2, (x, y) -> { 392 | tc.assertEquals(thread, Thread.currentThread().getName()); 393 | tc.assertEquals(42, x); 394 | tc.assertEquals(1, y); 395 | return x + y; 396 | }).thenAccept(i -> { 397 | tc.assertEquals(thread, Thread.currentThread().getName()); 398 | tc.assertEquals(i, 43); 399 | async.complete(); 400 | }); 401 | }); 402 | 403 | } 404 | 405 | @Test 406 | public void testCombineWithFailures(TestContext tc) { 407 | // Success and success 408 | Async async1 = tc.async(); 409 | // Failure and success 410 | Async async2 = tc.async(); 411 | // Success and Failure 412 | Async async3 = tc.async(); 413 | // Failure and failure 414 | Async async4 = tc.async(); 415 | 416 | VertxCompletableFuture success1 = new VertxCompletableFuture<>(vertx); 417 | VertxCompletableFuture success2 = new VertxCompletableFuture<>(vertx); 418 | VertxCompletableFuture failure1 = new VertxCompletableFuture<>(vertx); 419 | VertxCompletableFuture failure2 = new VertxCompletableFuture<>(vertx); 420 | 421 | vertx.runOnContext(v -> { 422 | String thread = Thread.currentThread().getName(); 423 | 424 | success1.thenCombine(success2, (x, y) -> { 425 | tc.assertEquals(thread, Thread.currentThread().getName()); 426 | tc.assertEquals(42, x); 427 | tc.assertEquals(1, y); 428 | return x + y; 429 | }).thenAccept(i -> { 430 | tc.assertEquals(thread, Thread.currentThread().getName()); 431 | tc.assertEquals(i, 43); 432 | async1.complete(); 433 | }); 434 | 435 | failure1.thenCombine(success1, (x, y) -> { 436 | tc.fail("Should not be called"); 437 | return null; 438 | }).whenComplete((res, err) -> { 439 | tc.assertEquals(thread, Thread.currentThread().getName()); 440 | tc.assertNotNull(err); 441 | tc.assertNull(res); 442 | async2.complete(); 443 | }); 444 | 445 | success1.thenCombine(failure2, (x, y) -> { 446 | tc.fail("Should not be called"); 447 | return null; 448 | }).whenComplete((res, err) -> { 449 | tc.assertEquals(thread, Thread.currentThread().getName()); 450 | tc.assertNotNull(err); 451 | tc.assertNull(res); 452 | async3.complete(); 453 | }); 454 | 455 | failure1.thenCombine(failure2, (x, y) -> { 456 | tc.fail("Should not be called"); 457 | return null; 458 | }).whenComplete((res, err) -> { 459 | tc.assertEquals(thread, Thread.currentThread().getName()); 460 | tc.assertNotNull(err); 461 | tc.assertNull(res); 462 | async4.complete(); 463 | }); 464 | 465 | success1.complete(42); 466 | success2.complete(1); 467 | failure1.completeExceptionally(new Exception("1")); 468 | failure2.completeExceptionally(new Exception("2")); 469 | }); 470 | 471 | } 472 | 473 | @Test 474 | public void testCombineAsync(TestContext tc) { 475 | Async async = tc.async(); 476 | 477 | VertxCompletableFuture future2 = new VertxCompletableFuture<>(vertx); 478 | future2.complete(1); 479 | 480 | vertx.runOnContext(v -> { 481 | String thread = Thread.currentThread().getName(); 482 | 483 | VertxCompletableFuture future1 = new VertxCompletableFuture<>(vertx); 484 | future1.complete(42); 485 | 486 | future1.thenCombineAsync(future2, (x, y) -> { 487 | tc.assertEquals(thread, Thread.currentThread().getName()); 488 | tc.assertEquals(42, x); 489 | tc.assertEquals(1, y); 490 | return x + y; 491 | }).thenAccept(i -> { 492 | tc.assertEquals(thread, Thread.currentThread().getName()); 493 | tc.assertEquals(i, 43); 494 | async.complete(); 495 | }); 496 | }); 497 | 498 | } 499 | 500 | @Test 501 | public void testCombineAsyncWithExecutor(TestContext tc) { 502 | Async async = tc.async(); 503 | 504 | VertxCompletableFuture future2 = new VertxCompletableFuture<>(vertx); 505 | future2.complete(1); 506 | 507 | vertx.runOnContext(v -> { 508 | String thread = Thread.currentThread().getName(); 509 | 510 | VertxCompletableFuture future1 = new VertxCompletableFuture<>(vertx); 511 | future1.complete(42); 512 | 513 | CompletableFuture test = future1.thenCombineAsync(future2, (x, y) -> { 514 | tc.assertNotEquals(thread, Thread.currentThread().getName()); 515 | tc.assertEquals(42, x); 516 | tc.assertEquals(1, y); 517 | return x + y; 518 | }, executor); 519 | System.out.println(test); 520 | test.thenAcceptAsync(i -> { 521 | tc.assertEquals(thread, Thread.currentThread().getName()); 522 | tc.assertEquals(i, 43); 523 | async.complete(); 524 | }); 525 | }); 526 | 527 | } 528 | 529 | @Test 530 | public void testExceptionally(TestContext tc) { 531 | Async async1 = tc.async(); 532 | Async async2 = tc.async(); 533 | 534 | VertxCompletableFuture failure = new VertxCompletableFuture<>(vertx); 535 | VertxCompletableFuture success = new VertxCompletableFuture<>(vertx); 536 | 537 | failure.exceptionally(t -> 43).thenAccept(i -> { 538 | tc.assertEquals(i, 43); 539 | async1.complete(); 540 | }); 541 | 542 | success.exceptionally(t -> 43).thenAccept(i -> { 543 | tc.assertEquals(i, 42); 544 | async2.complete(); 545 | }); 546 | 547 | success.complete(42); 548 | failure.completeExceptionally(new RuntimeException("my bad")); 549 | 550 | } 551 | 552 | @Test 553 | public void testHandle(TestContext tc) { 554 | Async async1 = tc.async(); 555 | Async async2 = tc.async(); 556 | 557 | VertxCompletableFuture failure = new VertxCompletableFuture<>(vertx); 558 | VertxCompletableFuture success = new VertxCompletableFuture<>(vertx); 559 | 560 | failure.handle((r, t) -> 43).thenAccept(i -> { 561 | tc.assertEquals(i, 43); 562 | async1.complete(); 563 | }); 564 | 565 | success.handle((r, t) -> r).thenAccept(i -> { 566 | tc.assertEquals(i, 42); 567 | async2.complete(); 568 | }); 569 | 570 | success.complete(42); 571 | failure.completeExceptionally(new RuntimeException("my bad")); 572 | 573 | } 574 | 575 | @Test 576 | public void testHandleAsync(TestContext tc) { 577 | Async async1 = tc.async(); 578 | Async async2 = tc.async(); 579 | 580 | VertxCompletableFuture failure = new VertxCompletableFuture<>(vertx); 581 | VertxCompletableFuture success = new VertxCompletableFuture<>(vertx); 582 | 583 | vertx.runOnContext(v -> { 584 | String thread = Thread.currentThread().getName(); 585 | Context context = Vertx.currentContext(); 586 | 587 | failure.withContext(context).handleAsync((r, t) -> { 588 | tc.assertEquals(thread, Thread.currentThread().getName()); 589 | if (t != null) { 590 | return 43; 591 | } 592 | return r; 593 | }).thenAccept(i -> { 594 | tc.assertEquals(i, 43); 595 | async1.complete(); 596 | }); 597 | 598 | success.withContext(context).handleAsync((r, t) -> { 599 | tc.assertEquals(Thread.currentThread().getName(), thread); 600 | if (t != null) { 601 | return 43; 602 | } 603 | return r; 604 | }).thenAccept(i -> { 605 | tc.assertEquals(i, 42); 606 | async2.complete(); 607 | }); 608 | }); 609 | success.complete(42); 610 | failure.completeExceptionally(new RuntimeException("my bad")); 611 | 612 | } 613 | 614 | @Test 615 | public void testHandleAsyncWithExecutor(TestContext tc) { 616 | Async async1 = tc.async(); 617 | Async async2 = tc.async(); 618 | 619 | VertxCompletableFuture failure = new VertxCompletableFuture<>(vertx); 620 | VertxCompletableFuture success = new VertxCompletableFuture<>(vertx); 621 | 622 | vertx.runOnContext(v -> { 623 | String thread = Thread.currentThread().getName(); 624 | Context context = Vertx.currentContext(); 625 | 626 | failure.withContext(context).handleAsync((r, t) -> { 627 | tc.assertNotEquals(Thread.currentThread().getName(), thread); 628 | if (t != null) { 629 | return 43; 630 | } 631 | return r; 632 | }, executor).thenAccept(i -> { 633 | tc.assertEquals(i, 43); 634 | async1.complete(); 635 | }); 636 | 637 | success.withContext(context).handleAsync((r, t) -> { 638 | tc.assertNotEquals(Thread.currentThread().getName(), thread); 639 | if (t != null) { 640 | return 43; 641 | } 642 | return r; 643 | }, executor).thenAccept(i -> { 644 | tc.assertEquals(i, 42); 645 | async2.complete(); 646 | }); 647 | }); 648 | success.complete(42); 649 | failure.completeExceptionally(new RuntimeException("my bad")); 650 | } 651 | 652 | @Test 653 | public void testAcceptBoth(TestContext tc) { 654 | Async async = tc.async(); 655 | 656 | VertxCompletableFuture future1 = new VertxCompletableFuture<>(vertx); 657 | future1.complete(42); 658 | 659 | VertxCompletableFuture future2 = new VertxCompletableFuture<>(vertx); 660 | future2.complete(1); 661 | 662 | vertx.runOnContext(v -> { 663 | String thread = Thread.currentThread().getName(); 664 | 665 | future1.thenAcceptBoth(future2, (x, y) -> { 666 | tc.assertEquals(thread, Thread.currentThread().getName()); 667 | tc.assertEquals(42, x); 668 | tc.assertEquals(1, y); 669 | async.complete(); 670 | }); 671 | }); 672 | 673 | } 674 | 675 | @Test 676 | public void testAcceptBothAsync(TestContext tc) { 677 | Async async = tc.async(); 678 | 679 | VertxCompletableFuture future2 = new VertxCompletableFuture<>(vertx); 680 | future2.complete(1); 681 | 682 | vertx.runOnContext(v -> { 683 | String thread = Thread.currentThread().getName(); 684 | 685 | VertxCompletableFuture future1 = new VertxCompletableFuture<>(vertx); 686 | future1.complete(42); 687 | 688 | future1.thenAcceptBothAsync(future2, (x, y) -> { 689 | tc.assertEquals(thread, Thread.currentThread().getName()); 690 | tc.assertEquals(42, x); 691 | tc.assertEquals(1, y); 692 | async.complete(); 693 | }); 694 | }); 695 | 696 | } 697 | 698 | @Test 699 | public void testAcceptBothAsyncWithExecutor(TestContext tc) { 700 | Async async = tc.async(); 701 | Async async2 = tc.async(); 702 | 703 | 704 | VertxCompletableFuture future2 = new VertxCompletableFuture<>(vertx); 705 | future2.complete(1); 706 | 707 | vertx.runOnContext(v -> { 708 | String thread = Thread.currentThread().getName(); 709 | 710 | VertxCompletableFuture future1 = new VertxCompletableFuture<>(vertx); 711 | vertx.setTimer(10, l -> future1.complete(42)); 712 | 713 | future1.thenAcceptBothAsync(future2, (x, y) -> { 714 | tc.assertNotEquals(thread, Thread.currentThread().getName()); 715 | tc.assertEquals(42, x); 716 | tc.assertEquals(1, y); 717 | async.complete(); 718 | }, executor); 719 | 720 | future2.thenAcceptBothAsync(future1, (x, y) -> { 721 | tc.assertNotEquals(thread, Thread.currentThread().getName()); 722 | tc.assertEquals(42, y); 723 | tc.assertEquals(1, x); 724 | async2.complete(); 725 | }, executor); 726 | }); 727 | 728 | } 729 | 730 | @Test 731 | public void testRunAfterBoth(TestContext tc) { 732 | Async async = tc.async(); 733 | 734 | VertxCompletableFuture future1 = new VertxCompletableFuture<>(vertx); 735 | future1.complete(42); 736 | 737 | VertxCompletableFuture future2 = new VertxCompletableFuture<>(vertx); 738 | future2.complete(1); 739 | 740 | vertx.runOnContext(v -> { 741 | String thread = Thread.currentThread().getName(); 742 | 743 | future1.runAfterBoth(future2, () -> { 744 | tc.assertEquals(thread, Thread.currentThread().getName()); 745 | async.complete(); 746 | }); 747 | }); 748 | 749 | } 750 | 751 | @Test 752 | public void testRunAfterBothAsync(TestContext tc) { 753 | Async async = tc.async(); 754 | 755 | VertxCompletableFuture future2 = new VertxCompletableFuture<>(vertx); 756 | future2.complete(1); 757 | 758 | vertx.runOnContext(v -> { 759 | String thread = Thread.currentThread().getName(); 760 | 761 | VertxCompletableFuture future1 = new VertxCompletableFuture<>(vertx); 762 | future1.complete(42); 763 | 764 | future1.runAfterBoth(future2, () -> { 765 | tc.assertEquals(thread, Thread.currentThread().getName()); 766 | async.complete(); 767 | }); 768 | }); 769 | 770 | } 771 | 772 | @Test 773 | public void testRunAfterBothAsyncWithExecutor(TestContext tc) { 774 | Async async = tc.async(); 775 | Async async2 = tc.async(); 776 | 777 | 778 | VertxCompletableFuture future2 = new VertxCompletableFuture<>(vertx); 779 | future2.complete(1); 780 | 781 | vertx.runOnContext(v -> { 782 | String thread = Thread.currentThread().getName(); 783 | 784 | VertxCompletableFuture future1 = new VertxCompletableFuture<>(vertx); 785 | vertx.setTimer(10, l -> future1.complete(42)); 786 | 787 | future1.runAfterBothAsync(future2, () -> { 788 | tc.assertNotEquals(thread, Thread.currentThread().getName()); 789 | async.complete(); 790 | }, executor); 791 | 792 | future2.runAfterBothAsync(future1, () -> { 793 | tc.assertNotEquals(thread, Thread.currentThread().getName()); 794 | async2.complete(); 795 | }, executor); 796 | }); 797 | 798 | } 799 | 800 | private CompletableFuture inc(int input) { 801 | VertxCompletableFuture future = new VertxCompletableFuture<>(vertx); 802 | vertx.setTimer(10, l -> future.complete(input + 1)); 803 | return future; 804 | } 805 | 806 | @Test 807 | public void testThenCompose(TestContext tc) { 808 | Async async = tc.async(); 809 | 810 | VertxCompletableFuture future1 = new VertxCompletableFuture<>(vertx); 811 | 812 | vertx.runOnContext(v -> { 813 | String thread = Thread.currentThread().getName(); 814 | 815 | future1.thenCompose(this::inc).thenCompose(this::inc).thenAccept(i -> { 816 | tc.assertEquals(thread, Thread.currentThread().getName()); 817 | tc.assertEquals(i, 44); 818 | async.complete(); 819 | }); 820 | 821 | future1.complete(42); 822 | }); 823 | } 824 | 825 | @Test 826 | public void testThenComposeAsync(TestContext tc) { 827 | Async async = tc.async(); 828 | 829 | VertxCompletableFuture future1 = new VertxCompletableFuture<>(vertx); 830 | 831 | vertx.runOnContext(v -> { 832 | String thread = Thread.currentThread().getName(); 833 | 834 | future1.withContext() 835 | .thenComposeAsync(this::inc) 836 | .thenComposeAsync(this::inc) 837 | .thenAccept(i -> { 838 | tc.assertEquals(thread, Thread.currentThread().getName()); 839 | tc.assertEquals(i, 44); 840 | async.complete(); 841 | }); 842 | 843 | future1.complete(42); 844 | }); 845 | } 846 | 847 | @Test 848 | public void testThenComposeAsyncWithExecutor(TestContext tc) { 849 | Async async = tc.async(); 850 | 851 | VertxCompletableFuture future1 = new VertxCompletableFuture<>(vertx); 852 | 853 | vertx.runOnContext(v -> { 854 | String thread = Thread.currentThread().getName(); 855 | 856 | future1.thenComposeAsync(this::inc, executor).thenCompose(this::inc).thenAccept(i -> { 857 | tc.assertNotEquals(thread, Thread.currentThread().getName()); 858 | tc.assertEquals(i, 44); 859 | async.complete(); 860 | }); 861 | 862 | future1.complete(42); 863 | }); 864 | } 865 | 866 | @Test 867 | public void testAcceptEither(TestContext tc) { 868 | // Success and success 869 | Async async1 = tc.async(); 870 | Async async11 = tc.async(); 871 | // Failure and Success 872 | Async async2 = tc.async(); 873 | // Success and Failure 874 | Async async3 = tc.async(); 875 | 876 | VertxCompletableFuture success = new VertxCompletableFuture<>(vertx); 877 | success.complete(1); 878 | 879 | VertxCompletableFuture success2 = new VertxCompletableFuture<>(vertx); 880 | vertx.setTimer(500, l -> success2.complete(42)); 881 | 882 | 883 | VertxCompletableFuture failure = new VertxCompletableFuture<>(vertx); 884 | failure.completeExceptionally(new RuntimeException("My bad")); 885 | 886 | vertx.runOnContext(v -> { 887 | String thread = Thread.currentThread().getName(); 888 | 889 | success.acceptEither( 890 | success2, 891 | i -> { 892 | tc.assertEquals(thread, Thread.currentThread().getName()); 893 | tc.assertEquals(i, 1); 894 | async1.complete(); 895 | } 896 | ); 897 | 898 | success2.acceptEither( 899 | success, 900 | i -> { 901 | tc.assertEquals(thread, Thread.currentThread().getName()); 902 | tc.assertEquals(i, 1); 903 | async11.complete(); 904 | } 905 | ); 906 | 907 | failure.acceptEither( 908 | success, 909 | i -> { 910 | tc.fail("Should not be called"); 911 | } 912 | ).whenComplete((res, err) -> { 913 | tc.assertNotNull(err); 914 | tc.assertNull(res); 915 | tc.assertEquals(thread, Thread.currentThread().getName()); 916 | async2.complete(); 917 | }); 918 | 919 | success.acceptEither( 920 | failure, 921 | i -> { 922 | tc.assertEquals(thread, Thread.currentThread().getName()); 923 | tc.assertEquals(i, 1); 924 | async3.complete(); 925 | } 926 | ); 927 | }); 928 | 929 | } 930 | 931 | @Test 932 | public void testAcceptEitherAsync(TestContext tc) { 933 | // Success and success 934 | Async async1 = tc.async(); 935 | Async async11 = tc.async(); 936 | // Failure and Success 937 | Async async2 = tc.async(); 938 | // Success and Failure 939 | Async async3 = tc.async(); 940 | 941 | VertxCompletableFuture success = new VertxCompletableFuture<>(vertx); 942 | success.complete(1); 943 | 944 | VertxCompletableFuture success2 = new VertxCompletableFuture<>(vertx); 945 | vertx.setTimer(100, l -> success2.complete(42)); 946 | 947 | 948 | VertxCompletableFuture failure = new VertxCompletableFuture<>(vertx); 949 | failure.completeExceptionally(new RuntimeException("My bad")); 950 | 951 | vertx.runOnContext(v -> { 952 | String thread = Thread.currentThread().getName(); 953 | 954 | success.withContext().acceptEitherAsync( 955 | success2, 956 | i -> { 957 | tc.assertEquals(thread, Thread.currentThread().getName()); 958 | tc.assertEquals(i, 1); 959 | async1.complete(); 960 | } 961 | ); 962 | 963 | success2.withContext().acceptEitherAsync( 964 | success, 965 | i -> { 966 | tc.assertEquals(thread, Thread.currentThread().getName()); 967 | tc.assertEquals(i, 1); 968 | async11.complete(); 969 | } 970 | ); 971 | 972 | failure.acceptEitherAsync( 973 | success, 974 | i -> tc.fail("Should not be called") 975 | ).whenComplete((res, err) -> { 976 | tc.assertNotNull(err); 977 | tc.assertNull(res); 978 | async2.complete(); 979 | }); 980 | 981 | success.withContext().acceptEitherAsync( 982 | failure, 983 | i -> { 984 | tc.assertEquals(thread, Thread.currentThread().getName()); 985 | tc.assertEquals(i, 1); 986 | async3.complete(); 987 | } 988 | ); 989 | }); 990 | 991 | } 992 | 993 | @Test 994 | @Repeat(50) 995 | public void testAcceptEitherAsyncWithExecutor(TestContext tc) { 996 | // Success and success 997 | Async async1 = tc.async(); 998 | Async async11 = tc.async(); 999 | // Failure and Success 1000 | Async async2 = tc.async(); 1001 | // Success and Failure 1002 | Async async3 = tc.async(); 1003 | 1004 | VertxCompletableFuture success = new VertxCompletableFuture<>(vertx); 1005 | success.complete(1); 1006 | 1007 | VertxCompletableFuture success2 = new VertxCompletableFuture<>(vertx); 1008 | vertx.setTimer(100, l -> success2.complete(42)); 1009 | 1010 | 1011 | VertxCompletableFuture failure = new VertxCompletableFuture<>(vertx); 1012 | failure.completeExceptionally(new RuntimeException("My bad")); 1013 | 1014 | vertx.runOnContext(v -> { 1015 | String thread = Thread.currentThread().getName(); 1016 | 1017 | success.acceptEitherAsync( 1018 | success2, 1019 | i -> { 1020 | tc.assertNotEquals(thread, Thread.currentThread().getName()); 1021 | tc.assertEquals(i, 1); 1022 | async1.complete(); 1023 | }, executor 1024 | ); 1025 | 1026 | success2.acceptEitherAsync( 1027 | success, 1028 | i -> { 1029 | tc.assertNotEquals(thread, Thread.currentThread().getName()); 1030 | tc.assertEquals(i, 1); 1031 | async11.complete(); 1032 | }, 1033 | executor 1034 | ); 1035 | 1036 | failure.acceptEitherAsync( 1037 | success, 1038 | i -> tc.fail("Should not be called"), 1039 | executor 1040 | ).whenComplete((res, err) -> { 1041 | tc.assertNotNull(err); 1042 | tc.assertNull(res); 1043 | // We can't enforce the thread used here - it's non-deterministic. 1044 | async2.complete(); 1045 | }); 1046 | 1047 | success.acceptEitherAsync( 1048 | failure, 1049 | i -> { 1050 | tc.assertNotEquals(thread, Thread.currentThread().getName()); 1051 | tc.assertEquals(i, 1); 1052 | async3.complete(); 1053 | }, 1054 | executor 1055 | ); 1056 | }); 1057 | 1058 | } 1059 | 1060 | @Test 1061 | public void testApplyToEither(TestContext tc) { 1062 | // Success and success 1063 | Async async1 = tc.async(); 1064 | Async async11 = tc.async(); 1065 | // Failure and Success 1066 | Async async2 = tc.async(); 1067 | // Success and Failure 1068 | Async async3 = tc.async(); 1069 | 1070 | VertxCompletableFuture success = new VertxCompletableFuture<>(vertx); 1071 | success.complete(1); 1072 | 1073 | VertxCompletableFuture success2 = new VertxCompletableFuture<>(vertx); 1074 | vertx.setTimer(100, l -> success2.complete(42)); 1075 | 1076 | 1077 | VertxCompletableFuture failure = new VertxCompletableFuture<>(vertx); 1078 | failure.completeExceptionally(new RuntimeException("My bad")); 1079 | 1080 | vertx.runOnContext(v -> { 1081 | String thread = Thread.currentThread().getName(); 1082 | 1083 | success.applyToEither( 1084 | success2, 1085 | i -> { 1086 | tc.assertEquals(thread, Thread.currentThread().getName()); 1087 | tc.assertEquals(i, 1); 1088 | return i + 1; 1089 | } 1090 | ).whenComplete((res, err) -> { 1091 | tc.assertNotNull(res); 1092 | tc.assertNull(err); 1093 | tc.assertEquals(res, 2); 1094 | async1.complete(); 1095 | }); 1096 | 1097 | success2.applyToEither( 1098 | success, 1099 | i -> { 1100 | tc.assertEquals(thread, Thread.currentThread().getName()); 1101 | tc.assertEquals(i, 1); 1102 | return i + 5; 1103 | } 1104 | ).whenComplete((res, err) -> { 1105 | tc.assertNotNull(res); 1106 | tc.assertNull(err); 1107 | tc.assertEquals(res, 6); 1108 | async11.complete(); 1109 | }); 1110 | 1111 | success.applyToEither( 1112 | failure, 1113 | i -> { 1114 | tc.assertEquals(thread, Thread.currentThread().getName()); 1115 | tc.assertEquals(i, 1); 1116 | return i * 2; 1117 | } 1118 | ).whenComplete((res, err) -> { 1119 | tc.assertNotNull(res); 1120 | tc.assertNull(err); 1121 | tc.assertEquals(res, 2); 1122 | async3.complete(); 1123 | }); 1124 | 1125 | // Fails, because the first one fails. 1126 | failure.applyToEither( 1127 | success, 1128 | i -> { 1129 | tc.fail("Should not be called"); 1130 | return null; 1131 | } 1132 | ).whenComplete((res, err) -> { 1133 | tc.assertNotNull(err); 1134 | tc.assertNull(res); 1135 | tc.assertEquals(thread, Thread.currentThread().getName()); 1136 | async2.complete(); 1137 | }); 1138 | }); 1139 | } 1140 | 1141 | @Test 1142 | public void testApplyEitherAsync(TestContext tc) { 1143 | // Success and success 1144 | Async async1 = tc.async(); 1145 | Async async11 = tc.async(); 1146 | // Failure and Success 1147 | Async async2 = tc.async(); 1148 | // Success and Failure 1149 | Async async3 = tc.async(); 1150 | 1151 | 1152 | vertx.runOnContext(v -> { 1153 | VertxCompletableFuture success = new VertxCompletableFuture<>(vertx); 1154 | success.complete(1); 1155 | 1156 | VertxCompletableFuture success2 = new VertxCompletableFuture<>(vertx); 1157 | vertx.setTimer(100, l -> success2.complete(42)); 1158 | 1159 | VertxCompletableFuture failure = new VertxCompletableFuture<>(vertx); 1160 | failure.completeExceptionally(new RuntimeException("My bad")); 1161 | String thread = Thread.currentThread().getName(); 1162 | 1163 | success.applyToEitherAsync( 1164 | success2, 1165 | i -> { 1166 | tc.assertEquals(thread, Thread.currentThread().getName()); 1167 | tc.assertEquals(i, 1); 1168 | return i + 1; 1169 | } 1170 | ).whenComplete((res, err) -> { 1171 | tc.assertNotNull(res); 1172 | tc.assertNull(err); 1173 | tc.assertEquals(res, 2); 1174 | async1.complete(); 1175 | }); 1176 | 1177 | success2.applyToEitherAsync( 1178 | success, 1179 | i -> { 1180 | tc.assertEquals(thread, Thread.currentThread().getName()); 1181 | tc.assertEquals(i, 1); 1182 | return i + 5; 1183 | } 1184 | ).whenComplete((res, err) -> { 1185 | tc.assertNotNull(res); 1186 | tc.assertNull(err); 1187 | tc.assertEquals(res, 6); 1188 | async11.complete(); 1189 | }); 1190 | 1191 | failure.applyToEitherAsync( 1192 | success, 1193 | i -> { 1194 | tc.fail("Should not be called"); 1195 | return null; 1196 | } 1197 | ).whenComplete((res, err) -> { 1198 | tc.assertNotNull(err); 1199 | tc.assertNull(res); 1200 | tc.assertEquals(thread, Thread.currentThread().getName()); 1201 | async2.complete(); 1202 | }); 1203 | 1204 | success.applyToEitherAsync( 1205 | failure, 1206 | i -> { 1207 | tc.assertEquals(thread, Thread.currentThread().getName()); 1208 | tc.assertEquals(i, 1); 1209 | return i * 2; 1210 | } 1211 | ).whenComplete((res, err) -> { 1212 | tc.assertNotNull(res); 1213 | tc.assertNull(err); 1214 | tc.assertEquals(res, 2); 1215 | async3.complete(); 1216 | }); 1217 | }); 1218 | 1219 | } 1220 | 1221 | @Test 1222 | @Repeat(50) 1223 | public void testApplyEitherAsyncWithExecutor(TestContext tc) { 1224 | // Success and success 1225 | Async async1 = tc.async(); 1226 | Async async11 = tc.async(); 1227 | // Failure and Success 1228 | Async async2 = tc.async(); 1229 | // Success and Failure 1230 | Async async3 = tc.async(); 1231 | 1232 | VertxCompletableFuture success = new VertxCompletableFuture<>(vertx); 1233 | vertx.setTimer(500, l -> success.complete(1)); 1234 | 1235 | VertxCompletableFuture success2 = new VertxCompletableFuture<>(vertx); 1236 | vertx.setTimer(1500, l -> success2.complete(42)); 1237 | 1238 | VertxCompletableFuture failure = new VertxCompletableFuture<>(vertx); 1239 | failure.completeExceptionally(new RuntimeException("My bad")); 1240 | 1241 | VertxCompletableFuture failure2 = new VertxCompletableFuture<>(vertx); 1242 | vertx.setTimer(1500, l -> failure.completeExceptionally(new RuntimeException("My bad"))); 1243 | 1244 | vertx.runOnContext(v -> { 1245 | String thread = Thread.currentThread().getName(); 1246 | 1247 | success.applyToEitherAsync( 1248 | success2, 1249 | i -> { 1250 | tc.assertNotEquals(thread, Thread.currentThread().getName()); 1251 | tc.assertEquals(i, 1); 1252 | return i + 1; 1253 | }, 1254 | executor 1255 | ).whenComplete((res, err) -> { 1256 | tc.assertNotNull(res); 1257 | tc.assertNull(err); 1258 | tc.assertEquals(res, 2); 1259 | async1.complete(); 1260 | }); 1261 | 1262 | success2.applyToEitherAsync( 1263 | success, 1264 | i -> { 1265 | tc.assertNotEquals(thread, Thread.currentThread().getName()); 1266 | tc.assertEquals(i, 1); 1267 | return i + 5; 1268 | }, 1269 | executor 1270 | ).whenComplete((res, err) -> { 1271 | tc.assertNotNull(res); 1272 | tc.assertNull(err); 1273 | tc.assertEquals(res, 6); 1274 | async11.complete(); 1275 | }); 1276 | 1277 | failure 1278 | .applyToEitherAsync( 1279 | success2, 1280 | i -> { 1281 | tc.fail("should not be called"); 1282 | return "not the right result"; 1283 | }, 1284 | executor 1285 | ) 1286 | .whenComplete((res, err) -> { 1287 | tc.assertNotNull(err); 1288 | tc.assertNull(res); 1289 | // We can't enforce the thread used here. 1290 | async2.complete(); 1291 | }); 1292 | 1293 | success.applyToEitherAsync( 1294 | failure2, 1295 | i -> { 1296 | tc.assertNotEquals(thread, Thread.currentThread().getName()); 1297 | tc.assertEquals(i, 1); 1298 | return i * 2; 1299 | }, 1300 | executor 1301 | ).whenComplete((res, err) -> { 1302 | tc.assertNotNull(res); 1303 | tc.assertNull(err); 1304 | tc.assertNotEquals(thread, Thread.currentThread().getName()); 1305 | tc.assertEquals(res, 2); 1306 | async3.complete(); 1307 | }); 1308 | }); 1309 | 1310 | } 1311 | 1312 | 1313 | @Test 1314 | public void testGetGetNowAndJoin(TestContext tc) throws ExecutionException, InterruptedException, 1315 | TimeoutException { 1316 | CompletableFuture success = new VertxCompletableFuture<>(vertx); 1317 | CompletableFuture failure = new VertxCompletableFuture<>(vertx); 1318 | 1319 | vertx.setTimer(1000, l -> { 1320 | success.complete(42); 1321 | failure.completeExceptionally(new Exception("My Bad !")); 1322 | }); 1323 | 1324 | try { 1325 | success.get(1, TimeUnit.MILLISECONDS); 1326 | tc.fail("timeout expected"); 1327 | } catch (TimeoutException e) { 1328 | // OK 1329 | } 1330 | 1331 | tc.assertEquals(22, success.getNow(22)); 1332 | 1333 | tc.assertEquals(success.get(), 42); 1334 | tc.assertEquals(success.join(), 42); 1335 | 1336 | try { 1337 | failure.get(); 1338 | tc.fail("Exception expected"); 1339 | } catch (ExecutionException e) { 1340 | // OK. 1341 | } 1342 | 1343 | try { 1344 | failure.join(); 1345 | tc.fail("Exception expected"); 1346 | } catch (CompletionException e) { 1347 | // OK 1348 | } 1349 | 1350 | tc.assertEquals(42, success.get(10, TimeUnit.SECONDS)); 1351 | 1352 | CompletableFuture immediate = new VertxCompletableFuture<>(vertx); 1353 | immediate.complete(55); 1354 | // Immediate get. 1355 | tc.assertEquals(immediate.get(), 55); 1356 | tc.assertEquals(immediate.join(), 55); 1357 | tc.assertEquals(immediate.getNow(23), 55); 1358 | tc.assertEquals(immediate.get(1, TimeUnit.SECONDS), 55); 1359 | 1360 | immediate.obtrudeValue(23); 1361 | tc.assertEquals(immediate.get(), 23); 1362 | tc.assertEquals(immediate.join(), 23); 1363 | tc.assertEquals(immediate.get(1, TimeUnit.SECONDS), 23); 1364 | 1365 | CompletableFuture cancelled = new VertxCompletableFuture<>(vertx); 1366 | cancelled.cancel(false); 1367 | 1368 | try { 1369 | cancelled.get(); 1370 | tc.fail("Exception expected"); 1371 | } catch (CancellationException e) { 1372 | // OK 1373 | } 1374 | 1375 | try { 1376 | cancelled.join(); 1377 | tc.fail("Exception expected"); 1378 | } catch (CancellationException e) { 1379 | // OK 1380 | } 1381 | 1382 | try { 1383 | cancelled.getNow(22); 1384 | tc.fail("Exception expected"); 1385 | } catch (CancellationException e) { 1386 | // OK 1387 | } 1388 | } 1389 | 1390 | 1391 | @Test 1392 | public void testTwoDependents(TestContext tc) { 1393 | Async async1 = tc.async(); 1394 | Async async2 = tc.async(); 1395 | 1396 | vertx.runOnContext(v -> { 1397 | VertxCompletableFuture future = new VertxCompletableFuture<>(vertx); 1398 | future.complete(42); 1399 | 1400 | future.thenApplyAsync(i -> { 1401 | tc.assertEquals(42, i); 1402 | return i + 1; 1403 | }).thenAcceptAsync(i -> { 1404 | tc.assertEquals(43, i); 1405 | async1.complete(); 1406 | }); 1407 | 1408 | future.thenAccept(i -> { 1409 | tc.assertEquals(42, i); 1410 | async2.complete(); 1411 | }); 1412 | }); 1413 | } 1414 | 1415 | private CompletableFuture display(String s) { 1416 | CompletableFuture future = new VertxCompletableFuture<>(vertx); 1417 | future.complete("Result: " + s); 1418 | return future; 1419 | } 1420 | 1421 | @Test 1422 | public void testMoreComplexComposition() throws ExecutionException, InterruptedException { 1423 | CompletableFuture first = new VertxCompletableFuture<>(vertx); 1424 | CompletableFuture second = first.thenApply(x -> x + 1); 1425 | CompletableFuture toString = second.thenApplyAsync(i -> Integer.toString(i)); 1426 | CompletableFuture displayed = toString.thenCompose(this::display); 1427 | 1428 | vertx.setTimer(100, l -> first.complete(42)); 1429 | 1430 | Assert.assertEquals("Result: " + 43, displayed.join()); 1431 | Assert.assertTrue(first.get() == 42); 1432 | Assert.assertTrue(second.get() == 43); 1433 | Assert.assertEquals(toString.get(), "43"); 1434 | Assert.assertTrue(displayed.isDone()); 1435 | } 1436 | 1437 | @Test 1438 | public void testCancellationOnDependents() { 1439 | CompletableFuture first = new VertxCompletableFuture<>(vertx); 1440 | CompletableFuture second = first.thenApply(x -> x + 1); 1441 | CompletableFuture toString = second.thenApplyAsync(i -> Integer.toString(i)); 1442 | CompletableFuture displayed = toString.thenCompose(this::display); 1443 | 1444 | vertx.setTimer(1000, l -> first.complete(42)); 1445 | 1446 | first.cancel(true); 1447 | 1448 | Assert.assertTrue(first.isCancelled()); 1449 | Assert.assertTrue(second.isCompletedExceptionally()); 1450 | Assert.assertTrue(toString.isCompletedExceptionally()); 1451 | Assert.assertTrue(displayed.isCompletedExceptionally()); 1452 | } 1453 | 1454 | 1455 | @Test 1456 | public void testWhenComplete(TestContext tc) { 1457 | Async async1 = tc.async(); 1458 | Async async2 = tc.async(); 1459 | 1460 | CompletableFuture first = new VertxCompletableFuture<>(vertx); 1461 | first 1462 | .thenApply(x -> x + 1) 1463 | .thenApplyAsync(i -> Integer.toString(i)) 1464 | .thenCompose(this::display) 1465 | .whenComplete((res, err) -> { 1466 | tc.assertNull(err); 1467 | tc.assertEquals(res, "Result: " + 43); 1468 | async1.complete(); 1469 | }); 1470 | 1471 | first.complete(42); 1472 | 1473 | VertxCompletableFuture second = new VertxCompletableFuture<>(vertx); 1474 | second 1475 | .thenApply(x -> x + 1) 1476 | .thenApplyAsync(i -> Integer.toString(i)) 1477 | .thenCompose(this::display) 1478 | .whenComplete((res, err) -> { 1479 | tc.assertNull(res); 1480 | tc.assertNotNull(err); 1481 | async2.complete(); 1482 | }); 1483 | 1484 | second.completeExceptionally(new Exception("My bad")); 1485 | 1486 | } 1487 | 1488 | @Test 1489 | public void testWhenCompleteAsync(TestContext tc) { 1490 | Async async1 = tc.async(); 1491 | Async async2 = tc.async(); 1492 | 1493 | VertxCompletableFuture first = new VertxCompletableFuture<>(vertx); 1494 | first 1495 | .thenApply(x -> x + 1) 1496 | .thenApplyAsync(i -> Integer.toString(i)) 1497 | .thenCompose(this::display) 1498 | .whenCompleteAsync((res, err) -> { 1499 | tc.assertTrue(Context.isOnEventLoopThread()); 1500 | tc.assertNull(err); 1501 | tc.assertEquals(res, "Result: " + 43); 1502 | async1.complete(); 1503 | }); 1504 | 1505 | first.complete(42); 1506 | 1507 | VertxCompletableFuture second = new VertxCompletableFuture<>(vertx); 1508 | second 1509 | .thenApply(x -> x + 1) 1510 | .thenApplyAsync(i -> Integer.toString(i)) 1511 | .thenCompose(this::display) 1512 | .whenCompleteAsync((res, err) -> { 1513 | tc.assertTrue(Context.isOnEventLoopThread()); 1514 | tc.assertNull(res); 1515 | tc.assertNotNull(err); 1516 | async2.complete(); 1517 | }); 1518 | 1519 | second.completeExceptionally(new Exception("My bad")); 1520 | } 1521 | 1522 | @Test 1523 | public void testWhenCompleteAsyncWithExecutor(TestContext tc) { 1524 | Async async1 = tc.async(); 1525 | Async async2 = tc.async(); 1526 | 1527 | VertxCompletableFuture first = new VertxCompletableFuture<>(vertx); 1528 | first 1529 | .thenApply(x -> x + 1) 1530 | .thenApplyAsync(i -> Integer.toString(i)) 1531 | .thenCompose(this::display) 1532 | .whenCompleteAsync((res, err) -> { 1533 | tc.assertFalse(Context.isOnEventLoopThread()); 1534 | tc.assertNull(err); 1535 | tc.assertEquals(res, "Result: " + 43); 1536 | async1.complete(); 1537 | }, executor); 1538 | 1539 | first.complete(42); 1540 | 1541 | VertxCompletableFuture second = new VertxCompletableFuture<>(vertx); 1542 | second 1543 | .thenApply(x -> x + 1) 1544 | .thenApplyAsync(i -> Integer.toString(i)) 1545 | .thenCompose(this::display) 1546 | .whenCompleteAsync((res, err) -> { 1547 | tc.assertFalse(Context.isOnEventLoopThread()); 1548 | tc.assertNull(res); 1549 | tc.assertNotNull(err); 1550 | async2.complete(); 1551 | }, executor); 1552 | 1553 | second.completeExceptionally(new Exception("My bad")); 1554 | } 1555 | 1556 | @Test 1557 | public void testAcceptFailurePropagation(TestContext tc) { 1558 | Async async0 = tc.async(); 1559 | Async async1 = tc.async(); 1560 | Async async2 = tc.async(); 1561 | CompletableFuture failure1 = new VertxCompletableFuture<>(vertx); 1562 | CompletableFuture failure2 = new VertxCompletableFuture<>(vertx); 1563 | 1564 | failure1.thenAccept(i -> tc.fail("Should not be called")) 1565 | .whenComplete((res, err) -> { 1566 | tc.assertEquals(err.getClass(), CompletionException.class); 1567 | tc.assertTrue(err.getMessage().contains("A")); 1568 | async0.complete(); 1569 | }); 1570 | 1571 | failure1.thenAcceptBoth(failure2, (a, b) -> tc.fail("Should not be called")) 1572 | .whenComplete((res, err) -> { 1573 | tc.assertEquals(err.getClass(), CompletionException.class); 1574 | tc.assertTrue(err.getMessage().contains("A")); 1575 | async2.complete(); 1576 | }); 1577 | 1578 | failure1.acceptEither(failure2, i -> tc.fail("Should not be called")) 1579 | .whenComplete((res, err) -> { 1580 | tc.assertEquals(err.getClass(), CompletionException.class); 1581 | tc.assertTrue(err.getMessage().contains("B")); 1582 | async1.complete(); 1583 | }); 1584 | 1585 | failure2.completeExceptionally(new RuntimeException("B")); 1586 | failure1.completeExceptionally(new RuntimeException("A")); 1587 | } 1588 | 1589 | @Test 1590 | public void testApplyFailurePropagation(TestContext tc) { 1591 | Async async0 = tc.async(); 1592 | Async async1 = tc.async(); 1593 | CompletableFuture failure1 = new VertxCompletableFuture<>(vertx); 1594 | CompletableFuture failure2 = new VertxCompletableFuture<>(vertx); 1595 | 1596 | failure1.thenApply(i -> { 1597 | tc.fail("Should not be called"); 1598 | return 0; 1599 | }).whenComplete((res, err) -> { 1600 | tc.assertEquals(err.getClass(), CompletionException.class); 1601 | tc.assertTrue(err.getMessage().contains("A")); 1602 | async0.complete(); 1603 | }); 1604 | 1605 | failure1.applyToEither(failure2, i -> { 1606 | tc.fail("Should not be called"); 1607 | return 0; 1608 | }).whenComplete((res, err) -> { 1609 | tc.assertEquals(err.getClass(), CompletionException.class); 1610 | tc.assertTrue(err.getMessage().contains("B")); 1611 | async1.complete(); 1612 | }); 1613 | 1614 | failure2.completeExceptionally(new RuntimeException("B")); 1615 | failure1.completeExceptionally(new RuntimeException("A")); 1616 | 1617 | } 1618 | 1619 | @Test 1620 | public void testCombineFailurePropagation(TestContext tc) { 1621 | Async async0 = tc.async(); 1622 | Async async1 = tc.async(); 1623 | CompletableFuture failure1 = new VertxCompletableFuture<>(vertx); 1624 | CompletableFuture failure2 = new VertxCompletableFuture<>(vertx); 1625 | 1626 | VertxCompletableFuture future1 = new VertxCompletableFuture<>(vertx); 1627 | future1.complete(42); 1628 | 1629 | VertxCompletableFuture future2 = new VertxCompletableFuture<>(vertx); 1630 | future2.complete(1); 1631 | 1632 | 1633 | failure1.thenCombine(failure2, (a, b) -> { 1634 | tc.fail("Should not be called"); 1635 | return 0; 1636 | }).whenComplete((res, err) -> { 1637 | tc.assertEquals(err.getClass(), CompletionException.class); 1638 | tc.assertTrue(err.getMessage().contains("A")); 1639 | async0.complete(); 1640 | }); 1641 | 1642 | failure1.thenCombine(failure2, (a, b) -> { 1643 | tc.fail("Should not be called"); 1644 | return 0; 1645 | }).whenComplete((res, err) -> { 1646 | tc.assertEquals(err.getClass(), CompletionException.class); 1647 | // Here we are not mimicking CompletableFuture behavior. We return the failure1 cause instead of the first 1648 | // one that has arrived. For pure completable future is would be: 1649 | //tc.assertTrue(err.getMessage().contains("B")); 1650 | // For us it's 1651 | tc.assertTrue(err.getMessage().contains("A")); 1652 | async1.complete(); 1653 | }); 1654 | 1655 | failure2.completeExceptionally(new RuntimeException("B")); 1656 | failure1.completeExceptionally(new RuntimeException("A")); 1657 | } 1658 | 1659 | private Integer square(int x) { 1660 | return x * x; 1661 | } 1662 | 1663 | @Test 1664 | public void testExceptionallyPropagation(TestContext tc) { 1665 | Async async1 = tc.async(); 1666 | Async async2 = tc.async(); 1667 | CompletableFuture future = new VertxCompletableFuture<>(vertx); 1668 | future.thenApply(this::square) 1669 | .exceptionally(t -> 22) 1670 | .whenComplete((res, err) -> { 1671 | tc.assertEquals(res, 16); 1672 | async1.complete(); 1673 | }) 1674 | .thenAccept(System.out::print) 1675 | .thenRun(System.out::println); 1676 | future.complete(4); 1677 | 1678 | future = new VertxCompletableFuture<>(vertx); 1679 | future.thenApply(this::square).thenApply(i -> { 1680 | throw new RuntimeException("My Bad"); 1681 | }) 1682 | .exceptionally(t -> 22) 1683 | .whenComplete((res, err) -> { 1684 | tc.assertEquals(res, 22); 1685 | async2.complete(); 1686 | }) 1687 | .thenAccept(System.out::print) 1688 | .thenRun(System.out::println); 1689 | future.complete(4); 1690 | } 1691 | 1692 | @Test 1693 | public void testRunFailurePropagation(TestContext tc) { 1694 | Async async0 = tc.async(); 1695 | Async async1 = tc.async(); 1696 | Async async2 = tc.async(); 1697 | CompletableFuture failure1 = new VertxCompletableFuture<>(vertx); 1698 | CompletableFuture failure2 = new VertxCompletableFuture<>(vertx); 1699 | 1700 | failure1.thenRun(() -> tc.fail("Should not be called")) 1701 | .whenComplete((res, err) -> { 1702 | tc.assertEquals(err.getClass(), CompletionException.class); 1703 | tc.assertTrue(err.getMessage().contains("A")); 1704 | async0.complete(); 1705 | }); 1706 | 1707 | failure1.runAfterBoth(failure2, () -> tc.fail("Should not be called")) 1708 | .whenComplete((res, err) -> { 1709 | tc.assertEquals(err.getClass(), CompletionException.class); 1710 | tc.assertTrue(err.getMessage().contains("A")); 1711 | async2.complete(); 1712 | }); 1713 | 1714 | failure1.runAfterEither(failure2, () -> tc.fail("Should not be called")) 1715 | .whenComplete((res, err) -> { 1716 | tc.assertEquals(err.getClass(), CompletionException.class); 1717 | tc.assertTrue(err.getMessage().contains("B")); 1718 | async1.complete(); 1719 | }); 1720 | 1721 | failure2.completeExceptionally(new RuntimeException("B")); 1722 | failure1.completeExceptionally(new RuntimeException("A")); 1723 | } 1724 | 1725 | 1726 | @Test 1727 | public void testFromCompletableStageJavadoc() { 1728 | VertxCompletableFuture future = new VertxCompletableFuture<>(vertx); 1729 | future 1730 | .thenApply(this::square) 1731 | .thenAccept(System.out::print) 1732 | .thenRun(System.out::println); 1733 | 1734 | future.complete(42); 1735 | 1736 | } 1737 | 1738 | @Test 1739 | public void testWithContext(TestContext tc) { 1740 | Async async1 = tc.async(); 1741 | Async async2 = tc.async(); 1742 | VertxCompletableFuture future = new VertxCompletableFuture<>(vertx); 1743 | assertThat(future.context(), is(not(vertx.getOrCreateContext()))); 1744 | vertx.runOnContext(v -> { 1745 | assertThat(future.withContext().context(), is(vertx.getOrCreateContext())); 1746 | async1.complete(); 1747 | }); 1748 | 1749 | vertx.executeBlocking( 1750 | fut -> { 1751 | assertThat(future.withContext().context(), is(Vertx.currentContext())); 1752 | async2.complete(); 1753 | }, 1754 | null 1755 | ); 1756 | } 1757 | 1758 | @Test 1759 | public void testToVertxFuture(TestContext tc) { 1760 | Async async1 = tc.async(); 1761 | Async async2 = tc.async(); 1762 | Async async3 = tc.async(); 1763 | Async async4 = tc.async(); 1764 | Async async5 = tc.async(); 1765 | 1766 | VertxCompletableFuture future = new VertxCompletableFuture<>(vertx); 1767 | Future success = future.thenApply(i -> i + 1).thenApplyAsync(i -> i + 1).toFuture(); 1768 | Future success2 = future.thenApply(i -> i + 1).thenApplyAsync(i -> i + 1).thenRunAsync(() -> { 1769 | // Do nothing 1770 | }).toFuture(); 1771 | success.setHandler(ar -> { 1772 | assertThat(ar.failed(), is(false)); 1773 | assertThat(ar.succeeded(), is(true)); 1774 | assertThat(ar.result(), is(44)); 1775 | async1.complete(); 1776 | }); 1777 | success2.setHandler(ar -> { 1778 | assertThat(ar.failed(), is(false)); 1779 | assertThat(ar.succeeded(), is(true)); 1780 | assertThat(ar.result(), is(nullValue())); 1781 | async2.complete(); 1782 | }); 1783 | 1784 | VertxCompletableFuture failingFuture = new VertxCompletableFuture<>(vertx); 1785 | Future failure = failingFuture.thenApply(i -> i + 1).thenApplyAsync(i -> i + i + 1).toFuture(); 1786 | failure.setHandler(ar -> { 1787 | assertThat(ar.failed(), is(true)); 1788 | assertThat(ar.succeeded(), is(false)); 1789 | assertThat(ar.result(), is(nullValue())); 1790 | assertThat(ar.cause(), is(notNullValue())); 1791 | assertThat(ar.cause().getMessage(), containsString("My bad")); 1792 | async3.complete(); 1793 | }); 1794 | 1795 | // test that `VertxCompletableFuture` receives callbacks from the Vert.x Future 1796 | VertxCompletableFuture awaitedFuture = new VertxCompletableFuture<>(vertx); 1797 | 1798 | awaitedFuture.handle((val, except) -> { 1799 | assertThat(val, is(42)); 1800 | assertThat(except, is(nullValue())); 1801 | async4.complete(); 1802 | 1803 | return (Void) null; 1804 | }); 1805 | 1806 | VertxCompletableFuture awaitedFailingFuture = new VertxCompletableFuture<>(vertx); 1807 | 1808 | awaitedFailingFuture.handle((val, except) -> { 1809 | assertThat(val, is(nullValue())); 1810 | assertThat(except, is(notNullValue())); 1811 | assertThat(except.getMessage(), containsString("My bad again")); 1812 | async5.complete(); 1813 | 1814 | return (Void) null; 1815 | }); 1816 | 1817 | future.complete(42); 1818 | failingFuture.completeExceptionally(new Exception("My bad")); 1819 | awaitedFuture.toFuture().complete(42); 1820 | awaitedFailingFuture.completeExceptionally(new Exception("My bad again")); 1821 | } 1822 | 1823 | @Test 1824 | public void testFromVertxFuture(TestContext tc) { 1825 | Async async1 = tc.async(); 1826 | Async async2 = tc.async(); 1827 | 1828 | Future vertxFuture1 = Future.future(); 1829 | Future vertxFuture2 = Future.future(); 1830 | VertxCompletableFuture.from(vertx, vertxFuture1).thenApply(i -> i + 1).whenComplete((res, err) -> { 1831 | tc.assertNotNull(res); 1832 | tc.assertNull(err); 1833 | tc.assertEquals(43, res); 1834 | async1.complete(); 1835 | }); 1836 | 1837 | VertxCompletableFuture.from(vertx, vertxFuture2).thenApply(i -> i + 1).whenComplete((res, err) -> { 1838 | tc.assertNotNull(err); 1839 | tc.assertNull(res); 1840 | tc.assertTrue(err.getMessage().contains("My bad")); 1841 | async2.complete(); 1842 | }); 1843 | 1844 | vertxFuture1.complete(42); 1845 | vertxFuture2.fail(new Exception("My bad")); 1846 | } 1847 | 1848 | } 1849 | -------------------------------------------------------------------------------- /src/test/java/me/escoffier/vertx/completablefuture/examples/HttpClientTest.java: -------------------------------------------------------------------------------- 1 | package me.escoffier.vertx.completablefuture.examples; 2 | 3 | import io.vertx.core.Vertx; 4 | import io.vertx.core.http.HttpClient; 5 | import io.vertx.core.http.HttpClientOptions; 6 | import io.vertx.ext.unit.Async; 7 | import io.vertx.ext.unit.TestContext; 8 | import io.vertx.ext.unit.junit.VertxUnitRunner; 9 | import me.escoffier.vertx.completablefuture.VertxCompletableFuture; 10 | import org.junit.After; 11 | import org.junit.Before; 12 | import org.junit.Test; 13 | import org.junit.runner.RunWith; 14 | 15 | /** 16 | * @author Clement Escoffier 17 | */ 18 | @RunWith(VertxUnitRunner.class) 19 | public class HttpClientTest { 20 | 21 | private Vertx vertx; 22 | 23 | @Before 24 | public void setUp(TestContext tc) { 25 | vertx = Vertx.vertx(); 26 | 27 | vertx.createHttpServer().requestHandler(request -> { 28 | switch (request.path()) { 29 | case "/A": 30 | request.response().end("42"); 31 | break; 32 | case "/B": 33 | request.response().end("23"); 34 | break; 35 | default: 36 | request.response().end("Hello"); 37 | } 38 | } 39 | ).listen(8080, tc.asyncAssertSuccess()); 40 | } 41 | 42 | @After 43 | public void tearDown(TestContext tc) { 44 | vertx.close(tc.asyncAssertSuccess()); 45 | } 46 | 47 | @Test 48 | public void test(TestContext tc) { 49 | Async async = tc.async(); 50 | 51 | HttpClientOptions options = new HttpClientOptions().setDefaultPort(8080).setDefaultHost("localhost"); 52 | HttpClient client1 = vertx.createHttpClient(options); 53 | HttpClient client2 = vertx.createHttpClient(options); 54 | 55 | VertxCompletableFuture requestA = new VertxCompletableFuture<>(vertx); 56 | client1.get("/A").handler(resp -> { 57 | resp.exceptionHandler(requestA::completeExceptionally) 58 | .bodyHandler(buffer -> { 59 | requestA.complete(Integer.parseInt(buffer.toString())); 60 | }); 61 | }).exceptionHandler(requestA::completeExceptionally).end(); 62 | 63 | VertxCompletableFuture requestB = new VertxCompletableFuture<>(vertx); 64 | client2.get("/B").handler(resp -> { 65 | resp.exceptionHandler(requestB::completeExceptionally) 66 | .bodyHandler(buffer -> { 67 | requestB.complete(Integer.parseInt(buffer.toString())); 68 | }); 69 | }).exceptionHandler(requestB::completeExceptionally).end(); 70 | 71 | 72 | VertxCompletableFuture.allOf(requestA, requestB).thenApply(v -> requestA.join() + requestB.join()) 73 | .thenAccept(i -> { 74 | tc.assertEquals(65, i); 75 | async.complete(); 76 | }); 77 | } 78 | 79 | } 80 | --------------------------------------------------------------------------------