/*
* 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.web.mvc.beans.GenericBean;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.portlet.*;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
/**
* This class provides a framework for developing MVC portlets by utilizing
* IPortletViewer and IPortletAction implementations.
* <p/>
* IPortletViewer implementations return a JSP file for invoking, IPortletAction
* implementations return null OR a bean to be auto-supplied to a view.
*
* @author matt rajkowski
*/
public abstract class AbstractPortletModule extends GenericPortlet {
// Logger
private static Log LOG = LogFactory.getLog(AbstractPortletModule.class);
/**
* The key to the map for an action and its associated viewer.
*/
public static final String COMMAND = "portlet-command";
/**
* The storage prefix for a form bean in the session
*/
public static final String FORM_BEAN = "portletFormBean";
/**
* A map containing all the IPortletViewer objects for a given portlet.
*/
protected Map<String, IPortletViewer> viewers = new HashMap<String, IPortletViewer>();
/**
* A map containing all the IPortletAction objects for a given portlet.
*/
protected Map<String, IPortletAction> actions = new HashMap<String, IPortletAction>();
/**
* The default command for a portlet. This is the value of the command that the
* portlet framework uses to determine the first viewer to use when the portlet
* is rendered for the first time.
*/
protected String defaultCommand;
/**
* This is the standard GenericPortlet's init method. It has the additional
* responsibility of putting the actions and viewers into the viewer and
* action maps for subsequent use by the framework.
*/
public void init(PortletConfig portletConfig) throws PortletException {
super.init(portletConfig);
LOG.info("Initializing: " + this.getClass().getSimpleName());
doPopulateActionsAndViewers();
}
protected abstract void doPopulateActionsAndViewers();
protected void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException {
LOG.debug("Determining routing using viewers map (" + viewers.size() + ") and actions map (" + actions.size() + ")");
if (LOG.isTraceEnabled()) {
showAllRequestParameters(request);
showAllSessionParameters(request);
}
try {
// Determine the command to execute, as set by the portlet
String command = request.getParameter(COMMAND);
if (!StringUtils.isEmpty(command)) {
LOG.debug("using command from portlet: " + command);
} else {
// Determine the command to execute, as set by the portlet session
PortletSession session = request.getPortletSession(false);
if (session != null) {
command = (String) session.getAttribute(COMMAND);
}
if (!StringUtils.isEmpty(command)) {
LOG.debug("using command from portlet action: " + command);
session.removeAttribute(COMMAND);
} else {
// Determine the command to execute, as set by the portal
command = (String) request.getAttribute(COMMAND);
if (!StringUtils.isEmpty(command)) {
LOG.debug("using command from portal: " + command);
} else {
// Use the default command
command = defaultCommand;
LOG.debug("using default command: " + command);
}
}
}
request.setAttribute("portletCommand", command);
// Retrieve the viewer from the map
LOG.debug("Searching for viewer using command: " + command);
IPortletViewer viewer = viewers.get(command);
if (viewer == null) {
LOG.error("A viewer was not found for command: " + command);
} else {
// Manage the form bean and make available to the request
PortalUtils.processFormBean(request);
// Execute the command
LOG.debug("Executing viewer: " + viewer.getClass().getName());
String jsp = viewer.doView(request, response);
// The viewer optionally returns a JSP view
if (jsp != null) {
PortletContext context = getPortletContext();
PortletRequestDispatcher requestDispatcher = context.getRequestDispatcher(jsp);
if (requestDispatcher == null) {
LOG.error("JSP compile error: " + jsp);
}
LOG.debug("Dispatching to JSP: " + jsp);
requestDispatcher.include(request, response);
}
}
} catch (Throwable t) {
LOG.error("doView", t);
}
}
public void processAction(ActionRequest request, ActionResponse response) {
if (actions.size() == 0) {
return;
}
if (LOG.isTraceEnabled()) {
showAllRequestParameters(request);
showAllSessionParameters(request);
}
// Determine the command to execute, as set by the portlet
String command = request.getParameter(COMMAND);
if (!StringUtils.isEmpty(command)) {
LOG.debug("using command from portlet: " + command);
} else {
// Determine the command to execute, as set by the portal
command = (String) request.getAttribute(COMMAND);
if (!StringUtils.isEmpty(command)) {
LOG.debug("using command from portal: " + command);
} else {
LOG.error("could not find a command");
}
}
request.setAttribute("portletCommand", command);
// Retrieve the action from the map
LOG.debug("Searching for action using command: " + command);
IPortletAction action = actions.get(command);
if (action == null) {
LOG.error("An action was not found for command: " + command);
}
try {
GenericBean formBean = action.processAction(request, response);
if (formBean != null) {
// Need to add the form bean to the portlet session because the portal
// uses redirects which voids the request
PortletSession session = request.getPortletSession();
session.setAttribute(FORM_BEAN, formBean);
// Handle parameters
if ("true".equals(request.getParameter("popup"))) {
response.setRenderParameter("popup", "true");
}
if (request.getParameter("redirectTo") != null) {
response.setRenderParameter("redirectTo", request.getParameter("redirectTo"));
}
if (request.getAttribute("portlet-command") != null) {
session.setAttribute(COMMAND, request.getAttribute("portlet-command"));
}
}
} catch (Throwable t) {
LOG.error("Error with command: " + command, t);
}
}
private void showAllRequestParameters(PortletRequest request) {
Enumeration paramNames = request.getParameterNames();
LOG.trace("=============== Portlet Request Parameters ===============");
while (paramNames.hasMoreElements()) {
String paramName = (String) paramNames.nextElement();
String[] paramValues = request.getParameterValues(paramName);
if (paramValues.length == 1) {
String paramValue = paramValues[0];
LOG.trace(paramName + "=" + paramValue);
} else {
for (String paramValue : paramValues) {
LOG.trace(paramName + "=" + paramValue);
}
LOG.trace("====");
}
}
}
private void showAllSessionParameters(PortletRequest request) {
//HttpSession pSession = ((HttpServletRequest) request).getSession();
PortletSession pSession = request.getPortletSession();
Enumeration attrNames = pSession.getAttributeNames();
LOG.trace("=============== Portlet Session Parameters ===============");
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
Object attrValue = pSession.getAttribute(attrName);
LOG.trace(attrName + "=[" + attrValue + "]");
}
}
}