#68: Implementation of Async Method Invocation pattern

This commit is contained in:
Tapio Rautonen 2015-07-20 22:55:39 +03:00
parent d3642cc94c
commit 3e8ef01288
8 changed files with 218 additions and 0 deletions

View File

@ -0,0 +1,18 @@
<?xml version="1.0"?>
<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.3.0</version>
</parent>
<artifactId>async-method-invocation</artifactId>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,50 @@
package com.iluwatar.async.method.invocation;
import java.util.concurrent.Callable;
public class App {
public static void main(String[] args) throws Exception {
AsyncExecutor executor = new ThreadAsyncExecutor();
AsyncResult<Integer> asyncResult1 = executor.startProcess(lazyval(10, 500));
AsyncResult<String> asyncResult2 = executor.startProcess(lazyval("test", 300));
AsyncResult<Long> asyncResult3 = executor.startProcess(lazyval(50L, 700));
AsyncResult<Integer> asyncResult4 = executor.startProcess(lazyval(20, 400), callback("Callback result 4"));
AsyncResult<String> asyncResult5 = executor.startProcess(lazyval("callback", 600), callback("Callback result 5"));
Thread.sleep(350); // Oh boy I'm working hard here
log("Some hard work done");
Integer result1 = executor.endProcess(asyncResult1);
String result2 = executor.endProcess(asyncResult2);
Long result3 = executor.endProcess(asyncResult3);
asyncResult4.await();
asyncResult5.await();
log("Result 1: " + result1);
log("Result 2: " + result2);
log("Result 3: " + result3);
}
private static <T> Callable<T> lazyval(T value, long delayMillis) {
return () -> {
Thread.sleep(delayMillis);
log("Task completed with: " + value);
return value;
};
}
private static <T> AsyncCallback<T> callback(String name) {
return (value, ex) -> {
if (ex.isPresent()) {
log(name + " failed: " + ex.map(Exception::getMessage).orElse(""));
} else {
log(name + ": " + value);
}
};
}
private static void log(String msg) {
System.out.println(String.format("[%1$-10s] - %2$s", Thread.currentThread().getName(), msg));
}
}

View File

@ -0,0 +1,9 @@
package com.iluwatar.async.method.invocation;
import java.util.Optional;
public interface AsyncCallback<T> {
void onComplete(T value, Optional<Exception> ex);
}

View File

@ -0,0 +1,14 @@
package com.iluwatar.async.method.invocation;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
public interface AsyncExecutor {
<T> AsyncResult<T> startProcess(Callable<T> task);
<T> AsyncResult<T> startProcess(Callable<T> task, AsyncCallback<T> callback);
<T> T endProcess(AsyncResult<T> asyncResult) throws ExecutionException, InterruptedException;
}

View File

@ -0,0 +1,12 @@
package com.iluwatar.async.method.invocation;
import java.util.concurrent.ExecutionException;
public interface AsyncResult<T> {
boolean isCompleted();
T getValue() throws ExecutionException;
void await() throws InterruptedException;
}

View File

@ -0,0 +1,101 @@
package com.iluwatar.async.method.invocation;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadAsyncExecutor implements AsyncExecutor {
private final AtomicInteger idx = new AtomicInteger(0);
@Override
public <T> AsyncResult<T> startProcess(Callable<T> task) {
return startProcess(task, null);
}
@Override
public <T> AsyncResult<T> startProcess(Callable<T> task, AsyncCallback<T> callback) {
CompletableResult<T> result = new CompletableResult<>(callback);
new Thread(() -> {
try {
result.setValue(task.call());
} catch (Exception ex) {
result.setException(ex);
}
}, "executor-" + idx.incrementAndGet()).start();
return result;
}
@Override
public <T> T endProcess(AsyncResult<T> asyncResult) throws ExecutionException, InterruptedException {
if (asyncResult.isCompleted()) {
return asyncResult.getValue();
} else {
asyncResult.await();
return asyncResult.getValue();
}
}
private static class CompletableResult<T> implements AsyncResult<T> {
static final int RUNNING = 1;
static final int FAILED = 2;
static final int COMPLETED = 3;
final Object lock;
final Optional<AsyncCallback<T>> callback;
volatile int state = RUNNING;
T value;
Exception exception;
CompletableResult(AsyncCallback<T> callback) {
this.lock = new Object();
this.callback = Optional.ofNullable(callback);
}
void setValue(T value) {
this.value = value;
this.state = COMPLETED;
this.callback.ifPresent(ac -> ac.onComplete(value, Optional.<Exception>empty()));
synchronized (lock) {
lock.notifyAll();
}
}
void setException(Exception exception) {
this.exception = exception;
this.state = FAILED;
this.callback.ifPresent(ac -> ac.onComplete(null, Optional.of(exception)));
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();
}
}
}
}
}

View File

@ -0,0 +1,13 @@
package com.iluwatar.async.method.invocation;
import org.junit.Test;
public class AppTest {
@Test
public void test() throws Exception {
String[] args = {};
App.main(args);
}
}

View File

@ -71,6 +71,7 @@
<module>naked-objects</module>
<module>front-controller</module>
<module>repository</module>
<module>async-method-invocation</module>
</modules>
<dependencyManagement>