MultiServlet.java :  » Web-Services » Gomba » org » gomba » Java Open Source

Java Open Source » Web Services » Gomba 
Gomba » org » gomba » MultiServlet.java
package org.gomba;

import java.io.CharArrayWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

import org.gomba.utils.servlet.ServletParameterUtils;

/**
 * This servlets dispatches the same request to mutiple servlets overriding the
 * original HTTP method and capturing the output of each servlet into the
 * request scope. The output of the last operation is sent directly the client.
 * 
 * <p>
 * This servlet inherits the init-params of
 * {@link org.gomba.TransactorAbstractServlet}
 * </p>
 * 
 * <p>
 * Init-params:
 * <dl>
 * <dt>http-method</dt>
 * <dd>The value can be GET, POST, PUT or DELETE. (Required)</dd>
 * <dt>operations</dt>
 * <dd>A list of operations to execute, one per line. Syntax:
 * <code>HTTPMethod ResourceNameOrPath [RequestAttributeName]</code>.
 * Example: <code>GET myServlet</code>. (Required)</dd>
 * </dl>
 * </p>
 * 
 * @author Flavio Tordini
 * @version $Id: MultiServlet.java,v 1.1 2004/11/26 17:52:58 flaviotordini Exp $
 */
public class MultiServlet extends TransactorAbstractServlet {

    /**
     * <code>true</code> if this servlet supports the GET HTTP method.
     */
    private boolean supportGet;

    /**
     * <code>true</code> if this servlet supports the POST HTTP method.
     */
    private boolean supportPost;

    /**
     * <code>true</code> if this servlet supports the PUT HTTP method.
     */
    private boolean supportPut;

    /**
     * <code>true</code> if this servlet supports the DELETE HTTP method.
     */
    private boolean supportDelete;

    /**
     * List of MultiServlet.Operation.
     */
    private List operations;

    /**
     * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig)
     */
    public void init(ServletConfig config) throws ServletException {
        super.init(config);

        // supported HTTP method
        String httpMethod = config.getInitParameter("http-method");
        if (httpMethod == null) {
            throw new ServletException("Missing init-param: http-method");
        }
        if (httpMethod.equals("GET")) {
            this.supportGet = true;
        } else if (httpMethod.equals("POST")) {
            this.supportPost = true;
        } else if (httpMethod.equals("PUT")) {
            this.supportPut = true;
        } else if (httpMethod.equals("DELETE")) {
            this.supportDelete = true;
        } else {
            throw new ServletException("Unsupported HTTP method: " + httpMethod);
        }

        // parse operation list
        String operationStr = config.getInitParameter("operations");
        if (operationStr == null) {
            throw new ServletException("Missing init-param: operations");
        }
        this.operations = parseOperations(operationStr);

    }

    /**
     * Parse an operation line.
     */
    private static Operation parseOperationLine(String line)
            throws ServletException {
        StringTokenizer tokenizer = new StringTokenizer(line, " ");
        String[] tokens = new String[3];
        int i = 0;
        for (; i < tokens.length && tokenizer.hasMoreTokens(); i++) {
            String token = tokenizer.nextToken();
            tokens[i] = token;
        }
        if (i < 1) {
            throw new ServletException("Not enough tokens on this line: "
                    + line);
        }
        if (tokenizer.hasMoreTokens()) {
            throw new ServletException("Too many tokens on this line: " + line);
        }

        Operation operation = new Operation(tokens[0], tokens[1], tokens[2]);

        return operation;
    }

    /**
     * Parse operations config: HTTPMethod nameOrPath [varName]
     */
    private static List parseOperations(String operations)
            throws ServletException {

        List operationList = new ArrayList();
        int currentPos = 0;

        do {

            int nextLineFeed = operations.indexOf('\n', currentPos + 1);
            String line;
            if (nextLineFeed != -1) {
                line = operations.substring(currentPos, nextLineFeed);
            } else {
                line = operations.substring(currentPos);
            }

            Operation operation = parseOperationLine(line.trim());
            operationList.add(operation);

            currentPos = nextLineFeed;

        } while (currentPos != -1);

        return operationList;
    }

    /**
     * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest,
     *           javax.servlet.http.HttpServletResponse)
     */
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        if (this.supportGet) {
            processRequest(request, response);
        } else {
            response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
        }
    }

    /**
     * @see javax.servlet.http.HttpServlet#doDelete(javax.servlet.http.HttpServletRequest,
     *           javax.servlet.http.HttpServletResponse)
     */
    protected void doDelete(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        if (this.supportDelete) {
            processRequest(request, response);
        } else {
            response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
        }
    }

    /**
     * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest,
     *           javax.servlet.http.HttpServletResponse)
     */
    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        if (this.supportPost) {
            processRequest(request, response);
        } else {
            response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
        }
    }

    /**
     * @see javax.servlet.http.HttpServlet#doPut(javax.servlet.http.HttpServletRequest,
     *           javax.servlet.http.HttpServletResponse)
     */
    protected void doPut(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        if (this.supportPut) {
            processRequest(request, response);
        } else {
            response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
        }
    }

    /**
     * Do the real work.
     */
    private void processRequest(HttpServletRequest request,
            HttpServletResponse response) throws ServletException {

        final ServletContext context = getServletContext();

        // get a TransactionServlet instance
        TransactionServlet transactionServlet = (TransactionServlet) getServletContext()
                .getAttribute(TransactionServlet.CONTEXT_ATTRIBUTE_NAME_SERVLET);
        if (transactionServlet == null) {
            throw new ServletException("Missing TransactionServlet.");
        }

        // create the parameter resolver that will help us throughout this
        // request
        final ParameterResolver parameterResolver = new ParameterResolver(
                request);

        // this flag is true if we create our own transaction, instead of
        // using a transaction specified by the client
        boolean implicitTransaction = false;

        Transaction transaction = null;
        try {

            // start the transaction
            transaction = getTransaction(parameterResolver);
            if (transaction == null) {
                transaction = transactionServlet.createTransaction();
                implicitTransaction = true;
            }

            // put the transaction in the request scope so AbstractServlet knows
            // that requests are part of the transaction.
            request.setAttribute(REQUEST_ATTRIBUTE_NAME_TRANSACTION,
                    transaction);

            // loop through operations
            for (Iterator i = this.operations.iterator(); i.hasNext();) {
                Operation operation = (Operation) i.next();

                // get the dispatcher
                RequestDispatcher dispatcher = ServletParameterUtils
                        .getRequestDispatcher(context, operation
                                .getResourceNameOrPath());

                // wrap our request in order to override the HTTP method
                RequestWrapper requestWrapper = new RequestWrapper(request,
                        operation.getHttpMethod());

                // if this is the last operation just forward the request
                if (!i.hasNext()) {
                    dispatcher.forward(requestWrapper, response);
                    break;
                }

                // wrap our response in order to capture its body
                ResponseWrapper responseWrapper = new ResponseWrapper(response);

                // otherwise process this operation
                dispatcher.include(requestWrapper, responseWrapper);

                // check for successful response
                int responseStatus = responseWrapper.getStatus();
                if (responseStatus < 200 || responseStatus > 299) {
                    throw new ServletException("Operation "
                            + operation.getResourceNameOrPath()
                            + " returned HTTP status " + responseStatus);
                }

                // put the result in request scope
                String opName = operation.getName();
                if (opName != null && opName.length() > 0) {
                    String responseBody = responseWrapper.getBody();
                    request.setAttribute(opName, responseBody);
                }

            }

            // commit the transaction
            if (implicitTransaction) {
                transaction.commit();
            }

        } catch (Exception e) {

            // rollback the transaction
            // if the transaction was started by the client, it is the client
            // responsibility to rollback it.
            if (transaction != null && implicitTransaction) {
                try {
                    transaction.rollback();
                } catch (Exception e2) {
                    log("Error rolling back transaction.", e2);
                }
            }
            throw new ServletException("Error processing request.", e);

        }

    }

    private static class Operation {

        private final String httpMethod;

        private final String resourceNameOrPath;

        private final String name;

        /**
         * Constructor.
         * 
         * @param httpMethod
         *                   The HTTP method to use.
         * @param resourceNameOrPath
         *                   a resource to dispatch the request to. May be the resource
         *                   name or the resource path.
         * @param name
         *                   The optional request attribute name to hold the response
         *                   body.
         */
        private Operation(final String httpMethod,
                final String resourceNameOrPath, final String name) {
            this.httpMethod = httpMethod;
            this.resourceNameOrPath = resourceNameOrPath;
            this.name = name;
        }

        /**
         * @return Returns the httpMethod.
         */
        public String getHttpMethod() {
            return this.httpMethod;
        }

        /**
         * @return Returns the name.
         */
        public String getName() {
            return this.name;
        }

        /**
         * @return Returns the resourceNameOrPath.
         */
        public String getResourceNameOrPath() {
            return this.resourceNameOrPath;
        }
    }

    /**
     * Wraps a HttpServletRequest and override the HTTP method.
     */
    private static class RequestWrapper extends HttpServletRequestWrapper {

        // TODO add support for in-memory request body
        // in order to let multiple servlets use the request body.

        private final String httpMethod;

        private RequestWrapper(HttpServletRequest request, String httpMethod) {
            super(request);
            this.httpMethod = httpMethod;
        }

        /**
         * @see javax.servlet.http.HttpServletRequest#getMethod()
         */
        public String getMethod() {
            // System.out.println("getMethod() " + this.httpMethod);
            return this.httpMethod;
        }

    }

    /**
     * Wraps a HttpServletResponse in order to capture its response body.
     */
    private static class ResponseWrapper extends HttpServletResponseWrapper {

        private Writer writer;

        private int status = SC_OK;

        /**
         * Constructor.
         */
        private ResponseWrapper(HttpServletResponse response) {
            super(response);

        }

        /**
         * @see javax.servlet.ServletResponse#getOutputStream()
         */
        public ServletOutputStream getOutputStream() throws IOException {
            // TODO implement
            throw new UnsupportedOperationException(
                    "This implementation does not yet support getOutputStream().");
        }

        /**
         * @see javax.servlet.ServletResponse#getWriter()
         */
        public PrintWriter getWriter() throws IOException {
            if (this.writer != null) {
                throw new IOException("PrintWriter has already been obtained.");
            }
            // we could also use a StringWriter...
            // what's the difference?
            this.writer = new CharArrayWriter();
            return new PrintWriter(this.writer);
        }

        /**
         * @return The body of this response.
         */
        private String getBody() {
            return this.writer.toString();
        }

        /**
         * @see javax.servlet.http.HttpServletResponseWrapper#setStatus(int)
         */
        public void setStatus(int status) {
            super.setStatus(status);
            this.status = status;
        }

        /**
         * @see javax.servlet.http.HttpServletResponse#setStatus(int,
         *           java.lang.String)
         */
        public void setStatus(int status, String message) {
            super.setStatus(status, message);
            this.status = status;
        }

        /**
         * @see javax.servlet.http.HttpServletResponse#sendError(int,
         *           java.lang.String)
         */
        public void sendError(int status, String message) throws IOException {
            super.sendError(status, message);
            this.status = status;
        }

        /**
         * @see javax.servlet.http.HttpServletResponse#sendError(int)
         */
        public void sendError(int status) throws IOException {
            super.sendError(status);
            this.status = status;
        }

        /**
         * @return Returns the status.
         */
        protected int getStatus() {
            return this.status;
        }
    }

}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.