package org.jboss.seam.remoting.gwt;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.ParseException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jboss.seam.core.ConversationPropagation;
import org.jboss.seam.log.LogProvider;
import org.jboss.seam.log.Logging;
import org.jboss.seam.servlet.ContextualHttpServletRequest;
import org.jboss.seam.web.AbstractResource;
import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.server.rpc.RPC;
import com.google.gwt.user.server.rpc.RPCServletUtils;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.google.gwt.user.server.rpc.SerializationPolicy;
import com.google.gwt.user.server.rpc.SerializationPolicyLoader;
import com.google.gwt.user.server.rpc.SerializationPolicyProvider;
import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader;
import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter;
/**
* Abstract base class for GWT 1.5 integration.
*
* @author Shane Bryzak
*/
public abstract class GWTService extends AbstractResource implements SerializationPolicyProvider {
protected static final LogProvider log = Logging.getLogProvider(GWTService.class);
/**
* A cache of moduleBaseURL and serialization policy strong name to
* {@link SerializationPolicy}.
*/
private final Map<String, SerializationPolicy> serializationPolicyCache = new HashMap<String, SerializationPolicy>();
@Override
public String getResourcePath() {
return "/gwt";
}
protected abstract ServerSerializationStreamReader getStreamReader();
protected abstract ServerSerializationStreamWriter getStreamWriter();
@SuppressWarnings("unchecked")
protected abstract String createResponse(ServerSerializationStreamWriter stream, Class responseType,
Object responseObj, boolean isException);
// private final Set knownImplementedInterfaces = new HashSet();
private final ThreadLocal<HttpServletRequest> perThreadRequest = new ThreadLocal<HttpServletRequest>();
private final ThreadLocal<HttpServletResponse> perThreadResponse = new ThreadLocal<HttpServletResponse>();
/**
* This is called internally.
*
* @see RemoteServiceServlet#doPost
*/
@Override
public final void getResource(final HttpServletRequest request, final HttpServletResponse response)
throws ServletException, IOException
{
try {
// Store the request & response objects in thread-local storage.
perThreadRequest.set(request);
perThreadResponse.set(response);
new ContextualHttpServletRequest(request) {
@Override
public void process() throws Exception {
try {
// Read the request fully.
//
String requestPayload = RemoteServiceServlet_readContent(request);
RemoteServiceServlet_onBeforeRequestDeserialized(requestPayload);
// Invoke the core dispatching logic, which returns the
// serialized result
String responsePayload = processCall(requestPayload);
RemoteServiceServlet_onAfterResponseSerialized(responsePayload);
// Write the response.
//
RemoteServiceServlet_writeResponse(request, response, responsePayload);
} catch (Throwable e) {
RemoteServiceServlet_doUnexpectedFailure(e);
}
}
@Override
protected void restoreConversationId() {
ConversationPropagation.instance().setConversationId(
GWTService.this.perThreadRequest.get().getParameter("conversationId"));
}
@Override
protected void handleConversationPropagation() {
}
}.run();
} finally {
perThreadRequest.remove();
perThreadResponse.remove();
}
}
public String processCall(String payload) throws SerializationException {
try {
SeamRPCRequest rpcRequest = SeamRPC.decodeRequest(payload, this.getClass(), this);
return SeamRPC.invokeAndEncodeResponse(
this, rpcRequest.getMethod(), rpcRequest.getParameterTypes(), rpcRequest.getParameters(), rpcRequest.getSerializationPolicy());
} catch (IncompatibleRemoteServiceException ex) {
getServletContext().log("An IncompatibleRemoteServiceException was thrown while processing this call.", ex);
return RPC.encodeResponseForFailure(null, ex);
}
}
protected final HttpServletRequest getThreadLocalRequest() {
return perThreadRequest.get();
}
protected final HttpServletResponse getThreadLocalResponse() {
return perThreadResponse.get();
}
protected void RemoteServiceServlet_onAfterResponseSerialized(String serializedResponse) {
}
protected void RemoteServiceServlet_onBeforeRequestDeserialized(String serializedRequest) {
}
protected String RemoteServiceServlet_readContent(HttpServletRequest request) throws ServletException, IOException {
return RPCServletUtils.readContentAsUtf8(request, true);
}
public final SerializationPolicy getSerializationPolicy(String moduleBaseURL, String strongName) {
SerializationPolicy serializationPolicy = getCachedSerializationPolicy(moduleBaseURL, strongName);
if (serializationPolicy != null) {
return serializationPolicy;
}
serializationPolicy = doGetSerializationPolicy(getThreadLocalRequest(), moduleBaseURL, strongName);
if (serializationPolicy == null) {
// Failed to get the requested serialization policy; use the default
getServletContext().log(
"WARNING: Failed to get the SerializationPolicy '"
+ strongName
+ "' for module '"
+ moduleBaseURL
+ "'; a legacy, 1.3.3 compatible, serialization policy will be used. You may experience SerializationExceptions as a result.");
serializationPolicy = RPC.getDefaultSerializationPolicy();
}
// This could cache null or an actual instance. Either way we will not
// attempt to lookup the policy again.
putCachedSerializationPolicy(moduleBaseURL, strongName,
serializationPolicy);
return serializationPolicy;
}
private SerializationPolicy getCachedSerializationPolicy(
String moduleBaseURL, String strongName) {
synchronized (serializationPolicyCache) {
return serializationPolicyCache.get(moduleBaseURL + strongName);
}
}
private void putCachedSerializationPolicy(String moduleBaseURL,
String strongName, SerializationPolicy serializationPolicy) {
synchronized (serializationPolicyCache) {
serializationPolicyCache.put(moduleBaseURL + strongName,
serializationPolicy);
}
}
protected SerializationPolicy doGetSerializationPolicy(
HttpServletRequest request, String moduleBaseURL, String strongName) {
// The request can tell you the path of the web app relative to the
// container root.
String contextPath = request.getContextPath();
String modulePath = null;
if (moduleBaseURL != null) {
try {
modulePath = new URL(moduleBaseURL).getPath();
} catch (MalformedURLException ex) {
// log the information, we will default
getServletContext().log(
"Malformed moduleBaseURL: " + moduleBaseURL, ex);
}
}
SerializationPolicy serializationPolicy = null;
/*
* Check that the module path must be in the same web app as the servlet
* itself. If you need to implement a scheme different than this, override
* this method.
*/
if (modulePath == null || !modulePath.startsWith(contextPath)) {
String message = "ERROR: The module path requested, "
+ modulePath
+ ", is not in the same web application as this servlet, "
+ contextPath
+ ". Your module may not be properly configured or your client and server code maybe out of date.";
getServletContext().log(message);
} else {
// Strip off the context path from the module base URL. It should be a
// strict prefix.
String contextRelativePath = modulePath
.substring(contextPath.length());
String serializationPolicyFilePath = SerializationPolicyLoader
.getSerializationPolicyFileName(contextRelativePath + strongName);
// Open the RPC resource file read its contents.
InputStream is = getServletContext().getResourceAsStream(
serializationPolicyFilePath);
try {
if (is != null) {
try {
serializationPolicy = SerializationPolicyLoader
.loadFromStream(is, null);
} catch (ParseException e) {
getServletContext().log(
"ERROR: Failed to parse the policy file '"
+ serializationPolicyFilePath + "'", e);
} catch (IOException e) {
getServletContext().log(
"ERROR: Could not read the policy file '"
+ serializationPolicyFilePath + "'", e);
}
} else {
String message = "ERROR: The serialization policy file '"
+ serializationPolicyFilePath
+ "' was not found; did you forget to include it in this deployment?";
getServletContext().log(message);
}
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
// Ignore this error
}
}
}
}
return serializationPolicy;
}
private void RemoteServiceServlet_writeResponse(HttpServletRequest request,
HttpServletResponse response, String responsePayload)
throws IOException {
boolean gzipEncode = RPCServletUtils.acceptsGzipEncoding(request)
&& shouldCompressResponse(request, response, responsePayload);
RPCServletUtils.writeResponse(getServletContext(), response,
responsePayload, gzipEncode);
}
protected void RemoteServiceServlet_doUnexpectedFailure(Throwable e) {
ServletContext servletContext = getServletContext();
RPCServletUtils.writeResponseForUnexpectedFailure(servletContext,
getThreadLocalResponse(), e);
}
protected boolean shouldCompressResponse(HttpServletRequest request, HttpServletResponse response, String responsePayload) {
return RPCServletUtils.exceedsUncompressedContentLengthLimit(responsePayload);
}
}