├── .gitignore ├── .travis.yml ├── LICENSE.txt ├── README.md ├── completion-stage.iml ├── header.txt ├── pom.xml └── src ├── main └── java │ └── net │ └── javacrumbs │ └── completionstage │ ├── CallbackRegistry.java │ ├── CompletableCompletionStage.java │ ├── CompletionStageAdapter.java │ ├── CompletionStageFactory.java │ ├── SimpleCompletionStage.java │ └── spi │ └── CompletableCompletionStageFactory.java └── test └── java └── net └── javacrumbs └── completionstage ├── AbstractCompletionStageTest.java ├── AbstractUnfinishedCompletionStageTest.java ├── CompletionStageFactoryTest.java ├── FinishedCompletableFutureTest.java ├── FinishedCompletionStageFactoryTest.java ├── SimpleCompletionStageTest.java ├── UnfinishedCompletableFutureTest.java ├── UnfinishedCompletionStageFactoryTest.java ├── completecompleted ├── AbstractCanYouCompleteAlreadyCompletedTest.java ├── CompletableFutureCanYouCompleteCompletedTest.java └── CompletionStageCanYouCompleteCompletedTest.java ├── examples ├── CompletableFutureComposeTest.java ├── LaunchTest.java └── SpringListenableFutureTest.java └── load └── LoadTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Package Files # 4 | *.jar 5 | *.war 6 | *.ear 7 | *.ipr 8 | *.iml 9 | *.iws 10 | target 11 | .idea 12 | .classpath 13 | .project 14 | .settings/* 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | addons: 3 | apt: 4 | packages: 5 | - oracle-java8-installer 6 | 7 | language: java 8 | jdk: 9 | - oraclejdk8 -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | CompletionStage alternative implementation 2 | ========================================== 3 | 4 | [![Build Status](https://travis-ci.org/lukas-krecan/completion-stage.png?branch=master)](https://travis-ci.org/lukas-krecan/completion-stage) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/net.javacrumbs.completion-stage/completion-stage/badge.svg)](https://maven-badges.herokuapp.com/maven-central/net.javacrumbs.completion-stage/completion-stage) 5 | 6 | An alternative implementation to Java 8 [CompletableFuture](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html). 7 | Its main focus is to simplify support of [CompletionStage](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletionStage.html) in 8 | other libraries. 9 | 10 | The objective is to provide a simple, easy to understand alternative. You can check the 11 | [code](https://github.com/lukas-krecan/completion-stage/blob/master/src/main/java/net/javacrumbs/completionstage/SimpleCompletionStage.java) 12 | and see for yourself. 13 | 14 | # How to use 15 | 16 | Add Maven dependency 17 | 18 | 19 | net.javacrumbs.completion-stage 20 | completion-stage 21 | 0.0.9 22 | 23 | 24 | And enjoy 25 | 26 | private final CompletionStageFactory factory = 27 | new CompletionStageFactory(defaultAsyncExecutor); 28 | 29 | ... 30 | 31 | CompletableCompletionStage completionStage = factory.createCompletionStage(); 32 | 33 | ... 34 | 35 | // once the result is ready, let us know 36 | completionStage.complete(value); 37 | 38 | ... 39 | // in case of exception 40 | completionStage.completeExceptionally(exception); 41 | 42 | ... 43 | // create already completed stage 44 | CompletionStage completed = factory.completedStage(value); 45 | 46 | ... 47 | // asynchronously execute supplier 48 | CompletionStage asyncStage = factory.supplyAsync(supplier); 49 | 50 | 51 | For example, to convert Spring [ListenableFuture](http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/util/concurrent/ListenableFuture.html) 52 | to CompletionStage you can 53 | 54 | CompletableCompletionStage completionStage = factory.createCompletionStage(); 55 | 56 | springListenableFuture.addCallback(new ListenableFutureCallback() { 57 | @Override 58 | public void onSuccess(String result) { 59 | completionStage.complete(result); 60 | } 61 | 62 | @Override 63 | public void onFailure(Throwable t) { 64 | completionStage.completeExceptionally(t); 65 | } 66 | }); 67 | 68 | # Design 69 | The best way to understand how it works is to check the [code](https://github.com/lukas-krecan/completion-stage/blob/master/src/main/java/net/javacrumbs/completionstage/SimpleCompletionStage.java). 70 | I have written two articles describing design decisions behind the implementation you can read it [here](http://java.dzone.com/articles/implementing-java-8) and [here](http://java.dzone.com/articles/implementing-java-8-0). 71 | 72 | # Why can't I just use a CompletableFuture? 73 | 74 | You definitely can. The main problem I have that it is tightly coupled with fork-join framework. 75 | And fork-join framework is meant to be used mainly for CPU intensive tasks. But my usual use-case 76 | is the opposite, I want to use asynchronous processing mainly for blocking tasks. I do not want to 77 | block a thread while waiting for a network or database operation. 78 | 79 | Of course, you do not have to use fork-join executors with CompletableFutures. You just have to be careful since it is 80 | the default choice for async methods. 81 | 82 | # Release notes 83 | 84 | ### 0.0.9 85 | * Fine grained locks 86 | 87 | ### 0.0.8 88 | * SimpleCompletionStage made extensible 89 | 90 | ### 0.0.7 91 | * Incorrect release - do not use 92 | 93 | ### 0.0.6 94 | * Added CompletableCompletionStage.doCompleteMethods 95 | 96 | ### 0.0.5 97 | * Added factory methods to CompletionStageFactory 98 | 99 | ### 0.0.4 100 | * Small optimizations 101 | 102 | ### 0.0.3 103 | * thenComposeAsync error handling fixed 104 | * thenCombineAsync uses correct executor 105 | * whenCompleteAsync passes value to next stage 106 | * Internal refactoring 107 | 108 | ### 0.0.2 109 | * Fixed error handling in thenCombine function 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /completion-stage.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /header.txt: -------------------------------------------------------------------------------- 1 | Copyright 2009-2015 the original author or authors. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | net.javacrumbs.completion-stage 6 | completion-stage 7 | 0.0.10-SNAPSHOT 8 | 9 | 10 | org.sonatype.oss 11 | oss-parent 12 | 9 13 | 14 | 15 | 16 | 17 | org.mockito 18 | mockito-core 19 | 1.9.5 20 | test 21 | 22 | 23 | junit 24 | junit 25 | 4.11 26 | test 27 | 28 | 29 | org.assertj 30 | assertj-core 31 | 1.7.0 32 | test 33 | 34 | 35 | org.springframework 36 | spring-core 37 | 4.0.3.RELEASE 38 | test 39 | 40 | 41 | 42 | 43 | 44 | The Apache Software License, Version 2.0 45 | LICENSE.txt 46 | 47 | 48 | 49 | 50 | release-sign-artifacts 51 | 52 | 53 | performRelease 54 | true 55 | 56 | 57 | 58 | 59 | 60 | org.apache.maven.plugins 61 | maven-gpg-plugin 62 | 63 | 195BE743 64 | 65 | 1.6 66 | 67 | 68 | sign-artifacts 69 | verify 70 | 71 | sign 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | scm:git:git@github.com:lukas-krecan/completion-stage.git 83 | scm:git:git@github.com:lukas-krecan/completion-stage.git 84 | scm:git:git@github.com:lukas-krecan/completion-stage.git 85 | 86 | 87 | 88 | 89 | 90 | com.mycila.maven-license-plugin 91 | maven-license-plugin 92 | 1.9.0 93 | 94 |
header.txt
95 | 96 | LICENSE.txt 97 | 98 |
99 |
100 | 101 | org.apache.maven.plugins 102 | maven-jar-plugin 103 | 2.5 104 | 105 | 106 | org.apache.maven.plugins 107 | maven-javadoc-plugin 108 | 2.10.1 109 | 110 | 111 | org.apache.maven.plugins 112 | maven-source-plugin 113 | 2.4 114 | 115 |
116 | 117 | 118 | 119 | 120 | org.apache.maven.plugins 121 | maven-compiler-plugin 122 | 3.2 123 | 124 | 1.8 125 | 1.8 126 | 127 | 128 | 129 | 130 |
131 |
132 | -------------------------------------------------------------------------------- /src/main/java/net/javacrumbs/completionstage/CallbackRegistry.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2009-2015 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.javacrumbs.completionstage; 17 | 18 | import java.util.LinkedList; 19 | import java.util.Objects; 20 | import java.util.Queue; 21 | import java.util.concurrent.Executor; 22 | import java.util.function.Consumer; 23 | 24 | /** 25 | * Registry for Consumer callbacks. Works as a state machine switching between InitialState, IntermediateState, Success and Failure state. 26 | *

27 | *

Inspired by {@code org.springframework.util.concurrent.ListenableFutureCallbackRegistry} and 28 | * {@code com.google.common.util.concurrent.ExecutionList}

29 | *

30 | * Explicit synchronization only around blocks responsible for state switching. 31 | */ 32 | final class CallbackRegistry { 33 | private State state = InitialState.instance(); 34 | 35 | private final Object mutex = new Object(); 36 | 37 | /** 38 | * Adds the given callbacks to this registry. 39 | */ 40 | void addCallbacks(Consumer successCallback, Consumer failureCallback, Executor executor) { 41 | Objects.requireNonNull(successCallback, "'successCallback' must not be null"); 42 | Objects.requireNonNull(failureCallback, "'failureCallback' must not be null"); 43 | Objects.requireNonNull(executor, "'executor' must not be null"); 44 | 45 | synchronized (mutex) { 46 | state = state.addCallbacks(successCallback, failureCallback, executor); 47 | } 48 | } 49 | 50 | /** 51 | * To be called to set the result value. 52 | * 53 | * @param result the result value 54 | * @return true if this result will be used (first result registered) 55 | */ 56 | boolean success(T result) { 57 | State oldState; 58 | synchronized (mutex) { 59 | if (state.isCompleted()) { 60 | return false; 61 | } 62 | oldState = state; 63 | state = state.getSuccessState(result); 64 | } 65 | oldState.callSuccessCallbacks(result); 66 | return true; 67 | 68 | } 69 | 70 | /** 71 | * To be called to set the failure exception 72 | * 73 | * @param failure the exception 74 | * @return true if this result will be used (first result registered) 75 | */ 76 | boolean failure(Throwable failure) { 77 | State oldState; 78 | synchronized (mutex) { 79 | if (state.isCompleted()) { 80 | return false; 81 | } 82 | oldState = state; 83 | state = state.getFailureState(failure); 84 | } 85 | oldState.callFailureCallbacks(failure); 86 | return true; 87 | } 88 | 89 | /** 90 | * State of the registry. All subclasses are meant to be used form a synchronized block and are NOT 91 | * thread safe on their own. 92 | */ 93 | private static abstract class State { 94 | protected abstract State addCallbacks(Consumer successCallback, Consumer failureCallback, Executor executor); 95 | 96 | protected State getSuccessState(S result) { 97 | throw new IllegalStateException("success method should not be called multiple times"); 98 | } 99 | 100 | protected void callSuccessCallbacks(S result) { 101 | } 102 | 103 | protected State getFailureState(Throwable failure) { 104 | throw new IllegalStateException("failure method should not be called multiple times"); 105 | } 106 | 107 | protected void callFailureCallbacks(Throwable failure) { 108 | } 109 | 110 | protected boolean isCompleted() { 111 | return true; 112 | } 113 | } 114 | 115 | /** 116 | * Result is not known yet and no callbacks registered. Using shared instance so we do not allocate instance where 117 | * it may not be needed. 118 | */ 119 | private static class InitialState extends State { 120 | private static final InitialState instance = new InitialState<>(); 121 | 122 | @Override 123 | protected State addCallbacks(Consumer successCallback, Consumer failureCallback, Executor executor) { 124 | IntermediateState intermediateState = new IntermediateState<>(); 125 | intermediateState.addCallbacks(successCallback, failureCallback, executor); 126 | return intermediateState; 127 | } 128 | 129 | @Override 130 | protected State getSuccessState(S result) { 131 | return new SuccessState<>(result); 132 | } 133 | 134 | @Override 135 | protected State getFailureState(Throwable failure) { 136 | return new FailureState<>(failure); 137 | } 138 | 139 | @Override 140 | protected boolean isCompleted() { 141 | return false; 142 | } 143 | 144 | @SuppressWarnings("unchecked") 145 | private static State instance() { 146 | return (State) instance; 147 | } 148 | } 149 | 150 | /** 151 | * Result is not known yet. 152 | */ 153 | private static class IntermediateState extends State { 154 | private final Queue> callbacks = new LinkedList<>(); 155 | 156 | @Override 157 | protected State addCallbacks(Consumer successCallback, Consumer failureCallback, Executor executor) { 158 | callbacks.add(new CallbackHolder<>(successCallback, failureCallback, executor)); 159 | return this; 160 | } 161 | 162 | @Override 163 | protected State getSuccessState(S result) { 164 | return new SuccessState<>(result); 165 | } 166 | 167 | @Override 168 | protected void callSuccessCallbacks(S result) { 169 | // no need to remove callbacks from the queue, this instance will be thrown away at once 170 | for (CallbackHolder callback : callbacks) { 171 | callback.callSuccessCallback(result); 172 | } 173 | } 174 | 175 | @Override 176 | protected State getFailureState(Throwable failure) { 177 | return new FailureState<>(failure); 178 | } 179 | 180 | @Override 181 | protected void callFailureCallbacks(Throwable failure) { 182 | // no need to remove callbacks from the queue, this instance will be thrown away at once 183 | for (CallbackHolder callback : callbacks) { 184 | callback.callFailureCallback(failure); 185 | } 186 | } 187 | 188 | @Override 189 | protected boolean isCompleted() { 190 | return false; 191 | } 192 | } 193 | 194 | /** 195 | * Holds the result. 196 | */ 197 | private static final class SuccessState extends State { 198 | private final S result; 199 | 200 | private SuccessState(S result) { 201 | this.result = result; 202 | } 203 | 204 | @Override 205 | protected State addCallbacks(Consumer successCallback, Consumer failureCallback, Executor executor) { 206 | callCallback(successCallback, result, executor); 207 | return this; 208 | } 209 | } 210 | 211 | /** 212 | * Holds the failure. 213 | */ 214 | private static final class FailureState extends State { 215 | private final Throwable failure; 216 | 217 | private FailureState(Throwable failure) { 218 | this.failure = failure; 219 | } 220 | 221 | @Override 222 | protected State addCallbacks(Consumer successCallback, Consumer failureCallback, Executor executor) { 223 | callCallback(failureCallback, failure, executor); 224 | return this; 225 | } 226 | } 227 | 228 | 229 | private static final class CallbackHolder { 230 | private final Consumer successCallback; 231 | private final Consumer failureCallback; 232 | private final Executor executor; 233 | 234 | private CallbackHolder(Consumer successCallback, Consumer failureCallback, Executor executor) { 235 | this.successCallback = successCallback; 236 | this.failureCallback = failureCallback; 237 | this.executor = executor; 238 | } 239 | 240 | void callSuccessCallback(S result) { 241 | callCallback(successCallback, result, executor); 242 | } 243 | 244 | void callFailureCallback(Throwable failure) { 245 | callCallback(failureCallback, failure, executor); 246 | } 247 | } 248 | 249 | private static void callCallback(Consumer callback, T value, Executor executor) { 250 | executor.execute(() -> callback.accept(value)); 251 | } 252 | 253 | } 254 | -------------------------------------------------------------------------------- /src/main/java/net/javacrumbs/completionstage/CompletableCompletionStage.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2009-2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.javacrumbs.completionstage; 17 | 18 | import java.util.concurrent.CompletionStage; 19 | 20 | /** 21 | * Completion stage you can complete. On top of standard {@link java.util.concurrent.CompletionStage} methods 22 | * provides {@link net.javacrumbs.completionstage.CompletableCompletionStage#complete(Object)} and 23 | * {@link net.javacrumbs.completionstage.CompletableCompletionStage#completeExceptionally(Throwable)}. 24 | */ 25 | public interface CompletableCompletionStage extends CompletionStage { 26 | 27 | /** 28 | * Call this if you want to start processing of the result.. 29 | * 30 | * @param result the result value. 31 | * @return {@code true} if this invocation caused this CompletionStage 32 | * to transition to a completed state, else {@code false} 33 | */ 34 | public boolean complete(T result); 35 | 36 | /** 37 | * Call this if you want to start processing of failure. 38 | * 39 | * @param ex the exception 40 | * @return {@code true} if this invocation caused this CompletionStage 41 | * to transition to a completed state, else {@code false} 42 | */ 43 | public boolean completeExceptionally(Throwable ex); 44 | 45 | /** 46 | * Sets this CompletionStage as success with provided value 47 | * if it hasn't been already completed. Same as {@link #complete(Object) complete(T)}, the only difference 48 | * is the return type which makes this method more suitable to be used as method reference. 49 | * 50 | * @param result the success value. May be null. 51 | */ 52 | public default void doComplete(T result) { 53 | complete(result); 54 | } 55 | 56 | /** 57 | * Accepts a value and a throwable to complete this CompletionStage 58 | * if it hasn't been already completed. If throwable is null, completes normally, if 59 | * throwable is not null, completes exceptionally. 60 | * 61 | * @param result the success value. May be null. 62 | * Completes this computation as a success with this value only if throwable is null. 63 | * @param throwable the failure value. 64 | * If not null, completes this computation as a failure with this value. 65 | */ 66 | public default void doComplete(T result, Throwable throwable) { 67 | if (throwable == null) { 68 | complete(result); 69 | } else { 70 | completeExceptionally(throwable); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/net/javacrumbs/completionstage/CompletionStageAdapter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2009-2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.javacrumbs.completionstage; 17 | 18 | import java.util.concurrent.CompletionStage; 19 | import java.util.concurrent.Executor; 20 | import java.util.function.BiConsumer; 21 | import java.util.function.BiFunction; 22 | import java.util.function.Consumer; 23 | import java.util.function.Function; 24 | 25 | /** 26 | * Contains boring methods from {@link java.util.concurrent.CompletionStage} that just call 27 | * async method with executor. 28 | * @param 29 | */ 30 | abstract class CompletionStageAdapter implements CompletionStage { 31 | protected static final Executor SAME_THREAD_EXECUTOR = new Executor() { 32 | @Override 33 | public void execute(Runnable command) { 34 | command.run(); 35 | } 36 | @Override 37 | public String toString() { 38 | return "SAME_THREAD_EXECUTOR"; 39 | } 40 | }; 41 | /** 42 | * Default executor to be used for Async methods. 43 | */ 44 | private final Executor defaultExecutor; 45 | 46 | CompletionStageAdapter(Executor defaultExecutor) { 47 | this.defaultExecutor = defaultExecutor; 48 | } 49 | 50 | @Override 51 | public CompletionStage thenApply(Function fn) { 52 | return thenApplyAsync(fn, SAME_THREAD_EXECUTOR); 53 | } 54 | 55 | @Override 56 | public CompletionStage thenApplyAsync(Function fn) { 57 | return thenApplyAsync(fn, defaultExecutor); 58 | } 59 | 60 | @Override 61 | public CompletionStage thenAccept(Consumer action) { 62 | return thenAcceptAsync(action, SAME_THREAD_EXECUTOR); 63 | } 64 | 65 | @Override 66 | public CompletionStage thenAcceptAsync(Consumer action) { 67 | return thenAcceptAsync(action, defaultExecutor); 68 | } 69 | 70 | @Override 71 | public CompletionStage thenRun(Runnable action) { 72 | return thenRunAsync(action, SAME_THREAD_EXECUTOR); 73 | } 74 | 75 | @Override 76 | public CompletionStage thenRunAsync(Runnable action) { 77 | return thenRunAsync(action, defaultExecutor); 78 | } 79 | 80 | @Override 81 | public CompletionStage thenCombine(CompletionStage other, BiFunction fn) { 82 | return thenCombineAsync(other, fn, SAME_THREAD_EXECUTOR); 83 | } 84 | 85 | @Override 86 | public CompletionStage thenCombineAsync(CompletionStage other, BiFunction fn) { 87 | return thenCombineAsync(other, fn, defaultExecutor); 88 | } 89 | 90 | @Override 91 | public CompletionStage thenAcceptBoth(CompletionStage other, BiConsumer action) { 92 | return thenAcceptBothAsync(other, action, SAME_THREAD_EXECUTOR); 93 | } 94 | 95 | @Override 96 | public CompletionStage thenAcceptBothAsync(CompletionStage other, BiConsumer action) { 97 | return thenAcceptBothAsync(other, action, defaultExecutor); 98 | } 99 | 100 | @Override 101 | public CompletionStage runAfterBoth(CompletionStage other, Runnable action) { 102 | return runAfterBothAsync(other, action, SAME_THREAD_EXECUTOR); 103 | } 104 | 105 | @Override 106 | public CompletionStage runAfterBothAsync(CompletionStage other, Runnable action) { 107 | return runAfterBothAsync(other, action, defaultExecutor); 108 | } 109 | 110 | @Override 111 | public CompletionStage applyToEither(CompletionStage other, Function fn) { 112 | return applyToEitherAsync(other, fn, SAME_THREAD_EXECUTOR); 113 | } 114 | 115 | @Override 116 | public CompletionStage applyToEitherAsync(CompletionStage other, Function fn) { 117 | return applyToEitherAsync(other, fn, defaultExecutor); 118 | } 119 | 120 | @Override 121 | public CompletionStage acceptEither(CompletionStage other, Consumer action) { 122 | return acceptEitherAsync(other, action, SAME_THREAD_EXECUTOR); 123 | } 124 | 125 | @Override 126 | public CompletionStage acceptEitherAsync(CompletionStage other, Consumer action) { 127 | return acceptEitherAsync(other, action, defaultExecutor); 128 | } 129 | 130 | @Override 131 | public CompletionStage runAfterEither(CompletionStage other, Runnable action) { 132 | return runAfterEitherAsync(other, action, SAME_THREAD_EXECUTOR); 133 | } 134 | 135 | @Override 136 | public CompletionStage runAfterEitherAsync(CompletionStage other, Runnable action) { 137 | return runAfterEitherAsync(other, action, defaultExecutor); 138 | } 139 | 140 | @Override 141 | public CompletionStage thenCompose(Function> fn) { 142 | return thenComposeAsync(fn, SAME_THREAD_EXECUTOR); 143 | } 144 | 145 | @Override 146 | public CompletionStage thenComposeAsync(Function> fn) { 147 | return thenComposeAsync(fn, defaultExecutor); 148 | } 149 | 150 | @Override 151 | public CompletionStage whenComplete(BiConsumer action) { 152 | return whenCompleteAsync(action, SAME_THREAD_EXECUTOR); 153 | } 154 | 155 | @Override 156 | public CompletionStage whenCompleteAsync(BiConsumer action) { 157 | return whenCompleteAsync(action, defaultExecutor); 158 | } 159 | 160 | @Override 161 | public CompletionStage handle(BiFunction fn) { 162 | return handleAsync(fn, SAME_THREAD_EXECUTOR); 163 | } 164 | 165 | @Override 166 | public CompletionStage handleAsync(BiFunction fn) { 167 | return handleAsync(fn, defaultExecutor); 168 | } 169 | 170 | protected final Executor getDefaultExecutor() { 171 | return defaultExecutor; 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/main/java/net/javacrumbs/completionstage/CompletionStageFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2009-2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.javacrumbs.completionstage; 17 | 18 | import net.javacrumbs.completionstage.spi.CompletableCompletionStageFactory; 19 | 20 | import java.util.Objects; 21 | import java.util.concurrent.CompletionStage; 22 | import java.util.concurrent.Executor; 23 | import java.util.function.Supplier; 24 | 25 | /** 26 | * Factory for {@link java.util.concurrent.CompletionStage} implementation. 27 | */ 28 | public class CompletionStageFactory implements CompletableCompletionStageFactory { 29 | private final Executor defaultAsyncExecutor; 30 | 31 | /** 32 | * Creates factory. 33 | * @param defaultAsyncExecutor executor to be used for async methods without executor parameter 34 | */ 35 | public CompletionStageFactory(Executor defaultAsyncExecutor) { 36 | this.defaultAsyncExecutor = defaultAsyncExecutor; 37 | } 38 | 39 | /** 40 | * Creates completion stage. 41 | * @param type of the CompletionStage 42 | * @return CompletionStage 43 | */ 44 | public CompletableCompletionStage createCompletionStage() { 45 | return new SimpleCompletionStage<>(defaultAsyncExecutor, this); 46 | } 47 | 48 | /** 49 | * Returns a new CompletionStage that is already completed with 50 | * the given value. 51 | * 52 | * @param value the value 53 | * @param the type of the value 54 | * @return the completed CompletionStage 55 | */ 56 | public final CompletionStage completedStage(T value) { 57 | CompletableCompletionStage result = createCompletionStage(); 58 | result.complete(value); 59 | return result; 60 | } 61 | 62 | /** 63 | * Returns a new CompletionStage that is asynchronously completed 64 | * by a task running in the defaultAsyncExecutor with 65 | * the value obtained by calling the given Supplier. 66 | * 67 | * @param supplier a function returning the value to be used 68 | * to complete the returned CompletionStage 69 | * @param the function's return type 70 | * @return the new CompletionStage 71 | */ 72 | public final CompletionStage supplyAsync(Supplier supplier) { 73 | return supplyAsync(supplier, defaultAsyncExecutor); 74 | } 75 | 76 | /** 77 | * Returns a new CompletionStage that is asynchronously completed 78 | * by a task running in the given executor with the value obtained 79 | * by calling the given Supplier. Subsequent completion stages will 80 | * use defaultAsyncExecutor as their default executor. 81 | * 82 | * @param supplier a function returning the value to be used 83 | * to complete the returned CompletionStage 84 | * @param executor the executor to use for asynchronous execution 85 | * @param the function's return type 86 | * @return the new CompletionStage 87 | */ 88 | public final CompletionStage supplyAsync(Supplier supplier, Executor executor) { 89 | Objects.requireNonNull(supplier, "supplier must not be null"); 90 | return completedStage(null).thenApplyAsync((ignored) -> supplier.get(), executor); 91 | } 92 | 93 | /** 94 | * Returns a new CompletionStage that is asynchronously completed 95 | * by a task running in the defaultAsyncExecutor after 96 | * it runs the given action. 97 | * 98 | * @param runnable the action to run before completing the 99 | * returned CompletionStage 100 | * @return the new CompletionStage 101 | */ 102 | public final CompletionStage runAsync(Runnable runnable) { 103 | return runAsync(runnable, defaultAsyncExecutor); 104 | } 105 | 106 | /** 107 | * Returns a new CompletionStage that is asynchronously completed 108 | * by a task running in the given executor after it runs the given 109 | * action. Subsequent completion stages will 110 | * use defaultAsyncExecutor as their default executor. 111 | * 112 | * @param runnable the action to run before completing the 113 | * returned CompletionStage 114 | * @param executor the executor to use for asynchronous execution 115 | * @return the new CompletionStage 116 | */ 117 | public final CompletionStage runAsync(Runnable runnable, Executor executor) { 118 | Objects.requireNonNull(runnable, "runnable must not be null"); 119 | return completedStage(null).thenRunAsync(runnable, executor); 120 | } 121 | 122 | protected final Executor getDefaultAsyncExecutor() { 123 | return defaultAsyncExecutor; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/net/javacrumbs/completionstage/SimpleCompletionStage.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2009-2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.javacrumbs.completionstage; 17 | 18 | import net.javacrumbs.completionstage.spi.CompletableCompletionStageFactory; 19 | 20 | import java.util.concurrent.CompletableFuture; 21 | import java.util.concurrent.CompletionException; 22 | import java.util.concurrent.CompletionStage; 23 | import java.util.concurrent.Executor; 24 | import java.util.function.BiConsumer; 25 | import java.util.function.BiFunction; 26 | import java.util.function.Consumer; 27 | import java.util.function.Function; 28 | import java.util.function.Supplier; 29 | 30 | /** 31 | * Please do not use this class directly, use {@link CompletionStageFactory} to create instances. 32 | * {@link java.util.concurrent.CompletionStage} implementation that is built on top of standard executors. 33 | */ 34 | public class SimpleCompletionStage extends CompletionStageAdapter implements CompletableCompletionStage { 35 | 36 | private final CallbackRegistry callbackRegistry = new CallbackRegistry<>(); 37 | private final CompletableCompletionStageFactory completionStageFactory; 38 | 39 | /** 40 | * Creates SimpleCompletionStage. 41 | * 42 | * @param defaultExecutor executor to be used for all async method without executor parameter. 43 | * @param completionStageFactory factory to create next stages 44 | */ 45 | public SimpleCompletionStage(Executor defaultExecutor, CompletableCompletionStageFactory completionStageFactory) { 46 | super(defaultExecutor); 47 | this.completionStageFactory = completionStageFactory; 48 | } 49 | 50 | /** 51 | * Notifies all callbacks about the result. 52 | * 53 | * @param result result of the previous stage. 54 | */ 55 | @Override 56 | public boolean complete(T result) { 57 | return callbackRegistry.success(result); 58 | } 59 | 60 | /** 61 | * Notifies all callbacks about the failure. 62 | * 63 | * @param ex exception thrown from the previous stage. 64 | */ 65 | @Override 66 | public boolean completeExceptionally(Throwable ex) { 67 | return callbackRegistry.failure(ex); 68 | } 69 | 70 | @Override 71 | public CompletionStage thenApplyAsync( 72 | Function fn, 73 | Executor executor 74 | ) { 75 | CompletableCompletionStage nextStage = newCompletableCompletionStage(); 76 | addCallbacks( 77 | result -> acceptResult(nextStage, () -> fn.apply(result)), 78 | handleFailure(nextStage), 79 | executor 80 | ); 81 | return nextStage; 82 | } 83 | 84 | @Override 85 | public CompletionStage thenAcceptAsync(Consumer action, Executor executor) { 86 | return thenApplyAsync(convertConsumerToFunction(action), executor); 87 | } 88 | 89 | @Override 90 | public CompletionStage thenRunAsync(Runnable action, Executor executor) { 91 | return thenApplyAsync(convertRunnableToFunction(action), executor); 92 | } 93 | 94 | @Override 95 | public CompletionStage thenCombineAsync( 96 | CompletionStage other, 97 | BiFunction fn, 98 | Executor executor) { 99 | return thenCompose(result1 -> other.thenApplyAsync(result2 -> fn.apply(result1, result2), executor)); 100 | } 101 | 102 | @Override 103 | public CompletionStage thenAcceptBothAsync(CompletionStage other, BiConsumer action, Executor executor) { 104 | return thenCombineAsync( 105 | other, 106 | // transform BiConsumer to BiFunction 107 | (t, u) -> { 108 | action.accept(t, u); 109 | return null; 110 | }, 111 | executor 112 | ); 113 | } 114 | 115 | @Override 116 | public CompletionStage runAfterBothAsync(CompletionStage other, Runnable action, Executor executor) { 117 | return thenCombineAsync( 118 | other, 119 | // transform Runnable to BiFunction 120 | (t, r) -> { 121 | action.run(); 122 | return null; 123 | }, 124 | executor 125 | ); 126 | } 127 | 128 | @Override 129 | public CompletionStage applyToEitherAsync( 130 | CompletionStage other, 131 | Function fn, 132 | Executor executor) { 133 | return doApplyToEitherAsync(this, other, fn, executor); 134 | } 135 | 136 | /** 137 | * This method exists just to reconcile generics when called from {@link #runAfterEitherAsync} 138 | * which has unexpected type of parameter "other". The alternative is to ignore compiler warning. 139 | */ 140 | private CompletionStage doApplyToEitherAsync( 141 | CompletionStage first, 142 | CompletionStage second, 143 | Function fn, 144 | Executor executor) { 145 | CompletableCompletionStage nextStage = newCompletableCompletionStage(); 146 | 147 | // only the first result is accepted by completion stage, 148 | // the other one is ignored 149 | BiConsumer action = completeHandler(nextStage); 150 | first.whenComplete(action); 151 | second.whenComplete(action); 152 | 153 | return nextStage.thenApplyAsync(fn, executor); 154 | } 155 | 156 | @Override 157 | public CompletionStage acceptEitherAsync(CompletionStage other, Consumer action, Executor executor) { 158 | return applyToEitherAsync(other, convertConsumerToFunction(action), executor); 159 | } 160 | 161 | @Override 162 | public CompletionStage runAfterEitherAsync(CompletionStage other, Runnable action, Executor executor) { 163 | return doApplyToEitherAsync(this, other, convertRunnableToFunction(action), executor); 164 | } 165 | 166 | @Override 167 | public CompletionStage thenComposeAsync(Function> fn, Executor executor) { 168 | CompletableCompletionStage nextStage = newCompletableCompletionStage(); 169 | addCallbacks( 170 | result1 -> { 171 | try { 172 | fn.apply(result1).whenComplete( completeHandler(nextStage) ); 173 | } catch (Throwable e) { 174 | handleFailure(nextStage, e); 175 | } 176 | }, 177 | handleFailure(nextStage), 178 | executor 179 | ); 180 | return nextStage; 181 | } 182 | 183 | @Override 184 | public CompletionStage exceptionally(Function fn) { 185 | CompletableCompletionStage nextStage = newCompletableCompletionStage(); 186 | addCallbacks( 187 | nextStage::complete, 188 | e -> acceptResult(nextStage, () -> fn.apply(e)), 189 | SAME_THREAD_EXECUTOR 190 | ); 191 | return nextStage; 192 | } 193 | 194 | @Override 195 | public CompletionStage whenCompleteAsync(BiConsumer action, Executor executor) { 196 | CompletableCompletionStage nextStage = newCompletableCompletionStage(); 197 | addCallbacks( 198 | result -> acceptResult( 199 | nextStage, 200 | () -> { 201 | action.accept(result, null); 202 | return result; 203 | } 204 | ), 205 | failure -> { 206 | try { 207 | action.accept(null, failure); 208 | handleFailure(nextStage, failure); 209 | } catch (Throwable e) { 210 | handleFailure(nextStage, e); 211 | } 212 | }, executor 213 | ); 214 | return nextStage; 215 | } 216 | 217 | @Override 218 | public CompletionStage handleAsync( 219 | BiFunction fn, 220 | Executor executor) { 221 | CompletableCompletionStage nextStage = newCompletableCompletionStage(); 222 | addCallbacks( 223 | result -> acceptResult(nextStage,() -> fn.apply(result, null)), 224 | // exceptions are treated as success 225 | e -> acceptResult(nextStage, () -> fn.apply(null, e)), 226 | executor 227 | ); 228 | return nextStage; 229 | } 230 | 231 | @Override 232 | public CompletableFuture toCompletableFuture() { 233 | CompletableFuture completableFuture = new CompletableFuture<>(); 234 | addCallbacks( 235 | completableFuture::complete, 236 | completableFuture::completeExceptionally, 237 | SAME_THREAD_EXECUTOR 238 | ); 239 | return completableFuture; 240 | } 241 | 242 | 243 | private CompletableCompletionStage newCompletableCompletionStage() { 244 | return completionStageFactory.createCompletionStage(); 245 | } 246 | 247 | 248 | private Function convertConsumerToFunction(Consumer action) { 249 | return result -> { 250 | action.accept(result); 251 | return null; 252 | }; 253 | } 254 | 255 | private Function convertRunnableToFunction(Runnable action) { 256 | return result -> { 257 | action.run(); 258 | return null; 259 | }; 260 | } 261 | 262 | /** 263 | * Accepts result provided by the Supplier. If an exception is thrown by the supplier, completes exceptionally. 264 | * 265 | * @param supplier generates result 266 | * 267 | */ 268 | 269 | private static void acceptResult(CompletableCompletionStage s, Supplier supplier) { 270 | try { 271 | // exception can be thrown only by supplier. All callbacks are generated by us and they do not throw any exceptions 272 | s.complete(supplier.get()); 273 | } catch (Throwable e) { 274 | handleFailure(s, e); 275 | } 276 | } 277 | 278 | /** 279 | * Handler that can be used in whenComplete method. 280 | * 281 | * @return BiConsumer that passes values to this CompletionStage. 282 | */ 283 | private static BiConsumer completeHandler(CompletableCompletionStage s) { 284 | return (result, failure) -> { 285 | if (failure == null) { 286 | s.complete(result); 287 | } else { 288 | handleFailure(s, failure); 289 | } 290 | }; 291 | } 292 | 293 | /** 294 | * Wraps exception completes exceptionally. 295 | */ 296 | private static Consumer handleFailure(CompletableCompletionStage s) { 297 | return (e) -> handleFailure(s, e); 298 | } 299 | 300 | private static void handleFailure(CompletableCompletionStage s, Throwable e) { 301 | s.completeExceptionally(wrapException(e)); 302 | } 303 | 304 | 305 | /** 306 | * Wraps exception to a {@link java.util.concurrent.CompletionException} if needed. 307 | * 308 | * @param e exception to be wrapped 309 | * @return CompletionException 310 | */ 311 | private static Throwable wrapException(Throwable e) { 312 | if (e instanceof CompletionException) { 313 | return e; 314 | } else { 315 | return new CompletionException(e); 316 | } 317 | } 318 | 319 | private void addCallbacks(Consumer successCallback, Consumer failureCallback, Executor executor) { 320 | callbackRegistry.addCallbacks(successCallback, failureCallback, executor); 321 | } 322 | } 323 | -------------------------------------------------------------------------------- /src/main/java/net/javacrumbs/completionstage/spi/CompletableCompletionStageFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2009-2016 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.javacrumbs.completionstage.spi; 17 | 18 | import net.javacrumbs.completionstage.CompletableCompletionStage; 19 | import net.javacrumbs.completionstage.SimpleCompletionStage; 20 | 21 | /** 22 | * Factory for customizing {@link SimpleCompletionStage}. 23 | */ 24 | public interface CompletableCompletionStageFactory { 25 | CompletableCompletionStage createCompletionStage(); 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/net/javacrumbs/completionstage/AbstractCompletionStageTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2009-2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.javacrumbs.completionstage; 17 | 18 | import org.junit.Test; 19 | import org.mockito.ArgumentMatcher; 20 | 21 | import java.util.List; 22 | import java.util.concurrent.CompletableFuture; 23 | import java.util.concurrent.CompletionException; 24 | import java.util.concurrent.CompletionStage; 25 | import java.util.concurrent.CopyOnWriteArrayList; 26 | import java.util.concurrent.CountDownLatch; 27 | import java.util.concurrent.ExecutionException; 28 | import java.util.concurrent.Executor; 29 | import java.util.function.BiConsumer; 30 | import java.util.function.BiFunction; 31 | import java.util.function.Consumer; 32 | import java.util.function.Function; 33 | 34 | import static java.lang.Thread.currentThread; 35 | import static org.assertj.core.api.Assertions.assertThat; 36 | import static org.junit.Assert.assertEquals; 37 | import static org.junit.Assert.assertNotEquals; 38 | import static org.junit.Assert.fail; 39 | import static org.mockito.Matchers.any; 40 | import static org.mockito.Matchers.anyString; 41 | import static org.mockito.Matchers.argThat; 42 | import static org.mockito.Matchers.isNull; 43 | import static org.mockito.Mockito.doThrow; 44 | import static org.mockito.Mockito.mock; 45 | import static org.mockito.Mockito.times; 46 | import static org.mockito.Mockito.verify; 47 | import static org.mockito.Mockito.verifyZeroInteractions; 48 | import static org.mockito.Mockito.when; 49 | 50 | 51 | /** 52 | * original test followingStagesShouldBeCalledInTeSameThread 53 | * callback hell 54 | * followingStagesShouldBeCalledInTeSameThread - can be executed in the main thread 55 | * Duplicity 56 | * Transformation to function 57 | * then compose 58 | * state 59 | * coverage is great 60 | * exception handling in combine (which one will handle the exception?) 61 | * CompletableFuture.doThenCombine 62 | */ 63 | @SuppressWarnings("unchecked") 64 | public abstract class AbstractCompletionStageTest { 65 | protected static final String VALUE = "test"; 66 | protected static final String VALUE2 = "value2"; 67 | protected static final RuntimeException EXCEPTION = new RuntimeException("Test"); 68 | protected static final String IN_EXECUTOR_THREAD_NAME = "in executor"; 69 | protected static final String IN_DEFAULT_EXECUTOR_THREAD_NAME = "in default executor"; 70 | private static final String MAIN = "main"; 71 | private final Executor executor = new ThreadNamingExecutor(IN_EXECUTOR_THREAD_NAME); 72 | private final Consumer consumer = mock(Consumer.class); 73 | private final BiConsumer biConsumer = mock(BiConsumer.class); 74 | private final Consumer intConsumer = mock(Consumer.class); 75 | 76 | protected abstract CompletionStage createCompletionStage(String value); 77 | 78 | protected abstract CompletionStage createCompletionStage(Throwable e); 79 | 80 | protected abstract void finish(CompletionStage c); 81 | 82 | private final List failures = new CopyOnWriteArrayList<>(); 83 | 84 | protected Executor defaultExecutor = new ThreadNamingExecutor(IN_DEFAULT_EXECUTOR_THREAD_NAME); 85 | 86 | protected boolean finished() { 87 | return true; 88 | } 89 | 90 | @Test 91 | public void acceptEitherAcceptsOnlyOneValue() { 92 | CompletionStage completionStage = createCompletionStage(VALUE); 93 | CompletionStage completionStage2 = createCompletionStage(VALUE2); 94 | finish(completionStage); 95 | finish(completionStage2); 96 | 97 | completionStage.acceptEither(completionStage2, consumer); 98 | 99 | verify(consumer, times(1)).accept(any(String.class)); 100 | } 101 | 102 | @Test 103 | public void runAfterEitherCalledOnlyOnce() { 104 | CompletionStage completionStage = createCompletionStage(VALUE); 105 | CompletionStage completionStage2 = createCompletionStage(VALUE2); 106 | finish(completionStage); 107 | 108 | Runnable runnable = mock(Runnable.class); 109 | completionStage.runAfterEither(completionStage2, runnable); 110 | 111 | finish(completionStage2); 112 | 113 | verify(runnable, times(1)).run(); 114 | } 115 | 116 | @Test 117 | public void acceptShouldWork() { 118 | CompletionStage completionStage = createCompletionStage(VALUE); 119 | 120 | completionStage.thenAccept(consumer); 121 | 122 | finish(completionStage); 123 | 124 | verify(consumer).accept(VALUE); 125 | } 126 | 127 | @Test 128 | public void acceptAsyncShouldBeCalledUsingExecutor() throws InterruptedException { 129 | CompletionStage completionStage = createCompletionStage(VALUE); 130 | 131 | CountDownLatch waitLatch = new CountDownLatch(1); 132 | 133 | completionStage.thenAcceptAsync(r -> { 134 | assertEquals(IN_EXECUTOR_THREAD_NAME, currentThread().getName()); 135 | waitLatch.countDown(); 136 | }, executor).exceptionally(errorHandler(waitLatch)); 137 | 138 | finish(completionStage); 139 | 140 | waitLatch.await(); 141 | assertThat(failures).isEmpty(); 142 | } 143 | 144 | @Test 145 | public void followingStagesShouldBeCalledInTeSameThread() throws InterruptedException { 146 | CompletionStage completionStage = createCompletionStage(VALUE); 147 | 148 | CountDownLatch waitLatch = new CountDownLatch(1); 149 | 150 | completionStage 151 | .thenApplyAsync(r -> { 152 | assertEquals(IN_EXECUTOR_THREAD_NAME, currentThread().getName()); 153 | return "a"; 154 | }, executor) 155 | .thenAccept(r -> { 156 | // In fact it can be executed even in main thread depending if the previous callback finished sooner than 157 | // thenAccept is called 158 | // assertEquals(IN_EXECUTOR_THREAD_NAME, currentThread().getName()); 159 | assertEquals("a", r); 160 | waitLatch.countDown(); 161 | }) 162 | .exceptionally(errorHandler(waitLatch)); 163 | 164 | finish(completionStage); 165 | 166 | waitLatch.await(); 167 | assertThat(failures).isEmpty(); 168 | } 169 | 170 | @Test 171 | public void allAsyncCallShouldBeCalledInDefaultExecutor() throws InterruptedException { 172 | CompletionStage completionStage = createCompletionStage(VALUE); 173 | 174 | CountDownLatch waitLatch = new CountDownLatch(1); 175 | currentThread().setName(MAIN); 176 | 177 | completionStage 178 | .thenApplyAsync(r -> { 179 | assertNotEquals(MAIN, currentThread().getName()); 180 | return "a"; 181 | }) 182 | .thenAcceptAsync(r -> { 183 | assertNotEquals(MAIN, currentThread().getName()); 184 | assertEquals("a", r); 185 | waitLatch.countDown(); 186 | }) 187 | .exceptionally(errorHandler(waitLatch)); 188 | 189 | finish(completionStage); 190 | 191 | waitLatch.await(); 192 | assertThat(failures).isEmpty(); 193 | } 194 | 195 | private Function errorHandler(CountDownLatch waitLatch) { 196 | return e -> { 197 | failures.add(e); 198 | waitLatch.countDown(); 199 | return null; 200 | }; 201 | } 202 | 203 | @Test 204 | public void whenCompleteShouldAcceptUnwrappedException() { 205 | CompletionStage completionStage = createCompletionStage(EXCEPTION); 206 | 207 | completionStage.whenComplete(biConsumer); 208 | 209 | finish(completionStage); 210 | verify(biConsumer, times(1)).accept(null, EXCEPTION); 211 | } 212 | 213 | @Test 214 | public void handleShouldAcceptUnwrappedException() { 215 | CompletionStage completionStage = createCompletionStage(EXCEPTION); 216 | 217 | BiFunction handler = mock(BiFunction.class); 218 | completionStage.handle(handler); 219 | 220 | finish(completionStage); 221 | verify(handler, times(1)).apply(null, EXCEPTION); 222 | } 223 | 224 | @Test 225 | public void exceptionallyShouldTranslateExceptionToAValue() { 226 | CompletionStage completionStage = createCompletionStage(EXCEPTION); 227 | 228 | Function function = mock(Function.class); 229 | when(function.apply(EXCEPTION)).thenReturn(VALUE); 230 | completionStage.exceptionally(function).thenAccept(consumer); 231 | 232 | finish(completionStage); 233 | 234 | verify(function, times(1)).apply(EXCEPTION); 235 | verify(consumer, times(1)).accept(VALUE); 236 | } 237 | 238 | @Test 239 | public void exceptionallyShouldPassValue() { 240 | CompletionStage completionStage = createCompletionStage(VALUE); 241 | 242 | Function function = mock(Function.class); 243 | when(function.apply(EXCEPTION)).thenReturn(VALUE); 244 | completionStage.exceptionally(function).thenAccept(consumer); 245 | 246 | finish(completionStage); 247 | 248 | verifyZeroInteractions(function); 249 | verify(consumer).accept(VALUE); 250 | } 251 | 252 | @Test 253 | public void exceptionFromThenApplyShouldBePassedToTheNextPhase() { 254 | CompletionStage completionStage = createCompletionStage(VALUE); 255 | 256 | Function conversion = mock(Function.class); 257 | Function errorHandler = mock(Function.class); 258 | when(errorHandler.apply(EXCEPTION)).thenReturn(null); 259 | when(conversion.apply(VALUE)).thenThrow(EXCEPTION); 260 | completionStage.thenApply(conversion).exceptionally(errorHandler); 261 | 262 | finish(completionStage); 263 | 264 | verify(errorHandler).apply(isACompletionException()); 265 | verify(conversion).apply(VALUE); 266 | } 267 | 268 | @Test 269 | public void ifExceptionallyFunctionFailsItShouldBePassedFurther() { 270 | CompletionStage completionStage = createCompletionStage(EXCEPTION); 271 | 272 | Function errorHandler = mock(Function.class); 273 | when(errorHandler.apply(EXCEPTION)).thenReturn(VALUE); 274 | completionStage.exceptionally(e -> { 275 | throw EXCEPTION; 276 | }).exceptionally(errorHandler).thenAccept(consumer); 277 | 278 | finish(completionStage); 279 | 280 | verify(errorHandler).apply(isACompletionException()); 281 | verify(consumer).accept(null); 282 | } 283 | 284 | @Test 285 | public void shouldCombineValues() { 286 | CompletionStage completionStage1 = createCompletionStage(VALUE); 287 | CompletionStage completionStage2 = createCompletionStage(VALUE2); 288 | finish(completionStage2); 289 | 290 | BiFunction combiner = mock(BiFunction.class); 291 | when(combiner.apply(VALUE, VALUE2)).thenReturn(5); 292 | 293 | completionStage1.thenCombine(completionStage2, combiner).thenAccept(intConsumer); 294 | finish(completionStage1); 295 | 296 | verify(combiner).apply(VALUE, VALUE2); 297 | verify(intConsumer).accept(5); 298 | } 299 | 300 | @Test 301 | public void shouldCombineValuesInOppositeOrder() { 302 | CompletionStage completionStage1 = createCompletionStage(VALUE); 303 | CompletionStage completionStage2 = createCompletionStage(VALUE2); 304 | finish(completionStage2); 305 | 306 | BiFunction combiner = mock(BiFunction.class); 307 | 308 | when(combiner.apply(VALUE2, VALUE)).thenReturn(5); 309 | 310 | completionStage2.thenCombine(completionStage1, combiner).thenAccept(intConsumer); 311 | finish(completionStage1); 312 | 313 | verify(combiner).apply(VALUE2, VALUE); 314 | verify(intConsumer).accept(5); 315 | } 316 | 317 | @Test 318 | public void combineShouldHandleExceptionCorrectly() { 319 | CompletionStage completionStage1 = createCompletionStage(VALUE); 320 | CompletionStage completionStage2 = createCompletionStage(VALUE2); 321 | finish(completionStage2); 322 | 323 | 324 | BiFunction handler = mock(BiFunction.class); 325 | 326 | completionStage1.thenCombine(completionStage2, (a, b) -> { 327 | throw EXCEPTION; 328 | }).handle(handler); 329 | 330 | finish(completionStage1); 331 | 332 | verify(handler).apply(isNull(String.class), isACompletionException()); 333 | } 334 | 335 | @Test 336 | public void combineShouldHandlePreviousStageFailureCorrectly() { 337 | CompletionStage completionStage1 = createCompletionStage(EXCEPTION); 338 | CompletionStage completionStage2 = createCompletionStage(VALUE2); 339 | finish(completionStage2); 340 | 341 | 342 | BiFunction handler = mock(BiFunction.class); 343 | BiFunction combiner = mock(BiFunction.class); 344 | 345 | completionStage1.thenCombine(completionStage2, combiner).handle(handler); 346 | 347 | finish(completionStage1); 348 | 349 | verify(handler).apply(isNull(String.class), isACompletionException()); 350 | verifyZeroInteractions(combiner); 351 | } 352 | 353 | @Test 354 | public void combineShouldHandleTheOtherPreviousStageFailureCorrectly() { 355 | CompletionStage completionStage1 = createCompletionStage(VALUE); 356 | CompletionStage completionStage2 = createCompletionStage(EXCEPTION); 357 | finish(completionStage2); 358 | 359 | 360 | BiFunction handler = mock(BiFunction.class); 361 | BiFunction combiner = mock(BiFunction.class); 362 | 363 | completionStage1.thenCombine(completionStage2, combiner).handle(handler); 364 | 365 | finish(completionStage1); 366 | 367 | verify(handler).apply(isNull(String.class), isACompletionException()); 368 | verifyZeroInteractions(combiner); 369 | } 370 | 371 | @Test 372 | public void combineAsyncShouldExecuteFunctionInCorrectExecutor() throws ExecutionException, InterruptedException { 373 | CompletionStage completionStage1 = createCompletionStage(VALUE); 374 | CompletionStage completionStage2 = createCompletionStage(VALUE2); 375 | 376 | 377 | CompletableFuture completableFuture = completionStage1.thenCombineAsync(completionStage2, (r1, r2) -> { 378 | assertEquals(IN_EXECUTOR_THREAD_NAME, currentThread().getName()); 379 | return null; 380 | }, executor).toCompletableFuture(); 381 | 382 | finish(completionStage1); 383 | finish(completionStage2); 384 | 385 | completableFuture.get(); 386 | } 387 | 388 | @Test 389 | public void shouldAcceptBothValues() { 390 | CompletionStage completionStage1 = createCompletionStage(VALUE); 391 | CompletionStage completionStage2 = createCompletionStage(VALUE2); 392 | finish(completionStage2); 393 | 394 | BiConsumer biConsumer = mock(BiConsumer.class); 395 | 396 | completionStage1.thenAcceptBoth(completionStage2, biConsumer); 397 | if (!finished()) { 398 | verifyZeroInteractions(biConsumer); 399 | } 400 | 401 | finish(completionStage1); 402 | 403 | verify(biConsumer).accept(VALUE, VALUE2); 404 | } 405 | 406 | @Test 407 | public void shouldRunAfterBothValues() { 408 | CompletionStage completionStage1 = createCompletionStage(VALUE); 409 | CompletionStage completionStage2 = createCompletionStage(VALUE2); 410 | finish(completionStage2); 411 | 412 | Runnable runnable = mock(Runnable.class); 413 | 414 | completionStage1.runAfterBoth(completionStage2, runnable); 415 | if (!finished()) { 416 | verifyZeroInteractions(runnable); 417 | } 418 | 419 | finish(completionStage1); 420 | 421 | verify(runnable).run(); 422 | } 423 | 424 | 425 | @Test 426 | public void exceptionFromThenAcceptShouldBePassedToTheNextPhase() { 427 | CompletionStage completionStage = createCompletionStage(VALUE); 428 | 429 | Function errorHandler = mock(Function.class); 430 | when(errorHandler.apply(EXCEPTION)).thenReturn(null); 431 | doThrow(EXCEPTION).when(consumer).accept(VALUE); 432 | completionStage.thenAccept(consumer).exceptionally(errorHandler); 433 | 434 | finish(completionStage); 435 | 436 | verify(errorHandler).apply(isACompletionException()); 437 | verify(consumer).accept(VALUE); 438 | } 439 | 440 | @Test 441 | public void thenApplyShouldTransformTheValue() { 442 | CompletionStage completionStage = createCompletionStage(VALUE); 443 | 444 | completionStage.thenApply(String::length).thenApply(i -> i * 2).thenAccept(intConsumer); 445 | 446 | finish(completionStage); 447 | 448 | verify(intConsumer).accept(8); 449 | } 450 | 451 | @Test 452 | public void exceptionFromTheNextPhaseShouldNotAffectPreviousPhases() { 453 | CompletionStage completionStage = createCompletionStage(VALUE); 454 | BiFunction handler = mock(BiFunction.class); 455 | 456 | completionStage.thenApply(String::length).thenApply(i -> { 457 | throw EXCEPTION; 458 | }).handle(handler); 459 | finish(completionStage); 460 | 461 | verify(handler, times(1)).apply(isNull(), isACompletionException()); 462 | } 463 | 464 | @Test 465 | public void shouldNotFailOnException() { 466 | CompletionStage completionStage = createCompletionStage(EXCEPTION); 467 | 468 | Function errorFunction = mock(Function.class); 469 | completionStage.thenApply(String::length).thenApply(i -> i * 2).thenAccept(intConsumer).exceptionally(errorFunction); 470 | 471 | finish(completionStage); 472 | 473 | verifyZeroInteractions(intConsumer); 474 | verify(errorFunction, times(1)).apply(isACompletionException()); 475 | } 476 | 477 | @Test 478 | public void handleShouldBeCalled() { 479 | CompletionStage completionStage = createCompletionStage(VALUE); 480 | 481 | BiFunction consumer = mock(BiFunction.class); 482 | completionStage.handle(consumer); 483 | 484 | finish(completionStage); 485 | 486 | verify(consumer).apply(VALUE, null); 487 | } 488 | 489 | @Test 490 | public void exceptionFromHandleShouldBePropagatedOnSuccess() { 491 | CompletionStage completionStage = createCompletionStage(VALUE); 492 | 493 | BiFunction consumer = (s, throwable) -> { 494 | throw EXCEPTION; 495 | }; 496 | 497 | Function errorHandler = mock(Function.class); 498 | completionStage.handle(consumer).exceptionally(errorHandler); 499 | 500 | finish(completionStage); 501 | 502 | verify(errorHandler).apply(isACompletionException()); 503 | } 504 | 505 | @Test 506 | public void exceptionFromHandleShouldBePropagatedOnError() { 507 | CompletionStage completionStage = createCompletionStage(EXCEPTION); 508 | 509 | BiFunction consumer = (s, throwable) -> { 510 | throw EXCEPTION; 511 | }; 512 | 513 | Function errorHandler = mock(Function.class); 514 | completionStage.handle(consumer).exceptionally(errorHandler); 515 | 516 | finish(completionStage); 517 | 518 | verify(errorHandler).apply(isACompletionException()); 519 | } 520 | 521 | @Test 522 | public void handleShouldNotPassException() { 523 | CompletionStage completionStage = createCompletionStage(EXCEPTION); 524 | 525 | BiFunction consumer = mock(BiFunction.class); 526 | Function errorHandler = mock(Function.class); 527 | completionStage.handle(consumer).exceptionally(errorHandler); 528 | 529 | finish(completionStage); 530 | 531 | verify(consumer).apply(null, EXCEPTION); 532 | verifyZeroInteractions(errorHandler); 533 | } 534 | 535 | 536 | @Test 537 | public void whenCompleteShouldAcceptValue() { 538 | CompletionStage completionStage = createCompletionStage(VALUE); 539 | 540 | BiConsumer consumer = mock(BiConsumer.class); 541 | completionStage.thenApply(String::length).thenApply(i -> i * 2).whenComplete(consumer).thenAccept(intConsumer); 542 | 543 | finish(completionStage); 544 | 545 | verify(consumer).accept(8, null); 546 | verify(intConsumer).accept(8); 547 | } 548 | 549 | @Test 550 | public void whenCompleteShouldPassException() { 551 | CompletionStage completionStage = createCompletionStage(EXCEPTION); 552 | 553 | BiConsumer consumer = mock(BiConsumer.class); 554 | Function errorHandler = mock(Function.class); 555 | completionStage.thenApply(String::length).thenApply(i -> i * 2).whenComplete(consumer).exceptionally(errorHandler); 556 | 557 | finish(completionStage); 558 | 559 | verify(consumer).accept((Integer) isNull(), isACompletionException()); 560 | verify(errorHandler).apply(isACompletionException()); 561 | } 562 | 563 | @Test 564 | public void whenCompleteShouldPassExceptionFromConsumerOnSuccess() { 565 | CompletionStage completionStage = createCompletionStage(VALUE); 566 | 567 | BiConsumer consumer = (r, e) -> { 568 | throw EXCEPTION; 569 | }; 570 | Function errorHandler = mock(Function.class); 571 | completionStage.whenComplete(consumer).exceptionally(errorHandler); 572 | 573 | finish(completionStage); 574 | 575 | verify(errorHandler).apply(isACompletionException()); 576 | } 577 | 578 | @Test 579 | public void whenCompleteShouldPassExceptionFromConsumerOnFailure() { 580 | CompletionStage completionStage = createCompletionStage(EXCEPTION); 581 | 582 | BiConsumer consumer = (r, e) -> { 583 | throw EXCEPTION; 584 | }; 585 | Function errorHandler = mock(Function.class); 586 | completionStage.whenComplete(consumer).exceptionally(errorHandler); 587 | 588 | finish(completionStage); 589 | 590 | verify(errorHandler).apply(isACompletionException()); 591 | } 592 | 593 | @Test 594 | public void thenRunShouldRun() { 595 | CompletionStage completionStage = createCompletionStage(VALUE); 596 | 597 | Runnable runnable = mock(Runnable.class); 598 | completionStage.thenRun(runnable); 599 | 600 | finish(completionStage); 601 | 602 | verify(runnable).run(); 603 | } 604 | 605 | @Test 606 | public void thenComposeWaitsForTheOtherResult() { 607 | CompletionStage completionStage = createCompletionStage(VALUE); 608 | CompletionStage completionStage2 = createCompletionStage(VALUE2); 609 | 610 | completionStage.thenCompose(r -> completionStage2).thenAccept(consumer); 611 | 612 | finish(completionStage); 613 | finish(completionStage2); 614 | 615 | verify(consumer, times(1)).accept(VALUE2); 616 | } 617 | 618 | @Test 619 | public void thenComposePassesPreviousFailure() { 620 | CompletionStage completionStage = createCompletionStage(EXCEPTION); 621 | CompletionStage completionStage2 = createCompletionStage(VALUE2); 622 | 623 | 624 | BiFunction handler = mock(BiFunction.class); 625 | completionStage.thenCompose(r -> completionStage2).handle(handler); 626 | 627 | finish(completionStage); 628 | finish(completionStage2); 629 | 630 | verify(handler).apply(isNull(String.class), isACompletionException()); 631 | } 632 | 633 | @Test 634 | public void thenComposePassesFailureFromTheOtherCompletionStage() { 635 | CompletionStage completionStage = createCompletionStage(VALUE); 636 | CompletionStage completionStage2 = createCompletionStage(EXCEPTION); 637 | 638 | completionStage.thenCompose(r -> completionStage2).whenComplete(biConsumer); 639 | 640 | finish(completionStage); 641 | finish(completionStage2); 642 | 643 | 644 | verify(biConsumer).accept(isNull(String.class), isACompletionException()); 645 | } 646 | 647 | 648 | @Test 649 | public void toCompletableFutureShouldPassValue() throws ExecutionException, InterruptedException { 650 | CompletionStage completionStage = createCompletionStage(VALUE); 651 | 652 | CompletableFuture completableFuture = completionStage.toCompletableFuture(); 653 | 654 | finish(completionStage); 655 | 656 | assertThat(completableFuture.get()).isEqualTo(VALUE); 657 | } 658 | 659 | @Test 660 | public void toCompletableFutureShouldPassException() throws ExecutionException, InterruptedException { 661 | CompletionStage completionStage = createCompletionStage(EXCEPTION); 662 | 663 | CompletableFuture completableFuture = completionStage.toCompletableFuture(); 664 | 665 | finish(completionStage); 666 | 667 | try { 668 | completableFuture.get(); 669 | } catch (ExecutionException e) { 670 | assertThat(e.getCause()).isSameAs(EXCEPTION); 671 | } 672 | } 673 | 674 | @Test 675 | public void thenComposeWrapsExceptionIfFunctionFails() { 676 | CompletionStage completionStage = createCompletionStage(VALUE); 677 | 678 | BiFunction handler = mock(BiFunction.class); 679 | completionStage.thenCompose(r -> { 680 | throw EXCEPTION; 681 | }).handle(handler); 682 | 683 | finish(completionStage); 684 | 685 | verify(handler, times(1)).apply(isNull(String.class), isACompletionException()); 686 | } 687 | 688 | @Test 689 | public void combineAsyncShouldWork() throws ExecutionException, InterruptedException { 690 | CompletionStage completionStage1 = createCompletionStage(VALUE); 691 | CompletionStage completionStage2 = createCompletionStage(VALUE2); 692 | 693 | CompletionStage combined = completionStage1.thenCombineAsync(completionStage2, (a, b) -> a + b); 694 | 695 | finish(completionStage1); 696 | finish(completionStage2); 697 | 698 | assertThat(combined.toCompletableFuture().get()).isEqualTo(VALUE + VALUE2); 699 | } 700 | 701 | @Test 702 | public void runAfterBothAsyncShouldWaitForBoth() { 703 | CompletionStage completionStage1 = createCompletionStage(VALUE); 704 | CompletionStage completionStage2 = createCompletionStage(VALUE2); 705 | 706 | Runnable runnable = mock(Runnable.class); 707 | CompletionStage newCompletionStage = completionStage1.runAfterBothAsync(completionStage2, runnable); 708 | 709 | finish(completionStage1); 710 | finish(completionStage2); 711 | 712 | waitForIt(newCompletionStage); 713 | verify(runnable, times(1)).run(); 714 | } 715 | 716 | @Test 717 | public void thenAcceptBothAsyncShouldWaitForBoth() { 718 | CompletionStage completionStage1 = createCompletionStage(VALUE); 719 | CompletionStage completionStage2 = createCompletionStage(VALUE2); 720 | 721 | BiConsumer consumer = mock(BiConsumer.class); 722 | CompletionStage newCompletionStage = completionStage1.thenAcceptBothAsync(completionStage2, consumer); 723 | 724 | finish(completionStage1); 725 | finish(completionStage2); 726 | 727 | waitForIt(newCompletionStage); 728 | verify(consumer).accept(VALUE, VALUE2); 729 | } 730 | 731 | @Test 732 | public void applyToEitherShouldProcessOnlyOne() { 733 | CompletionStage completionStage1 = createCompletionStage(VALUE); 734 | CompletionStage completionStage2 = createCompletionStage(VALUE2); 735 | finish(completionStage1); 736 | 737 | Function function = mock(Function.class); 738 | completionStage1.applyToEither(completionStage2, function); 739 | 740 | finish(completionStage2); 741 | 742 | verify(function, times(1)).apply(anyString()); 743 | } 744 | 745 | @Test 746 | public void applyToEitherAsyncShouldExecuteFunctionInExecutor() { 747 | CompletionStage completionStage1 = createCompletionStage(VALUE); 748 | CompletionStage completionStage2 = createCompletionStage(VALUE2); 749 | finish(completionStage1); 750 | 751 | CompletionStage newCompletionStage = completionStage1.applyToEitherAsync(completionStage2, s -> { 752 | assertEquals(IN_EXECUTOR_THREAD_NAME, currentThread().getName()); 753 | return null; 754 | }, executor); 755 | 756 | finish(completionStage2); 757 | 758 | waitForIt(newCompletionStage); 759 | 760 | } 761 | 762 | @Test // just for code coverage. The code is tested by sync version 763 | public void acceptEitherAsyncDoesNotFail() { 764 | CompletionStage completionStage1 = createCompletionStage(VALUE); 765 | CompletionStage completionStage2 = createCompletionStage(VALUE2); 766 | finish(completionStage1); 767 | finish(completionStage2); 768 | 769 | completionStage1.acceptEitherAsync(completionStage2, x -> { 770 | }); 771 | } 772 | 773 | @Test // just for code coverage. The code is tested by sync version 774 | public void runAfterEitherAsyncDoesNotFail() { 775 | CompletionStage completionStage1 = createCompletionStage(VALUE); 776 | CompletionStage completionStage2 = createCompletionStage(VALUE2); 777 | finish(completionStage1); 778 | finish(completionStage2); 779 | 780 | completionStage1.runAfterEitherAsync(completionStage2, () -> { 781 | }); 782 | } 783 | 784 | @Test // just for code coverage. The code is tested by sync version 785 | public void thenComposeAsyncAsyncDoesNotFail() { 786 | CompletionStage completionStage1 = createCompletionStage(VALUE); 787 | CompletionStage completionStage2 = createCompletionStage(VALUE2); 788 | finish(completionStage1); 789 | finish(completionStage2); 790 | 791 | completionStage1.thenComposeAsync(x -> completionStage2); 792 | } 793 | 794 | @Test // just for code coverage. The code is tested by sync version 795 | public void whenCompleteAsyncDoesNotFail() { 796 | CompletionStage completionStage1 = createCompletionStage(VALUE); 797 | finish(completionStage1); 798 | 799 | completionStage1.whenCompleteAsync((r, e) -> { 800 | }); 801 | } 802 | 803 | @Test // just for code coverage. The code is tested by sync version 804 | public void handleAsyncDoesNotFail() { 805 | CompletionStage completionStage1 = createCompletionStage(VALUE); 806 | finish(completionStage1); 807 | 808 | completionStage1.handleAsync((r, e) -> 1); 809 | } 810 | 811 | @Test // just for code coverage. The code is tested by sync version 812 | public void thenRunAsyncDoesNotFail() { 813 | CompletionStage completionStage1 = createCompletionStage(VALUE); 814 | finish(completionStage1); 815 | 816 | completionStage1.thenRunAsync(() -> { 817 | }); 818 | } 819 | 820 | 821 | private void waitForIt(CompletionStage newCompletionStage) { 822 | try { 823 | newCompletionStage.toCompletableFuture().get(); 824 | } catch (InterruptedException | ExecutionException e) { 825 | fail(e.getMessage()); 826 | } 827 | } 828 | 829 | private CompletionException isACompletionException() { 830 | return argThat(new ArgumentMatcher() { 831 | @Override 832 | public boolean matches(Object argument) { 833 | return argument instanceof CompletionException && ((CompletionException) argument).getCause() == EXCEPTION; 834 | } 835 | }); 836 | } 837 | 838 | /** 839 | * Names thread 840 | */ 841 | protected static class ThreadNamingExecutor implements Executor { 842 | private final String threadName; 843 | 844 | protected ThreadNamingExecutor(String threadName) { 845 | this.threadName = threadName; 846 | } 847 | 848 | @Override 849 | public void execute(Runnable command) { 850 | String originalName = currentThread().getName(); 851 | currentThread().setName(threadName); 852 | try { 853 | command.run(); 854 | } finally { 855 | currentThread().setName(originalName); 856 | } 857 | } 858 | } 859 | } -------------------------------------------------------------------------------- /src/test/java/net/javacrumbs/completionstage/AbstractUnfinishedCompletionStageTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2009-2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.javacrumbs.completionstage; 17 | 18 | import org.junit.Test; 19 | 20 | import java.util.concurrent.CompletionException; 21 | import java.util.concurrent.CompletionStage; 22 | import java.util.function.Consumer; 23 | import java.util.function.Function; 24 | 25 | import static org.mockito.Matchers.any; 26 | import static org.mockito.Mockito.mock; 27 | import static org.mockito.Mockito.times; 28 | import static org.mockito.Mockito.verify; 29 | import static org.mockito.Mockito.verifyZeroInteractions; 30 | 31 | public abstract class AbstractUnfinishedCompletionStageTest extends AbstractCompletionStageTest { 32 | 33 | protected boolean finished() { 34 | return false; 35 | } 36 | 37 | @Test 38 | public void acceptEitherTakesOtherValueIfTheFirstIsNotReady() { 39 | CompletionStage completionStage = createCompletionStage(VALUE); 40 | CompletionStage completionStage2 = createCompletionStage(VALUE2); 41 | finish(completionStage2); 42 | 43 | Consumer consumer = mockConsumer(); 44 | completionStage.acceptEither(completionStage2, consumer); 45 | 46 | verify(consumer, times(1)).accept(VALUE2); 47 | finish(completionStage); 48 | 49 | verify(consumer, times(1)).accept(any(String.class)); 50 | } 51 | 52 | @Test 53 | public void acceptEitherPropagatesExceptionFromSecondCompletable() { 54 | CompletionStage completionStage = createCompletionStage(VALUE); 55 | CompletionStage completionStage2 = createCompletionStage(EXCEPTION); 56 | finish(completionStage2); 57 | 58 | Consumer consumer = mockConsumer(); 59 | 60 | Function errorHandler = mockFunction(); 61 | completionStage.acceptEither(completionStage2, consumer).exceptionally(errorHandler); 62 | 63 | verify(errorHandler, times(1)).apply(any(CompletionException.class)); 64 | finish(completionStage); 65 | 66 | verifyZeroInteractions(consumer); 67 | } 68 | 69 | @SuppressWarnings("unchecked") 70 | private Consumer mockConsumer() { 71 | return mock(Consumer.class); 72 | } 73 | 74 | @Test 75 | public void acceptEitherPropagatesExceptionFromFirstCompletable() { 76 | CompletionStage completionStage = createCompletionStage(EXCEPTION); 77 | CompletionStage completionStage2 = createCompletionStage(VALUE2); 78 | finish(completionStage); 79 | 80 | Consumer consumer = mockConsumer(); 81 | 82 | Function errorHandler = mockFunction(); 83 | completionStage.acceptEither(completionStage2, consumer).exceptionally(errorHandler); 84 | 85 | verify(errorHandler, times(1)).apply(any(CompletionException.class)); 86 | finish(completionStage2); 87 | 88 | verifyZeroInteractions(consumer); 89 | } 90 | 91 | @SuppressWarnings("unchecked") 92 | private Function mockFunction() { 93 | return mock(Function.class); 94 | } 95 | 96 | @Test 97 | public void acceptEitherPropagatesExceptionThrownByConsumerWhenProcessingFirstStage() { 98 | CompletionStage completionStage = createCompletionStage(VALUE); 99 | CompletionStage completionStage2 = createCompletionStage(VALUE2); 100 | finish(completionStage); 101 | 102 | Consumer consumer = s -> { 103 | throw EXCEPTION; 104 | }; 105 | 106 | 107 | Function errorHandler = mockFunction(); 108 | completionStage.acceptEither(completionStage2, consumer).exceptionally(errorHandler); 109 | 110 | verify(errorHandler, times(1)).apply(any(CompletionException.class)); 111 | finish(completionStage2); 112 | } 113 | 114 | @Test 115 | public void acceptEitherPropagatesExceptionThrownByConsumerWhenProcessingSecondStage() { 116 | CompletionStage completionStage = createCompletionStage(VALUE); 117 | CompletionStage completionStage2 = createCompletionStage(VALUE2); 118 | finish(completionStage2); 119 | 120 | Consumer consumer = s -> { 121 | throw EXCEPTION; 122 | }; 123 | 124 | 125 | Function errorHandler = mockFunction(); 126 | completionStage.acceptEither(completionStage2, consumer).exceptionally(errorHandler); 127 | 128 | verify(errorHandler, times(1)).apply(any(CompletionException.class)); 129 | finish(completionStage); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/test/java/net/javacrumbs/completionstage/CompletionStageFactoryTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2009-2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.javacrumbs.completionstage; 17 | 18 | import org.junit.Before; 19 | import org.junit.Test; 20 | import org.junit.runner.RunWith; 21 | import org.mockito.ArgumentCaptor; 22 | import org.mockito.Captor; 23 | import org.mockito.Mock; 24 | import org.mockito.runners.MockitoJUnitRunner; 25 | 26 | import java.util.concurrent.CompletableFuture; 27 | import java.util.concurrent.CompletionException; 28 | import java.util.concurrent.CompletionStage; 29 | import java.util.concurrent.ExecutionException; 30 | import java.util.concurrent.Executor; 31 | import java.util.concurrent.atomic.AtomicReference; 32 | import java.util.function.Consumer; 33 | import java.util.function.Supplier; 34 | 35 | import static org.assertj.core.api.Assertions.assertThat; 36 | import static org.junit.Assert.assertEquals; 37 | import static org.junit.Assert.assertFalse; 38 | import static org.junit.Assert.assertSame; 39 | import static org.junit.Assert.assertTrue; 40 | import static org.junit.Assert.fail; 41 | import static org.mockito.Matchers.any; 42 | import static org.mockito.Mockito.mock; 43 | import static org.mockito.Mockito.verify; 44 | import static org.mockito.Mockito.verifyZeroInteractions; 45 | import static org.mockito.Mockito.when; 46 | 47 | @RunWith(MockitoJUnitRunner.class) 48 | public class CompletionStageFactoryTest { 49 | 50 | public static final RuntimeException TEST_EXCEPTION = new RuntimeException("Test exception"); 51 | public static final String TEST_VALUE = "test"; 52 | private CompletionStageFactory factory; 53 | 54 | @Mock 55 | private Executor defaultExecutor; 56 | 57 | @Mock 58 | private Executor alternativeExecutor; 59 | 60 | @Mock 61 | private Supplier supplier; 62 | 63 | @Mock 64 | private Runnable runnable; 65 | 66 | @Mock 67 | private Consumer consumer; 68 | 69 | @Captor 70 | private ArgumentCaptor runnableCaptor; 71 | 72 | @Before 73 | public void before() { 74 | factory = new CompletionStageFactory(defaultExecutor); 75 | } 76 | 77 | @Test 78 | public void completedFutureTest() throws Exception { 79 | CompletionStage stage = factory.completedStage(TEST_VALUE); 80 | 81 | CompletableFuture future = stage.toCompletableFuture(); 82 | assertTrue(future.isDone()); 83 | assertEquals(TEST_VALUE, future.get()); 84 | } 85 | 86 | private static void executeCapturedRunnable(ArgumentCaptor runnableCaptor) { 87 | runnableCaptor.getValue().run(); 88 | } 89 | 90 | private void doSupplyAsyncTest(Executor executor, CompletionStage stage, Object supplier, String expectedResult) throws Exception { 91 | CompletableFuture future = doSupplyAsyncTestWithoutResultCheck(executor, stage, supplier); 92 | assertEquals(expectedResult, future.get()); 93 | } 94 | 95 | private CompletableFuture doSupplyAsyncTestWithoutResultCheck(Executor executor, CompletionStage stage, Object supplier) { 96 | CompletableFuture future = stage.toCompletableFuture(); 97 | 98 | // preconditions: 99 | assertFalse(future.isDone()); 100 | verifyZeroInteractions(supplier); 101 | 102 | verify(executor).execute(runnableCaptor.capture()); 103 | executeCapturedRunnable(runnableCaptor); 104 | 105 | assertTrue(future.isDone()); 106 | return future; 107 | } 108 | 109 | @Test 110 | public void supplyAsyncTest() throws Exception { 111 | when(supplier.get()).thenReturn(TEST_VALUE); 112 | 113 | CompletionStage stage = factory.supplyAsync(supplier); 114 | 115 | doSupplyAsyncTest(defaultExecutor, stage, supplier, TEST_VALUE); 116 | } 117 | 118 | @Test 119 | public void supplyAsyncWithExecutorTest() throws Exception { 120 | when(supplier.get()).thenReturn(TEST_VALUE); 121 | 122 | CompletionStage stage = factory.supplyAsync(supplier, alternativeExecutor); 123 | doSupplyAsyncTest(alternativeExecutor, stage, supplier, TEST_VALUE); 124 | 125 | // subsequent async method should use default executor again. 126 | stage.thenAcceptAsync(consumer); 127 | verify(defaultExecutor).execute(any(Runnable.class)); 128 | } 129 | 130 | @Test 131 | public void exceptionFromSupplierShouldBePropagated() throws Exception { 132 | CompletionStage stage = factory.supplyAsync(supplier, alternativeExecutor); 133 | 134 | checkExceptionThrown(stage); 135 | } 136 | 137 | /** 138 | * Cross check if the CompletableFuture has the same behavior. 139 | * 140 | * @throws Exception 141 | */ 142 | @Test 143 | public void exceptionFromSupplierShouldBePropagatedInCompletableFuture() throws Exception { 144 | checkExceptionThrown(CompletableFuture.supplyAsync(supplier, alternativeExecutor)); 145 | } 146 | 147 | protected void checkExceptionThrown(CompletionStage stage) { 148 | when(supplier.get()).thenThrow(TEST_EXCEPTION); 149 | 150 | AtomicReference exception = new AtomicReference<>(); 151 | stage.exceptionally(e -> { 152 | exception.set(e); 153 | return null; 154 | }); 155 | 156 | CompletableFuture future = doSupplyAsyncTestWithoutResultCheck(alternativeExecutor, stage, supplier); 157 | 158 | 159 | try { 160 | future.get(); 161 | fail("Exception expected"); 162 | } catch (Exception e) { 163 | assertThat(e).isInstanceOf(ExecutionException.class); 164 | assertSame(TEST_EXCEPTION, e.getCause()); 165 | } 166 | 167 | assertThat(exception.get()).isInstanceOf(CompletionException.class); 168 | assertSame(TEST_EXCEPTION, exception.get().getCause()); 169 | } 170 | 171 | 172 | @Test 173 | public void runAsyncTest() throws Exception { 174 | CompletionStage stage = factory.runAsync(runnable); 175 | 176 | doSupplyAsyncTest(defaultExecutor, stage, runnable, null); 177 | } 178 | 179 | @Test 180 | public void runAsyncWithExecutorTest() throws Exception { 181 | Executor executor = mock(Executor.class); 182 | 183 | CompletionStage stage = factory.runAsync(runnable, executor); 184 | 185 | doSupplyAsyncTest(executor, stage, runnable, null); 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /src/test/java/net/javacrumbs/completionstage/FinishedCompletableFutureTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2009-2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.javacrumbs.completionstage; 17 | 18 | import org.junit.Ignore; 19 | 20 | import java.util.concurrent.CompletableFuture; 21 | import java.util.concurrent.CompletionStage; 22 | 23 | /** 24 | * Tests CompletableFuture. Just to be sure I am reading the spec correctly. Same tests are executed on 25 | * CompletionStage and CompletableFuture. 26 | */ 27 | public class FinishedCompletableFutureTest extends AbstractCompletionStageTest { 28 | 29 | @Override 30 | protected CompletionStage createCompletionStage(String value) { 31 | CompletableFuture completableFuture = new CompletableFuture<>(); 32 | completableFuture.complete(value); 33 | return completableFuture; 34 | } 35 | 36 | @Override 37 | protected CompletionStage createCompletionStage(Throwable e) { 38 | CompletableFuture completableFuture = new CompletableFuture<>(); 39 | completableFuture.completeExceptionally(e); 40 | return completableFuture; 41 | } 42 | 43 | @Override 44 | protected void finish(CompletionStage completionStage) { 45 | 46 | } 47 | } -------------------------------------------------------------------------------- /src/test/java/net/javacrumbs/completionstage/FinishedCompletionStageFactoryTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2009-2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.javacrumbs.completionstage; 17 | 18 | import java.util.concurrent.CompletionStage; 19 | 20 | public class FinishedCompletionStageFactoryTest extends AbstractCompletionStageTest { 21 | private final CompletionStageFactory factory = new CompletionStageFactory(defaultExecutor); 22 | 23 | @Override 24 | protected CompletionStage createCompletionStage(String value) { 25 | return factory.completedStage(value); 26 | } 27 | 28 | @Override 29 | protected CompletionStage createCompletionStage(Throwable e) { 30 | CompletableCompletionStage completionStage = factory.createCompletionStage(); 31 | completionStage.completeExceptionally(e); 32 | return completionStage; 33 | } 34 | 35 | @Override 36 | protected void finish(CompletionStage completionStage) { 37 | 38 | } 39 | } -------------------------------------------------------------------------------- /src/test/java/net/javacrumbs/completionstage/SimpleCompletionStageTest.java: -------------------------------------------------------------------------------- 1 | package net.javacrumbs.completionstage; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.concurrent.CompletableFuture; 6 | import java.util.function.BiConsumer; 7 | 8 | import static org.mockito.Mockito.mock; 9 | import static org.mockito.Mockito.verify; 10 | 11 | public class SimpleCompletionStageTest { 12 | 13 | private static final String VALUE = "value"; 14 | private static final RuntimeException EXCEPTION = new RuntimeException("test"); 15 | 16 | private final CompletionStageFactory factory = new CompletionStageFactory(Runnable::run); 17 | 18 | @SuppressWarnings("unchecked") 19 | private final BiConsumer action = mock(BiConsumer.class); 20 | private final CompletableCompletionStage stage; 21 | 22 | public SimpleCompletionStageTest() { 23 | stage = factory.createCompletionStage(); 24 | stage.whenComplete(action); 25 | } 26 | 27 | @Test 28 | public void shouldCompleteUsingMethodReference() { 29 | CompletableFuture future = CompletableFuture.completedFuture(VALUE); 30 | future.whenComplete(stage::doComplete); 31 | 32 | verify(action).accept(VALUE, null); 33 | } 34 | 35 | @Test 36 | public void shouldCompleteExceptionallyUsingMethodReference() { 37 | CompletableFuture future = new CompletableFuture<>(); 38 | future.completeExceptionally(EXCEPTION); 39 | future.whenComplete(stage::doComplete); 40 | verify(action).accept(null, EXCEPTION); 41 | } 42 | 43 | @Test 44 | public void shouldHandleUsingMethodReference() { 45 | CompletableFuture future = CompletableFuture.completedFuture(VALUE); 46 | future.thenAccept(stage::doComplete); 47 | verify(action).accept(VALUE, null); 48 | } 49 | } -------------------------------------------------------------------------------- /src/test/java/net/javacrumbs/completionstage/UnfinishedCompletableFutureTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2009-2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.javacrumbs.completionstage; 17 | 18 | import java.util.concurrent.CompletableFuture; 19 | import java.util.concurrent.CompletionStage; 20 | import java.util.function.Consumer; 21 | 22 | /** 23 | * Tests CompletableFuture. Just to be sure I am reading the spec correctly. Same tests are executed on 24 | * CompletionStage and CompletableFuture. 25 | */ 26 | public class UnfinishedCompletableFutureTest extends AbstractUnfinishedCompletionStageTest { 27 | 28 | @Override 29 | protected CompletionStage createCompletionStage(String value) { 30 | return new DelayedCompletableFuture<>(c -> c.complete(value)); 31 | } 32 | 33 | @Override 34 | protected CompletionStage createCompletionStage(Throwable e) { 35 | return new DelayedCompletableFuture<>(c -> c.completeExceptionally(e)); 36 | } 37 | 38 | 39 | @Override 40 | protected void finish(CompletionStage c) { 41 | ((DelayedCompletableFuture) c).executeDelayedAction(); 42 | } 43 | 44 | private static class DelayedCompletableFuture extends CompletableFuture { 45 | private final Consumer> delayedAction; 46 | 47 | private DelayedCompletableFuture(Consumer> delayedAction) { 48 | this.delayedAction = delayedAction; 49 | } 50 | 51 | private void executeDelayedAction() { 52 | delayedAction.accept(this); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /src/test/java/net/javacrumbs/completionstage/UnfinishedCompletionStageFactoryTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2009-2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.javacrumbs.completionstage; 17 | 18 | import net.javacrumbs.completionstage.spi.CompletableCompletionStageFactory; 19 | 20 | import java.util.concurrent.CompletionStage; 21 | import java.util.concurrent.Executor; 22 | import java.util.function.Consumer; 23 | 24 | public class UnfinishedCompletionStageFactoryTest extends AbstractUnfinishedCompletionStageTest { 25 | 26 | @Override 27 | protected CompletionStage createCompletionStage(String value) { 28 | return new DelayedSimpleCompletionStage(c -> c.complete(value), defaultExecutor); 29 | } 30 | 31 | @Override 32 | protected CompletionStage createCompletionStage(Throwable e) { 33 | return new DelayedSimpleCompletionStage(c -> c.completeExceptionally(e), defaultExecutor); 34 | } 35 | 36 | @Override 37 | protected void finish(CompletionStage completionStage) { 38 | ((DelayedSimpleCompletionStage)completionStage).executeDelayedAction(); 39 | } 40 | 41 | private static class DelayedSimpleCompletionStage extends SimpleCompletionStage { 42 | private final Consumer delayedAction; 43 | 44 | private DelayedSimpleCompletionStage(Consumer delayedAction, Executor defaultExecutor) { 45 | super(defaultExecutor, new CompletionStageFactory(defaultExecutor)); 46 | this.delayedAction = delayedAction; 47 | } 48 | 49 | private void executeDelayedAction() { 50 | delayedAction.accept(this); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /src/test/java/net/javacrumbs/completionstage/completecompleted/AbstractCanYouCompleteAlreadyCompletedTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2009-2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.javacrumbs.completionstage.completecompleted; 17 | 18 | import net.javacrumbs.completionstage.CompletableCompletionStage; 19 | import org.junit.Test; 20 | 21 | import java.util.concurrent.ExecutionException; 22 | import java.util.function.BiFunction; 23 | 24 | import static org.junit.Assert.assertFalse; 25 | import static org.junit.Assert.assertTrue; 26 | import static org.mockito.Mockito.mock; 27 | import static org.mockito.Mockito.times; 28 | import static org.mockito.Mockito.verify; 29 | 30 | public abstract class AbstractCanYouCompleteAlreadyCompletedTest { 31 | 32 | protected static final String VALUE = "value"; 33 | protected static final String VALUE2 = "value2"; 34 | protected static final RuntimeException EXCEPTION = new RuntimeException(); 35 | 36 | protected abstract CompletableCompletionStage createCompletionStage(); 37 | 38 | @Test 39 | public void attemptToCompleteAlreadyCompletedShouldSilentlyIgnoreTheOtherValue() throws ExecutionException, InterruptedException { 40 | CompletableCompletionStage completable = createCompletionStage(); 41 | assertTrue(completable.complete(VALUE)); 42 | assertFalse(completable.complete(VALUE2)); 43 | 44 | BiFunction handler = createMockHandler(); 45 | completable.handle(handler); 46 | verify(handler, times(1)).apply(VALUE, null); 47 | } 48 | 49 | @Test 50 | public void attemptToCompleteExceptionallyAlreadyCompletedShouldSilentlyIgnoreTheException() throws ExecutionException, InterruptedException { 51 | CompletableCompletionStage completable = createCompletionStage(); 52 | assertTrue(completable.complete(VALUE)); 53 | assertFalse(completable.completeExceptionally(EXCEPTION)); 54 | 55 | BiFunction handler = createMockHandler(); 56 | completable.handle(handler); 57 | verify(handler, times(1)).apply(VALUE, null); 58 | } 59 | 60 | @Test 61 | public void attemptToCompleteAlreadyFailedShouldSilentlyIgnoreTheValue() throws ExecutionException, InterruptedException { 62 | CompletableCompletionStage completable = createCompletionStage(); 63 | assertTrue(completable.completeExceptionally(EXCEPTION)); 64 | assertFalse(completable.complete(VALUE)); 65 | 66 | 67 | BiFunction handler = createMockHandler(); 68 | completable.handle(handler); 69 | verify(handler, times(1)).apply(null, EXCEPTION); 70 | } 71 | 72 | @SuppressWarnings("unchecked") 73 | private BiFunction createMockHandler() { 74 | return mock(BiFunction.class); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/test/java/net/javacrumbs/completionstage/completecompleted/CompletableFutureCanYouCompleteCompletedTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2009-2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.javacrumbs.completionstage.completecompleted; 17 | 18 | import net.javacrumbs.completionstage.CompletableCompletionStage; 19 | 20 | import java.util.concurrent.CompletableFuture; 21 | 22 | public class CompletableFutureCanYouCompleteCompletedTest extends AbstractCanYouCompleteAlreadyCompletedTest { 23 | @Override 24 | protected CompletableCompletionStage createCompletionStage() { 25 | return new CompletableCompletableFuture<>(); 26 | } 27 | 28 | private static class CompletableCompletableFuture extends CompletableFuture implements CompletableCompletionStage { 29 | 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/net/javacrumbs/completionstage/completecompleted/CompletionStageCanYouCompleteCompletedTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2009-2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.javacrumbs.completionstage.completecompleted; 17 | 18 | import net.javacrumbs.completionstage.CompletableCompletionStage; 19 | import net.javacrumbs.completionstage.CompletionStageFactory; 20 | 21 | public class CompletionStageCanYouCompleteCompletedTest extends AbstractCanYouCompleteAlreadyCompletedTest { 22 | 23 | private final CompletionStageFactory completionStageFactory = new CompletionStageFactory(Runnable::run); 24 | 25 | @Override 26 | protected CompletableCompletionStage createCompletionStage() { 27 | return completionStageFactory.createCompletionStage(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/test/java/net/javacrumbs/completionstage/examples/CompletableFutureComposeTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2009-2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.javacrumbs.completionstage.examples; 17 | 18 | import org.junit.Test; 19 | 20 | import java.util.concurrent.CompletableFuture; 21 | 22 | public class CompletableFutureComposeTest { 23 | 24 | @Test 25 | public void completedAfter() { 26 | CompletableFuture future1 = new CompletableFuture<>(); 27 | CompletableFuture future2 = new CompletableFuture<>(); 28 | 29 | future1.thenCompose(x -> future2).whenComplete((r, e) -> System.out.println("After: " + e)); 30 | 31 | future1.complete("value"); 32 | future2.completeExceptionally(new RuntimeException()); 33 | } 34 | 35 | @Test 36 | public void completedBefore() { 37 | CompletableFuture future1 = new CompletableFuture<>(); 38 | CompletableFuture future2 = new CompletableFuture<>(); 39 | 40 | future1.complete("value"); 41 | future2.completeExceptionally(new RuntimeException()); 42 | 43 | future1.thenCompose(x -> future2).whenComplete((r, e) -> System.out.println("Before: " +e)); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/net/javacrumbs/completionstage/examples/LaunchTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2009-2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.javacrumbs.completionstage.examples; 17 | 18 | import org.junit.Test; 19 | 20 | import java.util.concurrent.CompletableFuture; 21 | 22 | public class LaunchTest { 23 | 24 | @Test 25 | public void testLaunch() { 26 | CompletableFuture authorizationCode = getAuthorizationCode(); 27 | authorizationCode 28 | .thenApply(this::launchMissiles) 29 | .thenApply(this::generateDamageReport) 30 | .thenAccept(this::updateMainScreen) 31 | .exceptionally(this::playAlertSound); 32 | } 33 | 34 | @Test 35 | public void testLaunchWithLavaLamp() { 36 | 37 | CompletableFuture missilesLaunched = 38 | getAuthorizationCode() 39 | .thenApply(this::launchMissiles); 40 | 41 | missilesLaunched 42 | .thenApply(this::generateDamageReport) 43 | .thenAccept(this::updateMainScreen) 44 | .exceptionally(this::playAlertSound); 45 | 46 | missilesLaunched.thenAccept(LavaLamp::turnOn); 47 | } 48 | 49 | private Void playAlertSound(Throwable throwable) { 50 | return null; 51 | } 52 | 53 | private void updateMainScreen(String s) { 54 | 55 | } 56 | 57 | private String generateDamageReport(LaunchResult result) { 58 | return null; 59 | } 60 | 61 | private LaunchResult launchMissiles(AuthorizationCode authorizationCode) { 62 | return null; 63 | } 64 | 65 | 66 | private CompletableFuture getAuthorizationCode() { 67 | return new CompletableFuture<>(); 68 | } 69 | 70 | private class AuthorizationCode { 71 | } 72 | 73 | private static class LavaLamp { 74 | public static void turnOn(LaunchResult aBoolean) { 75 | 76 | } 77 | } 78 | 79 | private class LaunchResult { 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/test/java/net/javacrumbs/completionstage/examples/SpringListenableFutureTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2009-2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.javacrumbs.completionstage.examples; 17 | 18 | import net.javacrumbs.completionstage.CompletableCompletionStage; 19 | import net.javacrumbs.completionstage.CompletionStageFactory; 20 | import org.junit.Test; 21 | import org.springframework.core.task.AsyncListenableTaskExecutor; 22 | import org.springframework.core.task.support.TaskExecutorAdapter; 23 | import org.springframework.util.concurrent.ListenableFuture; 24 | import org.springframework.util.concurrent.ListenableFutureCallback; 25 | 26 | public class SpringListenableFutureTest { 27 | private final AsyncListenableTaskExecutor executor = new TaskExecutorAdapter(Runnable::run); 28 | private final CompletionStageFactory factory = new CompletionStageFactory(Runnable::run); 29 | 30 | @Test 31 | public void testTransformationFromSpring() { 32 | ListenableFuture springListenableFuture = createSpringListenableFuture(); 33 | 34 | CompletableCompletionStage completionStage = factory.createCompletionStage(); 35 | springListenableFuture.addCallback(new ListenableFutureCallback() { 36 | @Override 37 | public void onSuccess(String result) { 38 | completionStage.complete(result); 39 | } 40 | 41 | @Override 42 | public void onFailure(Throwable t) { 43 | completionStage.completeExceptionally(t); 44 | } 45 | }); 46 | 47 | completionStage.thenAccept(System.out::println); 48 | } 49 | 50 | public ListenableFuture createSpringListenableFuture() { 51 | return executor.submitListenable(() -> "testValue"); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/test/java/net/javacrumbs/completionstage/load/LoadTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2009-2015 the original author or authors. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package net.javacrumbs.completionstage.load; 17 | 18 | import net.javacrumbs.completionstage.CompletableCompletionStage; 19 | import net.javacrumbs.completionstage.CompletionStageFactory; 20 | import org.junit.Ignore; 21 | import org.junit.Test; 22 | 23 | import java.util.List; 24 | import java.util.Random; 25 | import java.util.concurrent.CompletionStage; 26 | import java.util.concurrent.CopyOnWriteArrayList; 27 | import java.util.concurrent.ExecutionException; 28 | import java.util.concurrent.ExecutorService; 29 | import java.util.concurrent.Executors; 30 | import java.util.concurrent.Future; 31 | import java.util.concurrent.atomic.AtomicInteger; 32 | 33 | import static org.assertj.core.api.Assertions.assertThat; 34 | 35 | 36 | /** 37 | * Really naive load test, JMH anyone? 38 | */ 39 | public class LoadTest { 40 | private final ExecutorService executor = Executors.newFixedThreadPool(50); 41 | private final CompletionStageFactory factory = new CompletionStageFactory(Runnable::run); 42 | private final Random random = new Random(); 43 | private final List errors = new CopyOnWriteArrayList<>(); 44 | private final AtomicInteger processed = new AtomicInteger(); 45 | 46 | @Test 47 | @Ignore 48 | public void testLoad() throws InterruptedException, ExecutionException { 49 | for (int i = 0; i < 50_000_000; i++) { 50 | //testWithValueSetAtTheBeginning(); 51 | testWithValueSetAtTheEnd(); 52 | } 53 | assertThat(errors).isEmpty(); 54 | System.out.println(processed.get()); 55 | } 56 | 57 | private void testWithValueSetAtTheEnd() throws ExecutionException, InterruptedException { 58 | int value = getValue(); 59 | CompletionStage start = factory.createCompletionStage(); 60 | Future future = createStages(value, start).toCompletableFuture(); 61 | ((CompletableCompletionStage)start).complete(value); 62 | future.get(); 63 | } 64 | 65 | private void testWithValueSetAtTheBeginning() throws ExecutionException, InterruptedException { 66 | int value = getValue(); 67 | CompletionStage start = completedStage(value); 68 | Future future = createStages(value, start).toCompletableFuture(); 69 | future.get(); 70 | } 71 | 72 | private int getValue() { 73 | return random.nextInt(Integer.MAX_VALUE / 3); 74 | } 75 | 76 | private CompletionStage createStages(int value, CompletionStage start) { 77 | return start 78 | .thenApplyAsync(i -> i * 2) 79 | .thenCombine(completedStage(50), (a, b) -> a + b) 80 | .thenApply(i -> i / 2) 81 | .thenApply(i -> i - value) 82 | .thenAccept(i -> { 83 | processed.incrementAndGet(); 84 | if (i != 25) { 85 | errors.add("Error from " + value + " result " + i); 86 | } 87 | }); 88 | } 89 | 90 | private CompletionStage completedStage(T value) { 91 | return factory.completedStage(value); 92 | } 93 | } 94 | --------------------------------------------------------------------------------