22
33import java .util .concurrent .Callable ;
44import 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 ;
59import java .util .function .Consumer ;
610import 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+ }
0 commit comments