1.1 --- a/json-tck/src/main/java/net/java/html/json/tests/WebSocketTest.java Fri Aug 23 09:42:19 2013 +0200
1.2 +++ b/json-tck/src/main/java/net/java/html/json/tests/WebSocketTest.java Fri Aug 23 22:23:30 2013 +0200
1.3 @@ -34,6 +34,7 @@
1.4 @Model(className = "WebSocketik", properties = {
1.5 @Property(name = "fetched", type = Person.class),
1.6 @Property(name = "fetchedCount", type = int.class),
1.7 + @Property(name = "open", type = int.class),
1.8 @Property(name = "fetchedResponse", type = String.class),
1.9 @Property(name = "fetchedSex", type = Sex.class, array = true)
1.10 })
1.11 @@ -43,7 +44,11 @@
1.12
1.13 @OnReceive(url = "{url}", data = Sex.class, method = "WebSocket", onError = "error")
1.14 static void querySex(WebSocketik model, Person data) {
1.15 - model.setFetched(data);
1.16 + if (data == null) {
1.17 + model.setOpen(1);
1.18 + } else {
1.19 + model.setFetched(data);
1.20 + }
1.21 }
1.22
1.23 @KOTest public void connectUsingWebSocket() throws Throwable {
1.24 @@ -58,12 +63,23 @@
1.25 js.applyBindings();
1.26
1.27 js.setFetched(null);
1.28 - js.querySex(url, Sex.FEMALE);
1.29 +
1.30 + // connects to the server
1.31 + js.querySex(url, null);
1.32 }
1.33
1.34 if (bailOutIfNotSupported(js)) {
1.35 return;
1.36 }
1.37 +
1.38 + if (js.getOpen() == 0) {
1.39 + throw new InterruptedException();
1.40 + }
1.41 + if (js.getOpen() == 1) {
1.42 + // send a query to the server
1.43 + js.querySex(url, Sex.FEMALE);
1.44 + js.setOpen(2);
1.45 + }
1.46
1.47 Person p = js.getFetched();
1.48 if (p == null) {
1.49 @@ -72,6 +88,17 @@
1.50
1.51 assert "Mitar".equals(p.getFirstName()) : "Unexpected: " + p.getFirstName();
1.52 assert Sex.FEMALE.equals(p.getSex()) : "Expecting FEMALE: " + p.getSex();
1.53 +
1.54 + if (js.getOpen() == 2) {
1.55 + // close the socket
1.56 + js.querySex(url, null);
1.57 + js.setOpen(3);
1.58 + }
1.59 +
1.60 + if (js.getFetchedResponse() == null) {
1.61 + throw new InterruptedException();
1.62 + }
1.63 + assert "null".equals(js.getFetchedResponse()) : "Should be null: " + js.getFetchedResponse();
1.64 }
1.65
1.66 @KOTest public void errorUsingWebSocket() throws Throwable {
1.67 @@ -85,7 +112,11 @@
1.68 }
1.69
1.70 static void error(WebSocketik model, Exception ex) {
1.71 - model.setFetchedResponse(ex.getClass() + ":" + ex.getMessage());
1.72 + if (ex != null) {
1.73 + model.setFetchedResponse(ex.getClass() + ":" + ex.getMessage());
1.74 + } else {
1.75 + model.setFetchedResponse("null");
1.76 + }
1.77 }
1.78
1.79 private static BrwsrCtx newContext() {
2.1 --- a/json/src/main/java/org/apidesign/html/json/impl/JSON.java Fri Aug 23 09:42:19 2013 +0200
2.2 +++ b/json/src/main/java/org/apidesign/html/json/impl/JSON.java Fri Aug 23 22:23:30 2013 +0200
2.3 @@ -37,6 +37,9 @@
2.4 * @author Jaroslav Tulach <jtulach@netbeans.org>
2.5 */
2.6 public final class JSON {
2.7 + /** represents null exception value */
2.8 + public static final Exception NULL = new NullPointerException();
2.9 +
2.10 private JSON() {
2.11 }
2.12
2.13 @@ -132,6 +135,16 @@
2.14 public static Character charValue(Object val) {
2.15 return (Character)val;
2.16 }
2.17 +
2.18 + public static Exception excValue(Object val) {
2.19 + if (val == NULL) {
2.20 + return null;
2.21 + }
2.22 + if (val instanceof Exception) {
2.23 + return (Exception)val;
2.24 + }
2.25 + return new Exception(val.toString());
2.26 + }
2.27
2.28 public static Boolean boolValue(Object val) {
2.29 if (val instanceof String) {
2.30 @@ -196,6 +209,9 @@
2.31 return read(c, modelClazz, tr.toJSON((InputStream)data));
2.32 }
2.33 public static <T> T read(BrwsrCtx c, Class<T> modelClazz, Object data) {
2.34 + if (data == null) {
2.35 + return null;
2.36 + }
2.37 if (modelClazz == String.class) {
2.38 return modelClazz.cast(data.toString());
2.39 }
3.1 --- a/json/src/main/java/org/apidesign/html/json/impl/ModelProcessor.java Fri Aug 23 09:42:19 2013 +0200
3.2 +++ b/json/src/main/java/org/apidesign/html/json/impl/ModelProcessor.java Fri Aug 23 22:23:30 2013 +0200
3.3 @@ -952,6 +952,12 @@
3.4 error("The method needs to have one @Model class as parameter", e);
3.5 }
3.6 String n = e.getSimpleName().toString();
3.7 + if ("WebSocket".equals(onR.method())) {
3.8 + body.append(" /** Performs WebSocket communication. Call with <code>null</code> data parameter\n");
3.9 + body.append(" * to open the connection (even if not required). Call with non-null data to\n");
3.10 + body.append(" * send messages to server. Call again with <code>null</code> data to close the socket.\n");
3.11 + body.append(" */\n");
3.12 + }
3.13 body.append(" public void ").append(n).append("(");
3.14 StringBuilder urlBefore = new StringBuilder();
3.15 StringBuilder urlAfter = new StringBuilder();
3.16 @@ -996,7 +1002,7 @@
3.17 return false;
3.18 }
3.19 body.append(" ").append(clazz.getSimpleName()).append(".").append(onR.onError()).append("(");
3.20 - body.append(className).append(".this, (Exception)value); return;\n");
3.21 + body.append(className).append(".this, org.apidesign.html.json.impl.JSON.excValue(value)); return;\n");
3.22 }
3.23 body.append(
3.24 " }\n"
4.1 --- a/json/src/main/java/org/apidesign/html/json/spi/JSONCall.java Fri Aug 23 09:42:19 2013 +0200
4.2 +++ b/json/src/main/java/org/apidesign/html/json/spi/JSONCall.java Fri Aug 23 22:23:30 2013 +0200
4.3 @@ -23,6 +23,7 @@
4.4
4.5 import java.io.IOException;
4.6 import java.io.OutputStream;
4.7 +import org.apidesign.html.json.impl.JSON;
4.8
4.9 /** Description of a JSON call request that is supposed to be processed
4.10 * by {@link Transfer#loadJSON(org.apidesign.html.json.spi.JSONCall)} implementors.
4.11 @@ -88,7 +89,7 @@
4.12 }
4.13
4.14 public void notifyError(Throwable error) {
4.15 - this.result[0] = error;
4.16 + this.result[0] = error == null ? JSON.NULL : error;
4.17 this.whenDone.run();
4.18 }
4.19 }
5.1 --- a/ko-fx/src/main/java/org/apidesign/html/kofx/LoadWS.java Fri Aug 23 09:42:19 2013 +0200
5.2 +++ b/ko-fx/src/main/java/org/apidesign/html/kofx/LoadWS.java Fri Aug 23 22:23:30 2013 +0200
5.3 @@ -59,8 +59,14 @@
5.4 call.notifyError(new UnsupportedOperationException("WebSocket API is not supported"));
5.5 return;
5.6 }
5.7 + } else {
5.8 + if (!call.isDoOutput()) {
5.9 + close(load.ws);
5.10 + }
5.11 }
5.12 - load.push(call);
5.13 + if (call.isDoOutput()) {
5.14 + load.push(call);
5.15 + }
5.16 }
5.17
5.18 private synchronized void push(JSONCall call) {
5.19 @@ -78,9 +84,15 @@
5.20 }
5.21 }
5.22
5.23 - synchronized void onOpen(Object ev) {
5.24 - Deque<JSONCall> p = pending;
5.25 - pending = null;
5.26 + void onOpen(Object ev) {
5.27 + Deque<JSONCall> p;
5.28 + synchronized (this) {
5.29 + p = pending;
5.30 + pending = null;
5.31 + }
5.32 + if (!call.isDoOutput()) {
5.33 + call.notifySuccess(null);
5.34 + }
5.35 for (JSONCall c : p) {
5.36 push(c);
5.37 }
5.38 @@ -105,13 +117,18 @@
5.39 call.notifyError(new Exception(ev.toString()));
5.40 }
5.41
5.42 + void onClose(boolean wasClean, int code, String reason) {
5.43 + call.notifyError(null);
5.44 + }
5.45 +
5.46 @JavaScriptBody(args = { "back", "url" }, javacall = true, body = ""
5.47 + "if (window.WebSocket) {"
5.48 + " try {"
5.49 + " var ws = new window.WebSocket(url);"
5.50 + " ws.onopen = function(ev) { back.@org.apidesign.html.kofx.LoadWS::onOpen(Ljava/lang/Object;)(ev); };"
5.51 + " ws.onmessage = function(ev) { back.@org.apidesign.html.kofx.LoadWS::onMessage(Ljava/lang/Object;Ljava/lang/String;)(ev, ev.data); };"
5.52 - + " ws.onclose = function(ev) { back.@org.apidesign.html.kofx.LoadWS::onError(Ljava/lang/Object;)(ev); };"
5.53 + + " ws.onerror = function(ev) { back.@org.apidesign.html.kofx.LoadWS::onError(Ljava/lang/Object;)(ev); };"
5.54 + + " ws.onclose = function(ev) { back.@org.apidesign.html.kofx.LoadWS::onClose(ZILjava/lang/String;)(ev.wasClean, ev.code, ev.reason); };"
5.55 + " return ws;"
5.56 + " } catch (ex) {"
5.57 + " return null;"
5.58 @@ -130,4 +147,8 @@
5.59 )
5.60 private void send(Object ws, String msg) {
5.61 }
5.62 +
5.63 + @JavaScriptBody(args = { "ws" }, body = "ws.close();")
5.64 + private static void close(Object ws) {
5.65 + }
5.66 }