FileDocCategorySizeDatePackage
Jsr168Dispatcher.javaAPI DocExample25906Mon Jul 23 13:26:38 BST 2007org.apache.struts2.portlet.dispatcher

Jsr168Dispatcher

public class Jsr168Dispatcher extends javax.portlet.GenericPortlet implements org.apache.struts2.StrutsStatics, org.apache.struts2.portlet.PortletActionConstants

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

Name Description Default 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>


Fields Summary
private static final Log
LOG
private com.opensymphony.xwork2.ActionProxyFactory
factory
private Map
modeMap
private Map
actionMap
private String
portletNamespace
private org.apache.struts2.dispatcher.Dispatcher
dispatcherUtils
private org.apache.struts2.dispatcher.mapper.ActionMapper
actionMapper
Constructors Summary
Methods Summary
public java.util.HashMapcreateContextMap(java.util.Map requestMap, java.util.Map parameterMap, java.util.Map sessionMap, java.util.Map applicationMap, javax.portlet.PortletRequest request, javax.portlet.PortletResponse response, javax.portlet.PortletConfig portletConfig, java.lang.Integer phase)
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.


        // TODO Must put http request/response objects into map for use with
        // ServletActionContext
        HashMap<String,Object> extraContext = new HashMap<String,Object>();
        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;
    
public voiddestroy()

        if (dispatcherUtils == null) {
            LOG.warn("something is seriously wrong, DispatcherUtil is not initialized (null) ");
        } else {
            dispatcherUtils.cleanup();
        }
    
protected org.apache.struts2.dispatcher.mapper.ActionMappinggetActionMapping(javax.portlet.PortletRequest request)
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.

        ActionMapping mapping = null;
        String actionPath = null;
        if (resetAction(request)) {
            mapping = (ActionMapping) actionMap.get(request.getPortletMode());
        } else {
            actionPath = request.getParameter(ACTION_PARAM);
            if (!TextUtils.stringSet(actionPath)) {
                mapping = (ActionMapping) actionMap.get(request
                        .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
                HttpServletRequestMock httpRequest = new HttpServletRequestMock()
                    .setServletPath(actionPath + ".action")
                    .setParameterMap(request.getParameterMap());
                mapping = actionMapper.getMapping(httpRequest, 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;
    
java.lang.StringgetActionName(java.lang.String actionPath)
Get the action name part of the action path.

param
actionPath Full path to action
return
The action name.

        int idx = actionPath.lastIndexOf('/");
        String action = actionPath;
        if (idx >= 0) {
            action = actionPath.substring(idx + 1);
        }
        return action;
    
protected java.util.MapgetApplicationMap()
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.

        return new PortletApplicationMap(getPortletContext());
    
java.lang.StringgetNamespace(java.lang.String actionPath)
Get the namespace part of the action path.

param
actionPath Full path to action
return
The namespace part.

        int idx = actionPath.lastIndexOf('/");
        String namespace = "";
        if (idx >= 0) {
            namespace = actionPath.substring(0, idx);
        }
        return namespace;
    
protected java.util.MapgetParameterMap(javax.portlet.PortletRequest request)
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.

        return new HashMap(request.getParameterMap());
    
protected java.util.MapgetRequestMap(javax.portlet.PortletRequest request)
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.

        return new PortletRequestMap(request);
    
protected java.util.MapgetSessionMap(javax.portlet.PortletRequest 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.

        return new PortletSessionMap(request);
    
public voidinit(javax.portlet.PortletConfig cfg)
Initialize the portlet with the init parameters from portlet.xml


                  
          
        super.init(cfg);
        LOG.debug("Initializing portlet " + getPortletName());
        
        Map<String,String> params = new HashMap<String,String>();
        for (Enumeration e = cfg.getInitParameterNames(); e.hasMoreElements(); ) {
            String name = (String) e.nextElement();
            String value = cfg.getInitParameter(name);
            params.put(name, value);
        }
        
        Dispatcher.setPortletSupportActive(true);
        dispatcherUtils = new Dispatcher(ServletContextHolderListener.getServletContext(), params);
        dispatcherUtils.init();
        
        // For testability
        if (factory == null) {
            factory = dispatcherUtils.getConfigurationManager().getConfiguration().getContainer().getInstance(ActionProxyFactory.class);
        }
        portletNamespace = cfg.getInitParameter("portletNamespace");
        LOG.debug("PortletNamespace: " + portletNamespace);
        parseModeConfig(cfg, PortletMode.VIEW, "viewNamespace",
                "defaultViewAction");
        parseModeConfig(cfg, PortletMode.EDIT, "editNamespace",
                "defaultEditAction");
        parseModeConfig(cfg, PortletMode.HELP, "helpNamespace",
                "defaultHelpAction");
        parseModeConfig(cfg, new PortletMode("config"), "configNamespace",
                "defaultConfigAction");
        parseModeConfig(cfg, new PortletMode("about"), "aboutNamespace",
                "defaultAboutAction");
        parseModeConfig(cfg, new PortletMode("print"), "printNamespace",
                "defaultPrintAction");
        parseModeConfig(cfg, new PortletMode("preview"), "previewNamespace",
                "defaultPreviewAction");
        parseModeConfig(cfg, new PortletMode("edit_defaults"),
                "editDefaultsNamespace", "defaultEditDefaultsAction");
        if (!TextUtils.stringSet(portletNamespace)) {
            portletNamespace = "";
        }
        LocalizedTextUtil
                .addDefaultResourceBundle("org/apache/struts2/struts-messages");

        Container 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);
    
private voidparseModeConfig(javax.portlet.PortletConfig portletConfig, javax.portlet.PortletMode portletMode, java.lang.String nameSpaceParam, java.lang.String defaultActionParam)
Parse the mode to namespace mappings configured in portlet.xml

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.

        String namespace = portletConfig.getInitParameter(nameSpaceParam);
        if (!TextUtils.stringSet(namespace)) {
            namespace = "";
        }
        modeMap.put(portletMode, namespace);
        String defaultAction = portletConfig
                .getInitParameter(defaultActionParam);
        if (!TextUtils.stringSet(defaultAction)) {
            defaultAction = DEFAULT_ACTION_NAME;
        }
        StringBuffer fullPath = new StringBuffer();
        if (TextUtils.stringSet(portletNamespace)) {
            fullPath.append(portletNamespace);
        }
        if (TextUtils.stringSet(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()));
        actionMap.put(portletMode, mapping);
    
public voidprocessAction(javax.portlet.ActionRequest request, javax.portlet.ActionResponse response)
Service an action from the event phase.

see
javax.portlet.Portlet#processAction(javax.portlet.ActionRequest, javax.portlet.ActionResponse)

        LOG.debug("Entering processAction");
        resetActionContext();
        try {
            serviceAction(request, response, getActionMapping(request),
                    getRequestMap(request), getParameterMap(request),
                    getSessionMap(request), getApplicationMap(),
                    portletNamespace, EVENT_PHASE);
            LOG.debug("Leaving processAction");
        } finally {
            ActionContext.setContext(null);
        }
    
public voidrender(javax.portlet.RenderRequest request, javax.portlet.RenderResponse response)
Service an action from the render phase.

see
javax.portlet.Portlet#render(javax.portlet.RenderRequest, javax.portlet.RenderResponse)


        LOG.debug("Entering render");
        resetActionContext();
        response.setTitle(getTitle(request));
        try {
            // Check to see if an event set the render to be included directly
            serviceAction(request, response, getActionMapping(request),
                    getRequestMap(request), getParameterMap(request),
                    getSessionMap(request), getApplicationMap(),
                    portletNamespace, RENDER_PHASE);
            LOG.debug("Leaving render");
        } finally {
            resetActionContext();
        }
    
private booleanresetAction(javax.portlet.PortletRequest request)
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.

        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;
            }
        }
        return reset;
    
private voidresetActionContext()
Reset the action context.

        ActionContext.setContext(null);
    
public voidserviceAction(javax.portlet.PortletRequest request, javax.portlet.PortletResponse response, org.apache.struts2.dispatcher.mapper.ActionMapping mapping, java.util.Map requestMap, java.util.Map parameterMap, java.util.Map sessionMap, java.util.Map applicationMap, java.lang.String portletNamespace, java.lang.Integer phase)
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
mapping the action mapping.
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})

        LOG.debug("serviceAction");
        HashMap extraContext = createContextMap(requestMap, parameterMap,
                sessionMap, applicationMap, request, response,
                getPortletConfig(), phase);
        String actionName = mapping.getName();
        String namespace = mapping.getNamespace();
        Dispatcher.setInstance(dispatcherUtils);
        try {
            LOG.debug("Creating action proxy for name = " + actionName
                    + ", namespace = " + namespace);
            ActionProxy proxy = factory.createActionProxy(namespace,
                    actionName, extraContext);
            proxy.setMethod(mapping.getMethod());
            request.setAttribute("struts.valueStack", proxy.getInvocation()
                    .getStack());
            if (PortletActionConstants.RENDER_PHASE.equals(phase)
                    && TextUtils.stringSet(request
                            .getParameter(EVENT_ACTION))) {

                ActionProxy action = (ActionProxy) request.getPortletSession()
                        .getAttribute(EVENT_ACTION);
                if (action != null) {
                    ValueStack stack = proxy.getInvocation().getStack();
                    Object top = stack.pop();
                    stack.push(action.getInvocation().getAction());
                    stack.push(top);
                }
            }
            proxy.execute();
            if (PortletActionConstants.EVENT_PHASE.equals(phase)) {
                // Store the executed action in the session for retrieval in the
                // render phase.
                ActionResponse actionResp = (ActionResponse) response;
                request.getPortletSession().setAttribute(EVENT_ACTION, proxy);
                actionResp.setRenderParameter(EVENT_ACTION, "true");
            }
        } 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);
        }
    
public voidsetActionMapper(org.apache.struts2.dispatcher.mapper.ActionMapper actionMapper)

param
actionMapper the actionMapper to set

        this.actionMapper = actionMapper;
    
protected voidsetActionProxyFactory(com.opensymphony.xwork2.ActionProxyFactory factory)
Convenience method to ease testing.

param
factory

        this.factory = factory;