1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package Torello.Browser;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import java.util.function.Function;
import javax.json.JsonObject;

/**
 * Primarily a wrapper around Java's {@code CompletableFuture} that provides two or three 
 * simplified methods for <B STYLE='color:red;'><I>awaiting</I></B> a promise.
 * 
 * <BR /><BR /><EMBED CLASS='external-html' DATA-FILE-ID=PROMISE>
 * @param <RESULT> <EMBED CLASS='external-html' DATA-FILE-ID=RESULT>
 */
public class Promise<RESULT> extends CompletableFuture<RESULT>
{
    /** The {@link Script} that generated this {@code Promise} */
    public final Script<RESULT> script;

    /**
     * This field will hold the json response which has been recived by the {@code WebSocket's} 
     * layer.  This field is assigned immediately preceeding this future's completion.  The json
     * stored here will only be the {@code "result"} property of the surrounding datagram json
     * envelope.
     * 
     * <BR /><BR />
     * Because CDP may or may not be a "moving target", this field is provided as a manner of
     * conenience to the developer's wanting to see how their response POJO's were built.  In 
     * normal operations, there is likely little use for inspecting the raw protocol json.
     * 
     * <BR /><BR /><DIV CLASS=JDHint>
     * This field is updated by the response handler's immediately prior to invoking the
     * {@code CompletableFuture.complete} method.  It is initialized to null.  There are also logs
     * which contain the json that has been sent and received over the WebSocket's layer, although 
     * they are less convenient to use.
     * </DIV>
     */
    public volatile JsonObject jo = null;

    Promise(final Script<RESULT> script)
    { this.script = script; }

    String stateToString()
    {
        return this.isCompletedExceptionally()
            ? "already completed with an error."
            : this.isCancelled()
                ? "already cancelled."
                : "already completed.";
    }


    /**
     * Nearly identical to the {@code CompletableFuture} method {@code join}.
     * 
     * <BR /><BR /><DIV CLASS=JDHint>
     * This method is nothing more than a wrapper around the {@code CompletableFuture} method known
     * as {@code join(long)}.  This does a little bit of Exception Unwrapping, as may be viewed in
     * the Hi-Lited Code, below.
     * </DIV>
     * 
     * @return the long-lost, long-awaited-for, result.
     */
    public RESULT await()
    {
        try 
            { return this.join(); }

        catch (CompletionException e)
        {
            Throwable c = e.getCause();
            if (c == null) throw e;
            if (c instanceof RDPException)          throw (RDPException) c;
            if (c instanceof BrowserException)      throw (BrowserException) c;
            if (c instanceof BrowserErrorException) throw (BrowserErrorException) c;
            if (c instanceof AsynchronousException) throw (AsynchronousException) c;
            throw e;
        }
    }

    /**
     * Waits if necessary for at most the given time for this future to complete, and then returns
     * its result, if available.
     * 
     * <BR /><BR /><DIV CLASS=JDHint>
     * This method is nothing more than a wrapper around the {@code CompletableFuture} method known
     * as {@code get(long, TimeUnit)}.  This method simply converts a slew of Checked-Exceptions 
     * into Unchecked / RuntimeExceptions.  No more, no less.
     * </DIV>
     * 
     * @param timeout The maximum time to wait
     * @param unit the time unit of the timeout parameter
     * @return the result value
     * @throws UncheckedExecutionException if this future completed exceptionally
     * @throws UncheckedInterruptedException if the current thread was interrupted while waiting
     * @throws UncheckedTimeoutException if the waiting timed out
     * @throws CompletionException if this future was cancelled
     * @throws RDPException 
     * @throws BrowserException
     * @throws BrowserErrorException
     * @throws AsynchronousException
     */
    public RESULT await(final long timeout, final TimeUnit unit)
    {
        try 
            { return this.get(timeout, unit); }

        catch (ExecutionException e)
            { throw new UncheckedExecutionException(e); }

        catch (InterruptedException e)
            { throw new UncheckedInterruptedException(e); }

        catch (TimeoutException e)
            { throw new UncheckedTimeoutException(e); }

        catch (CompletionException e)
        {
            Throwable c = e.getCause();
            if (c == null) throw e;
            if (c instanceof RDPException)          throw (RDPException) c;
            if (c instanceof BrowserException)      throw (BrowserException) c;
            if (c instanceof BrowserErrorException) throw (BrowserErrorException) c;
            if (c instanceof AsynchronousException) throw (AsynchronousException) c;
            throw e;
        }
    }
}