001package Torello.Java.Additional; 002 003import java.util.function.Function; 004import javax.json.JsonObject; // Needed for @link 005 006/** 007 * The class script simply sends an asynchronous request, and returns an instance of {@link Promise} 008 * which may be awaited. 009 * 010 * <BR /><BR /><EMBED CLASS='external-html' DATA-FILE-ID=SCRIPT> 011 * 012 * @param <INPUT> <EMBED CLASS='external-html' DATA-FILE-ID=INPUT> 013 * @param <RESPONSE> <EMBED CLASS='external-html' DATA-FILE-ID=RESPONSE> 014 * @param <RESULT> <EMBED CLASS='external-html' DATA-FILE-ID=RESULT> 015 */ 016public class Script<INPUT, RESPONSE, RESULT> implements java.io.Serializable 017{ 018 /** <EMBED CLASS='external-html' DATA-FILE-ID=SVUID> */ 019 protected static final long serialVersionUID = 1; 020 021 /** 022 * When opening a connection to an asynchronous channel, for instance a Web-Socket to a Google 023 * Chrome Browser, the web-socket connecton to that browser receives messages from this sender. 024 * 025 * <BR /><BR />If a user needs to have more than one browser opened at the same time, a second 026 * sender can be created, and messages may be sent to a different browser by using the 027 * {@link #exec(Sender)} method. 028 */ 029 public final Sender<INPUT> defaultSender; 030 031 /** 032 * ID number that was ascribed to this request. Each request must have an ID because this 033 * script is used in an asynchronous communications context. The only way to link a response 034 * to a request is by matching an ID number received as a response, to an ID that was sent with 035 * a particular request. 036 */ 037 public final int requestID; 038 039 /** 040 * This is the request that the {@code 'sender'} is going to send over the Asynchronous I/O 041 * Channel. 042 */ 043 public final INPUT request; 044 045 /** 046 * The {@link Promise} that is produced by this class, after invoking an {@code 'exec'} method 047 * needs to be able to do any processing on the raw-output that is received from the 048 * Asynchronous Communication's Channel. 049 * 050 * <BR /><BR />If the type of the {@code 'RESPONSE'} was {@code JsonObject}, then this function 051 * pointer would have to perform the <I><B STYLE='color: red;'>Json-Binding</B></I> to convert 052 * the {@link JsonObject} into a Java {@code Object} (ultimately having type {@code 'RESULT'}). 053 */ 054 public final Function<RESPONSE, RESULT> receiver; 055 056 /** 057 * Constructs an instance of this class. This constructor does not actually execute the 058 * asychronous-request, but rather just initializes the fields. The asynchronous call is not 059 * made until the user calls an {@code 'exec'} method. Furthermore, the user will not get a 060 * response until he has awaited the {@link Promise} object that is returned from that 061 * {@code 'exec'} call. 062 * 063 * @param defaultSender This parameter needs to be passed here to actually do the sending. 064 * Perhaps it may be confusing that the sender is passed to this constructor. The reason that 065 * it is not automatically included is because it allows the user to change or modify the 066 * sender used before actually executing this script. 067 * 068 * @param requestID This is just an ID used to send the message. When these asynchronous 069 * classes are applied / used-by the {@code WebSocket's} package for communicating with a 070 * headless browser, this ID is the Web-Socket request ID. The Web-Socket ID allows the 071 * Web-Socket client to match a response to a request. 072 * 073 * @param request The request that is passed to the sender's {@code 'send'} method. 074 * 075 * @param receiver This Function-Pointer needs to be able to convert the Asynchronous Channel's 076 * {@code 'RESPONSE'}-typed object into type {@code 'RESULT'}, which is what the user is 077 * ultimately expecting. 078 * 079 * <BR /><BR /><B STYLE='color: red;'>NOTE:</B> Thus far in Java HTML Development, the Type 080 * Parameter {@code 'RESPONSE'} has always been {@link JsonObject}. This {@code 'receiver'} 081 * parameter is actually expected to perform the <B STYLE='color: red;'>JSON-Binding</B> that 082 * maps Json-Properties into 'Plain Old Java Objects' (POJO's). 083 * 084 * <BR /><BR />It is conceivable that later uses of these {@code Promise / Script} Generic 085 * Classes wouldn't necessarily operate over Web-Sockets, and wouldn't use Json as a 086 * communication medium / protocol. 087 */ 088 public Script( 089 Sender<INPUT> defaultSender, 090 int requestID, 091 INPUT request, 092 Function<RESPONSE, RESULT> receiver 093 ) 094 { 095 // Standard Java FAIL-FAST checks 096 if (request == null) throw new NullPointerException("Parameter 'request' is null."); 097 if (receiver == null) throw new NullPointerException("Parameter 'receiver' is null."); 098 099 this.defaultSender = defaultSender; 100 this.requestID = requestID; 101 this.request = request; 102 this.receiver = receiver; 103 } 104 105 /** 106 * This builds a {@link Promise} object, and then uses the {@code 'defaultSender'} to send a 107 * message to the Asynchronous I/O Channel. 108 * 109 * <BR /><BR />When using {@code Script's} that were generated by the Browser Remote Debug 110 * Protocol API, the {@code 'defaultSender'} is merely a connection to the first 111 * Chrome-Browser opened by the class {@code BDRPC} (which is a a browser connection class). 112 * 113 * <BR /><BR /><B>ASIDE:</B> The instance of {@code Promise} must be created here, not 114 * because it would be "dangerous" to allow {@link Sender} to create it (even though it is a user 115 * implemented functional-interface), but rather because as a Java-Generic, the only way to 116 * guarantee that the Generic-Type parameters are easy to deal with, is by creating the promise 117 * here and returning the generic immediately to the user. 118 * 119 * @return <EMBED CLASS='external-html' DATA-FILE-ID=SCRIPTRET> 120 */ 121 public Promise<RESPONSE, RESULT> exec() 122 { 123 Promise<RESPONSE, RESULT> promise = new Promise<>(receiver); 124 defaultSender.send(requestID, request, promise); 125 return promise; 126 } 127 128 /** 129 * If there are reasons to use a different sender - <I>because certain configurations need to 130 * change</I> - then using this {@code exec} method allows a user to change senders. 131 * 132 * @param alternateUserProvidedSender A different sender when executing a {@code Script} 133 * object-instance. This would allow a user to ask that a pre-compiled script use an alternate 134 * Aynchronous I/O Channel for sending his compiled-request to whatever server is receiving 135 * these communications. 136 * 137 * <BR /><BR />In the Browser Remote Debug Package (Headless Chrome), if multiple headless 138 * browses instances were started and opened, using this variant of {@code 'exec'} would allow 139 * a programmer to decide which requests / compiled-scripts were sent to which opened-browser. 140 * 141 * @return <EMBED CLASS='external-html' DATA-FILE-ID=SCRIPTRET> 142 */ 143 public Promise<RESPONSE, RESULT> exec(Sender<INPUT> alternateUserProvidedSender) 144 { 145 Promise<RESPONSE, RESULT> promise = new Promise<>(receiver); 146 alternateUserProvidedSender.send(requestID, request, promise); 147 return promise; 148 } 149}