Extract a RequestTransport interface and DefaultRequestTransport implementation from RequestFactory.
This will allow end-users to have arbitrary control over the way RF communicates with the server.
Patch by: bobv
Review by: rjrjr
Review at http://gwt-code-reviews.appspot.com/890802
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@8812 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/requestfactory/client/DefaultRequestTransport.java b/user/src/com/google/gwt/requestfactory/client/DefaultRequestTransport.java
new file mode 100644
index 0000000..1fcc82a
--- /dev/null
+++ b/user/src/com/google/gwt/requestfactory/client/DefaultRequestTransport.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2010 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.gwt.requestfactory.client;
+
+import static com.google.gwt.user.client.rpc.RpcRequestBuilder.STRONG_NAME_HEADER;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.event.shared.EventBus;
+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.requestfactory.shared.RequestEvent;
+import com.google.gwt.requestfactory.shared.RequestFactory;
+import com.google.gwt.requestfactory.shared.RequestTransport;
+import com.google.gwt.requestfactory.shared.RequestEvent.State;
+import com.google.gwt.user.client.Window.Location;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * An implementation of {@link RequestTransport} that uses a
+ * {@link RequestBuilder}.
+ */
+public class DefaultRequestTransport implements RequestTransport {
+ /*
+ * A separate logger for wire activity, which does not get logged by the
+ * remote log handler, so we avoid infinite loops. All log messages that could
+ * happen every time a request is made from the server should be logged to
+ * this logger.
+ */
+ private static Logger wireLogger = Logger.getLogger("WireActivityLogger");
+ private static final String SERVER_ERROR = "Server Error";
+ private final EventBus eventBus;
+ private String requestUrl = GWT.getHostPageBaseURL() + RequestFactory.URL;
+
+ /**
+ * Construct a DefaultRequestTransport.
+ *
+ * @param eventBus the same EventBus passed into {@link RequestFactory#init}.
+ */
+ public DefaultRequestTransport(EventBus eventBus) {
+ if (eventBus == null) {
+ throw new IllegalArgumentException("eventBus must not be null");
+ }
+ this.eventBus = eventBus;
+ }
+
+ /**
+ * Returns the current URL used by this transport.
+ */
+ public String getRequestUrl() {
+ return requestUrl;
+ }
+
+ public void send(String payload, Receiver receiver) {
+ RequestBuilder builder = createRequestBuilder();
+ configureRequestBuilder(builder);
+
+ builder.setRequestData(payload);
+ builder.setCallback(createRequestCallback(receiver));
+
+ try {
+ wireLogger.finest("Sending fire request");
+ builder.send();
+ postRequestEvent(State.SENT, null);
+ } catch (RequestException e) {
+ wireLogger.log(Level.SEVERE, SERVER_ERROR + " (" + e.getMessage() + ")",
+ e);
+ }
+ }
+
+ /**
+ * Override the default URL used by this transport.
+ */
+ public void setRequestUrl(String url) {
+ this.requestUrl = url;
+ }
+
+ /**
+ * Override to change the headers sent in the HTTP request.
+ */
+ protected void configureRequestBuilder(RequestBuilder builder) {
+ builder.setHeader("Content-Type", RequestFactory.JSON_CONTENT_TYPE_UTF8);
+ builder.setHeader("pageurl", Location.getHref());
+ builder.setHeader(STRONG_NAME_HEADER, GWT.getPermutationStrongName());
+ }
+
+ /**
+ * Constructs a RequestBuilder using the {@link RequestBuilder#POST} method
+ * sent to the URL returned from {@link #getRequestUrl()}.
+ */
+ protected RequestBuilder createRequestBuilder() {
+ return new RequestBuilder(RequestBuilder.POST, getRequestUrl());
+ }
+
+ /**
+ * Creates a RequestCallback that maps the HTTP response onto the
+ * {@link Receiver} interface.
+ */
+ protected RequestCallback createRequestCallback(final Receiver receiver) {
+ return new RequestCallback() {
+
+ public void onError(Request request, Throwable exception) {
+ postRequestEvent(State.RECEIVED, null);
+ wireLogger.log(Level.SEVERE, SERVER_ERROR, exception);
+ receiver.onFailure(exception.getMessage());
+ }
+
+ public void onResponseReceived(Request request, Response response) {
+ wireLogger.finest("Response received");
+ try {
+ if (200 == response.getStatusCode()) {
+ String text = response.getText();
+ receiver.onSuccess(text);
+ } else if (Response.SC_UNAUTHORIZED == response.getStatusCode()) {
+ String message = "Need to log in";
+ wireLogger.finest(message);
+ receiver.onFailure(message);
+ } else if (response.getStatusCode() > 0) {
+ /*
+ * During the redirection for logging in, we get a response with no
+ * status code, but it's not an error, so we only log errors with
+ * bad status codes here.
+ */
+ String message = SERVER_ERROR + " " + response.getStatusCode()
+ + " " + response.getText();
+ wireLogger.severe(message);
+ receiver.onFailure(message);
+ }
+ } finally {
+ postRequestEvent(State.RECEIVED, response);
+ }
+ }
+ };
+ }
+
+ private void postRequestEvent(State received, Response response) {
+ eventBus.fireEvent(new RequestEvent(received, response));
+ }
+}
diff --git a/user/src/com/google/gwt/requestfactory/client/impl/RequestFactoryJsonImpl.java b/user/src/com/google/gwt/requestfactory/client/impl/RequestFactoryJsonImpl.java
index 99c84d1..e310b62 100644
--- a/user/src/com/google/gwt/requestfactory/client/impl/RequestFactoryJsonImpl.java
+++ b/user/src/com/google/gwt/requestfactory/client/impl/RequestFactoryJsonImpl.java
@@ -15,26 +15,20 @@
*/
package com.google.gwt.requestfactory.client.impl;
-import com.google.gwt.core.client.GWT;
import com.google.gwt.event.shared.EventBus;
-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.requestfactory.client.DefaultRequestTransport;
import com.google.gwt.requestfactory.shared.EntityProxy;
import com.google.gwt.requestfactory.shared.EntityProxyId;
import com.google.gwt.requestfactory.shared.ProxyRequest;
-import com.google.gwt.requestfactory.shared.RequestEvent;
-import com.google.gwt.requestfactory.shared.RequestEvent.State;
import com.google.gwt.requestfactory.shared.RequestFactory;
import com.google.gwt.requestfactory.shared.RequestObject;
+import com.google.gwt.requestfactory.shared.RequestTransport;
+import com.google.gwt.requestfactory.shared.ServerFailure;
import com.google.gwt.requestfactory.shared.WriteOperation;
-import com.google.gwt.user.client.Window.Location;
+import com.google.gwt.requestfactory.shared.impl.RequestData;
import java.util.HashMap;
import java.util.Map;
-import java.util.logging.Level;
import java.util.logging.Logger;
/**
@@ -59,8 +53,9 @@
return perSchemaMap.get(datastoreId);
}
- /* returns the previous futureId, if any*/
- Object put(Object datastoreId, ProxySchema<? extends ProxyImpl> schema, Object futureId) {
+ /* returns the previous futureId, if any */
+ Object put(Object datastoreId, ProxySchema<? extends ProxyImpl> schema,
+ Object futureId) {
Map<Object, Object> perSchemaMap = internalMap.get(schema);
if (perSchemaMap == null) {
perSchemaMap = new HashMap<Object, Object>();
@@ -69,25 +64,19 @@
return perSchemaMap.put(datastoreId, futureId);
}
}
+
static final boolean IS_FUTURE = true;
static final boolean NOT_FUTURE = false;
private static Logger logger = Logger.getLogger(RequestFactory.class.getName());
- // A separate logger for wire activity, which does not get logged by the
- // remote log handler, so we avoid infinite loops. All log messages that
- // could happen every time a request is made from the server should be logged
- // to this logger.
- private static Logger wireLogger = Logger.getLogger("WireActivityLogger");
-
- private static String SERVER_ERROR = "Server Error";
-
private final Integer initialVersion = 1;
/*
* Keeping these maps forever is not a desirable solution because of the
* memory overhead but need these if want to provide stable {@EntityProxyId}.
*
- * futureToDatastoreMap is currently not used, will be useful in find requests.
+ * futureToDatastoreMap is currently not used, will be useful in find
+ * requests.
*/
final Map<Object, Object> futureToDatastoreMap = new HashMap<Object, Object>();
@@ -99,6 +88,8 @@
private EventBus eventBus;
+ private RequestTransport transport;
+
public <R extends ProxyImpl> R create(Class<R> token,
ProxyToTypeMap recordToTypeMap) {
@@ -114,56 +105,31 @@
return findRequest().find(proxyId);
}
- public void fire(final RequestObject<?> requestObject) {
- RequestBuilder builder = new RequestBuilder(RequestBuilder.POST,
- GWT.getHostPageBaseURL() + RequestFactory.URL);
- builder.setHeader("Content-Type", RequestFactory.JSON_CONTENT_TYPE_UTF8);
- builder.setHeader("pageurl", Location.getHref());
-
- builder.setRequestData(ClientRequestHelper.getRequestString(((AbstractRequest<?, ?>) requestObject).getRequestData().getRequestMap(
- ((AbstractRequest<?, ?>) requestObject).deltaValueStore.toJson())));
- builder.setCallback(new RequestCallback() {
-
- public void onError(Request request, Throwable exception) {
- postRequestEvent(State.RECEIVED, null);
- wireLogger.log(Level.SEVERE, SERVER_ERROR, exception);
+ public void fire(RequestObject<?> requestObject) {
+ final AbstractRequest<?, ?> abstractRequest = (AbstractRequest<?, ?>) requestObject;
+ RequestData requestData = ((AbstractRequest<?, ?>) requestObject).getRequestData();
+ Map<String, String> requestMap = requestData.getRequestMap(abstractRequest.deltaValueStore.toJson());
+ String payload = ClientRequestHelper.getRequestString(requestMap);
+ transport.send(payload, new RequestTransport.Receiver() {
+ public void onFailure(String message) {
+ abstractRequest.receiver.onFailure(new ServerFailure(message, null,
+ null));
}
- public void onResponseReceived(Request request, Response response) {
- wireLogger.finest("Response received");
- try {
- if (200 == response.getStatusCode()) {
- String text = response.getText();
- ((AbstractRequest<?, ?>) requestObject).handleResponseText(text);
- } else if (Response.SC_UNAUTHORIZED == response.getStatusCode()) {
- wireLogger.finest("Need to log in");
- } else if (response.getStatusCode() > 0) {
- // During the redirection for logging in, we get a response with no
- // status code, but it's not an error, so we only log errors with
- // bad status codes here.
- wireLogger.severe(SERVER_ERROR + " " + response.getStatusCode()
- + " " + response.getText());
- }
- } finally {
- postRequestEvent(State.RECEIVED, response);
- }
+ public void onSuccess(String payload) {
+ abstractRequest.handleResponseText(payload);
}
});
-
- try {
- wireLogger.finest("Sending fire request");
- builder.send();
- postRequestEvent(State.SENT, null);
- } catch (RequestException e) {
- wireLogger.log(Level.SEVERE, SERVER_ERROR + " (" + e.getMessage() + ")",
- e);
- }
}
public Class<? extends EntityProxy> getClass(EntityProxy proxy) {
return ((ProxyImpl) proxy).getSchema().getProxyClass();
}
+ public RequestTransport getRequestTransport() {
+ return transport;
+ }
+
public abstract ProxySchema<?> getSchema(String token);
public String getWireFormat(EntityProxyId proxyId) {
@@ -173,19 +139,22 @@
// search for the datastore id for this futureId.
Long datastoreId = (Long) futureToDatastoreMap.get(id);
if (datastoreId == null) {
- throw new IllegalArgumentException("Cannot call find on a proxyId before persisting");
+ throw new IllegalArgumentException(
+ "Cannot call find on a proxyId before persisting");
}
id = datastoreId;
}
return ProxyImpl.getWireFormatId(id, NOT_FUTURE, proxyIdImpl.schema);
}
- /**
- * @param eventBus
- */
public void init(EventBus eventBus) {
+ init(eventBus, new DefaultRequestTransport(eventBus));
+ }
+
+ public void init(EventBus eventBus, RequestTransport transport) {
this.valueStore = new ValueStoreJsonImpl();
this.eventBus = eventBus;
+ this.transport = transport;
logger.fine("Successfully initialized RequestFactory");
}
@@ -200,7 +169,7 @@
}
return schema.getProxyClass();
}
-
+
/**
* TODO(amitmanjhi): remove this method, use getProxyId instead.
*/
@@ -229,7 +198,8 @@
return schema.create(ProxyJsoImpl.create(id, -1, schema, this));
}
- protected EntityProxyId getProxyId(String token, ProxyToTypeMap recordToTypeMap) {
+ protected EntityProxyId getProxyId(String token,
+ ProxyToTypeMap recordToTypeMap) {
String[] bits = token.split(EntityProxyIdImpl.SEPARATOR);
if (bits.length != 2) {
return null;
@@ -291,8 +261,4 @@
schema, this);
return schema.create(newRecord, IS_FUTURE);
}
-
- private void postRequestEvent(State received, Response response) {
- eventBus.fireEvent(new RequestEvent(received, response));
- }
}
diff --git a/user/src/com/google/gwt/requestfactory/shared/RequestFactory.java b/user/src/com/google/gwt/requestfactory/shared/RequestFactory.java
index c7ef713..0b25860 100644
--- a/user/src/com/google/gwt/requestfactory/shared/RequestFactory.java
+++ b/user/src/com/google/gwt/requestfactory/shared/RequestFactory.java
@@ -86,7 +86,13 @@
String getToken(EntityProxy proxy);
/**
- * Start this request factory.
+ * Start this request factory with a
+ * {@link com.google.gwt.requestfactory.client.DefaultRequestTransport}.
*/
void init(EventBus eventBus);
+
+ /**
+ * Start this request factory with a user-provided transport.
+ */
+ void init(EventBus eventBus, RequestTransport transport);
}
diff --git a/user/src/com/google/gwt/requestfactory/shared/RequestTransport.java b/user/src/com/google/gwt/requestfactory/shared/RequestTransport.java
new file mode 100644
index 0000000..f184f08
--- /dev/null
+++ b/user/src/com/google/gwt/requestfactory/shared/RequestTransport.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2010 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.gwt.requestfactory.shared;
+
+/**
+ * Abstracts the mechanism by which a RequestFactory instance transmits its
+ * payload to the backend.
+ *
+ * @see com.google.gwt.requestfactory.client.DefaultRequestTransport
+ */
+public interface RequestTransport {
+ /**
+ * A callback interface.
+ */
+ public interface Receiver {
+ void onSuccess(String payload);
+
+ void onFailure(String message);
+ }
+
+ /**
+ * Called by the RequestFactory implementation.
+ */
+ void send(String payload, Receiver receiver);
+}