001package Torello.Browser;
002
003import java.util.concurrent.CompletableFuture;
004import java.util.concurrent.CompletionException;
005import java.util.concurrent.ExecutionException;
006import java.util.concurrent.TimeUnit;
007import java.util.concurrent.TimeoutException;
008
009import java.util.function.Function;
010import javax.json.JsonObject;
011
012/**
013 * Primarily a wrapper around Java's {@code CompletableFuture} that provides two or three 
014 * simplified methods for <B STYLE='color:red;'><I>awaiting</I></B> a promise.
015 * 
016 * <BR /><BR /><EMBED CLASS='external-html' DATA-FILE-ID=PROMISE>
017 * @param <RESULT> <EMBED CLASS='external-html' DATA-FILE-ID=RESULT>
018 */
019public class Promise<RESULT> extends CompletableFuture<RESULT>
020{
021    /** The {@link Script} that generated this {@code Promise} */
022    public final Script<RESULT> script;
023
024    /**
025     * This field will hold the json response which has been recived by the {@code WebSocket's} 
026     * layer.  This field is assigned immediately preceeding this future's completion.  The json
027     * stored here will only be the {@code "result"} property of the surrounding datagram json
028     * envelope.
029     * 
030     * <BR /><BR />
031     * Because CDP may or may not be a "moving target", this field is provided as a manner of
032     * conenience to the developer's wanting to see how their response POJO's were built.  In 
033     * normal operations, there is likely little use for inspecting the raw protocol json.
034     * 
035     * <BR /><BR /><DIV CLASS=JDHint>
036     * This field is updated by the response handler's immediately prior to invoking the
037     * {@code CompletableFuture.complete} method.  It is initialized to null.  There are also logs
038     * which contain the json that has been sent and received over the WebSocket's layer, although 
039     * they are less convenient to use.
040     * </DIV>
041     */
042    public volatile JsonObject jo = null;
043
044    Promise(final Script<RESULT> script)
045    { this.script = script; }
046
047    String stateToString()
048    {
049        return this.isCompletedExceptionally()
050            ? "already completed with an error."
051            : this.isCancelled()
052                ? "already cancelled."
053                : "already completed.";
054    }
055
056
057    /**
058     * Nearly identical to the {@code CompletableFuture} method {@code join}.
059     * 
060     * <BR /><BR /><DIV CLASS=JDHint>
061     * This method is nothing more than a wrapper around the {@code CompletableFuture} method known
062     * as {@code join(long)}.  This does a little bit of Exception Unwrapping, as may be viewed in
063     * the Hi-Lited Code, below.
064     * </DIV>
065     * 
066     * @return the long-lost, long-awaited-for, result.
067     */
068    public RESULT await()
069    {
070        try 
071            { return this.join(); }
072
073        catch (CompletionException e)
074        {
075            Throwable c = e.getCause();
076            if (c == null) throw e;
077            if (c instanceof RDPException)          throw (RDPException) c;
078            if (c instanceof BrowserException)      throw (BrowserException) c;
079            if (c instanceof BrowserErrorException) throw (BrowserErrorException) c;
080            if (c instanceof AsynchronousException) throw (AsynchronousException) c;
081            throw e;
082        }
083    }
084
085    /**
086     * Waits if necessary for at most the given time for this future to complete, and then returns
087     * its result, if available.
088     * 
089     * <BR /><BR /><DIV CLASS=JDHint>
090     * This method is nothing more than a wrapper around the {@code CompletableFuture} method known
091     * as {@code get(long, TimeUnit)}.  This method simply converts a slew of Checked-Exceptions 
092     * into Unchecked / RuntimeExceptions.  No more, no less.
093     * </DIV>
094     * 
095     * @param timeout The maximum time to wait
096     * @param unit the time unit of the timeout parameter
097     * @return the result value
098     * @throws UncheckedExecutionException if this future completed exceptionally
099     * @throws UncheckedInterruptedException if the current thread was interrupted while waiting
100     * @throws UncheckedTimeoutException if the waiting timed out
101     * @throws CompletionException if this future was cancelled
102     * @throws RDPException 
103     * @throws BrowserException
104     * @throws BrowserErrorException
105     * @throws AsynchronousException
106     */
107    public RESULT await(final long timeout, final TimeUnit unit)
108    {
109        try 
110            { return this.get(timeout, unit); }
111
112        catch (ExecutionException e)
113            { throw new UncheckedExecutionException(e); }
114
115        catch (InterruptedException e)
116            { throw new UncheckedInterruptedException(e); }
117
118        catch (TimeoutException e)
119            { throw new UncheckedTimeoutException(e); }
120
121        catch (CompletionException e)
122        {
123            Throwable c = e.getCause();
124            if (c == null) throw e;
125            if (c instanceof RDPException)          throw (RDPException) c;
126            if (c instanceof BrowserException)      throw (BrowserException) c;
127            if (c instanceof BrowserErrorException) throw (BrowserErrorException) c;
128            if (c instanceof AsynchronousException) throw (AsynchronousException) c;
129            throw e;
130        }
131    }
132}