Return-Path: X-Original-To: apmail-struts-commits-archive@minotaur.apache.org Delivered-To: apmail-struts-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id CFED946F7 for ; Thu, 23 Jun 2011 20:19:11 +0000 (UTC) Received: (qmail 97877 invoked by uid 500); 23 Jun 2011 20:19:11 -0000 Delivered-To: apmail-struts-commits-archive@struts.apache.org Received: (qmail 97831 invoked by uid 500); 23 Jun 2011 20:19:11 -0000 Mailing-List: contact commits-help@struts.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@struts.apache.org Delivered-To: mailing list commits@struts.apache.org Received: (qmail 97713 invoked by uid 99); 23 Jun 2011 20:19:11 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 23 Jun 2011 20:19:11 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 23 Jun 2011 20:19:07 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id 3BCBE2388994 for ; Thu, 23 Jun 2011 20:18:47 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1139061 [2/5] - in /struts/archive/plugins: ./ src/ src/main/ src/main/java/ src/main/java/org/ src/main/java/org/apache/ src/main/java/org/apache/struts2/ src/main/java/org/apache/struts2/components/ src/main/java/org/apache/struts2/portl... Date: Thu, 23 Jun 2011 20:18:46 -0000 To: commits@struts.apache.org From: jogep@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20110623201847.3BCBE2388994@eris.apache.org> Added: struts/archive/plugins/src/main/java/org/apache/struts2/portlet/dispatcher/Jsr168Dispatcher.java URL: http://svn.apache.org/viewvc/struts/archive/plugins/src/main/java/org/apache/struts2/portlet/dispatcher/Jsr168Dispatcher.java?rev=1139061&view=auto ============================================================================== --- struts/archive/plugins/src/main/java/org/apache/struts2/portlet/dispatcher/Jsr168Dispatcher.java (added) +++ struts/archive/plugins/src/main/java/org/apache/struts2/portlet/dispatcher/Jsr168Dispatcher.java Thu Jun 23 20:18:43 2011 @@ -0,0 +1,628 @@ +/* + * $Id: Jsr168Dispatcher.java 1099157 2011-05-03 17:53:55Z jogep $ + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.struts2.portlet.dispatcher; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +import javax.portlet.ActionRequest; +import javax.portlet.ActionResponse; +import javax.portlet.GenericPortlet; +import javax.portlet.PortletConfig; +import javax.portlet.PortletException; +import javax.portlet.PortletMode; +import javax.portlet.PortletRequest; +import javax.portlet.PortletResponse; +import javax.portlet.RenderRequest; +import javax.portlet.RenderResponse; +import javax.portlet.WindowState; +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.struts2.StrutsConstants; +import org.apache.struts2.StrutsException; +import org.apache.struts2.StrutsStatics; +import org.apache.struts2.dispatcher.ApplicationMap; +import org.apache.struts2.dispatcher.Dispatcher; +import org.apache.struts2.dispatcher.RequestMap; +import org.apache.struts2.dispatcher.SessionMap; +import org.apache.struts2.dispatcher.mapper.ActionMapper; +import org.apache.struts2.dispatcher.mapper.ActionMapping; +import org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper; +import org.apache.struts2.portlet.PortletActionConstants; +import org.apache.struts2.portlet.PortletApplicationMap; +import org.apache.struts2.portlet.PortletRequestMap; +import org.apache.struts2.portlet.PortletSessionMap; +import org.apache.struts2.portlet.context.PortletActionContext; +import org.apache.struts2.portlet.servlet.PortletServletContext; +import org.apache.struts2.portlet.servlet.PortletServletRequest; +import org.apache.struts2.portlet.servlet.PortletServletResponse; +import org.apache.struts2.util.AttributeMap; +import org.apache.commons.lang.StringUtils; + +import com.opensymphony.xwork2.ActionContext; +import com.opensymphony.xwork2.ActionProxy; +import com.opensymphony.xwork2.ActionProxyFactory; +import com.opensymphony.xwork2.config.ConfigurationException; +import com.opensymphony.xwork2.inject.Container; +import com.opensymphony.xwork2.util.FileManager; +import com.opensymphony.xwork2.util.LocalizedTextUtil; +import com.opensymphony.xwork2.util.logging.Logger; +import com.opensymphony.xwork2.util.logging.LoggerFactory; + +/** + * + *

+ * Struts JSR-168 portlet dispatcher. Similar to the WW2 Servlet dispatcher, + * but adjusted to a portal environment. The portlet is configured through the portlet.xml + * descriptor. Examples and descriptions follow below: + *

+ * + * + * @author Nils-Helge Garli + * @author Rainer Hermanns + * + *

Init parameters

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
NameDescriptionDefault value
portletNamespaceThe namespace for the portlet in the xwork configuration. This + * namespace is prepended to all action lookups, and makes it possible to host multiple + * portlets in the same portlet application. If this parameter is set, the complete namespace + * will be /portletNamespace/modeNamespace/actionNameThe default namespace
viewNamespaceBase namespace in the xwork configuration for the view portlet + * modeThe default namespace
editNamespaceBase namespace in the xwork configuration for the edit portlet + * modeThe default namespace
helpNamespaceBase namespace in the xwork configuration for the help portlet + * modeThe default namespace
defaultViewActionDefault action to invoke in the view portlet mode if no action is + * specifieddefault
defaultEditActionDefault action to invoke in the edit portlet mode if no action is + * specifieddefault
defaultHelpActionDefault action to invoke in the help portlet mode if no action is + * specifieddefault
+ * + *

Example:

+ *
+ * 
+ *
+ * <init-param>
+ *     <!-- The view mode namespace. Maps to a namespace in the xwork config file -->
+ *     <name>viewNamespace</name>
+ *     <value>/view</value>
+ * </init-param>
+ * <init-param>
+ *    <!-- The default action to invoke in view mode -->
+ *    <name>defaultViewAction</name>
+ *    <value>index</value>
+ * </init-param>
+ * <init-param>
+ *     <!-- The view mode namespace. Maps to a namespace in the xwork config file -->
+ *     <name>editNamespace</name>
+ *     <value>/edit</value>
+ * </init-param>
+ * <init-param>
+ *     <!-- The default action to invoke in view mode -->
+ *     <name>defaultEditAction</name>
+ *     <value>index</value>
+ * </init-param>
+ * <init-param>
+ *     <!-- The view mode namespace. Maps to a namespace in the xwork config file -->
+ *     <name>helpNamespace</name>
+ *     <value>/help</value>
+ * </init-param>
+ * <init-param>
+ *     <!-- The default action to invoke in view mode -->
+ *     <name>defaultHelpAction</name>
+ *     <value>index</value>
+ * </init-param>
+ *
+ * 
+ * 
+ */ +public class Jsr168Dispatcher extends GenericPortlet implements StrutsStatics, + PortletActionConstants { + + private static final Logger LOG = LoggerFactory.getLogger(Jsr168Dispatcher.class); + + private ActionProxyFactory factory = null; + + private Map modeMap = new HashMap(3); + + private Map actionMap = new HashMap(3); + + private String portletNamespace = null; + + private Dispatcher dispatcherUtils; + + private ActionMapper actionMapper; + + private Container container; + + /** + * Initialize the portlet with the init parameters from portlet.xml + */ + public void init(PortletConfig cfg) throws PortletException { + super.init(cfg); + if (LOG.isDebugEnabled()) LOG.debug("Initializing portlet " + getPortletName()); + + Map params = new HashMap(); + for (Enumeration e = cfg.getInitParameterNames(); e.hasMoreElements(); ) { + String name = (String) e.nextElement(); + String value = cfg.getInitParameter(name); + params.put(name, value); + } + + dispatcherUtils = new Dispatcher(new PortletServletContext(cfg.getPortletContext()), params); + dispatcherUtils.init(); + + // For testability + if (factory == null) { + factory = dispatcherUtils.getConfigurationManager().getConfiguration().getContainer().getInstance(ActionProxyFactory.class); + } + portletNamespace = cfg.getInitParameter("portletNamespace"); + if (LOG.isDebugEnabled()) LOG.debug("PortletNamespace: " + portletNamespace); + parseModeConfig(actionMap, cfg, PortletMode.VIEW, "viewNamespace", + "defaultViewAction"); + parseModeConfig(actionMap, cfg, PortletMode.EDIT, "editNamespace", + "defaultEditAction"); + parseModeConfig(actionMap, cfg, PortletMode.HELP, "helpNamespace", + "defaultHelpAction"); + parseModeConfig(actionMap, cfg, new PortletMode("config"), "configNamespace", + "defaultConfigAction"); + parseModeConfig(actionMap, cfg, new PortletMode("about"), "aboutNamespace", + "defaultAboutAction"); + parseModeConfig(actionMap, cfg, new PortletMode("print"), "printNamespace", + "defaultPrintAction"); + parseModeConfig(actionMap, cfg, new PortletMode("preview"), "previewNamespace", + "defaultPreviewAction"); + parseModeConfig(actionMap, cfg, new PortletMode("edit_defaults"), + "editDefaultsNamespace", "defaultEditDefaultsAction"); + if (StringUtils.isEmpty(portletNamespace)) { + portletNamespace = ""; + } + LocalizedTextUtil + .addDefaultResourceBundle("org/apache/struts2/struts-messages"); + + container = dispatcherUtils.getContainer(); + //check for configuration reloading + if ("true".equalsIgnoreCase(container.getInstance(String.class, StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD))) { + FileManager.setReloadingConfigs(true); + } + + actionMapper = container.getInstance(ActionMapper.class); + } + + /** + * Parse the mode to namespace mappings configured in portlet.xml + * @param actionMap The map with mode <-> default action mapping. + * @param portletConfig The PortletConfig. + * @param portletMode The PortletMode. + * @param nameSpaceParam Name of the init parameter where the namespace for the mode + * is configured. + * @param defaultActionParam Name of the init parameter where the default action to + * execute for the mode is configured. + */ + void parseModeConfig(Map actionMap, PortletConfig portletConfig, + PortletMode portletMode, String nameSpaceParam, + String defaultActionParam) { + String namespace = portletConfig.getInitParameter(nameSpaceParam); + if (StringUtils.isEmpty(namespace)) { + namespace = ""; + } + modeMap.put(portletMode, namespace); + String defaultAction = portletConfig + .getInitParameter(defaultActionParam); + String method = null; + if (StringUtils.isEmpty(defaultAction)) { + defaultAction = DEFAULT_ACTION_NAME; + } + if(defaultAction.indexOf('!') >= 0) { + method = defaultAction.substring(defaultAction.indexOf('!') + 1); + defaultAction = defaultAction.substring(0, defaultAction.indexOf('!')); + } + StringBuffer fullPath = new StringBuffer(); + if (StringUtils.isNotEmpty(portletNamespace)) { + fullPath.append(portletNamespace); + } + if (StringUtils.isNotEmpty(namespace)) { + fullPath.append(namespace).append("/"); + } else { + fullPath.append("/"); + } + fullPath.append(defaultAction); + ActionMapping mapping = new ActionMapping(); + mapping.setName(getActionName(fullPath.toString())); + mapping.setNamespace(getNamespace(fullPath.toString())); + if(method != null) { + mapping.setMethod(method); + } + actionMap.put(portletMode, mapping); + } + + /** + * Service an action from the event phase. + * + * @see javax.portlet.Portlet#processAction(javax.portlet.ActionRequest, + * javax.portlet.ActionResponse) + */ + public void processAction(ActionRequest request, ActionResponse response) + throws PortletException, IOException { + if (LOG.isDebugEnabled()) LOG.debug("Entering processAction"); + resetActionContext(); + try { + serviceAction(request, response, getRequestMap(request), getParameterMap(request), + getSessionMap(request), getApplicationMap(), + portletNamespace, EVENT_PHASE); + if (LOG.isDebugEnabled()) LOG.debug("Leaving processAction"); + } finally { + ActionContext.setContext(null); + } + } + + /** + * Service an action from the render phase. + * + * @see javax.portlet.Portlet#render(javax.portlet.RenderRequest, + * javax.portlet.RenderResponse) + */ + public void render(RenderRequest request, RenderResponse response) + throws PortletException, IOException { + + if (LOG.isDebugEnabled()) LOG.debug("Entering render"); + resetActionContext(); + response.setTitle(getTitle(request)); + if(!request.getWindowState().equals(WindowState.MINIMIZED)) { + try { + // Check to see if an event set the render to be included directly + serviceAction(request, response, getRequestMap(request), getParameterMap(request), + getSessionMap(request), getApplicationMap(), + portletNamespace, RENDER_PHASE); + if (LOG.isDebugEnabled()) LOG.debug("Leaving render"); + } finally { + resetActionContext(); + } + } + } + + /** + * Reset the action context. + */ + private void resetActionContext() { + ActionContext.setContext(null); + } + + /** + * Merges all application and portlet attributes into a single + * HashMap to represent the entire Action context. + * + * @param requestMap a Map of all request attributes. + * @param parameterMap a Map of all request parameters. + * @param sessionMap a Map of all session attributes. + * @param applicationMap a Map of all servlet context attributes. + * @param request the PortletRequest object. + * @param response the PortletResponse object. + * @param portletConfig the PortletConfig object. + * @param phase The portlet phase (render or action, see + * {@link PortletActionConstants}) + * @return a HashMap representing the Action context. + */ + public HashMap createContextMap(Map requestMap, Map parameterMap, + Map sessionMap, Map applicationMap, PortletRequest request, + PortletResponse response, HttpServletRequest servletRequest, HttpServletResponse servletResponse, ServletContext servletContext, PortletConfig portletConfig, Integer phase) throws IOException { + + // TODO Must put http request/response objects into map for use with + container.inject(servletRequest); + + // ServletActionContext + HashMap extraContext = new HashMap(); + // The dummy servlet objects. Eases reuse of existing interceptors that uses the servlet objects. + extraContext.put(StrutsStatics.HTTP_REQUEST, servletRequest); + extraContext.put(StrutsStatics.HTTP_RESPONSE, servletResponse); + extraContext.put(StrutsStatics.SERVLET_CONTEXT, servletContext); + // End dummy servlet objects + extraContext.put(ActionContext.PARAMETERS, parameterMap); + extraContext.put(ActionContext.SESSION, sessionMap); + extraContext.put(ActionContext.APPLICATION, applicationMap); + + String defaultLocale = dispatcherUtils.getContainer().getInstance(String.class, StrutsConstants.STRUTS_LOCALE); + Locale locale = null; + if (defaultLocale != null) { + locale = LocalizedTextUtil.localeFromString(defaultLocale, request.getLocale()); + } else { + locale = request.getLocale(); + } + extraContext.put(ActionContext.LOCALE, locale); + + extraContext.put(StrutsStatics.STRUTS_PORTLET_CONTEXT, getPortletContext()); + extraContext.put(REQUEST, request); + extraContext.put(RESPONSE, response); + extraContext.put(PORTLET_CONFIG, portletConfig); + extraContext.put(PORTLET_NAMESPACE, portletNamespace); + extraContext.put(DEFAULT_ACTION_FOR_MODE, actionMap.get(request.getPortletMode())); + // helpers to get access to request/session/application scope + extraContext.put("request", requestMap); + extraContext.put("session", sessionMap); + extraContext.put("application", applicationMap); + extraContext.put("parameters", parameterMap); + extraContext.put(MODE_NAMESPACE_MAP, modeMap); + + extraContext.put(PHASE, phase); + + AttributeMap attrMap = new AttributeMap(extraContext); + extraContext.put("attr", attrMap); + + return extraContext; + } + + /** + * Loads the action and executes it. This method first creates the action + * context from the given parameters then loads an ActionProxy + * from the given action name and namespace. After that, the action is + * executed and output channels throught the response object. + * + * @param request the HttpServletRequest object. + * @param response the HttpServletResponse object. + * @param requestMap a Map of request attributes. + * @param parameterMap a Map of request parameters. + * @param sessionMap a Map of all session attributes. + * @param applicationMap a Map of all application attributes. + * @param portletNamespace the namespace or context of the action. + * @param phase The portlet phase (render or action, see + * {@link PortletActionConstants}) + */ + public void serviceAction(PortletRequest request, PortletResponse response, Map requestMap, Map parameterMap, + Map sessionMap, Map applicationMap, String portletNamespace, + Integer phase) throws PortletException { + if (LOG.isDebugEnabled()) LOG.debug("serviceAction"); + Dispatcher.setInstance(dispatcherUtils); + String actionName = null; + String namespace = null; + try { + ServletContext servletContext = new PortletServletContext(getPortletContext()); + HttpServletRequest servletRequest = new PortletServletRequest(request, getPortletContext()); + HttpServletResponse servletResponse = new PortletServletResponse(response); + if(EVENT_PHASE.equals(phase)) { + servletRequest = dispatcherUtils.wrapRequest(servletRequest, servletContext); + if(servletRequest instanceof MultiPartRequestWrapper) { + // Multipart request. Request parameters are encoded in the multipart data, + // so we need to manually add them to the parameter map. + parameterMap.putAll(servletRequest.getParameterMap()); + } + } + container.inject(servletRequest); + ActionMapping mapping = getActionMapping(request, servletRequest); + actionName = mapping.getName(); + namespace = mapping.getNamespace(); + HashMap extraContext = createContextMap(requestMap, parameterMap, + sessionMap, applicationMap, request, response, servletRequest, servletResponse, + servletContext, getPortletConfig(), phase); + extraContext.put(PortletActionConstants.ACTION_MAPPING, mapping); + if (LOG.isDebugEnabled()) { + LOG.debug("Creating action proxy for name = " + actionName + ", namespace = " + namespace); + } + ActionProxy proxy = factory.createActionProxy(namespace, + actionName, mapping.getMethod(), extraContext); + request.setAttribute("struts.valueStack", proxy.getInvocation() + .getStack()); + proxy.execute(); + } catch (ConfigurationException e) { + LOG.error("Could not find action", e); + throw new PortletException("Could not find action " + actionName, e); + } catch (Exception e) { + LOG.error("Could not execute action", e); + throw new PortletException("Error executing action " + actionName, + e); + } finally { + Dispatcher.setInstance(null); + } + } + + /** + * Returns a Map of all application attributes. Copies all attributes from + * the {@link PortletActionContext}into an {@link ApplicationMap}. + * + * @return a Map of all application attributes. + */ + protected Map getApplicationMap() { + return new PortletApplicationMap(getPortletContext()); + } + + /** + * Gets the namespace of the action from the request. The namespace is the + * same as the portlet mode. E.g, view mode is mapped to namespace + * view, and edit mode is mapped to the namespace + * edit + * + * @param request the PortletRequest object. + * @return the namespace of the action. + */ + protected ActionMapping getActionMapping(final PortletRequest portletRequest, final HttpServletRequest servletRequest) { + ActionMapping mapping = null; + String actionPath = null; + if (resetAction(portletRequest)) { + mapping = (ActionMapping) actionMap.get(portletRequest.getPortletMode()); + } else { + actionPath = servletRequest.getParameter(ACTION_PARAM); + if (StringUtils.isEmpty(actionPath)) { + mapping = (ActionMapping) actionMap.get(portletRequest + .getPortletMode()); + } else { + + // Use the usual action mapper, but it is expecting an action extension + // on the uri, so we add the default one, which should be ok as the + // portlet is a portlet first, a servlet second + mapping = actionMapper.getMapping(servletRequest, dispatcherUtils.getConfigurationManager()); + } + } + + if (mapping == null) { + throw new StrutsException("Unable to locate action mapping for request, probably due to " + + "an invalid action path: "+actionPath); + } + return mapping; + } + + /** + * Get the namespace part of the action path. + * @param actionPath Full path to action + * @return The namespace part. + */ + String getNamespace(String actionPath) { + int idx = actionPath.lastIndexOf('/'); + String namespace = ""; + if (idx >= 0) { + namespace = actionPath.substring(0, idx); + } + return namespace; + } + + /** + * Get the action name part of the action path. + * @param actionPath Full path to action + * @return The action name. + */ + String getActionName(String actionPath) { + int idx = actionPath.lastIndexOf('/'); + String action = actionPath; + if (idx >= 0) { + action = actionPath.substring(idx + 1); + } + return action; + } + + /** + * Returns a Map of all request parameters. This implementation just calls + * {@link PortletRequest#getParameterMap()}. + * + * @param request the PortletRequest object. + * @return a Map of all request parameters. + * @throws IOException if an exception occurs while retrieving the parameter + * map. + */ + protected Map getParameterMap(PortletRequest request) throws IOException { + return new HashMap(request.getParameterMap()); + } + + /** + * Returns a Map of all request attributes. The default implementation is to + * wrap the request in a {@link RequestMap}. Override this method to + * customize how request attributes are mapped. + * + * @param request the PortletRequest object. + * @return a Map of all request attributes. + */ + protected Map getRequestMap(PortletRequest request) { + return new PortletRequestMap(request); + } + + /** + * Returns a Map of all session attributes. The default implementation is to + * wrap the reqeust in a {@link SessionMap}. Override this method to + * customize how session attributes are mapped. + * + * @param request the PortletRequest object. + * @return a Map of all session attributes. + */ + protected Map getSessionMap(PortletRequest request) { + return new PortletSessionMap(request); + } + + /** + * Convenience method to ease testing. + * @param factory + */ + protected void setActionProxyFactory(ActionProxyFactory factory) { + this.factory = factory; + } + + /** + * Check to see if the action parameter is valid for the current portlet mode. If the portlet + * mode has been changed with the portal widgets, the action name is invalid, since the + * action name belongs to the previous executing portlet mode. If this method evaluates to + * true the default<Mode>Action is used instead. + * @param request The portlet request. + * @return true if the action should be reset. + */ + private boolean resetAction(PortletRequest request) { + boolean reset = false; + Map paramMap = request.getParameterMap(); + String[] modeParam = (String[]) paramMap.get(MODE_PARAM); + if (modeParam != null && modeParam.length == 1) { + String originatingMode = modeParam[0]; + String currentMode = request.getPortletMode().toString(); + if (!currentMode.equals(originatingMode)) { + reset = true; + } + } + if(reset) { + request.setAttribute(ACTION_RESET, Boolean.TRUE); + } + else { + request.setAttribute(ACTION_RESET, Boolean.FALSE); + } + return reset; + } + + public void destroy() { + if (dispatcherUtils == null) { + if (LOG.isWarnEnabled()) { + LOG.warn("something is seriously wrong, DispatcherUtil is not initialized (null) "); + } + } else { + dispatcherUtils.cleanup(); + } + } + + /** + * @param actionMapper the actionMapper to set + */ + public void setActionMapper(ActionMapper actionMapper) { + this.actionMapper = actionMapper; + } + +} Added: struts/archive/plugins/src/main/java/org/apache/struts2/portlet/interceptor/PortletAwareInterceptor.java URL: http://svn.apache.org/viewvc/struts/archive/plugins/src/main/java/org/apache/struts2/portlet/interceptor/PortletAwareInterceptor.java?rev=1139061&view=auto ============================================================================== --- struts/archive/plugins/src/main/java/org/apache/struts2/portlet/interceptor/PortletAwareInterceptor.java (added) +++ struts/archive/plugins/src/main/java/org/apache/struts2/portlet/interceptor/PortletAwareInterceptor.java Thu Jun 23 20:18:43 2011 @@ -0,0 +1,92 @@ +/* + * $Id: PortletAwareInterceptor.java 1099157 2011-05-03 17:53:55Z jogep $ + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.struts2.portlet.interceptor; + +import javax.portlet.PortletConfig; +import javax.portlet.PortletContext; +import javax.portlet.PortletRequest; +import javax.portlet.PortletResponse; + +import org.apache.struts2.StrutsStatics; +import org.apache.struts2.interceptor.PrincipalAware; +import org.apache.struts2.portlet.PortletActionConstants; + +import com.opensymphony.xwork2.ActionContext; +import com.opensymphony.xwork2.ActionInvocation; +import com.opensymphony.xwork2.interceptor.AbstractInterceptor; +import com.opensymphony.xwork2.util.logging.Logger; +import com.opensymphony.xwork2.util.logging.LoggerFactory; + +public class PortletAwareInterceptor extends AbstractInterceptor implements PortletActionConstants, StrutsStatics { + + private static final long serialVersionUID = 2476509721059587700L; + + private static final Logger LOG = LoggerFactory.getLogger(PortletAwareInterceptor.class); + + /** + * Sets action properties based on the interfaces an action implements. Things like application properties, + * parameters, session attributes, etc are set based on the implementing interface. + * + * @param invocation an encapsulation of the action execution state. + * @throws Exception if an error occurs when setting action properties. + */ + public String intercept(ActionInvocation invocation) throws Exception { + final Object action = invocation.getAction(); + final ActionContext context = invocation.getInvocationContext(); + + if (action instanceof PortletRequestAware) { + PortletRequest request = (PortletRequest) context.get(REQUEST); + ((PortletRequestAware) action).setPortletRequest(request); + } + + if (action instanceof PortletResponseAware) { + PortletResponse response = (PortletResponse) context.get(RESPONSE); + ((PortletResponseAware) action).setPortletResponse(response); + } + if (action instanceof PrincipalAware) { + PortletRequest request = (PortletRequest) context.get(REQUEST); + ((PrincipalAware) action).setPrincipalProxy(new PortletPrincipalProxy(request)); + } + if (action instanceof PortletContextAware) { + PortletContext portletContext = (PortletContext) context.get(STRUTS_PORTLET_CONTEXT); + ((PortletContextAware) action).setPortletContext(portletContext); + } + if (action instanceof PortletConfigAware) { + PortletConfig portletConfig = (PortletConfig)context.get(PORTLET_CONFIG); + ((PortletConfigAware) action).setPortletConfig(portletConfig); + } + if (action instanceof PortletPreferencesAware) { + PortletRequest request = (PortletRequest) context.get(REQUEST); + + // Check if running in a servlet environment + if (request == null) { + if (LOG.isWarnEnabled()) { + LOG.warn("This portlet preferences implementation should only be used during development"); + } + ((PortletPreferencesAware)action).setPortletPreferences(new ServletPortletPreferences(ActionContext.getContext().getSession())); + } else { + ((PortletPreferencesAware)action).setPortletPreferences(request.getPreferences()); + } + } + return invocation.invoke(); + } +} Added: struts/archive/plugins/src/main/java/org/apache/struts2/portlet/interceptor/PortletConfigAware.java URL: http://svn.apache.org/viewvc/struts/archive/plugins/src/main/java/org/apache/struts2/portlet/interceptor/PortletConfigAware.java?rev=1139061&view=auto ============================================================================== --- struts/archive/plugins/src/main/java/org/apache/struts2/portlet/interceptor/PortletConfigAware.java (added) +++ struts/archive/plugins/src/main/java/org/apache/struts2/portlet/interceptor/PortletConfigAware.java Thu Jun 23 20:18:43 2011 @@ -0,0 +1,34 @@ +/* + * $Id: $ + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.struts2.portlet.interceptor; + +import javax.portlet.PortletConfig; + + +/** + * Actions that wants a reference to the PortletConfig object can + * implement this interface. + * + */ +public interface PortletConfigAware { + + void setPortletConfig(PortletConfig portletConfig); +} Added: struts/archive/plugins/src/main/java/org/apache/struts2/portlet/interceptor/PortletContextAware.java URL: http://svn.apache.org/viewvc/struts/archive/plugins/src/main/java/org/apache/struts2/portlet/interceptor/PortletContextAware.java?rev=1139061&view=auto ============================================================================== --- struts/archive/plugins/src/main/java/org/apache/struts2/portlet/interceptor/PortletContextAware.java (added) +++ struts/archive/plugins/src/main/java/org/apache/struts2/portlet/interceptor/PortletContextAware.java Thu Jun 23 20:18:43 2011 @@ -0,0 +1,30 @@ +/* + * $Id: PortletContextAware.java 651946 2008-04-27 13:41:38Z apetrelli $ + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.struts2.portlet.interceptor; + +import javax.portlet.PortletContext; + +public interface PortletContextAware { + + void setPortletContext(PortletContext portletContext); + +} Added: struts/archive/plugins/src/main/java/org/apache/struts2/portlet/interceptor/PortletPreferencesAware.java URL: http://svn.apache.org/viewvc/struts/archive/plugins/src/main/java/org/apache/struts2/portlet/interceptor/PortletPreferencesAware.java?rev=1139061&view=auto ============================================================================== --- struts/archive/plugins/src/main/java/org/apache/struts2/portlet/interceptor/PortletPreferencesAware.java (added) +++ struts/archive/plugins/src/main/java/org/apache/struts2/portlet/interceptor/PortletPreferencesAware.java Thu Jun 23 20:18:43 2011 @@ -0,0 +1,40 @@ +/* + * $Id: PortletPreferencesAware.java 651946 2008-04-27 13:41:38Z apetrelli $ + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.struts2.portlet.interceptor; + +import javax.portlet.PortletPreferences; + + +/** + * All Actions that want to have access to the portlet preferences should + * implement this interface. If running in a servlet environment, an + * appropriate testing implementation will be provided. + */ +public interface PortletPreferencesAware { + + /** + * Sets the HTTP request object in implementing classes. + * + * @param request the HTTP request. + */ + public void setPortletPreferences(PortletPreferences prefs); +} Added: struts/archive/plugins/src/main/java/org/apache/struts2/portlet/interceptor/PortletPrincipalProxy.java URL: http://svn.apache.org/viewvc/struts/archive/plugins/src/main/java/org/apache/struts2/portlet/interceptor/PortletPrincipalProxy.java?rev=1139061&view=auto ============================================================================== --- struts/archive/plugins/src/main/java/org/apache/struts2/portlet/interceptor/PortletPrincipalProxy.java (added) +++ struts/archive/plugins/src/main/java/org/apache/struts2/portlet/interceptor/PortletPrincipalProxy.java Thu Jun 23 20:18:43 2011 @@ -0,0 +1,94 @@ +/* + * $Id: PortletPrincipalProxy.java 651946 2008-04-27 13:41:38Z apetrelli $ + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.struts2.portlet.interceptor; + +import org.apache.struts2.interceptor.PrincipalProxy; + +import javax.portlet.PortletRequest; +import javax.servlet.http.HttpServletRequest; +import java.security.Principal; + +/** + * PrincipalProxy implementation for using PortletRequest Principal related methods. + */ +public class PortletPrincipalProxy implements PrincipalProxy { + + private PortletRequest request; + + /** + * Constructs a proxy + * + * @param request The underlying request + */ + public PortletPrincipalProxy(PortletRequest request) { + this.request = request; + } + + /** + * True if the user is in the given role + * + * @param role The role + * @return True if the user is in that role + */ + public boolean isUserInRole(String role) { + return request.isUserInRole(role); + } + + /** + * Gets the user principal + * + * @return The principal + */ + public Principal getUserPrincipal() { + return request.getUserPrincipal(); + } + + /** + * Gets the user id + * + * @return The user id + */ + public String getRemoteUser() { + return request.getRemoteUser(); + } + + /** + * Is the request using https? + * + * @return True if using https + */ + public boolean isRequestSecure() { + return request.isSecure(); + } + + /** + * Gets the request. + * + * @return The request + * @throws UnsupportedOperationException not supported in this implementation. + * @deprecated To obtain the HttpServletRequest in your action, use + * {@link org.apache.struts2.servlet.ServletRequestAware}, since this method will be dropped in future. + */ + public HttpServletRequest getRequest() { + throw new UnsupportedOperationException("Usage of getRequest() method is deprecadet and not supported for this implementation"); + } +} Added: struts/archive/plugins/src/main/java/org/apache/struts2/portlet/interceptor/PortletRequestAware.java URL: http://svn.apache.org/viewvc/struts/archive/plugins/src/main/java/org/apache/struts2/portlet/interceptor/PortletRequestAware.java?rev=1139061&view=auto ============================================================================== --- struts/archive/plugins/src/main/java/org/apache/struts2/portlet/interceptor/PortletRequestAware.java (added) +++ struts/archive/plugins/src/main/java/org/apache/struts2/portlet/interceptor/PortletRequestAware.java Thu Jun 23 20:18:43 2011 @@ -0,0 +1,30 @@ +/* + * $Id: PortletRequestAware.java 651946 2008-04-27 13:41:38Z apetrelli $ + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.struts2.portlet.interceptor; + +import javax.portlet.PortletRequest; + +public interface PortletRequestAware { + + void setPortletRequest(PortletRequest request); + +} Added: struts/archive/plugins/src/main/java/org/apache/struts2/portlet/interceptor/PortletResponseAware.java URL: http://svn.apache.org/viewvc/struts/archive/plugins/src/main/java/org/apache/struts2/portlet/interceptor/PortletResponseAware.java?rev=1139061&view=auto ============================================================================== --- struts/archive/plugins/src/main/java/org/apache/struts2/portlet/interceptor/PortletResponseAware.java (added) +++ struts/archive/plugins/src/main/java/org/apache/struts2/portlet/interceptor/PortletResponseAware.java Thu Jun 23 20:18:43 2011 @@ -0,0 +1,30 @@ +/* + * $Id: PortletResponseAware.java 651946 2008-04-27 13:41:38Z apetrelli $ + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.struts2.portlet.interceptor; + +import javax.portlet.PortletResponse; + +public interface PortletResponseAware { + + void setPortletResponse(PortletResponse response); + +} Added: struts/archive/plugins/src/main/java/org/apache/struts2/portlet/interceptor/PortletStateInterceptor.java URL: http://svn.apache.org/viewvc/struts/archive/plugins/src/main/java/org/apache/struts2/portlet/interceptor/PortletStateInterceptor.java?rev=1139061&view=auto ============================================================================== --- struts/archive/plugins/src/main/java/org/apache/struts2/portlet/interceptor/PortletStateInterceptor.java (added) +++ struts/archive/plugins/src/main/java/org/apache/struts2/portlet/interceptor/PortletStateInterceptor.java Thu Jun 23 20:18:43 2011 @@ -0,0 +1,95 @@ +/* + * $Id: PortletStateInterceptor.java 1076544 2011-03-03 07:19:37Z lukaszlenart $ + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.struts2.portlet.interceptor; + +import com.opensymphony.xwork2.ActionInvocation; +import com.opensymphony.xwork2.interceptor.AbstractInterceptor; +import com.opensymphony.xwork2.util.CompoundRoot; +import com.opensymphony.xwork2.util.ValueStack; +import com.opensymphony.xwork2.util.logging.Logger; +import com.opensymphony.xwork2.util.logging.LoggerFactory; +import org.apache.commons.lang.StringUtils; +import org.apache.struts2.portlet.PortletActionConstants; +import org.apache.struts2.portlet.dispatcher.DirectRenderFromEventAction; + +import javax.portlet.ActionResponse; +import javax.portlet.RenderRequest; +import java.util.Map; + +public class PortletStateInterceptor extends AbstractInterceptor implements PortletActionConstants { + + private final static Logger LOG = LoggerFactory.getLogger(PortletStateInterceptor.class); + + private static final long serialVersionUID = 6138452063353911784L; + + @Override + public String intercept(ActionInvocation invocation) throws Exception { + Integer phase = (Integer) invocation.getInvocationContext().get(PHASE); + if (RENDER_PHASE.equals(phase)) { + restoreStack(invocation); + return invocation.invoke(); + } else if (EVENT_PHASE.equals(phase)) { + try { + return invocation.invoke(); + } finally { + saveStack(invocation); + } + } else { + return invocation.invoke(); + } + } + + @SuppressWarnings("unchecked") + private void saveStack(ActionInvocation invocation) { + Map session = invocation.getInvocationContext().getSession(); + session.put(STACK_FROM_EVENT_PHASE, invocation.getStack()); + ActionResponse actionResponse = (ActionResponse) invocation.getInvocationContext().get(RESPONSE); + actionResponse.setRenderParameter(EVENT_ACTION, "true"); + } + + @SuppressWarnings("unchecked") + private void restoreStack(ActionInvocation invocation) { + RenderRequest request = (RenderRequest) invocation.getInvocationContext().get(REQUEST); + if (StringUtils.isNotEmpty(request.getParameter(EVENT_ACTION))) { + if(!isProperPrg(invocation)) { + if (LOG.isDebugEnabled()) LOG.debug("Restoring value stack from event phase"); + ValueStack oldStack = (ValueStack) invocation.getInvocationContext().getSession().get( + STACK_FROM_EVENT_PHASE); + if (oldStack != null) { + CompoundRoot oldRoot = oldStack.getRoot(); + ValueStack currentStack = invocation.getStack(); + CompoundRoot root = currentStack.getRoot(); + root.addAll(0, oldRoot); + if (LOG.isDebugEnabled()) LOG.debug("Restored stack"); + } + } + else { + if (LOG.isDebugEnabled()) LOG.debug("Won't restore stack from event phase since it's a proper PRG request"); + } + } + } + + private boolean isProperPrg(ActionInvocation invocation) { + return !(invocation.getAction() instanceof DirectRenderFromEventAction); + } + +} Added: struts/archive/plugins/src/main/java/org/apache/struts2/portlet/interceptor/ServletPortletPreferences.java URL: http://svn.apache.org/viewvc/struts/archive/plugins/src/main/java/org/apache/struts2/portlet/interceptor/ServletPortletPreferences.java?rev=1139061&view=auto ============================================================================== --- struts/archive/plugins/src/main/java/org/apache/struts2/portlet/interceptor/ServletPortletPreferences.java (added) +++ struts/archive/plugins/src/main/java/org/apache/struts2/portlet/interceptor/ServletPortletPreferences.java Thu Jun 23 20:18:43 2011 @@ -0,0 +1,96 @@ +/* + * $Id: ServletPortletPreferences.java 651946 2008-04-27 13:41:38Z apetrelli $ + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.struts2.portlet.interceptor; + +import java.io.IOException; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.Vector; + +import javax.portlet.PortletPreferences; +import javax.portlet.ReadOnlyException; +import javax.portlet.ValidatorException; + +/** + * Simple portlet preferences implementation that uses a map in the Session + * as storage. + */ +public class ServletPortletPreferences implements PortletPreferences { + + private Map session; + private String PREFERENCES_KEY = "_portlet-preferences"; + + public ServletPortletPreferences(Map session) { + this.session = session; + } + + public Map getMap() { + Map map = (Map) session.get(PREFERENCES_KEY); + if (map == null) { + map = new HashMap(); + session.put(PREFERENCES_KEY, map); + } + return map; + } + + public Enumeration getNames() { + return new Vector(getMap().keySet()).elements(); + } + + public String getValue(String key, String def) { + String val = (String) getMap().get(key); + if (val == null) { + val = def; + } + return val; + } + + public String[] getValues(String key, String[] def) { + String[] val = (String[]) getMap().get(key); + if (val == null) { + val = def; + } + return val; + } + + public boolean isReadOnly(String arg0) { + return false; + } + + public void reset(String arg0) throws ReadOnlyException { + session.put(PREFERENCES_KEY, new HashMap()); + } + + public void setValue(String key, String value) throws ReadOnlyException { + getMap().put(key, value); + } + + public void setValues(String key, String[] value) throws ReadOnlyException { + getMap().put(key, value); + } + + public void store() throws IOException, ValidatorException { + + } + +} Added: struts/archive/plugins/src/main/java/org/apache/struts2/portlet/result/PortletActionRedirectResult.java URL: http://svn.apache.org/viewvc/struts/archive/plugins/src/main/java/org/apache/struts2/portlet/result/PortletActionRedirectResult.java?rev=1139061&view=auto ============================================================================== --- struts/archive/plugins/src/main/java/org/apache/struts2/portlet/result/PortletActionRedirectResult.java (added) +++ struts/archive/plugins/src/main/java/org/apache/struts2/portlet/result/PortletActionRedirectResult.java Thu Jun 23 20:18:43 2011 @@ -0,0 +1,269 @@ +/* + * $Id: PortletActionRedirectResult.java 833239 2009-11-05 23:46:50Z rgielen $ + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.struts2.portlet.result; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import javax.portlet.PortletMode; + +import org.apache.struts2.dispatcher.ServletActionRedirectResult; +import org.apache.struts2.dispatcher.mapper.ActionMapper; +import org.apache.struts2.dispatcher.mapper.ActionMapping; +import org.apache.struts2.portlet.PortletActionConstants; +import org.apache.struts2.views.util.UrlHelper; + +import com.opensymphony.xwork2.ActionInvocation; +import com.opensymphony.xwork2.util.reflection.ReflectionExceptionHandler; +import com.opensymphony.xwork2.util.reflection.ReflectionException; +import com.opensymphony.xwork2.util.logging.Logger; +import com.opensymphony.xwork2.util.logging.LoggerFactory; +import com.opensymphony.xwork2.config.entities.ResultConfig; +import com.opensymphony.xwork2.inject.Inject; + +/** + * + * Portlet modification of the {@link ServletActionRedirectResult}. + * + * + * + * This result uses the {@link ActionMapper} provided by the + * {@link ActionMapperFactory} to instruct the render phase to invoke the + * specified action and (optional) namespace. This is better than the + * {@link PortletResult} because it does not require you to encode the URL + * patterns processed by the {@link ActionMapper} in to your struts.xml + * configuration files. This means you can change your URL patterns at any point + * and your application will still work. It is strongly recommended that if you + * are redirecting to another action, you use this result rather than the + * standard redirect result. + * + * See examples below for an example of how request parameters could be passed + * in. + * + * + * + * This result type takes the following parameters: + * + * + * + *
    + * + *
  • actionName (default) - the name of the action that will be + * redirect to
  • + * + *
  • namespace - used to determine which namespace the action is in + * that we're redirecting to . If namespace is null, this defaults to the + * current namespace
  • + * + *
+ * + * + * + * Example: + * + *
+ * <!-- START SNIPPET: example -->
+ *  <package name="public" extends="struts-default">
+ *      <action name="login" class="...">
+ *          <!-- Redirect to another namespace -->
+ *          <result type="redirectAction">
+ *              <param name="actionName">dashboard</param>
+ *              <param name="namespace">/secure</param>
+ *          </result>
+ *      </action>
+ *  </package>
+ *
+ *  <package name="secure" extends="struts-default" namespace="/secure">
+ *      <-- Redirect to an action in the same namespace -->
+ *      <action name="dashboard" class="...">
+ *          <result>dashboard.jsp</result>
+ *          <result name="error" type="redirectAction">error</result>
+ *      </action>
+ *
+ *      <action name="error" class="...">
+ *          <result>error.jsp</result>
+ *      </action>
+ *  </package>
+ *
+ *  <package name="passingRequestParameters" extends="struts-default" namespace="/passingRequestParameters">
+ *     <-- Pass parameters (reportType, width and height) -->
+ *     <!--
+ *     The redirectAction url generated will be :
+ *     /genReport/generateReport.action?reportType=pie&width=100&height=100
+ *     -->
+ *     <action name="gatherReportInfo" class="...">
+ *        <result name="showReportResult" type="redirectAction">
+ *           <param name="actionName">generateReport</param>
+ *           <param name="namespace">/genReport</param>
+ *           <param name="reportType">pie</param>
+ *           <param name="width">100</param>
+ *           <param name="height">100</param>
+ *        </result>
+ *     </action>
+ *  </package>
+ *
+ *
+ *  <!-- END SNIPPET: example -->
+ * 
+ * + * @see ActionMapper + */ +public class PortletActionRedirectResult extends PortletResult implements ReflectionExceptionHandler { + + private static final long serialVersionUID = -7627388936683562557L; + + private static final Logger LOG = LoggerFactory.getLogger(PortletActionRedirectResult.class); + + /** The default parameter */ + public static final String DEFAULT_PARAM = "actionName"; + + protected String actionName; + + protected String namespace; + + protected String method; + + private Map requestParameters = new LinkedHashMap(); + + private ActionMapper actionMapper; + + public PortletActionRedirectResult() { + super(); + } + + public PortletActionRedirectResult(String actionName) { + this(null, actionName, null); + } + + public PortletActionRedirectResult(String actionName, String method) { + this(null, actionName, method); + } + + public PortletActionRedirectResult(String namespace, String actionName, String method) { + super(null); + this.namespace = namespace; + this.actionName = actionName; + this.method = method; + } + + protected List prohibitedResultParam = Arrays.asList(new String[] { DEFAULT_PARAM, "namespace", "method", + "encode", "parse", "location", "prependServletContext" }); + + @Inject + public void setActionMapper(ActionMapper actionMapper) { + this.actionMapper = actionMapper; + } + + /** + * @see com.opensymphony.xwork2.Result#execute(com.opensymphony.xwork2.ActionInvocation) + */ + public void execute(ActionInvocation invocation) throws Exception { + actionName = conditionalParse(actionName, invocation); + if (portletMode != null) { + Map namespaceMap = (Map) invocation.getInvocationContext().get( + PortletActionConstants.MODE_NAMESPACE_MAP); + namespace = namespaceMap.get(portletMode); + } + if (namespace == null) { + namespace = invocation.getProxy().getNamespace(); + } else { + namespace = conditionalParse(namespace, invocation); + } + if (method == null) { + method = ""; + } else { + method = conditionalParse(method, invocation); + } + + String resultCode = invocation.getResultCode(); + if (resultCode != null) { + ResultConfig resultConfig = invocation.getProxy().getConfig().getResults().get(resultCode); + Map resultConfigParams = resultConfig.getParams(); + for (Iterator i = resultConfigParams.entrySet().iterator(); i.hasNext();) { + Map.Entry e = (Map.Entry) i.next(); + if (!prohibitedResultParam.contains(e.getKey())) { + requestParameters.put(e.getKey().toString(), e.getValue() == null ? "" : conditionalParse(e + .getValue().toString(), invocation)); + } + } + } + + StringBuilder tmpLocation = new StringBuilder(actionMapper.getUriFromActionMapping(new ActionMapping(actionName, + namespace, method, null))); + UrlHelper.buildParametersString(requestParameters, tmpLocation, "&"); + + setLocation(tmpLocation.toString()); + + super.execute(invocation); + } + + /** + * Sets the action name + * + * @param actionName + * The name + */ + public void setActionName(String actionName) { + this.actionName = actionName; + } + + /** + * Sets the namespace + * + * @param namespace + * The namespace + */ + public void setNamespace(String namespace) { + this.namespace = namespace; + } + + /** + * Sets the method + * + * @param method + * The method + */ + public void setMethod(String method) { + this.method = method; + } + + /** + * Adds a request parameter to be added to the redirect url + * + * @param key + * The parameter name + * @param value + * The parameter value + */ + public PortletActionRedirectResult addParameter(String key, Object value) { + requestParameters.put(key, String.valueOf(value)); + return this; + } + + public void handle(ReflectionException ex) { + // Only log as debug as they are probably parameters to be appended to the url + if (LOG.isDebugEnabled()) LOG.debug(ex.getMessage(), ex); + } +} Added: struts/archive/plugins/src/main/java/org/apache/struts2/portlet/result/PortletResult.java URL: http://svn.apache.org/viewvc/struts/archive/plugins/src/main/java/org/apache/struts2/portlet/result/PortletResult.java?rev=1139061&view=auto ============================================================================== --- struts/archive/plugins/src/main/java/org/apache/struts2/portlet/result/PortletResult.java (added) +++ struts/archive/plugins/src/main/java/org/apache/struts2/portlet/result/PortletResult.java Thu Jun 23 20:18:43 2011 @@ -0,0 +1,243 @@ +/* + * $Id: PortletResult.java 1076544 2011-03-03 07:19:37Z lukaszlenart $ + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.struts2.portlet.result; + +import com.opensymphony.xwork2.ActionInvocation; +import com.opensymphony.xwork2.inject.Inject; +import com.opensymphony.xwork2.util.logging.Logger; +import com.opensymphony.xwork2.util.logging.LoggerFactory; +import org.apache.commons.lang.StringUtils; +import org.apache.struts2.ServletActionContext; +import org.apache.struts2.dispatcher.StrutsResultSupport; +import org.apache.struts2.portlet.PortletActionConstants; +import org.apache.struts2.portlet.context.PortletActionContext; + +import javax.portlet.ActionResponse; +import javax.portlet.PortletContext; +import javax.portlet.PortletException; +import javax.portlet.PortletMode; +import javax.portlet.PortletRequestDispatcher; +import javax.portlet.RenderRequest; +import javax.portlet.RenderResponse; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Map; +import java.util.StringTokenizer; + +/** + * Result type that includes a JSP to render. + * + */ +public class PortletResult extends StrutsResultSupport implements PortletActionConstants { + + private static final long serialVersionUID = 434251393926178567L; + + private boolean useDispatcherServlet; + + private String dispatcherServletName = DEFAULT_DISPATCHER_SERVLET_NAME; + + /** + * Logger instance. + */ + private static final Logger LOG = LoggerFactory.getLogger(PortletResult.class); + + private String contentType = "text/html"; + + private String title; + + protected PortletMode portletMode; + + public PortletResult() { + super(); + } + + public PortletResult(String location) { + super(location); + } + + /** + * Execute the result. Obtains the + * {@link javax.portlet.PortletRequestDispatcher}from the + * {@link PortletActionContext}and includes the JSP. + * + * @see com.opensymphony.xwork2.Result#execute(com.opensymphony.xwork2.ActionInvocation) + */ + public void doExecute(String finalLocation, ActionInvocation actionInvocation) throws Exception { + + if (PortletActionContext.isRender()) { + executeRenderResult(finalLocation); + } else if (PortletActionContext.isEvent()) { + executeActionResult(finalLocation, actionInvocation); + } else { + executeRegularServletResult(finalLocation, actionInvocation); + } + } + + /** + * Executes the regular servlet result. + * + * @param finalLocation + * @param actionInvocation + */ + private void executeRegularServletResult(String finalLocation, ActionInvocation actionInvocation) + throws ServletException, IOException { + ServletContext ctx = ServletActionContext.getServletContext(); + HttpServletRequest req = ServletActionContext.getRequest(); + HttpServletResponse res = ServletActionContext.getResponse(); + try { + ctx.getRequestDispatcher(finalLocation).include(req, res); + } catch (ServletException e) { + LOG.error("ServletException including " + finalLocation, e); + throw e; + } catch (IOException e) { + LOG.error("IOException while including result '" + finalLocation + "'", e); + throw e; + } + } + + /** + * Executes the action result. + * + * @param finalLocation + * @param invocation + */ + protected void executeActionResult(String finalLocation, ActionInvocation invocation) throws Exception { + if (LOG.isDebugEnabled()) LOG.debug("Executing result in Event phase"); + ActionResponse res = PortletActionContext.getActionResponse(); + Map sessionMap = invocation.getInvocationContext().getSession(); + if (LOG.isDebugEnabled()) LOG.debug("Setting event render parameter: " + finalLocation); + if (finalLocation.indexOf('?') != -1) { + convertQueryParamsToRenderParams(res, finalLocation.substring(finalLocation.indexOf('?') + 1)); + finalLocation = finalLocation.substring(0, finalLocation.indexOf('?')); + } + if (finalLocation.endsWith(".action")) { + // View is rendered with a view action...luckily... + finalLocation = finalLocation.substring(0, finalLocation.lastIndexOf(".")); + res.setRenderParameter(ACTION_PARAM, finalLocation); + } else { + // View is rendered outside an action...uh oh... + String namespace = invocation.getProxy().getNamespace(); + if ( namespace != null && namespace.length() > 0 && !namespace.endsWith("/")) { + namespace += "/"; + + } + res.setRenderParameter(ACTION_PARAM, namespace + "renderDirect"); + sessionMap.put(RENDER_DIRECT_LOCATION, finalLocation); + } + if(portletMode != null) { + res.setPortletMode(portletMode); + res.setRenderParameter(PortletActionConstants.MODE_PARAM, portletMode.toString()); + } + else { + res.setRenderParameter(PortletActionConstants.MODE_PARAM, PortletActionContext.getRequest().getPortletMode() + .toString()); + } + } + + /** + * Converts the query params to render params. + * + * @param response + * @param queryParams + */ + protected static void convertQueryParamsToRenderParams(ActionResponse response, String queryParams) { + StringTokenizer tok = new StringTokenizer(queryParams, "&"); + while (tok.hasMoreTokens()) { + String token = tok.nextToken(); + String key = token.substring(0, token.indexOf('=')); + String value = token.substring(token.indexOf('=') + 1); + response.setRenderParameter(key, value); + } + } + + /** + * Executes the render result. + * + * @param finalLocation + * @throws PortletException + * @throws IOException + */ + protected void executeRenderResult(final String finalLocation) throws PortletException, IOException { + if (LOG.isDebugEnabled()) LOG.debug("Executing result in Render phase"); + PortletContext ctx = PortletActionContext.getPortletContext(); + RenderRequest req = PortletActionContext.getRenderRequest(); + RenderResponse res = PortletActionContext.getRenderResponse(); + res.setContentType(contentType); + if (StringUtils.isNotEmpty(title)) { + res.setTitle(title); + } + if (LOG.isDebugEnabled()) LOG.debug("Location: " + finalLocation); + if (useDispatcherServlet) { + req.setAttribute(DISPATCH_TO, finalLocation); + PortletRequestDispatcher dispatcher = ctx.getNamedDispatcher(dispatcherServletName); + if(dispatcher == null) { + throw new PortletException("Could not locate dispatcher servlet \"" + dispatcherServletName + "\". Please configure it in your web.xml file"); + } + dispatcher.include(req, res); + } else { + PortletRequestDispatcher dispatcher = ctx.getRequestDispatcher(finalLocation); + if (dispatcher == null) { + throw new PortletException("Could not locate dispatcher for '" + finalLocation + "'"); + } + dispatcher.include(req, res); + } + } + + /** + * Sets the content type. + * + * @param contentType + * The content type to set. + */ + public void setContentType(String contentType) { + this.contentType = contentType; + } + + /** + * Sets the title. + * + * @param title + * The title to set. + */ + public void setTitle(String title) { + this.title = title; + } + + public void setPortletMode(String portletMode) { + if(portletMode != null) { + this.portletMode = new PortletMode(portletMode); + } + } + + @Inject("struts.portlet.useDispatcherServlet") + public void setUseDispatcherServlet(String useDispatcherServlet) { + this.useDispatcherServlet = "true".equalsIgnoreCase(useDispatcherServlet); + } + + @Inject("struts.portlet.dispatcherServletName") + public void setDispatcherServletName(String dispatcherServletName) { + this.dispatcherServletName = dispatcherServletName; + } +} Added: struts/archive/plugins/src/main/java/org/apache/struts2/portlet/result/PortletVelocityResult.java URL: http://svn.apache.org/viewvc/struts/archive/plugins/src/main/java/org/apache/struts2/portlet/result/PortletVelocityResult.java?rev=1139061&view=auto ============================================================================== --- struts/archive/plugins/src/main/java/org/apache/struts2/portlet/result/PortletVelocityResult.java (added) +++ struts/archive/plugins/src/main/java/org/apache/struts2/portlet/result/PortletVelocityResult.java Thu Jun 23 20:18:43 2011 @@ -0,0 +1,291 @@ +/* + * $Id: PortletVelocityResult.java 1074051 2011-02-24 06:53:20Z lukaszlenart $ + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.struts2.portlet.result; + +import com.opensymphony.xwork2.ActionContext; +import com.opensymphony.xwork2.ActionInvocation; +import com.opensymphony.xwork2.inject.Inject; +import com.opensymphony.xwork2.util.ValueStack; +import com.opensymphony.xwork2.util.logging.Logger; +import com.opensymphony.xwork2.util.logging.LoggerFactory; +import org.apache.struts2.ServletActionContext; +import org.apache.struts2.StrutsConstants; +import org.apache.struts2.dispatcher.StrutsResultSupport; +import org.apache.struts2.portlet.PortletActionConstants; +import org.apache.struts2.portlet.context.PortletActionContext; +import org.apache.struts2.views.JspSupportServlet; +import org.apache.struts2.views.velocity.VelocityManager; +import org.apache.velocity.Template; +import org.apache.velocity.app.VelocityEngine; +import org.apache.velocity.context.Context; + +import javax.portlet.ActionResponse; +import javax.servlet.Servlet; +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.jsp.JspFactory; +import javax.servlet.jsp.PageContext; +import java.io.OutputStreamWriter; +import java.io.Writer; + +/** + * + * + * Using the Servlet container's {@link JspFactory}, this result mocks a JSP + * execution environment and then displays a Velocity template that will be + * streamed directly to the servlet output. + * + *

This result type takes the + * following parameters: + * + * + * + *

    + * + *
  • location (default) - the location of the template to process. + *
  • + * + *
  • parse - true by default. If set to false, the location param + * will not be parsed for Ognl expressions.
  • + * + *
+ *

+ * This result follows the same rules from {@link StrutsResultSupport}. + *

+ * + * + * + * Example: + * + *
+ * <!-- START SNIPPET: example -->
+ *  <result name="success" type="velocity">
+ *    <param name="location">foo.vm</param>
+ *  </result>
+ *  <!-- END SNIPPET: example -->
+ * 
+ * + */ +public class PortletVelocityResult extends StrutsResultSupport { + + private static final long serialVersionUID = -8241086555872212274L; + + private static final Logger LOG = LoggerFactory.getLogger(PortletVelocityResult.class); + + private String defaultEncoding; + private VelocityManager velocityManager; + private JspFactory jspFactory = JspFactory.getDefaultFactory(); + + public PortletVelocityResult() { + super(); + } + + public PortletVelocityResult(String location) { + super(location); + } + + @Inject + public void setVelocityManager(VelocityManager mgr) { + this.velocityManager = mgr; + } + + @Inject(StrutsConstants.STRUTS_I18N_ENCODING) + public void setDefaultEncoding(String encoding) { + this.defaultEncoding = encoding; + } + + /* (non-Javadoc) + * @see org.apache.struts2.dispatcher.StrutsResultSupport#doExecute(java.lang.String, com.opensymphony.xwork2.ActionInvocation) + */ + public void doExecute(String location, ActionInvocation invocation) + throws Exception { + if (PortletActionContext.isEvent()) { + executeActionResult(location, invocation); + } else if (PortletActionContext.isRender()) { + executeRenderResult(location, invocation); + } + } + + /** + * Executes the result + * + * @param location The location string + * @param invocation The action invocation + */ + private void executeActionResult(String location, + ActionInvocation invocation) { + ActionResponse res = PortletActionContext.getActionResponse(); + // View is rendered outside an action...uh oh... + String namespace = invocation.getProxy().getNamespace(); + if ( namespace != null && namespace.length() > 0 && !namespace.endsWith("/")) { + namespace += "/"; + + } + res.setRenderParameter(PortletActionConstants.ACTION_PARAM, namespace + "freemarkerDirect"); + res.setRenderParameter("location", location); + res.setRenderParameter(PortletActionConstants.MODE_PARAM, PortletActionContext + .getRequest().getPortletMode().toString()); + + } + + /** + * Creates a Velocity context from the action, loads a Velocity template and + * executes the template. Output is written to the servlet output stream. + * + * @param finalLocation the location of the Velocity template + * @param invocation an encapsulation of the action execution state. + * @throws Exception if an error occurs when creating the Velocity context, + * loading or executing the template or writing output to the + * servlet response stream. + */ + public void executeRenderResult(String finalLocation, + ActionInvocation invocation) throws Exception { + ValueStack stack = ActionContext.getContext().getValueStack(); + + HttpServletRequest request = ServletActionContext.getRequest(); + HttpServletResponse response = ServletActionContext.getResponse(); + ServletContext servletContext = ServletActionContext + .getServletContext(); + Servlet servlet = JspSupportServlet.jspSupportServlet; + + velocityManager.init(servletContext); + + boolean usedJspFactory = false; + PageContext pageContext = (PageContext) ActionContext.getContext().get( + ServletActionContext.PAGE_CONTEXT); + + if (pageContext == null && servlet != null) { + pageContext = jspFactory.getPageContext(servlet, request, response, + null, true, 8192, true); + ActionContext.getContext().put(ServletActionContext.PAGE_CONTEXT, + pageContext); + usedJspFactory = true; + } + + try { + String encoding = getEncoding(finalLocation); + String contentType = getContentType(finalLocation); + + if (encoding != null) { + contentType = contentType + ";charset=" + encoding; + } + response.setContentType(contentType); + Template t = getTemplate(stack, + velocityManager.getVelocityEngine(), invocation, + finalLocation, encoding); + + Context context = createContext(velocityManager, stack, request, + response, finalLocation); + Writer writer = new OutputStreamWriter(response.getOutputStream(), + encoding); + + t.merge(context, writer); + + // always flush the writer (we used to only flush it if this was a + // jspWriter, but someone asked + // to do it all the time (WW-829). Since Velocity support is being + // deprecated, we'll oblige :) + writer.flush(); + } catch (Exception e) { + LOG.error("Unable to render Velocity Template, '" + finalLocation + + "'", e); + throw e; + } finally { + if (usedJspFactory) { + jspFactory.releasePageContext(pageContext); + } + } + + return; + } + + /** + * Retrieve the content type for this template.

People can override + * this method if they want to provide specific content types for specific + * templates (eg text/xml). + * + * @return The content type associated with this template (default + * "text/html") + */ + protected String getContentType(String templateLocation) { + return "text/html"; + } + + /** + * Retrieve the encoding for this template.

People can override this + * method if they want to provide specific encodings for specific templates. + * + * @return The encoding associated with this template (defaults to the value + * of 'struts.i18n.encoding' property) + */ + protected String getEncoding(String templateLocation) { + String encoding = defaultEncoding; + if (encoding == null) { + encoding = System.getProperty("file.encoding"); + } + if (encoding == null) { + encoding = "UTF-8"; + } + return encoding; + } + + /** + * Given a value stack, a Velocity engine, and an action invocation, this + * method returns the appropriate Velocity template to render. + * + * @param stack the value stack to resolve the location again (when parse + * equals true) + * @param velocity the velocity engine to process the request against + * @param invocation an encapsulation of the action execution state. + * @param location the location of the template + * @param encoding the charset encoding of the template + * @return the template to render + * @throws Exception when the requested template could not be found + */ + protected Template getTemplate(ValueStack stack, + VelocityEngine velocity, ActionInvocation invocation, + String location, String encoding) throws Exception { + if (!location.startsWith("/")) { + location = invocation.getProxy().getNamespace() + "/" + location; + } + + Template template = velocity.getTemplate(location, encoding); + + return template; + } + + /** + * Creates the VelocityContext that we'll use to render this page. + * + * @param velocityManager a reference to the velocityManager to use + * @param stack the value stack to resolve the location against (when parse + * equals true) + * @param location the name of the template that is being used + * @return the a minted Velocity context. + */ + protected Context createContext(VelocityManager velocityManager, + ValueStack stack, HttpServletRequest request, + HttpServletResponse response, String location) { + return velocityManager.createContext(stack, request, response); + } +}