├── .gitignore ├── .travis.yml ├── License ├── README.md ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── settings.gradle └── src ├── main └── java │ └── io │ └── advantageous │ └── reakt │ ├── Breaker.java │ ├── Callback.java │ ├── CallbackHandler.java │ ├── Expected.java │ ├── Invokable.java │ ├── Observer.java │ ├── PromiseSupplier.java │ ├── Result.java │ ├── Stream.java │ ├── StreamResult.java │ ├── exception │ ├── RejectedPromiseException.java │ ├── RejectedStreamException.java │ ├── ResultFailedException.java │ └── ThenHandlerException.java │ ├── impl │ ├── BreakerImpl.java │ ├── ExpectedImpl.java │ ├── ResultImpl.java │ └── StreamResultImpl.java │ ├── promise │ ├── Promise.java │ ├── PromiseHandler.java │ ├── Promises.java │ ├── ReplayPromise.java │ └── impl │ │ ├── AllBlockingPromise.java │ │ ├── AllPromise.java │ │ ├── AllReplayPromise.java │ │ ├── AnyBlockingPromise.java │ │ ├── AnyPromise.java │ │ ├── AnyReplayPromise.java │ │ ├── BasePromise.java │ │ ├── BlockingPromise.java │ │ ├── FinalPromise.java │ │ ├── InvokerPromise.java │ │ ├── PromiseUtil.java │ │ └── ReplayPromiseImpl.java │ └── reactor │ ├── Reactor.java │ ├── TimeSource.java │ └── impl │ └── ReactorImpl.java └── test └── java └── io └── advantageous └── reakt ├── BreakerTest.java ├── CallbackTest.java ├── ExpectedTest.java ├── ResultTest.java ├── StreamTest.java ├── impl └── ExpectedImplTest.java ├── promise ├── InvokablePromiseTest.java └── PromiseTest.java └── reactor └── impl ├── ReactorImplTest.java └── TestTimer.java /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle/ 2 | .idea/ 3 | build/ 4 | gradlew.bat 5 | 6 | reakt.iml 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk8 4 | -------------------------------------------------------------------------------- /License: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2015 Richard Hightower and Geoff Chandler 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # reakt [![Build Status](https://travis-ci.org/advantageous/reakt.svg)](https://travis-ci.org/advantageous/reakt) [![Join the chat at https://gitter.im/advantageous/reakt](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/advantageous/reakt?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 2 | 3 | [Reakt website](http://advantageous.github.io/reakt) 4 | 5 | ***Reactive interfaces for Java.*** 6 | 7 | Reakt is reactive interfaces for Java which includes: 8 | * [Promises](https://github.com/advantageous/reakt/wiki/Promise), 9 | * [Streams](https://github.com/advantageous/reakt/wiki/Stream), 10 | * [Callbacks](https://github.com/advantageous/reakt/wiki/Callback), 11 | * [Async Results](https://github.com/advantageous/reakt/wiki/Result) with [Expected](https://github.com/advantageous/reakt/wiki/Expected) 12 | * [Circuit Breakers](https://github.com/advantageous/reakt/wiki/Breaker) 13 | 14 | The emphasis is on defining interfaces that enable lambda expressions, 15 | and fluent APIs for asynchronous programming for Java. 16 | 17 | Note: This mostly just provides the interfaces not the implementations. There are some starter implementations but the idea is that anyone can implement this. It is all about interfaces. There will be adapters for Vertx, RxJava, Reactive Streams, etc. There is support for ***Guava Async*** (used by Cassandra) and the ***QBit*** microservices lib. [Elekt](http://advantageous.github.io/elekt/) uses Reakt for its reactive leadership election. 18 | 19 | 20 | 21 | ## Have a question? 22 | [Reakt Mailing List](https://groups.google.com/forum/#!forum/reakt) 23 | 24 | ## Getting started 25 | #### Using from maven 26 | 27 | Reakt is published in the [maven public repo](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22io.advantageous.reakt%22). 28 | 29 | ```xml 30 | 31 | io.advantageous.reakt 32 | reakt 33 | 3.0.4.RELEASE 34 | 35 | ``` 36 | 37 | #### Using from gradle 38 | ```xml 39 | compile 'io.advantageous.reakt:reakt:3.0.4.RELEASE' 40 | ``` 41 | 42 | #### Fluent Promise API 43 | ```java 44 | employeeService.lookupEmployee(33) 45 | .then(e -> saveEmployee(e)) 46 | .catchError(error -> logger.error("Unable to lookup employee", error)) 47 | .invoke(); 48 | ``` 49 | 50 | 51 | 52 | Promises are both a callback and a Result; however, you can work 53 | with Callbacks directly. 54 | 55 | #### Using Result and callback directly 56 | ```java 57 | employeeService.lookupEmployee(33, result -> { 58 | result.then(e -> saveEmployee(e)) 59 | .catchError(error -> logger.error("Unable to lookup", error)); 60 | }); 61 | ``` 62 | 63 | In both of these examples, lookupEmployee would look like: 64 | 65 | #### Using Result and callback directly 66 | ```java 67 | 68 | public void lookupEmployee(long employeeId, Callback callback){...} 69 | 70 | ``` 71 | 72 | You can use Promises to transform into other promises. 73 | 74 | #### Transforming into another type of promise using thenMap 75 | ```java 76 | 77 | Promise employeePromise = Promises.blockingPromise(); 78 | 79 | Promise sheepPromise = employeePromise 80 | .thenMap(employee1 -> new Sheep(employee1.getId())); 81 | ``` 82 | 83 | The `thenMap` will return a new type of Promise. 84 | 85 | You can find more examples in the [reakt wiki](https://github.com/advantageous/reakt/wiki). 86 | 87 | We also support working with streams. 88 | 89 | 90 | ## Promise concepts 91 | 92 | This has been adapted from this [article on ES6 promises](http://www.html5rocks.com/en/tutorials/es6/promises/). 93 | A promise can be: 94 | 95 | * fulfilled The callback/action relating to the promise succeeded 96 | * rejected The callback/action relating to the promise failed 97 | * pending The callback/action has not been fulfilled or rejected yet 98 | * completed The callback/action has been fulfilled/resolved or rejected 99 | 100 | Java is not single threaded, meaning that two bits of code can run at 101 | the same time, so the design of this promise and streaming library takes 102 | that into account. 103 | 104 | There are three types of promises: 105 | * Callback promises 106 | * Blocking promises (for testing and legacy integration) 107 | * Replay promises (allow promises to be handled on the same thread as caller) 108 | 109 | Replay promises are the most like their JS cousins. Replay promises are usually 110 | managed by the Reakt `Reactor` and supports environments like Vert.x and QBit. 111 | See the wiki for more details on Replay promises. 112 | 113 | It is common to make async calls to store data in 114 | a NoSQL store or to call a remote REST interface or deal with a 115 | distributed cache or queue. Also Java is strongly typed so the library 116 | that mimics JS promises is going to look a bit different. We tried to 117 | use similar terminology where it makes sense. 118 | 119 | Events and Streams are great for things that can happen multiple times 120 | on the same object — keyup, touchstart, or event a 121 | user action stream from Kafka, etc. 122 | 123 | With those events you don't really care about what happened before 124 | when you attached the listener. 125 | 126 | But often times when dealing with services and data repositories, 127 | you want to handle a response with a specific next action, 128 | and a different action if there was an error 129 | or timeout from the responses. You essentially want to call and handle 130 | a response asynchronously and that is what promises allow. 131 | 132 | This is not our first time to bat with Promises. QBit has had Promises for 133 | a few years now. We just called them CallbackBuilders instead. 134 | We wanted to use more standard terminology and wanted to use the same 135 | terminology and modeling on projects that do not use QBit like Conekt, 136 | Vert.x, RxJava, and reactive streams. 137 | 138 | At their most basic level, promises are like event listeners except: 139 | 140 | A promise can only succeed or fail once. A promise cannot succeed or 141 | fail twice, neither can it switch from 142 | success to failure. Once it enters its `completed` state, then it is done. 143 | 144 | 145 | 146 | ## Bridges 147 | 148 | [Reakt Guava Bridge](http://advantageous.github.io/reakt-guava/) which 149 | allows libs that use Guava async support to now have a modern Java feel. 150 | 151 | 152 | #### Cassandra Reakt example 153 | 154 | ```java 155 | 156 | register(session.executeAsync("SELECT release_version FROM system.local"), 157 | promise().thenExpect(expected -> 158 | gui.setMessage("Cassandra version is " + 159 | expected.get().one().getString("release_version")) 160 | ).catchError(error -> 161 | gui.setMessage("Error while reading Cassandra version: " 162 | + error.getMessage()) 163 | ) 164 | ); 165 | 166 | ``` 167 | 168 | ***QBit 1*** ships with a bridge and ***QBit 2***will use ***Reakt*** as its 169 | primary reactive callback mechanism. 170 | 171 | ***Conekt***, a slimmed down fork of Vert.x, will also use ***Reakt***. 172 | 173 | See [QBit](https://github.com/advantageous/qbit) microservices lib 174 | for more details. 175 | 176 | See our wiki for more details on [Reakt](https://github.com/advantageous/reakt/wiki). 177 | 178 | 179 | ## Further reading 180 | 181 | [What is Microservices Architecture?](http://www.mammatustech.com/microservices-architecture) 182 | 183 | [QBit Java Micorservices lib tutorials](https://github.com/MammatusTech/qbit-microservices-examples/wiki) 184 | 185 | The Java microservice lib. QBit is a reactive programming lib for building microservices - JSON, HTTP, WebSocket, and REST. QBit uses reactive programming to build elastic REST, and WebSockets based cloud friendly, web services. SOA evolved for mobile and cloud. ServiceDiscovery, Health, reactive StatService, events, Java idiomatic reactive programming for Microservices. 186 | 187 | [Find more tutorial on QBit](https://github.com/MammatusTech/qbit-microservices-examples/wiki). 188 | 189 | 190 | [Reactive Programming](http://rick-hightower.blogspot.com/2015/03/reactive-programming-service-discovery.html), [Java Microservices](http://rick-hightower.blogspot.com/2015/03/java-microservices-architecture.html), [Rick Hightower](http://www.linkedin.com/in/rickhigh) 191 | 192 | 193 | 194 | [High-speed microservices consulting firm and authors of QBit with lots of experience with Vertx - Mammatus Technology](http://www.mammatustech.com/) 195 | 196 | [Highly recommended consulting and training firm who specializes in microservices architecture and mobile development that are already very familiar with QBit and Vertx as well as iOS and Android - About Objects](http://www.aboutobjects.com/) 197 | 198 | [Java Microservices Architecture](http://www.mammatustech.com/java-microservices-architecture) 199 | 200 | [Microservice Service Discovery with Consul] (http://www.mammatustech.com/Microservice-Service-Discovery-with-Consul) 201 | 202 | [Microservices Service Discovery Tutorial with Consul](http://www.mammatustech.com/consul-service-discovery-and-health-for-microservices-architecture-tutorial) 203 | 204 | [Reactive Microservices] 205 | (http://www.mammatustech.com/reactive-microservices) 206 | 207 | [High Speed Microservices] 208 | (http://www.mammatustech.com/high-speed-microservices) 209 | 210 | [Java Microservices Consulting](http://www.mammatustech.com/java-microservices-consulting) 211 | 212 | [Microservices Training](http://www.mammatustech.com/java-reactive-microservice-training) 213 | 214 | 215 | [Reactive Microservices Tutorial, using the Reactor] 216 | (https://github.com/MammatusTech/qbit-microservices-examples/wiki/Reactor-tutorial--%7C-reactively-handling-async-calls-with-QBit-Reactive-Microservices) 217 | 218 | [QBit is mentioned in the Restlet blog](http://restlet.com/blog/2015/09/04/this-week-in-api-land-20/) 219 | 220 | [All code is written using JetBrains Idea - the best IDE ever!](https://www.jetbrains.com/idea/) 221 | 222 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | group 'io.advantageous.reakt' 20 | version '4.0.1' 21 | 22 | apply plugin: 'java' 23 | apply plugin: 'maven' 24 | apply plugin: 'signing' 25 | apply plugin: 'idea' 26 | 27 | 28 | 29 | task wrapper(type: Wrapper) { 30 | gradleVersion = '2.11' 31 | } 32 | 33 | test.onlyIf { !Boolean.getBoolean('skip.tests') } 34 | 35 | 36 | repositories { 37 | mavenCentral() 38 | } 39 | 40 | sourceCompatibility = JavaVersion.VERSION_1_8 41 | targetCompatibility = JavaVersion.VERSION_1_8 42 | 43 | dependencies { 44 | testCompile group: 'junit', name: 'junit', version: '4.11' 45 | } 46 | 47 | task javadocJar(type: Jar, dependsOn: javadoc) { 48 | classifier = 'javadoc' 49 | from 'build/docs/javadoc' 50 | } 51 | 52 | task sourcesJar(type: Jar) { 53 | from sourceSets.main.allSource 54 | classifier = 'sources' 55 | } 56 | 57 | artifacts { 58 | archives jar 59 | archives javadocJar 60 | archives sourcesJar 61 | } 62 | 63 | signing { 64 | required false 65 | sign configurations.archives 66 | } 67 | 68 | 69 | 70 | uploadArchives { 71 | repositories { 72 | mavenDeployer { 73 | beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } 74 | repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") { 75 | try { 76 | authentication(userName: sonatypeUsername, password: sonatypePassword) 77 | } catch (MissingPropertyException ignore) { 78 | } 79 | } 80 | 81 | pom.project { 82 | packaging 'jar' 83 | name project.name 84 | description "Reactive interfaces. Promises, Streams, Callbacks, Async results, interfaces for Java that are lambda expression friendly and fluent." 85 | 86 | url 'https://github.com/advantageous/reakt' 87 | 88 | scm { 89 | url 'scm:git@github.com:advantageous/reakt.git' 90 | connection 'scm:git@github.com:advantageous/reakt.git' 91 | developerConnection 'scm:git@github.com:advantageous/reakt.git' 92 | } 93 | 94 | licenses { 95 | license { 96 | name 'The Apache Software License, Version 2.0' 97 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt' 98 | distribution 'repo' 99 | } 100 | } 101 | 102 | developers { 103 | developer { 104 | id 'richardHightower' 105 | name 'Richard Hightower' 106 | } 107 | developer { 108 | id 'sailorgeoffrey' 109 | name 'Geoffrey Chandler' 110 | } 111 | } 112 | } 113 | } 114 | } 115 | } 116 | 117 | 118 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/advantageous/reakt/da3f2a704983bf4fb57341af87ad7a392bbd0ee0/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | # 18 | #Tue Apr 05 16:58:33 PDT 2016 19 | distributionBase=GRADLE_USER_HOME 20 | distributionPath=wrapper/dists 21 | zipStoreBase=GRADLE_USER_HOME 22 | zipStorePath=wrapper/dists 23 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.11-bin.zip 24 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | rootProject.name = 'reakt' 20 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/reakt/Breaker.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package io.advantageous.reakt; 20 | 21 | import io.advantageous.reakt.impl.BreakerImpl; 22 | 23 | import java.util.function.Consumer; 24 | import java.util.function.Predicate; 25 | 26 | 27 | /** 28 | * Represents a Circuit Breaker. 29 | * The contained service can be broken (open circuit) or operational (closed circuit). 30 | *

31 | * This represents a service which may or may not be available. 32 | *

33 | * We were using Expected a lot where we really wanted something like a Breaker. 34 | *

35 | * This could be extended to blow the circuit with different conditions by providing 36 | * your own Breaker. 37 | *

38 | *

39 | * Also we want to use interfaces for all core concepts. 40 | *

41 | * In addition we wanted callback for ifBroken and ifOperational. 42 | *

43 | * If a service is active and healthy, {@code isOperational()} will return {@code true}. 44 | * If a service is not healthy or not working, {code isBroken()} will return {@code true}. 45 | *

46 | * This is heavily modeled after {@code Expected} optional. 47 | * 48 | * @author Rick Hightower 49 | * @author Geoff Chandler 50 | */ 51 | public interface Breaker { 52 | /** 53 | * Common instance for {@code broken()}. 54 | */ 55 | Breaker OPENED = new BreakerImpl<>(); 56 | 57 | /** 58 | * Returns an open {@code Breaker} instance. No service is present for this 59 | * value. 60 | * 61 | * @param Type of the non-existent value 62 | * @return an empty {@code ExpectedImpl} 63 | */ 64 | static Breaker broken() { 65 | return opened(); 66 | } 67 | 68 | /** 69 | * Returns an open/broken {@code Breaker} instance. No service is present for this 70 | * value. 71 | * 72 | * @param Type of the non-existent value 73 | * @return an empty {@code ExpectedImpl} 74 | */ 75 | static Breaker opened() { 76 | @SuppressWarnings("unchecked") 77 | Breaker t = OPENED; 78 | return t; 79 | } 80 | 81 | /** 82 | * Returns an {@code Breaker} using the specified present value, which must not be null. 83 | * 84 | * @param the class of the value 85 | * @param value the value to be present. Must be non-null 86 | * @return an {@code ExpectedImpl} with the value present 87 | * @throws NullPointerException if value is null 88 | */ 89 | static Breaker operational(T value) { 90 | return new BreakerImpl<>(value); 91 | } 92 | 93 | /** 94 | * Returns an {@code Breaker} using the specified present value, which must not be null. 95 | * 96 | * @param the class of the value 97 | * @param value the value to be present. Must be non-null 98 | * @param maxErrorsCount max error count 99 | * @return an {@code ExpectedImpl} with the value present 100 | * @throws NullPointerException if value is null 101 | */ 102 | static Breaker operational(T value, final int maxErrorsCount) { 103 | return new BreakerImpl<>(value, maxErrorsCount); 104 | } 105 | 106 | 107 | /** 108 | * Returns an {@code Breaker} using the specified present value, which must not be null. 109 | * 110 | * @param the class of the value 111 | * @param value the value to be present. Must be non-null 112 | * @param maxErrorsCount max error count 113 | * @param brokenPredicate supplier to determine if connection to service is broken 114 | * @return an {@code ExpectedImpl} with the value present 115 | * @throws NullPointerException if value is null 116 | */ 117 | static Breaker operational(T value, final int maxErrorsCount, final Predicate brokenPredicate) { 118 | return new BreakerImpl<>(value, maxErrorsCount, brokenPredicate); 119 | } 120 | 121 | 122 | /** 123 | * Returns an {@code Breaker} using the specified present value, which must not be null. 124 | * 125 | * @param the class of the value 126 | * @param value the value to be present. Must be non-null 127 | * @param brokenPredicate predicate to determine if connection to service is broken 128 | * @return an {@code ExpectedImpl} with the value present 129 | * @throws NullPointerException if value is null 130 | */ 131 | static Breaker operational(T value, final Predicate brokenPredicate) { 132 | return new BreakerImpl<>(value, 0, brokenPredicate); 133 | } 134 | 135 | /** 136 | * x 137 | * Return {@code true} if the service is broken, otherwise {@code false}. 138 | * 139 | * @return {@code true} if the service is broken, otherwise {@code false} 140 | */ 141 | boolean isBroken(); 142 | 143 | /** 144 | * Return {@code true} if the service is working, otherwise {@code false}. 145 | * 146 | * @return {@code true} if the service is working, otherwise {@code false}. 147 | */ 148 | boolean isOperational(); 149 | 150 | /** 151 | * Short version of isOperational. 152 | * 153 | * @return ok 154 | */ 155 | default boolean isOk() { 156 | return isOperational(); 157 | } 158 | 159 | /** 160 | * If a service is beleived to be working, invoke the consumer with the value. 161 | *

162 | * This tracks errors thrown by the consumer. 163 | * 164 | * @param consumer executed if a value is present 165 | * @return this, fluent API 166 | * @throws NullPointerException if value is present and {@code consumer} is 167 | * null 168 | */ 169 | Breaker ifOperational(Consumer consumer); 170 | 171 | /** 172 | * Short version of ifOperational. 173 | * If a service is beleived to be working, invoke the consumer with the value. 174 | * 175 | * @param consumer executed if a value is present 176 | * @return this, fluent API 177 | * @throws NullPointerException if value is present and {@code consumer} is 178 | * null 179 | */ 180 | default Breaker ifOk(Consumer consumer) { 181 | return ifOperational(consumer); 182 | } 183 | 184 | /** 185 | * If a service is broken, invoke the runnable. 186 | * 187 | * @param runnable executed if a value is not present 188 | * @return this, fluent API 189 | */ 190 | Breaker ifBroken(Runnable runnable); 191 | 192 | /** 193 | * If a service is broken but present, invoke the consumer. 194 | * This is used to do clean up, like closing a connection. 195 | * 196 | * @param consumer executed if a value is not present 197 | * @return this, fluent API 198 | */ 199 | Breaker cleanup(Consumer consumer); 200 | 201 | /** 202 | * @return number of errors detected. 203 | */ 204 | long errorCount(); 205 | 206 | } 207 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/reakt/Callback.java: -------------------------------------------------------------------------------- 1 | package io.advantageous.reakt; 2 | 3 | 4 | /** 5 | * Simplified interface to a callback, hiding a callbacks hierarchy and dual roles. 6 | * This is the service view of a callback. 7 | * This focuses on just resolution methods of a callback. 8 | */ 9 | public interface Callback { 10 | 11 | /** 12 | * (Service view) 13 | * This allows services to send back a failed result easily to the client/handler. 14 | *

15 | * This is a helper methods for producers (services that produce results) to send a failed result. 16 | * 17 | * @param error error 18 | */ 19 | void reject(final Throwable error); 20 | 21 | 22 | /** 23 | * (Service view) 24 | * This allows services to send back a failed result easily to the client/handler. 25 | *

26 | * This is a helper methods for producers (services that produce results) to send a failed result. 27 | * 28 | * @param errorMessage error message 29 | */ 30 | void reject(final String errorMessage); 31 | 32 | 33 | /** 34 | * (Service view) 35 | * This allows services to send back a failed result easily to the client/handler. 36 | *

37 | * This is a helper methods for producers (services that produce results) to send a failed result. 38 | * 39 | * @param errorMessage error message 40 | * @param error exception 41 | */ 42 | void reject(final String errorMessage, final Throwable error); 43 | 44 | 45 | /** 46 | * Calls replayDone, for VOID callback only. ES6 promise style. 47 | */ 48 | void resolve(); 49 | 50 | /** 51 | * Resolve resolves a promise. 52 | * 53 | * @param result makes it more compatible with ES6 style promises 54 | */ 55 | void resolve(final T result); 56 | 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/reakt/CallbackHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package io.advantageous.reakt; 20 | 21 | import io.advantageous.reakt.exception.RejectedPromiseException; 22 | import io.advantageous.reakt.impl.ResultImpl; 23 | 24 | import java.util.function.Consumer; 25 | 26 | import static io.advantageous.reakt.Result.doneResult; 27 | 28 | /** 29 | * A generic event handler which can be thought of as a callback handler. 30 | *

31 | * This is like an async future or promise. 32 | *

33 | * This was modeled after QBit's callback, and JavaScripts callbacks. 34 | * The {@link Result} result represents the result or error from an async operation. 35 | *

36 | * A {@code CallbackHandler} is a {@code Consumer} and can be used anywhere a consumer is used. 37 | * This is for easy integration with non-Reakt libs and code bases. 38 | *

39 | * 40 | * @param type of result returned from callback 41 | * @author Rick Hightower 42 | * @author Geoff Chandler 43 | */ 44 | public interface CallbackHandler extends Consumer, Callback { 45 | 46 | /** 47 | * (Client view) 48 | * A result was returned so handle it. 49 | *

50 | * This is registered from the callers (or event receivers perspective). 51 | * A client of a service would override {@code onResult}. 52 | * 53 | * @param result to handle 54 | */ 55 | void onResult(Result result); 56 | 57 | 58 | /** 59 | * (Service view) 60 | * This allows services to send back a failed result easily to the client/handler. 61 | *

62 | * This is a helper methods for producers (services that produce results) to send a failed result. 63 | * 64 | * @param error error 65 | */ 66 | default void reject(final Throwable error) { 67 | onResult(new ResultImpl<>(error)); 68 | } 69 | 70 | 71 | /** 72 | * (Service view) 73 | * This allows services to send back a failed result easily to the client/handler. 74 | *

75 | * This is a helper methods for producers (services that produce results) to send a failed result. 76 | * 77 | * @param errorMessage error message 78 | */ 79 | default void reject(final String errorMessage) { 80 | reject(new RejectedPromiseException(errorMessage)); 81 | } 82 | 83 | 84 | /** 85 | * (Service view) 86 | * This allows services to send back a failed result easily to the client/handler. 87 | *

88 | * This is a helper methods for producers (services that produce results) to send a failed result. 89 | * 90 | * @param errorMessage error message 91 | * @param error exception 92 | */ 93 | default void reject(final String errorMessage, final Throwable error) { 94 | reject(new RejectedPromiseException(errorMessage, error)); 95 | } 96 | 97 | /** 98 | * Calls replayDone, for VOID callback only. ES6 promise style. 99 | */ 100 | @SuppressWarnings("unused") 101 | default void resolve() { 102 | onResult((Result) doneResult()); 103 | } 104 | 105 | /** 106 | * Resolve resolves a promise. 107 | * 108 | * @param result makes it more compatible with ES6 style promises 109 | */ 110 | @SuppressWarnings("unused") 111 | default void resolve(final T result) { 112 | onResult(new ResultImpl<>(result)); 113 | } 114 | 115 | /** 116 | * Bridge between Consumer world and CallbackHandler world 117 | * Performs this operation on the given argument. 118 | * 119 | * @param t the input argument 120 | */ 121 | @Override 122 | default void accept(T t) { 123 | resolve(t); 124 | } 125 | 126 | /** 127 | * Used to convert the error handling of the callback or promise 128 | * into a Consumer so you can easily integrate with non-Reakt code. 129 | * 130 | * @return Consumer version of error handling. 131 | */ 132 | default Consumer errorConsumer() { 133 | return this::reject; 134 | } 135 | 136 | /** 137 | * Used to easily cast this callback to a consumer. 138 | * 139 | * @return Consumer version of this callback. 140 | */ 141 | default Consumer consumer() { 142 | return this; 143 | } 144 | 145 | 146 | } 147 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/reakt/Expected.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package io.advantageous.reakt; 20 | 21 | import io.advantageous.reakt.impl.ExpectedImpl; 22 | 23 | import java.util.NoSuchElementException; 24 | import java.util.Optional; 25 | import java.util.function.Consumer; 26 | import java.util.function.Function; 27 | import java.util.function.Predicate; 28 | 29 | /** 30 | * Same concept as Optional in Java JDK and Option in Scala. 31 | * We added a new concept because this one is expected to come through callbacks and 32 | * is used in places where Optional does not make sense. 33 | *

34 | * Also we want to use interfaces for all core concepts. 35 | *

36 | * In addition we wanted callback for ifPresent and ifEmpty. 37 | *

38 | * Contains an value object which may not be set. This is like {@code Optional} but could be the value from an async operation 39 | * which sent a null. 40 | *

41 | * If a value is present, {@code isPresent()} will return {@code true} and 42 | * {@code get()} will return the value. 43 | *

44 | * This is heavily modeled after {@link java.util.Optional} optional. 45 | * 46 | * @author Rick Hightower 47 | * @author Geoff Chandler 48 | */ 49 | public interface Expected { 50 | /** 51 | * Common instance for {@code empty()}. 52 | */ 53 | Expected EMPTY = new ExpectedImpl<>(); 54 | 55 | /** 56 | * Returns an empty {@code ExpectedImpl} instance. No value is present for this 57 | * value. 58 | * 59 | * @param Type of the non-existent value 60 | * @return an empty {@code ExpectedImpl} 61 | */ 62 | static Expected empty() { 63 | @SuppressWarnings("unchecked") 64 | final Expected t = EMPTY; 65 | return t; 66 | } 67 | 68 | /** 69 | * Returns an {@code ExpectedImpl} using the specified present value, which must not be null. 70 | * 71 | * @param the class of the value 72 | * @param value the value to be present. Must be non-null 73 | * @return an {@code ExpectedImpl} with the value present 74 | * @throws NullPointerException if value is null 75 | */ 76 | static Expected of(T value) { 77 | return new ExpectedImpl<>(value); 78 | } 79 | 80 | 81 | /** 82 | * Returns an {@code ExpectedImpl} using the specified present value, which must not be null. 83 | * 84 | * @param the class of the value 85 | * @param value the value to be present. Must be non-null 86 | * @return an {@code ExpectedImpl} with the value present 87 | * @throws NullPointerException if value is null 88 | */ 89 | static Expected expected(T value) { 90 | return new ExpectedImpl<>(value); 91 | } 92 | 93 | /** 94 | * Returns an {@code ExpectedImpl} describing the specified value, if non-null, 95 | * otherwise returns an empty {@code ExpectedImpl}. 96 | * 97 | * @param the class of the value 98 | * @param value the possibly non-existent value 99 | * @return an {@code ExpectedImpl} with a present value if the specified value 100 | * is non-null, otherwise an empty {@code Optional} 101 | */ 102 | static Expected ofNullable(T value) { 103 | return value == null ? empty() : of(value); 104 | } 105 | 106 | /** 107 | * Returns an {@code ExpectedImpl} describing the specified value, if non-null, 108 | * otherwise returns an empty {@code ExpectedImpl}. 109 | * 110 | * @param the class of the value 111 | * @param value the possibly non-existent value 112 | * @return an {@code ExpectedImpl} with a present value if the specified value 113 | * is non-null, otherwise an empty {@code Optional} 114 | */ 115 | static Expected expectedNullable(T value) { 116 | return value == null ? empty() : of(value); 117 | } 118 | 119 | 120 | /** 121 | * Returns an {@code ExpectedImpl} describing the specified value, if non-null, 122 | * otherwise returns an empty {@code ExpectedImpl}. 123 | * 124 | * @param the class of the value 125 | * @param value the possibly non-existent value 126 | * @return an {@code ExpectedImpl} with a present value if the specified value 127 | * is not empty, otherwise an empty {@code Optional} 128 | */ 129 | static Expected ofOptional(final Optional value) { 130 | return !value.isPresent() ? empty() : of(value.get()); 131 | } 132 | 133 | 134 | /** 135 | * If a value is not present (null), invoke the runnable. 136 | * 137 | * @return returns true if value is null 138 | */ 139 | boolean isAbsent(); 140 | 141 | /** 142 | * If a value is not present (null), invoke the runnable. 143 | * 144 | * @param runnable executed if a value is not present 145 | * @return self, fluent 146 | */ 147 | Expected ifAbsent(final Runnable runnable); 148 | 149 | /** 150 | * If a value is present in this {@code Expected}, returns the value, 151 | * otherwise throws {@code NoSuchElementException}. 152 | * 153 | * @return the value held by this {@code Expected} 154 | * @throws NoSuchElementException if there is no value present 155 | * @see Expected#isPresent() 156 | */ 157 | T get(); 158 | 159 | /** 160 | * Return {@code true} if there is a value present, otherwise {@code false}. 161 | * 162 | * @return {@code true} if there is a value present, otherwise {@code false} 163 | */ 164 | boolean isPresent(); 165 | 166 | 167 | /** 168 | * If a value is not empty. See {@link Expected#ifEmpty(Runnable)}, and {@link Expected#isEmpty()} 169 | * for more details. 170 | * 171 | * @param consumer executed if a value is not present 172 | * @return fluent, this 173 | */ 174 | Expected ifNotEmpty(Consumer consumer); 175 | 176 | /** 177 | * If a value is not present or present and empty (empty check only works with 178 | * collection, string, charSequence size/length 0). 179 | * return true. 180 | *

181 | * If you just want a null check, use {@code isAbsent}. 182 | * 183 | * @return {@code true} if there is not a value present or present and empty, 184 | * otherwise {@code false} 185 | */ 186 | boolean isEmpty(); 187 | 188 | 189 | /** 190 | * If a value is not present or present and empty (collection, string, charSequence size/length 0), 191 | * invoke the runnable. See for more details {@link Expected#isAbsent()}. 192 | *

193 | * If you just want a null check, use {@code ifAbsent}. 194 | * 195 | * @param runnable executed if a value is not present. 196 | * @return this, fluent API 197 | */ 198 | Expected ifEmpty(Runnable runnable); 199 | 200 | /** 201 | * If a value is present, invoke the consumer with the value. 202 | * 203 | * @param consumer executed if a value is present 204 | * @return this, fluent API 205 | * @throws NullPointerException if value is present and {@code consumer} is 206 | * null 207 | */ 208 | Expected ifPresent(Consumer consumer); 209 | 210 | 211 | /** 212 | * If a value is present, and the value matches the given predicate, 213 | * return an {@code ExpectedImpl} describing the value, otherwise return an 214 | * empty {@code ExpectedImpl}. 215 | * 216 | * @param predicate a predicate to apply to the value, if present 217 | * @return an {@code ExpectedImpl} the value {@code Expected} 218 | * if present and the value matches the predicate, 219 | * otherwise an empty {@code Expected} 220 | * @throws NullPointerException if the predicate is null 221 | */ 222 | Expected filter(Predicate predicate); 223 | 224 | /** 225 | * If a value present, use the mapping function to it, 226 | * and if the result is present, return an {@code ExpectedImpl} with the result. 227 | * Otherwise return an empty value {@code Expected}. 228 | * 229 | * @param The type of the result of the mapping function 230 | * @param mapper a mapper to apply to the value, if present 231 | * @return a value {@code Expected} which is the result of the mapper 232 | * function applied to {@code Expected} value if present or an empty value. 233 | * @throws NullPointerException if the mapper is null 234 | */ 235 | Expected map(Function mapper); 236 | 237 | /** 238 | * Return the value if present. If not present return {@code other}. 239 | * 240 | * @param other value which is returned if no value present. 241 | * @return the value, if present, or if not present return {@code other} 242 | */ 243 | T orElse(T other); 244 | 245 | /** 246 | * Indicates whether some other object is "equal to" the value. 247 | * The other value is equal if Object.equals(value, other) returns true. 248 | * 249 | * @param value checks for equality of inner value which is contained in reference 250 | * @return true if equal 251 | */ 252 | boolean equalsValue(Object value); 253 | 254 | } 255 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/reakt/Invokable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package io.advantageous.reakt; 20 | 21 | /** 22 | * Marker interface to indicate something is invokable. This is used in a promise to detect if something is invokable. 23 | * 24 | * @author Rick Hightower 25 | */ 26 | public interface Invokable { 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/reakt/Observer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package io.advantageous.reakt; 20 | 21 | 22 | /** 23 | * Provides a mechanism for receiving push-based notifications. 24 | * This class represents traditional Subscriber/Observer and how to integrate with them. 25 | * 26 | * @param the type of item the Observer expects to observe 27 | * @author Rick Hightower 28 | * @see ReactiveX documentation: Observable 29 | */ 30 | public interface Observer { 31 | 32 | /** 33 | * Adapts an observer to callback. 34 | * 35 | * @param observer observer that you want to turn into a callback. 36 | * @param value of results, which will be a singleton. 37 | * @return new CallbackHandler 38 | */ 39 | static CallbackHandler callback(final Observer observer) { 40 | return result -> { 41 | if (result.success()) { 42 | observer.onNext(result.get()); 43 | } else { 44 | observer.onError(result.cause()); 45 | } 46 | observer.onCompleted(); 47 | }; 48 | } 49 | 50 | /** 51 | * Adapts an observer to a stream. 52 | * 53 | * @param observer observer 54 | * @param value of results which will be a singleton. 55 | * @return a new Reakt stream which represents the results returned. 56 | */ 57 | static Stream stream(final Observer observer) { 58 | return result -> { 59 | if (result.success()) { 60 | observer.onNext(result.get()); 61 | } else { 62 | observer.onError(result.cause()); 63 | } 64 | if (result.complete()) { 65 | observer.onCompleted(); 66 | } 67 | }; 68 | } 69 | 70 | /** 71 | * Notifies the Observer that the an Observable has finished sending push-based notifications. 72 | *

73 | * The Observable will not call this method if it calls {@link #onError}. 74 | */ 75 | void onCompleted(); 76 | 77 | /** 78 | * Notifies the Observer that the Observable has hit an error condition. 79 | *

80 | * If the Observable calls this method, it will not thereafter call {@link #onNext} or 81 | * {@link #onCompleted}. 82 | * 83 | * @param e the exception encountered by the Observable 84 | */ 85 | void onError(final Throwable e); 86 | 87 | /** 88 | * Provides the Observer with a new item to observe. 89 | *

90 | * The Observable may call this method 0 or more times. In the case of a scalar call {@link CallbackHandler#onResult(Result)} 91 | * it will get called once. In the case of {@link Stream#onNext(StreamResult)} the stream will get called many times. 92 | *

93 | * The {@code Observable} will not call this method again after it calls either {@link #onCompleted} or 94 | * {@link #onError}. 95 | * 96 | * @param t the item emitted by the Observable 97 | */ 98 | void onNext(final T t); 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/reakt/PromiseSupplier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package io.advantageous.reakt; 20 | 21 | import io.advantageous.reakt.promise.PromiseHandler; 22 | 23 | import java.util.function.Supplier; 24 | 25 | /** 26 | * Represents an async supplier of results. 27 | *

28 | * Use this instead of the AsyncSupplier. 29 | * 30 | * @param the type of results supplied by this supplier 31 | * @author Rick Hightower 32 | * @author Geoff Chandler 33 | */ 34 | public interface PromiseSupplier extends Supplier> { 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/reakt/Result.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package io.advantageous.reakt; 20 | 21 | import io.advantageous.reakt.impl.ResultImpl; 22 | 23 | import java.util.function.Consumer; 24 | 25 | /** 26 | * The result of an async operation. 27 | *

28 | * This was modeled after Vert.x AsyncResult and after the types of 29 | * results one would deal with in JavaScript. 30 | * 31 | * @param type of value expected in the result. 32 | * @author Rick Hightower 33 | * @author Geoff Chandler 34 | */ 35 | public interface Result { 36 | 37 | /** 38 | * DONE Result for CallbackHandler Void. 39 | */ 40 | Result DONE = new ResultImpl<>(null); 41 | 42 | /** 43 | * Create a result. 44 | * 45 | * @param value value 46 | * @param T 47 | * @return result 48 | */ 49 | static Result result(T value) { 50 | return new ResultImpl<>(value); 51 | } 52 | 53 | /** 54 | * Create a result 55 | * 56 | * @param error error 57 | * @param T 58 | * @return result 59 | */ 60 | static Result error(Throwable error) { 61 | return new ResultImpl<>(error); 62 | } 63 | 64 | /** 65 | * Done results 66 | * 67 | * @return returns constant which is a result that is done. 68 | */ 69 | static Result doneResult() { 70 | return DONE; 71 | } 72 | 73 | /** 74 | * If a result is sent, and there was no error, then handle the result. 75 | * 76 | * @param consumer executed if result has no error. 77 | * @return this, fluent API 78 | * @throws NullPointerException if result is present and {@code consumer} is 79 | * null 80 | */ 81 | Result then(Consumer consumer); 82 | 83 | /** 84 | * If a result is sent, and there was no error, then handle the result as a value which could be null. 85 | * 86 | * @param consumer executed if result has no error. 87 | * @return this, fluent API 88 | * @throws NullPointerException if result is present and {@code consumer} is 89 | * null 90 | */ 91 | Result thenExpect(Consumer> consumer); 92 | 93 | /** 94 | * If a result is sent, and there is an error, then handle handle the error. 95 | * 96 | * @param consumer executed if result has error. 97 | * @return this, fluent API 98 | * @throws NullPointerException if result is present and {@code consumer} is 99 | * null 100 | */ 101 | Result catchError(Consumer consumer); 102 | 103 | /** 104 | * @return true if result is sent successfully. 105 | */ 106 | boolean success(); 107 | 108 | /** 109 | * @return true if result is sent and this is the last result. 110 | */ 111 | boolean complete(); 112 | 113 | /** 114 | * If failure is true then cause will not be null. 115 | * 116 | * @return true if result is sent and result outcome is a failure. 117 | */ 118 | boolean failure(); 119 | 120 | /** 121 | * If failure is true, the cause will not be null. 122 | * 123 | * @return cause of error associated with the result 124 | */ 125 | Throwable cause(); 126 | 127 | /** 128 | * If the value of the result can be null, it is better to use Expected which is like Optional. 129 | * 130 | * @return value associated with a successful result. 131 | */ 132 | Expected expect(); 133 | 134 | /** 135 | * Raw value of the result. 136 | * You should not use this if the result could be null, use expect instead. 137 | * 138 | * @return raw value associated with the result. 139 | */ 140 | T get(); 141 | 142 | /** 143 | * Return the value if no error. If there was ane error return {@code other}. 144 | * 145 | * @param other value which is returned if no there was an error. 146 | * @return the value, if no error, or if error return {@code other} 147 | */ 148 | T orElse(T other); 149 | 150 | } 151 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/reakt/Stream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package io.advantageous.reakt; 20 | 21 | import io.advantageous.reakt.exception.RejectedStreamException; 22 | import io.advantageous.reakt.impl.StreamResultImpl; 23 | 24 | import java.util.function.Consumer; 25 | 26 | /** 27 | * A generic event handler for N results, i.e., a stream of results. 28 | * 29 | * This is a like a type of {@link CallbackHandler} for streaming results. 30 | * While {@code CallbackHandler} can be considered for scalar results, a 31 | * {@code Stream} is more appropriate for non-scalar results, i.e., {@code Stream#onNext} 32 | * will get called many times which can be thought of as a callback handler. 33 | * This is like an async future or promise. 34 | * 35 | * @param type of result returned from callback 36 | * @author Rick Hightower 37 | */ 38 | public interface Stream extends Callback { 39 | 40 | /** 41 | * (Client view) 42 | * A result was returned so handle it. 43 | * This is registered from the callers (or event receivers perspective). 44 | * A client of a service would override {@code onResult}. 45 | * 46 | * @param result to handle 47 | */ 48 | void onNext(final StreamResult result); 49 | 50 | /** 51 | * (Service view) 52 | * This allows services to send back a last result easily to the client/handler. 53 | * This is a helper methods for producers (services that produce results) to send a result. 54 | * 55 | * @param result result value to send. 56 | */ 57 | default void complete(final T result) { 58 | this.onNext(new StreamResultImpl<>(result, true, Expected.empty(), Expected.empty())); 59 | } 60 | 61 | /** 62 | * (Service view) 63 | * This allows services to send back a next result easily to the client/handler. 64 | * This is a helper methods for producers (services that produce results) to send a result. 65 | * 66 | * @param result result value to send. 67 | */ 68 | default void reply(final T result) { 69 | this.onNext(new StreamResultImpl<>(result, false, Expected.empty(), Expected.empty())); 70 | } 71 | 72 | /** 73 | * (Service view) 74 | * This allows services to send back a next result easily to the client/handler 75 | * and pass done flag to denote completeness. 76 | * This is a helper methods for producers (services that produce results) to send a result. 77 | * 78 | * @param result result value to send. 79 | * @param done if true signifies that that this is the last result. 80 | */ 81 | default void reply(final T result, final boolean done) { 82 | this.onNext(new StreamResultImpl<>(result, done, Expected.empty(), Expected.empty())); 83 | } 84 | 85 | /** 86 | * (Service view) 87 | * This allows services to send back a next result easily to the client/handler 88 | * and pass done flag to denote completeness. 89 | * This is a helper methods for producers (services that produce results) to send a result. 90 | * 91 | * @param result result value to send. 92 | * @param done if true signifies that that this is the last result. 93 | * @param cancelHandler cancel handler if you support canceling. 94 | */ 95 | default void reply(final T result, final boolean done, final Runnable cancelHandler) { 96 | this.onNext(new StreamResultImpl<>(result, done, Expected.of(cancelHandler), Expected.empty())); 97 | } 98 | 99 | /** 100 | * (Service view) 101 | * This allows services to send back a next result easily to the client/handler 102 | * and pass done flag to denote completeness. 103 | * This is a helper methods for producers (services that produce results) to send a result. 104 | * 105 | * @param result result value to send. 106 | * @param done if true signifies that that this is the last result. 107 | * @param cancelHandler cancel handler if you support canceling the stream 108 | * @param wantsMore handler so client can request more items if this is supported. 109 | */ 110 | default void reply(final T result, final boolean done, 111 | final Runnable cancelHandler, 112 | final Consumer wantsMore) { 113 | 114 | this.onNext(new StreamResultImpl<>(result, done, Expected.of(cancelHandler), Expected.of(wantsMore))); 115 | } 116 | 117 | /** 118 | * (Service view) 119 | * Don't use this method anymore. Going to mark it deprecated. 120 | * Use reject instead. 121 | * This allows services to send back a failed result easily to the client/handler. 122 | * This is a helper methods for producers (services that produce results) to send a failed result. 123 | * 124 | * @param error error 125 | */ 126 | default void fail(final Throwable error) { 127 | this.onNext(new StreamResultImpl<>(error, true, Expected.empty(), Expected.empty())); 128 | } 129 | 130 | /** 131 | * (Service view) 132 | * Don't use this method anymore. Going to mark it deprecated. 133 | * Use reject instead. 134 | * This allows services to send back a failed result easily to the client/handler. 135 | * This is a helper methods for producers (services that produce results) to send a failed result. 136 | * 137 | * @param errorMessage error message 138 | */ 139 | default void fail(final String errorMessage) { 140 | this.onNext(new StreamResultImpl<>( 141 | new IllegalStateException(errorMessage), true, Expected.empty(), Expected.empty())); 142 | } 143 | 144 | 145 | /** 146 | * (Service view) 147 | * This allows services to send back a failed result easily to the client/handler. 148 | * This is a helper methods for producers (services that produce results) to send a failed result. 149 | * 150 | * @param error error 151 | */ 152 | default void reject(final Throwable error) { 153 | this.onNext(new StreamResultImpl<>(error, true, Expected.empty(), Expected.empty())); 154 | } 155 | 156 | /** 157 | * (Service view) 158 | * This allows services to send back a failed result easily to the client/handler. 159 | * This is a helper methods for producers (services that produce results) to send a failed result. 160 | * 161 | * @param errorMessage error message 162 | */ 163 | default void reject(final String errorMessage) { 164 | this.onNext(new StreamResultImpl<>( 165 | new RejectedStreamException(errorMessage), true, Expected.empty(), Expected.empty())); 166 | } 167 | 168 | 169 | /** 170 | * (Service view) 171 | * This allows services to send back a failed result easily to the client/handler. 172 | * This is a helper methods for producers (services that produce results) to send a failed result. 173 | * 174 | * @param errorMessage error message 175 | */ 176 | default void reject(final String errorMessage, final Throwable error) { 177 | this.onNext(new StreamResultImpl<>( 178 | new RejectedStreamException(errorMessage, error), true, Expected.empty(), Expected.empty())); 179 | } 180 | 181 | 182 | /** 183 | * Calls replayDone, for VOID callback only. ES6 promise style. 184 | */ 185 | default void resolve() { 186 | this.onNext(new StreamResultImpl(null, false, Expected.empty(), Expected.empty())); 187 | } 188 | 189 | /** 190 | * Resolve resolves a promise or replies to a stream. 191 | * 192 | * @param result makes it more compatible with ES6 style promises 193 | */ 194 | default void resolve(final T result) { 195 | reply(result); 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/reakt/StreamResult.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package io.advantageous.reakt; 20 | 21 | /** 22 | * The result of an async operations with optional callbacks for cancel and request more. 23 | * 24 | * @param Type of result. 25 | * @author Rick Hightower 26 | */ 27 | public interface StreamResult extends Result { 28 | 29 | /** 30 | * Request more results 31 | * 32 | * @param n number of results that you are requesting. 33 | */ 34 | void request(long n); 35 | 36 | /** 37 | * Stop sending results. 38 | */ 39 | void cancel(); 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/reakt/exception/RejectedPromiseException.java: -------------------------------------------------------------------------------- 1 | package io.advantageous.reakt.exception; 2 | 3 | 4 | public class RejectedPromiseException extends RuntimeException { 5 | 6 | public RejectedPromiseException(String s) { 7 | super(s); 8 | } 9 | 10 | public RejectedPromiseException(String message, Throwable cause) { 11 | super(message, cause); 12 | } 13 | 14 | public RejectedPromiseException(Throwable cause) { 15 | super(cause); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/reakt/exception/RejectedStreamException.java: -------------------------------------------------------------------------------- 1 | package io.advantageous.reakt.exception; 2 | 3 | public class RejectedStreamException extends RuntimeException { 4 | 5 | public RejectedStreamException() { 6 | super(); 7 | } 8 | 9 | public RejectedStreamException(String message) { 10 | super(message); 11 | } 12 | 13 | public RejectedStreamException(String message, Throwable cause) { 14 | super(message, cause); 15 | } 16 | 17 | public RejectedStreamException(Throwable cause) { 18 | super(cause); 19 | } 20 | 21 | protected RejectedStreamException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { 22 | super(message, cause, enableSuppression, writableStackTrace); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/reakt/exception/ResultFailedException.java: -------------------------------------------------------------------------------- 1 | package io.advantageous.reakt.exception; 2 | 3 | public class ResultFailedException extends RuntimeException { 4 | 5 | public ResultFailedException(Throwable cause) { 6 | super(cause); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/reakt/exception/ThenHandlerException.java: -------------------------------------------------------------------------------- 1 | package io.advantageous.reakt.exception; 2 | 3 | public class ThenHandlerException extends RuntimeException { 4 | 5 | public ThenHandlerException(Throwable cause) { 6 | super(cause); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/reakt/impl/BreakerImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package io.advantageous.reakt.impl; 20 | 21 | import io.advantageous.reakt.Breaker; 22 | import io.advantageous.reakt.Expected; 23 | 24 | import java.util.Objects; 25 | import java.util.concurrent.atomic.AtomicLong; 26 | import java.util.function.Consumer; 27 | import java.util.function.Predicate; 28 | 29 | /** 30 | * Circuit breaker. 31 | * 32 | * @author Rick Hightower 33 | */ 34 | public class BreakerImpl implements Breaker { 35 | 36 | /** 37 | * hold service 38 | */ 39 | private final T service; 40 | 41 | /** 42 | * Hold blow circuit breaker flag. 43 | */ 44 | private final AtomicLong errors = new AtomicLong(); 45 | private final int maxErrorCount; 46 | private final Expected> brokenPredicate; 47 | 48 | public BreakerImpl() { 49 | this.maxErrorCount = 0; 50 | this.service = null; 51 | this.brokenPredicate = Expected.empty(); 52 | } 53 | 54 | public BreakerImpl(final T value) { 55 | this.maxErrorCount = 0; 56 | this.service = Objects.requireNonNull(value); 57 | this.brokenPredicate = Expected.empty(); 58 | } 59 | 60 | public BreakerImpl(final T value, final int maxErrorCount) { 61 | this.service = Objects.requireNonNull(value); 62 | this.maxErrorCount = maxErrorCount; 63 | this.brokenPredicate = Expected.empty(); 64 | } 65 | 66 | public BreakerImpl(final T value, final int maxErrorCount, final Predicate brokenPredicate) { 67 | this.service = Objects.requireNonNull(value); 68 | this.maxErrorCount = maxErrorCount; 69 | this.brokenPredicate = Expected.of(brokenPredicate); 70 | } 71 | 72 | 73 | @Override 74 | public boolean isOperational() { 75 | return (this.service != null) && 76 | (this.maxErrorCount == 0 || errorCount() < this.maxErrorCount) && 77 | (this.brokenPredicate.isEmpty() || !this.brokenPredicate.get().test(service)); 78 | } 79 | 80 | @Override 81 | public boolean isBroken() { 82 | return !isOperational(); 83 | } 84 | 85 | @Override 86 | public Breaker ifOperational(final Consumer consumer) { 87 | try { 88 | if (isOperational()) consumer.accept(this.service); 89 | return this; 90 | } catch (Exception ex) { 91 | this.errors.incrementAndGet(); 92 | throw new IllegalStateException("Operation failed", ex); 93 | } 94 | } 95 | 96 | @Override 97 | public Breaker ifBroken(final Runnable runnable) { 98 | if (isBroken()) runnable.run(); 99 | return this; 100 | } 101 | 102 | 103 | @Override 104 | public Breaker cleanup(final Consumer consumer) { 105 | if (this.service != null) consumer.accept(this.service); 106 | return this; 107 | } 108 | 109 | @Override 110 | public long errorCount() { 111 | return this.errors.get(); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/reakt/impl/ExpectedImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package io.advantageous.reakt.impl; 20 | 21 | import io.advantageous.reakt.Expected; 22 | 23 | import java.lang.reflect.Array; 24 | import java.util.Collection; 25 | import java.util.NoSuchElementException; 26 | import java.util.Objects; 27 | import java.util.function.Consumer; 28 | import java.util.function.Function; 29 | import java.util.function.Predicate; 30 | 31 | /** 32 | * Contains an value object which may not be set. 33 | * If a value is present, {@code isPresent()} will return {@code true} and 34 | * {@code get()} will return the value. 35 | * 36 | * @author Rick Hightower 37 | */ 38 | public class ExpectedImpl implements Expected { 39 | 40 | /** 41 | * If non-null, the value; if null, indicates no value is present 42 | */ 43 | private final T value; 44 | 45 | /** 46 | * Constructs an empty instance of value. 47 | */ 48 | public ExpectedImpl() { 49 | this.value = null; 50 | } 51 | 52 | /** 53 | * Constructs an instance with the value present. 54 | * 55 | * @param value the non-null value to be present 56 | * @throws NullPointerException if value is null 57 | */ 58 | public ExpectedImpl(T value) { 59 | this.value = Objects.requireNonNull(value); 60 | } 61 | 62 | /** 63 | * If a value is present in this {@code Expected}, returns the value, 64 | * otherwise throws {@code NoSuchElementException}. 65 | * 66 | * @return the value held by this {@code Expected} 67 | * @throws NoSuchElementException if there is no value present 68 | * @see ExpectedImpl#isPresent() 69 | */ 70 | @Override 71 | public T get() { 72 | if (this.value == null) throw new NoSuchElementException("No value present"); 73 | return this.value; 74 | } 75 | 76 | /** 77 | * Return {@code true} if there is a value present, otherwise {@code false}. 78 | * 79 | * @return {@code true} if there is a value present, otherwise {@code false} 80 | */ 81 | @Override 82 | public boolean isPresent() { 83 | return this.value != null; 84 | } 85 | 86 | 87 | /** 88 | * Return {@code true} if there is not a value present, otherwise {@code false}. 89 | * 90 | * @return {@code true} if there is not a value present, otherwise {@code false} 91 | */ 92 | @Override 93 | public boolean isAbsent() { 94 | return this.value == null; 95 | } 96 | 97 | /** 98 | * Return {@code true} if there is not a value present, otherwise {@code false}. 99 | * 100 | * @return {@code true} if there is not a value present, otherwise {@code false} 101 | */ 102 | @Override 103 | public boolean isEmpty() { 104 | if (this.value == null) { 105 | return true; 106 | } else if (value instanceof Collection) { 107 | final Collection c = ((Collection) value); 108 | if (c.size() == 0) { 109 | return true; 110 | } 111 | } else if (value instanceof CharSequence) { 112 | final CharSequence cs = ((CharSequence) value); 113 | if (cs.length() == 0) { 114 | return true; 115 | } 116 | } else if (value.getClass().isArray()) { 117 | if (Array.getLength(value) == 0) { 118 | return true; 119 | } 120 | } 121 | return false; 122 | } 123 | 124 | /** 125 | * If a value is present, invoke the consumer with the value. 126 | * 127 | * @param consumer executed if a value is present 128 | * @throws NullPointerException if value is present and {@code consumer} is 129 | * null 130 | */ 131 | @Override 132 | public Expected ifPresent(final Consumer consumer) { 133 | if (this.value != null) consumer.accept(this.value); 134 | return this; 135 | } 136 | 137 | /** 138 | * If a value is not present or present but empty, invoke the runnable. 139 | * 140 | * @param runnable executed if a value is not present 141 | */ 142 | @Override 143 | public Expected ifEmpty(final Runnable runnable) { 144 | if (isEmpty()) { 145 | runnable.run(); 146 | } 147 | return this; 148 | } 149 | 150 | /** 151 | * If a value is not empty. 152 | * 153 | * @param consumer executed if a value is not present 154 | */ 155 | public Expected ifNotEmpty(final Consumer consumer) { 156 | if (!isEmpty()) consumer.accept(this.value); 157 | return this; 158 | } 159 | 160 | /** 161 | * If a value is not present (null), invoke the runnable. 162 | * 163 | * @param runnable executed if a value is not present 164 | */ 165 | @Override 166 | public Expected ifAbsent(final Runnable runnable) { 167 | if (isAbsent()) { 168 | runnable.run(); 169 | } 170 | return this; 171 | } 172 | 173 | 174 | /** 175 | * If a value is present, and the value matches the given predicate, 176 | * return an {@code ExpectedImpl} describing the value, otherwise return an 177 | * empty {@code ExpectedImpl}. 178 | * 179 | * @param predicate a predicate to apply to the value, if present 180 | * @return an {@code ExpectedImpl} the value {@code ExpectedImpl} 181 | * if present and the value matches the predicate, 182 | * otherwise an empty {@code ExpectedImpl} 183 | * @throws NullPointerException if the predicate is null 184 | */ 185 | @Override 186 | public Expected filter(final Predicate predicate) { 187 | Objects.requireNonNull(predicate); 188 | return !isPresent() ? this : predicate.test(value) ? this : Expected.empty(); 189 | } 190 | 191 | /** 192 | * If a value present, use the mapping function to it, 193 | * and if the result is present, return an {@code ExpectedImpl} with the result. 194 | * Otherwise return an empty value {@code ExpectedImpl}. 195 | * 196 | * @param The type of the result of the mapping function 197 | * @param mapper a mapper to apply to the value, if present 198 | * @return a value {@code ExpectedImpl} which is the result of the mapper 199 | * function applied to {@code ExpectedImpl} value if present or an empty value. 200 | * @throws NullPointerException if the mapper is null 201 | */ 202 | @Override 203 | public Expected map(final Function mapper) { 204 | Objects.requireNonNull(mapper); 205 | return !isPresent() ? Expected.empty() : Expected.ofNullable(mapper.apply(this.value)); 206 | } 207 | 208 | /** 209 | * Return the value if present. If not present return {@code other}. 210 | * 211 | * @param other value which is returned if no value present. 212 | * @return the value, if present, or if not present return {@code other} 213 | */ 214 | @Override 215 | public T orElse(T other) { 216 | return this.value != null ? this.value : other; 217 | } 218 | 219 | /** 220 | * Indicates whether some other object is "equal to" the value. 221 | * The other value is equal if: 222 | *

    223 | *
  • it is also an {@code ExpectedImpl} and; 224 | *
  • both instances have no value present or; 225 | *
  • the present values are "equal to" to the other value. 226 | *
227 | * 228 | * @param other an object to be tested for equality 229 | * @return {code true} if the other object is "equal to" this object 230 | * otherwise {@code false} 231 | */ 232 | @Override 233 | public boolean equals(final Object other) { 234 | if (this == other) { 235 | return true; 236 | } 237 | if (!(other instanceof Expected)) { 238 | return false; 239 | } 240 | final ExpectedImpl otherValue = (ExpectedImpl) other; 241 | return Objects.equals(this.value, otherValue.value); 242 | } 243 | 244 | /** 245 | * Indicates whether some other object is "equal to" the value. 246 | * The other value is equal if Object.equals(value, other) returns true. 247 | */ 248 | @Override 249 | public boolean equalsValue(final Object other) { 250 | return Objects.equals(this.value, other); 251 | } 252 | 253 | /** 254 | * Returns the hash code value of the present value, if any, or 0 (zero) if 255 | * no value is present. 256 | * 257 | * @return hash code value of the present value or 0 if no value is present 258 | */ 259 | @Override 260 | public int hashCode() { 261 | return Objects.hashCode(this.value); 262 | } 263 | 264 | /** 265 | * Returns a non-empty string representation of this Expected suitable for 266 | * debugging. The exact presentation format is unspecified and may vary 267 | * between implementations and versions. 268 | * 269 | * @return the string representation of this instance 270 | */ 271 | @Override 272 | public String toString() { 273 | return "Expected{" + "value=" + this.value + '}'; 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/reakt/impl/ResultImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package io.advantageous.reakt.impl; 20 | 21 | import io.advantageous.reakt.Expected; 22 | import io.advantageous.reakt.Result; 23 | import io.advantageous.reakt.exception.ResultFailedException; 24 | 25 | import java.util.function.Consumer; 26 | 27 | /** 28 | * The result of an async operation. 29 | *

30 | * This was modeled after Vert.x AsyncResult and after the types of results one would deal with in JavaScript. 31 | * 32 | * @param type of value expected in the result. 33 | * @author Rick Hightower 34 | */ 35 | public class ResultImpl implements Result { 36 | 37 | private final Object object; 38 | 39 | public ResultImpl(final Object object) { 40 | this.object = object; 41 | } 42 | 43 | @Override 44 | public Result thenExpect(final Consumer> consumer) { 45 | if (success()) consumer.accept(expect()); 46 | return this; 47 | } 48 | 49 | @Override 50 | public Result then(final Consumer consumer) { 51 | if (success()) consumer.accept(get()); 52 | return this; 53 | } 54 | 55 | @Override 56 | public Result catchError(final Consumer handler) { 57 | if (failure()) handler.accept(cause()); 58 | return this; 59 | } 60 | 61 | public boolean success() { 62 | return !(this.object instanceof Throwable); 63 | } 64 | 65 | @Override 66 | public boolean complete() { 67 | return true; 68 | } 69 | 70 | public boolean failure() { 71 | return this.object instanceof Throwable; 72 | } 73 | 74 | @Override 75 | public Throwable cause() { 76 | return this.object instanceof Throwable ? (Throwable) this.object : null; 77 | } 78 | 79 | @SuppressWarnings("unchecked") 80 | public Expected expect() { 81 | if (failure()) throw new IllegalStateException(cause()); 82 | return Expected.ofNullable((T) this.object); 83 | } 84 | 85 | @SuppressWarnings("unchecked") 86 | public T get() { 87 | if (failure()) { 88 | if (cause() instanceof RuntimeException) { 89 | throw (RuntimeException) cause(); 90 | } else { 91 | throw new ResultFailedException(cause()); 92 | } 93 | } 94 | return (T) this.object; 95 | } 96 | 97 | @Override 98 | @SuppressWarnings("unchecked") 99 | public T orElse(final T other) { 100 | return success() ? (T) this.object : other; 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/reakt/impl/StreamResultImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package io.advantageous.reakt.impl; 20 | 21 | import io.advantageous.reakt.Expected; 22 | import io.advantageous.reakt.StreamResult; 23 | 24 | import java.util.function.Consumer; 25 | 26 | /** 27 | * The result of an async operations with optional callbacks for cancel and request more. 28 | * 29 | * @param Type of result. 30 | * @author Rick Hightower 31 | */ 32 | public class StreamResultImpl extends ResultImpl implements StreamResult { 33 | private final boolean done; 34 | private final Expected cancelCallback; 35 | private final Expected> requestMore; 36 | 37 | public StreamResultImpl(final Object object, 38 | final boolean done, 39 | final Expected cancelCallback, 40 | final Expected> requestMore) { 41 | super(object); 42 | this.done = done; 43 | this.cancelCallback = cancelCallback; 44 | this.requestMore = requestMore; 45 | } 46 | 47 | @Override 48 | public boolean complete() { 49 | return done; 50 | } 51 | 52 | @Override 53 | public void cancel() { 54 | cancelCallback.ifPresent(Runnable::run); 55 | } 56 | 57 | @Override 58 | public void request(long n) { 59 | requestMore.ifPresent(longConsumer -> longConsumer.accept(n)); 60 | } 61 | } 62 | 63 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/reakt/promise/Promise.java: -------------------------------------------------------------------------------- 1 | package io.advantageous.reakt.promise; 2 | 3 | import io.advantageous.reakt.Expected; 4 | 5 | import java.time.Duration; 6 | import java.util.function.Consumer; 7 | 8 | /** 9 | * A promise is like a non-blocking {@code Future}({@link java.util.concurrent.Future}). 10 | * You get notified of changes instead of having to call {@code get}. 11 | *

12 | * A promise is a sort of deferred value. 13 | *

14 | * @param value of result. 15 | * @author Rick Hightower 16 | * @author Geoff Chandler 17 | */ 18 | public interface Promise { 19 | 20 | 21 | /** 22 | * If a result is sent, and there was no error, then handle the result. 23 | *

24 | * There is only one {@code then} Handler. 25 | *

26 | * Unlike ES6, {@code then(..)} cannot be chained per se, but {@code whenComplete(..)}, and 27 | * {@code }thenMap(...)} can be nested. 28 | * 29 | * @param consumer executed if result has no error. 30 | * @return this, fluent API 31 | * @throws NullPointerException if result is present and {@code consumer} is null 32 | */ 33 | Promise then(Consumer consumer); 34 | 35 | /** 36 | * If a result is sent, and there was no error, then handle the result as a value which could be null. 37 | *

38 | * There is only one thenExpect handler per promise. 39 | *

40 | * Unlike ES6, {@code thenExpect(..)} cannot be chained per se as it does not create a new promise, 41 | * but {@code whenComplete(..)}, and {@code }thenMap(...)} can be chained. 42 | *

43 | * This does not create a new promise. 44 | * 45 | * @param consumer executed if result has no error. 46 | * @return this, fluent API 47 | * @throws NullPointerException if result is present and {@code consumer} is 48 | * null 49 | */ 50 | Promise thenExpect(Consumer> consumer); 51 | 52 | /** 53 | * If a result is sent, and there is an error, then handle handle the error. 54 | * 55 | * @param consumer executed if result has error. 56 | * @return this, fluent API 57 | * @throws NullPointerException if result is present and {@code consumer} is null 58 | */ 59 | Promise catchError(Consumer consumer); 60 | 61 | 62 | /** 63 | * Allows promises returned from, for example, proxy stubs for services methods to invoke the operation. 64 | *

65 | * This allows use to set up the catchError and then before the method is async invoked. 66 | *

67 | * Example Remote Proxy Gen to support returning Reakt invokeable promise 68 | *

 69 |      * 
 70 |      *     employeeService.lookupEmployee("123")
 71 |      *           .then((employee)-> {...})
 72 |      *           .catchError(...)
 73 |      *           .invoke();
 74 |      * 
 75 |      * 
76 | * 77 | */ 78 | void invoke(); 79 | 80 | /** 81 | * If the thenSafeExpect handler throws an exception, this will report it as if it it was caught by catchError. 82 | *

83 | * This is convenient if you are running your handler with an async lib that is not reporting or catching 84 | * exceptions as your code is running on their threads. 85 | *

86 | * If a result is sent, and there was no error, then handle the result as a value which could be null. 87 | *

88 | * There is only one thenSafeExpect or thenExpect handler per promise. 89 | * Once then is called all other then* handlers are safe. 90 | *

91 | * Unlike ES6, {@code thenExpect(..)} cannot be chained per se as it does not create a new promise, 92 | * but {@code whenComplete(..)}, and {@code }thenMap(...)} can be chained. 93 | *

94 | * This does not create a new promise. 95 | * 96 | * @param consumer executed if result has no error. 97 | * @return this, fluent API 98 | * @throws NullPointerException if result is present and {@code consumer} is 99 | * null 100 | */ 101 | Promise thenSafeExpect(Consumer> consumer); 102 | 103 | 104 | /** 105 | * If the {@code then} handler throws an exception, this will report the exception as if it were caught 106 | * by {@code catchError}. 107 | *

108 | * This is convenient if you are running your handler with an async lib that is not reporting or catching 109 | * exceptions as your code is running on their threads. 110 | *

111 | * If a result is sent, and there was no error, then handle the result as a value which could be null. 112 | *

113 | * There is only one thenSafe or then handler per promise. 114 | * Once then is called all other then* handlers are safe. 115 | *

116 | * Unlike ES6, {@code thenExpect(..)} cannot be chained per se as it does not create a new promise, 117 | * but {@code whenComplete(..)}, and {@code }thenMap(...)} can be chained. 118 | *

119 | * This does not create a new promise. 120 | * 121 | * @param consumer executed if result has no error. 122 | * @return this, fluent API 123 | * @throws NullPointerException if result is present and {@code consumer} is 124 | * null 125 | */ 126 | Promise thenSafe(Consumer consumer); 127 | 128 | 129 | /** 130 | * Used for testing and legacy integration. 131 | * This turns an async promise into a blocking promise and then does a get operations. 132 | * 133 | * @param duration duration to wait for call 134 | * @return result of call, blocks until return comes back. 135 | */ 136 | T blockingGet(Duration duration); 137 | 138 | /** 139 | * Used for testing and legacy integration. 140 | * This turns an async promise into a blocking promise and then does a get operations. 141 | * 142 | * @return result of call, blocks until return comes back. 143 | */ 144 | T blockingGet(); 145 | 146 | /** 147 | * If backed by a PromiseHandler then this will return that promise, 148 | * otherwise throws a Class Cast exception. 149 | * 150 | * @return promise that backs this handle 151 | */ 152 | default PromiseHandler asHandler() { 153 | return (PromiseHandler) this; 154 | } 155 | 156 | } 157 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/reakt/promise/PromiseHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package io.advantageous.reakt.promise; 20 | 21 | import io.advantageous.reakt.*; 22 | import io.advantageous.reakt.promise.impl.BasePromise; 23 | import io.advantageous.reakt.reactor.Reactor; 24 | 25 | import java.time.Duration; 26 | import java.util.function.Consumer; 27 | import java.util.function.Function; 28 | 29 | /** 30 | * This combines a Result and callback handler to simplify implementation of a Promise. 31 | * A promise is like a non-blocking {@code Future}({@link java.util.concurrent.Future}). 32 | * You get notified of changes instead of having to call {@code get}. 33 | *

34 | * A promise handler is both a {@code CallbackHandler} ({@link CallbackHandler}), 35 | * and a {@code Result} {@link io.advantageous.reakt.Result}. 36 | *

37 | *

38 | * A promise is a sort of deferred value. 39 | * 40 | * @param value of result. 41 | * @author Rick Hightower 42 | * @author Geoff Chandler 43 | */ 44 | public interface PromiseHandler extends CallbackHandler, Result, Promise { 45 | 46 | /** 47 | * Creates an immutable promise. 48 | * 49 | * @return final promise 50 | */ 51 | default Promise freeze() { 52 | return BasePromise.provideFinalPromise(this); 53 | } 54 | 55 | /** 56 | * If a result is sent, and there was no error, then handle the result. 57 | *

58 | * There is only one {@code then} Handler. 59 | *

60 | * Unlike ES6, {@code then(..)} cannot be chained per se, but {@code whenComplete(..)}, and 61 | * {@code }thenMap(...)} can be nested. 62 | * 63 | * @param consumer executed if result has no error. 64 | * @return this, fluent API 65 | * @throws NullPointerException if result is present and {@code consumer} is null 66 | */ 67 | PromiseHandler then(Consumer consumer); 68 | 69 | 70 | /** 71 | * Notified of completeness. 72 | *

73 | * If you want N handlers for when the promise gets called back use whenComplete instead of 74 | * {@code then} or {@code thenExpect}. 75 | *

76 | * There can be many {@code whenComplete} handlers. 77 | *

78 | * This does not create a new promise. 79 | * 80 | * @param doneListener doneListener 81 | * @return this, fluent API 82 | */ 83 | PromiseHandler whenComplete(Consumer> doneListener); 84 | 85 | /** 86 | * If a result is sent, and there was no error, then handle the result as a value which could be null. 87 | *

88 | * There is only one thenExpect handler per promise. 89 | *

90 | * Unlike ES6, {@code thenExpect(..)} cannot be chained per se as it does not create a new promise, 91 | * but {@code whenComplete(..)}, and {@code }thenMap(...)} can be chained. 92 | *

93 | * This does not create a new promise. 94 | * 95 | * @param consumer executed if result has no error. 96 | * @return this, fluent API 97 | * @throws NullPointerException if result is present and {@code consumer} is 98 | * null 99 | */ 100 | PromiseHandler thenExpect(Consumer> consumer); 101 | 102 | 103 | /** 104 | * This method can be chained, and it creates a new promise, which can be a different type. 105 | * 106 | * @param mapper mapper function 107 | * @param new type for new promise 108 | * @return a promise that uses mapper function to map old promise result to new result. 109 | */ 110 | PromiseHandler thenMap(Function mapper); 111 | 112 | /** 113 | * If a result is sent, and there is an error, then handle handle the error. 114 | * 115 | * @param consumer executed if result has error. 116 | * @return this, fluent API 117 | * @throws NullPointerException if result is present and {@code consumer} is null 118 | */ 119 | PromiseHandler catchError(Consumer consumer); 120 | 121 | /** 122 | * Returns true if this PromiseHandler is an Invokable PromiseHandler. 123 | *

124 | * Hint: if you have to ask, the answer is no. 125 | * 126 | * @return true if it is invokable 127 | */ 128 | default boolean isInvokable() { 129 | return this instanceof Invokable; 130 | } 131 | 132 | /** 133 | * Allows promises returned from, for example, proxy stubs for services methods to invoke the operation. 134 | *

135 | * This allows use to set up the catchError and then before the method is async invoked. 136 | *

137 | * Example Remote Proxy Gen to support returning Reakt invokeable promise 138 | *

139 |      * 
140 |      *     employeeService.lookupEmployee("123")
141 |      *           .then((employee)-> {...})
142 |      *           .catchError(...)
143 |      *           .invoke();
144 |      * 
145 |      * 
146 | * 147 | */ 148 | default void invoke() { 149 | throw new UnsupportedOperationException("This is not an invokable promise."); 150 | } 151 | 152 | 153 | /** 154 | * Allows you to pass an existing promise as a handler. 155 | * 156 | * @param promise promise 157 | * @return this, fluent 158 | */ 159 | default PromiseHandler thenPromise(Promise promise) { 160 | thenCallback((CallbackHandler) promise); 161 | return this; 162 | } 163 | 164 | 165 | /** 166 | * Allows you to pass an existing callback as a handler. 167 | * 168 | * @param callback callback 169 | * @return this, fluent 170 | */ 171 | default PromiseHandler thenCallback(CallbackHandler callback) { 172 | this.catchError(callback::reject).then(callback::resolve); 173 | return this; 174 | } 175 | 176 | default PromiseHandler thenCallback(Callback callback) { 177 | this.catchError(callback::reject).then(callback::resolve); 178 | return this; 179 | } 180 | 181 | 182 | /** 183 | * Allows you to pass an existing promise as a handler. 184 | * 185 | * @param promise promise 186 | */ 187 | default void invokeWithPromise(Promise promise) { 188 | thenPromise(promise).invoke(); 189 | } 190 | 191 | /** 192 | * Allows you to pass an existing promise as a handler. 193 | * 194 | * @param promise promise 195 | */ 196 | default void invokeWithPromise(PromiseHandler promise) { 197 | thenPromise(promise).invoke(); 198 | } 199 | 200 | /** 201 | * Use this to run the promise in replay mode on a reactor. 202 | * Allows a promise to be invoked with a reactor 203 | * 204 | * @param reactor reactor to use 205 | * @return new PromiseHandler that wraps the promise. New promise is associated with the reactor. 206 | */ 207 | PromiseHandler invokeWithReactor(Reactor reactor); 208 | 209 | 210 | /** 211 | * Use this to run the promise in replay mode on a reactor. 212 | * Allows a promise to be invoked with a reactor 213 | * 214 | * @param reactor reactor to use 215 | * @param timeout if the promise does not return in the allotted time, the reactor will time it out. 216 | * @return new PromiseHandler that wraps the promise. New promise is associated with the reactor. 217 | */ 218 | PromiseHandler invokeWithReactor(Reactor reactor, Duration timeout); 219 | 220 | /** 221 | * If the thenSafeExpect handler throws an exception, this will report it as if it it was caught by catchError. 222 | *

223 | * This is convenient if you are running your handler with an async lib that is not reporting or catching 224 | * exceptions as your code is running on their threads. 225 | *

226 | * If a result is sent, and there was no error, then handle the result as a value which could be null. 227 | *

228 | * There is only one thenSafeExpect or thenExpect handler per promise. 229 | * Once then is called all other then* handlers are safe. 230 | *

231 | * Unlike ES6, {@code thenExpect(..)} cannot be chained per se as it does not create a new promise, 232 | * but {@code whenComplete(..)}, and {@code }thenMap(...)} can be chained. 233 | *

234 | * This does not create a new promise. 235 | * 236 | * @param consumer executed if result has no error. 237 | * @return this, fluent API 238 | * @throws NullPointerException if result is present and {@code consumer} is 239 | * null 240 | */ 241 | default PromiseHandler thenSafeExpect(Consumer> consumer) { 242 | throw new UnsupportedOperationException("PromiseHandler provider does not support thenSafeExpect"); 243 | } 244 | 245 | 246 | /** 247 | * If the {@code then} handler throws an exception, this will report the exception as if it were caught 248 | * by {@code catchError}. 249 | *

250 | * This is convenient if you are running your handler with an async lib that is not reporting or catching 251 | * exceptions as your code is running on their threads. 252 | *

253 | * If a result is sent, and there was no error, then handle the result as a value which could be null. 254 | *

255 | * There is only one thenSafe or then handler per promise. 256 | * Once then is called all other then* handlers are safe. 257 | *

258 | * Unlike ES6, {@code thenExpect(..)} cannot be chained per se as it does not create a new promise, 259 | * but {@code whenComplete(..)}, and {@code }thenMap(...)} can be chained. 260 | *

261 | * This does not create a new promise. 262 | * 263 | * @param consumer executed if result has no error. 264 | * @return this, fluent API 265 | * @throws NullPointerException if result is present and {@code consumer} is 266 | * null 267 | */ 268 | default PromiseHandler thenSafe(Consumer consumer) { 269 | throw new UnsupportedOperationException("PromiseHandler provider does not support thenSafeExpect"); 270 | } 271 | 272 | /** 273 | * Denotes if the promise provider supports thenSafe and thenSafeExpect. 274 | * 275 | * @return true if safe operations are supported. 276 | */ 277 | default boolean supportsSafe() { 278 | return false; 279 | } 280 | 281 | /** 282 | * Used for testing and legacy integration. 283 | * This turns an async promise into a blocking promise. 284 | * 285 | * @return blocking promise 286 | */ 287 | default PromiseHandler invokeAsBlockingPromise() { 288 | PromiseHandler blockingPromise = (PromiseHandler) Promises.blockingPromise(); 289 | this.invokeWithPromise(blockingPromise); 290 | return blockingPromise; 291 | } 292 | 293 | 294 | /** 295 | * Used for testing and legacy integration. 296 | * This turns an async promise into a blocking promise. 297 | * 298 | * @param duration duration to wait for call 299 | * @return blocking promise 300 | */ 301 | default PromiseHandler invokeAsBlockingPromise(Duration duration) { 302 | PromiseHandler blockingPromise = (PromiseHandler) Promises.blockingPromise(duration); 303 | this.invokeWithPromise(blockingPromise); 304 | return blockingPromise; 305 | } 306 | 307 | 308 | /** 309 | * Used for testing and legacy integration. 310 | * This turns an async promise into a blocking promise and then does a get operations. 311 | * 312 | * @param duration duration to wait for call 313 | * @return result of call, blocks until return comes back. 314 | */ 315 | default T blockingGet(Duration duration) { 316 | return invokeAsBlockingPromise(duration).get(); 317 | } 318 | 319 | /** 320 | * Used for testing and legacy integration. 321 | * This turns an async promise into a blocking promise and then does a get operations. 322 | * 323 | * @return result of call, blocks until return comes back. 324 | */ 325 | default T blockingGet() { 326 | return invokeAsBlockingPromise().get(); 327 | } 328 | 329 | 330 | } 331 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/reakt/promise/ReplayPromise.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package io.advantageous.reakt.promise; 20 | 21 | import java.util.function.Consumer; 22 | 23 | /** 24 | * Replay promise ensures that the event handler callbacks (then, catchError) happen on the calling thread. 25 | * 26 | * @param T value of the result. 27 | * @author Rick Hightower 28 | */ 29 | public interface ReplayPromise extends PromiseHandler { 30 | 31 | /** 32 | * Return true if timed out. 33 | * If this has timed out, it will be marked completed. 34 | * 35 | * @param time current time 36 | * @return true if done 37 | */ 38 | boolean checkTimeout(long time); 39 | 40 | /** 41 | * @param handler handle timeout. 42 | * @return this fluent 43 | * @throws NullPointerException if result is present and {@code handler} is null 44 | */ 45 | ReplayPromise onTimeout(Runnable handler); 46 | 47 | /** 48 | * Handler after the async result has been processed and data copied to this thread. 49 | * 50 | * @param handler handler 51 | * @return this fluent 52 | */ 53 | ReplayPromise afterResultProcessed(Consumer handler); 54 | 55 | /** 56 | * Replay the promise on another thread. 57 | */ 58 | void replay(); 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/reakt/promise/impl/AllBlockingPromise.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package io.advantageous.reakt.promise.impl; 20 | 21 | import io.advantageous.reakt.Invokable; 22 | import io.advantageous.reakt.promise.Promise; 23 | import io.advantageous.reakt.promise.PromiseHandler; 24 | 25 | public class AllBlockingPromise extends BlockingPromise implements PromiseHandler, Invokable { 26 | 27 | 28 | private final Promise[] promises; 29 | private boolean invoked; 30 | 31 | public AllBlockingPromise(final Promise... promises) { 32 | this.promises = promises; 33 | PromiseUtil.all(this, (Promise[]) promises); 34 | } 35 | 36 | 37 | @Override 38 | public void invoke() { 39 | if (invoked) { 40 | throw new IllegalStateException("PromiseHandler can only be invoked once"); 41 | } 42 | invoked = true; 43 | for (Promise promise : promises) { 44 | if (!promise.asHandler().isInvokable()) { 45 | throw new IllegalStateException("AllBlockingPromise can only be invoked if all children are invokeable"); 46 | } 47 | promise.invoke(); 48 | } 49 | } 50 | 51 | @Override 52 | public boolean isInvokable() { 53 | return true; 54 | } 55 | 56 | } -------------------------------------------------------------------------------- /src/main/java/io/advantageous/reakt/promise/impl/AllPromise.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package io.advantageous.reakt.promise.impl; 20 | 21 | import io.advantageous.reakt.Invokable; 22 | import io.advantageous.reakt.promise.Promise; 23 | import io.advantageous.reakt.promise.PromiseHandler; 24 | 25 | public class AllPromise extends BasePromise implements PromiseHandler, Invokable { 26 | 27 | private final Promise[] promises; 28 | private boolean invoked; 29 | 30 | public AllPromise(final Promise... promises) { 31 | this.promises = (Promise[]) promises; 32 | PromiseUtil.all((Promise) this, this.promises); 33 | } 34 | 35 | 36 | @Override 37 | public void invoke() { 38 | if (invoked) { 39 | throw new IllegalStateException("PromiseHandler can only be invoked once"); 40 | } 41 | invoked = true; 42 | for (Promise promise : promises) { 43 | if (!promise.asHandler().isInvokable()) { 44 | throw new IllegalStateException("AllPromise can only be invoked if all children are invokeable"); 45 | } 46 | promise.invoke(); 47 | } 48 | } 49 | 50 | @Override 51 | public boolean isInvokable() { 52 | return true; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/reakt/promise/impl/AllReplayPromise.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package io.advantageous.reakt.promise.impl; 20 | 21 | import io.advantageous.reakt.Invokable; 22 | import io.advantageous.reakt.promise.Promise; 23 | import io.advantageous.reakt.promise.PromiseHandler; 24 | import io.advantageous.reakt.reactor.Reactor; 25 | 26 | import java.time.Duration; 27 | 28 | public class AllReplayPromise extends ReplayPromiseImpl implements PromiseHandler, Invokable { 29 | 30 | 31 | private final Promise[] promises; 32 | private boolean invoked; 33 | 34 | public AllReplayPromise(final Duration timeout, final long startTime, Promise... promises) { 35 | super(timeout, startTime); 36 | PromiseUtil.all(this, (Promise[]) promises); 37 | this.promises = promises; 38 | } 39 | 40 | 41 | @Override 42 | public PromiseHandler invokeWithReactor(final Reactor reactor) { 43 | if (invoked) { 44 | throw new IllegalStateException("PromiseHandler can only be invoked once"); 45 | } 46 | invoked = true; 47 | for (Promise promise : promises) { 48 | if (!promise.asHandler().isInvokable()) { 49 | throw new IllegalStateException("AllReplayPromise can only be invoked if all children are invokeable"); 50 | } 51 | promise.invoke(); 52 | } 53 | return this; 54 | } 55 | 56 | @Override 57 | public boolean isInvokable() { 58 | return true; 59 | } 60 | } -------------------------------------------------------------------------------- /src/main/java/io/advantageous/reakt/promise/impl/AnyBlockingPromise.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package io.advantageous.reakt.promise.impl; 20 | 21 | import io.advantageous.reakt.promise.Promise; 22 | import io.advantageous.reakt.promise.PromiseHandler; 23 | 24 | public class AnyBlockingPromise extends BlockingPromise implements PromiseHandler { 25 | public AnyBlockingPromise(Promise... promises) { 26 | PromiseUtil.any(this, (Promise[]) promises); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/reakt/promise/impl/AnyPromise.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package io.advantageous.reakt.promise.impl; 20 | 21 | import io.advantageous.reakt.Invokable; 22 | import io.advantageous.reakt.promise.Promise; 23 | import io.advantageous.reakt.promise.PromiseHandler; 24 | 25 | public class AnyPromise extends BasePromise implements PromiseHandler, Invokable { 26 | 27 | private final Promise[] promises; 28 | private boolean invoked; 29 | 30 | public AnyPromise(Promise... promises) { 31 | this.promises = promises; 32 | PromiseUtil.any((Promise) this, (Promise[]) promises); 33 | } 34 | 35 | 36 | @Override 37 | public void invoke() { 38 | if (invoked) { 39 | throw new IllegalStateException("PromiseHandler can only be invoked once"); 40 | } 41 | invoked = true; 42 | for (Promise promise : promises) { 43 | if (!promise.asHandler().isInvokable()) { 44 | throw new IllegalStateException("AnyPromise can only be invoked if all children are invokeable"); 45 | } 46 | promise.invoke(); 47 | } 48 | } 49 | 50 | @Override 51 | public boolean isInvokable() { 52 | return true; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/reakt/promise/impl/AnyReplayPromise.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package io.advantageous.reakt.promise.impl; 20 | 21 | import io.advantageous.reakt.Invokable; 22 | import io.advantageous.reakt.promise.Promise; 23 | import io.advantageous.reakt.promise.PromiseHandler; 24 | import io.advantageous.reakt.reactor.Reactor; 25 | 26 | import java.time.Duration; 27 | 28 | public class AnyReplayPromise extends ReplayPromiseImpl implements PromiseHandler, Invokable { 29 | 30 | private final Promise[] promises; 31 | private boolean invoked; 32 | 33 | public AnyReplayPromise(final Duration timeout, final long startTime, Promise... promises) { 34 | super(timeout, startTime); 35 | this.promises = promises; 36 | PromiseUtil.any(this, (Promise[]) promises); 37 | } 38 | 39 | 40 | @Override 41 | public PromiseHandler invokeWithReactor(final Reactor reactor) { 42 | if (invoked) { 43 | throw new IllegalStateException("PromiseHandler can only be invoked once"); 44 | } 45 | invoked = true; 46 | for (Promise promise : promises) { 47 | if (!promise.asHandler().isInvokable()) { 48 | throw new IllegalStateException("AnyReplayPromise can only be invoked if all children are invokeable"); 49 | } 50 | promise.invoke(); 51 | } 52 | return this; 53 | } 54 | 55 | @Override 56 | public boolean isInvokable() { 57 | return true; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/reakt/promise/impl/BasePromise.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | package io.advantageous.reakt.promise.impl; 19 | 20 | 21 | import io.advantageous.reakt.Expected; 22 | import io.advantageous.reakt.Result; 23 | import io.advantageous.reakt.exception.ThenHandlerException; 24 | import io.advantageous.reakt.promise.PromiseHandler; 25 | import io.advantageous.reakt.reactor.Reactor; 26 | 27 | import java.time.Duration; 28 | import java.util.List; 29 | import java.util.NoSuchElementException; 30 | import java.util.concurrent.CopyOnWriteArrayList; 31 | import java.util.concurrent.atomic.AtomicReference; 32 | import java.util.function.Consumer; 33 | import java.util.function.Function; 34 | 35 | public class BasePromise implements PromiseHandler { 36 | 37 | 38 | protected final AtomicReference> result = new AtomicReference<>(); 39 | protected Expected> thenConsumer = Expected.empty(); 40 | protected Expected>> thenExpectedConsumer = Expected.empty(); 41 | protected Expected> catchConsumer = Expected.empty(); 42 | protected Expected>>> completeListeners = Expected.empty(); 43 | private boolean safe; 44 | 45 | public static PromiseHandler provideFinalPromise(PromiseHandler promise) { 46 | if (promise instanceof BasePromise) { 47 | BasePromise basePromise = ((BasePromise) promise); 48 | return new FinalPromise<>(basePromise.thenConsumer, 49 | basePromise.catchConsumer, 50 | basePromise.thenExpectedConsumer, 51 | basePromise.completeListeners, true); 52 | } else { 53 | throw new IllegalStateException("Operation not supported use FinalPromise directly"); 54 | } 55 | } 56 | 57 | @Override 58 | public PromiseHandler thenSafeExpect(Consumer> consumer) { 59 | safe = true; 60 | thenExpectedConsumer = Expected.of(consumer); 61 | return this; 62 | } 63 | 64 | @Override 65 | public PromiseHandler thenSafe(Consumer consumer) { 66 | safe = true; 67 | thenConsumer = Expected.of(consumer); 68 | return this; 69 | } 70 | 71 | @Override 72 | public boolean supportsSafe() { 73 | return true; 74 | } 75 | 76 | public synchronized PromiseHandler then(final Consumer consumer) { 77 | thenConsumer = Expected.of(consumer); 78 | return this; 79 | } 80 | 81 | @Override 82 | public PromiseHandler whenComplete(final Consumer> doneListener) { 83 | if (completeListeners.isEmpty()) { 84 | completeListeners = Expected.of(new CopyOnWriteArrayList<>()); 85 | } 86 | completeListeners.get().add(doneListener); 87 | return this; 88 | } 89 | 90 | @Override 91 | public synchronized PromiseHandler thenExpect(Consumer> consumer) { 92 | thenExpectedConsumer = Expected.of(consumer); 93 | return this; 94 | } 95 | 96 | @Override 97 | public PromiseHandler catchError(Consumer consumer) { 98 | catchConsumer = Expected.of(consumer); 99 | return this; 100 | } 101 | 102 | @Override 103 | public PromiseHandler invokeWithReactor(final Reactor reactor) { 104 | final BasePromise reactorPromise = (BasePromise) reactor.promise(); 105 | copyPromiseFieldsToReactorPromise(reactorPromise); 106 | return this; 107 | } 108 | 109 | 110 | @Override 111 | public PromiseHandler invokeWithReactor(final Reactor reactor, Duration timeout) { 112 | final BasePromise reactorPromise = (BasePromise) reactor.promise(timeout); 113 | copyPromiseFieldsToReactorPromise(reactorPromise); 114 | return this; 115 | } 116 | 117 | 118 | @Override 119 | public boolean success() { 120 | if (!complete()) { 121 | throw new NoSuchElementException("No value present, result not returned."); 122 | } 123 | return result.get().success(); 124 | } 125 | 126 | @Override 127 | public boolean complete() { 128 | return result.get() != null; 129 | } 130 | 131 | @Override 132 | public boolean failure() { 133 | 134 | if (!complete()) { 135 | throw new NoSuchElementException("No value present, result not returned."); 136 | } 137 | return result.get().failure(); 138 | } 139 | 140 | @Override 141 | public Throwable cause() { 142 | 143 | if (!complete()) { 144 | throw new NoSuchElementException("No value present, result not returned."); 145 | } 146 | return result.get().cause(); 147 | } 148 | 149 | /** 150 | * If the value of the promise can be null, it is better to use Expected which is like Optional. 151 | * 152 | * @return value associated with a successful result. 153 | */ 154 | public Expected expect() { 155 | if (!complete()) { 156 | throw new NoSuchElementException("No value present, result not returned."); 157 | } 158 | if (failure()) { 159 | throw new IllegalStateException(cause()); 160 | } 161 | return result.get().expect(); 162 | } 163 | 164 | /** 165 | * Raw value of the result. 166 | * You should not use this if the result could be null, use expect instead. 167 | * 168 | * @return raw value associated with the result. 169 | */ 170 | public T get() { 171 | return PromiseUtil.doGet(result, this); 172 | } 173 | 174 | @Override 175 | public T orElse(T other) { 176 | if (!complete()) { 177 | throw new NoSuchElementException("No value present, result not returned."); 178 | } 179 | return success() ? result.get().get() : other; 180 | } 181 | 182 | @Override 183 | public void onResult(final Result result) { 184 | if (this.result.compareAndSet(null, result)) { 185 | doOnResult(result); 186 | } 187 | } 188 | 189 | protected void doOnResult(final Result result) { 190 | 191 | 192 | if (result.success()) { 193 | if (!safe) { 194 | thenConsumer.ifPresent(consumer -> consumer.accept(result.get())); 195 | thenExpectedConsumer.ifPresent(valueConsumer -> valueConsumer.accept(result.expect())); 196 | } else { 197 | try { 198 | thenConsumer.ifPresent(consumer -> consumer.accept(result.get())); 199 | thenExpectedConsumer.ifPresent(valueConsumer -> valueConsumer.accept(result.expect())); 200 | } catch (Exception ex) { 201 | catchConsumer.ifPresent(catchConsumer -> catchConsumer.accept( 202 | new ThenHandlerException(ex))); 203 | } 204 | } 205 | } else { 206 | catchConsumer.ifPresent(catchConsumer -> catchConsumer.accept(result.cause())); 207 | } 208 | 209 | this.completeListeners.ifPresent(runnables -> 210 | runnables.forEach(promiseConsumer -> promiseConsumer.accept(this))); 211 | } 212 | 213 | 214 | @Override 215 | public PromiseHandler thenMap(Function mapper) { 216 | return PromiseUtil.mapPromise(this, mapper); 217 | } 218 | 219 | private void copyPromiseFieldsToReactorPromise(BasePromise reactorPromise) { 220 | reactorPromise.catchConsumer = this.catchConsumer; 221 | reactorPromise.thenConsumer = this.thenConsumer; 222 | reactorPromise.thenExpectedConsumer = this.thenExpectedConsumer; 223 | reactorPromise.safe = this.safe; 224 | this.thenExpectedConsumer = Expected.empty(); 225 | this.thenConsumer = Expected.empty(); 226 | this.catchConsumer = Expected.empty(); 227 | 228 | 229 | completeListeners.ifPresent(consumers -> 230 | consumers.forEach(reactorPromise::whenComplete)); 231 | 232 | this.thenPromise(reactorPromise); 233 | this.invoke(); 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/reakt/promise/impl/BlockingPromise.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package io.advantageous.reakt.promise.impl; 20 | 21 | import io.advantageous.reakt.Expected; 22 | import io.advantageous.reakt.Result; 23 | import io.advantageous.reakt.promise.PromiseHandler; 24 | import io.advantageous.reakt.promise.Promises; 25 | 26 | import java.time.Duration; 27 | import java.util.concurrent.Callable; 28 | import java.util.concurrent.CountDownLatch; 29 | import java.util.function.Function; 30 | 31 | import static java.util.concurrent.TimeUnit.MILLISECONDS; 32 | 33 | /** 34 | * This is very much like a Java Future. It is blocking. 35 | * This is useful for testing and for legacy integration. 36 | * 37 | * @param value of result. 38 | */ 39 | public class BlockingPromise extends BasePromise { 40 | 41 | private final CountDownLatch countDownLatch = new CountDownLatch(1); 42 | private final Expected duration; 43 | 44 | public BlockingPromise() { 45 | this.duration = Expected.empty(); 46 | } 47 | 48 | 49 | public BlockingPromise(final Duration duration) { 50 | this.duration = Expected.of(duration); 51 | } 52 | 53 | @Override 54 | public void onResult(Result result) { 55 | super.onResult(result); 56 | countDownLatch.countDown(); 57 | } 58 | 59 | @Override 60 | public Expected expect() { 61 | await(); 62 | return super.expect(); 63 | } 64 | 65 | @Override 66 | public T get() { 67 | await(); 68 | return super.get(); 69 | } 70 | 71 | @Override 72 | public Throwable cause() { 73 | await(); 74 | return super.cause(); 75 | } 76 | 77 | @Override 78 | public boolean failure() { 79 | await(); 80 | return super.failure(); 81 | } 82 | 83 | @Override 84 | public boolean success() { 85 | await(); 86 | return super.success(); 87 | } 88 | 89 | 90 | private void doAwait(final Callable runnable) { 91 | try { 92 | runnable.call(); 93 | } catch (Exception e) { 94 | throw new IllegalStateException(e); 95 | } 96 | } 97 | 98 | 99 | protected boolean _success() { 100 | return super.success(); 101 | } 102 | 103 | 104 | private void await() { 105 | duration.ifPresent(duration1 -> { 106 | doAwait(() -> { 107 | countDownLatch.await(duration1.toMillis(), MILLISECONDS); 108 | return null; 109 | }); 110 | }).ifEmpty(() -> { 111 | doAwait(() -> { 112 | countDownLatch.await(); 113 | return null; 114 | }); 115 | }); 116 | } 117 | 118 | @Override 119 | public PromiseHandler thenMap(Function mapper) { 120 | final PromiseHandler mappedPromise = (PromiseHandler) Promises.blockingPromise(); 121 | this.whenComplete(p -> { 122 | final BlockingPromise promise = (BlockingPromise) p; 123 | if (promise._success()) { 124 | final T t = promise.result.get().get(); 125 | final U mapped = mapper.apply(t); 126 | mappedPromise.resolve(mapped); 127 | } else { 128 | mappedPromise.reject(promise.cause()); 129 | } 130 | }); 131 | return mappedPromise; 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/reakt/promise/impl/FinalPromise.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package io.advantageous.reakt.promise.impl; 20 | 21 | 22 | import io.advantageous.reakt.Expected; 23 | import io.advantageous.reakt.Result; 24 | import io.advantageous.reakt.exception.ThenHandlerException; 25 | import io.advantageous.reakt.promise.PromiseHandler; 26 | import io.advantageous.reakt.reactor.Reactor; 27 | 28 | import java.time.Duration; 29 | import java.util.List; 30 | import java.util.NoSuchElementException; 31 | import java.util.concurrent.atomic.AtomicReference; 32 | import java.util.function.Consumer; 33 | import java.util.function.Function; 34 | 35 | public class FinalPromise implements PromiseHandler { 36 | 37 | protected final Expected>>> completeListeners; 38 | protected final AtomicReference> result = new AtomicReference<>(); 39 | protected final Expected> thenConsumer; 40 | protected final Expected> catchConsumer; 41 | protected final Expected>> thenValueConsumer; 42 | private final boolean safe; 43 | 44 | public FinalPromise(Expected> thenConsumer, 45 | Expected> catchConsumer, 46 | Expected>> thenValueConsumer, 47 | Expected>>> completeListeners, 48 | boolean safe) { 49 | this.thenConsumer = thenConsumer; 50 | this.catchConsumer = catchConsumer; 51 | this.thenValueConsumer = thenValueConsumer; 52 | this.completeListeners = completeListeners; 53 | this.safe = safe; 54 | } 55 | 56 | 57 | protected void doFail(Throwable cause) { 58 | catchConsumer.ifPresent(catchConsumer -> catchConsumer.accept(cause)); 59 | } 60 | 61 | protected void doThen(T value) { 62 | thenConsumer.ifPresent(consumer -> consumer.accept(value)); 63 | } 64 | 65 | @Override 66 | public PromiseHandler thenSafeExpect(Consumer> consumer) { 67 | throw new UnsupportedOperationException("then(..) not supported for final promise"); 68 | } 69 | 70 | @Override 71 | public PromiseHandler thenSafe(Consumer consumer) { 72 | throw new UnsupportedOperationException("thenSafe(..) not supported for final promise"); 73 | } 74 | 75 | @Override 76 | public boolean supportsSafe() { 77 | throw new UnsupportedOperationException("supportsSafe(..) not supported for final promise"); 78 | } 79 | 80 | public PromiseHandler then(final Consumer consumer) { 81 | throw new UnsupportedOperationException("then(..) not supported for final promise"); 82 | 83 | } 84 | 85 | @Override 86 | public PromiseHandler whenComplete(Consumer> doneListener) { 87 | throw new UnsupportedOperationException("whenComplete(..) not supported for final promise"); 88 | } 89 | 90 | 91 | @Override 92 | public synchronized PromiseHandler thenExpect(Consumer> consumer) { 93 | throw new UnsupportedOperationException("thenExpect(..) not supported for final promise"); 94 | } 95 | 96 | @Override 97 | public PromiseHandler catchError(Consumer consumer) { 98 | throw new UnsupportedOperationException("catchError(..) not supported for final promise"); 99 | } 100 | 101 | @Override 102 | public PromiseHandler invokeWithReactor(Reactor reactor) { 103 | throw new UnsupportedOperationException("invokeWithReactor(..) not supported for final promise"); 104 | } 105 | 106 | @Override 107 | public PromiseHandler invokeWithReactor(Reactor reactor, Duration timeout) { 108 | throw new UnsupportedOperationException("invokeWithReactor(..) not supported for final promise"); 109 | } 110 | 111 | @Override 112 | public boolean success() { 113 | if (result.get() == null) { 114 | throw new NoSuchElementException("No value present, result not returned."); 115 | } 116 | return result.get().success(); 117 | } 118 | 119 | @Override 120 | public boolean complete() { 121 | return result.get() != null; 122 | } 123 | 124 | @Override 125 | public boolean failure() { 126 | 127 | if (!complete()) { 128 | throw new NoSuchElementException("No value present, result not returned."); 129 | } 130 | return result.get().failure(); 131 | } 132 | 133 | @Override 134 | public Throwable cause() { 135 | 136 | if (!complete()) { 137 | throw new NoSuchElementException("No value present, result not returned."); 138 | } 139 | return result.get().cause(); 140 | } 141 | 142 | 143 | /** 144 | * If the value of the promise can be null, it is better to use Expected which is like Optional. 145 | * 146 | * @return value associated with a successful result. 147 | */ 148 | public Expected expect() { 149 | if (!complete()) { 150 | throw new NoSuchElementException("No value present, result not returned."); 151 | } 152 | if (failure()) { 153 | throw new IllegalStateException(cause()); 154 | } 155 | return result.get().expect(); 156 | } 157 | 158 | /** 159 | * Raw value of the result. 160 | * You should not use this if the result could be null, use expect instead. 161 | * 162 | * @return raw value associated with the result. 163 | */ 164 | public T get() { 165 | return PromiseUtil.doGet(result, this); 166 | } 167 | 168 | @Override 169 | public void onResult(Result result) { 170 | if (this.result.compareAndSet(null, result)) { 171 | doOnResult(result); 172 | } 173 | } 174 | 175 | 176 | protected void doOnResult(Result result) { 177 | if (result.success()) { 178 | handleSuccess(result); 179 | } else { 180 | catchConsumer.ifPresent(catchConsumer -> catchConsumer.accept(result.cause())); 181 | } 182 | this.completeListeners.ifPresent(runnables -> 183 | runnables.forEach(promiseConsumer -> promiseConsumer.accept(this))); 184 | } 185 | 186 | private void handleSuccess(Result result) { 187 | if (safe) { 188 | try { 189 | thenConsumer.ifPresent(consumer -> consumer.accept(result.get())); 190 | thenValueConsumer.ifPresent(valueConsumer -> valueConsumer.accept(result.expect())); 191 | } catch (Exception ex) { 192 | catchConsumer.ifPresent(catchConsumer -> catchConsumer.accept(new ThenHandlerException(ex))); 193 | } 194 | } else { 195 | thenConsumer.ifPresent(consumer -> consumer.accept(result.get())); 196 | thenValueConsumer.ifPresent(valueConsumer -> valueConsumer.accept(result.expect())); 197 | } 198 | } 199 | 200 | protected void doThenValue(final Result result) { 201 | this.thenValueConsumer.ifPresent(valueConsumer -> valueConsumer.accept(result.expect())); 202 | } 203 | 204 | @Override 205 | public PromiseHandler thenMap(Function mapper) { 206 | throw new UnsupportedOperationException("then(..) not supported for final promise"); 207 | } 208 | 209 | @Override 210 | public T orElse(T other) { 211 | if (!complete()) { 212 | throw new NoSuchElementException("No value present, result not returned."); 213 | } 214 | return success() ? result.get().get() : other; 215 | } 216 | 217 | } 218 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/reakt/promise/impl/InvokerPromise.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package io.advantageous.reakt.promise.impl; 20 | 21 | import io.advantageous.reakt.Callback; 22 | import io.advantageous.reakt.Invokable; 23 | 24 | import java.util.function.Consumer; 25 | 26 | public class InvokerPromise extends BasePromise implements Invokable { 27 | 28 | private final Consumer> consumer; 29 | private boolean invoked; 30 | 31 | public InvokerPromise(Consumer> consumer) { 32 | this.consumer = consumer; 33 | } 34 | 35 | @Override 36 | public void invoke() { 37 | if (invoked) { 38 | throw new IllegalStateException("PromiseHandler can only be invoked once"); 39 | } 40 | invoked = true; 41 | consumer.accept(this); 42 | } 43 | 44 | @Override 45 | public boolean isInvokable() { 46 | return true; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/reakt/promise/impl/PromiseUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package io.advantageous.reakt.promise.impl; 20 | 21 | import io.advantageous.reakt.Result; 22 | import io.advantageous.reakt.exception.RejectedPromiseException; 23 | import io.advantageous.reakt.promise.Promise; 24 | import io.advantageous.reakt.promise.PromiseHandler; 25 | import io.advantageous.reakt.promise.Promises; 26 | 27 | import java.util.NoSuchElementException; 28 | import java.util.concurrent.atomic.AtomicBoolean; 29 | import java.util.concurrent.atomic.AtomicInteger; 30 | import java.util.concurrent.atomic.AtomicReference; 31 | import java.util.function.Consumer; 32 | import java.util.function.Function; 33 | 34 | public interface PromiseUtil { 35 | 36 | /** 37 | * Does the all logic for All*PromiseHandler. 38 | * This promise only fires (comes back) if all of the child promises come back. 39 | * 40 | * @param parent parent 41 | * @param childPromises promises that all have to come back before this promise comes back 42 | * @param type of result 43 | */ 44 | static void all(Promise parent, Promise[] childPromises) { 45 | final AtomicInteger count = new AtomicInteger(childPromises.length); 46 | final AtomicBoolean done = new AtomicBoolean(); 47 | 48 | final Consumer> consumer = (childPromise) -> { 49 | 50 | if (done.get()) { 51 | return; 52 | } 53 | 54 | /** If any promise fails then stop processing. */ 55 | if (childPromise.failure()) { 56 | if (done.compareAndSet(false, true)) { 57 | parent.asHandler().reject(childPromise.cause()); 58 | count.set(-1); 59 | } 60 | } else { 61 | /** If the count is 0, then we are done. */ 62 | int currentCount = count.decrementAndGet(); 63 | if (currentCount == 0 && !parent.asHandler().complete()) { 64 | if (done.compareAndSet(false, true)) { 65 | parent.asHandler().onResult(Result.result(null)); 66 | } 67 | } 68 | } 69 | }; 70 | /** Register the listener. */ 71 | for (Promise childPromise : childPromises) { 72 | childPromise.asHandler().whenComplete(consumer); 73 | } 74 | } 75 | 76 | /** 77 | * Does the any logic for Any*PromiseHandler. 78 | * If any child comes back, then the parent comes back. 79 | * 80 | * @param parent parent promise 81 | * @param childPromises list of promises 82 | * @param type of result 83 | */ 84 | static void any(Promise parent, Promise[] childPromises) { 85 | 86 | 87 | final AtomicBoolean done = new AtomicBoolean(); 88 | final Consumer> runnable = (childPromise) -> { 89 | /** If any promise fails then stop processing. */ 90 | if (childPromise.failure()) { 91 | if (done.compareAndSet(false, true)) { 92 | parent.asHandler().reject(childPromise.cause()); 93 | } 94 | } else { 95 | /** Only fire if the child promise is the first promise 96 | * so the parent does not fire multiple times. */ 97 | if (done.compareAndSet(false, true)) { 98 | parent.asHandler().reject(childPromise.cause()); 99 | } 100 | } 101 | 102 | }; 103 | for (Promise childPromise : childPromises) { 104 | childPromise.asHandler().whenComplete(runnable); 105 | } 106 | } 107 | 108 | static PromiseHandler mapPromise(PromiseHandler thisPromise, Function mapper) { 109 | final PromiseHandler mappedPromise = (PromiseHandler) Promises.promise(); 110 | thisPromise.whenComplete(promise -> { 111 | if (promise.success()) { 112 | final U mapped = mapper.apply(promise.get()); 113 | mappedPromise.resolve(mapped); 114 | } else { 115 | mappedPromise.reject(promise.cause()); 116 | } 117 | }); 118 | return mappedPromise; 119 | } 120 | 121 | 122 | static T doGet(AtomicReference> result, PromiseHandler promise) { 123 | 124 | if (!promise.complete()) { 125 | throw new NoSuchElementException("No value present, result not returned."); 126 | } 127 | if (promise.failure()) { 128 | if (promise.cause() instanceof RuntimeException) { 129 | throw (RuntimeException) promise.cause(); 130 | } else { 131 | throw new RejectedPromiseException(promise.cause()); 132 | } 133 | } 134 | return result.get().get(); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/reakt/promise/impl/ReplayPromiseImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package io.advantageous.reakt.promise.impl; 20 | 21 | import io.advantageous.reakt.Expected; 22 | import io.advantageous.reakt.Result; 23 | import io.advantageous.reakt.impl.ResultImpl; 24 | import io.advantageous.reakt.promise.PromiseHandler; 25 | import io.advantageous.reakt.promise.ReplayPromise; 26 | 27 | import java.time.Duration; 28 | import java.util.concurrent.TimeoutException; 29 | import java.util.concurrent.atomic.AtomicBoolean; 30 | import java.util.function.Consumer; 31 | 32 | public class ReplayPromiseImpl extends BasePromise implements ReplayPromise { 33 | 34 | private final Duration timeoutDuration; 35 | private final long startTime; 36 | private final AtomicBoolean replayed = new AtomicBoolean(); 37 | private Expected timeoutHandler = Expected.empty(); 38 | private Expected> afterResultProcessedHandler = Expected.empty(); 39 | 40 | 41 | public ReplayPromiseImpl(final Duration timeout, final long startTime) { 42 | 43 | this.timeoutDuration = timeout; 44 | this.startTime = startTime; 45 | } 46 | 47 | @Override 48 | public void onResult(final Result result) { 49 | 50 | //Ensure this is only handled one time. 51 | if (this.result.compareAndSet(null, result)) { 52 | afterResultProcessedHandler.ifPresent(replayPromiseConsumer -> replayPromiseConsumer.accept(this)); 53 | } 54 | 55 | } 56 | 57 | @Override 58 | public boolean checkTimeout(final long time) { 59 | 60 | if (!complete()) { 61 | if ((time - startTime) > timeoutDuration.toMillis()) { 62 | handleTimeout(time); 63 | return true; 64 | } else { 65 | return false; 66 | } 67 | } else { 68 | return false; 69 | } 70 | 71 | } 72 | 73 | private void handleResultPresent(Result theResult) { 74 | doOnResult(theResult); 75 | } 76 | 77 | private void handleTimeout(long time) { 78 | timeoutHandler.ifPresent(Runnable::run); 79 | result.set(new ResultImpl<>( 80 | new TimeoutException(String.format("Operation timed out start time %d timeout " + 81 | "duration ms %d time %d elapsed time %d", 82 | startTime, timeoutDuration.toMillis(), time, time - startTime)))); 83 | replay(); 84 | } 85 | 86 | @Override 87 | public synchronized ReplayPromise onTimeout(final Runnable handler) { 88 | timeoutHandler = Expected.of(handler); 89 | return this; 90 | } 91 | 92 | @Override 93 | public synchronized ReplayPromise afterResultProcessed(Consumer handler) { 94 | afterResultProcessedHandler = Expected.of(handler); 95 | return this; 96 | } 97 | 98 | 99 | @Override 100 | public void replay() { 101 | if (replayed.compareAndSet(false, true)) { 102 | handleResultPresent(result.get()); 103 | } 104 | } 105 | 106 | @Override 107 | public PromiseHandler freeze() { 108 | throw new IllegalStateException("Freeze (freeze()) only makes sense for callback " + 109 | "and blocking promises because replay promises are only accessed from one " + 110 | "thread so mutability is ok."); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/reakt/reactor/Reactor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package io.advantageous.reakt.reactor; 20 | 21 | import io.advantageous.reakt.promise.Promise; 22 | import io.advantageous.reakt.reactor.impl.ReactorImpl; 23 | 24 | import java.time.Duration; 25 | import java.util.Collection; 26 | import java.util.List; 27 | import java.util.Map; 28 | import java.util.Set; 29 | 30 | /** 31 | * Ensures that tasks, repeating tasks and callbacks run in the callers thread. 32 | * Used with actor service models like QBit, Vertx, etc. 33 | * 34 | * @author Rick Hightower 35 | */ 36 | public interface Reactor { 37 | 38 | /** 39 | * Creates a default reactor. 40 | * 41 | * @return a reactor 42 | */ 43 | static Reactor reactor() { 44 | return reactor(Duration.ofSeconds(30)); 45 | } 46 | 47 | /** 48 | * Creates a default reactor with timeout. 49 | * 50 | * @param timeout timeout 51 | * @return a reactor 52 | */ 53 | static Reactor reactor(final Duration timeout) { 54 | return reactor(timeout, System::currentTimeMillis); 55 | } 56 | 57 | /** 58 | * Creates a default reactor with timeout and timesource. 59 | * 60 | * @param timeout timeout 61 | * @param timeSource time source 62 | * @return a reactor 63 | */ 64 | static Reactor reactor(final Duration timeout, final TimeSource timeSource) { 65 | return new ReactorImpl(timeout, timeSource); 66 | } 67 | 68 | /** 69 | * Create a promise. 70 | * After you create a promise you register its then(...) and catchError(...) and then you use it to 71 | * handle a callback. 72 | *

73 | * Creates a replay promise that is managed by this Reactor. 74 | * 75 | * @param type of result 76 | * @return new promise 77 | */ 78 | Promise promise(); 79 | 80 | 81 | Promise promise(Duration timeout); 82 | 83 | 84 | /** 85 | * All promises must complete. 86 | * 87 | * @param promises promises 88 | * @return return containing promise 89 | */ 90 | Promise all(final Promise... promises); 91 | 92 | 93 | /** 94 | * All promises must complete. 95 | * 96 | * @param timeout timeout 97 | * @param promises promises 98 | * @return return containing promise 99 | */ 100 | Promise all(final Duration timeout, final Promise... promises); 101 | 102 | /** 103 | * All promises must complete. 104 | * 105 | * @param promises promises 106 | * @param types of promise 107 | * @return return containing promise 108 | */ 109 | Promise all(final List> promises); 110 | 111 | /** 112 | * All promises must complete. 113 | * 114 | * @param timeout timeout 115 | * @param promises promises 116 | * @param types of promise 117 | * @return return containing promise 118 | */ 119 | Promise all(final Duration timeout, final List> promises); 120 | 121 | /** 122 | * Any promises must complete. 123 | * 124 | * @param promises promises 125 | * @return return containing promise 126 | */ 127 | Promise any(final Promise... promises); 128 | 129 | /** 130 | * Any promises must complete. 131 | * 132 | * @param timeout timeout 133 | * @param promises promises 134 | * @return return containing promise 135 | */ 136 | Promise any(final Duration timeout, final Promise... promises); 137 | 138 | /** 139 | * All promises must complete. 140 | * 141 | * @param promises promises 142 | * @param types of promise 143 | * @return return containing promise 144 | */ 145 | Promise any(final List> promises); 146 | 147 | /** 148 | * All promises must complete. 149 | * 150 | * @param timeout timeout 151 | * @param promises promises 152 | * @param types of promise 153 | * @return return containing promise 154 | */ 155 | Promise any(final Duration timeout, final List> promises); 156 | 157 | /** 158 | * Add a repeating task that will run every interval 159 | * 160 | * @param interval duration of interval 161 | * @param runnable runnable to run. 162 | */ 163 | void addRepeatingTask(final Duration interval, final Runnable runnable); 164 | 165 | /** 166 | * Add a task that will run once after the interval. 167 | * 168 | * @param afterInterval duration of interval 169 | * @param runnable runnable to run. 170 | */ 171 | void runTaskAfter(final Duration afterInterval, final Runnable runnable); 172 | 173 | /** 174 | * Run on this Reactor's thread as soon as you can. 175 | * 176 | * @param runnable runnable 177 | */ 178 | void deferRun(final Runnable runnable); 179 | 180 | /** 181 | * Allows the reactor to process its tasks, and promises (callbacks). 182 | */ 183 | void process(); 184 | 185 | /** 186 | * Returns a String promise 187 | * 188 | * @return returns a string promise 189 | */ 190 | Promise promiseString(); 191 | 192 | /** 193 | * Returns a Integer promise 194 | * 195 | * @return returns an int promise 196 | */ 197 | Promise promiseInt(); 198 | 199 | /** 200 | * Returns a Long promise 201 | * 202 | * @return returns an long promise 203 | */ 204 | Promise promiseLong(); 205 | 206 | /** 207 | * Returns a Double promise 208 | * 209 | * @return returns an double promise 210 | */ 211 | Promise promiseDouble(); 212 | 213 | /** 214 | * Returns a Float promise 215 | * 216 | * @return returns an float promise 217 | */ 218 | Promise promiseFloat(); 219 | 220 | /** 221 | * Returns a void promise for notify of outcome but no value returned. 222 | *

223 | * CallbackHandler replyDone can be used instead of replay on service side. 224 | * 225 | * @return void promise 226 | */ 227 | Promise promiseNotify(); 228 | 229 | /** 230 | * Boolean promise 231 | * 232 | * @return promises a boolean 233 | */ 234 | Promise promiseBoolean(); 235 | 236 | 237 | /** 238 | * Generic promise. 239 | * 240 | * @param cls type 241 | * @param promise of a result of T 242 | * @return new PromiseHandler of type T 243 | */ 244 | @SuppressWarnings("unused") 245 | Promise promise(final Class cls); 246 | 247 | 248 | /** 249 | * Generic list promise. 250 | * 251 | * @param componentType component type of list 252 | * @param promise a list of type T 253 | * @return new PromiseHandler for a list of type T 254 | */ 255 | @SuppressWarnings("unused") 256 | Promise> promiseList(final Class componentType); 257 | 258 | /** 259 | * Generic collection promise. 260 | * 261 | * @param componentType component type of collection 262 | * @param promise a collection of type T 263 | * @return new PromiseHandler for a collection of type T 264 | */ 265 | @SuppressWarnings("unused") 266 | Promise> promiseCollection(final Class componentType); 267 | 268 | /** 269 | * Generic map promise. 270 | * 271 | * @param keyType type of map key 272 | * @param valueType type of map value 273 | * @param promise a map of key type K 274 | * @param promise a map of value type V 275 | * @return new PromiseHandler for a collection of type T 276 | */ 277 | @SuppressWarnings("unused") 278 | Promise> promiseMap(final Class keyType, final Class valueType); 279 | 280 | /** 281 | * Generic set promise. 282 | * 283 | * @param componentType component type of set 284 | * @param promise a set of type T 285 | * @return new PromiseHandler for a set of type T 286 | */ 287 | @SuppressWarnings("unused") 288 | Promise> promiseSet(final Class componentType); 289 | 290 | 291 | } 292 | -------------------------------------------------------------------------------- /src/main/java/io/advantageous/reakt/reactor/TimeSource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package io.advantageous.reakt.reactor; 20 | 21 | /** 22 | * Time source for a reactor. This interface is used so that a thread may provide time when available instead of 23 | * calling the system every time a check in in needed. 24 | * 25 | * @author Rick Hightower 26 | */ 27 | public interface TimeSource { 28 | long getTime(); 29 | } 30 | -------------------------------------------------------------------------------- /src/test/java/io/advantageous/reakt/BreakerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package io.advantageous.reakt; 20 | 21 | import org.junit.Test; 22 | 23 | import java.util.concurrent.atomic.AtomicBoolean; 24 | import java.util.concurrent.atomic.AtomicInteger; 25 | 26 | import static org.junit.Assert.*; 27 | 28 | public class BreakerTest { 29 | 30 | @Test 31 | public void testBroken() { 32 | 33 | AtomicBoolean okCalled = new AtomicBoolean(); 34 | 35 | AtomicBoolean brokenCalled = new AtomicBoolean(); 36 | 37 | final Breaker broken = Breaker.broken(); 38 | broken.ifOk(o -> okCalled.set(true)); 39 | 40 | broken.ifBroken(() -> brokenCalled.set(true)); 41 | 42 | assertFalse(okCalled.get()); 43 | assertTrue(broken.isBroken()); 44 | assertTrue(brokenCalled.get()); 45 | assertFalse(broken.isOk()); 46 | 47 | broken.cleanup(o -> { 48 | }); 49 | } 50 | 51 | 52 | @Test 53 | public void testSupplierNotBroken() { 54 | final Breaker ok = Breaker.operational(new Object(), o -> false); 55 | assertTrue(ok.isOk()); 56 | } 57 | 58 | @Test 59 | public void testSupplierBroken() { 60 | final Breaker ok = Breaker.operational(new Object(), o -> true); 61 | assertTrue(!ok.isOk()); 62 | } 63 | 64 | 65 | @Test 66 | public void testSupplierOkThenBroken() { 67 | final AtomicInteger service = new AtomicInteger(); 68 | 69 | final Breaker breaker = Breaker.operational(service, theService -> !(theService.get() == 0)); 70 | assertTrue(breaker.isOk()); 71 | 72 | service.incrementAndGet(); 73 | 74 | assertTrue(breaker.isBroken()); 75 | 76 | } 77 | 78 | 79 | @Test 80 | public void testOK() { 81 | 82 | AtomicBoolean okCalled = new AtomicBoolean(); 83 | AtomicBoolean brokenCalled = new AtomicBoolean(); 84 | final Breaker ok = Breaker.operational(new Object()); 85 | ok.ifOk(o -> okCalled.set(true)); 86 | ok.ifBroken(() -> brokenCalled.set(true)); 87 | 88 | 89 | assertEquals(0, ok.errorCount()); 90 | assertTrue(okCalled.get()); 91 | assertFalse(ok.isBroken()); 92 | assertTrue(ok.isOk()); 93 | 94 | try { 95 | ok.ifOk(o -> { 96 | throw new IllegalStateException("ack"); 97 | }); 98 | fail(); 99 | } catch (Exception ex) { 100 | 101 | } 102 | 103 | assertEquals(1, ok.errorCount()); 104 | 105 | } 106 | 107 | @Test 108 | public void testOKWithErrorCount() { 109 | 110 | AtomicBoolean okCalled = new AtomicBoolean(); 111 | AtomicBoolean brokenCalled = new AtomicBoolean(); 112 | final Breaker ok = Breaker.operational(new Object(), 10); 113 | ok.ifOk(o -> okCalled.set(true)); 114 | ok.ifBroken(() -> brokenCalled.set(true)); 115 | 116 | 117 | assertEquals(0, ok.errorCount()); 118 | assertTrue(okCalled.get()); 119 | assertFalse(ok.isBroken()); 120 | assertTrue(ok.isOk()); 121 | 122 | try { 123 | ok.ifOk(o -> { 124 | throw new IllegalStateException("ack"); 125 | }); 126 | fail(); 127 | } catch (Exception ex) { 128 | 129 | } 130 | 131 | assertEquals(1, ok.errorCount()); 132 | 133 | assertFalse(ok.isBroken()); 134 | 135 | 136 | for (int index = 0; index < 20; index++) { 137 | try { 138 | ok.ifOk(o -> { 139 | throw new IllegalStateException("ack"); 140 | }); 141 | } catch (Exception ex) { 142 | 143 | } 144 | } 145 | 146 | assertEquals(10, ok.errorCount()); 147 | 148 | assertTrue(ok.isBroken()); 149 | 150 | } 151 | 152 | 153 | } -------------------------------------------------------------------------------- /src/test/java/io/advantageous/reakt/CallbackTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package io.advantageous.reakt; 20 | 21 | import org.junit.Test; 22 | 23 | import static org.junit.Assert.*; 24 | 25 | public class CallbackTest { 26 | 27 | @Test 28 | public void test() throws Exception { 29 | 30 | TestService testService = new TestService(); 31 | Result[] results = new Result[1]; 32 | Employee[] employee = new Employee[1]; 33 | testService.simple(result -> { 34 | results[0] = result; 35 | result.then(e -> employee[0] = e).catchError(error -> { 36 | System.err.println(error.getMessage()); 37 | }); 38 | }); 39 | 40 | assertTrue(results[0].complete()); 41 | assertFalse(results[0].failure()); 42 | assertTrue(results[0].success()); 43 | assertNotNull(employee[0]); 44 | } 45 | 46 | 47 | @Test 48 | public void testError() throws Exception { 49 | 50 | TestService testService = new TestService(); 51 | Result[] results = new Result[1]; 52 | testService.error(result -> { 53 | results[0] = result; 54 | 55 | }); 56 | assertTrue(results[0].complete()); 57 | assertTrue(results[0].failure()); 58 | assertFalse(results[0].success()); 59 | } 60 | 61 | 62 | @Test 63 | public void testErrorConsumer() throws Exception { 64 | 65 | TestService testService = new TestService(); 66 | Result[] results = new Result[1]; 67 | testService.errorConsumer(result -> { 68 | results[0] = result; 69 | 70 | }); 71 | assertTrue(results[0].complete()); 72 | assertTrue(results[0].failure()); 73 | assertFalse(results[0].success()); 74 | } 75 | 76 | 77 | @Test 78 | public void testConsumer() throws Exception { 79 | 80 | TestService testService = new TestService(); 81 | Result[] results = new Result[1]; 82 | Employee[] employee = new Employee[1]; 83 | testService.simpleConsumer(result -> { 84 | results[0] = result; 85 | result.then(e -> employee[0] = e).catchError(error -> { 86 | System.err.println(error.getMessage()); 87 | }); 88 | }); 89 | 90 | assertTrue(results[0].complete()); 91 | assertFalse(results[0].failure()); 92 | assertTrue(results[0].success()); 93 | assertNotNull(employee[0]); 94 | } 95 | 96 | @Test 97 | public void testNoReturn() throws Exception { 98 | 99 | TestService testService = new TestService(); 100 | Result[] results = new Result[1]; 101 | Employee[] employee = new Employee[1]; 102 | testService.simpleNoReturn(result -> { 103 | results[0] = result; 104 | result.then(e -> employee[0] = e).catchError(error -> { 105 | System.err.println(error.getMessage()); 106 | }); 107 | }); 108 | 109 | assertTrue(results[0].complete()); 110 | assertFalse(results[0].failure()); 111 | assertTrue(results[0].success()); 112 | assertNull(employee[0]); 113 | } 114 | 115 | 116 | @Test 117 | public void testException() throws Exception { 118 | 119 | TestService testService = new TestService(); 120 | Result[] results = new Result[1]; 121 | testService.exception(result -> { 122 | results[0] = result; 123 | 124 | }); 125 | assertTrue(results[0].complete()); 126 | assertTrue(results[0].failure()); 127 | assertFalse(results[0].success()); 128 | } 129 | 130 | 131 | static class Employee { 132 | private final String id; 133 | 134 | Employee(String id) { 135 | this.id = id; 136 | } 137 | 138 | @Override 139 | public boolean equals(Object o) { 140 | if (this == o) return true; 141 | if (o == null || getClass() != o.getClass()) return false; 142 | 143 | Employee employee = (Employee) o; 144 | 145 | return id != null ? id.equals(employee.id) : employee.id == null; 146 | 147 | } 148 | 149 | @Override 150 | public int hashCode() { 151 | return id != null ? id.hashCode() : 0; 152 | } 153 | } 154 | 155 | public static class TestService { 156 | 157 | public void simple(CallbackHandler callback) { 158 | 159 | callback.resolve(new Employee("Rick")); 160 | } 161 | 162 | public void simpleNoReturn(CallbackHandler callback) { 163 | 164 | callback.resolve(); 165 | } 166 | 167 | public void simpleConsumer(CallbackHandler callback) { 168 | callback.consumer().accept(new Employee("Rick")); 169 | } 170 | 171 | 172 | public void error(CallbackHandler callback) { 173 | callback.reject("Error"); 174 | } 175 | 176 | 177 | public void errorConsumer(CallbackHandler callback) { 178 | callback.errorConsumer().accept(new IllegalStateException("Error")); 179 | } 180 | 181 | public void exception(CallbackHandler callback) { 182 | callback.reject("force exception", new IllegalStateException("Error")); 183 | } 184 | } 185 | } -------------------------------------------------------------------------------- /src/test/java/io/advantageous/reakt/ExpectedTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package io.advantageous.reakt; 20 | 21 | import org.junit.Test; 22 | 23 | import java.util.NoSuchElementException; 24 | import java.util.Optional; 25 | 26 | import static org.junit.Assert.*; 27 | 28 | 29 | public class ExpectedTest { 30 | 31 | @Test 32 | public void testEmpty() { 33 | 34 | final Expected empty = Expected.empty(); 35 | emptyTest(empty); 36 | 37 | } 38 | 39 | @Test 40 | public void testEmptyFromNull() { 41 | 42 | final Expected empty = Expected.ofNullable(null); 43 | emptyTest(empty); 44 | 45 | } 46 | 47 | @Test 48 | public void testNotEmptyFromNullable() { 49 | 50 | final Expected rick = Expected.ofNullable(new Employee("Rick")); 51 | notEmptyTest(rick); 52 | 53 | } 54 | 55 | @Test 56 | public void testNotEmpty() { 57 | 58 | final Expected rick = Expected.of(new Employee("Rick")); 59 | notEmptyTest(rick); 60 | 61 | } 62 | 63 | @Test 64 | public void testNotEmptyFromOptional() { 65 | 66 | final Expected rick = Expected.ofOptional(Optional.of(new Employee("Rick"))); 67 | notEmptyTest(rick); 68 | 69 | } 70 | 71 | 72 | private void emptyTest(Expected empty) { 73 | final boolean[] flag = new boolean[1]; 74 | 75 | assertTrue(empty.isEmpty()); 76 | assertFalse(empty.isPresent()); 77 | 78 | /* Test ifEmpty and ifPresent. */ 79 | empty.ifEmpty(() -> flag[0] = true); 80 | assertTrue(flag[0]); 81 | empty.ifPresent(employee -> flag[0] = false); 82 | assertTrue(flag[0]); 83 | 84 | final Employee bob = empty.orElse(new Employee("bob")); 85 | assertNotNull(bob); 86 | 87 | 88 | try { 89 | empty.get(); 90 | fail(); 91 | } catch (NoSuchElementException nsee) { 92 | 93 | } 94 | assertFalse(empty.filter(employee -> employee.id.equals("Rick")).isPresent()); 95 | assertFalse(empty.filter(employee -> employee.id.equals("Bob")).isPresent()); 96 | 97 | 98 | final Expected sheepValue = empty.map(employee -> new Sheep(employee.id)); 99 | assertTrue(sheepValue.isEmpty()); 100 | } 101 | 102 | 103 | private void notEmptyTest(Expected rick) { 104 | final boolean[] flag = new boolean[1]; 105 | 106 | assertFalse(rick.isEmpty()); 107 | assertTrue(rick.isPresent()); 108 | 109 | flag[0] = false; 110 | /* Test ifEmpty and ifPresent. */ 111 | rick.ifEmpty(() -> flag[0] = true); 112 | assertFalse(flag[0]); 113 | rick.ifPresent((Employee employee) -> flag[0] = true); 114 | assertTrue(flag[0]); 115 | 116 | 117 | final Employee notBob = rick.orElse(new Employee("bob")); 118 | assertEquals("Rick", notBob.id); 119 | 120 | final Sheep sheep = rick.map(employee -> new Sheep(employee.id)).get(); 121 | assertEquals("Rick", sheep.id); 122 | 123 | 124 | assertTrue(rick.filter(employee -> employee.id.equals("Rick")).isPresent()); 125 | assertFalse(rick.filter(employee -> employee.id.equals("Bob")).isPresent()); 126 | 127 | /** Test equals. */ 128 | assertTrue(rick.equalsValue(new Employee("Rick"))); 129 | assertTrue(rick.equals(rick)); 130 | assertTrue(rick.equals(Expected.of(new Employee("Rick")))); 131 | assertFalse(rick.equals(new Employee("Rick"))); 132 | rick.hashCode(); 133 | rick.toString(); 134 | } 135 | 136 | static class Employee { 137 | private final String id; 138 | 139 | Employee(String id) { 140 | this.id = id; 141 | } 142 | 143 | @Override 144 | public boolean equals(Object o) { 145 | if (this == o) return true; 146 | if (o == null || getClass() != o.getClass()) return false; 147 | 148 | Employee employee = (Employee) o; 149 | 150 | return id != null ? id.equals(employee.id) : employee.id == null; 151 | 152 | } 153 | 154 | @Override 155 | public int hashCode() { 156 | return id != null ? id.hashCode() : 0; 157 | } 158 | } 159 | 160 | 161 | static class Sheep { 162 | private final String id; 163 | 164 | Sheep(String id) { 165 | this.id = id; 166 | } 167 | } 168 | 169 | } -------------------------------------------------------------------------------- /src/test/java/io/advantageous/reakt/ResultTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package io.advantageous.reakt; 20 | 21 | import org.junit.Test; 22 | 23 | import java.io.IOException; 24 | 25 | import static org.junit.Assert.*; 26 | 27 | public class ResultTest { 28 | 29 | 30 | @Test 31 | public void testSuccess() { 32 | final Result rick = Result.result(new Employee("Rick")); 33 | Employee[] employee = new Employee[1]; 34 | rick.then(e -> employee[0] = e); 35 | assertNotNull(employee[0]); 36 | Expected[] employeeValue = new Expected[1]; 37 | 38 | rick.thenExpect(ev -> employeeValue[0] = ev); 39 | assertNotNull(employeeValue[0]); 40 | assertTrue(employeeValue[0].isPresent()); 41 | 42 | assertTrue(rick.complete()); 43 | assertFalse(rick.failure()); 44 | assertTrue(rick.success()); 45 | 46 | } 47 | 48 | 49 | @Test 50 | public void testFail() { 51 | final Result rick = Result.error(new IOException("Rick")); 52 | Employee[] employee = new Employee[1]; 53 | rick.then(e -> employee[0] = e); 54 | assertNull(employee[0]); 55 | Expected[] employeeValue = new Expected[1]; 56 | 57 | rick.thenExpect(ev -> employeeValue[0] = ev); 58 | assertNull(employeeValue[0]); 59 | 60 | assertTrue(rick.complete()); 61 | assertTrue(rick.failure()); 62 | assertFalse(rick.success()); 63 | 64 | boolean[] flag = new boolean[1]; 65 | 66 | rick.catchError(throwable -> flag[0] = true); 67 | 68 | try { 69 | rick.get(); 70 | fail(); 71 | } catch (Exception e) { 72 | 73 | } 74 | 75 | 76 | try { 77 | rick.expect(); 78 | fail(); 79 | } catch (Exception e) { 80 | 81 | } 82 | 83 | 84 | assertTrue(flag[0]); 85 | 86 | final Employee richard = rick.orElse(new Employee("richard")); 87 | 88 | assertNotNull(richard); 89 | 90 | } 91 | 92 | static class Employee { 93 | private final String id; 94 | 95 | Employee(String id) { 96 | this.id = id; 97 | } 98 | 99 | @Override 100 | public boolean equals(Object o) { 101 | if (this == o) return true; 102 | if (o == null || getClass() != o.getClass()) return false; 103 | 104 | Employee employee = (Employee) o; 105 | 106 | return id != null ? id.equals(employee.id) : employee.id == null; 107 | 108 | } 109 | 110 | @Override 111 | public int hashCode() { 112 | return id != null ? id.hashCode() : 0; 113 | } 114 | } 115 | 116 | } -------------------------------------------------------------------------------- /src/test/java/io/advantageous/reakt/StreamTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package io.advantageous.reakt; 20 | 21 | import org.junit.Test; 22 | 23 | import java.util.concurrent.CountDownLatch; 24 | import java.util.concurrent.atomic.AtomicBoolean; 25 | import java.util.concurrent.atomic.AtomicLong; 26 | 27 | import static org.junit.Assert.*; 28 | 29 | public class StreamTest { 30 | 31 | private static void sleep() { 32 | try { 33 | Thread.sleep(10); 34 | } catch (InterruptedException e) { 35 | e.printStackTrace(); 36 | } 37 | } 38 | 39 | @Test 40 | public void test() throws Exception { 41 | TestStreamService testService = new TestStreamService(); 42 | Result[] results = new Result[1]; 43 | Employee[] employee = new Employee[1]; 44 | testService.simple(result -> { 45 | results[0] = result; 46 | result.then(e -> employee[0] = e); 47 | }); 48 | assertTrue(results[0].complete()); 49 | assertFalse(results[0].failure()); 50 | assertTrue(results[0].success()); 51 | assertNotNull(employee[0]); 52 | } 53 | 54 | @Test 55 | public void testError() throws Exception { 56 | 57 | TestStreamService testService = new TestStreamService(); 58 | Result[] results = new Result[1]; 59 | testService.error(result -> { 60 | results[0] = result; 61 | 62 | }); 63 | assertTrue(results[0].complete()); 64 | assertTrue(results[0].failure()); 65 | assertFalse(results[0].success()); 66 | } 67 | 68 | @Test 69 | public void testException() throws Exception { 70 | 71 | TestStreamService testService = new TestStreamService(); 72 | Result[] results = new Result[1]; 73 | testService.exception(result -> { 74 | results[0] = result; 75 | 76 | }); 77 | assertTrue(results[0].complete()); 78 | assertTrue(results[0].failure()); 79 | assertFalse(results[0].success()); 80 | } 81 | 82 | @Test 83 | public void testStream() throws Exception { 84 | TestStreamService testService = new TestStreamService(); 85 | 86 | CountDownLatch countDownLatch = new CountDownLatch(3); 87 | AtomicLong counter = new AtomicLong(); 88 | testService.streaming(result -> { 89 | counter.incrementAndGet(); 90 | countDownLatch.countDown(); 91 | 92 | }); 93 | 94 | countDownLatch.await(); 95 | assertEquals(3L, counter.get()); 96 | } 97 | 98 | @Test 99 | public void testStreamWithCancel() throws Exception { 100 | TestStreamService testService = new TestStreamService(); 101 | 102 | AtomicLong counter = new AtomicLong(); 103 | testService.streamingWithCancel(result -> { 104 | counter.incrementAndGet(); 105 | result.request(5); 106 | if (counter.get() == 3) { 107 | result.cancel(); 108 | } 109 | 110 | }); 111 | 112 | sleep(); 113 | sleep(); 114 | sleep(); 115 | sleep(); 116 | sleep(); 117 | sleep(); 118 | assertEquals(3L, counter.get()); 119 | } 120 | 121 | static class Employee { 122 | private final String id; 123 | 124 | Employee(String id) { 125 | this.id = id; 126 | } 127 | 128 | @Override 129 | public boolean equals(Object o) { 130 | if (this == o) return true; 131 | if (o == null || getClass() != o.getClass()) return false; 132 | 133 | Employee employee = (Employee) o; 134 | 135 | return id != null ? id.equals(employee.id) : employee.id == null; 136 | 137 | } 138 | 139 | @Override 140 | public int hashCode() { 141 | return id != null ? id.hashCode() : 0; 142 | } 143 | } 144 | 145 | public static class TestStreamService { 146 | 147 | public void simple(Stream stream) { 148 | stream.complete(new Employee("Rick")); 149 | } 150 | 151 | 152 | public void streaming(final Stream stream) { 153 | 154 | 155 | new Thread(() -> { 156 | stream.reply(new Employee("Rick")); 157 | sleep(); 158 | stream.reply(new Employee("Geoff")); 159 | sleep(); 160 | stream.reply(new Employee("Paul"), true); 161 | sleep(); 162 | }).start(); 163 | } 164 | 165 | 166 | public void streamingWithCancel(final Stream stream) { 167 | 168 | AtomicBoolean cancelled = new AtomicBoolean(); 169 | 170 | new Thread(() -> { 171 | if (!cancelled.get()) stream.reply(new Employee("Rick")); 172 | sleep(); 173 | 174 | if (!cancelled.get()) 175 | stream.reply(new Employee("Geoff"), false, () -> cancelled.set(true), sendMore -> { 176 | 177 | }); 178 | sleep(); 179 | if (!cancelled.get()) stream.reply(new Employee("Paul"), false, () -> cancelled.set(true)); 180 | sleep(); 181 | sleep(); 182 | if (!cancelled.get()) stream.reply(new Employee("Alex"), true, () -> cancelled.set(true)); 183 | sleep(); 184 | sleep(); 185 | }).start(); 186 | } 187 | 188 | public void error(Stream callback) { 189 | callback.fail("Error"); 190 | } 191 | 192 | public void exception(Stream callback) { 193 | callback.fail(new IllegalStateException("Error")); 194 | } 195 | } 196 | } -------------------------------------------------------------------------------- /src/test/java/io/advantageous/reakt/impl/ExpectedImplTest.java: -------------------------------------------------------------------------------- 1 | package io.advantageous.reakt.impl; 2 | 3 | import io.advantageous.reakt.Expected; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | 7 | import java.util.Collections; 8 | 9 | import static org.junit.Assert.*; 10 | 11 | public class ExpectedImplTest { 12 | 13 | 14 | @Test 15 | public void isAbsent() throws Exception { 16 | 17 | final Expected objectExpected = Expected.ofNullable(null); 18 | assertTrue(objectExpected.isAbsent()); 19 | } 20 | 21 | @Test 22 | public void isEmpty() throws Exception { 23 | 24 | final Expected expected1 = Expected.ofNullable(null); 25 | assertTrue(expected1.isEmpty()); 26 | final Expected expected2 = Expected.ofNullable(Collections.emptyList()); 27 | assertTrue(expected2.isEmpty()); 28 | final Expected expected3 = Expected.ofNullable(new Object()); 29 | assertFalse(expected3.isEmpty()); 30 | final Expected expected4 = Expected.ofNullable(""); 31 | assertTrue(expected4.isEmpty()); 32 | 33 | final Expected expected5 = Expected.ofNullable(new Object[0]); 34 | assertTrue(expected5.isEmpty()); 35 | } 36 | 37 | @Test 38 | public void ifEmpty() throws Exception { 39 | 40 | final Expected expected = Expected.ofNullable(new Object()); 41 | expected.ifEmpty(Assert::fail); 42 | final Expected expected2 = Expected.ofNullable(Collections.singleton(new Object())); 43 | expected2.ifEmpty(Assert::fail); 44 | 45 | final Expected expected3 = Expected.ofNullable("abc"); 46 | expected3.ifEmpty(Assert::fail); 47 | 48 | } 49 | 50 | @Test 51 | public void ifNotEmpty() throws Exception { 52 | final Expected expected = Expected.ofNullable(null); 53 | expected.ifNotEmpty((it) -> fail()); 54 | 55 | final Expected expected2 = Expected.ofNullable(Collections.emptyList()); 56 | expected2.ifNotEmpty((it) -> fail()); 57 | 58 | } 59 | 60 | @Test 61 | public void ifAbsent() throws Exception { 62 | 63 | 64 | final Expected expected = Expected.ofNullable(new Object()); 65 | 66 | expected.ifAbsent(Assert::fail); 67 | } 68 | 69 | } -------------------------------------------------------------------------------- /src/test/java/io/advantageous/reakt/promise/InvokablePromiseTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package io.advantageous.reakt.promise; 20 | 21 | import io.advantageous.reakt.Expected; 22 | import org.junit.After; 23 | import org.junit.Before; 24 | import org.junit.Test; 25 | 26 | import java.net.URI; 27 | import java.util.Queue; 28 | import java.util.concurrent.*; 29 | import java.util.concurrent.atomic.AtomicBoolean; 30 | import java.util.concurrent.atomic.AtomicReference; 31 | 32 | import static io.advantageous.reakt.promise.Promises.deferCall; 33 | import static io.advantageous.reakt.promise.Promises.invokablePromise; 34 | import static org.junit.Assert.*; 35 | 36 | public class InvokablePromiseTest { 37 | 38 | 39 | final URI successResult = URI.create("http://localhost:8080/employeeService/"); 40 | ServiceDiscovery serviceDiscovery; 41 | ServiceDiscovery asyncServiceDiscovery; 42 | URI empURI; 43 | CountDownLatch latch; 44 | AtomicReference returnValue; 45 | AtomicReference errorRef; 46 | 47 | @Before 48 | public void before() { 49 | latch = new CountDownLatch(1); 50 | returnValue = new AtomicReference<>(); 51 | errorRef = new AtomicReference<>(); 52 | serviceDiscovery = new ServiceDiscoveryImpl(); 53 | asyncServiceDiscovery = new ServiceDiscoveryAsyncImpl(); 54 | asyncServiceDiscovery.start(); 55 | empURI = URI.create("marathon://default/employeeService?env=staging"); 56 | } 57 | 58 | @After 59 | public void after() { 60 | asyncServiceDiscovery.shutdown(); 61 | } 62 | 63 | public void await() { 64 | try { 65 | latch.await(10, TimeUnit.SECONDS); 66 | } catch (InterruptedException e) { 67 | throw new IllegalStateException(e); 68 | } 69 | } 70 | 71 | @Test 72 | public void testServiceWithReturnPromiseSuccess() { 73 | serviceDiscovery.lookupService(empURI).then(this::handleSuccess) 74 | .catchError(this::handleError).invoke(); 75 | await(); 76 | assertNotNull("We have a return", returnValue.get()); 77 | assertNull("There were no errors", errorRef.get()); 78 | assertEquals("The result is the expected result", successResult, returnValue.get()); 79 | } 80 | 81 | @Test 82 | public void testServiceWithReturnPromiseSuccess2() { 83 | serviceDiscovery.lookupService2(empURI).then(this::handleSuccess) 84 | .catchError(this::handleError).invoke(); 85 | await(); 86 | assertNotNull("We have a return", returnValue.get()); 87 | assertNull("There were no errors", errorRef.get()); 88 | assertEquals("The result is the expected result", successResult, returnValue.get()); 89 | } 90 | 91 | @Test 92 | public void testServiceWithReturnPromiseFail() { 93 | 94 | 95 | serviceDiscovery.lookupService(null).then(this::handleSuccess) 96 | .catchError(this::handleError).invoke(); 97 | 98 | await(); 99 | assertNull("We do not have a return", returnValue.get()); 100 | assertNotNull("There were errors", errorRef.get()); 101 | } 102 | 103 | @Test 104 | public void testServiceWithReturnPromiseFail2() { 105 | 106 | 107 | serviceDiscovery.lookupService2(null).then(this::handleSuccess) 108 | .catchError(this::handleError).invoke(); 109 | 110 | await(); 111 | assertNull("We do not have a return", returnValue.get()); 112 | assertNotNull("There were errors", errorRef.get()); 113 | } 114 | 115 | 116 | @Test 117 | public void testAsyncServiceWithReturnPromiseSuccess() { 118 | asyncServiceDiscovery.lookupService(empURI).then(this::handleSuccess) 119 | .catchError(this::handleError).invoke(); 120 | await(); 121 | assertNotNull("We have a return from async", returnValue.get()); 122 | assertNull("There were no errors form async", errorRef.get()); 123 | assertEquals("The result is the expected result form async", successResult, returnValue.get()); 124 | } 125 | 126 | @Test 127 | public void testAsyncServiceWithInvokeWithPromise() { 128 | 129 | Promise promise = Promises.blockingPromise(); 130 | promise.then(this::handleSuccess) 131 | .catchError(this::handleError); 132 | 133 | asyncServiceDiscovery.lookupService(empURI).asHandler() 134 | .invokeWithPromise(promise.asHandler()); 135 | 136 | final Expected expect = promise.asHandler().expect(); 137 | 138 | assertFalse(expect.isEmpty()); 139 | 140 | assertNotNull("We have a return from async", returnValue.get()); 141 | assertNull("There were no errors form async", errorRef.get()); 142 | assertEquals("The result is the expected result form async", successResult, returnValue.get()); 143 | } 144 | 145 | 146 | @Test 147 | public void testAsyncServiceWithInvokePromiseFail() { 148 | 149 | 150 | Promise promise = Promises.blockingPromise(); 151 | promise.then(this::handleSuccess) 152 | .catchError(this::handleError); 153 | 154 | asyncServiceDiscovery.lookupService(null).asHandler() 155 | .invokeWithPromise(promise.asHandler()); 156 | 157 | 158 | try { 159 | promise.asHandler().get(); 160 | fail(); 161 | } catch (Exception ex) { 162 | 163 | } 164 | 165 | assertNull("We do not have a return from async", returnValue.get()); 166 | assertNotNull("There were errors from async", errorRef.get()); 167 | } 168 | 169 | @Test 170 | public void testAsyncServiceWithInvokePromiseFail2() { 171 | 172 | 173 | Promise promise = Promises.blockingPromise(); 174 | promise.then(this::handleSuccess) 175 | .catchError(this::handleError); 176 | 177 | asyncServiceDiscovery.lookupService(null).asHandler().thenCallback(promise.asHandler()).invoke(); 178 | 179 | 180 | try { 181 | promise.asHandler().get(); 182 | fail(); 183 | } catch (Exception ex) { 184 | 185 | } 186 | 187 | assertNull("We do not have a return from async", returnValue.get()); 188 | assertNotNull("There were errors from async", errorRef.get()); 189 | } 190 | 191 | @Test 192 | public void testAsyncServiceWithReturnPromiseFail() { 193 | 194 | 195 | asyncServiceDiscovery.lookupService(null).then(this::handleSuccess) 196 | .catchError(this::handleError).invoke(); 197 | 198 | await(); 199 | assertNull("We do not have a return from async", returnValue.get()); 200 | assertNotNull("There were errors from async", errorRef.get()); 201 | } 202 | 203 | @Test(expected = IllegalStateException.class) 204 | public void testServiceWithReturnPromiseSuccessInvokeTwice() { 205 | final Promise promise = serviceDiscovery.lookupService(empURI).then(this::handleSuccess) 206 | .catchError(this::handleError); 207 | promise.invoke(); 208 | promise.invoke(); 209 | } 210 | 211 | @Test 212 | public void testIsInvokable() { 213 | final Promise promise = serviceDiscovery.lookupService(empURI).then(this::handleSuccess) 214 | .catchError(this::handleError); 215 | 216 | assertTrue("Is this an invokable promise", promise.asHandler().isInvokable()); 217 | } 218 | 219 | 220 | private void handleError(Throwable error) { 221 | errorRef.set(error); 222 | latch.countDown(); 223 | } 224 | 225 | private void handleSuccess(URI uri) { 226 | returnValue.set(uri); 227 | latch.countDown(); 228 | } 229 | 230 | 231 | interface ServiceDiscovery { 232 | Promise lookupService(URI uri); 233 | 234 | 235 | Promise lookupService2(URI uri); 236 | 237 | default void shutdown() { 238 | } 239 | 240 | default void start() { 241 | } 242 | 243 | 244 | } 245 | 246 | class ServiceDiscoveryImpl implements ServiceDiscovery { 247 | 248 | @Override 249 | public Promise lookupService(URI uri) { 250 | return invokablePromise(promise -> { 251 | 252 | if (uri == null) { 253 | promise.reject("URI was null"); 254 | } else { 255 | promise.resolve(successResult); 256 | } 257 | }); 258 | } 259 | 260 | @Override 261 | public Promise lookupService2(URI uri) { 262 | return deferCall(callbackHandle -> { 263 | 264 | if (uri == null) { 265 | callbackHandle.reject("URI was null"); 266 | } else { 267 | callbackHandle.resolve(successResult); 268 | } 269 | }); 270 | } 271 | } 272 | 273 | 274 | class ServiceDiscoveryAsyncImpl implements ServiceDiscovery { 275 | 276 | final ExecutorService executorService; 277 | 278 | final Queue runnables; 279 | 280 | final AtomicBoolean stop; 281 | 282 | public ServiceDiscoveryAsyncImpl() { 283 | executorService = Executors.newSingleThreadExecutor(); 284 | runnables = new LinkedTransferQueue<>(); 285 | stop = new AtomicBoolean(); 286 | } 287 | 288 | @Override 289 | public Promise lookupService(URI uri) { 290 | return invokablePromise(promise -> { 291 | runnables.offer(() -> { 292 | if (uri == null) { 293 | promise.reject("URI was null lookupService"); 294 | } else { 295 | promise.resolve(URI.create("http://localhost:8080/employeeService/")); 296 | } 297 | }); 298 | }); 299 | } 300 | 301 | public Promise lookupService2(URI uri) { 302 | return invokablePromise(promise -> { 303 | runnables.offer(() -> { 304 | if (uri == null) { 305 | promise.reject("URI was null lookupService2"); 306 | } else { 307 | promise.resolve(URI.create("http://localhost:8080/employeeService/")); 308 | } 309 | }); 310 | }); 311 | } 312 | 313 | 314 | @Override 315 | public void shutdown() { 316 | stop.set(true); 317 | executorService.shutdown(); 318 | } 319 | 320 | @Override 321 | public void start() { 322 | executorService.submit((Runnable) () -> { 323 | 324 | try { 325 | Thread.sleep(10); 326 | } catch (InterruptedException e) { 327 | e.printStackTrace(); 328 | } 329 | while (true) { 330 | if (stop.get()) break; 331 | Runnable runnable = runnables.poll(); 332 | while (runnable != null) { 333 | runnable.run(); 334 | runnable = runnables.poll(); 335 | } 336 | } 337 | 338 | }); 339 | } 340 | } 341 | } 342 | -------------------------------------------------------------------------------- /src/test/java/io/advantageous/reakt/reactor/impl/TestTimer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2016. Rick Hightower, Geoff Chandler 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | package io.advantageous.reakt.reactor.impl; 20 | 21 | import io.advantageous.reakt.reactor.TimeSource; 22 | 23 | public class TestTimer implements TimeSource { 24 | 25 | 26 | private long time; 27 | 28 | @Override 29 | public long getTime() { 30 | return time; 31 | } 32 | 33 | public TestTimer setTime(long time) { 34 | this.time = time; 35 | return this; 36 | } 37 | } 38 | --------------------------------------------------------------------------------