From 3e8ef01288d862ea9e6db0a8f8aa0727aff12dfd Mon Sep 17 00:00:00 2001 From: Tapio Rautonen Date: Mon, 20 Jul 2015 22:55:39 +0300 Subject: [PATCH] #68: Implementation of Async Method Invocation pattern --- async-method-invocation/pom.xml | 18 ++++ .../iluwatar/async/method/invocation/App.java | 50 +++++++++ .../method/invocation/AsyncCallback.java | 9 ++ .../method/invocation/AsyncExecutor.java | 14 +++ .../async/method/invocation/AsyncResult.java | 12 +++ .../invocation/ThreadAsyncExecutor.java | 101 ++++++++++++++++++ .../async/method/invocation/AppTest.java | 13 +++ pom.xml | 1 + 8 files changed, 218 insertions(+) create mode 100644 async-method-invocation/pom.xml create mode 100644 async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/App.java create mode 100644 async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncCallback.java create mode 100644 async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncExecutor.java create mode 100644 async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncResult.java create mode 100644 async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutor.java create mode 100644 async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/AppTest.java diff --git a/async-method-invocation/pom.xml b/async-method-invocation/pom.xml new file mode 100644 index 000000000..be932bca5 --- /dev/null +++ b/async-method-invocation/pom.xml @@ -0,0 +1,18 @@ + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.3.0 + + async-method-invocation + + + junit + junit + test + + + diff --git a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/App.java b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/App.java new file mode 100644 index 000000000..f59f831b2 --- /dev/null +++ b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/App.java @@ -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 asyncResult1 = executor.startProcess(lazyval(10, 500)); + AsyncResult asyncResult2 = executor.startProcess(lazyval("test", 300)); + AsyncResult asyncResult3 = executor.startProcess(lazyval(50L, 700)); + AsyncResult asyncResult4 = executor.startProcess(lazyval(20, 400), callback("Callback result 4")); + AsyncResult 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 Callable lazyval(T value, long delayMillis) { + return () -> { + Thread.sleep(delayMillis); + log("Task completed with: " + value); + return value; + }; + } + + private static AsyncCallback 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)); + } +} diff --git a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncCallback.java b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncCallback.java new file mode 100644 index 000000000..0477f70e1 --- /dev/null +++ b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncCallback.java @@ -0,0 +1,9 @@ +package com.iluwatar.async.method.invocation; + +import java.util.Optional; + +public interface AsyncCallback { + + void onComplete(T value, Optional ex); + +} diff --git a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncExecutor.java b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncExecutor.java new file mode 100644 index 000000000..dd23a8bfb --- /dev/null +++ b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncExecutor.java @@ -0,0 +1,14 @@ +package com.iluwatar.async.method.invocation; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; + +public interface AsyncExecutor { + + AsyncResult startProcess(Callable task); + + AsyncResult startProcess(Callable task, AsyncCallback callback); + + T endProcess(AsyncResult asyncResult) throws ExecutionException, InterruptedException; + +} diff --git a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncResult.java b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncResult.java new file mode 100644 index 000000000..5bf6145b8 --- /dev/null +++ b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncResult.java @@ -0,0 +1,12 @@ +package com.iluwatar.async.method.invocation; + +import java.util.concurrent.ExecutionException; + +public interface AsyncResult { + + boolean isCompleted(); + + T getValue() throws ExecutionException; + + void await() throws InterruptedException; +} diff --git a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutor.java b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutor.java new file mode 100644 index 000000000..b368e284d --- /dev/null +++ b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutor.java @@ -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 AsyncResult startProcess(Callable task) { + return startProcess(task, null); + } + + @Override + public AsyncResult startProcess(Callable task, AsyncCallback callback) { + CompletableResult 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 endProcess(AsyncResult asyncResult) throws ExecutionException, InterruptedException { + if (asyncResult.isCompleted()) { + return asyncResult.getValue(); + } else { + asyncResult.await(); + return asyncResult.getValue(); + } + } + + private static class CompletableResult implements AsyncResult { + + static final int RUNNING = 1; + static final int FAILED = 2; + static final int COMPLETED = 3; + + final Object lock; + final Optional> callback; + + volatile int state = RUNNING; + T value; + Exception exception; + + CompletableResult(AsyncCallback 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.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(); + } + } + } + } +} diff --git a/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/AppTest.java b/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/AppTest.java new file mode 100644 index 000000000..989fa6d1d --- /dev/null +++ b/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/AppTest.java @@ -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); + } + +} diff --git a/pom.xml b/pom.xml index aa484f15a..93bb137b4 100644 --- a/pom.xml +++ b/pom.xml @@ -71,6 +71,7 @@ naked-objects front-controller repository + async-method-invocation