InvokerBase.java :  » Content-Management-System » contelligent » de » finix » contelligent » servlet » Java Open Source

Java Open Source » Content Management System » contelligent 
contelligent » de » finix » contelligent » servlet » InvokerBase.java
/*
 * Copyright 2001-2006 C:1 Financial Services GmbH
 *
 * This software is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License Version 2.1, as published by the Free Software Foundation.
 *
 * This software 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
 */

package de.finix.contelligent.servlet;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.UnavailableException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import de.finix.contelligent.CallData;
import de.finix.contelligent.Component;
import de.finix.contelligent.ComponentManager;
import de.finix.contelligent.ComponentNotFoundException;
import de.finix.contelligent.ComponentPath;
import de.finix.contelligent.Contelligent;
import de.finix.contelligent.HTTPCallData;
import de.finix.contelligent.MaxSessionsExceededException;
import de.finix.contelligent.Session;
import de.finix.contelligent.SessionCreationException;
import de.finix.contelligent.action.Action;
import de.finix.contelligent.category.CategoryException;
import de.finix.contelligent.content.BinaryContent;
import de.finix.contelligent.content.Content;
import de.finix.contelligent.content.ContentProvider;
import de.finix.contelligent.content.Metadata;
import de.finix.contelligent.core.BasicComponentManager;
import de.finix.contelligent.core.BuildInfo;
import de.finix.contelligent.core.ComponentManagerInternal;
import de.finix.contelligent.core.ContelligentImpl;
import de.finix.contelligent.core.ContelligentSession;
import de.finix.contelligent.core.ErrorHandler;
import de.finix.contelligent.core.HTTPCallDataImpl;
import de.finix.contelligent.core.SystemFilesServletImpl;
import de.finix.contelligent.core.TimeService;
import de.finix.contelligent.core.UserHome;
import de.finix.contelligent.core.security.ComponentPermission;
import de.finix.contelligent.core.security.ContelligentSecurityManager;
import de.finix.contelligent.core.security.User;
import de.finix.contelligent.exception.ContelligentException;
import de.finix.contelligent.exception.ContelligentExceptionID;
import de.finix.contelligent.exception.ContelligentRuntimeException;
import de.finix.contelligent.exception.ContelligentSecurityException;
import de.finix.contelligent.exception.DuplicateLoginException;
import de.finix.contelligent.exception.InvalidSessionException;
import de.finix.contelligent.exception.NoReadPermissionException;
import de.finix.contelligent.logging.LoggingService;
import de.finix.contelligent.render.DefaultRenderer;
import de.finix.contelligent.render.PageRenderer;
import de.finix.contelligent.render.Renderable;
import de.finix.contelligent.render.Renderer;
import de.finix.contelligent.util.ThreadedMem;

/**
 * The main servlet for handling any HTTP request into the Contelligent system
 * 
 * @web:servlet name="Contelligent Servlet" description="Main Contelligent
 *              servlet"
 *              load-on-startup="@contelligent.servlet.load-on-startup@"
 * 
 * @web:servlet-mapping url-pattern="@contelligent.servlet.url-pattern@/*"
 * @web:servlet-mapping url-pattern="@contelligent.servlet.sso-pattern@/*"
 * 
 * @web:resource-ref name="jdbc/DataSource" type="javax.sql.DataSource"
 *                   auth="Container"
 * @web:resource-ref name="jdbc/DataSourceNoTx" type="javax.sql.DataSource"
 *                   auth="Container"
 * 
 * @web:env-entry name="contelligentDir" value="@contelligent.home@"
 *                type="java.lang.String"
 * 
 * @jboss:resource-ref res-ref-name="jdbc/DataSource"
 *                     jndi-name="java:contelligentDB"
 * @jboss:resource-ref res-ref-name="jdbc/DataSourceNoTx"
 *                     jndi-name="java:contelligentDBnoTx"
 * 
 * @weblogic:resource-description res-ref-name="jdbc/DataSource"
 *                                jndi-name="/weblogic/jdbc/contelligentDB"
 * @weblogic:resource-description res-ref-name="jdbc/DataSourceNoTx"
 *                                jndi-name="/weblogic/jdbc/contelligentDBnoTx"
 * 
 * @websphere:resource-ref res-ref-name="jdbc/DataSource"
 *                         jndi-name="java:contelligentDB"
 * @websphere:resource-ref res-ref-name="jdbc/DataSourceNoTx"
 *                         jndi-name="java:contelligentDBnoTx"
 */
public class InvokerBase implements Servlet {
    final static org.apache.log4j.Logger log = LoggingService.getLogger(InvokerBase.class);

    // search engine bots user agents, used as substrings
    private final static String[] searchEngineBots = new String[] {
        "googlebot", "scooter", "slurp", "ia_archiver", "architextspider",
        "sidewinder", "spider", "msnbot", "sproose", "iccrawler", "crawler",
        "jeeves", "exabot", "snapbot", "findlinks", "turnitinbot",
        "mj12bot", "holmes", "krugle", "seekbot", "envolk" };

    /** The http-session key for the contelligent session object. */
    private static final String CONTELLIGENT_SESSION_KEY = "contelligent/session";

    private static final String METADATA_HEADER_PREFIX = "header:";

    private static HashSet instances = new HashSet();

    private boolean initialized = false; // flag to prevent double

    // initialization.

    private ServletConfig config;

    private ServletContext servletContext;

    protected ContelligentImpl contelligent;

    private ErrorHandler errorHandler;

    private String defaultEncoding;

    private static int threadNum = 1;

    /**
     * Initializes this servlet and the
     * {@link ContelligentImpl Contelligent-System}.
     * 
     * @param config
     *            the <code>ServletConfig</code> of this servlet
     * @exception UnavailableException
     *                if the {@link ContelligentImpl Contelligent-System} could
     *                not be initialized.
     * @exception ServletException
     *                if an internal error occurs during initializarion of this
     *                servlet.
     */
    public synchronized void init(ServletConfig config) throws ServletException {
        synchronized (instances) {
            if (initialized) {
                servletContext.log("Attempt to initialize again!!");
                return;
            }
            this.config = config;
            servletContext = config.getServletContext();
            try {
                log.debug("init() - starting initialization ...");
                if (!ContelligentImpl.isInitialized()) {
                    ContelligentImpl.init(new SystemFilesServletImpl(servletContext, "META-INF/")); // throws
                                                                                                    // Exception
                    // on error
                }
                contelligent = ContelligentImpl.getInstance();
                errorHandler = ContelligentImpl.getInstance().getErrorHandler();
                if (errorHandler == null) {
                    throw new ContelligentException("No Error-Handler defined!");
                }
                defaultEncoding = ContelligentImpl.getInstance().getDefaultEncoding();

                initialized = true;
                instances.add(this);
                log.debug("init() - ... initialization done.");
                // XXX: if we throw an UnavailableException and this servlet
                // gets called after Contelligent
                // was initialized properly but via a different servlet (e.g.
                // ServerInfo), this servlet
                // never gets into service! (2002/11/08 rs)
            } catch (ContelligentException e) {
                // log.fatal("init() - ContelligenException while initializing
                // Contelligent: "+e);
                // XXX: JBoss 3 does a complete undeploy in case of an
                // exception, so don't throw it
                // throw new UnavailableException("Exception while initializing
                // Contelligent: "+e.getMessage());
            } catch (Exception e) {
                log.fatal("init() - Exception while initializing main servlet: ", e);
                throw new ServletException("Exception while initializing main servlet: ", e);
            } finally {
                log.info("init() - ... end.");
            }
        }
    }

    public ServletConfig getServletConfig() {
        return config;
    }

    public String getServletInfo() {
        return "Contelligent Main Servlet, (c) 2001 - 2006  C:1 financial services";
    }

    public synchronized void destroy() {

        synchronized (instances) {
            instances.remove(this);

            if (instances.isEmpty()) {

                // if system is shutted down while SystemIndex is still indexing components, the pending components to be indexed would be forgotten.
                // because of that, we have to save a list of this components per context/ComponentManager, so that SystemIndex can continue where it stopped.

                ContelligentImpl contelligent = ContelligentImpl.getInstance();

                if (contelligent.hasSystemIndex()) {

                    log.debug("Trying to save pending components to be indexed by SystemIndex...");

                    Set componentManagerNames = contelligent.getComponentManagerHierarchy().getComponentManagerNames();
                    Iterator componentManagerNamesIterator = componentManagerNames.iterator();

                    ComponentManager rootCM = contelligent.getRootComponentManager();
                    User indexUser = ContelligentSecurityManager.getIndexUser();
                    try {
                        Session session = contelligent.beginSession(indexUser, rootCM);
                        contelligent.beginTx(3600);
                        CallData callData = contelligent.createCallData(session);

                        ComponentPath homePath = UserHome.getRootPath().append(indexUser.getGroupId()).append(
                                indexUser.getName());

                        if (rootCM.componentExists(homePath)) {
                            rootCM.deleteComponentTree(callData, homePath);
                        }

                        while (componentManagerNamesIterator.hasNext()) {

                            String componentManagerName = (String) componentManagerNamesIterator.next();
                            BasicComponentManager cm = (BasicComponentManager) contelligent
                                    .getComponentManager(componentManagerName);

                            // stop the thread/run() method of SystemIndexer
                            cm.getSystemIndexer().stopThread();

                            while (cm.getSystemIndexer().isRunning()) {
                                try {
                                    Thread.sleep(100);
                                    continue;
                                } catch (InterruptedException e) {
                                    break;
                                }
                            }

                            cm.getSystemIndexer().savePendingComponents(callData);
                        }

                        contelligent.commitTx();
                    } catch (SessionCreationException sce) {
                        log.error("Failed to save indexing status.", sce);
                    } catch (Exception e) {
                        log.error("Failed to save indexing status.", e);
                        try {
                            contelligent.rollbackTx();
                        } catch (javax.transaction.SystemException se) {
                            log.error("Rollback failed.", se);
                        }
                    }
                }
                log.debug("destroy() - calling contelligent shutdown ...");

                ContelligentImpl.shutdown();
            }
            log.debug("destroy() - instance destroyed.");
        }
    }

    /**
     * Handles any HTTP request. <BR>
     * Currently this method may only be called once for a single request, so
     * forward is not allowed! Since we don't support JSPs with Contelligent
     * tags at the moment this is not an issue. <BR>
     * Note that this method starts a new transaction for every call.
     * 
     * @param req
     *            a <code>HttpServletRequest</code> value
     * @param res
     *            a <code>HttpServletResponse</code> value
     * @exception ServletException
     *                if an error occurs
     * @exception IOException
     *                if an error occurs
     */
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        HttpServletRequest request;
        HttpServletResponse response;

        try {
            request = (HttpServletRequest) req;

            // Make thread identifiable
            Thread currentThread = Thread.currentThread();
            int myNum = 0;
            synchronized (this) {
                myNum = threadNum;
                threadNum++;
                if (threadNum > 10000) {
                    threadNum = 1;
                }
            }
            currentThread.setName("Request-" + request.getRemoteAddr() + "#" + String.valueOf(myNum));

            // Set the character encoding to force Tomcat to decode the body properly.
            // This must be done before we do anything else with the request or it will silently return garbage.
            try {
                request.setCharacterEncoding(ContelligentImpl.getInstance().getParameterEncoding());
            } catch (Exception e) {
                log.error("Unable to set request encoding: ", e);
            }

            // Set the timeout once per request; it's a cheap operation anyway
            int timeout = ContelligentImpl.getInstance().getDefaultTransactionTimeout();
            request.getSession().setMaxInactiveInterval(timeout);
            response = (HttpServletResponse) res;

        } catch (ClassCastException e) {
            throw new ServletException("non-HTTP request or response");
        }

        final boolean debugEnabled = log.isDebugEnabled();
        // XXX: DEBUG ONLY
        if (debugEnabled) {
            Enumeration enumeration = request.getHeaderNames();
            while (enumeration.hasMoreElements()) {
                String headerName = (String) enumeration.nextElement();
                log.debug("... header '" + headerName + "'='" + request.getHeader(headerName) + "' ...");
            }
        }

        String method = request.getMethod();
        boolean get = method.equals("GET");
        boolean post = method.equals("POST");
        boolean head = method.equals("HEAD");
        String servletPath = null;
        HTTPCallDataImpl callData = null;
        ContelligentSession session = null;
        ComponentPath compPath = null;

        if (get || post || head) {
            boolean txStarted = false;
            boolean txCommited = false;
            try {
                if (request.getAttribute("javax.servlet.include.path_info") == null) {
                    servletPath = request.getPathInfo();
                } else {
                    servletPath = (String) request.getAttribute("javax.servlet.include.path_info");
                }

                compPath = new ComponentPath(servletPath);
                String extension = "";

                txStarted = contelligent.beginTx();

                callData = setupEnvironment(request, response, getServletConfig().getServletContext(), null, null);
                session = (ContelligentSession) callData.getContelligentSession();

                if (session.isMarkedForLogout()) {
                    User user = session.getUser();
                    HttpSession httpSession = callData.getHttpServletRequest().getSession(false);
                    if (httpSession != null) {
                        httpSession.invalidate();
                    }
                    callData.getSystem().resetSession(session);
                    throw new DuplicateLoginException(user.getName());
                }

                // override category map for this call
                Map paramMap = callData.getParameters();
                if (debugEnabled)
                    log.debug("service() - ... param-map=" + paramMap);
                // XXX: the default for a PageRenderer is MODE_REF now which is
                // right for almost any
                // call except top-level ones. Therefore set mode to markup if
                // not explicitly set. (rs)
                if (!paramMap.containsKey(PageRenderer.MODE)) {
                    paramMap.put(PageRenderer.MODE, new String[] { PageRenderer.MODE_MARKUP });
                }

                ComponentManager manager = session.getComponentManager();
                if (debugEnabled) {
                    log.debug("service() - trying to get component '" + compPath + "' from manager '" + manager
                            + "' ...");
                }

                if (request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid()) {
                    if (!contelligent.isInPublicDir(compPath)) {
                        throw new InvalidSessionException("Requested session id not valid (potential session timeout)");
                    }
                }

                Component component;
                try {
                    component = manager.getComponent(compPath, callData);
                } catch (ComponentNotFoundException cne) {
                    // Component with extension was not found; try the short path then if possible
                    int dotPos = servletPath.lastIndexOf('.');
                    int slashPos = servletPath.lastIndexOf('/');
                    if ((dotPos != 1) && (slashPos < dotPos)) {
                        compPath = new ComponentPath(servletPath.substring(0, dotPos));
                        extension = servletPath.substring(dotPos);
                        component = manager.getComponent(compPath, callData);
                    } else {
                        // Rethrow to normal handler
                        throw cne;
                    }
                }
                // suffix is currently not used ....
                // String suffix = (sep==-1) ? "" : servletPath.substring(sep);

                // XXX: maybe use unsynchronized stream to improve performance?
                // (rs)
                ByteArrayOutputStream outStream = new ByteArrayOutputStream(16384);

                // we always set the Last-Modified header
                long lastModified = getLastModified(component, callData, request);
                if (get) {
                    long ifModifiedSince = request.getDateHeader("If-Modified-Since"); // if
                                                                                        // !=-1
                    // this is a
                    // conditional
                    // GET
                    if (ifModifiedSince == -1 || (ifModifiedSince < lastModified)) {
                        handleRequest(outStream, component, true, callData, request, response, true, extension);
                    } else {
                        if (debugEnabled)
                            log.debug("service() - ... 'If-Modified-Since' <= 'Last-Modified' --> sending 304.");
                        response.setStatus(304); // not-modified
                    }
                } else if (head) {
                    // false: don't include body
                    handleRequest(outStream, component, true, callData, request, response, false, extension);
                } else { // POST
                    handleRequest(outStream, component, true, callData, request, response, true, extension);
                }

                if (component.isDynamic()) {
                    if (!response.containsHeader("Cache-Control")) {
                        response.setHeader("Cache-Control", "no-cache");
                    }
                    // Be extra explicit, so IE understands us...
                    if (!response.containsHeader("Pragma")) {
                        response.setHeader("Pragma", "no-cache");
                    }
                    if (!response.containsHeader("Expires")) {
                        response.setHeader("Expires", "0");
                    }
                } else {
                    // private since we have always authentication. Use public
                    // if a proxy should cache it!
                    if (!response.containsHeader("Cache-Control")) {
                        response.setHeader("Cache-Control", "private");
                    }
                    // response.setDateHeader("Expires",
                    // (TimeService.getInstance().currentTimeMillis()+(1000*3600)));
                    // response.setDateHeader("Date",
                    // TimeService.getInstance().currentTimeMillis());
                    if (!response.containsHeader("Last-Modified")) {
                        response.setDateHeader("Last-Modified", lastModified);
                        if (debugEnabled)
                            log.debug("service() - set 'Last-Modified' header to '" + lastModified + "' ...");
                    }
                }

                // first commit, then write contents !
                if (txStarted) {
                    // important: set this true BEFORE actually trying to commit
                    // because every exception
                    // thrown by commitTx() means we can neither commit nor
                    // rollback in the finally block ! (rs)
                    txCommited = true;
                    contelligent.commitTx();
                    if (debugEnabled)
                        log.debug("service() - ... successfully commited transaction.");
                }
                // this is for components that do sendRedirect or use forward!!!
                // Never write to stream if already committed!!
                if (!response.isCommitted()) {
                    outStream.writeTo(response.getOutputStream());
                    response.flushBuffer();
                }
            } catch (NoReadPermissionException e) {
                if (debugEnabled) {
                    log.warn("service() - trying to handle throwable: ", e);
                } else {
                    log.warn("service() - trying to handle throwable: " + e.getMessage());
                }
                handleException(e, compPath, callData, request, response); // throws ServletException, IOException
            } catch (MaxSessionsExceededException e) {
                log.error("service() - max session exceeded exception: ", e);
                handleException(e, compPath, callData, request, response); // throws ServletException, IOException
            } catch (OutOfMemoryError oome) {
                synchronized (this.getClass()) {
                    log.error("Ran out of memory while processing request. Adjusting cache size.");
                    ComponentManager rootManager = ContelligentImpl.getInstance().getRootComponentManager();
                    if (rootManager instanceof ComponentManagerInternal) {
                        ComponentManagerInternal cmi = (ComponentManagerInternal) rootManager;
                        int actualCacheSize = cmi.getActualCacheSize();
                        int lastMaxCacheSize = cmi.getMaxCacheSize();
                        log.info("Current cache size is " + actualCacheSize);
                        log.info("Last maximum cache size was " + lastMaxCacheSize);
                        // Reduce current size by 10% to free some space for overhead
                        int newMaxCacheSize = (int) Math.round(actualCacheSize * 0.90);
                        log.info("Setting maximum cache size to " + newMaxCacheSize);
                        cmi.setMaxCacheSize(newMaxCacheSize);
                    } else {
                        log.warn("The current rootManager does not support this feature!");
                    }
                    handleException(oome, compPath, callData, request, response); // throws ServletException, IOException
                }
            } catch (SessionCreationException sce) {
                // Cant use the normal error page mechanism here
                response.sendError(500, sce.getMessage());
            } catch (Throwable t) {
                log.error("service() - trying to handle throwable: ", t);
                handleException(t, compPath, callData, request, response); // throws ServletException, IOException
            } finally {
                try {
                    if (txStarted && !txCommited) { // means something went wrong before trying to call commitTx() !
                        try {
                            StringBuffer httpRequest = new StringBuffer();
                            httpRequest.append(method).append(" ").append(servletPath);
                            log.info("service() - Exception while serving '" + httpRequest
                                    + "', calling rollbackTx() ...");
                            contelligent.rollbackTx();
                            log.info("service() - ... transaction for serving '" + httpRequest + "' rolled back.");
                        } catch (Exception e) {
                            log.error("service() - Exception during rollback: ", e);
                            throw new ServletException("Exception during rollback!", e);
                        }
                    }
                } finally {
                    if (callData != null) {
                        if (callData.isSearchEngineSpider()) {
                            contelligent.invalidateSession(callData.getContelligentSession());
                        }
                    }
                }
            }
        } else if (method.equals("OPTIONS")) {
            response.setHeader("Allow", "GET, HEAD, POST, OPTIONS");
        } else {
            response.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, "HTTP method '" + method + "' not supported!");
        }
    }

    /**
     * Dispatches the request depending on the interfaces the given component
     * does implement. The specific methods must set the
     * {@link ServletResponse#setContentType content-type} and the
     * {@link ServletResponse#setContentLength content-length} of the response.
     */
    private void handleRequest(OutputStream outStream, Component component, boolean checkAccess, HTTPCallData callData,
            HttpServletRequest request, HttpServletResponse response, boolean includeBody, String extension)
            throws Exception {
        if (!(component instanceof Action)) {
            // Check read permission for any requested object except Actions
            ContelligentSession session = (ContelligentSession) callData.getContelligentSession();
            User caller = session.getUser();
            if (!callData.getActualManager().callerHasPermission(caller, callData, component, ComponentPermission.READ)) {
                throw new NoReadPermissionException(caller, component.getComponentContext().getPath());
            }
            if (component.getComponentContext().requiresSecureTransfer() && !(request.isSecure() || request.getScheme().toLowerCase().equals("https"))
                    && ContelligentImpl.getInstance().supportSecureComponents()) {
                // throw new InsecureRequestException(component.getComponentContext().getPath());
                response.sendRedirect(callData.getSecureBaseURL() + component.getComponentContext().getPath()
                        + extension);
                return;
            }
        }

        // Set headers as specified in component metadata
        Metadata metadata = component.getComponentContext().getMetadata();
        Iterator metaKeys = metadata.getKeys().iterator();
        while (metaKeys.hasNext()) {
            String key = (String) metaKeys.next();
            if (key.toLowerCase().startsWith(METADATA_HEADER_PREFIX)) {
                String headerName = key.substring(METADATA_HEADER_PREFIX.length());
                Iterator metaValues = metadata.getValues(key).iterator();
                while (metaValues.hasNext()) {
                    String headerValue = (String) metaValues.next();
                    response.addHeader(headerName, headerValue);
                }
            }
        }

        if (component instanceof ContentProvider) {
            processContent(outStream, (ContentProvider) component, checkAccess, callData, request, response,
                    includeBody);
        } else if (component instanceof Renderable) {
            processRenderable(outStream, (Renderable) component, checkAccess, callData, request, response, includeBody);
        } else {
            log.error("handleRequest() - unknown type '" + component + "'!");
        }
        if (log.isDebugEnabled())
            log.debug("handleRequest() - ... request handled.");
    }

    private void handleException(Throwable throwable, ComponentPath componentPath, HTTPCallData callData,
            HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        log.debug("handleException() - searching error-page for Throwable '" + throwable.getClass().getName()
                + "' and component-path '" + componentPath + "' ...");
        try {
            ErrorHandler.ErrorMapping errorMapping = errorHandler.getErrorPage(throwable, componentPath);
            sendPageOrError(errorMapping.errorPagePath, componentPath, errorMapping.errorCode, callData, request,
                    response, throwable);
        } catch (ContelligentException e) {
            log.warn("handleException() - Exception while fetching path to error-page for path '" + componentPath
                    + "' and exception-class '" + throwable.getClass().getName() + "': " + e);
            throw new ServletException("Could not get error-page for path '" + componentPath
                    + "' and exception-class '" + throwable.getClass().getName() + "'!", e);
        }
    }

    /**
     * Either prints the contents of the component found in path
     * error-path+errorCode or if such a component does not exist the given
     * <tt>errorCode</tt> is simply passed to
     * {@link HttpServletResponse#sendError} using the
     * {@link Throwable#getMessage message} of the given <tt>throwable</tt> as
     * descriptive message. If the response has already been commited a
     * <code>ServletException</code> is thrown which gets wrapped around the
     * given <tt>throwable</tt>.
     */
    private void sendPageOrError(ComponentPath errorPagePath, ComponentPath componentPath, int errorCode,
            HTTPCallData callData, HttpServletRequest request, HttpServletResponse response, Throwable throwable)
            throws ServletException, IOException {
        StringBuffer sb = new StringBuffer("Render stack at time of failure:\n");
        Vector renderStack = callData.cloneRenderStack();
        Iterator it = renderStack.iterator();
        while (it.hasNext()) {
            ComponentPath stackElement = (ComponentPath) it.next();
            sb.append(stackElement.toString());
            sb.append("\n");
        }
        log.info(sb.toString());
        if (response.isCommitted()) {
            log
                    .warn("sendPageOrError() - response was already committed, can not send error -> throwing ServletException!");
            throw new ServletException(
                    "Could not display error-page nor send error-code because response was already commited!",
                    throwable);
        }
        ComponentManager manager = callData.getActualManager();
        try {
            // XXX: maybe switch user to SystemUser to avoid (maybe another) SecurityException ?
            if (manager.componentExists(errorPagePath)) {
                Component errorPage = manager.getComponent(errorPagePath, callData);
                String channel = getChannel(callData.getCategoryMap());
                String contentType = getContentType(channel);
                response.setContentType(contentType);
                response.setStatus(errorCode);

                // write information of problematic page into request
                callData.setRequestAttribute("errorCode", (new Integer(errorCode)).toString());
                callData.setRequestAttribute("originalRequestPath", componentPath.toPath());

                // dispatch again but ignore access restrictions:
                handleRequest(response.getOutputStream(), errorPage, false, callData, request, response, true, ".html");
                return;
            } else {
                log.warn("sendPageOrError() - error-page '" + errorPagePath + "' does not exist! Sending error-code "
                        + errorCode + " instead ...");
            }
        } catch (Exception e) {
            log.warn("sendPageOrError() - Exception while trying to serve error-page '" + errorPagePath
                    + "' (ignored), sending error-code " + errorCode + " instead ... (exception was: ", e);
        }
        response.sendError(errorCode, throwable.getMessage());
    }

    private String getEncoding(String channel) {
        if ("XML".equalsIgnoreCase(channel)) {
            return "UTF-8";
        } else {
            return defaultEncoding;
        }
    }

    private String getContentType(String channel) {
        StringBuffer s = new StringBuffer();

        if (channel.indexOf("app_") == 0) {
            s.append("application/");
            try {
                channel = channel.substring(4);
            } catch (Exception e) {
                channel = "";
            }
        } else {
            s.append("text/");
        }

        if ("wml".equals(channel)) {
            s.append("vnd.wap.wml");
        } else {
            s.append(channel);
        }
        s.append("; charset=").append(getEncoding(channel));
        if (log.isDebugEnabled())
            log.debug("getContentType() - returning '" + s + "' ...");
        return s.toString();
    }

    private String getChannel(Map categoryMap) {
        return ((categoryMap.containsKey("channel")) ? (String) categoryMap.get("channel") : "html");
    }

    /**
     * Describe <code>setupEnvironment</code> method here.
     * 
     * @param request
     *            a <code>HttpServletRequest</code> value
     * @param response
     *            a <code>HttpServletResponse</code> value
     * @param context
     *            a <code>ServletContext</code> value
     * @return a <code>CallData</code> value
     * @exception SessionCreationException
     *                if an error occurs
     * @exception CategoryException
     *                if an error occurs
     */
    HTTPCallDataImpl setupEnvironment(HttpServletRequest request, HttpServletResponse response, ServletContext context,
            String requestBaseURL, String requestHttpAuthBaseURL) throws SessionCreationException, CategoryException {
        final boolean debugEnabled = log.isDebugEnabled();
        boolean newSession = false;
        boolean configurePreview = false;
        String requestType = request.getContentType();
        String userAgent = request.getHeader("User-Agent");
        if (userAgent == null) {
            userAgent = "unspecified";
        }
        if (debugEnabled) {
            log.debug("setupEnvironment() - remote user is: " + userAgent);
        }
        boolean flashRequest = false;
        boolean searchEngineSpider = isSearchEngineSpider(userAgent);
        if (userAgent != null && userAgent.startsWith("Shockwave Flash")) {
            flashRequest = true;
        }
        if (requestType != null && requestType.equals("text/xml")) {
            if (debugEnabled) {
                log.debug("setupEnvironment() - request type is: " + requestType);
            }
            flashRequest = true;
        }

        HttpSession httpSession = request.getSession(!searchEngineSpider);
        ContelligentSession contellSession = null;

        if (!searchEngineSpider) {
            contellSession = (ContelligentSession) httpSession.getAttribute(CONTELLIGENT_SESSION_KEY);
        }
        if (contellSession == null) {
            log.debug("setupEnvironment() - ... contelligent-session not set -> generating new one ...");
            // check for auto-login
            /*
             * int sCount = contelligent.getNumberOfSessions();
             * 
             * if(sCount >= contelligent.getLicense().getMaxSessions() &&
             * contelligent.getLicense().getMaxSessions() > -1 ) { throw new
             * MaxSessionsExceededException("max number of sessions exceeded.
             * license restricts the number of sessions to:
             * "+contelligent.getLicense().getMaxSessions()); }
             */

            Cookie[] cookies = request.getCookies();
            if (cookies != null) {
                for (int i = 0; i < cookies.length; i++) {
                    Cookie cookie = cookies[i];
                    if (cookie.getName().equals(de.finix.contelligent.core.security.AutologinCookie.NAME)) {
                        log
                                .debug("setupEnvironment() - ... found cookie for autologin, trying to generate session from it ...");
                        contellSession = (ContelligentSession) contelligent.beginSession(cookie);
                    }
                }
            }
            if (contellSession == null) {
                ComponentManager manager = contelligent.getRootComponentManager();
                User initialUser = ContelligentSecurityManager.getInstance().getGuest();
                // FIXME: use authentication data for initial user
                contellSession = (ContelligentSession) contelligent.beginSession(initialUser, manager);
                newSession = true;
            }

            // store contelligent-session in http-session:
            if (!searchEngineSpider) {
                httpSession.setAttribute(CONTELLIGENT_SESSION_KEY, contellSession);
            }
            // to work around a strange bug in tomcat, remove the call data from
            // the request
            // the callData is not null only after invalidating the HTTP session
            // if (request.getAttribute(HTTPCallData.SESSION_KEY) != null) {
            // request.removeAttribute(HTTPCallData.SESSION_KEY);
            // }
            if (debugEnabled)
                log.debug("setupEnvironment() - ... created session: " + contellSession);
        }

        // honor contelligent user principal in request
        if (request.getUserPrincipal() != null && request.getUserPrincipal() instanceof User) {
            contellSession.setNamedUser((User) request.getUserPrincipal());
        }

        String cSessionId = null;
        int previewMode = ContelligentSession.PREVIEW;
        if (!flashRequest) {
            cSessionId = request.getParameter(Contelligent.CONTELLIGENT_SESSION_COOKIE);
            String mode = request.getParameter(Contelligent.PREVIEW_MODE);
            if (mode != null && mode.equals(Contelligent.EDIT)) {
                previewMode = ContelligentSession.EDIT;
            }
        }

        // cSessionId overloads the userPrincipal from
        // request.getUserPrincipal as set above
        if (cSessionId != null) {
            Session s = contelligent.getSession(cSessionId);
            if (s != null) {
                contellSession.addObserverSession((ContelligentSession) s);
                contellSession.setComponentManager(s.getComponentManager());
                contellSession.setPreviewMode(previewMode);
                contellSession.setNamedUser(s.getUser());
                configurePreview = true;

                log.debug("Added session " + cSessionId + " as observer to " + contellSession.getId());
            }
        }

        ThreadedMem.setActualManager(contellSession.getComponentManager());
        if (debugEnabled)
            log.debug("setupEnvironment() - set actual manager " + contellSession.getComponentManager());

        HTTPCallDataImpl callData = null;// (HTTPCallData)request.getAttribute(HTTPCallData.SESSION_KEY);

        boolean currentHttpAuth = false;
        boolean currentProtocolHttps = false;

        if (requestBaseURL == null) {
            requestBaseURL = request.getContextPath(); // = webApp
            requestHttpAuthBaseURL = request.getContextPath(); // = webApp

            String requestServletMapping = request.getServletPath();

            if (!(request.getAttribute("javax.servlet.include.servlet_path") == null)) {
                requestServletMapping = (String) request.getAttribute("javax.servlet.include.servlet_path");
            }

            // if ( requestServletMapping != null) {
            // requestBaseURL = requestBaseURL + requestServletMapping;
            // } else if(contelligent.getServletMapping() != null) {
            String tmp = contelligent.getServletMapping();

            if (!tmp.startsWith("/"))
                tmp = "/" + tmp;
            requestBaseURL = requestBaseURL + tmp;

            tmp = contelligent.getSSOServletMapping();

            if (!tmp.startsWith("/"))
                tmp = "/" + tmp;

            requestHttpAuthBaseURL = requestHttpAuthBaseURL + tmp;

            // if SSO context
            if (requestServletMapping.substring(0).equals(tmp)) {
                currentHttpAuth = true;
            } else {
                currentHttpAuth = false;
            }

            if (request.isSecure() || request.getScheme().toLowerCase().equals("https")) {
                currentProtocolHttps = true;
            } else {
                currentProtocolHttps = false;
            }

            // }
        }

        if (debugEnabled) {
            log.debug("setupEnvironment() - ... content type is: " + requestType);
            log.debug("setupEnvironment() - ... remote user is: " + userAgent);
        }
        if (requestType != null && requestType.startsWith("multipart")) {
            callData = new HTTPCallDataImpl(contellSession, requestBaseURL, requestHttpAuthBaseURL, contelligent
                    .getHTTPPort(), contelligent.getHTTPSPort(), currentProtocolHttps, currentHttpAuth, request,
                    response, context, true);
        } else if (flashRequest) {
            String requestBody = null;
            StringBuffer buffer = new StringBuffer(1024);
            try {
                // XXX: we use UTF-8 here because we assume body contains XML in
                // this encoding
                BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream(), "UTF-8"));
                String line = "";
                while ((line = reader.readLine()) != null) {
                    buffer.append(line);
                }
                requestBody = buffer.toString();
                if (debugEnabled) {
                    log.debug("setupEnvironment() - ... request body is: " + requestBody);
                }
            } catch (IOException e) {
                requestBody = null;
            }
            callData = new HTTPCallDataImpl(contellSession, requestBaseURL, requestHttpAuthBaseURL, contelligent
                    .getHTTPPort(), contelligent.getHTTPSPort(), currentProtocolHttps, currentHttpAuth, request,
                    response, context, false);

            // XXX HACK for Flash XML handling START
            if (BuildInfo.CHECK_FLASH_XML_REQUEST && requestBody.indexOf("<http-request") != -1) {
                int realBodyStart = requestBody.indexOf("<request");
                int realBodyEnd = requestBody.indexOf("</request>") + 10;

                String realBody = requestBody.substring(realBodyStart, realBodyEnd);
                if (debugEnabled) {
                    log.debug("setupEnvironment() - real-body=" + realBody);
                }
                try {
                    InputStream inStream = new ByteArrayInputStream(requestBody.getBytes());
                    DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
                    Document document = builder.parse(inStream);
                    Element parameters = (Element) document.getElementsByTagName("parameters").item(0);

                    NodeList params = parameters.getElementsByTagName("param");
                    for (int i = 0; i < params.getLength(); i++) {
                        Element param = (Element) params.item(i);
                        String name = param.getAttributes().getNamedItem("name").getNodeValue();

                        NodeList contentList = param.getChildNodes();
                        StringBuffer valBuf = new StringBuffer();
                        for (int a = 0; a < contentList.getLength(); a++) {
                            valBuf.append(contentList.item(a).getNodeValue());
                        }
                        String value = valBuf.toString().trim();
                        if (debugEnabled) {
                            log.debug("setupEnvironment() - defining parameter name=" + name + ", value=" + value);
                        }
                        callData.getParameters().put(name, new String[] { value });
                    }
                } catch (Throwable t) {
                    log.error("setupEnvironment() - Exception while parsing request-body [ignored]: ", t);
                }
                callData.setRequestBody(realBody);
                // XXX HACK for Flash XML handling END
            } else {
                callData.setRequestBody(requestBody);
            }
        } else {
            callData = new HTTPCallDataImpl(contellSession, requestBaseURL, requestHttpAuthBaseURL, contelligent
                    .getHTTPPort(), contelligent.getHTTPSPort(), currentProtocolHttps, currentHttpAuth, request,
                    response, getServletConfig().getServletContext(), true);
        }
        // the contelligent-taglib and jsp's access the call-data through the
        // http-request:
        // request.setAttribute(HTTPCallData.SESSION_KEY, callData);
        log.debug("setupEnvironment() - created calldata " + callData);
        // }
        ArrayList locales = new ArrayList();
        Enumeration enumeration = request.getLocales();

        while (enumeration.hasMoreElements()) {
            Object locale = enumeration.nextElement();

            if (locale != null) { // this is for orion
                locales.add(locale);
            }
        }
        callData.setLocales(locales);

        // create the category-map if not already done:
        // if (contellSession.getCategoryMap()==null ||
        // contellSession.getCategoryMap().size()==0) {
        // Map categoryMap =
        // contelligent.getCategoryManager().getCategoryMap(callData);
        // contellSession.setCategoryMap(categoryMap);
        // log.debug("setupEnvironment() - created category map
        // "+contellSession.getCategoryMap());
        // }

        if (newSession) {
            Map categoryMap = contelligent.getCategoryManager().getCategoryMap(callData, true);
            contellSession.setCategoryMap(categoryMap);
        }

        Map categoryMap = new HashMap(contellSession.getCategoryMap());
        categoryMap.putAll(contelligent.getCategoryManager().getCategoryMap(callData, false));

        // Actually check the clientsession flag instead of relying on the
        // user agent.
        if (contellSession.isClientSession()) {
            if (contelligent.isSecureClient() && !(request.isSecure() || request.getScheme().toLowerCase().equals("https"))) {
                throw new ContelligentRuntimeException(ContelligentExceptionID.security_client_requireSecure);
            }
            categoryMap.putAll(contelligent.getCategoryManager().getCategoryMap(callData.getParameters()));
        }
        callData.setCategoryMap(categoryMap);
        callData.setSearchEngineSpider(searchEngineSpider);
        ThreadedMem.setCallData(callData);
        if (debugEnabled)
            log.debug("setupEnvironment() - ... callData '" + callData + "' bound to threaded-mem.");

        if (configurePreview) {
            String config = request.getParameter(Contelligent.PREVIEW_CONFIG);
            String date = request.getParameter(Contelligent.PREVIEW_DATE);

            if (config != null) {
                try {
                    contelligent.configurePreview(new ComponentPath(config), date, contellSession, callData);
                } catch (ContelligentSecurityException cse) {
                    throw new SessionCreationException(cse.getMessage(), cse);
                } catch (Exception e) {
                    // if we cant configure the requested preview (possibly for
                    // security reasons), dont preview at all.
                    throw new SessionCreationException(e);
                }
            }
        }
        return callData;
    }

    private boolean isSearchEngineSpider(String userAgent) {
        userAgent = userAgent.toLowerCase();

        for (int i = 0; i < searchEngineBots.length; i++) {
            if (userAgent.indexOf(searchEngineBots[i]) != -1) {
                return true;
            }
        }
        return false;
    }

    /**
     * Returns the date the given component was modified in a per second
     * resolution to be used as Last-Modified header or -1 if the component is
     * either full dynamic or does not specifiy a modification date.
     */
    private long getLastModified(Component component, HTTPCallData callData, HttpServletRequest request) {

        // Since currently we have no way of finding the most recent component
        // of *all* components involved in the request, we leave this at the
        // current timestamp for everything. The only exception to this are
        // binaries, for which the content object is asked for a timestamp.
        // (Cant just use the ComponentContext, since sometimes even binaries
        // are dynamic)
        try {
            if (component instanceof ContentProvider) {
                Content content = ((ContentProvider) component).getContent();
                if (content instanceof BinaryContent) {
                    return content.lastModified(callData);
                }
            }
        } catch (Exception e) {
            log.error("getLastModified() - Exception: ", e);
        }
        return TimeService.getInstance().currentTimeMillis();
    }

    /**
     * Returns a map containing (String,String[]) entries mapping parameters
     * names to their corresponding value(s). If a parameter has only one value
     * the string array has length 1.
     * 
     * @param request
     *            a <code>HttpServletRequest</code> value
     * @return a non-null <code>Map</code> value
     * @see ServletRequest#getParameterValues
     */
    protected Map getRequestMap(HttpServletRequest request) {
        Enumeration enumeration = request.getParameterNames();
        Map requestMap = new HashMap();
        while (enumeration.hasMoreElements()) {
            String key = (String) enumeration.nextElement();
            String[] values = request.getParameterValues(key);
            requestMap.put(key, values);
        }
        return requestMap;
    }

    private void processRenderable(OutputStream outStream, Renderable renderable, boolean checkAccess,
            HTTPCallData callData, HttpServletRequest request, HttpServletResponse response, boolean includeBody)
            throws Exception {
        String channel = getChannel(callData.getCategoryMap());
        String encoding = getEncoding(channel);
        response.setContentType(getContentType(channel));
        if (includeBody) {
            Renderer renderer = renderable.getRenderer();
            Writer writer = new BufferedWriter(new OutputStreamWriter(outStream, encoding));
            ComponentPath realPath = renderable.getComponentContext().getPath();
            callData.pushRenderStack(realPath);
            // FIXME: get encoding from renderer
            renderer.render(writer, callData.getParameters(), callData);
            callData.popRenderStack();
            writer.flush();
        }
    }

    void processContent(OutputStream outStream, ContentProvider component, boolean checkAccess, HTTPCallData callData,
            HttpServletRequest request, HttpServletResponse response, boolean includeBody) throws Exception {
        Content content = component.getContent();
        if (content instanceof BinaryContent) {
            BinaryContent binaryContent = (BinaryContent) content;
            response.setContentType(binaryContent.getContentType(callData));
            // response.setContentLength(content.length(callData));
            if (includeBody) {
                // XXX: we directly use the output-stream of the response here
                // because of memory-usage!
                // this means we could not have a binary ContelligentProvider
                // which sets cookies
                // or make a proper error handling. (2002/12/10, rs)
                // if
                // (binaryContent.length(callData)<=contelligent.getSmallBinaryThreshold())
                // {
                // binaryContent.streamBinary(outStream, callData);
                // } else {
                binaryContent.streamBinary(response.getOutputStream(), callData);
                if (log.isDebugEnabled()) {
                    log.debug("... successfully streamed binary-content of '" + content + "'.");
                }
            }
        } else {
            if (component instanceof Renderable) {
                processRenderable(outStream, (Renderable) component, checkAccess, callData, request, response,
                        includeBody);
            } else {
                // FIXME: set content-type (but Content interface doesn't have
                // one ...!)
                // response.setContentType(content.getContentType(callData));
                String channel = getChannel(callData.getCategoryMap());
                String encoding = getEncoding(channel);
                response.setContentType(getContentType(channel));
                // response.setHeader("Content-Encoding", encoding);
                // response.setContentLength(content.length(callData));
                if (includeBody) {
                    String contentAsString = new DefaultRenderer(content).getContentAsString(callData);
                    Writer writer = new BufferedWriter(new OutputStreamWriter(outStream, encoding));
                    writer.write(contentAsString);
                    writer.flush();
                }
            }
        }
    }

}
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.