Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document the use of ParameterMappingInterceptor for typical Portlet MVC setup [SPR-3162] #7848

Closed
spring-projects-issues opened this issue Feb 14, 2007 · 14 comments
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) status: invalid An issue that we don't feel is valid

Comments

@spring-projects-issues
Copy link
Collaborator

Jonathan van Alteren opened SPR-3162 and commented

I'm evaluating Portlet MVC for use in WebSphere Portal 5.1 JSR-168 portlets.

I have created a simple form controller that processes a very simple command class. I have configured it to use a validator and I have set the 'formView' and 'successView' properties. Furthermore, I have configured an internal resource view resolver in a separate applicationContext.xml file.

I am able to navigate the portlet to get to the form, so I guess the formView works for that part. If I enter faulty data in my form I can see in the logs that validation errors occur. However, I am returned to the 'home' view (which is where I started from) instead of going back the formView. I need to be able to show the errors to the user and allow him to resubmit the form (as you would expect).

Am I doing something wrong here? I looked at the code in SimpleFormController (in the ...web.portlet.mvc package) and unlike the non-portlet variety, it does not call showForm(..) (see below).

I can't find anything about this in the docs. Please help!

----------- excerpt from org.springframework.web.portlet.mvc.SimpleFormController.java:
...
protected void processFormSubmission(
ActionRequest request, ActionResponse response, Object command, BindException errors)
throws Exception {

if (errors.hasErrors()) {
     if (logger.isDebugEnabled()) {
          logger.debug("Data binding errors: " + errors.getErrorCount());
     }
     if (isRedirectAction()) {
          setFormSubmit(response);
     }
     passRenderParameters(request, response);
}
else if (isFormChangeRequest(request)) {

...


Affects: 2.0.2

Attachments:

1 votes, 4 watchers

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

Note that the "showForm" call happens in SimpleFormController's "renderFormSubmission" method, not in "processFormSubmission", due to the separate action/render phases in a Portlet environment. This does work in our test cases, so it doesn't seem that there's a general problem in SimpleFormController's workflow. Can you please double-check the behavior that you get, and post the relevant parts of your configuration?

Juergen

@spring-projects-issues
Copy link
Collaborator Author

Jonathan van Alteren commented

Hi Juergen,

First of all, thanks for your fast response to my issue.

You are correct, I did not notice the 'showForm' call in the 'renderFormSubmission' method. However, it turns out that the 'renderFormSubmission' method on my form controller is never called after submission of the form. Instead, the render phase (after the form submission action) is handled by my basic default controller that just forwards to the 'home' view.

After debugging, it seems that the flow is:

  1. click on the link that goes to the 'registerUser' form
    a. portlet:renderURL<portlet:param name="action" value="registerUser" /></portlet:renderURL>
  2. the 'handleRenderRequestInternal' method of my RegisterUserController is called, which results in the method 'showNewForm' being called
  3. the form view is rendered in the portlet
  4. fill in faulty data in the form (keep the fields blank) and submit it
  5. the 'handleActionRequestInternal' method of the RegisterUserController is called, which results in form validation and the 'processFormSubmission' method being called
  6. the 'processFormSubmission' method detects data binding errors and calls the 'passRenderParameters' method
  7. the 'passRenderParameters' method returns immediately, because 'this.renderParameters' is null
  8. my basic default SelfcareController is then called for the render phase
  9. the 'home' view is rendered in the portlet

At step 7, the parameter 'action' with value 'registerUser' is not passed to the render request. This results in the default controller being invoked, instead of returning to the form view with errors. The javadoc description of the 'passRenderParameters' method is:

'Pass the specified list of action request parameters to the render phase by putting them into the action response object. This may not be called when... '

Can I conclude that the implementation of this method in the AbstractFormController is incorrect?

For completeness, I will attach my 'selfcare-portlet.xml' and 'applicationContext.xml' configurations.

Kind regards,
Jonathan

@spring-projects-issues
Copy link
Collaborator Author

Jonathan van Alteren commented

DispatcherPortlet configuration

@spring-projects-issues
Copy link
Collaborator Author

Jonathan van Alteren commented

Application context configuration

@spring-projects-issues
Copy link
Collaborator Author

Jonathan van Alteren commented

PS Please remove attachment #3 (selfcare-portlet.xml 06:13 AM)

@spring-projects-issues
Copy link
Collaborator Author

Jonathan van Alteren commented

Any progress on this issue?

Jonathan

@spring-projects-issues
Copy link
Collaborator Author

Chris Elgar commented

"Pass the specified list of action request parameters to the render phase by putting them into the action response object."

You need to specify the list of parameters to pass to the next phase by setting the renderParameters property. See the setRenderParameters method in AbstractFormController, and also the exposed configuration properties section of the class level javadoc for details.

Hopefully, by specifying "action" as one of the parameters to be passed this will resolve your issue.

@spring-projects-issues
Copy link
Collaborator Author

Jonathan van Alteren commented

Thank you Chris, this does resolve the immediate problem.

In my opinion however, this should not be necessary. Without specifying the 'action' parameter, the form functionality does not work.

Both the Spring Portlet MVC reference and the wiki do not mention the 'renderParameters' property. Also, the Spring Portlet sample provided by the wiki does not use the 'renderParameters' property.

Shouldn't the 'action' parameter always be passed to the render phase, regardless of the 'renderParameters' property?

@spring-projects-issues
Copy link
Collaborator Author

Chris Elgar commented

I am fairly new to the framework and at first I agreed with your comment. But when I thought about it, the only reason that the action parameter needs to be mapped is you have multiple controllers backing your portlet. So this is actually a handler mapping issue instead of a controller issue. The class org.springframework.web.portlet.handler.ParameterMappingInterceptor is probably the intended method of solving this issue when using multiple controllers in this fashion (or alternatively the solution I suggested above if redirects are required - the javadoc for the ParameterMappingInterceptor explains this). See http://static.springframework.org/spring/docs/2.0.x/reference/portlet.html#portlet-handlermapping for the explanation on use of different handler mappings. Section 16.5.6 explains the use of the ParameterMappingInterceptor to ensure that the same controller handles the action and render phases.

Hope this helps.

Chris.

@spring-projects-issues
Copy link
Collaborator Author

Chris Elgar commented

BTW - the sample application available from http://opensource.atlassian.com/confluence/spring/display/JSR168/Home has a books example that uses this approach. The relevant section of the config to declare and use the ParameterMappingInterceptor is:

<bean id="parameterMappingInterceptor" class="org.springframework.web.portlet.handler.ParameterMappingInterceptor"/>

<bean id="portletModeParameterHandlerMapping" class="org.springframework.web.portlet.handler.PortletModeParameterHandlerMapping">
    <property name="order" value="10"/>
	<property name="interceptors">
		<list>
			<ref bean="parameterMappingInterceptor"/>
		</list>
	</property>
	<property name="portletModeParameterMap">
		<map>
			<entry key="view">
				<map>
					<entry key="books"><ref bean="booksController"/></entry>
					<entry key="addBook"><ref bean="bookAddController"/></entry>
				</map>
			</entry>
		</map>
	</property>
</bean>

@spring-projects-issues
Copy link
Collaborator Author

Jonathan van Alteren commented

Thank you Chris! That did the job.

As you might have guessed, I'm also new to the framework and I missed the importance of that section in the reference manual. For every portlet that uses more than 1 controller, this part of the configuration is absolutely vital to get the framework working (IMHO).

To help other (new) users of the Portlet MVC framework, I'd like to suggest to add another configuration example to (or after) section 16.5.6 that demonstrates the combined use of the portlet mode and portlet mode parameter handler mappings, together with the parameter mapping interceptor:

<bean id="parameterMappingInterceptor" class="org.springframework.web.portlet.handler.ParameterMappingInterceptor" />

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

<bean id="portletModeHandlerMapping" class="org.springframework.web.portlet.handler.PortletModeHandlerMapping">
    <property name="order" value="20" />
    <property name="portletModeMap">
        <map>
            <entry key="view" value-ref="viewHandler" />
            <entry key="edit" value-ref="editHandler" />
            <entry key="help" value-ref="helpHandler" />
        </map>
    </property>
</bean>

I guess this issue can be closed. Thanks for your help!

@spring-projects-issues
Copy link
Collaborator Author

Jonathan van Alteren commented

By the way... I agree with you on it being a handler mapping issue instead of a controller issue ;-)

@spring-projects-issues
Copy link
Collaborator Author

Jani Kaarela commented

I think this "feature" is badly enough documented to be called a bug. The section 16.5.2 "ParameterHandlerMapping" should clearly point out the need for ParameterMappingInterceptor. Or, in order to be more intuitive: 1) the whole notion of one portlet -> multiple handlers should be discouraged and the specific steps needed to enable it well documented, or 2) calling passRenderParameters() should by default be called automatically, regardless of the outcome of the earlier processing stages.

Personally, I found it puzzling that processFormSubmission() doesn't call passRenderParameters() in the typical case (ie. no errors, not a form change request). Without digging into the issue, it seemed unintuitive that in some cases it's called and in some not. The behaviour is not obvious and should thus be reconsidered or at least much, much more clearly documented.

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

I'm considering this issue as invalid in the meantime, since the entire form controller hierarchy has been deprecated as of 3.0 and has been removed for 4.0. That said, we do plan to revisit our Portlet support in 4.1, so there's an opportunity for new, up-to-date issues to be considered rather immediately.

Juergen

@spring-projects-issues spring-projects-issues added status: invalid An issue that we don't feel is valid in: web Issues in web modules (web, webmvc, webflux, websocket) type: task A general task and removed type: task A general task labels Jan 11, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) status: invalid An issue that we don't feel is valid
Projects
None yet
Development

No branches or pull requests

1 participant