com.google.gwt.user.server.rpc.AbstractRemoteServiceServlet.java Source code

Java tutorial

Introduction

Here is the source code for com.google.gwt.user.server.rpc.AbstractRemoteServiceServlet.java

Source

/*
 * Copyright 2009 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.server.rpc;

import static com.google.gwt.user.client.rpc.RpcRequestBuilder.STRONG_NAME_HEADER;

import java.io.IOException;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * An abstract base class containing utility methods.
 */
public abstract class AbstractRemoteServiceServlet extends HttpServlet {

    protected transient ThreadLocal<HttpServletRequest> perThreadRequest;
    protected transient ThreadLocal<HttpServletResponse> perThreadResponse;

    public AbstractRemoteServiceServlet() {
        super();
    }

    /**
     * Standard HttpServlet method: handle the POST. Delegates to
     * {@link #processPost(HttpServletRequest, HttpServletResponse)}.
     * 
     * This doPost method swallows ALL exceptions, logs them in the
     * ServletContext, and returns a GENERIC_FAILURE_MSG response with status code
     * 500.
     */
    @Override
    public final void doPost(HttpServletRequest request, HttpServletResponse response) {
        // Ensure the thread-local data fields have been initialized

        try {
            // Store the request & response objects in thread-local storage.
            //
            synchronized (this) {
                validateThreadLocalData();
                perThreadRequest.set(request);
                perThreadResponse.set(response);
            }

            processPost(request, response);

        } catch (Throwable e) {
            // Give a subclass a chance to either handle the exception or rethrow it
            //
            doUnexpectedFailure(e);
        } finally {
            // null the thread-locals to avoid holding request/response
            //
            perThreadRequest.set(null);
            perThreadResponse.set(null);
        }
    }

    /**
     * Override this method to control what should happen when an exception
     * escapes the {@link #doPost} method. The default implementation will log the
     * failure and send a generic failure response to the client.
     * <p>
     * An "expected failure" is an exception thrown by a service method that is
     * declared in the signature of the service method. These exceptions are
     * serialized back to the client, and are not passed to this method. This
     * method is called only for exceptions or errors that are not part of the
     * service method's signature, or that result from SecurityExceptions,
     * SerializationExceptions, or other failures within the RPC framework.
     * <p>
     * Note that if the desired behavior is to both send the GENERIC_FAILURE_MSG
     * response AND to rethrow the exception, then this method should first send
     * the GENERIC_FAILURE_MSG response itself (using getThreadLocalResponse), and
     * then rethrow the exception. Rethrowing the exception will cause it to
     * escape into the servlet container.
     * 
     * @param e the exception which was thrown
     */
    protected void doUnexpectedFailure(Throwable e) {
        try {
            getThreadLocalResponse().reset();
        } catch (IllegalStateException ex) {
            /*
             * If we can't reset the request, the only way to signal that something
             * has gone wrong is to throw an exception from here. It should be the
             * case that we call the user's implementation code before emitting data
             * into the response, so the only time that gets tripped is if the object
             * serialization code blows up.
             */
            throw new RuntimeException("Unable to report failure", e);
        }
        ServletContext servletContext = getServletContext();
        RPCServletUtils.writeResponseForUnexpectedFailure(servletContext, getThreadLocalResponse(), e);
    }

    /**
     * Returns the strong name of the permutation, as reported by the client that
     * issued the request, or <code>null</code> if it could not be determined.
     * This information is encoded in the
     * {@value com.google.gwt.user.client.rpc.RpcRequestBuilder#STRONG_NAME_HEADER}
     * HTTP header.
     */
    protected final String getPermutationStrongName() {
        return getThreadLocalRequest().getHeader(STRONG_NAME_HEADER);
    }

    /**
     * Gets the <code>HttpServletRequest</code> object for the current call. It is
     * stored thread-locally so that simultaneous invocations can have different
     * request objects.
     */
    protected final HttpServletRequest getThreadLocalRequest() {
        synchronized (this) {
            validateThreadLocalData();
            return perThreadRequest.get();
        }
    }

    /**
     * Gets the <code>HttpServletResponse</code> object for the current call. It
     * is stored thread-locally so that simultaneous invocations can have
     * different response objects.
     */
    protected final HttpServletResponse getThreadLocalResponse() {
        synchronized (this) {
            validateThreadLocalData();
            return perThreadResponse.get();
        }
    }

    /**
     * Override this method to examine the deserialized version of the request
     * before the call to the servlet method is made. The default implementation
     * does nothing and need not be called by subclasses.
     * 
     * @param rpcRequest
     */
    protected void onAfterRequestDeserialized(RPCRequest rpcRequest) {
    }

    /**
     * Called by {@link #doPost} for type-specific processing of the request.
     * Because <code>doPost</code> swallows all <code>Throwables</code>, this
     * method may throw any exception the implementor wishes.
     */
    protected abstract void processPost(HttpServletRequest request, HttpServletResponse response) throws Throwable;

    /**
     * Override this method in order to control the parsing of the incoming
     * request. For example, you may want to bypass the check of the Content-Type
     * and character encoding headers in the request, as some proxies re-write the
     * request headers. Note that bypassing these checks may expose the servlet to
     * some cross-site vulnerabilities. Your implementation should comply with the
     * HTTP/1.1 specification, which includes handling both requests which include
     * a Content-Length header and requests utilizing <code>Transfer-Encoding:
     * chuncked</code>.
     * 
     * @param request the incoming request
     * @return the content of the incoming request encoded as a string.
     */
    protected String readContent(HttpServletRequest request) throws ServletException, IOException {
        return RPCServletUtils.readContentAsGwtRpc(request);
    }

    /**
     * Initializes the perThreadRequest and perThreadResponse fields if they are
     * null. This will occur the first time they are accessed after an instance of
     * this class is constructed or deserialized. This method should be called
     * from within a 'synchronized(this) {}' block in order to ensure that only
     * one thread creates the objects.
     */
    private void validateThreadLocalData() {
        if (perThreadRequest == null) {
            perThreadRequest = new ThreadLocal<HttpServletRequest>();
        }
        if (perThreadResponse == null) {
            perThreadResponse = new ThreadLocal<HttpServletResponse>();
        }
    }
}