SWF-953 View data binding results lost after Action phase in Portlet environment, not propogating to Render phase.

The mapping results are cached in flash scope for portlet mvc requests.  JSF portlets still demonstrate this issue.
This commit is contained in:
Scott Andrews
2008-11-12 16:39:28 +00:00
parent ac8e806d00
commit 4b63c514ab
5 changed files with 190 additions and 0 deletions

View File

@@ -0,0 +1,65 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed 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.springframework.webflow.mvc.portlet;
import org.springframework.binding.mapping.MappingResults;
/**
* Holder class for mapping results to pass them from an ActionRequest to a RenderRequest
*
* @author Scott Andrews
*/
class MappingResultsHolder {
static final String MAPPING_RESULTS_HOLDER_KEY = "org.springframework.webflow.mvc.portlet.MAPPING_RESULTS_HOLDER";
private String eventId;
private MappingResults mappingResults;
private boolean viewErrors;
public MappingResultsHolder(String eventId, MappingResults mappingResults, boolean viewErrors) {
this.eventId = eventId;
this.mappingResults = mappingResults;
this.viewErrors = viewErrors;
}
public String getEventId() {
return eventId;
}
public void setEventId(String eventId) {
this.eventId = eventId;
}
public MappingResults getMappingResults() {
return mappingResults;
}
public void setMappingResults(MappingResults mappingResults) {
this.mappingResults = mappingResults;
}
public boolean getViewErrors() {
return viewErrors;
}
public void setViewErrors(boolean viewErrors) {
this.viewErrors = viewErrors;
}
}

View File

@@ -43,6 +43,18 @@ public class PortletMvcView extends AbstractMvcView {
*/
public PortletMvcView(org.springframework.web.servlet.View view, RequestContext context) {
super(view, context);
}
/*
* Cache {@link MappingResultsHolder} attributes into flow scope so they can be accessed during the RenderRequest
*
* @see AbstractMvcView#processUserEvent()
*/
public void processUserEvent() {
super.processUserEvent();
MappingResultsHolder holder = new MappingResultsHolder(getEventId(), getMappingResults(), getViewErrors());
this.getRequestContext().getFlashScope().put(MappingResultsHolder.MAPPING_RESULTS_HOLDER_KEY, holder);
}
protected void doRender(Map model) throws Exception {

View File

@@ -29,6 +29,7 @@ import org.springframework.webflow.mvc.view.FlowViewResolver;
* Creates Portlet MVC views.
*
* @author Keith Donald
* @author Scott Andrews
*/
public class PortletMvcViewFactory extends AbstractMvcViewFactory {
@@ -49,4 +50,22 @@ public class PortletMvcViewFactory extends AbstractMvcViewFactory {
return new PortletMvcView(view, context);
}
/*
* Populates attributes from {@link MappingResultsHolder}, if available, into the view.
*
* @see AbstractMvcViewFactory#getView(RequestContext)
*/
public org.springframework.webflow.execution.View getView(RequestContext context) {
org.springframework.webflow.execution.View view = super.getView(context);
if (view instanceof AbstractMvcView
&& context.getFlashScope().contains(MappingResultsHolder.MAPPING_RESULTS_HOLDER_KEY)) {
AbstractMvcView mvcView = (AbstractMvcView) view;
MappingResultsHolder holder = (MappingResultsHolder) context.getFlashScope().get(
MappingResultsHolder.MAPPING_RESULTS_HOLDER_KEY);
mvcView.setEventId(holder.getEventId());
mvcView.setMappingResults(holder.getMappingResults());
mvcView.setViewErrors(holder.getViewErrors());
}
return view;
}
}

View File

@@ -415,6 +415,32 @@ public abstract class AbstractMvcView implements View {
eventId = WebUtils.findParameterValue(context.getRequestParameters().asMap(), eventIdParameterName);
}
// accessors for mapping results
public String getEventId() {
return eventId;
}
public void setEventId(String eventId) {
this.eventId = eventId;
}
public MappingResults getMappingResults() {
return mappingResults;
}
public void setMappingResults(MappingResults mappingResults) {
this.mappingResults = mappingResults;
}
public boolean getViewErrors() {
return viewErrors;
}
public void setViewErrors(boolean viewErrors) {
this.viewErrors = viewErrors;
}
private static class PropertyNotFoundError implements MappingResultsCriteria {
public boolean test(MappingResult result) {
return result.isError() && "propertyNotFound".equals(result.getCode());

View File

@@ -1,21 +1,37 @@
package org.springframework.webflow.mvc.portlet;
import java.util.Map;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import junit.framework.TestCase;
import org.easymock.EasyMock;
import org.springframework.binding.expression.support.StaticExpression;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockServletContext;
import org.springframework.mock.web.portlet.MockPortletContext;
import org.springframework.mock.web.portlet.MockRenderRequest;
import org.springframework.mock.web.portlet.MockRenderResponse;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewRendererServlet;
import org.springframework.webflow.execution.RequestContext;
import org.springframework.webflow.expression.DefaultExpressionParserFactory;
import org.springframework.webflow.mvc.view.AbstractMvcView;
import org.springframework.webflow.mvc.view.MvcViewTests.BindBean;
import org.springframework.webflow.test.MockFlowExecutionKey;
import org.springframework.webflow.test.MockRequestContext;
public class PortletMvcViewTests extends TestCase {
private boolean renderCalled;
private Map model;
public void testRender() throws Exception {
RenderRequest request = new MockRenderRequest();
RenderResponse response = new MockRenderResponse();
@@ -32,4 +48,56 @@ public class PortletMvcViewTests extends TestCase {
assertNotNull(request.getAttribute(ViewRendererServlet.MODEL_ATTRIBUTE));
}
public void testResumeEventModelBindingFieldMarkerFieldPresent() throws Exception {
MockRequestContext context = new MockRequestContext();
context.putRequestParameter("_eventId", "submit");
context.putRequestParameter("booleanProperty", "true");
context.putRequestParameter("_booleanProperty", "whatever");
BindBean bindBean = new BindBean();
StaticExpression modelObject = new StaticExpression(bindBean);
modelObject.setExpressionString("bindBean");
context.getCurrentState().getAttributes().put("model", modelObject);
context.getFlowScope().put("bindBean", bindBean);
context.getMockExternalContext().setNativeContext(new MockServletContext());
context.getMockExternalContext().setNativeRequest(new MockHttpServletRequest());
context.getMockExternalContext().setNativeResponse(new MockHttpServletResponse());
context.getMockFlowExecutionContext().setKey(new MockFlowExecutionKey("c1v1"));
org.springframework.web.servlet.View mvcView = new MockView();
AbstractMvcView view = new MockPortletMvcView(mvcView, context);
view.setExpressionParser(DefaultExpressionParserFactory.getExpressionParser());
view.processUserEvent();
assertEquals(true, bindBean.getBooleanProperty());
MappingResultsHolder holder = (MappingResultsHolder) context.getFlashScope().get(
MappingResultsHolder.MAPPING_RESULTS_HOLDER_KEY);
assertEquals("submit", holder.getEventId());
assertNotNull(holder.getMappingResults());
assertFalse(holder.getViewErrors());
}
private class MockPortletMvcView extends PortletMvcView {
public MockPortletMvcView(View view, RequestContext context) {
super(view, context);
}
protected void doRender(Map model) throws Exception {
getView().render(model, (HttpServletRequest) getRequestContext().getExternalContext().getNativeRequest(),
(HttpServletResponse) getRequestContext().getExternalContext().getNativeResponse());
}
}
private class MockView implements View {
public String getContentType() {
return "text/html";
}
public void render(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {
renderCalled = true;
model = model;
}
}
}