Skip to content

Commit 2b945ca

Browse files
committed
Work on iluwatar#403, removed dependency on async method invocation module, added more tests
1 parent 4bd1f14 commit 2b945ca

File tree

5 files changed

+275
-76
lines changed

5 files changed

+275
-76
lines changed

promise/pom.xml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,5 @@
4343
<artifactId>mockito-core</artifactId>
4444
<scope>test</scope>
4545
</dependency>
46-
<dependency>
47-
<groupId>com.iluwatar</groupId>
48-
<artifactId>async-method-invocation</artifactId>
49-
<version>1.13.0-SNAPSHOT</version>
50-
</dependency>
5146
</dependencies>
5247
</project>

promise/src/main/java/com/iluwatar/promise/App.java

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package com.iluwatar.promise;
22

3-
import com.iluwatar.async.method.invocation.ThreadAsyncExecutor;
3+
import java.util.concurrent.ExecutionException;
4+
import java.util.concurrent.Executor;
5+
import java.util.concurrent.ExecutorService;
6+
import java.util.concurrent.Executors;
47

58
/**
69
*
@@ -12,10 +15,19 @@ public class App {
1215
* Program entry point
1316
* @param args arguments
1417
* @throws InterruptedException if main thread is interruped.
18+
* @throws ExecutionException
1519
*/
16-
public static void main(String[] args) throws InterruptedException {
17-
ThreadAsyncExecutor executor = new ThreadAsyncExecutor();
18-
20+
public static void main(String[] args) throws InterruptedException, ExecutionException {
21+
ExecutorService executor = Executors.newSingleThreadExecutor();
22+
try {
23+
promiseUsage(executor);
24+
} finally {
25+
executor.shutdownNow();
26+
}
27+
}
28+
29+
private static void promiseUsage(Executor executor)
30+
throws InterruptedException, ExecutionException {
1931
Promise<Integer> consumedPromise = new Promise<>();
2032
consumedPromise.fulfillInAsync(() -> {
2133
Thread.sleep(1000);
@@ -29,10 +41,10 @@ public static void main(String[] args) throws InterruptedException {
2941
Thread.sleep(1000);
3042
return "10";
3143
}, executor).then(value -> { return Integer.parseInt(value); }).then(value -> {
32-
System.out.println(value);
44+
System.out.println("Consumed transformed int value: " + value);
3345
});
3446

35-
consumedPromise.await();
36-
transformedPromise.await();
47+
consumedPromise.get();
48+
transformedPromise.get();
3749
}
3850
}

promise/src/main/java/com/iluwatar/promise/Promise.java

Lines changed: 134 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,49 +2,49 @@
22

33
import java.util.concurrent.Callable;
44
import java.util.concurrent.ExecutionException;
5+
import java.util.concurrent.Executor;
6+
import java.util.concurrent.Future;
7+
import java.util.concurrent.TimeUnit;
8+
import java.util.concurrent.TimeoutException;
59
import java.util.function.Consumer;
610
import java.util.function.Function;
711

8-
import com.iluwatar.async.method.invocation.AsyncExecutor;
9-
import com.iluwatar.async.method.invocation.internal.CompletableResult;
10-
1112
/**
1213
* Implements the promise pattern.
1314
* @param <T> type of result.
1415
*/
15-
public class Promise<T> extends CompletableResult<T> {
16+
public class Promise<T> extends PromiseSupport<T> {
1617

1718
private Runnable fulfillmentAction;
1819

1920
/**
2021
* Creates a promise that will be fulfilled in future.
2122
*/
2223
public Promise() {
23-
super(null);
2424
}
2525

2626
/**
2727
* Fulfills the promise with the provided value.
28-
* @param value the fulfilled value that can be accessed using {@link #getValue()}.
28+
* @param value the fulfilled value that can be accessed using {@link #get()}.
2929
*/
3030
@Override
31-
public void setValue(T value) {
32-
super.setValue(value);
33-
postComplete();
31+
public void fulfill(T value) {
32+
super.fulfill(value);
33+
postFulfillment();
3434
}
3535

3636
/**
3737
* Fulfills the promise with exception due to error in execution.
3838
* @param exception the exception will be wrapped in {@link ExecutionException}
39-
* when accessing the value using {@link #getValue()}.
39+
* when accessing the value using {@link #get()}.
4040
*/
4141
@Override
42-
public void setException(Exception exception) {
43-
super.setException(exception);
44-
postComplete();
42+
public void fulfillExceptionally(Exception exception) {
43+
super.fulfillExceptionally(exception);
44+
postFulfillment();
4545
}
4646

47-
void postComplete() {
47+
void postFulfillment() {
4848
if (fulfillmentAction == null) {
4949
return;
5050
}
@@ -59,13 +59,12 @@ void postComplete() {
5959
* @param executor the executor in which the task should be run.
6060
* @return a promise that represents the result of running the task provided.
6161
*/
62-
public Promise<T> fulfillInAsync(final Callable<T> task, AsyncExecutor executor) {
63-
executor.startProcess(new Callable<Void>() {
64-
65-
@Override
66-
public Void call() throws Exception {
67-
setValue(task.call());
68-
return null;
62+
public Promise<T> fulfillInAsync(final Callable<T> task, Executor executor) {
63+
executor.execute(() -> {
64+
try {
65+
fulfill(task.call());
66+
} catch (Exception e) {
67+
fulfillExceptionally(e);
6968
}
7069
});
7170
return this;
@@ -91,53 +90,153 @@ public Promise<Void> then(Consumer<? super T> action) {
9190
*/
9291
public <V> Promise<V> then(Function<? super T, V> func) {
9392
Promise<V> dest = new Promise<>();
94-
fulfillmentAction = new FunctionAction<V>(this, dest, func);
93+
fulfillmentAction = new TransformAction<V>(this, dest, func);
9594
return dest;
9695
}
9796

97+
/**
98+
* A consume action provides the action, the value from source promise and fulfills the
99+
* destination promise.
100+
*/
98101
private class ConsumeAction implements Runnable {
99102

100-
private Promise<T> current;
103+
private Promise<T> src;
101104
private Promise<Void> dest;
102105
private Consumer<? super T> action;
103106

104-
public ConsumeAction(Promise<T> current, Promise<Void> dest, Consumer<? super T> action) {
105-
this.current = current;
107+
ConsumeAction(Promise<T> src, Promise<Void> dest, Consumer<? super T> action) {
108+
this.src = src;
106109
this.dest = dest;
107110
this.action = action;
108111
}
109112

110113
@Override
111114
public void run() {
112115
try {
113-
action.accept(current.getValue());
114-
dest.setValue(null);
116+
action.accept(src.get());
117+
dest.fulfill(null);
115118
} catch (Throwable e) {
116-
dest.setException((Exception) e.getCause());
119+
dest.fulfillExceptionally((Exception) e.getCause());
117120
}
118121
}
119122
}
120123

121-
private class FunctionAction<V> implements Runnable {
124+
/**
125+
* A function action provides transformation function, value from source promise and fulfills the
126+
* destination promise with the transformed value.
127+
*/
128+
private class TransformAction<V> implements Runnable {
122129

123-
private Promise<T> current;
130+
private Promise<T> src;
124131
private Promise<V> dest;
125132
private Function<? super T, V> func;
126133

127-
public FunctionAction(Promise<T> current, Promise<V> dest, Function<? super T, V> func) {
128-
this.current = current;
134+
TransformAction(Promise<T> src, Promise<V> dest, Function<? super T, V> func) {
135+
this.src = src;
129136
this.dest = dest;
130137
this.func = func;
131138
}
132139

133140
@Override
134141
public void run() {
135142
try {
136-
V result = func.apply(current.getValue());
137-
dest.setValue(result);
143+
V result = func.apply(src.get());
144+
dest.fulfill(result);
138145
} catch (Throwable e) {
139-
dest.setException((Exception) e.getCause());
146+
dest.fulfillExceptionally((Exception) e.getCause());
140147
}
141148
}
142149
}
143150
}
151+
152+
153+
/**
154+
* A really simplified implementation of future that allows completing it successfully with a value
155+
* or exceptionally with an exception.
156+
*/
157+
class PromiseSupport<T> implements Future<T> {
158+
159+
static final int RUNNING = 1;
160+
static final int FAILED = 2;
161+
static final int COMPLETED = 3;
162+
163+
final Object lock;
164+
165+
volatile int state = RUNNING;
166+
T value;
167+
Exception exception;
168+
169+
PromiseSupport() {
170+
this.lock = new Object();
171+
}
172+
173+
void fulfill(T value) {
174+
this.value = value;
175+
this.state = COMPLETED;
176+
synchronized (lock) {
177+
lock.notifyAll();
178+
}
179+
}
180+
181+
void fulfillExceptionally(Exception exception) {
182+
this.exception = exception;
183+
this.state = FAILED;
184+
synchronized (lock) {
185+
lock.notifyAll();
186+
}
187+
}
188+
189+
@Override
190+
public boolean cancel(boolean mayInterruptIfRunning) {
191+
return false;
192+
}
193+
194+
@Override
195+
public boolean isCancelled() {
196+
return false;
197+
}
198+
199+
@Override
200+
public boolean isDone() {
201+
return state > RUNNING;
202+
}
203+
204+
@Override
205+
public T get() throws InterruptedException, ExecutionException {
206+
if (state == COMPLETED) {
207+
return value;
208+
} else if (state == FAILED) {
209+
throw new ExecutionException(exception);
210+
} else {
211+
synchronized (lock) {
212+
lock.wait();
213+
if (state == COMPLETED) {
214+
return value;
215+
} else {
216+
throw new ExecutionException(exception);
217+
}
218+
}
219+
}
220+
}
221+
222+
@Override
223+
public T get(long timeout, TimeUnit unit)
224+
throws InterruptedException, ExecutionException, TimeoutException {
225+
if (state == COMPLETED) {
226+
return value;
227+
} else if (state == FAILED) {
228+
throw new ExecutionException(exception);
229+
} else {
230+
synchronized (lock) {
231+
lock.wait(unit.toMillis(timeout));
232+
if (state == COMPLETED) {
233+
return value;
234+
} else if (state == FAILED) {
235+
throw new ExecutionException(exception);
236+
} else {
237+
throw new TimeoutException();
238+
}
239+
}
240+
}
241+
}
242+
}

promise/src/test/java/com/iluwatar/promise/AppTest.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.iluwatar.promise;
22

3+
import java.util.concurrent.ExecutionException;
4+
35
import org.junit.Test;
46

57
/**
@@ -9,7 +11,7 @@
911
public class AppTest {
1012

1113
@Test
12-
public void testApp() throws InterruptedException {
14+
public void testApp() throws InterruptedException, ExecutionException {
1315
App.main(null);
1416
}
1517
}

0 commit comments

Comments
 (0)