Merge pull request #470 from iluwatar/Promise
Implements #403 Promise pattern
This commit is contained in:
		
							
								
								
									
										1
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								pom.xml
									
									
									
									
									
								
							@@ -129,6 +129,7 @@
 | 
				
			|||||||
		<module>hexagonal</module>
 | 
							<module>hexagonal</module>
 | 
				
			||||||
		<module>abstract-document</module>
 | 
							<module>abstract-document</module>
 | 
				
			||||||
		<module>aggregator-microservices</module>
 | 
							<module>aggregator-microservices</module>
 | 
				
			||||||
 | 
							<module>promise</module>
 | 
				
			||||||
        <module>page-object</module>
 | 
					        <module>page-object</module>
 | 
				
			||||||
	</modules>
 | 
						</modules>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										46
									
								
								promise/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								promise/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					---
 | 
				
			||||||
 | 
					layout: pattern
 | 
				
			||||||
 | 
					title: Promise
 | 
				
			||||||
 | 
					folder: promise
 | 
				
			||||||
 | 
					permalink: /patterns/promise/
 | 
				
			||||||
 | 
					categories: Concurrency
 | 
				
			||||||
 | 
					tags:
 | 
				
			||||||
 | 
					 - Java
 | 
				
			||||||
 | 
					 - Functional
 | 
				
			||||||
 | 
					 - Reactive
 | 
				
			||||||
 | 
					 - Difficulty-Intermediate
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Also known as
 | 
				
			||||||
 | 
					CompletableFuture
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Intent
 | 
				
			||||||
 | 
					A Promise represents a proxy for a value not necessarily known when the promise is created. It
 | 
				
			||||||
 | 
					allows you to associate dependent promises to an asynchronous action's eventual success value or 
 | 
				
			||||||
 | 
					failure reason. Promises are a way to write async code that still appears as though it is executing 
 | 
				
			||||||
 | 
					in a synchronous way.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Applicability
 | 
				
			||||||
 | 
					Promise pattern is applicable in concurrent programming when some work needs to be done asynchronously
 | 
				
			||||||
 | 
					and:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* code maintainablity and readability suffers due to callback hell.
 | 
				
			||||||
 | 
					* you need to compose promises and need better error handling for asynchronous tasks.
 | 
				
			||||||
 | 
					* you want to use functional style of programming.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Real world examples
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* [java.util.concurrent.CompletableFuture](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html)
 | 
				
			||||||
 | 
					* [Guava ListenableFuture](https://github.com/google/guava/wiki/ListenableFutureExplained)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Related Patterns
 | 
				
			||||||
 | 
					 * Async Method Invocation
 | 
				
			||||||
 | 
					 * Callback
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Credits
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* [You are missing the point to Promises](https://gist.github.com/domenic/3889970)
 | 
				
			||||||
 | 
					* [Functional style callbacks using CompleteableFuture](https://www.infoq.com/articles/Functional-Style-Callbacks-Using-CompletableFuture)
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								promise/etc/promise.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								promise/etc/promise.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 58 KiB  | 
							
								
								
									
										111
									
								
								promise/etc/promise.ucls
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								promise/etc/promise.ucls
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,111 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
 | 
					<class-diagram version="1.1.10" icons="true" automaticImage="PNG" always-add-relationships="false" 
 | 
				
			||||||
 | 
					  generalizations="true" realizations="true" associations="true" dependencies="false" nesting-relationships="true" 
 | 
				
			||||||
 | 
					  router="FAN">  
 | 
				
			||||||
 | 
					  <class id="1" language="java" name="com.iluwatar.promise.Promise" project="promise" 
 | 
				
			||||||
 | 
					    file="/promise/src/main/java/com/iluwatar/promise/Promise.java" binary="false" corner="BOTTOM_RIGHT">    
 | 
				
			||||||
 | 
					    <position height="-1" width="-1" x="524" y="541"/>    
 | 
				
			||||||
 | 
					    <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" 
 | 
				
			||||||
 | 
					      sort-features="false" accessors="true" visibility="true">      
 | 
				
			||||||
 | 
					      <attributes public="true" package="true" protected="true" private="false" static="true"/>      
 | 
				
			||||||
 | 
					      <operations public="true" package="false" protected="true" private="false" static="true"/>    
 | 
				
			||||||
 | 
					    </display>  
 | 
				
			||||||
 | 
					  </class>  
 | 
				
			||||||
 | 
					  <interface id="2" language="java" name="java.util.concurrent.Future" project="async-method-invocation" 
 | 
				
			||||||
 | 
					    file="/usr/lib/java/jdk1.8.0_45/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">    
 | 
				
			||||||
 | 
					    <position height="-1" width="-1" x="527" y="94"/>    
 | 
				
			||||||
 | 
					    <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" 
 | 
				
			||||||
 | 
					      sort-features="false" accessors="true" visibility="true">      
 | 
				
			||||||
 | 
					      <attributes public="true" package="true" protected="true" private="true" static="true"/>      
 | 
				
			||||||
 | 
					      <operations public="true" package="true" protected="true" private="true" static="true"/>    
 | 
				
			||||||
 | 
					    </display>  
 | 
				
			||||||
 | 
					  </interface>  
 | 
				
			||||||
 | 
					  <class id="3" language="java" name="com.iluwatar.promise.PromiseSupport" project="promise" 
 | 
				
			||||||
 | 
					    file="/promise/src/main/java/com/iluwatar/promise/PromiseSupport.java" binary="false" corner="BOTTOM_RIGHT">    
 | 
				
			||||||
 | 
					    <position height="-1" width="-1" x="524" y="313"/>    
 | 
				
			||||||
 | 
					    <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" 
 | 
				
			||||||
 | 
					      sort-features="false" accessors="true" visibility="true">      
 | 
				
			||||||
 | 
					      <attributes public="true" package="false" protected="true" private="false" static="true"/>      
 | 
				
			||||||
 | 
					      <operations public="true" package="false" protected="true" private="true" static="true"/>    
 | 
				
			||||||
 | 
					    </display>  
 | 
				
			||||||
 | 
					  </class>  
 | 
				
			||||||
 | 
					  <interface id="4" language="java" name="java.util.concurrent.Executor" project="async-method-invocation" 
 | 
				
			||||||
 | 
					    file="/usr/lib/java/jdk1.8.0_45/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">    
 | 
				
			||||||
 | 
					    <position height="-1" width="-1" x="798" y="541"/>    
 | 
				
			||||||
 | 
					    <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" 
 | 
				
			||||||
 | 
					      sort-features="false" accessors="true" visibility="true">      
 | 
				
			||||||
 | 
					      <attributes public="true" package="true" protected="true" private="true" static="true"/>      
 | 
				
			||||||
 | 
					      <operations public="true" package="true" protected="true" private="true" static="true"/>    
 | 
				
			||||||
 | 
					    </display>  
 | 
				
			||||||
 | 
					  </interface>  
 | 
				
			||||||
 | 
					  <interface id="5" language="java" name="java.util.concurrent.Callable" project="async-method-invocation" 
 | 
				
			||||||
 | 
					    file="/usr/lib/java/jdk1.8.0_45/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">    
 | 
				
			||||||
 | 
					    <position height="-1" width="-1" x="847" y="345"/>    
 | 
				
			||||||
 | 
					    <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" 
 | 
				
			||||||
 | 
					      sort-features="false" accessors="true" visibility="true">      
 | 
				
			||||||
 | 
					      <attributes public="true" package="true" protected="true" private="true" static="true"/>      
 | 
				
			||||||
 | 
					      <operations public="true" package="true" protected="true" private="true" static="true"/>    
 | 
				
			||||||
 | 
					    </display>  
 | 
				
			||||||
 | 
					  </interface>  
 | 
				
			||||||
 | 
					  <interface id="6" language="java" name="java.util.function.Consumer" project="async-method-invocation" 
 | 
				
			||||||
 | 
					    file="/usr/lib/java/jdk1.8.0_45/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">    
 | 
				
			||||||
 | 
					    <position height="-1" width="-1" x="158" y="336"/>    
 | 
				
			||||||
 | 
					    <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" 
 | 
				
			||||||
 | 
					      sort-features="false" accessors="true" visibility="true">      
 | 
				
			||||||
 | 
					      <attributes public="true" package="true" protected="true" private="true" static="true"/>      
 | 
				
			||||||
 | 
					      <operations public="true" package="true" protected="true" private="false" static="true"/>    
 | 
				
			||||||
 | 
					    </display>  
 | 
				
			||||||
 | 
					  </interface>  
 | 
				
			||||||
 | 
					  <interface id="7" language="java" name="java.util.function.Function" project="async-method-invocation" 
 | 
				
			||||||
 | 
					    file="/usr/lib/java/jdk1.8.0_45/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">    
 | 
				
			||||||
 | 
					    <position height="-1" width="-1" x="166" y="546"/>    
 | 
				
			||||||
 | 
					    <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" 
 | 
				
			||||||
 | 
					      sort-features="false" accessors="true" visibility="true">      
 | 
				
			||||||
 | 
					      <attributes public="true" package="true" protected="true" private="true" static="true"/>      
 | 
				
			||||||
 | 
					      <operations public="true" package="true" protected="true" private="false" static="false"/>    
 | 
				
			||||||
 | 
					    </display>  
 | 
				
			||||||
 | 
					  </interface>  
 | 
				
			||||||
 | 
					  <class id="8" language="java" name="com.iluwatar.promise.App" project="promise" 
 | 
				
			||||||
 | 
					    file="/promise/src/main/java/com/iluwatar/promise/App.java" binary="false" corner="BOTTOM_RIGHT">    
 | 
				
			||||||
 | 
					    <position height="-1" width="-1" x="801" y="189"/>    
 | 
				
			||||||
 | 
					    <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" 
 | 
				
			||||||
 | 
					      sort-features="false" accessors="true" visibility="true">      
 | 
				
			||||||
 | 
					      <attributes public="true" package="true" protected="true" private="false" static="true"/>      
 | 
				
			||||||
 | 
					      <operations public="true" package="true" protected="true" private="false" static="true"/>    
 | 
				
			||||||
 | 
					    </display>  
 | 
				
			||||||
 | 
					  </class>  
 | 
				
			||||||
 | 
					  <dependency id="9">    
 | 
				
			||||||
 | 
					    <end type="SOURCE" refId="1"/>    
 | 
				
			||||||
 | 
					    <end type="TARGET" refId="5"/>  
 | 
				
			||||||
 | 
					  </dependency>  
 | 
				
			||||||
 | 
					  <dependency id="10">    
 | 
				
			||||||
 | 
					    <end type="SOURCE" refId="8"/>    
 | 
				
			||||||
 | 
					    <end type="TARGET" refId="1"/>  
 | 
				
			||||||
 | 
					  </dependency>  
 | 
				
			||||||
 | 
					  <dependency id="11">    
 | 
				
			||||||
 | 
					    <end type="SOURCE" refId="1"/>    
 | 
				
			||||||
 | 
					    <end type="TARGET" refId="6"/>  
 | 
				
			||||||
 | 
					  </dependency>  
 | 
				
			||||||
 | 
					  <generalization id="12">    
 | 
				
			||||||
 | 
					    <end type="SOURCE" refId="1"/>    
 | 
				
			||||||
 | 
					    <end type="TARGET" refId="3"/>  
 | 
				
			||||||
 | 
					  </generalization>  
 | 
				
			||||||
 | 
					  <dependency id="13">    
 | 
				
			||||||
 | 
					    <end type="SOURCE" refId="1"/>    
 | 
				
			||||||
 | 
					    <end type="TARGET" refId="4"/>  
 | 
				
			||||||
 | 
					  </dependency>  
 | 
				
			||||||
 | 
					  <dependency id="14">    
 | 
				
			||||||
 | 
					    <end type="SOURCE" refId="1"/>    
 | 
				
			||||||
 | 
					    <end type="TARGET" refId="7"/>  
 | 
				
			||||||
 | 
					  </dependency>  
 | 
				
			||||||
 | 
					  <realization id="15">    
 | 
				
			||||||
 | 
					    <end type="SOURCE" refId="3"/>    
 | 
				
			||||||
 | 
					    <end type="TARGET" refId="2"/>  
 | 
				
			||||||
 | 
					  </realization>  
 | 
				
			||||||
 | 
					  <classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" 
 | 
				
			||||||
 | 
					    sort-features="false" accessors="true" visibility="true">    
 | 
				
			||||||
 | 
					    <attributes public="true" package="true" protected="true" private="true" static="true"/>    
 | 
				
			||||||
 | 
					    <operations public="true" package="true" protected="true" private="true" static="true"/>  
 | 
				
			||||||
 | 
					  </classifier-display>  
 | 
				
			||||||
 | 
					  <association-display labels="true" multiplicity="true"/>
 | 
				
			||||||
 | 
					</class-diagram>
 | 
				
			||||||
							
								
								
									
										47
									
								
								promise/pom.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								promise/pom.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0"?>
 | 
				
			||||||
 | 
					<!--
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The MIT License
 | 
				
			||||||
 | 
					    Copyright (c) 2014 Ilkka Seppälä
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
				
			||||||
 | 
					    of this software and associated documentation files (the "Software"), to deal
 | 
				
			||||||
 | 
					    in the Software without restriction, including without limitation the rights
 | 
				
			||||||
 | 
					    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
				
			||||||
 | 
					    copies of the Software, and to permit persons to whom the Software is
 | 
				
			||||||
 | 
					    furnished to do so, subject to the following conditions:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The above copyright notice and this permission notice shall be included in
 | 
				
			||||||
 | 
					    all copies or substantial portions of the Software.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
				
			||||||
 | 
					    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
				
			||||||
 | 
					    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
				
			||||||
 | 
					    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
				
			||||||
 | 
					    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
				
			||||||
 | 
					    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
				
			||||||
 | 
					    THE SOFTWARE.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-->
 | 
				
			||||||
 | 
					<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
 | 
				
			||||||
 | 
					    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 | 
				
			||||||
 | 
					  <modelVersion>4.0.0</modelVersion>
 | 
				
			||||||
 | 
					  <parent>
 | 
				
			||||||
 | 
					    <groupId>com.iluwatar</groupId>
 | 
				
			||||||
 | 
					    <artifactId>java-design-patterns</artifactId>
 | 
				
			||||||
 | 
					    <version>1.13.0-SNAPSHOT</version>
 | 
				
			||||||
 | 
					  </parent>
 | 
				
			||||||
 | 
					  <artifactId>promise</artifactId>
 | 
				
			||||||
 | 
					  <dependencies>
 | 
				
			||||||
 | 
					    <dependency>
 | 
				
			||||||
 | 
					      <groupId>junit</groupId>
 | 
				
			||||||
 | 
					      <artifactId>junit</artifactId>
 | 
				
			||||||
 | 
					      <scope>test</scope>
 | 
				
			||||||
 | 
					    </dependency>
 | 
				
			||||||
 | 
					    <dependency>
 | 
				
			||||||
 | 
					      <groupId>org.mockito</groupId>
 | 
				
			||||||
 | 
					      <artifactId>mockito-core</artifactId>
 | 
				
			||||||
 | 
					      <scope>test</scope>
 | 
				
			||||||
 | 
					    </dependency>
 | 
				
			||||||
 | 
					  </dependencies>
 | 
				
			||||||
 | 
					</project>
 | 
				
			||||||
							
								
								
									
										176
									
								
								promise/src/main/java/com/iluwatar/promise/App.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								promise/src/main/java/com/iluwatar/promise/App.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,176 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * The MIT License
 | 
				
			||||||
 | 
					 * Copyright (c) 2014 Ilkka Seppälä
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
				
			||||||
 | 
					 * of this software and associated documentation files (the "Software"), to deal
 | 
				
			||||||
 | 
					 * in the Software without restriction, including without limitation the rights
 | 
				
			||||||
 | 
					 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
				
			||||||
 | 
					 * copies of the Software, and to permit persons to whom the Software is
 | 
				
			||||||
 | 
					 * furnished to do so, subject to the following conditions:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The above copyright notice and this permission notice shall be included in
 | 
				
			||||||
 | 
					 * all copies or substantial portions of the Software.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
				
			||||||
 | 
					 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
				
			||||||
 | 
					 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
				
			||||||
 | 
					 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
				
			||||||
 | 
					 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
				
			||||||
 | 
					 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
				
			||||||
 | 
					 * THE SOFTWARE.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					package com.iluwatar.promise;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					import java.util.concurrent.CompletableFuture;
 | 
				
			||||||
 | 
					import java.util.concurrent.CountDownLatch;
 | 
				
			||||||
 | 
					import java.util.concurrent.ExecutionException;
 | 
				
			||||||
 | 
					import java.util.concurrent.ExecutorService;
 | 
				
			||||||
 | 
					import java.util.concurrent.Executors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *  The Promise object is used for asynchronous computations. A Promise represents an operation
 | 
				
			||||||
 | 
					 *  that hasn't completed yet, but is expected in the future.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * <p>A Promise represents a proxy for a value not necessarily known when the promise is created. It
 | 
				
			||||||
 | 
					 * allows you to associate dependent promises to an asynchronous action's eventual success value or
 | 
				
			||||||
 | 
					 * failure reason. This lets asynchronous methods return values like synchronous methods: instead 
 | 
				
			||||||
 | 
					 * of the final value, the asynchronous method returns a promise of having a value at some point 
 | 
				
			||||||
 | 
					 * in the future.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * <p>Promises provide a few advantages over callback objects:
 | 
				
			||||||
 | 
					 * <ul>
 | 
				
			||||||
 | 
					 * <li> Functional composition and error handling
 | 
				
			||||||
 | 
					 * <li> Prevents callback hell and provides callback aggregation
 | 
				
			||||||
 | 
					 * </ul>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * <p>
 | 
				
			||||||
 | 
					 * In this application the usage of promise is demonstrated with two examples:
 | 
				
			||||||
 | 
					 * <ul>
 | 
				
			||||||
 | 
					 * <li>Count Lines: In this example a file is downloaded and its line count is calculated.
 | 
				
			||||||
 | 
					 * The calculated line count is then consumed and printed on console.
 | 
				
			||||||
 | 
					 * <li>Lowest Character Frequency: In this example a file is downloaded and its lowest frequency
 | 
				
			||||||
 | 
					 * character is found and printed on console. This happens via a chain of promises, we start with
 | 
				
			||||||
 | 
					 * a file download promise, then a promise of character frequency, then a promise of lowest frequency
 | 
				
			||||||
 | 
					 * character which is finally consumed and result is printed on console.
 | 
				
			||||||
 | 
					 * </ul>
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * @see CompletableFuture
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class App {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static final String DEFAULT_URL = "https://raw.githubusercontent.com/iluwatar/java-design-patterns/Promise/promise/README.md";
 | 
				
			||||||
 | 
					  private final ExecutorService executor;
 | 
				
			||||||
 | 
					  private final CountDownLatch stopLatch;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private App() {
 | 
				
			||||||
 | 
					    executor = Executors.newFixedThreadPool(2);
 | 
				
			||||||
 | 
					    stopLatch = new CountDownLatch(2);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Program entry point
 | 
				
			||||||
 | 
					   * @param args arguments
 | 
				
			||||||
 | 
					   * @throws InterruptedException if main thread is interrupted.
 | 
				
			||||||
 | 
					   * @throws ExecutionException if an execution error occurs.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  public static void main(String[] args) throws InterruptedException, ExecutionException {
 | 
				
			||||||
 | 
					    App app = new App();
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      app.promiseUsage();
 | 
				
			||||||
 | 
					    } finally {
 | 
				
			||||||
 | 
					      app.stop();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private void promiseUsage() {
 | 
				
			||||||
 | 
					    calculateLineCount();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    calculateLowestFrequencyChar();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /*
 | 
				
			||||||
 | 
					   * Calculate the lowest frequency character and when that promise is fulfilled,
 | 
				
			||||||
 | 
					   * consume the result in a Consumer<Character>
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  private void calculateLowestFrequencyChar() {
 | 
				
			||||||
 | 
					    lowestFrequencyChar()
 | 
				
			||||||
 | 
					        .thenAccept(
 | 
				
			||||||
 | 
					          charFrequency -> {
 | 
				
			||||||
 | 
					            System.out.println("Char with lowest frequency is: " + charFrequency);
 | 
				
			||||||
 | 
					            taskCompleted();
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /*
 | 
				
			||||||
 | 
					   * Calculate the line count and when that promise is fulfilled, consume the result
 | 
				
			||||||
 | 
					   * in a Consumer<Integer>
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  private void calculateLineCount() {
 | 
				
			||||||
 | 
					    countLines()
 | 
				
			||||||
 | 
					        .thenAccept(
 | 
				
			||||||
 | 
					          count -> {
 | 
				
			||||||
 | 
					            System.out.println("Line count is: " + count);
 | 
				
			||||||
 | 
					            taskCompleted();
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /*
 | 
				
			||||||
 | 
					   * Calculate the character frequency of a file and when that promise is fulfilled,
 | 
				
			||||||
 | 
					   * then promise to apply function to calculate lowest character frequency.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  private Promise<Character> lowestFrequencyChar() {
 | 
				
			||||||
 | 
					    return characterFrequency()
 | 
				
			||||||
 | 
					        .thenApply(Utility::lowestFrequencyChar);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /*
 | 
				
			||||||
 | 
					   * Download the file at DEFAULT_URL and when that promise is fulfilled,
 | 
				
			||||||
 | 
					   * then promise to apply function to calculate character frequency.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  private Promise<Map<Character, Integer>> characterFrequency() {
 | 
				
			||||||
 | 
					    return download(DEFAULT_URL)
 | 
				
			||||||
 | 
					        .thenApply(Utility::characterFrequency);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /*
 | 
				
			||||||
 | 
					   * Download the file at DEFAULT_URL and when that promise is fulfilled,
 | 
				
			||||||
 | 
					   * then promise to apply function to count lines in that file.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  private Promise<Integer> countLines() {
 | 
				
			||||||
 | 
					    return download(DEFAULT_URL)
 | 
				
			||||||
 | 
					        .thenApply(Utility::countLines);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /*
 | 
				
			||||||
 | 
					   * Return a promise to provide the local absolute path of the file downloaded in background.
 | 
				
			||||||
 | 
					   * This is an async method and does not wait until the file is downloaded.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  private Promise<String> download(String urlString) {
 | 
				
			||||||
 | 
					    Promise<String> downloadPromise = new Promise<String>()
 | 
				
			||||||
 | 
					        .fulfillInAsync(
 | 
				
			||||||
 | 
					            () -> {
 | 
				
			||||||
 | 
					              return Utility.downloadFile(urlString);
 | 
				
			||||||
 | 
					            }, executor)
 | 
				
			||||||
 | 
					        .onError(
 | 
				
			||||||
 | 
					            throwable -> {
 | 
				
			||||||
 | 
					              throwable.printStackTrace();
 | 
				
			||||||
 | 
					              taskCompleted();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return downloadPromise;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private void stop() throws InterruptedException {
 | 
				
			||||||
 | 
					    stopLatch.await();
 | 
				
			||||||
 | 
					    executor.shutdownNow();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private void taskCompleted() {
 | 
				
			||||||
 | 
					    stopLatch.countDown();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										193
									
								
								promise/src/main/java/com/iluwatar/promise/Promise.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										193
									
								
								promise/src/main/java/com/iluwatar/promise/Promise.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,193 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * The MIT License
 | 
				
			||||||
 | 
					 * Copyright (c) 2014 Ilkka Seppälä
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
				
			||||||
 | 
					 * of this software and associated documentation files (the "Software"), to deal
 | 
				
			||||||
 | 
					 * in the Software without restriction, including without limitation the rights
 | 
				
			||||||
 | 
					 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
				
			||||||
 | 
					 * copies of the Software, and to permit persons to whom the Software is
 | 
				
			||||||
 | 
					 * furnished to do so, subject to the following conditions:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The above copyright notice and this permission notice shall be included in
 | 
				
			||||||
 | 
					 * all copies or substantial portions of the Software.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
				
			||||||
 | 
					 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
				
			||||||
 | 
					 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
				
			||||||
 | 
					 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
				
			||||||
 | 
					 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
				
			||||||
 | 
					 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
				
			||||||
 | 
					 * THE SOFTWARE.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					package com.iluwatar.promise;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.concurrent.Callable;
 | 
				
			||||||
 | 
					import java.util.concurrent.ExecutionException;
 | 
				
			||||||
 | 
					import java.util.concurrent.Executor;
 | 
				
			||||||
 | 
					import java.util.function.Consumer;
 | 
				
			||||||
 | 
					import java.util.function.Function;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * A Promise represents a proxy for a value not necessarily known when the promise is created. It
 | 
				
			||||||
 | 
					 * allows you to associate dependent promises to an asynchronous action's eventual success value or
 | 
				
			||||||
 | 
					 * failure reason. This lets asynchronous methods return values like synchronous methods: instead 
 | 
				
			||||||
 | 
					 * of the final value, the asynchronous method returns a promise of having a value at some point 
 | 
				
			||||||
 | 
					 * in the future.
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * @param <T> type of result.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class Promise<T> extends PromiseSupport<T> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private Runnable fulfillmentAction;
 | 
				
			||||||
 | 
					  private Consumer<? super Throwable> exceptionHandler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Creates a promise that will be fulfilled in future.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  public Promise() {
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Fulfills the promise with the provided value.
 | 
				
			||||||
 | 
					   * @param value the fulfilled value that can be accessed using {@link #get()}.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public void fulfill(T value) {
 | 
				
			||||||
 | 
					    super.fulfill(value);
 | 
				
			||||||
 | 
					    postFulfillment();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Fulfills the promise with exception due to error in execution.
 | 
				
			||||||
 | 
					   * @param exception the exception will be wrapped in {@link ExecutionException}
 | 
				
			||||||
 | 
					   *        when accessing the value using {@link #get()}.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public void fulfillExceptionally(Exception exception) {
 | 
				
			||||||
 | 
					    super.fulfillExceptionally(exception);
 | 
				
			||||||
 | 
					    handleException(exception);
 | 
				
			||||||
 | 
					    postFulfillment();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private void handleException(Exception exception) {
 | 
				
			||||||
 | 
					    if (exceptionHandler == null) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    exceptionHandler.accept(exception);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private void postFulfillment() {
 | 
				
			||||||
 | 
					    if (fulfillmentAction == null) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fulfillmentAction.run();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Executes the task using the executor in other thread and fulfills the promise returned
 | 
				
			||||||
 | 
					   * once the task completes either successfully or with an exception.
 | 
				
			||||||
 | 
					   * 
 | 
				
			||||||
 | 
					   * @param task the task that will provide the value to fulfill the promise.
 | 
				
			||||||
 | 
					   * @param executor the executor in which the task should be run.
 | 
				
			||||||
 | 
					   * @return a promise that represents the result of running the task provided.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  public Promise<T> fulfillInAsync(final Callable<T> task, Executor executor) {
 | 
				
			||||||
 | 
					    executor.execute(() -> {
 | 
				
			||||||
 | 
					      try {
 | 
				
			||||||
 | 
					        fulfill(task.call());
 | 
				
			||||||
 | 
					      } catch (Exception ex) {
 | 
				
			||||||
 | 
					        fulfillExceptionally(ex);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    return this;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Returns a new promise that, when this promise is fulfilled normally, is fulfilled with 
 | 
				
			||||||
 | 
					   * result of this promise as argument to the action provided.
 | 
				
			||||||
 | 
					   * @param action action to be executed.
 | 
				
			||||||
 | 
					   * @return a new promise.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  public Promise<Void> thenAccept(Consumer<? super T> action) {
 | 
				
			||||||
 | 
					    Promise<Void> dest = new Promise<>();
 | 
				
			||||||
 | 
					    fulfillmentAction = new ConsumeAction(this, dest, action);
 | 
				
			||||||
 | 
					    return dest;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Set the exception handler on this promise.
 | 
				
			||||||
 | 
					   * @param exceptionHandler a consumer that will handle the exception occurred while fulfilling
 | 
				
			||||||
 | 
					   *            the promise.
 | 
				
			||||||
 | 
					   * @return this
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  public Promise<T> onError(Consumer<? super Throwable> exceptionHandler) {
 | 
				
			||||||
 | 
					    this.exceptionHandler = exceptionHandler;
 | 
				
			||||||
 | 
					    return this;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Returns a new promise that, when this promise is fulfilled normally, is fulfilled with 
 | 
				
			||||||
 | 
					   * result of this promise as argument to the function provided.
 | 
				
			||||||
 | 
					   * @param func function to be executed.
 | 
				
			||||||
 | 
					   * @return a new promise.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  public <V> Promise<V> thenApply(Function<? super T, V> func) {
 | 
				
			||||||
 | 
					    Promise<V> dest = new Promise<>();
 | 
				
			||||||
 | 
					    fulfillmentAction = new TransformAction<V>(this, dest, func);
 | 
				
			||||||
 | 
					    return dest;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Accesses the value from source promise and calls the consumer, then fulfills the
 | 
				
			||||||
 | 
					   * destination promise.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  private class ConsumeAction implements Runnable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final Promise<T> src;
 | 
				
			||||||
 | 
					    private final Promise<Void> dest;
 | 
				
			||||||
 | 
					    private final Consumer<? super T> action;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private ConsumeAction(Promise<T> src, Promise<Void> dest, Consumer<? super T> action) {
 | 
				
			||||||
 | 
					      this.src = src;
 | 
				
			||||||
 | 
					      this.dest = dest;
 | 
				
			||||||
 | 
					      this.action = action;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void run() {
 | 
				
			||||||
 | 
					      try {
 | 
				
			||||||
 | 
					        action.accept(src.get());
 | 
				
			||||||
 | 
					        dest.fulfill(null);
 | 
				
			||||||
 | 
					      } catch (Throwable throwable) {
 | 
				
			||||||
 | 
					        dest.fulfillExceptionally((Exception) throwable.getCause());
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Accesses the value from source promise, then fulfills the destination promise using the
 | 
				
			||||||
 | 
					   * transformed value. The source value is transformed using the transformation function.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  private class TransformAction<V> implements Runnable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final Promise<T> src;
 | 
				
			||||||
 | 
					    private final Promise<V> dest;
 | 
				
			||||||
 | 
					    private final Function<? super T, V> func;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private TransformAction(Promise<T> src, Promise<V> dest, Function<? super T, V> func) {
 | 
				
			||||||
 | 
					      this.src = src;
 | 
				
			||||||
 | 
					      this.dest = dest;
 | 
				
			||||||
 | 
					      this.func = func;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void run() {
 | 
				
			||||||
 | 
					      try {
 | 
				
			||||||
 | 
					        dest.fulfill(func.apply(src.get()));
 | 
				
			||||||
 | 
					      } catch (Throwable throwable) {
 | 
				
			||||||
 | 
					        dest.fulfillExceptionally((Exception) throwable.getCause());
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										119
									
								
								promise/src/main/java/com/iluwatar/promise/PromiseSupport.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								promise/src/main/java/com/iluwatar/promise/PromiseSupport.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,119 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * The MIT License
 | 
				
			||||||
 | 
					 * Copyright (c) 2014 Ilkka Seppälä
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
				
			||||||
 | 
					 * of this software and associated documentation files (the "Software"), to deal
 | 
				
			||||||
 | 
					 * in the Software without restriction, including without limitation the rights
 | 
				
			||||||
 | 
					 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
				
			||||||
 | 
					 * copies of the Software, and to permit persons to whom the Software is
 | 
				
			||||||
 | 
					 * furnished to do so, subject to the following conditions:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The above copyright notice and this permission notice shall be included in
 | 
				
			||||||
 | 
					 * all copies or substantial portions of the Software.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
				
			||||||
 | 
					 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
				
			||||||
 | 
					 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
				
			||||||
 | 
					 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
				
			||||||
 | 
					 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
				
			||||||
 | 
					 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
				
			||||||
 | 
					 * THE SOFTWARE.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					package com.iluwatar.promise;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.concurrent.ExecutionException;
 | 
				
			||||||
 | 
					import java.util.concurrent.Future;
 | 
				
			||||||
 | 
					import java.util.concurrent.TimeUnit;
 | 
				
			||||||
 | 
					import java.util.concurrent.TimeoutException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * A really simplified implementation of future that allows completing it successfully with a value 
 | 
				
			||||||
 | 
					 * or exceptionally with an exception.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class PromiseSupport<T> implements Future<T> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static final int RUNNING = 1;
 | 
				
			||||||
 | 
					  private static final int FAILED = 2;
 | 
				
			||||||
 | 
					  private static final int COMPLETED = 3;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private final Object lock;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private volatile int state = RUNNING;
 | 
				
			||||||
 | 
					  private T value;
 | 
				
			||||||
 | 
					  private Exception exception;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  PromiseSupport() {
 | 
				
			||||||
 | 
					    this.lock = new Object();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void fulfill(T value) {
 | 
				
			||||||
 | 
					    this.value = value;
 | 
				
			||||||
 | 
					    this.state = COMPLETED;
 | 
				
			||||||
 | 
					    synchronized (lock) {
 | 
				
			||||||
 | 
					      lock.notifyAll();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void fulfillExceptionally(Exception exception) {
 | 
				
			||||||
 | 
					    this.exception = exception;
 | 
				
			||||||
 | 
					    this.state = FAILED;
 | 
				
			||||||
 | 
					    synchronized (lock) {
 | 
				
			||||||
 | 
					      lock.notifyAll();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public boolean cancel(boolean mayInterruptIfRunning) {
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public boolean isCancelled() {
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public boolean isDone() {
 | 
				
			||||||
 | 
					    return state > RUNNING;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public T get() throws InterruptedException, ExecutionException {
 | 
				
			||||||
 | 
					    if (state == COMPLETED) {
 | 
				
			||||||
 | 
					      return value;
 | 
				
			||||||
 | 
					    } else if (state == FAILED) {
 | 
				
			||||||
 | 
					      throw new ExecutionException(exception);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      synchronized (lock) {
 | 
				
			||||||
 | 
					        lock.wait();
 | 
				
			||||||
 | 
					        if (state == COMPLETED) {
 | 
				
			||||||
 | 
					          return value;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          throw new ExecutionException(exception);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public T get(long timeout, TimeUnit unit)
 | 
				
			||||||
 | 
					      throws InterruptedException, ExecutionException, TimeoutException {
 | 
				
			||||||
 | 
					    if (state == COMPLETED) {
 | 
				
			||||||
 | 
					      return value;
 | 
				
			||||||
 | 
					    } else if (state == FAILED) {
 | 
				
			||||||
 | 
					      throw new ExecutionException(exception);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      synchronized (lock) {
 | 
				
			||||||
 | 
					        lock.wait(unit.toMillis(timeout));
 | 
				
			||||||
 | 
					        if (state == COMPLETED) {
 | 
				
			||||||
 | 
					          return value;
 | 
				
			||||||
 | 
					        } else if (state == FAILED) {
 | 
				
			||||||
 | 
					          throw new ExecutionException(exception);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          throw new TimeoutException();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										101
									
								
								promise/src/main/java/com/iluwatar/promise/Utility.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								promise/src/main/java/com/iluwatar/promise/Utility.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,101 @@
 | 
				
			|||||||
 | 
					package com.iluwatar.promise;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.BufferedReader;
 | 
				
			||||||
 | 
					import java.io.File;
 | 
				
			||||||
 | 
					import java.io.FileReader;
 | 
				
			||||||
 | 
					import java.io.FileWriter;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.io.InputStreamReader;
 | 
				
			||||||
 | 
					import java.io.Reader;
 | 
				
			||||||
 | 
					import java.net.MalformedURLException;
 | 
				
			||||||
 | 
					import java.net.URL;
 | 
				
			||||||
 | 
					import java.util.HashMap;
 | 
				
			||||||
 | 
					import java.util.Iterator;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					import java.util.Map.Entry;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class Utility {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Calculates character frequency of the file provided.
 | 
				
			||||||
 | 
					   * @param fileLocation location of the file.
 | 
				
			||||||
 | 
					   * @return a map of character to its frequency, an empty map if file does not exist.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  public static Map<Character, Integer> characterFrequency(String fileLocation) {
 | 
				
			||||||
 | 
					    Map<Character, Integer> characterToFrequency = new HashMap<>();
 | 
				
			||||||
 | 
					    try (Reader reader = new FileReader(fileLocation);
 | 
				
			||||||
 | 
					        BufferedReader bufferedReader = new BufferedReader(reader)) {
 | 
				
			||||||
 | 
					      for (String line; (line = bufferedReader.readLine()) != null;) {
 | 
				
			||||||
 | 
					        for (char c : line.toCharArray()) {
 | 
				
			||||||
 | 
					          if (!characterToFrequency.containsKey(c)) {
 | 
				
			||||||
 | 
					            characterToFrequency.put(c, 1);
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            characterToFrequency.put(c, characterToFrequency.get(c) + 1);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } catch (IOException ex) {
 | 
				
			||||||
 | 
					      ex.printStackTrace();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return characterToFrequency;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * @return the character with lowest frequency if it exists, {@code Optional.empty()} otherwise.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  public static Character lowestFrequencyChar(Map<Character, Integer> charFrequency) {
 | 
				
			||||||
 | 
					    Character lowestFrequencyChar = null;
 | 
				
			||||||
 | 
					    Iterator<Entry<Character, Integer>> iterator = charFrequency.entrySet().iterator();
 | 
				
			||||||
 | 
					    Entry<Character, Integer> entry = iterator.next();
 | 
				
			||||||
 | 
					    int minFrequency = entry.getValue();
 | 
				
			||||||
 | 
					    lowestFrequencyChar = entry.getKey();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while (iterator.hasNext()) {
 | 
				
			||||||
 | 
					      entry = iterator.next();
 | 
				
			||||||
 | 
					      if (entry.getValue() < minFrequency) {
 | 
				
			||||||
 | 
					        minFrequency = entry.getValue();
 | 
				
			||||||
 | 
					        lowestFrequencyChar = entry.getKey();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return lowestFrequencyChar;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * @return number of lines in the file at provided location. 0 if file does not exist.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  public static Integer countLines(String fileLocation) {
 | 
				
			||||||
 | 
					    int lineCount = 0;
 | 
				
			||||||
 | 
					    try (Reader reader = new FileReader(fileLocation);
 | 
				
			||||||
 | 
					        BufferedReader bufferedReader = new BufferedReader(reader)) {
 | 
				
			||||||
 | 
					      while (bufferedReader.readLine() != null) {
 | 
				
			||||||
 | 
					        lineCount++;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } catch (IOException ex) {
 | 
				
			||||||
 | 
					      ex.printStackTrace();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return lineCount;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Downloads the contents from the given urlString, and stores it in a temporary directory.
 | 
				
			||||||
 | 
					   * @return the absolute path of the file downloaded.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  public static String downloadFile(String urlString) throws MalformedURLException, IOException {
 | 
				
			||||||
 | 
					    System.out.println("Downloading contents from url: " + urlString);
 | 
				
			||||||
 | 
					    URL url = new URL(urlString);
 | 
				
			||||||
 | 
					    File file = File.createTempFile("promise_pattern", null);
 | 
				
			||||||
 | 
					    try (Reader reader = new InputStreamReader(url.openStream());
 | 
				
			||||||
 | 
					        BufferedReader bufferedReader = new BufferedReader(reader);
 | 
				
			||||||
 | 
					        FileWriter writer = new FileWriter(file)) {
 | 
				
			||||||
 | 
					      for (String line; (line = bufferedReader.readLine()) != null; ) {
 | 
				
			||||||
 | 
					        writer.write(line);
 | 
				
			||||||
 | 
					        writer.write("\n");
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      System.out.println("File downloaded at: " + file.getAbsolutePath());
 | 
				
			||||||
 | 
					      return file.getAbsolutePath();
 | 
				
			||||||
 | 
					    } catch (IOException ex) {
 | 
				
			||||||
 | 
					      throw ex;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										39
									
								
								promise/src/test/java/com/iluwatar/promise/AppTest.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								promise/src/test/java/com/iluwatar/promise/AppTest.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * The MIT License
 | 
				
			||||||
 | 
					 * Copyright (c) 2014 Ilkka Seppälä
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
				
			||||||
 | 
					 * of this software and associated documentation files (the "Software"), to deal
 | 
				
			||||||
 | 
					 * in the Software without restriction, including without limitation the rights
 | 
				
			||||||
 | 
					 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
				
			||||||
 | 
					 * copies of the Software, and to permit persons to whom the Software is
 | 
				
			||||||
 | 
					 * furnished to do so, subject to the following conditions:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The above copyright notice and this permission notice shall be included in
 | 
				
			||||||
 | 
					 * all copies or substantial portions of the Software.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
				
			||||||
 | 
					 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
				
			||||||
 | 
					 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
				
			||||||
 | 
					 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
				
			||||||
 | 
					 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
				
			||||||
 | 
					 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
				
			||||||
 | 
					 * THE SOFTWARE.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					package com.iluwatar.promise;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.concurrent.ExecutionException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.junit.Test;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * Application test.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class AppTest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Test
 | 
				
			||||||
 | 
					  public void testApp() throws InterruptedException, ExecutionException {
 | 
				
			||||||
 | 
					    App.main(null);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										263
									
								
								promise/src/test/java/com/iluwatar/promise/PromiseTest.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										263
									
								
								promise/src/test/java/com/iluwatar/promise/PromiseTest.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,263 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * The MIT License
 | 
				
			||||||
 | 
					 * Copyright (c) 2014 Ilkka Seppälä
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
				
			||||||
 | 
					 * of this software and associated documentation files (the "Software"), to deal
 | 
				
			||||||
 | 
					 * in the Software without restriction, including without limitation the rights
 | 
				
			||||||
 | 
					 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
				
			||||||
 | 
					 * copies of the Software, and to permit persons to whom the Software is
 | 
				
			||||||
 | 
					 * furnished to do so, subject to the following conditions:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The above copyright notice and this permission notice shall be included in
 | 
				
			||||||
 | 
					 * all copies or substantial portions of the Software.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
				
			||||||
 | 
					 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
				
			||||||
 | 
					 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
				
			||||||
 | 
					 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
				
			||||||
 | 
					 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
				
			||||||
 | 
					 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
				
			||||||
 | 
					 * THE SOFTWARE.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					package com.iluwatar.promise;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static org.junit.Assert.assertEquals;
 | 
				
			||||||
 | 
					import static org.junit.Assert.assertFalse;
 | 
				
			||||||
 | 
					import static org.junit.Assert.assertTrue;
 | 
				
			||||||
 | 
					import static org.junit.Assert.fail;
 | 
				
			||||||
 | 
					import static org.mockito.Matchers.eq;
 | 
				
			||||||
 | 
					import static org.mockito.Mockito.mock;
 | 
				
			||||||
 | 
					import static org.mockito.Mockito.verify;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.concurrent.Callable;
 | 
				
			||||||
 | 
					import java.util.concurrent.ExecutionException;
 | 
				
			||||||
 | 
					import java.util.concurrent.Executor;
 | 
				
			||||||
 | 
					import java.util.concurrent.Executors;
 | 
				
			||||||
 | 
					import java.util.concurrent.TimeUnit;
 | 
				
			||||||
 | 
					import java.util.concurrent.TimeoutException;
 | 
				
			||||||
 | 
					import java.util.function.Consumer;
 | 
				
			||||||
 | 
					import java.util.function.Function;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.junit.Before;
 | 
				
			||||||
 | 
					import org.junit.Rule;
 | 
				
			||||||
 | 
					import org.junit.Test;
 | 
				
			||||||
 | 
					import org.junit.rules.ExpectedException;
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Tests Promise class.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class PromiseTest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private Executor executor;
 | 
				
			||||||
 | 
					  private Promise<Integer> promise;
 | 
				
			||||||
 | 
					  @Rule public ExpectedException exception = ExpectedException.none();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Before
 | 
				
			||||||
 | 
					  public void setUp() {
 | 
				
			||||||
 | 
					    executor = Executors.newSingleThreadExecutor();
 | 
				
			||||||
 | 
					    promise = new Promise<>();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Test
 | 
				
			||||||
 | 
					  public void promiseIsFulfilledWithTheResultantValueOfExecutingTheTask() 
 | 
				
			||||||
 | 
					      throws InterruptedException, ExecutionException {
 | 
				
			||||||
 | 
					    promise.fulfillInAsync(new NumberCrunchingTask(), executor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assertEquals(NumberCrunchingTask.CRUNCHED_NUMBER, promise.get());
 | 
				
			||||||
 | 
					    assertTrue(promise.isDone());
 | 
				
			||||||
 | 
					    assertFalse(promise.isCancelled());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  @Test
 | 
				
			||||||
 | 
					  public void promiseIsFulfilledWithAnExceptionIfTaskThrowsAnException() 
 | 
				
			||||||
 | 
					      throws InterruptedException, ExecutionException, TimeoutException {
 | 
				
			||||||
 | 
					    testWaitingForeverForPromiseToBeFulfilled();
 | 
				
			||||||
 | 
					    testWaitingSomeTimeForPromiseToBeFulfilled();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private void testWaitingForeverForPromiseToBeFulfilled() 
 | 
				
			||||||
 | 
					      throws InterruptedException, TimeoutException {
 | 
				
			||||||
 | 
					    Promise<Integer> promise = new Promise<>();
 | 
				
			||||||
 | 
					    promise.fulfillInAsync(new Callable<Integer>() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      @Override
 | 
				
			||||||
 | 
					      public Integer call() throws Exception {
 | 
				
			||||||
 | 
					        throw new RuntimeException("Barf!");
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }, executor);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      promise.get();
 | 
				
			||||||
 | 
					      fail("Fetching promise should result in exception if the task threw an exception");
 | 
				
			||||||
 | 
					    } catch (ExecutionException ex) {
 | 
				
			||||||
 | 
					      assertTrue(promise.isDone());
 | 
				
			||||||
 | 
					      assertFalse(promise.isCancelled());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      promise.get(1000, TimeUnit.SECONDS);
 | 
				
			||||||
 | 
					      fail("Fetching promise should result in exception if the task threw an exception");
 | 
				
			||||||
 | 
					    } catch (ExecutionException ex) {
 | 
				
			||||||
 | 
					      assertTrue(promise.isDone());
 | 
				
			||||||
 | 
					      assertFalse(promise.isCancelled());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  private void testWaitingSomeTimeForPromiseToBeFulfilled() 
 | 
				
			||||||
 | 
					      throws InterruptedException, TimeoutException {
 | 
				
			||||||
 | 
					    Promise<Integer> promise = new Promise<>();
 | 
				
			||||||
 | 
					    promise.fulfillInAsync(new Callable<Integer>() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      @Override
 | 
				
			||||||
 | 
					      public Integer call() throws Exception {
 | 
				
			||||||
 | 
					        throw new RuntimeException("Barf!");
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }, executor);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      promise.get(1000, TimeUnit.SECONDS);
 | 
				
			||||||
 | 
					      fail("Fetching promise should result in exception if the task threw an exception");
 | 
				
			||||||
 | 
					    } catch (ExecutionException ex) {
 | 
				
			||||||
 | 
					      assertTrue(promise.isDone());
 | 
				
			||||||
 | 
					      assertFalse(promise.isCancelled());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      promise.get();
 | 
				
			||||||
 | 
					      fail("Fetching promise should result in exception if the task threw an exception");
 | 
				
			||||||
 | 
					    } catch (ExecutionException ex) {
 | 
				
			||||||
 | 
					      assertTrue(promise.isDone());
 | 
				
			||||||
 | 
					      assertFalse(promise.isCancelled());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Test
 | 
				
			||||||
 | 
					  public void dependentPromiseIsFulfilledAfterTheConsumerConsumesTheResultOfThisPromise() 
 | 
				
			||||||
 | 
					      throws InterruptedException, ExecutionException {
 | 
				
			||||||
 | 
					    Promise<Void> dependentPromise = promise
 | 
				
			||||||
 | 
					        .fulfillInAsync(new NumberCrunchingTask(), executor)
 | 
				
			||||||
 | 
					        .thenAccept(value -> {
 | 
				
			||||||
 | 
					          assertEquals(NumberCrunchingTask.CRUNCHED_NUMBER, value);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dependentPromise.get();
 | 
				
			||||||
 | 
					    assertTrue(dependentPromise.isDone());
 | 
				
			||||||
 | 
					    assertFalse(dependentPromise.isCancelled());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Test
 | 
				
			||||||
 | 
					  public void dependentPromiseIsFulfilledWithAnExceptionIfConsumerThrowsAnException() 
 | 
				
			||||||
 | 
					      throws InterruptedException, ExecutionException, TimeoutException {
 | 
				
			||||||
 | 
					    Promise<Void> dependentPromise = promise
 | 
				
			||||||
 | 
					        .fulfillInAsync(new NumberCrunchingTask(), executor)
 | 
				
			||||||
 | 
					        .thenAccept(new Consumer<Integer>() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          @Override
 | 
				
			||||||
 | 
					          public void accept(Integer value) {
 | 
				
			||||||
 | 
					            throw new RuntimeException("Barf!");
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      dependentPromise.get();
 | 
				
			||||||
 | 
					      fail("Fetching dependent promise should result in exception "
 | 
				
			||||||
 | 
					          + "if the action threw an exception");
 | 
				
			||||||
 | 
					    } catch (ExecutionException ex) {
 | 
				
			||||||
 | 
					      assertTrue(promise.isDone());
 | 
				
			||||||
 | 
					      assertFalse(promise.isCancelled());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      dependentPromise.get(1000, TimeUnit.SECONDS);
 | 
				
			||||||
 | 
					      fail("Fetching dependent promise should result in exception "
 | 
				
			||||||
 | 
					          + "if the action threw an exception");
 | 
				
			||||||
 | 
					    } catch (ExecutionException ex) {
 | 
				
			||||||
 | 
					      assertTrue(promise.isDone());
 | 
				
			||||||
 | 
					      assertFalse(promise.isCancelled());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Test
 | 
				
			||||||
 | 
					  public void dependentPromiseIsFulfilledAfterTheFunctionTransformsTheResultOfThisPromise() 
 | 
				
			||||||
 | 
					      throws InterruptedException, ExecutionException {
 | 
				
			||||||
 | 
					    Promise<String> dependentPromise = promise
 | 
				
			||||||
 | 
					        .fulfillInAsync(new NumberCrunchingTask(), executor)
 | 
				
			||||||
 | 
					        .thenApply(value -> {
 | 
				
			||||||
 | 
					          assertEquals(NumberCrunchingTask.CRUNCHED_NUMBER, value); 
 | 
				
			||||||
 | 
					          return String.valueOf(value);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assertEquals(String.valueOf(NumberCrunchingTask.CRUNCHED_NUMBER), dependentPromise.get());
 | 
				
			||||||
 | 
					    assertTrue(dependentPromise.isDone());
 | 
				
			||||||
 | 
					    assertFalse(dependentPromise.isCancelled());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  @Test
 | 
				
			||||||
 | 
					  public void dependentPromiseIsFulfilledWithAnExceptionIfTheFunctionThrowsException() 
 | 
				
			||||||
 | 
					      throws InterruptedException, ExecutionException, TimeoutException {
 | 
				
			||||||
 | 
					    Promise<String> dependentPromise = promise
 | 
				
			||||||
 | 
					        .fulfillInAsync(new NumberCrunchingTask(), executor)
 | 
				
			||||||
 | 
					        .thenApply(new Function<Integer, String>() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          @Override
 | 
				
			||||||
 | 
					          public String apply(Integer value) {
 | 
				
			||||||
 | 
					            throw new RuntimeException("Barf!");
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      dependentPromise.get();
 | 
				
			||||||
 | 
					      fail("Fetching dependent promise should result in exception "
 | 
				
			||||||
 | 
					          + "if the function threw an exception");
 | 
				
			||||||
 | 
					    } catch (ExecutionException ex) {
 | 
				
			||||||
 | 
					      assertTrue(promise.isDone());
 | 
				
			||||||
 | 
					      assertFalse(promise.isCancelled());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      dependentPromise.get(1000, TimeUnit.SECONDS);
 | 
				
			||||||
 | 
					      fail("Fetching dependent promise should result in exception "
 | 
				
			||||||
 | 
					          + "if the function threw an exception");
 | 
				
			||||||
 | 
					    } catch (ExecutionException ex) {
 | 
				
			||||||
 | 
					      assertTrue(promise.isDone());
 | 
				
			||||||
 | 
					      assertFalse(promise.isCancelled());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  @Test
 | 
				
			||||||
 | 
					  public void fetchingAnAlreadyFulfilledPromiseReturnsTheFulfilledValueImmediately() 
 | 
				
			||||||
 | 
					      throws InterruptedException, ExecutionException, TimeoutException {
 | 
				
			||||||
 | 
					    Promise<Integer> promise = new Promise<>();
 | 
				
			||||||
 | 
					    promise.fulfill(NumberCrunchingTask.CRUNCHED_NUMBER);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    promise.get(1000, TimeUnit.SECONDS);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  @SuppressWarnings("unchecked")
 | 
				
			||||||
 | 
					  @Test
 | 
				
			||||||
 | 
					  public void exceptionHandlerIsCalledWhenPromiseIsFulfilledExceptionally() {
 | 
				
			||||||
 | 
					    Promise<Object> promise = new Promise<>();
 | 
				
			||||||
 | 
					    Consumer<Throwable> exceptionHandler = mock(Consumer.class);
 | 
				
			||||||
 | 
					    promise.onError(exceptionHandler);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    Exception exception = new Exception("barf!");
 | 
				
			||||||
 | 
					    promise.fulfillExceptionally(exception);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    verify(exceptionHandler).accept(eq(exception));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static class NumberCrunchingTask implements Callable<Integer> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final Integer CRUNCHED_NUMBER = Integer.MAX_VALUE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public Integer call() throws Exception {
 | 
				
			||||||
 | 
					      // Do number crunching
 | 
				
			||||||
 | 
					      Thread.sleep(100);
 | 
				
			||||||
 | 
					      return CRUNCHED_NUMBER;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user