Revision: 8812
Author: [email protected]
Date: Fri Sep 17 12:21:10 2010
Log: 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
http://code.google.com/p/google-web-toolkit/source/detail?r=8812
Added:
/trunk/user/src/com/google/gwt/requestfactory/client/DefaultRequestTransport.java
/trunk/user/src/com/google/gwt/requestfactory/shared/RequestTransport.java
Modified:
/trunk/user/src/com/google/gwt/requestfactory/client/impl/RequestFactoryJsonImpl.java
/trunk/user/src/com/google/gwt/requestfactory/shared/RequestFactory.java
=======================================
--- /dev/null
+++
/trunk/user/src/com/google/gwt/requestfactory/client/DefaultRequestTransport.java
Fri Sep 17 12:21:10 2010
@@ -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));
+ }
+}
=======================================
--- /dev/null
+++
/trunk/user/src/com/google/gwt/requestfactory/shared/RequestTransport.java
Fri Sep 17 12:21:10 2010
@@ -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);
+}
=======================================
---
/trunk/user/src/com/google/gwt/requestfactory/client/impl/RequestFactoryJsonImpl.java
Thu Sep 16 08:54:56 2010
+++
/trunk/user/src/com/google/gwt/requestfactory/client/impl/RequestFactoryJsonImpl.java
Fri Sep 17 12:21:10 2010
@@ -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,55 +105,30 @@
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);
@@ -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));
- }
-}
+}
=======================================
---
/trunk/user/src/com/google/gwt/requestfactory/shared/RequestFactory.java
Wed Sep 15 03:48:29 2010
+++
/trunk/user/src/com/google/gwt/requestfactory/shared/RequestFactory.java
Fri Sep 17 12:21:10 2010
@@ -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);
+}
--
http://groups.google.com/group/Google-Web-Toolkit-Contributors