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; } } |