Work on #403, added basic implementation of promise pattern
This commit is contained in:
parent
d4c2f0392d
commit
ea7503414e
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>
|
24
promise/src/main/java/com/iluwatar/promise/App.java
Normal file
24
promise/src/main/java/com/iluwatar/promise/App.java
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package com.iluwatar.promise;
|
||||||
|
|
||||||
|
public class App {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Program entry point
|
||||||
|
* @param args arguments
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) {
|
||||||
|
ThreadAsyncExecutor executor = new ThreadAsyncExecutor();
|
||||||
|
executor.execute(() -> {
|
||||||
|
Thread.sleep(1000);
|
||||||
|
return 10;
|
||||||
|
}).then(value -> {System.out.println("Consumed the value: " + value);})
|
||||||
|
.then(nullVal -> {System.out.println("Post consuming value");});
|
||||||
|
|
||||||
|
|
||||||
|
executor.execute(() -> {
|
||||||
|
Thread.sleep(1000);
|
||||||
|
return "10";
|
||||||
|
}).then(value -> {return 10 + Integer.parseInt(value);})
|
||||||
|
.then(intValue -> {System.out.println("Consumed int value: " + intValue);});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package com.iluwatar.promise;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import com.iluwatar.async.method.invocation.AsyncResult;
|
||||||
|
|
||||||
|
public interface ListenableAsyncResult<T> extends AsyncResult<T> {
|
||||||
|
ListenableAsyncResult<Void> then(Consumer<? super T> action);
|
||||||
|
<V> ListenableAsyncResult<V> then(Function<? super T, V> func);
|
||||||
|
ListenableAsyncResult<T> error(Consumer<? extends Throwable> action);
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package com.iluwatar.promise;
|
||||||
|
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
|
public interface PromiseAsyncExecutor {
|
||||||
|
<T> ListenableAsyncResult<T> execute(Callable<T> task);
|
||||||
|
}
|
@ -0,0 +1,173 @@
|
|||||||
|
package com.iluwatar.promise;
|
||||||
|
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
public class ThreadAsyncExecutor implements PromiseAsyncExecutor {
|
||||||
|
|
||||||
|
/** Index for thread naming */
|
||||||
|
private final AtomicInteger idx = new AtomicInteger(0);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> ListenableAsyncResult<T> execute(Callable<T> task) {
|
||||||
|
Promise<T> promise = new Promise<>();
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
promise.setValue(task.call());
|
||||||
|
promise.postComplete();
|
||||||
|
} catch (Exception ex) {
|
||||||
|
promise.setException(ex);
|
||||||
|
}
|
||||||
|
} , "executor-" + idx.incrementAndGet()).start();
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO there is scope of extending the completable future from async method invocation project. Do that.
|
||||||
|
private class Promise<T> implements ListenableAsyncResult<T> {
|
||||||
|
|
||||||
|
static final int RUNNING = 1;
|
||||||
|
static final int FAILED = 2;
|
||||||
|
static final int COMPLETED = 3;
|
||||||
|
|
||||||
|
final Object lock;
|
||||||
|
volatile int state = RUNNING;
|
||||||
|
T value;
|
||||||
|
Exception exception;
|
||||||
|
Runnable fulfilmentAction;
|
||||||
|
|
||||||
|
public Promise() {
|
||||||
|
this.lock = new Object();
|
||||||
|
}
|
||||||
|
|
||||||
|
void postComplete() {
|
||||||
|
fulfilmentAction.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value from successful execution and executes callback if available. Notifies any thread waiting for
|
||||||
|
* completion.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* value of the evaluated task
|
||||||
|
*/
|
||||||
|
public void setValue(T value) {
|
||||||
|
this.value = value;
|
||||||
|
this.state = COMPLETED;
|
||||||
|
synchronized (lock) {
|
||||||
|
lock.notifyAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the exception from failed execution and executes callback if available. Notifies any thread waiting for
|
||||||
|
* completion.
|
||||||
|
*
|
||||||
|
* @param exception
|
||||||
|
* exception of the failed task
|
||||||
|
*/
|
||||||
|
public void setException(Exception exception) {
|
||||||
|
this.exception = exception;
|
||||||
|
this.state = FAILED;
|
||||||
|
synchronized (lock) {
|
||||||
|
lock.notifyAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCompleted() {
|
||||||
|
return state > RUNNING;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T getValue() throws ExecutionException {
|
||||||
|
if (state == COMPLETED) {
|
||||||
|
return value;
|
||||||
|
} else if (state == FAILED) {
|
||||||
|
throw new ExecutionException(exception);
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException("Execution not completed yet");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void await() throws InterruptedException {
|
||||||
|
synchronized (lock) {
|
||||||
|
if (!isCompleted()) {
|
||||||
|
lock.wait();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListenableAsyncResult<Void> then(Consumer<? super T> action) {
|
||||||
|
Promise<Void> dest = new Promise<>();
|
||||||
|
fulfilmentAction = new ConsumeAction(this, dest, action);
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <V> ListenableAsyncResult<V> then(Function<? super T, V> func) {
|
||||||
|
Promise<V> dest = new Promise<>();
|
||||||
|
fulfilmentAction = new FunctionAction<V>(this, dest, func);
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListenableAsyncResult<T> error(Consumer<? extends Throwable> action) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ConsumeAction implements Runnable {
|
||||||
|
|
||||||
|
private Promise<T> current;
|
||||||
|
private Promise<Void> dest;
|
||||||
|
private Consumer<? super T> action;
|
||||||
|
|
||||||
|
public ConsumeAction(Promise<T> current, Promise<Void> dest, Consumer<? super T> action) {
|
||||||
|
this.current = current;
|
||||||
|
this.dest = dest;
|
||||||
|
this.action = action;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
action.accept(current.getValue());
|
||||||
|
dest.setValue(null);
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
dest.postComplete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class FunctionAction<V> implements Runnable {
|
||||||
|
|
||||||
|
private Promise<T> current;
|
||||||
|
private Promise<V> dest;
|
||||||
|
private Function<? super T, V> func;
|
||||||
|
|
||||||
|
public FunctionAction(Promise<T> current, Promise<V> dest, Function<? super T, V> func) {
|
||||||
|
this.current = current;
|
||||||
|
this.dest = dest;
|
||||||
|
this.func = func;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
V result = func.apply(current.getValue());
|
||||||
|
dest.setValue(result);
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
dest.postComplete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user