aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGert Scholten <gscholt@gmail.com>2009-11-25 11:22:01 +0100
committerGert Scholten <gscholt@gmail.com>2009-11-25 11:22:01 +0100
commit96d025bfaa31daeb2192ba41d9f7ce759201f864 (patch)
tree068b2d7add26f75a868b9b89a3063bbddb2e4d31
parent18edfa13362d089fb6c80f501ae3b2c7b94df323 (diff)
downloadgwtjsonrpc-96d025bfaa31daeb2192ba41d9f7ce759201f864.tar.gz
Add support for JSON-RPC version 2.0 over HTTP POST and GET for the client.
Change-Id: I0047ea8bb18cb5840f56824d08480d37fca77e22 Signed-off-by: Gert Scholten <gscholt@gmail.com>
-rw-r--r--src/main/java/com/google/gwtjsonrpc/client/AbstractJsonProxy.java6
-rw-r--r--src/main/java/com/google/gwtjsonrpc/client/JsonCall.java142
-rw-r--r--src/main/java/com/google/gwtjsonrpc/client/JsonCall11HttpPost.java158
-rw-r--r--src/main/java/com/google/gwtjsonrpc/client/JsonCall20.java137
-rw-r--r--src/main/java/com/google/gwtjsonrpc/client/JsonCall20HttpGet.java74
-rw-r--r--src/main/java/com/google/gwtjsonrpc/client/JsonCall20HttpPost.java54
-rw-r--r--src/main/java/com/google/gwtjsonrpc/client/JsonUtil.java11
-rw-r--r--src/main/java/com/google/gwtjsonrpc/client/RemoteJsonException.java49
-rw-r--r--src/main/java/com/google/gwtjsonrpc/client/RpcImpl.java67
-rw-r--r--src/main/java/com/google/gwtjsonrpc/rebind/ProxyCreator.java59
10 files changed, 607 insertions, 150 deletions
diff --git a/src/main/java/com/google/gwtjsonrpc/client/AbstractJsonProxy.java b/src/main/java/com/google/gwtjsonrpc/client/AbstractJsonProxy.java
index 5e80a62..f92216a 100644
--- a/src/main/java/com/google/gwtjsonrpc/client/AbstractJsonProxy.java
+++ b/src/main/java/com/google/gwtjsonrpc/client/AbstractJsonProxy.java
@@ -55,9 +55,13 @@ public abstract class AbstractJsonProxy implements JsonDefTarget {
if (url == null) {
throw new NoServiceEntryPointSpecifiedException();
}
- new JsonCall<T>(this, methodName, reqData, ser, cb).send();
+ newJsonCall(this, methodName, reqData, ser, cb).send();
}
+ protected abstract <T> JsonCall<T> newJsonCall(AbstractJsonProxy proxy,
+ final String methodName, final String reqData,
+ final ResultDeserializer<T> ser, final AsyncCallback<T> cb);
+
protected static native JavaScriptObject hostPageCacheGetOnce(String name)
/*-{ var r = $wnd[name];$wnd[name] = null;return r ? {result: r} : null; }-*/;
diff --git a/src/main/java/com/google/gwtjsonrpc/client/JsonCall.java b/src/main/java/com/google/gwtjsonrpc/client/JsonCall.java
index 4927fa5..fb4feed 100644
--- a/src/main/java/com/google/gwtjsonrpc/client/JsonCall.java
+++ b/src/main/java/com/google/gwtjsonrpc/client/JsonCall.java
@@ -20,14 +20,10 @@ import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
-import com.google.gwt.http.client.Response;
-import com.google.gwt.json.client.JSONObject;
import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.rpc.InvocationException;
-import com.google.gwt.user.client.rpc.StatusCodeException;
-class JsonCall<T> implements RequestCallback {
- private static final JavaScriptObject jsonParser;
+abstract class JsonCall<T> implements RequestCallback {
+ protected static final JavaScriptObject jsonParser;
static {
jsonParser = selectJsonParser();
@@ -61,12 +57,12 @@ class JsonCall<T> implements RequestCallback {
return function(expr) { return eval('(' + expr + ')'); };
}-*/;
- private final AbstractJsonProxy proxy;
- private final String methodName;
- private final String requestParams;
- private final ResultDeserializer<T> resultDeserializer;
- private final AsyncCallback<T> callback;
- private int attempts;
+ protected final AbstractJsonProxy proxy;
+ protected final String methodName;
+ protected final String requestParams;
+ protected final ResultDeserializer<T> resultDeserializer;
+ protected final AsyncCallback<T> callback;
+ protected int attempts;
JsonCall(final AbstractJsonProxy abstractJsonProxy, final String methodName,
final String requestParams,
@@ -87,27 +83,9 @@ class JsonCall<T> implements RequestCallback {
return methodName;
}
- void send() {
- final StringBuilder body = new StringBuilder();
- body.append("{\"version\":\"1.1\",\"method\":\"");
- body.append(methodName);
- body.append("\",\"params\":[");
- body.append(requestParams);
- body.append("]");
- final String xsrfKey = proxy.getXsrfManager().getToken(proxy);
- if (xsrfKey != null) {
- body.append(",\"xsrfKey\":");
- body.append(JsonSerializer.escapeString(xsrfKey));
- }
- body.append("}");
-
- final RequestBuilder rb;
- rb = new RequestBuilder(RequestBuilder.POST, proxy.url);
- rb.setHeader("Content-Type", JsonUtil.JSON_REQ_CT);
- rb.setHeader("Accept", JsonUtil.JSON_TYPE);
- rb.setCallback(this);
- rb.setRequestData(body.toString());
+ abstract void send();
+ protected void send(RequestBuilder rb) {
try {
attempts++;
rb.send();
@@ -121,59 +99,6 @@ class JsonCall<T> implements RequestCallback {
}
}
- public void onResponseReceived(final Request req, final Response rsp) {
- final int sc = rsp.getStatusCode();
- if (isJsonBody(rsp)) {
- final RpcResult r;
- try {
- r = parse(jsonParser, rsp.getText());
- } catch (RuntimeException e) {
- fireEvent(RpcCompleteEvent.e);
- callback.onFailure(new InvocationException("Bad JSON response: " + e));
- return;
- }
-
- if (r.xsrfKey() != null) {
- proxy.getXsrfManager().setToken(proxy, r.xsrfKey());
- }
-
- if (r.error() != null) {
- final String errmsg = r.error().message();
- if (JsonUtil.ERROR_INVALID_XSRF.equals(errmsg)) {
- if (attempts < 2) {
- // The XSRF cookie was invalidated (or didn't exist) and the
- // service demands we have one in place to make calls to it.
- // A new token was returned to us, so start the request over.
- //
- send();
- } else {
- fireEvent(RpcCompleteEvent.e);
- callback.onFailure(new InvocationException(errmsg));
- }
- } else {
- fireEvent(RpcCompleteEvent.e);
- callback.onFailure(new RemoteJsonException(errmsg, r.error().code(),
- new JSONObject(r.error()).get("error")));
- }
- return;
- }
-
- if (sc == Response.SC_OK) {
- fireEvent(RpcCompleteEvent.e);
- JsonUtil.invoke(resultDeserializer, callback, r);
- return;
- }
- }
-
- if (sc == Response.SC_OK) {
- fireEvent(RpcCompleteEvent.e);
- callback.onFailure(new InvocationException("No JSON response"));
- } else {
- fireEvent(RpcCompleteEvent.e);
- callback.onFailure(new StatusCodeException(sc, rsp.getStatusText()));
- }
- }
-
public void onError(final Request request, final Throwable exception) {
fireEvent(RpcCompleteEvent.e);
if (exception.getClass() == RuntimeException.class
@@ -188,53 +113,8 @@ class JsonCall<T> implements RequestCallback {
}
}
- private <S extends EventHandler> void fireEvent(BaseRpcEvent<S> e) {
+ protected <S extends EventHandler> void fireEvent(BaseRpcEvent<S> e) {
e.call = this;
JsonUtil.fireEvent(e);
}
-
- private static boolean isJsonBody(final Response rsp) {
- String type = rsp.getHeader("Content-Type");
- if (type == null) {
- return false;
- }
- int semi = type.indexOf(';');
- if (semi >= 0) {
- type = type.substring(0, semi).trim();
- }
- return JsonUtil.JSON_TYPE.equals(type);
- }
-
- /**
- * Call a JSON parser javascript function to parse an encoded JSON string.
- *
- * @param parser a javascript function
- * @param json encoded JSON text
- * @return the parsed data
- * @see #jsonParser
- */
- private static final native RpcResult parse(JavaScriptObject parserFunction,
- String json)
- /*-{
- return parserFunction(json);
- }-*/;
-
-
- private static class RpcResult extends JavaScriptObject {
- protected RpcResult() {
- }
-
- final native RpcError error()/*-{ return this.error; }-*/;
-
- final native String xsrfKey()/*-{ return this.xsrfKey; }-*/;
- }
-
- private static class RpcError extends JavaScriptObject {
- protected RpcError() {
- }
-
- final native String message()/*-{ return this.message; }-*/;
-
- final native int code()/*-{ return this.code; }-*/;
- }
}
diff --git a/src/main/java/com/google/gwtjsonrpc/client/JsonCall11HttpPost.java b/src/main/java/com/google/gwtjsonrpc/client/JsonCall11HttpPost.java
new file mode 100644
index 0000000..126d007
--- /dev/null
+++ b/src/main/java/com/google/gwtjsonrpc/client/JsonCall11HttpPost.java
@@ -0,0 +1,158 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtjsonrpc.client;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.http.client.Request;
+import com.google.gwt.http.client.RequestBuilder;
+import com.google.gwt.http.client.Response;
+import com.google.gwt.json.client.JSONObject;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.rpc.InvocationException;
+import com.google.gwt.user.client.rpc.StatusCodeException;
+
+/** JsonCall implementation for JsonRPC version 1.1 over HTTP POST */
+public class JsonCall11HttpPost<T> extends JsonCall<T> {
+
+ public JsonCall11HttpPost(AbstractJsonProxy abstractJsonProxy,
+ String methodName, String requestParams,
+ ResultDeserializer<T> resultDeserializer, AsyncCallback<T> callback) {
+ super(abstractJsonProxy, methodName, requestParams, resultDeserializer,
+ callback);
+ }
+
+ @Override
+ void send() {
+ final StringBuilder body = new StringBuilder();
+ body.append("{\"version\":\"1.1\",\"method\":\"");
+ body.append(methodName);
+ body.append("\",\"params\":");
+ body.append(requestParams);
+ final String xsrfKey = proxy.getXsrfManager().getToken(proxy);
+ if (xsrfKey != null) {
+ body.append(",\"xsrfKey\":");
+ body.append(JsonSerializer.escapeString(xsrfKey));
+ }
+ body.append("}");
+
+ final RequestBuilder rb;
+ rb = new RequestBuilder(RequestBuilder.POST, proxy.url);
+ rb.setHeader("Content-Type", JsonUtil.JSON_REQ_CT);
+ rb.setHeader("Accept", JsonUtil.JSON_TYPE);
+ rb.setCallback(this);
+ rb.setRequestData(body.toString());
+
+ send(rb);
+ }
+
+ @Override
+ public void onResponseReceived(final Request req, final Response rsp) {
+ final int sc = rsp.getStatusCode();
+ if (isJsonBody(rsp)) {
+ final RpcResult r;
+ try {
+ r = parse(jsonParser, rsp.getText());
+ } catch (RuntimeException e) {
+ fireEvent(RpcCompleteEvent.e);
+ callback.onFailure(new InvocationException("Bad JSON response: " + e));
+ return;
+ }
+
+ if (r.xsrfKey() != null) {
+ proxy.getXsrfManager().setToken(proxy, r.xsrfKey());
+ }
+
+ if (r.error() != null) {
+ final String errmsg = r.error().message();
+ if (JsonUtil.ERROR_INVALID_XSRF.equals(errmsg)) {
+ if (attempts < 2) {
+ // The XSRF cookie was invalidated (or didn't exist) and the
+ // service demands we have one in place to make calls to it.
+ // A new token was returned to us, so start the request over.
+ //
+ send();
+ } else {
+ fireEvent(RpcCompleteEvent.e);
+ callback.onFailure(new InvocationException(errmsg));
+ }
+ } else {
+ fireEvent(RpcCompleteEvent.e);
+ callback.onFailure(new RemoteJsonException(errmsg, r.error().code(),
+ new JSONObject(r.error()).get("error")));
+ }
+ return;
+ }
+
+ if (sc == Response.SC_OK) {
+ fireEvent(RpcCompleteEvent.e);
+ JsonUtil.invoke(resultDeserializer, callback, r);
+ return;
+ }
+ }
+
+ if (sc == Response.SC_OK) {
+ fireEvent(RpcCompleteEvent.e);
+ callback.onFailure(new InvocationException("No JSON response"));
+ } else {
+ fireEvent(RpcCompleteEvent.e);
+ callback.onFailure(new StatusCodeException(sc, rsp.getStatusText()));
+ }
+ }
+
+ private static boolean isJsonBody(final Response rsp) {
+ String type = rsp.getHeader("Content-Type");
+ if (type == null) {
+ return false;
+ }
+ int semi = type.indexOf(';');
+ if (semi >= 0) {
+ type = type.substring(0, semi).trim();
+ }
+ return JsonUtil.JSON_TYPE.equals(type);
+ }
+
+ /**
+ * Call a JSON parser javascript function to parse an encoded JSON string.
+ *
+ * @param parser a javascript function
+ * @param json encoded JSON text
+ * @return the parsed data
+ * @see #jsonParser
+ */
+ private static final native RpcResult parse(JavaScriptObject parserFunction,
+ String json)
+ /*-{
+ return parserFunction(json);
+ }-*/;
+
+
+ private static class RpcResult extends JavaScriptObject {
+ protected RpcResult() {
+ }
+
+ final native RpcError error()/*-{ return this.error; }-*/;
+
+ final native String xsrfKey()/*-{ return this.xsrfKey; }-*/;
+ }
+
+ private static class RpcError extends JavaScriptObject {
+ protected RpcError() {
+ }
+
+ final native String message()/*-{ return this.message; }-*/;
+
+ final native int code()/*-{ return this.code; }-*/;
+ }
+}
diff --git a/src/main/java/com/google/gwtjsonrpc/client/JsonCall20.java b/src/main/java/com/google/gwtjsonrpc/client/JsonCall20.java
new file mode 100644
index 0000000..27028de
--- /dev/null
+++ b/src/main/java/com/google/gwtjsonrpc/client/JsonCall20.java
@@ -0,0 +1,137 @@
+// Copyright 2009 Gert Scholten
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtjsonrpc.client;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.http.client.Request;
+import com.google.gwt.http.client.Response;
+import com.google.gwt.json.client.JSONObject;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.rpc.InvocationException;
+import com.google.gwt.user.client.rpc.StatusCodeException;
+
+/** Base JsonCall implementation for JsonRPC version 2.0 */
+abstract class JsonCall20<T> extends JsonCall<T> {
+ protected static int lastRequestId = 0;
+
+ protected int requestId;
+
+ JsonCall20(AbstractJsonProxy abstractJsonProxy, String methodName,
+ String requestParams, ResultDeserializer<T> resultDeserializer,
+ AsyncCallback<T> callback) {
+ super(abstractJsonProxy, methodName, requestParams, resultDeserializer,
+ callback);
+ }
+
+ @Override
+ public void onResponseReceived(final Request req, final Response rsp) {
+ final int sc = rsp.getStatusCode();
+ if (isJsonBody(rsp)) {
+ final RpcResult r;
+ try {
+ r = parse(jsonParser, rsp.getText());
+ } catch (RuntimeException e) {
+ fireEvent(RpcCompleteEvent.e);
+ callback.onFailure(new InvocationException("Bad JSON response: " + e));
+ return;
+ }
+
+ if (r.xsrfKey() != null) {
+ proxy.getXsrfManager().setToken(proxy, r.xsrfKey());
+ }
+
+ if (r.error() != null) {
+ // TODO: define status code for the invalid XSRF msg for 2.0 (-32099 ?)
+ final String errmsg = r.error().message();
+ if (JsonUtil.ERROR_INVALID_XSRF.equals(errmsg)) {
+ if (attempts < 2) {
+ // The XSRF cookie was invalidated (or didn't exist) and the
+ // service demands we have one in place to make calls to it.
+ // A new token was returned to us, so start the request over.
+ //
+ send();
+ } else {
+ fireEvent(RpcCompleteEvent.e);
+ callback.onFailure(new InvocationException(errmsg));
+ }
+ } else {
+ fireEvent(RpcCompleteEvent.e);
+ callback.onFailure(new RemoteJsonException(errmsg, r.error().code(),
+ new JSONObject(r.error()).get("data")));
+ }
+ return;
+ }
+
+ if (sc == Response.SC_OK) {
+ fireEvent(RpcCompleteEvent.e);
+ JsonUtil.invoke(resultDeserializer, callback, r);
+ return;
+ }
+ }
+
+ if (sc == Response.SC_OK) {
+ fireEvent(RpcCompleteEvent.e);
+ callback.onFailure(new InvocationException("No JSON response"));
+ } else {
+ fireEvent(RpcCompleteEvent.e);
+ callback.onFailure(new StatusCodeException(sc, rsp.getStatusText()));
+ }
+ }
+
+ protected static boolean isJsonBody(final Response rsp) {
+ String type = rsp.getHeader("Content-Type");
+ if (type == null) {
+ return false;
+ }
+ int semi = type.indexOf(';');
+ if (semi >= 0) {
+ type = type.substring(0, semi).trim();
+ }
+ return JsonUtil.JSONRPC20_ACCEPT_CTS.contains(type);
+ }
+
+ /**
+ * Call a JSON parser javascript function to parse an encoded JSON string.
+ *
+ * @param parser a javascript function
+ * @param json encoded JSON text
+ * @return the parsed data
+ * @see #jsonParser
+ */
+ private static final native RpcResult parse(JavaScriptObject parserFunction,
+ String json)
+ /*-{
+ return parserFunction(json);
+ }-*/;
+
+
+ private static class RpcResult extends JavaScriptObject {
+ protected RpcResult() {
+ }
+
+ final native RpcError error()/*-{ return this.error; }-*/;
+
+ final native String xsrfKey()/*-{ return this.xsrfKey; }-*/;
+ }
+
+ private static class RpcError extends JavaScriptObject {
+ protected RpcError() {
+ }
+
+ final native String message()/*-{ return this.message; }-*/;
+
+ final native int code()/*-{ return this.code; }-*/;
+ }
+}
diff --git a/src/main/java/com/google/gwtjsonrpc/client/JsonCall20HttpGet.java b/src/main/java/com/google/gwtjsonrpc/client/JsonCall20HttpGet.java
new file mode 100644
index 0000000..416ff3c
--- /dev/null
+++ b/src/main/java/com/google/gwtjsonrpc/client/JsonCall20HttpGet.java
@@ -0,0 +1,74 @@
+// Copyright 2009 Gert Scholten
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtjsonrpc.client;
+
+import com.google.gwt.http.client.RequestBuilder;
+import com.google.gwt.http.client.URL;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+/** JsonCall implementation for JsonRPC version 2.0 over HTTP POST */
+public class JsonCall20HttpGet<T> extends JsonCall20<T> {
+ private String encodedRequestParams;
+
+ public JsonCall20HttpGet(AbstractJsonProxy abstractJsonProxy,
+ String methodName, String requestParams,
+ ResultDeserializer<T> resultDeserializer, AsyncCallback<T> callback) {
+ super(abstractJsonProxy, methodName, requestParams, resultDeserializer,
+ callback);
+ encodedRequestParams = URL.encodeComponent(encodeBase64(requestParams));
+ }
+
+ @Override
+ void send() {
+ requestId = ++lastRequestId;
+ final StringBuilder url = new StringBuilder(proxy.url);
+ url.append("?jsonrpc=2.0&method=").append(methodName);
+ url.append("&params=").append(encodedRequestParams);
+ url.append("&id=").append(requestId);
+
+ final RequestBuilder rb;
+ rb = new RequestBuilder(RequestBuilder.GET, url.toString());
+ rb.setHeader("Content-Type", JsonUtil.JSONRPC20_REQ_CT);
+ rb.setHeader("Accept", JsonUtil.JSONRPC20_ACCEPT_CTS);
+ rb.setCallback(this);
+
+ send(rb);
+ }
+
+ /**
+ * Javascript base64 encoding implementation from.
+ *
+ * @see http://ecmanaut.googlecode.com/svn/trunk/lib/base64.js
+ */
+ private static native String encodeBase64(String data)
+ /*-{
+ var out = "", c1, c2, c3, e1, e2, e3, e4;
+ for (var i = 0; i < data.length; ) {
+ c1 = data.charCodeAt(i++);
+ c2 = data.charCodeAt(i++);
+ c3 = data.charCodeAt(i++);
+ e1 = c1 >> 2;
+ e2 = ((c1 & 3) << 4) + (c2 >> 4);
+ e3 = ((c2 & 15) << 2) + (c3 >> 6);
+ e4 = c3 & 63;
+ if (isNaN(c2))
+ e3 = e4 = 64;
+ else if (isNaN(c3))
+ e4 = 64;
+ out += tab.charAt(e1) + tab.charAt(e2) + tab.charAt(e3) + tab.charAt(e4);
+ }
+ return out;
+ }-*/;
+}
diff --git a/src/main/java/com/google/gwtjsonrpc/client/JsonCall20HttpPost.java b/src/main/java/com/google/gwtjsonrpc/client/JsonCall20HttpPost.java
new file mode 100644
index 0000000..a875d24
--- /dev/null
+++ b/src/main/java/com/google/gwtjsonrpc/client/JsonCall20HttpPost.java
@@ -0,0 +1,54 @@
+// Copyright 2009 Gert Scholten
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtjsonrpc.client;
+
+import com.google.gwt.http.client.RequestBuilder;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+/** JsonCall implementation for JsonRPC version 2.0 over HTTP POST */
+public class JsonCall20HttpPost<T> extends JsonCall20<T> {
+ public JsonCall20HttpPost(AbstractJsonProxy abstractJsonProxy,
+ String methodName, String requestParams,
+ ResultDeserializer<T> resultDeserializer, AsyncCallback<T> callback) {
+ super(abstractJsonProxy, methodName, requestParams, resultDeserializer,
+ callback);
+ }
+
+ @Override
+ void send() {
+ requestId = ++lastRequestId;
+ final StringBuilder body = new StringBuilder();
+ body.append("{\"jsonrpc\":\"2.0\",\"method\":\"");
+ body.append(methodName);
+ body.append("\",\"params\":[");
+ body.append(requestParams);
+ body.append("],\"id\":").append(requestId);
+ final String xsrfKey = proxy.getXsrfManager().getToken(proxy);
+ if (xsrfKey != null) {
+ body.append(",\"xsrfKey\":");
+ body.append(JsonSerializer.escapeString(xsrfKey));
+ }
+ body.append("}");
+
+ final RequestBuilder rb;
+ rb = new RequestBuilder(RequestBuilder.POST, proxy.url);
+ rb.setHeader("Content-Type", JsonUtil.JSONRPC20_REQ_CT);
+ rb.setHeader("Accept", JsonUtil.JSONRPC20_ACCEPT_CTS);
+ rb.setCallback(this);
+ rb.setRequestData(body.toString());
+
+ send(rb);
+ }
+}
diff --git a/src/main/java/com/google/gwtjsonrpc/client/JsonUtil.java b/src/main/java/com/google/gwtjsonrpc/client/JsonUtil.java
index 7f1f47a..2628cc4 100644
--- a/src/main/java/com/google/gwtjsonrpc/client/JsonUtil.java
+++ b/src/main/java/com/google/gwtjsonrpc/client/JsonUtil.java
@@ -33,6 +33,17 @@ public class JsonUtil {
/** Request Content-Type header for JSON data. */
static final String JSON_REQ_CT = JSON_TYPE + "; charset=utf-8";
+ /** Json-rpc 2.0: Proper Content-Type header value for JSON encoded data. */
+ public static final String JSONRPC20_TYPE = "application/json-rpc";
+
+ /** Json-rpc 2.0: Request Content-Type header for JSON data. */
+ static final String JSONRPC20_REQ_CT = JSON_TYPE + "; charset=utf-8";
+
+ /** Json-rpc 2.0: Content types that we SHOULD accept as being valid */
+ static final String JSONRPC20_ACCEPT_CTS =
+ JSON_TYPE + ",application/json,application/jsonrequest";
+
+
/** Error message when xsrfKey in request is missing or invalid. */
public static final String ERROR_INVALID_XSRF = "Invalid xsrfKey in request";
diff --git a/src/main/java/com/google/gwtjsonrpc/client/RemoteJsonException.java b/src/main/java/com/google/gwtjsonrpc/client/RemoteJsonException.java
index 4a7592f..a363e83 100644
--- a/src/main/java/com/google/gwtjsonrpc/client/RemoteJsonException.java
+++ b/src/main/java/com/google/gwtjsonrpc/client/RemoteJsonException.java
@@ -17,30 +17,38 @@ package com.google.gwtjsonrpc.client;
import com.google.gwt.json.client.JSONValue;
/**
- * Exception given to {@link com.google.gwt.user.client.rpc.AsyncCallback#onFailure(Throwable)}.
+ * Exception given to
+ * {@link com.google.gwt.user.client.rpc.AsyncCallback#onFailure(Throwable)}.
* <p>
* This exception is used if the remote JSON server has returned a well-formed
* JSON error response.
*/
public class RemoteJsonException extends Exception {
+ private static final long serialVersionUID = 1L;
private int code;
- private JSONValue error;
+ private JSONValue data;
/**
- * Construct a new exception representing a welformed JSON error response.
- *
+ * Construct a new exception representing a well formed JSON error response.
+ *
* @param message A String value that provides a short description of the
- * error.
- * @param code A number that indicates the actual error that occurred.
- * @param error A JSON value instance that carries custom and
- * application-specific error information.
+ * error
+ * @param code A number that indicates the actual error that occurred
+ * @param data A JSON value instance that carries custom and
+ * application-specific error information
*/
- public RemoteJsonException(final String message, int code, JSONValue error) {
+ public RemoteJsonException(final String message, int code, JSONValue data) {
super(message);
this.code = code;
- this.error = error;
+ this.data = data;
}
+ /**
+ * Creates a new RemoteJsonException with code 999 and no data.
+ *
+ * @param message A String value that provides a short description of the
+ * error
+ */
public RemoteJsonException(final String message) {
this(message, 999, null);
}
@@ -49,7 +57,7 @@ public class RemoteJsonException extends Exception {
* Gets the error code.
* <p>
* Note that the JSON-RPC 1.1 draf does not define error codes yet.
- *
+ *
* @return A number that indicates the actual error that occurred.
*/
public int getCode() {
@@ -57,12 +65,21 @@ public class RemoteJsonException extends Exception {
}
/**
- * Gets the extra error information.
- *
- * @return <code>null</code> if no error information was specified by the
- * server, or value.
+ * Same as getData.
+ *
+ * @return the error data, or <code>null</code> if none was specified
+ * @see #getData
*/
public JSONValue getError() {
- return error;
+ return data;
+ }
+
+ /**
+ * Gets the extra error information supplied by the service.
+ *
+ * @return the error data, or <code>null</code> if none was specified
+ */
+ public JSONValue getData() {
+ return data;
}
}
diff --git a/src/main/java/com/google/gwtjsonrpc/client/RpcImpl.java b/src/main/java/com/google/gwtjsonrpc/client/RpcImpl.java
new file mode 100644
index 0000000..9868e70
--- /dev/null
+++ b/src/main/java/com/google/gwtjsonrpc/client/RpcImpl.java
@@ -0,0 +1,67 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gwtjsonrpc.client;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * Specify the json rpc protocol version and transport mechanism to be used for
+ * a service.
+ * <p>
+ * Default is version 1.1 over HTTP POST.
+ * <p>
+ * <b>Note: if you use the generated (servlet), only version 1.1 over HTTP POST
+ * is supported</b>.
+ */
+@Target(ElementType.TYPE)
+public @interface RpcImpl {
+ /**
+ * JSON-RPC protocol versions.
+ */
+ public enum Version {
+ /**
+ * Version 1.1.
+ *
+ * @see <a
+ * href="http://groups.google.com/group/json-rpc/web/json-rpc-1-1-wd">Spec</a>
+ */
+ V1_1,
+ /**
+ * Version 2.0.
+ *
+ * @see <a
+ * href="http://groups.google.com/group/json-rpc/web/json-rpc-1-2-proposal">Spec</a>
+ */
+ V2_0
+ }
+ /**
+ * Supported transport mechanisms.
+ */
+ public enum Transport {
+ HTTP_POST, HTTP_GET
+ }
+
+ /**
+ * Specify the JSON-RPC version. Default is version 1.1.
+ */
+ Version version() default Version.V1_1;
+
+ /**
+ * Specify the transport protocol used to make the RPC call. Default is HTTP
+ * POST.
+ */
+ Transport transport() default Transport.HTTP_POST;
+}
diff --git a/src/main/java/com/google/gwtjsonrpc/rebind/ProxyCreator.java b/src/main/java/com/google/gwtjsonrpc/rebind/ProxyCreator.java
index 573254d..0bda41f 100644
--- a/src/main/java/com/google/gwtjsonrpc/rebind/ProxyCreator.java
+++ b/src/main/java/com/google/gwtjsonrpc/rebind/ProxyCreator.java
@@ -19,6 +19,7 @@ import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.TreeLogger.Type;
import com.google.gwt.core.ext.typeinfo.JArrayType;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JMethod;
@@ -37,9 +38,15 @@ import com.google.gwt.user.rebind.SourceWriter;
import com.google.gwtjsonrpc.client.AbstractJsonProxy;
import com.google.gwtjsonrpc.client.CallbackHandle;
import com.google.gwtjsonrpc.client.HostPageCache;
+import com.google.gwtjsonrpc.client.JsonCall11HttpPost;
+import com.google.gwtjsonrpc.client.JsonCall20HttpGet;
+import com.google.gwtjsonrpc.client.JsonCall20HttpPost;
import com.google.gwtjsonrpc.client.JsonSerializer;
import com.google.gwtjsonrpc.client.JsonUtil;
import com.google.gwtjsonrpc.client.ResultDeserializer;
+import com.google.gwtjsonrpc.client.RpcImpl;
+import com.google.gwtjsonrpc.client.RpcImpl.Transport;
+import com.google.gwtjsonrpc.client.RpcImpl.Version;
import java.io.PrintWriter;
import java.util.HashSet;
@@ -60,7 +67,8 @@ class ProxyCreator {
String create(final TreeLogger logger, final GeneratorContext context)
throws UnableToCompleteException {
serializerCreator = new SerializerCreator(context);
- deserializerCreator = new ResultDeserializerCreator(context, serializerCreator);
+ deserializerCreator =
+ new ResultDeserializerCreator(context, serializerCreator);
final TypeOracle typeOracle = context.getTypeOracle();
try {
asyncCallbackClass = typeOracle.getType(AsyncCallback.class.getName());
@@ -76,6 +84,7 @@ class ProxyCreator {
}
generateProxyConstructor(logger, srcWriter);
+ generateProxyCallCreator(logger, srcWriter);
generateProxyMethods(logger, srcWriter);
srcWriter.commit(logger);
@@ -199,6 +208,8 @@ class ProxyCreator {
cf.addImport(AbstractJsonProxy.class.getCanonicalName());
cf.addImport(JsonSerializer.class.getCanonicalName());
cf.addImport(JavaScriptObject.class.getCanonicalName());
+ cf.addImport(ResultDeserializer.class.getCanonicalName());
+ cf.addImport(AsyncCallback.class.getCanonicalName());
cf.addImport(GWT.class.getCanonicalName());
cf.setSuperclass(AbstractJsonProxy.class.getSimpleName());
cf.addImplementedInterface(svcInf.getErasedType().getQualifiedSourceName());
@@ -220,6 +231,48 @@ class ProxyCreator {
}
}
+ private void generateProxyCallCreator(final TreeLogger logger,
+ final SourceWriter w) throws UnableToCompleteException {
+ String callName = getJsonCallClassName(logger);
+ w.println();
+ w.println("@Override");
+ w.print("protected <T> ");
+ w.print(callName);
+ w.print("<T> newJsonCall(final AbstractJsonProxy proxy, ");
+ w.print("final String methodName, final String reqData, ");
+ w.println("final ResultDeserializer<T> ser, final AsyncCallback<T> cb) {");
+ w.indent();
+
+ w.print("return new ");
+ w.print(callName);
+ w.println("<T>(proxy, methodName, reqData, ser, cb);");
+
+ w.outdent();
+ w.println("}");
+ }
+
+ private String getJsonCallClassName(final TreeLogger logger)
+ throws UnableToCompleteException {
+ RpcImpl impl = svcInf.getAnnotation(RpcImpl.class);
+ if (impl == null) {
+ return JsonCall11HttpPost.class.getCanonicalName();
+ } else if (impl.version() == Version.V1_1
+ && impl.transport() == Transport.HTTP_POST) {
+ return JsonCall11HttpPost.class.getCanonicalName();
+ } else if (impl.version() == Version.V2_0
+ && impl.transport() == Transport.HTTP_POST) {
+ return JsonCall20HttpPost.class.getCanonicalName();
+ } else if (impl.version() == Version.V2_0
+ && impl.transport() == Transport.HTTP_GET) {
+ return JsonCall20HttpGet.class.getCanonicalName();
+ }
+
+ logger.log(Type.ERROR, "Unsupported JSON-RPC version and transport "
+ + "combination: Supported are 1.1 over HTTP POST and "
+ + "2.0 over HTTP POST and GET");
+ throw new UnableToCompleteException();
+ }
+
private void generateProxyMethods(final TreeLogger logger,
final SourceWriter srcWriter) {
final JMethod[] methodList = svcInf.getOverridableMethods();
@@ -334,11 +387,12 @@ class ProxyCreator {
final String reqDataStr;
if (params.length == 1) {
- reqDataStr = "\"\"";
+ reqDataStr = "\"[]\"";
} else {
final String reqData = nameFactory.createName("reqData");
w.println("final StringBuilder " + reqData + " = new StringBuilder();");
needsComma = false;
+ w.println(reqData + ".append('[');");
for (int i = 0; i < params.length - 1; i++) {
if (needsComma) {
w.println(reqData + ".append(\",\");");
@@ -376,6 +430,7 @@ class ProxyCreator {
w.println("}");
}
}
+ w.println(reqData + ".append(']');");
reqDataStr = reqData + ".toString()";
}