com.concursive.connect.web.portal.PortletManager.java Source code

Java tutorial

Introduction

Here is the source code for com.concursive.connect.web.portal.PortletManager.java

Source

/*
 * ConcourseConnect
 * Copyright 2009 Concursive Corporation
 * http://www.concursive.com
 *
 * This file is part of ConcourseConnect, an open source social business
 * software and community platform.
 *
 * Concursive ConcourseConnect 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, version 3 of the License.
 *
 * Under the terms of the GNU Affero General Public License you must release the
 * complete source code for any application that uses any part of ConcourseConnect
 * (system header files and libraries used by the operating system are excluded).
 * These terms must be included in any work that has ConcourseConnect components.
 * If you are developing and distributing open source applications under the
 * GNU Affero General Public License, then you are free to use ConcourseConnect
 * under the GNU Affero General Public License.
 *
 * If you are deploying a web site in which users interact with any portion of
 * ConcourseConnect over a network, the complete source code changes must be made
 * available.  For example, include a link to the source archive directly from
 * your web site.
 *
 * For OEMs, ISVs, SIs and VARs who distribute ConcourseConnect with their
 * products, and do not license and distribute their source code under the GNU
 * Affero General Public License, Concursive provides a flexible commercial
 * license.
 *
 * To anyone in doubt, we recommend the commercial license. Our commercial license
 * is competitively priced and will eliminate any confusion about how
 * ConcourseConnect can be used and distributed.
 *
 * ConcourseConnect 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 Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with ConcourseConnect.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Attribution Notice: ConcourseConnect is an Original Work of software created
 * by Concursive Corporation
 */
package com.concursive.connect.web.portal;

import com.concursive.commons.http.RequestUtils;
import com.concursive.commons.text.StringUtils;
import com.concursive.commons.web.mvc.actions.ActionContext;
import com.concursive.connect.Constants;
import com.concursive.connect.cache.utils.CacheUtils;
import com.concursive.connect.cms.portal.dao.DashboardPage;
import com.concursive.connect.cms.portal.dao.DashboardPortlet;
import com.concursive.connect.cms.portal.dao.DashboardPortletPrefs;
import com.concursive.connect.cms.portal.dao.DashboardTemplateList;
import com.concursive.connect.config.ApplicationPrefs;
import com.concursive.connect.config.ApplicationVersion;
import com.concursive.connect.web.modules.login.dao.User;
import com.concursive.connect.web.modules.profile.dao.Project;
import com.concursive.connect.web.portal.wsrp4j.consumer.proxyportlet.impl.ProducerRegistryImpl;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import oasis.names.tc.wsrp.v1.types.Property;
import oasis.names.tc.wsrp.v1.types.RegistrationData;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.pluto.PortletContainer;
import org.apache.pluto.PortletContainerException;
import org.apache.pluto.core.PortletContextManager;
import org.apache.pluto.driver.core.PortalRequestContext;
import org.apache.pluto.driver.core.PortalServletRequest;
import org.apache.pluto.driver.core.PortalServletResponse;
import org.apache.pluto.driver.core.PortletWindowImpl;
import org.apache.pluto.driver.services.portal.PortletWindowConfig;
import org.apache.pluto.driver.url.PortalURL;
import org.apache.pluto.driver.url.PortalURLParser;
import org.apache.wsrp4j.commons.consumer.driver.producer.ProducerImpl;
import org.apache.wsrp4j.commons.consumer.interfaces.producer.ProducerRegistry;
import org.apache.wsrp4j.commons.consumer.util.ConsumerConstants;
import org.apache.wsrp4j.commons.exception.WSRPException;

import javax.portlet.PortletException;
import javax.portlet.PortletMode;
import javax.portlet.WindowState;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import java.sql.Connection;
import java.util.*;
import java.util.concurrent.*;

/**
 * Handles fetching and rendering portlets
 *
 * @author matt rajkowski
 * @version $Id$
 * @created Feb 8, 2007
 */
public class PortletManager {

    public static final String CONCURSIVE_WSRP_PRODUCER_ID = "concursive-wsrp";

    private static final Log LOG = LogFactory.getLog(PortletManager.class);

    /**
     * @param context  The portal application's ActionContext
     * @param db       The database connection the page can use
     * @param thisPage The dashboard page to be rendered
     * @return if processing results in the portlet manager taking control of the dispatching for this request
     * @throws Exception any error
     */
    public static boolean processPage(ActionContext context, Connection db, DashboardPage thisPage)
            throws Exception {

        LOG.debug("processPage");

        ApplicationPrefs applicationPrefs = (ApplicationPrefs) context.getServletContext()
                .getAttribute("applicationPrefs");
        User user = (User) context.getSession().getAttribute(Constants.SESSION_USER);

        // The portal is using parameters in the URL, instead of directories, so enable them
        ArrayList<String> moduleParamNames = new ArrayList<String>();
        if (!thisPage.getObjectType().equals(DashboardTemplateList.TYPE_NAVIGATION)) {
            moduleParamNames.add("command");
            moduleParamNames.add("section");
            moduleParamNames.add("pid");
            moduleParamNames.add("dash");
            moduleParamNames.add("name");
            moduleParamNames.add("popup");
        }
        context.getRequest().setAttribute(PortalURLParserImpl.ALLOWED_PORTAL_PARAMETERS, moduleParamNames);

        // Populate the principal user that is required by the ProxyPortlet to establish sessions
        Map userInfo = new HashMap();
        userInfo.put("map", "init");
        userInfo.put("user.sessionId", String.valueOf(context.getSession().getId()));
        if (user != null) {
            userInfo.put("user.key", String.valueOf(user.getId()));
            userInfo.put("user.name.given", user.getFirstName());
            userInfo.put("user.name.family", user.getLastName());
        } else {
            userInfo.put("user.key", "-2");
            userInfo.put("user.name.given", "Guest");
        }
        context.getRequest().setAttribute("proxyportlet.user.info", userInfo);

        // Override Pluto's default mechanism for PortalURL
        PortalURLParser parser = null;
        if (thisPage.getObjectType().equals(DashboardTemplateList.TYPE_NAVIGATION)) {
            parser = ProjectPortalURLParserImpl.getParser();
        } else {
            parser = PortalURLParserImpl.getParser();
        }
        context.getRequest().setAttribute("ConcursivePortalURLParser", parser);

        // Get the portlet container
        PortletContainer container = (PortletContainer) context.getServletContext()
                .getAttribute("PortletContainer");

        // Initialize the portlet mappings
        String applicationId = checkRegistryService(context.getServletContext(),
                context.getServlet().getServletConfig(), context.getRequest());

        // Register ConcourseConnect consumer with the remote wsrp producer
        boolean isConsumerRegistered = checkServicesRegistry(context.getServletContext(), applicationPrefs);

        // Maintain the context for portlet communication this request
        PortalRequestContext portalRequestContext = new PortalRequestContext(context.getServletContext(),
                context.getRequest(), context.getResponse());
        PortalURL portalURL = portalRequestContext.getRequestedPortalURL();

        HashMap<String, Object> eventData = new HashMap<String, Object>();

        // NOTE: WSRP requires a context path to use the resource proxy, however, this prevents WSRP
        // from using the actual web-apps contextPath so URLs cannot be based on that
        String ctx = context.getRequest().getContextPath();
        if (!StringUtils.hasText(ctx)) {
            ctx = "/PlutoInvoker";
        }
        // Override WSRP's context path locator because it can't find a root context
        context.getServletContext().setAttribute(ConsumerConstants.WSRP_PORTLET_CONTEXT_PATH, ctx);

        // Build a list of portlets to process for this request
        ArrayList<PortletTask> portlets = new ArrayList<PortletTask>();

        // Priority 1: Process the lone action if there is one
        // Priority 2: Check the portlet windowState and portletMode so that only
        // the portlet being edited or the portlet that is maximized is rendered
        // Priority 3: Render portlets that generate events
        // Priority 4: Render all the other portlets
        // NOTE: Some portals like to show the minimized portlets in some way,
        // especially when editing content

        // Iterate the specific portlets on this page and add them for processing later
        int maximizedModeId = -1;
        int editModeId = -1;
        String actionWindowId = portalURL.getActionWindow();
        for (DashboardPortlet thisPortlet : thisPage.getPortletList()) {
            // Handle ProxyPortlet instances
            if ("ProxyPortlet".equals(thisPortlet.getName())) {
                if (!isConsumerRegistered) {
                    continue;
                }
                // Set the default portlet settings
                DashboardPortletPrefs portletHandle = thisPortlet.getDefaultPreferences()
                        .get(ConsumerConstants.WSRP_PORTLET_HANDLE);
                DashboardPortletPrefs producerId = new DashboardPortletPrefs(ConsumerConstants.WSRP_PRODUCER_ID,
                        PortletManager.CONCURSIVE_WSRP_PRODUCER_ID);
                DashboardPortletPrefs parentHandle = new DashboardPortletPrefs(ConsumerConstants.WSRP_PARENT_HANDLE,
                        portletHandle.getValues());
                // Inject proxy portlet preferences
                thisPortlet.getDefaultPreferences().put(ConsumerConstants.WSRP_PRODUCER_ID, producerId);
                thisPortlet.getDefaultPreferences().put(ConsumerConstants.WSRP_PARENT_HANDLE, parentHandle);
                // @todo if the portlet handle changed for this user, then do something...
                LOG.debug("portletHandle: " + portletHandle.getValue());
                LOG.debug("producerId: " + producerId.getValue());
                LOG.debug("parentHandle: " + parentHandle.getValue());
            }

            // Each portlet needs its own PortletWindow
            String windowConfigId;
            if (thisPortlet.getLoaded()) {
                windowConfigId = applicationId + "." + thisPortlet.getName() + "!" + thisPortlet.getId();
            } else {
                windowConfigId = applicationId + "." + thisPortlet.getName() + "!T" + thisPortlet.getId();
            }
            PortletWindowConfig windowConfig = PortletWindowConfig.fromId(windowConfigId);
            //windowConfig.setContextPath(context.getRequest().getContextPath());
            PortletWindowImpl portletWindow = new PortletWindowImpl(windowConfig, portalURL);
            // NOTE: potential concern when using different parsers
            thisPortlet.setWindowConfigId(PortalURLParserImpl.encodeCharacters(windowConfigId));
            thisPortlet.setPageName(thisPage.getName());

            // A single action
            if (actionWindowId != null) {
                if (actionWindowId.equals(portletWindow.getId().getStringId())) {
                    portlets.add(new PortletTask(thisPortlet, portletWindow));
                    break;
                }
                continue;
            }

            // A single edited portlet
            if (portletWindow.getPortletMode().equals(PortletMode.EDIT)) {
                portlets.clear();
                portlets.add(new PortletTask(thisPortlet, portletWindow));
                editModeId = thisPortlet.getId();
                break;
            }

            // A single maximized portlet
            if (portletWindow.getWindowState().equals(WindowState.MAXIMIZED)) {
                portlets.clear();
                portlets.add(new PortletTask(thisPortlet, portletWindow));
                maximizedModeId = thisPortlet.getId();
                break;
            }

            // Add generator portlets at the beginning
            if (!thisPortlet.getGenerateDataEvents().isEmpty()) {
                portlets.add(0, new PortletTask(thisPortlet, portletWindow));
            } else {
                portlets.add(new PortletTask(thisPortlet, portletWindow));
            }
        }
        context.getRequest().setAttribute("editModeId", editModeId);
        context.getRequest().setAttribute("maximizedModeId", maximizedModeId);

        // If ACTION then only the portlet the action for is executed
        if (actionWindowId != null) {
            PortletTask thisTask = portlets.get(0);
            DashboardPortlet thisPortlet = thisTask.getPortlet();
            PortletWindowImpl portletWindow = thisTask.getWindow();

            // Pass the request to the specified doAction
            // Since this is an embedded container, portlets can access the data store directly
            portalRequestContext.getRequest().setAttribute("connection", db);
            portalRequestContext.getRequest().setAttribute("dashboardPage", thisPage);
            portalRequestContext.getRequest().setAttribute("applicationPrefs", applicationPrefs);
            portalRequestContext.getRequest().setAttribute("user", user);
            portalRequestContext.getRequest().setAttribute("dashboardPortlet", thisPortlet);
            portalRequestContext.getRequest().setAttribute("objectHookManager",
                    context.getServletContext().getAttribute("ObjectHookManager"));
            portalRequestContext.getRequest().setAttribute("scheduler",
                    context.getServletContext().getAttribute("Scheduler"));
            portalRequestContext.getRequest().setAttribute("freemarkerConfiguration",
                    context.getServletContext().getAttribute("FreemarkerConfiguration"));
            portalRequestContext.getRequest().setAttribute("TEAM.KEY",
                    context.getServletContext().getAttribute("TEAM.KEY"));
            //portalRequestContext.getRequest().setAttribute("proxyportlet.user.info", context.getRequest().getAttribute("proxyportlet.user.info"));
            // If this portlet is requesting session data, provide it to the portlet
            if (!thisPortlet.getConsumeSessionData().isEmpty()) {
                for (String data : thisPortlet.getConsumeSessionData()) {
                    String contextName = context.getRequest().getContextPath();
                    if (contextName.startsWith("/")) {
                        contextName = contextName.substring(1);
                    }
                    // javax.portlet.p./.RegisterPortlet!T1?TEST=VALUE
                    // javax.portlet.p./contextname.RegisterPortlet!T1?TEST=VALUE
                    String sessionName = "javax.portlet.p./" + contextName + "." + thisPortlet.getName() + "!T"
                            + thisPortlet.getId() + "?" + data;
                    context.getSession().setAttribute(sessionName, context.getSession().getAttribute(data));
                }
            }
            // provide the application's url
            portalRequestContext.getRequest().setAttribute("url",
                    "http://" + RequestUtils.getServerUrl(context.getRequest()));
            // provide the secure url if enabled
            boolean sslEnabled = "true".equals(applicationPrefs.get("SSL"));
            String url = ("http" + (sslEnabled ? "s" : "") + "://"
                    + RequestUtils.getServerUrl(context.getRequest()));
            portalRequestContext.getRequest().setAttribute("secureUrl", url);
            try {
                container.doAction(portletWindow, portalRequestContext.getRequest(), context.getResponse());
                // @todo I think, if the WSRP portlet handle changed for this user, then record it here so that it can be used on the next request...
            } catch (PortletContainerException ex) {
                throw new ServletException(ex);
            } catch (PortletException ex) {
                throw new ServletException(ex);
            }
            LOG.debug("Action request processed.");
            // no more processing is needed, a portlet action was found
            return true;
        }

        // Render the portlets
        for (PortletTask task : portlets) {
            try {
                DashboardPortlet thisPortlet = task.getPortlet();

                // Bypass ProxyPortlet instances if consumer is not yet registered
                if ("ProxyPortlet".equals(thisPortlet.getName()) && !isConsumerRegistered) {
                    continue;
                }

                PortletWindowImpl portletWindow = task.getWindow();
                // Each uses a request/response
                PortalServletRequest portalRequest = new PortalServletRequest(context.getRequest(), portletWindow);
                PortalServletResponse portalResponse = new PortalServletResponse(context.getResponse());
                // The context path to be used for parsing pluto portlet references
                String contextName = context.getRequest().getContextPath();
                if (contextName != null && contextName.startsWith("/")) {
                    contextName = contextName.substring(1);
                }

                // Provide objects to the embedded portlets
                portalRequest.setAttribute("connection", db);
                portalRequest.setAttribute("dashboardPage", thisPage);
                portalRequest.setAttribute("applicationPrefs", applicationPrefs);
                portalRequest.setAttribute("user", context.getSession().getAttribute(Constants.SESSION_USER));
                portalRequest.setAttribute("dashboardPortlet", thisPortlet);
                portalRequest.setAttribute("objectHookManager",
                        context.getServletContext().getAttribute("ObjectHookManager"));
                portalRequest.setAttribute("scheduler", context.getServletContext().getAttribute("Scheduler"));
                portalRequest.setAttribute("freemarkerConfiguration",
                        context.getServletContext().getAttribute("FreemarkerConfiguration"));
                portalRequest.setAttribute("projectSearcher", context.getRequest().getAttribute("projectSearcher"));
                portalRequest.setAttribute("baseQueryString", context.getRequest().getAttribute("baseQueryString"));
                portalRequest.setAttribute("TEAM.KEY", context.getServletContext().getAttribute("TEAM.KEY"));
                portalRequest.setAttribute(AbstractPortletModule.COMMAND, thisPortlet.getViewer());
                //portalRequest.setAttribute("proxyportlet.user.info", context.getRequest().getAttribute("proxyportlet.user.info"));
                // Framework display parameters
                portalRequest.setAttribute("popup", context.getRequest().getParameter("popup"));
                // Script Node and XHR DataSources
                portalRequest.setAttribute("query", context.getRequest().getParameter("query"));
                // provide the application's url
                portalRequest.setAttribute("url", "http://" + RequestUtils.getServerUrl(context.getRequest()));
                // provide the secure url if enabled
                boolean sslEnabled = "true".equals(applicationPrefs.get("SSL"));
                String url = ("http" + (sslEnabled ? "s" : "") + "://"
                        + RequestUtils.getServerUrl(context.getRequest()));
                portalRequest.setAttribute("secureUrl", url);

                // If this portlet is requesting data, provide it to the portlet
                if (!thisPortlet.getConsumeDataEvents().isEmpty()) {
                    for (String event : thisPortlet.getConsumeDataEvents()) {
                        portalRequest.setAttribute("event" + event, eventData.get(event));
                    }
                }

                // Render the portlet, and have the PortalURL know which portlet is being rendered
                LOG.debug("Render windowId: " + portletWindow.getId().getStringId());
                portalURL.setRenderPath(portletWindow.getId().getStringId());

                // Add the base params to this portlet
                if (parser instanceof ProjectPortalURLParserImpl) {
                    LOG.debug("Adding all non-portlet specific parameters to portlet scope");
                    ProjectPortalURLParserImpl.addAllParameters(context.getRequest(), portalURL);
                }

                boolean renderedNewPortlet = renderPortlet(context, container, thisPage, thisPortlet, portletWindow,
                        portalRequest, portalResponse, isConsumerRegistered);
                if (!renderedNewPortlet) {
                    continue;
                }

                // If this portlet is sharing data, and the portlet was just rendered, then get it from the portlet and request
                if (!thisPortlet.getGenerateDataEvents().isEmpty()) {
                    if (LOG.isInfoEnabled()) {
                        Enumeration test = portalRequest.getAttributeNames();
                        while (test.hasMoreElements()) {
                            String thisName = (String) test.nextElement();
                            LOG.debug("Request attribute: " + thisName);
                        }
                    }
                    for (String event : thisPortlet.getGenerateDataEvents()) {
                        // Pluto_/.SearchResultsByProjectPortlet!T3_hits
                        // Pluto_/team/.SearchResultsByProjectPortlet!T3_hits
                        String thisPortletEvent = "Pluto_/" + contextName + "." + thisPortlet.getName() + "!T"
                                + thisPortlet.getId() + "_event" + event;
                        Object thisEvent = portalRequest.getAttribute(thisPortletEvent);
                        if (thisEvent == null) {
                            LOG.error("Shared event not found in context (" + contextName + ") : " + event);
                        }
                        eventData.put(event, portalRequest.getAttribute(thisPortletEvent));
                    }
                }
                // If this portlet is sharing request data, then get it from the portlet
                if (!thisPortlet.getGenerateRequestData().isEmpty()) {
                    for (String attribute : thisPortlet.getGenerateRequestData()) {
                        String thisPortletAttributeName = "Pluto_/" + contextName + "." + thisPortlet.getName()
                                + "!T" + thisPortlet.getId() + "_" + attribute;
                        Object thisAttribute = portalRequest.getAttribute(thisPortletAttributeName);
                        if (thisAttribute == null) {
                            LOG.error("Shared request object not found in context (" + contextName + ") : "
                                    + attribute);
                        }
                        portalRequest.setAttribute(attribute, thisAttribute);
                    }
                }
            } catch (Exception e) {
                LOG.error("Error loading portlet: " + e.getMessage());
                e.printStackTrace(System.out);
            }
        }
        return false;
    }

    public static synchronized String checkRegistryService(ServletContext context, ServletConfig config,
            HttpServletRequest request) throws PortletContainerException {
        // Add the registry service which preloads all of the portlets
        String contextPath = (String) context.getAttribute("PortletContextPath");
        if (contextPath == null) {
            PortletContextManager registryService = PortletContextManager.getManager();
            registryService.register(config);
            contextPath = request.getContextPath();
            if (!StringUtils.hasText(contextPath)) {
                // Pluto corrects for using a "/" as the context path
                contextPath = "/";
            }
            context.setAttribute("PortletContextPath", contextPath);
        }
        return contextPath;
    }

    private static synchronized boolean checkServicesRegistry(ServletContext context, ApplicationPrefs prefs) {
        if (!prefs.has("CONCURSIVE_SERVICES.SERVER")) {
            context.setAttribute("isConsumerRegistered", "false");
            return false;
        }
        boolean isConsumerRegistered = StringUtils.isTrue((String) context.getAttribute("isConsumerRegistered"));
        if (!isConsumerRegistered) {
            //load the WSRP producer information
            try {
                String wsrpServer = prefs.get(ApplicationPrefs.CONCURSIVE_SERVICES_SERVER);

                String markupURL = wsrpServer + "/services/WSRPBaseService";
                String serviceDescriptionURL = wsrpServer + "/services/WSRPServiceDescriptionService";
                String registrationURL = wsrpServer + "/services/WSRPRegistrationService";
                String portletManagementURL = wsrpServer + "/services/WSRPPortletManagementService";

                String consumerName = prefs.get(ApplicationPrefs.CONCURSIVE_SERVICES_ID);
                String consumerCode = prefs.get(ApplicationPrefs.CONCURSIVE_SERVICES_KEY);
                String consumerAgent = ApplicationVersion.TITLE + " " + ApplicationVersion.VERSION;

                ProducerImpl producer = new ProducerImpl(PortletManager.CONCURSIVE_WSRP_PRODUCER_ID, markupURL,
                        serviceDescriptionURL);
                //ID and misc info
                producer.setID(PortletManager.CONCURSIVE_WSRP_PRODUCER_ID);
                //WSRP registration interface
                producer.setRegistrationInterfaceEndpoint(registrationURL);
                //WSRP portlet management interface
                producer.setPortletManagementInterfaceEndpoint(portletManagementURL);

                //Registration
                RegistrationData registrationData = new RegistrationData();
                registrationData.setConsumerName(consumerName);
                registrationData.setConsumerAgent(consumerAgent);
                registrationData.setRegistrationProperties(new Property[2]);

                //Send the services key as a registration property; Server will authorize by enforcing a valid key
                Property key = new Property();
                key.setName(ApplicationPrefs.CONCURSIVE_SERVICES_KEY);
                key.setStringValue(consumerCode);

                //Send the services key as a registration property; Server will authorize by enforcing a valid key
                Property sessions = new Property();
                sessions.setName(ApplicationPrefs.CONSUMER_SESSION_SUPPORT);
                sessions.setStringValue("true");

                registrationData.setRegistrationProperties(0, key);
                registrationData.setRegistrationProperties(1, sessions);
                //producer.setRegistrationData(registrationData);

                //Register the consumer with the remote producer
                LOG.info("Registering ConcourseConnect consumer with remote producer <" + wsrpServer + ">: "
                        + producer.getID());
                producer.register(registrationData);

                ProducerRegistry producerRegistry = ProducerRegistryImpl.getInstance();
                producerRegistry.addProducer(producer);

                isConsumerRegistered = true;
                context.setAttribute("isConsumerRegistered", "true");
            } catch (WSRPException e) {
                isConsumerRegistered = false;
                LOG.error("Unable to register the consumer with the remote WSRP producer");
                context.setAttribute("isConsumerRegistered", "false");
            }
        }
        return isConsumerRegistered;
    }

    private static boolean renderPortlet(ActionContext context, PortletContainer container, DashboardPage thisPage,
            DashboardPortlet thisPortlet, PortletWindowImpl portletWindow, PortalServletRequest portalRequest,
            PortalServletResponse portalResponse, boolean isConsumerRegistered) {
        // Portlet Cache Implementation
        // Utilize the cache if the portlet is configured for caching,
        // skip portlets that share data with other portlets
        // skip when this is an ajax request
        if (!"text".equals(context.getParameter("out")) && thisPortlet.getCacheTime() > 0
                && thisPortlet.getGenerateDataEvents().isEmpty()
                && thisPortlet.getGenerateRequestData().isEmpty()) {
            // Check the cache for this portlet -- use a system-wide unique key
            String key = thisPage.getName() + "|" + thisPortlet.getWindowConfigId();
            LOG.debug("Checking the cache for key: " + key);
            Ehcache cache = CacheUtils.getCache(Constants.SYSTEM_DASHBOARD_PORTLET_CACHE);
            try {
                Element element = cache.get(key);
                if (element == null) {
                    // Render the portlet (important: all other cache.get calls will block until a put is called)
                    renderPortlet(context, container, thisPortlet, portletWindow, portalRequest, portalResponse);
                    // Set the cache
                    String portletCache = portalResponse.getInternalBuffer().toString();
                    element = new Element(key, portletCache);
                    element.setTimeToLive(thisPortlet.getCacheTime());
                    if (LOG.isDebugEnabled()) {
                        // Override the TTL for developers to 3 seconds
                        element.setTimeToLive(3);
                    }
                    cache.put(element);
                    context.getRequest().setAttribute("portal_response_" + thisPortlet.getId(), portletCache);
                    LOG.debug("Adding portlet response to the cache");
                    LOG.trace("Render response: " + portletCache);
                    return false;
                } else {
                    // Use the cached portlet
                    LOG.debug("Using the portlet cache value");
                    context.getRequest().setAttribute("portal_response_" + thisPortlet.getId(), element.getValue());
                    if (LOG.isTraceEnabled()) {
                        LOG.trace("Cached portlet data (" + element.getTimeToLive() + "): " + element.getValue());
                    }
                    return false;
                }
            } catch (Exception e) {
                // Release the lock
                cache.put(new Element(key, null));
                if ("ProxyPortlet".equals(thisPortlet.getName()) && isConsumerRegistered) {
                    LOG.debug("Unable to render a ProxyPortlet. Try to deregister first");
                }
                // The portlet could not be rendered so skip it
                LOG.error("Cache exception", e);
                return false;
            }
        } else {
            // No caching is involved, so render the portlet
            return renderPortlet(context, container, thisPortlet, portletWindow, portalRequest, portalResponse);
        }
    }

    public static class PortletRenderTask implements Callable<PortletWindowImpl> {
        private final PortletContainer container;
        private final PortletWindowImpl portletWindow;
        private final PortalServletRequest portalRequest;
        private final PortalServletResponse portalResponse;

        public PortletRenderTask(PortletContainer container, PortletWindowImpl portletWindow,
                PortalServletRequest portalRequest, PortalServletResponse portalResponse) {
            this.container = container;
            this.portletWindow = portletWindow;
            this.portalRequest = portalRequest;
            this.portalResponse = portalResponse;
        }

        public PortletWindowImpl call() throws Exception {
            try {
                // Remove the project since it might get set by another project
                if (LOG.isDebugEnabled()) {
                    LOG.debug("before container.doRender");
                    if (portalRequest.getAttribute("project") != null) {
                        LOG.debug("  The project for rendering: "
                                + ((Project) portalRequest.getAttribute("project")).getUniqueId());
                    }
                }
                portalRequest.removeAttribute("project");
                // Render the portlet
                container.doRender(portletWindow, portalRequest, portalResponse);
            } catch (Exception e) {
                LOG.error("Portlet render exception", e);
            }
            return portletWindow;
        }
    }

    private static boolean renderPortlet(ActionContext context, PortletContainer container,
            DashboardPortlet thisPortlet, PortletWindowImpl portletWindow, PortalServletRequest portalRequest,
            PortalServletResponse portalResponse) {
        ExecutorService executor = null;
        List<Future<PortletWindowImpl>> futures = null;
        try {
            long doRenderStartTime = System.currentTimeMillis();
            // NOTE: The infrastructure is here to run in threads, but more work needs to be done
            // for this to be reliable.  So the timeout feature of Executor cannot be used yet
            //      if (thisPortlet.getTimeout() <= 0) {
            if (true) {
                // Remove the project since it might get set by another project
                if (LOG.isDebugEnabled()) {
                    LOG.debug("before container.doRender");
                    if (portalRequest.getAttribute("project") != null) {
                        LOG.debug("  The project for rendering: "
                                + ((Project) portalRequest.getAttribute("project")).getUniqueId());
                    }
                }
                portalRequest.removeAttribute("project");
                // Render the portlet immediately
                container.doRender(portletWindow, portalRequest, portalResponse);
            } else {
                // Render the portlet using an executor; this will allow for cancelling timed-out portlets
                LOG.debug("Using executor...");
                List<PortletRenderTask> renderTasks = new ArrayList<PortletRenderTask>();
                renderTasks.add(new PortletRenderTask(container, portletWindow, portalRequest, portalResponse));
                executor = Executors.newFixedThreadPool(1);
                // NOTE: this wrapper fix is for Java 1.5
                final Collection<Callable<PortletWindowImpl>> wrapper = Collections
                        .<Callable<PortletWindowImpl>>unmodifiableCollection(renderTasks);
                if (thisPortlet.getTimeout() <= 0) {
                    futures = executor.invokeAll(wrapper);
                } else {
                    futures = executor.invokeAll(wrapper, thisPortlet.getTimeout(), TimeUnit.SECONDS);
                }
                for (Future<PortletWindowImpl> f : futures) {
                    if (f.isCancelled()) {
                        LOG.debug("Portlet was cancelled due to timeout");
                        return false;
                    }
                }
            }
            long doRenderEndTime = System.currentTimeMillis();
            LOG.debug("Portlet (" + thisPortlet.getName() + ") took: " + (doRenderEndTime - doRenderStartTime)
                    + " ms");
            // When the portlet is rendered, place the response in the request for displaying later
            context.getRequest().setAttribute("portal_response_" + thisPortlet.getId(),
                    portalResponse.getInternalBuffer().toString());
            LOG.trace("Render response: " + portalResponse.getInternalBuffer().toString());
        } catch (Exception re) {
            // The portlet could not be rendered so skip it
            LOG.error("Render exception", re);
            return false;
        } finally {
            if (executor != null) {
                executor.shutdown();
            }
        }
        return true;
    }
}