View Javadoc

1   /*
2    * $Id: Jsr168Dispatcher.java 454565 2006-10-10 00:02:56Z jmitchell $
3    *
4    * Copyright 2006 The Apache Software Foundation.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *      http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.struts2.portlet.dispatcher;
19  
20  import java.io.IOException;
21  import java.util.HashMap;
22  import java.util.Locale;
23  import java.util.Map;
24  
25  import javax.portlet.ActionRequest;
26  import javax.portlet.ActionResponse;
27  import javax.portlet.GenericPortlet;
28  import javax.portlet.PortletConfig;
29  import javax.portlet.PortletException;
30  import javax.portlet.PortletMode;
31  import javax.portlet.PortletRequest;
32  import javax.portlet.PortletResponse;
33  import javax.portlet.RenderRequest;
34  import javax.portlet.RenderResponse;
35  
36  import org.apache.commons.lang.StringUtils;
37  import org.apache.commons.logging.Log;
38  import org.apache.commons.logging.LogFactory;
39  import org.apache.struts2.StrutsConstants;
40  import org.apache.struts2.StrutsStatics;
41  import org.apache.struts2.config.Settings;
42  import org.apache.struts2.dispatcher.ApplicationMap;
43  import org.apache.struts2.dispatcher.Dispatcher;
44  import org.apache.struts2.dispatcher.RequestMap;
45  import org.apache.struts2.dispatcher.SessionMap;
46  import org.apache.struts2.dispatcher.mapper.ActionMapping;
47  import org.apache.struts2.portlet.PortletActionConstants;
48  import org.apache.struts2.portlet.PortletApplicationMap;
49  import org.apache.struts2.portlet.PortletRequestMap;
50  import org.apache.struts2.portlet.PortletSessionMap;
51  import org.apache.struts2.portlet.context.PortletActionContext;
52  import org.apache.struts2.portlet.context.ServletContextHolderListener;
53  import org.apache.struts2.util.AttributeMap;
54  import org.apache.struts2.util.ObjectFactoryInitializable;
55  
56  import com.opensymphony.xwork2.util.ClassLoaderUtil;
57  import com.opensymphony.xwork2.util.FileManager;
58  import com.opensymphony.xwork2.util.ValueStack;
59  import com.opensymphony.xwork2.ActionContext;
60  import com.opensymphony.xwork2.ActionProxy;
61  import com.opensymphony.xwork2.ActionProxyFactory;
62  import com.opensymphony.xwork2.ObjectFactory;
63  import com.opensymphony.xwork2.config.ConfigurationException;
64  import com.opensymphony.xwork2.util.LocalizedTextUtil;
65  
66  /***
67   * <!-- START SNIPPET: javadoc -->
68   * <p>
69   * Struts JSR-168 portlet dispatcher. Similar to the WW2 Servlet dispatcher,
70   * but adjusted to a portal environment. The portlet is configured through the <tt>portlet.xml</tt>
71   * descriptor. Examples and descriptions follow below:
72   * </p>
73   * <!-- END SNIPPET: javadoc --> 
74   * 
75   * @author <a href="nils-helge.garli@bekk.no">Nils-Helge Garli </a>
76   * @author Rainer Hermanns
77   * 
78   * <p><b>Init parameters</b></p>
79   * <!-- START SNIPPET: params -->
80   * <table class="confluenceTable">
81   * <tr>
82   * 	<th class="confluenceTh">Name</th>
83   * <th class="confluenceTh">Description</th>
84   * <th class="confluenceTh">Default value</th>
85   * </tr>
86   * <tr>
87   * 	<td class="confluenceTd">portletNamespace</td><td class="confluenceTd">The namespace for the portlet in the xwork configuration. This 
88   * 		namespace is prepended to all action lookups, and makes it possible to host multiple 
89   * 		portlets in the same portlet application. If this parameter is set, the complete namespace 
90   * 		will be <tt>/portletNamespace/modeNamespace/actionName</tt></td><td class="confluenceTd">The default namespace</td>
91   * </tr>
92   * <tr>
93   *  <td class="confluenceTd">viewNamespace</td><td class="confluenceTd">Base namespace in the xwork configuration for the <tt>view</tt> portlet 
94   * 		mode</td><td class="confluenceTd">The default namespace</td>
95   * </tr>
96   * <tr>
97   *  <td class="confluenceTd">editNamespace</td><td class="confluenceTd">Base namespace in the xwork configuration for the <tt>edit</tt> portlet 
98   * 		mode</td><td class="confluenceTd">The default namespace</td>
99   * </tr>
100  * <tr>
101  *  <td class="confluenceTd">helpNamespace</td><td class="confluenceTd">Base namespace in the xwork configuration for the <tt>help</tt> portlet 
102  * 		mode</td><td class="confluenceTd">The default namespace</td>
103  * </tr>
104  * <tr>
105  *  <td class="confluenceTd">defaultViewAction</td><td class="confluenceTd">Default action to invoke in the <tt>view</tt> portlet mode if no action is
106  * 		specified</td><td class="confluenceTd"><tt>default</tt></td>
107  * </tr>
108  * <tr>
109  *  <td class="confluenceTd">defaultEditAction</td><td class="confluenceTd">Default action to invoke in the <tt>edit</tt> portlet mode if no action is
110  * 		specified</td><td class="confluenceTd"><tt>default</tt></td>
111  * </tr>
112  * <tr>
113  *  <td class="confluenceTd">defaultHelpAction</td><td class="confluenceTd">Default action to invoke in the <tt>help</tt> portlet mode if no action is
114  * 		specified</td><td class="confluenceTd"><tt>default</tt></td>
115  * </tr>
116  * </table>
117  * <!-- END SNIPPET: params -->
118  * <p><b>Example:</b></p>
119  * <pre>
120  * <!-- START SNIPPET: example -->
121  * 
122  * &lt;init-param&gt;
123  *     &lt;!-- The view mode namespace. Maps to a namespace in the xwork config file --&gt;
124  *     &lt;name&gt;viewNamespace&lt;/name&gt;
125  *     &lt;value&gt;/view&lt;/value&gt;
126  * &lt;/init-param&gt;
127  * &lt;init-param&gt;
128  *    &lt;!-- The default action to invoke in view mode --&gt;
129  *	  &lt;name&gt;defaultViewAction&lt;/name&gt;
130  *    &lt;value&gt;index&lt;/value&gt;
131  * &lt;/init-param&gt;
132  * &lt;init-param&gt;
133  *     &lt;!-- The view mode namespace. Maps to a namespace in the xwork config file --&gt;
134  *     &lt;name&gt;editNamespace&lt;/name&gt;
135  *     &lt;value&gt;/edit&lt;/value&gt;
136  * &lt;/init-param&gt;
137  * &lt;init-param&gt;
138  *     &lt;!-- The default action to invoke in view mode --&gt;
139  *     &lt;name&gt;defaultEditAction&lt;/name&gt;
140  *	   &lt;value&gt;index&lt;/value&gt;
141  * &lt;/init-param&gt;
142  * &lt;init-param&gt;
143  *     &lt;!-- The view mode namespace. Maps to a namespace in the xwork config file --&gt;
144  *     &lt;name&gt;helpNamespace&lt;/name&gt;
145  *     &lt;value&gt;/help&lt;/value&gt;
146  * &lt;/init-param&gt;
147  * &lt;init-param&gt;
148  *     &lt;!-- The default action to invoke in view mode --&gt;
149  *     &lt;name&gt;defaultHelpAction&lt;/name&gt;
150  *     &lt;value&gt;index&lt;/value&gt;
151  * &lt;/init-param&gt;
152  *   
153  * <!-- END SNIPPET: example -->
154  * </pre>
155  */
156 public class Jsr168Dispatcher extends GenericPortlet implements StrutsStatics,
157         PortletActionConstants {
158 
159     private static final Log LOG = LogFactory.getLog(Jsr168Dispatcher.class);
160 
161     private ActionProxyFactory factory = null;
162 
163     private Map<PortletMode,String> modeMap = new HashMap<PortletMode,String>(3);
164 
165     private Map<PortletMode,ActionMapping> actionMap = new HashMap<PortletMode,ActionMapping>(3);
166 
167     private String portletNamespace = null;
168     
169     private Dispatcher dispatcherUtils;
170 
171     /***
172      * Initialize the portlet with the init parameters from <tt>portlet.xml</tt>
173      */
174     public void init(PortletConfig cfg) throws PortletException {
175         super.init(cfg);
176         LOG.debug("Initializing portlet " + getPortletName());
177         // For testability
178         if (factory == null) {
179             factory = ActionProxyFactory.getFactory();
180         }
181         portletNamespace = cfg.getInitParameter("portletNamespace");
182         LOG.debug("PortletNamespace: " + portletNamespace);
183         parseModeConfig(cfg, PortletMode.VIEW, "viewNamespace",
184                 "defaultViewAction");
185         parseModeConfig(cfg, PortletMode.EDIT, "editNamespace",
186                 "defaultEditAction");
187         parseModeConfig(cfg, PortletMode.HELP, "helpNamespace",
188                 "defaultHelpAction");
189         parseModeConfig(cfg, new PortletMode("config"), "configNamespace",
190                 "defaultConfigAction");
191         parseModeConfig(cfg, new PortletMode("about"), "aboutNamespace",
192                 "defaultAboutAction");
193         parseModeConfig(cfg, new PortletMode("print"), "printNamespace",
194                 "defaultPrintAction");
195         parseModeConfig(cfg, new PortletMode("preview"), "previewNamespace",
196                 "defaultPreviewAction");
197         parseModeConfig(cfg, new PortletMode("edit_defaults"),
198                 "editDefaultsNamespace", "defaultEditDefaultsAction");
199         if (StringUtils.isEmpty(portletNamespace)) {
200             portletNamespace = "";
201         }
202         LocalizedTextUtil
203                 .addDefaultResourceBundle("org/apache/struts2/struts-messages");
204 
205         //check for configuration reloading
206         if ("true".equalsIgnoreCase(Settings
207                 .get(StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD))) {
208             FileManager.setReloadingConfigs(true);
209         }
210 
211         if ("true".equalsIgnoreCase(Settings.get(StrutsConstants.STRUTS_DEVMODE))) {
212             Settings.set(StrutsConstants.STRUTS_I18N_RELOAD, "true");
213             Settings.set(StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD, "true");
214         }
215 
216         if (Settings.isSet(StrutsConstants.STRUTS_OBJECTFACTORY)) {
217             String className = (String) Settings
218                     .get(StrutsConstants.STRUTS_OBJECTFACTORY);
219             if (className.equals("spring")) {
220                 // note: this class name needs to be in string form so we don't put hard
221                 //       dependencies on spring, since it isn't technically required.
222                 className = "org.apache.struts2.spring.StrutsSpringObjectFactory";
223             } else if (className.equals("plexus")) {
224                 // note: this class name needs to be in string form so we don't put hard
225                 //       dependencies on spring, since it isn't technically required.
226                 className = "org.apache.struts2.plexus.PlexusObjectFactory";
227             }
228 
229             try {
230                 Class clazz = ClassLoaderUtil.loadClass(className,
231                         Jsr168Dispatcher.class);
232                 ObjectFactory objectFactory = (ObjectFactory) clazz
233                         .newInstance();
234                 if (objectFactory instanceof ObjectFactoryInitializable) {
235                     ((ObjectFactoryInitializable) objectFactory)
236                             .init(ServletContextHolderListener
237                                     .getServletContext());
238                 }
239                 ObjectFactory.setObjectFactory(objectFactory);
240             } catch (Exception e) {
241                 LOG.error("Could not load ObjectFactory named " + className
242                         + ". Using default ObjectFactory.", e);
243             }
244         }
245         Dispatcher.setPortletSupportActive(true);
246         dispatcherUtils = new Dispatcher(ServletContextHolderListener.getServletContext());
247     }
248 
249     /***
250      * Parse the mode to namespace mappings configured in portlet.xml
251      * @param portletConfig The PortletConfig
252      * @param portletMode The PortletMode
253      * @param nameSpaceParam Name of the init parameter where the namespace for the mode
254      * is configured.
255      * @param defaultActionParam Name of the init parameter where the default action to
256      * execute for the mode is configured.
257      */
258     private void parseModeConfig(PortletConfig portletConfig,
259             PortletMode portletMode, String nameSpaceParam,
260             String defaultActionParam) {
261         String namespace = portletConfig.getInitParameter(nameSpaceParam);
262         if (StringUtils.isEmpty(namespace)) {
263             namespace = "";
264         }
265         modeMap.put(portletMode, namespace);
266         String defaultAction = portletConfig
267                 .getInitParameter(defaultActionParam);
268         if (StringUtils.isEmpty(defaultAction)) {
269             defaultAction = DEFAULT_ACTION_NAME;
270         }
271         StringBuffer fullPath = new StringBuffer();
272         if (StringUtils.isNotEmpty(portletNamespace)) {
273             fullPath.append(portletNamespace + "/");
274         }
275         if (StringUtils.isNotEmpty(namespace)) {
276             fullPath.append(namespace + "/");
277         }
278         fullPath.append(defaultAction);
279         ActionMapping mapping = new ActionMapping();
280         mapping.setName(getActionName(fullPath.toString()));
281         mapping.setNamespace(getNamespace(fullPath.toString()));
282         actionMap.put(portletMode, mapping);
283     }
284 
285     /***
286      * Service an action from the <tt>event</tt> phase.
287      *
288      * @see javax.portlet.Portlet#processAction(javax.portlet.ActionRequest,
289      *      javax.portlet.ActionResponse)
290      */
291     public void processAction(ActionRequest request, ActionResponse response)
292             throws PortletException, IOException {
293         LOG.debug("Entering processAction");
294         resetActionContext();
295         try {
296             serviceAction(request, response, getActionMapping(request),
297                     getRequestMap(request), getParameterMap(request),
298                     getSessionMap(request), getApplicationMap(),
299                     portletNamespace, EVENT_PHASE);
300             LOG.debug("Leaving processAction");
301         } finally {
302             ActionContext.setContext(null);
303         }
304     }
305 
306     /***
307      * Service an action from the <tt>render</tt> phase.
308      *
309      * @see javax.portlet.Portlet#render(javax.portlet.RenderRequest,
310      *      javax.portlet.RenderResponse)
311      */
312     public void render(RenderRequest request, RenderResponse response)
313             throws PortletException, IOException {
314 
315         LOG.debug("Entering render");
316         resetActionContext();
317         response.setTitle(getTitle(request));
318         try {
319             // Check to see if an event set the render to be included directly
320             serviceAction(request, response, getActionMapping(request),
321                     getRequestMap(request), getParameterMap(request),
322                     getSessionMap(request), getApplicationMap(),
323                     portletNamespace, RENDER_PHASE);
324             LOG.debug("Leaving render");
325         } finally {
326             resetActionContext();
327         }
328     }
329 
330     /***
331      *  Reset the action context.
332      */
333     private void resetActionContext() {
334         ActionContext.setContext(null);
335     }
336 
337     /***
338      * Merges all application and portlet attributes into a single
339      * <tt>HashMap</tt> to represent the entire <tt>Action</tt> context.
340      *
341      * @param requestMap a Map of all request attributes.
342      * @param parameterMap a Map of all request parameters.
343      * @param sessionMap a Map of all session attributes.
344      * @param applicationMap a Map of all servlet context attributes.
345      * @param request the PortletRequest object.
346      * @param response the PortletResponse object.
347      * @param portletConfig the PortletConfig object.
348      * @param phase The portlet phase (render or action, see
349      *        {@link PortletActionConstants})
350      * @return a HashMap representing the <tt>Action</tt> context.
351      */
352     public HashMap createContextMap(Map requestMap, Map parameterMap,
353             Map sessionMap, Map applicationMap, PortletRequest request,
354             PortletResponse response, PortletConfig portletConfig, Integer phase) {
355 
356         // TODO Must put http request/response objects into map for use with
357         // ServletActionContext
358         HashMap<String,Object> extraContext = new HashMap<String,Object>();
359         extraContext.put(ActionContext.PARAMETERS, parameterMap);
360         extraContext.put(ActionContext.SESSION, sessionMap);
361         extraContext.put(ActionContext.APPLICATION, applicationMap);
362 
363         Locale locale = null;
364         if (Settings.isSet(StrutsConstants.STRUTS_LOCALE)) {
365             locale = LocalizedTextUtil.localeFromString(Settings.get(StrutsConstants.STRUTS_LOCALE), request.getLocale());
366         } else {
367             locale = request.getLocale();
368         }
369         extraContext.put(ActionContext.LOCALE, locale);
370 
371         extraContext.put(StrutsStatics.STRUTS_PORTLET_CONTEXT, getPortletContext());
372         extraContext.put(ActionContext.DEV_MODE, Boolean.valueOf(Settings.get(StrutsConstants.STRUTS_DEVMODE)));
373         extraContext.put(REQUEST, request);
374         extraContext.put(RESPONSE, response);
375         extraContext.put(PORTLET_CONFIG, portletConfig);
376         extraContext.put(PORTLET_NAMESPACE, portletNamespace);
377         extraContext.put(DEFAULT_ACTION_FOR_MODE, actionMap.get(request.getPortletMode()));
378         // helpers to get access to request/session/application scope
379         extraContext.put("request", requestMap);
380         extraContext.put("session", sessionMap);
381         extraContext.put("application", applicationMap);
382         extraContext.put("parameters", parameterMap);
383         extraContext.put(MODE_NAMESPACE_MAP, modeMap);
384 
385         extraContext.put(PHASE, phase);
386 
387         AttributeMap attrMap = new AttributeMap(extraContext);
388         extraContext.put("attr", attrMap);
389 
390         return extraContext;
391     }
392 
393     /***
394      * Loads the action and executes it. This method first creates the action
395      * context from the given parameters then loads an <tt>ActionProxy</tt>
396      * from the given action name and namespace. After that, the action is
397      * executed and output channels throught the response object.
398      *
399      * @param request the HttpServletRequest object.
400      * @param response the HttpServletResponse object.
401      * @param mapping the action mapping.
402      * @param requestMap a Map of request attributes.
403      * @param parameterMap a Map of request parameters.
404      * @param sessionMap a Map of all session attributes.
405      * @param applicationMap a Map of all application attributes.
406      * @param portletNamespace the namespace or context of the action.
407      * @param phase The portlet phase (render or action, see
408      *        {@link PortletActionConstants})
409      */
410     public void serviceAction(PortletRequest request, PortletResponse response,
411             ActionMapping mapping, Map requestMap, Map parameterMap,
412             Map sessionMap, Map applicationMap, String portletNamespace,
413             Integer phase) throws PortletException {
414         LOG.debug("serviceAction");
415         Dispatcher.setInstance(dispatcherUtils);
416         HashMap extraContext = createContextMap(requestMap, parameterMap,
417                 sessionMap, applicationMap, request, response,
418                 getPortletConfig(), phase);
419         String actionName = mapping.getName();
420         String namespace = mapping.getNamespace();
421         try {
422             LOG.debug("Creating action proxy for name = " + actionName
423                     + ", namespace = " + namespace);
424             ActionProxy proxy = factory.createActionProxy(
425                     dispatcherUtils.getConfigurationManager().getConfiguration(), namespace,
426                     actionName, extraContext);
427             request.setAttribute("struts.valueStack", proxy.getInvocation()
428                     .getStack());
429             if (PortletActionConstants.RENDER_PHASE.equals(phase)
430                     && StringUtils.isNotEmpty(request
431                             .getParameter(EVENT_ACTION))) {
432 
433                 ActionProxy action = (ActionProxy) request.getPortletSession()
434                         .getAttribute(EVENT_ACTION);
435                 if (action != null) {
436                     ValueStack stack = proxy.getInvocation().getStack();
437                     Object top = stack.pop();
438                     stack.push(action.getInvocation().getAction());
439                     stack.push(top);
440                 }
441             }
442             proxy.execute();
443             if (PortletActionConstants.EVENT_PHASE.equals(phase)) {
444                 // Store the executed action in the session for retrieval in the
445                 // render phase.
446                 ActionResponse actionResp = (ActionResponse) response;
447                 request.getPortletSession().setAttribute(EVENT_ACTION, proxy);
448                 actionResp.setRenderParameter(EVENT_ACTION, "true");
449             }
450         } catch (ConfigurationException e) {
451             LOG.error("Could not find action", e);
452             throw new PortletException("Could not find action " + actionName, e);
453         } catch (Exception e) {
454             LOG.error("Could not execute action", e);
455             throw new PortletException("Error executing action " + actionName,
456                     e);
457         }
458     }
459 
460     /***
461      * Returns a Map of all application attributes. Copies all attributes from
462      * the {@link PortletActionContext}into an {@link ApplicationMap}.
463      * 
464      * @return a Map of all application attributes.
465      */
466     protected Map getApplicationMap() {
467         return new PortletApplicationMap(getPortletContext());
468     }
469 
470     /***
471      * Gets the namespace of the action from the request. The namespace is the
472      * same as the portlet mode. E.g, view mode is mapped to namespace
473      * <code>view</code>, and edit mode is mapped to the namespace
474      * <code>edit</code>
475      * 
476      * @param request the PortletRequest object.
477      * @return the namespace of the action.
478      */
479     protected ActionMapping getActionMapping(PortletRequest request) {
480         ActionMapping mapping = new ActionMapping();
481         if (resetAction(request)) {
482             mapping = (ActionMapping) actionMap.get(request.getPortletMode());
483         } else {
484             String actionPath = request.getParameter(ACTION_PARAM);
485             if (StringUtils.isEmpty(actionPath)) {
486                 mapping = (ActionMapping) actionMap.get(request
487                         .getPortletMode());
488             } else {
489                 String namespace = "";
490                 String action = actionPath;
491                 int idx = actionPath.lastIndexOf('/');
492                 if (idx >= 0) {
493                     namespace = actionPath.substring(0, idx);
494                     action = actionPath.substring(idx + 1);
495                 }
496                 mapping.setName(action);
497                 mapping.setNamespace(namespace);
498             }
499         }
500         return mapping;
501     }
502 
503     /***
504      * Get the namespace part of the action path.
505      * @param actionPath Full path to action
506      * @return The namespace part.
507      */
508     String getNamespace(String actionPath) {
509         int idx = actionPath.lastIndexOf('/');
510         String namespace = "";
511         if (idx >= 0) {
512             namespace = actionPath.substring(0, idx);
513         }
514         return namespace;
515     }
516 
517     /***
518      * Get the action name part of the action path.
519      * @param actionPath Full path to action
520      * @return The action name.
521      */
522     String getActionName(String actionPath) {
523         int idx = actionPath.lastIndexOf('/');
524         String action = actionPath;
525         if (idx >= 0) {
526             action = actionPath.substring(idx + 1);
527         }
528         return action;
529     }
530 
531     /***
532      * Returns a Map of all request parameters. This implementation just calls
533      * {@link PortletRequest#getParameterMap()}.
534      * 
535      * @param request the PortletRequest object.
536      * @return a Map of all request parameters.
537      * @throws IOException if an exception occurs while retrieving the parameter
538      *         map.
539      */
540     protected Map getParameterMap(PortletRequest request) throws IOException {
541         return new HashMap(request.getParameterMap());
542     }
543 
544     /***
545      * Returns a Map of all request attributes. The default implementation is to
546      * wrap the request in a {@link RequestMap}. Override this method to
547      * customize how request attributes are mapped.
548      * 
549      * @param request the PortletRequest object.
550      * @return a Map of all request attributes.
551      */
552     protected Map getRequestMap(PortletRequest request) {
553         return new PortletRequestMap(request);
554     }
555 
556     /***
557      * Returns a Map of all session attributes. The default implementation is to
558      * wrap the reqeust in a {@link SessionMap}. Override this method to
559      * customize how session attributes are mapped.
560      * 
561      * @param request the PortletRequest object.
562      * @return a Map of all session attributes.
563      */
564     protected Map getSessionMap(PortletRequest request) {
565         return new PortletSessionMap(request);
566     }
567 
568     /***
569      * Convenience method to ease testing.
570      * @param factory
571      */
572     protected void setActionProxyFactory(ActionProxyFactory factory) {
573         this.factory = factory;
574     }
575 
576     /***
577      * Check to see if the action parameter is valid for the current portlet mode. If the portlet
578      * mode has been changed with the portal widgets, the action name is invalid, since the
579      * action name belongs to the previous executing portlet mode. If this method evaluates to 
580      * <code>true</code> the <code>default&lt;Mode&gt;Action</code> is used instead.
581      * @param request The portlet request.
582      * @return <code>true</code> if the action should be reset.
583      */
584     private boolean resetAction(PortletRequest request) {
585         boolean reset = false;
586         Map paramMap = request.getParameterMap();
587         String[] modeParam = (String[]) paramMap.get(MODE_PARAM);
588         if (modeParam != null && modeParam.length == 1) {
589             String originatingMode = modeParam[0];
590             String currentMode = request.getPortletMode().toString();
591             if (!currentMode.equals(originatingMode)) {
592                 reset = true;
593             }
594         }
595         return reset;
596     }
597 }