blob: 6e75a04ce2d3ecfa27622a572548cbe8b6b4b544 [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.http.client.Request;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.Response;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
import com.google.gwt.user.client.rpc.InvocationException;
import com.google.gwt.user.client.rpc.RpcTokenException;
import com.google.gwt.user.client.rpc.RpcTokenExceptionHandler;
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.StatusCodeException;
/**
* Adapter from a {@link RequestCallback} interface to an {@link AsyncCallback}
* interface.
*
* For internal use only.
*
* @param <T> the type parameter for the {@link AsyncCallback}
*/
public class RequestCallbackAdapter<T> implements RequestCallback {
/**
* Enumeration used to read specific return types out of a
* {@link SerializationStreamReader}.
*/
public enum ResponseReader {
BOOLEAN {
@Override
public Object read(SerializationStreamReader streamReader)
throws SerializationException {
return streamReader.readBoolean();
}
},
BYTE {
@Override
public Object read(SerializationStreamReader streamReader)
throws SerializationException {
return streamReader.readByte();
}
},
CHAR {
@Override
public Object read(SerializationStreamReader streamReader)
throws SerializationException {
return streamReader.readChar();
}
},
DOUBLE {
@Override
public Object read(SerializationStreamReader streamReader)
throws SerializationException {
return streamReader.readDouble();
}
},
FLOAT {
@Override
public Object read(SerializationStreamReader streamReader)
throws SerializationException {
return streamReader.readFloat();
}
},
INT {
@Override
public Object read(SerializationStreamReader streamReader)
throws SerializationException {
return streamReader.readInt();
}
},
LONG {
@Override
public Object read(SerializationStreamReader streamReader)
throws SerializationException {
return streamReader.readLong();
}
},
OBJECT {
@Override
public Object read(SerializationStreamReader streamReader)
throws SerializationException {
return streamReader.readObject();
}
},
SHORT {
@Override
public Object read(SerializationStreamReader streamReader)
throws SerializationException {
return streamReader.readShort();
}
},
STRING {
@Override
public Object read(SerializationStreamReader streamReader)
throws SerializationException {
return streamReader.readString();
}
},
VOID {
@Override
public Object read(SerializationStreamReader streamReader) {
return null;
}
};
public abstract Object read(SerializationStreamReader streamReader)
throws SerializationException;
}
/**
* {@link AsyncCallback} to notify or success or failure.
*/
private final AsyncCallback<T> callback;
/**
* Used for stats recording.
*/
private final String methodName;
/**
* Used for stats recording.
*/
private final RpcStatsContext statsContext;
/**
* Instance which will read the expected return type out of the
* {@link SerializationStreamReader}.
*/
private final ResponseReader responseReader;
/**
* {@link RpcTokenExceptionHandler} to notify of token exceptions.
*/
private final RpcTokenExceptionHandler tokenExceptionHandler;
/**
* {@link SerializationStreamFactory} for creating
* {@link SerializationStreamReader}s.
*/
private final SerializationStreamFactory streamFactory;
public RequestCallbackAdapter(SerializationStreamFactory streamFactory,
String methodName, RpcStatsContext statsContext,
AsyncCallback<T> callback, ResponseReader responseReader) {
this(streamFactory, methodName, statsContext, callback, null,
responseReader);
}
public RequestCallbackAdapter(SerializationStreamFactory streamFactory,
String methodName, RpcStatsContext statsContext,
AsyncCallback<T> callback,
RpcTokenExceptionHandler tokenExceptionHandler,
ResponseReader responseReader) {
assert (streamFactory != null);
assert (callback != null);
assert (responseReader != null);
this.streamFactory = streamFactory;
this.callback = callback;
this.methodName = methodName;
this.statsContext = statsContext;
this.responseReader = responseReader;
this.tokenExceptionHandler = tokenExceptionHandler;
}
public void onError(Request request, Throwable exception) {
callback.onFailure(exception);
}
@SuppressWarnings(value = {"unchecked", "unused"})
public void onResponseReceived(Request request, Response response) {
T result = null;
Throwable caught = null;
try {
String encodedResponse = response.getText();
int statusCode = response.getStatusCode();
boolean toss = statsContext.isStatsAvailable()
&& statsContext.stats(
statsContext.bytesStat(methodName, encodedResponse.length(), "responseReceived"));
if (statusCode != Response.SC_OK) {
caught = new StatusCodeException(statusCode, encodedResponse);
} else if (encodedResponse == null) {
// This can happen if the XHR is interrupted by the server dying
caught = new InvocationException("No response payload from " + methodName);
} else if (RemoteServiceProxy.isReturnValue(encodedResponse)) {
result = (T) responseReader.read(streamFactory.createStreamReader(encodedResponse));
} else if (RemoteServiceProxy.isThrownException(encodedResponse)) {
caught = (Throwable) streamFactory.createStreamReader(encodedResponse).readObject();
} else {
caught = new InvocationException(encodedResponse + " from " + methodName);
}
} catch (com.google.gwt.user.client.rpc.SerializationException e) {
caught = new IncompatibleRemoteServiceException(
"The response could not be deserialized", e);
} catch (Throwable e) {
caught = e;
} finally {
boolean toss = statsContext.isStatsAvailable()
&& statsContext.stats(statsContext.timeStat(methodName, "responseDeserialized"));
}
try {
if (caught == null) {
callback.onSuccess(result);
} else if (tokenExceptionHandler != null &&
caught instanceof RpcTokenException) {
tokenExceptionHandler.onRpcTokenException((RpcTokenException) caught);
} else {
callback.onFailure(caught);
}
} finally {
Object returned = (caught == null) ? result : caught;
boolean toss = statsContext.isStatsAvailable()
&& statsContext.stats(statsContext.timeStat(methodName, returned, "end"));
}
}
}