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
-~----------~----~----~----~------~----~------~--~---

Reply via email to