com.twinsoft.convertigo.engine.servlets.GenericServlet.java Source code

Java tutorial

Introduction

Here is the source code for com.twinsoft.convertigo.engine.servlets.GenericServlet.java

Source

/*
 * Copyright (c) 2001-2011 Convertigo SA.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License
 * as published by the Free Software Foundation; either version 3
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see<http://www.gnu.org/licenses/>.
 *
 * $URL$
 * $Author$
 * $Revision$
 * $Date$
 */

package com.twinsoft.convertigo.engine.servlets;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.xml.soap.SOAPMessage;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.w3c.dom.Document;

import com.twinsoft.convertigo.engine.AttachmentManager.AttachmentDetails;
import com.twinsoft.convertigo.engine.Engine;
import com.twinsoft.convertigo.engine.EnginePropertiesManager;
import com.twinsoft.convertigo.engine.EnginePropertiesManager.PropertyName;
import com.twinsoft.convertigo.engine.enums.HeaderName;
import com.twinsoft.convertigo.engine.enums.MimeType;
import com.twinsoft.convertigo.engine.enums.Parameter;
import com.twinsoft.convertigo.engine.requesters.Requester;
import com.twinsoft.convertigo.engine.requesters.ServletRequester;
import com.twinsoft.convertigo.engine.requesters.WebServiceServletRequester;
import com.twinsoft.convertigo.engine.util.FileUtils;
import com.twinsoft.convertigo.engine.util.GenericUtils;
import com.twinsoft.convertigo.engine.util.HttpServletRequestTwsWrapper;
import com.twinsoft.convertigo.engine.util.SOAPUtils;
import com.twinsoft.convertigo.engine.util.ServletUtils;
import com.twinsoft.convertigo.engine.util.XMLUtils;

public abstract class GenericServlet extends HttpServlet {

    private static final long serialVersionUID = -6386155197912471410L;

    public GenericServlet() {
    }

    protected void handleStaticData(HttpServletRequest request, HttpServletResponse response) {
        String resourceUri = request.getServletPath();
        Engine.logContext.debug("Serving static ressource: " + resourceUri);

        // TODO: enhance to support content types according to file extension
        if (resourceUri.endsWith(".xml") || resourceUri.endsWith(".cxml") || resourceUri.endsWith(".pxml"))
            response.setContentType(MimeType.TextXml.value());
        else
            response.setContentType(MimeType.Html.value());

        try {
            InputStream is = getServletContext().getResourceAsStream(resourceUri);
            if (is == null) {
                response.sendError(HttpServletResponse.SC_NOT_FOUND,
                        "Static resource " + resourceUri + " not found");
                return;
            }

            byte array[] = new byte[4096];

            OutputStream os = response.getOutputStream();
            while (is.available() != 0) {
                int nb = is.read(array);
                os.write(array, 0, nb);
            }
            os.flush();

        } catch (IOException e) {
            Engine.logContext.trace("Error serving static resource: " + resourceUri);
        }
    }

    protected static String getServletBaseUrl(HttpServletRequest request) {
        String base = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort();
        String requestURI = request.getRequestURI();
        int i = requestURI.lastIndexOf('/');
        return base + requestURI.substring(0, i);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, java.io.IOException {
        doRequest(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, java.io.IOException {
        doRequest(request, response);
    }

    @Override
    protected void doDelete(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, java.io.IOException {
        doRequest(request, response);
    }

    @Override
    protected void doHead(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, java.io.IOException {
        doRequest(request, response);
    }

    @Override
    protected void doOptions(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, java.io.IOException {
        doRequest(request, response);
    }

    @Override
    protected void doPut(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, java.io.IOException {
        doRequest(request, response);
    }

    @Override
    protected void doTrace(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, java.io.IOException {
        doRequest(request, response);
    }

    protected void doRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, java.io.IOException {
        HttpServletRequestTwsWrapper wrapped_request = new HttpServletRequestTwsWrapper(request);
        request = wrapped_request;

        String baseUrl = getServletBaseUrl(request);

        if ((baseUrl.indexOf("/projects/") != -1) || (baseUrl.indexOf("/webclipper/") != -1)) {
            long t0 = System.currentTimeMillis();
            try {
                String encoded = request.getParameter(Parameter.RsaEncoded.getName());
                if (encoded != null) {
                    String query = Engine.theApp.rsaManager.decrypt(encoded, request.getSession());
                    wrapped_request.clearParameters();
                    wrapped_request.addQuery(query);
                }

                Object result = processRequest(request);

                response.addHeader("Expires", "-1");

                if (getCacheControl(request).equals("false"))
                    HeaderName.CacheControl.addHeader(response,
                            "no-store, no-cache, must-revalidate, post-check=0, pre-check=0");

                /**
                 * Disabled since #253 : Too much HTML Connector cookies in
                 * response header make a tomcat exception
                 * http://sourceus.twinsoft.fr/ticket/253 cookies must be in xml
                 * if wanted, not in headers
                 * 
                 * Vector cookies = (Vector)
                 * request.getAttribute("convertigo.cookies"); for (int i=0;
                 * i<cookies.size(); i++) { String sCookie =
                 * (String)cookies.elementAt(i);
                 * response.addHeader("Set-Cookie", sCookie);
                 * Engine.logContext.trace("[GenericServlet] Set-Cookie: " +
                 * sCookie); }
                 */

                String trSessionId = (String) request.getAttribute("sequence.transaction.sessionid");
                if ((trSessionId != null) && (!trSessionId.equals(""))) {
                    response.setHeader("Transaction-JSessionId", trSessionId);
                }

                String requested_content_type = request.getParameter(Parameter.ContentType.getName());
                String content_type = getContentType(request);
                if (requested_content_type != null && !requested_content_type.equals(content_type)) {
                    Engine.logEngine.debug("(GenericServlet) Override Content-Type requested to change : "
                            + content_type + " to " + requested_content_type);
                    content_type = requested_content_type;
                } else {
                    requested_content_type = null;
                }

                response.setContentType(content_type);
                if (content_type.startsWith("text")) {
                    String charset = (String) request.getAttribute("convertigo.charset");
                    if (charset != null && charset.length() > 0) {
                        response.setCharacterEncoding(charset);
                    }
                }

                try {

                    if (result != null) {

                        Boolean b = (Boolean) request.getAttribute("convertigo.isErrorDocument");
                        if (b.booleanValue()) {
                            Requester requester = getRequester();
                            boolean bThrowHTTP500 = false;

                            if (requester instanceof WebServiceServletRequester) {
                                bThrowHTTP500 = Boolean.parseBoolean(EnginePropertiesManager.getProperty(
                                        EnginePropertiesManager.PropertyName.THROW_HTTP_500_SOAP_FAULT));
                            } else if (requester instanceof ServletRequester) {
                                bThrowHTTP500 = Boolean.parseBoolean(EnginePropertiesManager
                                        .getProperty(EnginePropertiesManager.PropertyName.THROW_HTTP_500));
                            }

                            if (bThrowHTTP500) {
                                response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                                Engine.logEngine.debug("(GenericServlet) Requested HTTP 500 status code");
                            }
                        } else {
                            applyCustomStatus(request, response);
                        }

                        if (result instanceof AttachmentDetails) {
                            AttachmentDetails attachment = (AttachmentDetails) result;
                            byte[] data = attachment.getData();
                            String contentType = attachment.getContentType();

                            if (requested_content_type != null) {
                                contentType = requested_content_type;
                            }

                            String name = attachment.getName();

                            HeaderName.ContentType.setHeader(response, contentType);
                            HeaderName.ContentLength.setHeader(response, "" + data.length);
                            HeaderName.ContentDisposition.setHeader(response, "attachment; filename=" + name);

                            applyCustomHeaders(request, response);

                            OutputStream out = response.getOutputStream();
                            out.write(data);
                            out.flush();
                        } else if (result instanceof byte[]) {
                            if (requested_content_type != null) {
                                response.setContentType(requested_content_type);
                            } else {
                                response.setContentType(getContentType(request));
                                response.setCharacterEncoding((String) request.getAttribute("convertigo.charset"));
                            }
                            HeaderName.ContentLength.addHeader(response, "" + ((byte[]) result).length);

                            applyCustomHeaders(request, response);

                            OutputStream out = response.getOutputStream();
                            out.write((byte[]) result);
                            out.flush();
                        } else {
                            String sResult = "";
                            if (result instanceof String) {
                                sResult = (String) result;
                            } else if (result instanceof Document) {
                                sResult = XMLUtils.prettyPrintDOM((Document) result);
                            } else if (result instanceof SOAPMessage) {
                                sResult = SOAPUtils.toString((SOAPMessage) result,
                                        (String) request.getAttribute("convertigo.charset"));
                            }

                            applyCustomHeaders(request, response);

                            Writer writer = response.getWriter();
                            writer.write(sResult);
                            writer.flush();
                        }
                    } else {
                        applyCustomHeaders(request, response);

                        response.setStatus(HttpServletResponse.SC_NO_CONTENT);
                    }
                } catch (IOException e) {
                    // The connection has probably been reset by peer
                    Engine.logContext
                            .warn("[GenericServlet] The connection has probably been reset by peer (IOException): "
                                    + e.getMessage());
                } finally {
                    // Supervision mode
                    String supervision = request.getParameter(Parameter.Supervision.getName());
                    if (supervision != null) {
                        Engine.logContext
                                .debug("[GenericServlet] Supervision mode => invalidating HTTP session in 30s.");
                        removeSession(request, 30);
                    }

                    // Removes context and session when finished
                    // Note: case of context.requireEndOfContext has been set in
                    // scope
                    if (request.getAttribute("convertigo.requireEndOfContext") != null) {
                        removeContext(request);
                        removeSession(request, 1);
                    }

                    // Removes context when finished
                    String removeContextParam = request.getParameter(Parameter.RemoveContext.getName());
                    if (removeContextParam == null) {
                        // case of a mother sequence (context is removed by
                        // default)
                        Boolean removeContextAttr = (Boolean) request
                                .getAttribute("convertigo.context.removalRequired");
                        if ((removeContextAttr != null) && removeContextAttr.booleanValue()) {
                            removeContext(request);
                        }
                    } else {
                        // other cases (remove context if exist __removeContext
                        // or __removeContext=true/false)
                        if (!("false".equals(removeContextParam))) {
                            removeContext(request);
                        }
                    }

                    // Removes session when finished
                    String removeSessionParam = request.getParameter(Parameter.RemoveSession.getName());
                    if (removeSessionParam != null) {
                        // __removeSession or __removeSession=true/false
                        // or __removeSession=xx (where xx is a number of seconds)
                        if (!("false".equals(removeSessionParam))) {
                            int interval = 1;
                            try {
                                interval = Integer.parseInt(removeSessionParam, 10);
                            } catch (Exception e) {
                            }
                            removeSession(request, interval);
                        }
                    }

                }
            } catch (Exception e) {
                Engine.logContext.error("Unable to process the request!", e);
                processException(request, response, e);
            } finally {
                long t1 = System.currentTimeMillis();
                Engine.theApp.pluginsManager.fireHttpServletRequestEnd(request, t0, t1);
            }
        } else {
            // Not a valid Convertigo invocation URL, use retrieve as static
            // resource
            handleStaticData(request, response);
            return;
        }
    }

    protected void removeContext(HttpServletRequest request) {
        if (Engine.isEngineMode()) {
            String contextID = (String) request.getAttribute("convertigo.context.contextID");
            if (contextID != null) {
                Engine.logContext
                        .debug("[GenericServlet] End of context " + contextID + " required => removing context");
                Engine.theApp.contextManager.remove(contextID);
            }
        }
    }

    protected void removeSession(HttpServletRequest request, int interval) {
        if (Engine.isEngineMode()) {
            Engine.logContext.debug("[GenericServlet] End of session required => try to invalidate session");
            try {
                HttpSession httpSession = request.getSession();
                boolean isAdminSession = "true".equals((String) httpSession.getAttribute("administration"));
                if (!isAdminSession && Engine.theApp.contextManager.isSessionEmtpy(httpSession.getId())) {
                    Engine.logContext.debug(
                            "[GenericServlet] The owner HTTP session is empty => invalidating HTTP session in "
                                    + interval + "s.");
                    httpSession.setMaxInactiveInterval(interval);
                }
            } catch (Exception e) {
                Engine.logContext
                        .debug("[GenericServlet] End of session required => failed to get the session: " + e);
            }
        }
    }

    public void processException(HttpServletRequest request, HttpServletResponse response, Exception e)
            throws ServletException {
        boolean hide_error = EnginePropertiesManager.getProperty(PropertyName.HIDING_ERROR_INFORMATION)
                .equals("true");
        boolean bThrowHTTP500 = Boolean.parseBoolean(
                EnginePropertiesManager.getProperty(EnginePropertiesManager.PropertyName.THROW_HTTP_500));

        Engine.logEngine.error("Unexpected exception", e);

        if (bThrowHTTP500) {
            if (hide_error)
                throw new ServletException();
            else
                throw new ServletException(e);
        } else {
            try {
                if (hide_error)
                    response.addHeader("Convertigo-Exception", "");
                else
                    response.addHeader("Convertigo-Exception", e.getClass().getName());
                response.setContentType(MimeType.Plain.value());
                PrintWriter out = response.getWriter();
                if (hide_error)
                    out.println("Convertigo error:");
                else
                    out.println("Convertigo error: " + e.getMessage());
            } catch (IOException e1) {
                Engine.logEngine.error("Unexpected exception", e1);
                if (hide_error)
                    throw new ServletException();
                else
                    throw new ServletException(e);
            }
        }
    }

    public Object processRequest(HttpServletRequest request) throws Exception {
        HttpServletRequestTwsWrapper twsRequest = request instanceof HttpServletRequestTwsWrapper
                ? (HttpServletRequestTwsWrapper) request
                : null;
        File temporaryFile = null;

        try {
            // Check multipart request
            if (ServletFileUpload.isMultipartContent(request)) {
                Engine.logContext.debug("(ServletRequester.initContext) Multipart resquest");

                // Create a factory for disk-based file items
                DiskFileItemFactory factory = new DiskFileItemFactory();

                // Set factory constraints
                factory.setSizeThreshold(1000);

                temporaryFile = File.createTempFile("c8o-multipart-files", ".tmp");
                int cptFile = 0;
                temporaryFile.delete();
                temporaryFile.mkdirs();
                factory.setRepository(temporaryFile);
                Engine.logContext.debug("(ServletRequester.initContext) Temporary folder for upload is : "
                        + temporaryFile.getAbsolutePath());

                // Create a new file upload handler
                ServletFileUpload upload = new ServletFileUpload(factory);

                // Set overall request size constraint
                upload.setSizeMax(
                        EnginePropertiesManager.getPropertyAsLong(PropertyName.FILE_UPLOAD_MAX_REQUEST_SIZE));
                upload.setFileSizeMax(
                        EnginePropertiesManager.getPropertyAsLong(PropertyName.FILE_UPLOAD_MAX_FILE_SIZE));

                // Parse the request
                List<FileItem> items = GenericUtils.cast(upload.parseRequest(request));

                for (FileItem fileItem : items) {
                    String parameterName = fileItem.getFieldName();
                    String parameterValue;
                    if (fileItem.isFormField()) {
                        parameterValue = fileItem.getString();
                        Engine.logContext.trace("(ServletRequester.initContext) Value for field '" + parameterName
                                + "' : " + parameterValue);
                    } else {
                        String name = fileItem.getName().replaceFirst("^.*(?:\\\\|/)(.*?)$", "$1");
                        if (name.length() > 0) {
                            File wDir = new File(temporaryFile, "" + (++cptFile));
                            wDir.mkdirs();
                            File wFile = new File(wDir, name);
                            fileItem.write(wFile);
                            fileItem.delete();
                            parameterValue = wFile.getAbsolutePath();
                            Engine.logContext
                                    .debug("(ServletRequester.initContext) Temporary uploaded file for field '"
                                            + parameterName + "' : " + parameterValue);
                        } else {
                            Engine.logContext
                                    .debug("(ServletRequester.initContext) No temporary uploaded file for field '"
                                            + parameterName + "', empty name");
                            parameterValue = "";
                        }
                    }

                    if (twsRequest != null) {
                        twsRequest.addParameter(parameterName, parameterValue);
                    }
                }
            }

            Requester requester = getRequester();
            request.setAttribute("convertigo.requester", requester);

            Object result = requester.processRequest(request);

            request.setAttribute("convertigo.cookies", requester.context.getCookieStrings());

            String trSessionId = requester.context.getSequenceTransactionSessionId();
            if (trSessionId != null) {
                request.setAttribute("sequence.transaction.sessionid", trSessionId);
            }

            if (requester.context.requireEndOfContext) {
                // request.setAttribute("convertigo.requireEndOfContext",
                // requester);
                request.setAttribute("convertigo.requireEndOfContext", Boolean.TRUE);
            }

            if (request.getAttribute("convertigo.contentType") == null) { // if
                // contentType
                // set by
                // webclipper
                // servlet
                // (#320)
                request.setAttribute("convertigo.contentType", requester.context.contentType);
            }

            request.setAttribute("convertigo.cacheControl", requester.context.cacheControl);
            request.setAttribute("convertigo.context.contextID", requester.context.contextID);
            request.setAttribute("convertigo.isErrorDocument", new Boolean(requester.context.isErrorDocument));
            request.setAttribute("convertigo.context.removalRequired",
                    new Boolean(requester.context.removalRequired()));
            if (requester.context.requestedObject != null) { // #397 : charset HTTP
                // header missing
                request.setAttribute("convertigo.charset", requester.context.requestedObject.getEncodingCharSet());
            } else { // #3803
                Engine.logEngine.warn(
                        "(GenericServlet) requestedObject is null. Set encoding to UTF-8 for processRequest.");
                request.setAttribute("convertigo.charset", "UTF-8");
            }

            return result;
        } finally {
            if (temporaryFile != null) {
                try {
                    Engine.logEngine.debug(
                            "(GenericServlet) Removing the temporary file : " + temporaryFile.getAbsolutePath());
                    FileUtils.deleteDirectory(temporaryFile);
                } catch (IOException e) {
                }
            }
        }
    }

    public abstract Requester getRequester();

    public String getContentType(HttpServletRequest request) {
        String contentType = (String) request.getAttribute("convertigo.contentType");
        if (contentType == null)
            contentType = getDefaultContentType();
        return contentType;
    }

    public String getCacheControl(HttpServletRequest request) {
        String cacheControl = (String) request.getAttribute("convertigo.cacheControl");
        if (cacheControl == null)
            cacheControl = "false";
        return cacheControl;
    }

    public String getDefaultContentType() {
        return MimeType.Html.value();
    }

    public abstract String getDocumentExtension();

    private void applyCustomHeaders(HttpServletRequest request, HttpServletResponse response) {
        ServletUtils.applyCustomHeaders(request, response);
    }

    private void applyCustomStatus(HttpServletRequest request, HttpServletResponse response) {
        ServletUtils.applyCustomStatus(request, response);
    }
}