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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package Torello.Java.Additional;

import java.util.function.Function;
import javax.json.JsonObject; // Needed for @link

/**
 * The class script simply sends an asynchronous request, and returns an instance of {@link Promise}
 * which may be awaited.
 * 
 * <BR /><BR /><EMBED CLASS='external-html' DATA-FILE-ID=SCRIPT>
 * 
 * @param <INPUT>       <EMBED CLASS='external-html' DATA-FILE-ID=INPUT>
 * @param <RESPONSE>    <EMBED CLASS='external-html' DATA-FILE-ID=RESPONSE>
 * @param <RESULT>      <EMBED CLASS='external-html' DATA-FILE-ID=RESULT>
 */
public class Script<INPUT, RESPONSE, RESULT> implements java.io.Serializable
{
    /** <EMBED CLASS='external-html' DATA-FILE-ID=SVUID> */
    protected static final long serialVersionUID = 1;

    /**
     * When opening a connection to an asynchronous channel, for instance a Web-Socket to a Google
     * Chrome Browser, the web-socket connecton to that browser receives messages from this sender.
     * 
     * <BR /><BR />If a user needs to have more than one browser opened at the same time, a second
     * sender can be created, and messages may be sent to a different browser by using the 
     * {@link #exec(Sender)} method.
     */
    public final Sender<INPUT> defaultSender;

    /**
     * ID number that was ascribed to this request.  Each request must have an ID because this
     * script is used in an asynchronous communications context.  The only way to link a response
     * to a request is by matching an ID number received as a response, to an ID that was sent with
     * a particular request.
     */
    public final int requestID;

    /**
     * This is the request that the {@code 'sender'} is going to send over the Asynchronous I/O
     * Channel.
     */
    public final INPUT request;

    /**
     * The {@link Promise} that is produced by this class, after invoking an {@code 'exec'} method
     * needs to be able to do any processing on the raw-output that is received from the
     * Asynchronous Communication's Channel.
     * 
     * <BR /><BR />If the type of the {@code 'RESPONSE'} was {@code JsonObject}, then this function
     * pointer would have to perform the <I><B STYLE='color: red;'>Json-Binding</B></I> to convert 
     * the {@link JsonObject} into a Java {@code Object} (ultimately having type {@code 'RESULT'}).
     */
    public final Function<RESPONSE, RESULT> receiver;

    /**
     * Constructs an instance of this class.  This constructor does not actually execute the
     * asychronous-request, but rather just initializes the fields.  The asynchronous call is not
     * made until the user calls an {@code 'exec'} method.  Furthermore, the user will not get a
     * response until he has awaited the {@link Promise} object that is returned from that 
     * {@code 'exec'} call.
     * 
     * @param defaultSender This parameter needs to be passed here to actually do the sending.
     * Perhaps it may be confusing that the sender is passed to this constructor.  The reason that
     * it is not automatically included is because it allows the user to change or modify the
     * sender used before actually executing this script.
     * 
     * @param requestID This is just an ID used to send the message.  When these asynchronous
     * classes are applied / used-by the {@code WebSocket's} package for communicating with a
     * headless browser, this ID is the Web-Socket request ID.  The Web-Socket ID allows the
     * Web-Socket client to match a response to a request.
     * 
     * @param request The request that is passed to the sender's {@code 'send'} method.
     * 
     * @param receiver This Function-Pointer needs to be able to convert the Asynchronous Channel's
     * {@code 'RESPONSE'}-typed object into type {@code 'RESULT'}, which is what the user is
     * ultimately expecting.
     * 
     * <BR /><BR /><B STYLE='color: red;'>NOTE:</B> Thus far in Java HTML Development, the Type
     * Parameter {@code 'RESPONSE'} has always been {@link JsonObject}.  This {@code 'receiver'}
     * parameter is actually expected to perform the <B STYLE='color: red;'>JSON-Binding</B> that
     * maps Json-Properties into 'Plain Old Java Objects' (POJO's).
     * 
     * <BR /><BR />It is conceivable that later uses of these {@code Promise / Script} Generic
     * Classes wouldn't necessarily operate over Web-Sockets, and wouldn't use Json as a
     * communication medium / protocol.
     */
    public Script(
            Sender<INPUT> defaultSender,
            int requestID,
            INPUT request,
            Function<RESPONSE, RESULT> receiver
        )
    {
        // Standard Java FAIL-FAST checks
        if (request == null) throw new NullPointerException("Parameter 'request' is null.");
        if (receiver == null) throw new NullPointerException("Parameter 'receiver' is null.");

        this.defaultSender  = defaultSender;
        this.requestID      = requestID;
        this.request        = request;
        this.receiver       = receiver;
    }

    /**
     * This builds a {@link Promise} object, and then uses the {@code 'defaultSender'} to send a
     * message to the Asynchronous I/O Channel.
     *
     * <BR /><BR />When using {@code Script's} that were generated by the Browser Remote Debug
     * Protocol API, the {@code 'defaultSender'} is merely a connection to the first
     * Chrome-Browser opened by the class {@code BDRPC} (which is a a browser connection class).
     * 
     * <BR /><BR /><B>ASIDE:</B> The instance of {@code Promise} must be created here, not 
     * because it would be "dangerous" to allow {@link Sender} to create it (even though it is a user
     * implemented functional-interface), but rather because as a Java-Generic, the only way to 
     * guarantee that the Generic-Type parameters are easy to deal with, is by creating the promise
     * here and returning the generic immediately to the user.
     * 
     * @return <EMBED CLASS='external-html' DATA-FILE-ID=SCRIPTRET>
     */
    public Promise<RESPONSE, RESULT> exec()
    {
        Promise<RESPONSE, RESULT> promise = new Promise<>(receiver);
        defaultSender.send(requestID, request, promise);
        return promise;
    }

    /**
     * If there are reasons to use a different sender - <I>because certain configurations need to
     * change</I> - then using this {@code exec} method allows a user to change senders.
     * 
     * @param alternateUserProvidedSender A different sender when executing a {@code Script}
     * object-instance.  This would allow a user to ask that a pre-compiled script use an alternate
     * Aynchronous I/O Channel for sending his compiled-request to whatever server is receiving
     * these communications.
     * 
     * <BR /><BR />In the Browser Remote Debug Package (Headless Chrome), if multiple headless
     * browses instances were started and opened, using this variant of {@code 'exec'} would allow
     * a programmer to decide which requests / compiled-scripts were sent to which opened-browser.
     * 
     * @return <EMBED CLASS='external-html' DATA-FILE-ID=SCRIPTRET>
     */
    public Promise<RESPONSE, RESULT> exec(Sender<INPUT> alternateUserProvidedSender)
    {
        Promise<RESPONSE, RESULT> promise = new Promise<>(receiver);
        alternateUserProvidedSender.send(requestID, request, promise);
        return promise;
    }
}