Java tutorial
/* * 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; } }