Hi,
today I wrote a simple 1-class-service-exporter which works much like
SL. Because the GWT code of RemoteServiceServlet has a lot
dependencies to ServletContext and not well-separated code, I had to
copy a lot of code :-(
Here's the (well working) result:
import java.io.ByteArrayOutputStream;
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 java.util.zip.GZIPOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.Ordered;
import org.springframework.core.io.Resource;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import
com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.server.rpc.RPC;
import com.google.gwt.user.server.rpc.RPCRequest;
import com.google.gwt.user.server.rpc.RPCServletUtils;
import com.google.gwt.user.server.rpc.SerializationPolicy;
import com.google.gwt.user.server.rpc.SerializationPolicyLoader;
import com.google.gwt.user.server.rpc.SerializationPolicyProvider;
public class RemoteServiceHandler implements HandlerMapping, Ordered,
Controller, SerializationPolicyProvider, ApplicationContextAware
{
private static final long serialVersionUID = 1L;
protected Log LOG=LogFactory.getLog(getClass());
protected int order;
public int getOrder()
{
return order;
}
public void setOrder(int order)
{
this.order = order;
}
protected String serviceUri;
@Required
public void setServiceUri(String serviceUri)
{
this.serviceUri = serviceUri;
}
protected RemoteService serviceBean;
@Required
public void setServiceBean(RemoteService serviceBean)
{
this.serviceBean = serviceBean;
}
protected HandlerExecutionChain chain=new HandlerExecutionChain
(this);
@Override
public HandlerExecutionChain getHandler(HttpServletRequest
request) throws Exception
{
if (serviceUri.equals(request.getPathInfo())) return chain;
return null;
}
protected ApplicationContext applicationContext;
@Autowired
public void setApplicationContext(ApplicationContext
applicationContext) throws BeansException
{
this.applicationContext=applicationContext;
}
////////////////////////////////////////////////////////////////////////////////////////////
/////
///// Modified GWT Code below
/////
////////////////////////////////////////////////////////////////////////////////////////////
@Override
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception
{
try
{
// Read the request fully.
//
String requestPayload = RPCServletUtils.readContentAsUtf8
(request, true);
// Invoke the core dispatching logic, which returns the
serialized
// result.
//
String responsePayload = processCall(requestPayload);
// Write the response.
//
writeResponse(request, response, responsePayload);
}
catch (Throwable e)
{
// Give a subclass a chance to either handle the exception
or rethrow it
//
doUnexpectedFailure(response,e);
}
return null;
}
protected static final String GENERIC_FAILURE_MSG = "The call
failed on the server; see server log for details";
protected void doUnexpectedFailure(HttpServletResponse response,
Throwable e)
{
LOG.warn("Exception while dispatching incoming RPC call", e);
// Send GENERIC_FAILURE_MSG with 500 status.
//
try
{
response.setContentType("text/plain");
response.setStatus
(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
response.getWriter().write(GENERIC_FAILURE_MSG);
}
catch (IOException ex)
{
LOG.warn("respondWithUnexpectedFailure failed while
sending the previous failure to the client",ex);
}
}
protected String processCall(String payload) throws
SerializationException
{
try
{
RPCRequest rpcRequest = RPC.decodeRequest(payload,
serviceBean.getClass(), this);
return RPC.invokeAndEncodeResponse(serviceBean,
rpcRequest.getMethod(),
rpcRequest.getParameters(),
rpcRequest.getSerializationPolicy());
}
catch (IncompatibleRemoteServiceException ex)
{
LOG.warn("An IncompatibleRemoteServiceException was thrown
while processing this call.",ex);
return RPC.encodeResponseForFailure(null, ex);
}
}
protected boolean shouldCompressResponse(HttpServletRequest
request,
HttpServletResponse response, String responsePayload)
{
return RPCServletUtils.exceedsUncompressedContentLengthLimit
(responsePayload);
}
private static final String CHARSET_UTF8 = "UTF-8";
private static final String CONTENT_ENCODING = "Content-Encoding";
private static final String CONTENT_ENCODING_GZIP = "gzip";
private static final String CONTENT_TYPE_APPLICATION_JSON_UTF8 =
"application/json; charset=utf-8";
private static final String CONTENT_DISPOSITION = "Content-
Disposition";
private static final String ATTACHMENT = "attachment";
private void writeResponse(HttpServletRequest request,
HttpServletResponse response, String responsePayload)
throws IOException
{
boolean gzipEncode = RPCServletUtils.acceptsGzipEncoding
(request)
&& shouldCompressResponse(request, response,
responsePayload);
byte[] responseBytes = responsePayload.getBytes
(CHARSET_UTF8);
if (gzipEncode)
{
// Compress the reply and adjust headers.
//
ByteArrayOutputStream output = null;
GZIPOutputStream gzipOutputStream = null;
Throwable caught = null;
try
{
output = new ByteArrayOutputStream
(responseBytes.length);
gzipOutputStream = new GZIPOutputStream(output);
gzipOutputStream.write(responseBytes);
gzipOutputStream.finish();
gzipOutputStream.flush();
response.setHeader(CONTENT_ENCODING,
CONTENT_ENCODING_GZIP);
responseBytes = output.toByteArray();
}
catch (IOException e)
{
caught = e;
}
finally
{
if (null != gzipOutputStream)
{
gzipOutputStream.close();
}
if (null != output)
{
output.close();
}
}
if (caught != null)
{
LOG.warn("Unable to compress response", caught);
response.sendError
(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return;
}
}
// Send the reply.
//
response.setContentLength(responseBytes.length);
response.setContentType(CONTENT_TYPE_APPLICATION_JSON_UTF8);
response.setStatus(HttpServletResponse.SC_OK);
response.setHeader(CONTENT_DISPOSITION, ATTACHMENT);
response.getOutputStream().write(responseBytes);
}
private final Map<String, SerializationPolicy>
serializationPolicyCache = new HashMap<String, SerializationPolicy>();
public final SerializationPolicy getSerializationPolicy(String
moduleBaseURL, String strongName)
{
SerializationPolicy serializationPolicy =
getCachedSerializationPolicy(moduleBaseURL, strongName);
if (serializationPolicy != null)
{
return serializationPolicy;
}
serializationPolicy = doGetSerializationPolicy
(moduleBaseURL, strongName);
if (serializationPolicy == null) {
// Failed to get the requested serialization policy; use
the default
LOG.warn(
"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;
}
protected SerializationPolicy doGetSerializationPolicy(String
moduleBaseURL, String strongName)
{
String modulePath = null;
if (moduleBaseURL != null) {
try {
modulePath = new URL(moduleBaseURL).getPath();
} catch (MalformedURLException ex) {
// log the information, we will default
LOG.warn("Malformed moduleBaseURL: " + moduleBaseURL,
ex);
return null;
}
}
String serializationPolicyFilePath =
SerializationPolicyLoader.getSerializationPolicyFileName(modulePath +
strongName);
Resource
serializationPolicyResource=applicationContext.getResource
(serializationPolicyFilePath);
if (!serializationPolicyResource.exists())
{
String message = "ERROR: The serialization policy file
'"
+ serializationPolicyFilePath
+ "' was not found; did you forget to include it in
this deployment?";
LOG.warn(message);
return null;
}
SerializationPolicy serializationPolicy = null;
// Open the RPC resource file read its contents.
try {
InputStream is =
serializationPolicyResource.getInputStream();
try {
serializationPolicy =
SerializationPolicyLoader.loadFromStream(is,
null);
} catch (ParseException e) {
LOG.warn(
"ERROR: Failed to parse the policy file '"
+ serializationPolicyFilePath + "'", e);
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
// Ignore this error
}
}
}
}
catch (IOException ex) {
LOG.warn(
"ERROR: Could not read the policy file '"
+ serializationPolicyFilePath + "'", ex);
}
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);
}
}
}
Usage:
<bean class="RemoteServiceHandler">
<property name="serviceUri" value="/service"/>
<property name="serviceBean" ref="backendImpl"/>
</bean>
Advantages:
- simple on-class copy'n'paste solution
- reads serialization policy as spring resource (should also work in
unit test environments)
- no ThreadLocal binding of Request/Response required
Disadvantages:
- much code copied and modified (changed getServletContext.log to
Logger.warn, ...)
- The service interface still needs to implement RemoteService
- quick'n'dirty solution.
Regards,
Michael.
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"Google Web Toolkit" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/Google-Web-Toolkit?hl=en
-~----------~----~----~----~------~----~------~--~---