blob: d00658c79ac695f6644f99811caa43c8f358e0ba [file] [log] [blame]
/*
* Copyright 2008 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.user.client.rpc.impl;
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.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.rpc.InvocationException;
import com.google.gwt.user.client.rpc.RpcRequestBuilder;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.client.rpc.SerializationStreamFactory;
import com.google.gwt.user.client.rpc.SerializationStreamReader;
import com.google.gwt.user.client.rpc.SerializationStreamWriter;
import com.google.gwt.user.client.rpc.ServiceDefTarget;
import com.google.gwt.user.client.rpc.impl.RequestCallbackAdapter.ResponseReader;
/**
* Superclass for client-side
* {@link com.google.gwt.user.client.rpc.RemoteService RemoteService} proxies.
*
* For internal use only.
*/
public abstract class RemoteServiceProxy implements SerializationStreamFactory,
ServiceDefTarget {
/**
* The content type to be used in HTTP requests.
*/
private static final String RPC_CONTENT_TYPE = "text/x-gwt-rpc; charset=utf-8";
/**
* @deprecated use {@link RpcStatsContext}.
*/
@Deprecated
public static JavaScriptObject bytesStat(String method, int count,
int bytes, String eventType) {
return new RpcStatsContext(count).bytesStat(method, bytes, eventType);
}
/**
* Indicates if RPC statistics should be gathered.
*
* @deprecated use {@link RpcStatsContext}.
*/
@Deprecated
public static boolean isStatsAvailable() {
return new RpcStatsContext(0).isStatsAvailable();
}
/**
* Always use this as {@link #isStatsAvailable()} &&
* {@link #stats(JavaScriptObject)}.
*
* @deprecated use {link RpcStatsContext}.
*/
@Deprecated
public static boolean stats(JavaScriptObject data) {
return new RpcStatsContext(0).stats(data);
}
/**
* @deprecated use {@link RpcStatsContext}.
*/
@Deprecated
public static JavaScriptObject timeStat(String method, int count,
String eventType) {
return new RpcStatsContext(count).timeStat(method, eventType);
}
/**
* @deprected use {@link RpcStatsContext}.
*/
@Deprecated
protected static int getNextRequestId() {
return RpcStatsContext.getNextRequestId();
}
/**
* @deprecated Use {@link RpcRequestBuilder} instead.
*/
@Deprecated
protected static int getRequestId() {
return RpcStatsContext.getLastRequestId();
}
/**
* Return <code>true</code> if the encoded response contains a value returned
* by the method invocation.
*
* @param encodedResponse
* @return <code>true</code> if the encoded response contains a value returned
* by the method invocation
*/
static boolean isReturnValue(String encodedResponse) {
return encodedResponse.startsWith("//OK");
}
/**
* Return <code>true</code> if the encoded response contains a checked
* exception that was thrown by the method invocation.
*
* @param encodedResponse
* @return <code>true</code> if the encoded response contains a checked
* exception that was thrown by the method invocation
*/
static boolean isThrownException(String encodedResponse) {
return encodedResponse.startsWith("//EX");
}
/**
* Returns a string that encodes the result of a method invocation.
* Effectively, this just removes any headers from the encoded response.
*
* @param encodedResponse
* @return string that encodes the result of a method invocation
*/
private static String getEncodedInstance(String encodedResponse) {
if (isReturnValue(encodedResponse) || isThrownException(encodedResponse)) {
return encodedResponse.substring(4);
}
return encodedResponse;
}
/**
* The module base URL as specified during construction.
*/
private final String moduleBaseURL;
/**
* URL of the {@link com.google.gwt.user.client.rpc.RemoteService
* RemoteService}.
*/
private String remoteServiceURL;
private RpcRequestBuilder rpcRequestBuilder;
/**
* The name of the serialization policy file specified during construction.
*/
private final String serializationPolicyName;
/**
* The {@link Serializer} instance used to serialize and deserialize
* instances.
*/
private final Serializer serializer;
protected RemoteServiceProxy(String moduleBaseURL,
String remoteServiceRelativePath, String serializationPolicyName,
Serializer serializer) {
this.moduleBaseURL = moduleBaseURL;
if (remoteServiceRelativePath != null) {
/*
* If the module relative URL is not null we set the remote service URL to
* be the module base URL plus the module relative remote service URL.
* Otherwise an explicit call to
* ServiceDefTarget.setServiceEntryPoint(String) is required.
*/
this.remoteServiceURL = moduleBaseURL + remoteServiceRelativePath;
}
this.serializer = serializer;
this.serializationPolicyName = serializationPolicyName;
}
/**
* Returns a {@link com.google.gwt.user.client.rpc.SerializationStreamReader
* SerializationStreamReader} that is ready for reading.
*
* @param encoded string that encodes the response of an RPC request
* @return {@link com.google.gwt.user.client.rpc.SerializationStreamReader
* SerializationStreamReader} that is ready for reading
* @throws SerializationException
*/
public SerializationStreamReader createStreamReader(String encoded)
throws SerializationException {
ClientSerializationStreamReader clientSerializationStreamReader = new ClientSerializationStreamReader(
serializer);
clientSerializationStreamReader.prepareToRead(getEncodedInstance(encoded));
return clientSerializationStreamReader;
}
/**
* Returns a {@link com.google.gwt.user.client.rpc.SerializationStreamWriter
* SerializationStreamWriter} that has had
* {@link ClientSerializationStreamWriter#prepareToWrite()} called on it and
* it has already had had the name of the remote service interface written as
* well.
*
* @return {@link com.google.gwt.user.client.rpc.SerializationStreamWriter
* SerializationStreamWriter} that has had
* {@link ClientSerializationStreamWriter#prepareToWrite()} called on
* it and it has already had had the name of the remote service
* interface written as well
*/
public SerializationStreamWriter createStreamWriter() {
ClientSerializationStreamWriter clientSerializationStreamWriter = new ClientSerializationStreamWriter(
serializer, moduleBaseURL, serializationPolicyName);
clientSerializationStreamWriter.prepareToWrite();
return clientSerializationStreamWriter;
}
public String getSerializationPolicyName() {
return serializationPolicyName;
}
/**
* @see ServiceDefTarget#getServiceEntryPoint()
*/
public String getServiceEntryPoint() {
return remoteServiceURL;
}
public void setRpcRequestBuilder(RpcRequestBuilder builder) {
this.rpcRequestBuilder = builder;
}
/**
* @see ServiceDefTarget#setServiceEntryPoint(String)
*/
public void setServiceEntryPoint(String url) {
this.remoteServiceURL = url;
}
protected <T> RequestCallback doCreateRequestCallback(
ResponseReader responseReader, String methodName, RpcStatsContext statsContext,
AsyncCallback<T> callback) {
return new RequestCallbackAdapter<T>(this, methodName, statsContext,
callback, responseReader);
}
/**
* Performs a remote service method invocation. This method is called by
* generated proxy classes.
*
* @param <T> return type for the AsyncCallback
* @param responseReader instance used to read the return value of the
* invocation
* @param requestData payload that encodes the addressing and arguments of the
* RPC call
* @param callback callback handler
*
* @return a {@link Request} object that can be used to track the request
*/
protected <T> Request doInvoke(ResponseReader responseReader,
String methodName, RpcStatsContext statsContext, String requestData,
AsyncCallback<T> callback) {
RequestBuilder rb = doPrepareRequestBuilderImpl(responseReader, methodName,
statsContext, requestData, callback);
try {
return rb.send();
} catch (RequestException ex) {
InvocationException iex = new InvocationException(
"Unable to initiate the asynchronous service invocation -- check the network connection",
ex);
callback.onFailure(iex);
} finally {
boolean toss = statsContext.isStatsAvailable() &&
statsContext.stats(statsContext.bytesStat(methodName, requestData.length(),
"requestSent"));
}
return null;
}
/**
* Configures a RequestBuilder to send an RPC request when the RequestBuilder
* is intended to be returned through the asynchronous proxy interface.
*
* @param <T> return type for the AsyncCallback
* @param responseReader instance used to read the return value of the
* invocation
* @param requestData payload that encodes the addressing and arguments of the
* RPC call
* @param callback callback handler
*
* @return a RequestBuilder object that is ready to have its
* {@link RequestBuilder#send()} method invoked.
*/
protected <T> RequestBuilder doPrepareRequestBuilder(
ResponseReader responseReader, String methodName, RpcStatsContext statsContext,
String requestData, AsyncCallback<T> callback) {
RequestBuilder rb = doPrepareRequestBuilderImpl(responseReader, methodName,
statsContext, requestData, callback);
return rb;
}
/**
* Configures a RequestBuilder to send an RPC request.
*
* @param <T> return type for the AsyncCallback
* @param responseReader instance used to read the return value of the
* invocation
* @param requestData payload that encodes the addressing and arguments of the
* RPC call
* @param callback callback handler
*
* @return a RequestBuilder object that is ready to have its
* {@link RequestBuilder#send()} method invoked.
*/
private <T> RequestBuilder doPrepareRequestBuilderImpl(
ResponseReader responseReader, String methodName, RpcStatsContext statsContext,
String requestData, AsyncCallback<T> callback) {
if (getServiceEntryPoint() == null) {
throw new NoServiceEntryPointSpecifiedException();
}
RequestCallback responseHandler = doCreateRequestCallback(responseReader,
methodName, statsContext, callback);
ensureRpcRequestBuilder();
rpcRequestBuilder.create(getServiceEntryPoint());
rpcRequestBuilder.setCallback(responseHandler);
rpcRequestBuilder.setContentType(RPC_CONTENT_TYPE);
rpcRequestBuilder.setRequestData(requestData);
rpcRequestBuilder.setRequestId(statsContext.getRequestId());
return rpcRequestBuilder.finish();
}
private void ensureRpcRequestBuilder() {
if (rpcRequestBuilder == null) {
rpcRequestBuilder = new RpcRequestBuilder();
}
}
}