Thursday, January 8, 2009

Additional Custom Portlet Modes Are Probably Not Needed With Spring MVC Thanks to PortletModeParameterHandlerMapping and Handler Chaining

You might have come here because are using Spring MVC and you think you need additional custom portlet modes other than the standard ones: view, edit, help (in addition to the others that your portal might also use). After struggling with this a bit and not seeing what was in front of my face, Eric told me about PortletModeParameterHandlerMapping. It turns out that in Spring MVC (for quite a while) you just configure a bean is basically a lot like having more portlet modes, while still only using one of the "regular" portlet modes (like view, edit, help). Then for the default (no param) version of one or more of the portlet modes (probably you want to do this for all of the modes that are supported), you also have a PortletModeHandlerMapping bean. You chain these by use of the "order" property. For example, the first mapping bean might have:
<property name="order" value="1"/>
and the second one in the chain would have a higher value (although it doesn't have to be sequential) like:
<property name="order" value="2"/>
So altogether you have something like:
<bean id="parameterMappingInterceptor" class="org.springframework.web.portlet.handler.ParameterMappingInterceptor"/>

<bean id="portletModeParameterHandlerMapping"
      class="org.springframework.web.portlet.handler.PortletModeParameterHandlerMapping">
    <property name="order" value="1"/>
    <property name="interceptors">
        <list>
            <ref bean="parameterMappingInterceptor"/>
        </list>
    </property>
    <property name="portletModeParameterMap">
        <map>
            <entry key="view"> <!-- portlet mode: view -->
                <map>
                    <entry key="add">
                        <ref bean="addItemHandler"/>
                    </entry>
                    <entry key="edit">
                        <ref bean="editItemHandler"/>
                    </entry>
                    <entry key="delete">
                        <ref bean="deleteItemHandler"/>
                    </entry>
                </map>
            </entry>
            <entry key="edit"> <!-- portlet mode: edit -->
                <map>
                    <entry key="prefs">
                        <ref bean="preferencesHandler"/>
                    </entry>
                    <entry key="resetPrefs">
                        <ref bean="resetPreferencesHandler"/>
                    </entry>
                </map>
            </entry>
        </map>
    </property>
</bean>

<bean id="portletModeHandlerMapping" class="org.springframework.web.portlet.handler.PortletModeHandlerMapping">
    <property name="order" value="2"/>
    <property name="portletModeMap">
        <map>
            <entry key="view">
                <ref bean="viewHandler"/>
            </entry>
            <entry key="edit">
                <ref bean="editHandler"/>
            </entry>
            <entry key="help">
                <ref bean="helpHandler"/>
            </entry>
        </map>
    </property>
</bean>
Then in the jsp, you just send the additional param in with the name "action" (you can override that name- see PortletModeParameterHandlerMapping javadoc. And it doesn't matter whether you construct an actionURL or renderURL. All that matters is that you specify the "action" param (or if you've overridden it, whatever param name you used). If you don't know what in the heck I'm talking about in regards to "actionURL" and "renderURL", see What is a Portlet? and Part 2.
    <portlet:actionURL>
        <portlet:param name="action" value="incrementBook"/>
        <portlet:param name="book" value="${book.key}"/>
        <portlet:param name="increment" value="-1"/>
    </portlet:actionURL>
Note that the parameterMappingInterceptor is important. Without it, action requests get the "action" parameter, but render requests do not. That makes it look like PortletModeParameterHandlerMapping isn't working/doesn't work or that there is a bug. Also note that if you need to change the action in the controller, one often used way of doing it is via the following which can be set on an ActionResponse (but not a RenderResponse):
response.setRenderParameter("action", "nameOfYourEntryKeyInPortletModeParameterMap");
There is an older presentation, spring-portlet-mvc.pdf, that describes this in more detail. For a sample project showing how this works, look at books.xml and booksView.jsp in spring-portlet-sample-maven.tar.gz. References: * http://www.ja-sig.org/wiki/download/attachments/19378/spring-portlet-mvc.pdf * http://forum.springframework.org/archive/index.php/t-22448.html * http://blog.foofactory.fi/2007/10/spring-portlet-sample-with-maven2-on.html * http://static.springframework.org/spring/docs/2.0.x/api/org/springframework/web/portlet/handler/ParameterMappingInterceptor.html

No comments: