polish of portlet fix

This commit is contained in:
Keith Donald
2008-11-14 04:45:20 +00:00
parent 588b3bc29e
commit d790c2b6f2
7 changed files with 98 additions and 134 deletions

View File

@@ -43,18 +43,6 @@ 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

@@ -50,22 +50,4 @@ 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

@@ -40,6 +40,7 @@ import org.springframework.binding.mapping.impl.DefaultMapper;
import org.springframework.binding.mapping.impl.DefaultMapping;
import org.springframework.binding.message.MessageBuilder;
import org.springframework.binding.message.MessageResolver;
import org.springframework.core.style.ToStringCreator;
import org.springframework.validation.BindingResult;
import org.springframework.web.util.WebUtils;
import org.springframework.webflow.core.collection.ParameterMap;
@@ -81,8 +82,6 @@ public abstract class AbstractMvcView implements View {
private MappingResults mappingResults;
private boolean viewErrors;
private BinderConfiguration binderConfiguration;
/**
@@ -159,6 +158,9 @@ public abstract class AbstractMvcView implements View {
}
model.put("currentUser", requestContext.getExternalContext().getCurrentUser());
try {
if (logger.isDebugEnabled()) {
logger.debug("Rendering MVC [" + view + "] with model map [" + model + "]");
}
doRender(model);
} catch (IOException e) {
throw e;
@@ -185,29 +187,27 @@ public abstract class AbstractMvcView implements View {
return;
}
if (logger.isDebugEnabled()) {
logger.debug("Obtained model " + model);
logger.debug("Resolved model " + model);
}
TransitionDefinition transition = requestContext.getMatchingTransition(eventId);
if (shouldBind(model, transition)) {
mappingResults = bind(model);
if (hasErrors(mappingResults)) {
viewErrors = true;
if (logger.isDebugEnabled()) {
logger.debug("Binding resulted in errors; adding error messages to context");
logger.debug("Model binding resulted in errors; adding error messages to context");
}
addErrorMessages(mappingResults);
}
}
if (shouldValidate(model, transition)) {
validate(model);
if (!viewErrors & requestContext.getMessageContext().hasErrorMessages()) {
viewErrors = true;
}
}
requestContext.getFlashScope().put(ViewActionStateHolder.KEY,
new ViewActionStateHolder(eventId, mappingResults));
}
public boolean hasFlowEvent() {
return eventId != null && !viewErrors;
return eventId != null && !requestContext.getMessageContext().hasErrorMessages();
}
public Event getFlowEvent() {
@@ -217,6 +217,10 @@ public abstract class AbstractMvcView implements View {
return new Event(this, eventId, requestContext.getRequestParameters().asAttributeMap());
}
public String toString() {
return new ToStringCreator(this).append("view", view).toString();
}
// subclassing hooks
/**
@@ -252,6 +256,14 @@ public abstract class AbstractMvcView implements View {
return WebUtils.findParameterValue(context.getRequestParameters().asMap(), eventIdParameterName);
}
/**
* Returns the id of the user event being processed.
* @return the user event
*/
protected String getEventId() {
return eventId;
}
/**
* Determines if model data binding should be invoked given the Transition that matched the current user event being
* processed. Returns true unless the <code>bind</code> attribute of the Transition has been set to false.
@@ -267,6 +279,21 @@ public abstract class AbstractMvcView implements View {
return transition.getAttributes().getBoolean("bind", Boolean.TRUE).booleanValue();
}
/**
* Returns the results of binding to the view's model, if model binding has occurred.
* @return the binding (mapping) results
*/
protected MappingResults getMappingResults() {
return mappingResults;
}
// package private
void restoreState(ViewActionStateHolder stateHolder) {
eventId = stateHolder.getEventId();
mappingResults = stateHolder.getMappingResults();
}
/**
* Determines if model validation should execute given the Transition that matched the current user event being
* processed. Returns true unless the <code>validate</code> attribute of the Transition has been set to false.
@@ -321,7 +348,7 @@ public abstract class AbstractMvcView implements View {
private MappingResults bind(Object model) {
if (logger.isDebugEnabled()) {
logger.debug("Binding to model" + model);
logger.debug("Binding to model");
}
DefaultMapper mapper = new DefaultMapper();
ParameterMap requestParameters = requestContext.getRequestParameters();
@@ -446,38 +473,12 @@ public abstract class AbstractMvcView implements View {
private void validate(Object model) {
if (logger.isDebugEnabled()) {
logger.debug("Validating model " + model);
logger.debug("Validating model");
}
new ValidationHelper(model, requestContext, eventId, getModelExpression().getExpressionString(),
expressionParser, mappingResults).validate();
}
// 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());
@@ -519,4 +520,5 @@ public abstract class AbstractMvcView implements View {
return "parameter:'" + parameterName + "'";
}
}
}

View File

@@ -83,6 +83,11 @@ public abstract class AbstractMvcViewFactory implements ViewFactory {
if (StringUtils.hasText(fieldMarkerPrefix)) {
mvcView.setFieldMarkerPrefix(fieldMarkerPrefix);
}
ViewActionStateHolder stateHolder = (ViewActionStateHolder) context.getFlashScope().get(
ViewActionStateHolder.KEY);
if (stateHolder != null) {
mvcView.restoreState(stateHolder);
}
return mvcView;
}

View File

@@ -13,53 +13,39 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.webflow.mvc.portlet;
package org.springframework.webflow.mvc.view;
import org.springframework.binding.mapping.MappingResults;
import org.springframework.core.style.ToStringCreator;
/**
* Holder class for mapping results to pass them from an ActionRequest to a RenderRequest
* Holder class for passing view state through a redirect.
*
* @author Scott Andrews
*/
class MappingResultsHolder {
public class ViewActionStateHolder {
static final String MAPPING_RESULTS_HOLDER_KEY = "org.springframework.webflow.mvc.portlet.MAPPING_RESULTS_HOLDER";
public static final String KEY = "webflowViewActionStateHolder";
private String eventId;
private MappingResults mappingResults;
private boolean viewErrors;
public MappingResultsHolder(String eventId, MappingResults mappingResults, boolean viewErrors) {
public ViewActionStateHolder(String eventId, MappingResults mappingResults) {
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 String toString() {
return new ToStringCreator(this).append("eventId", eventId).append("mappingResults", mappingResults).toString();
}
public boolean getViewErrors() {
return viewErrors;
}
public void setViewErrors(boolean viewErrors) {
this.viewErrors = viewErrors;
}
}
}

View File

@@ -18,6 +18,8 @@ package org.springframework.webflow.validation;
import java.lang.reflect.Method;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.binding.expression.ExpressionParser;
import org.springframework.binding.mapping.MappingResults;
@@ -25,6 +27,7 @@ import org.springframework.binding.message.MessageContext;
import org.springframework.binding.message.MessageContextErrors;
import org.springframework.binding.validation.ValidationContext;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.Errors;
@@ -38,6 +41,8 @@ import org.springframework.webflow.execution.RequestContext;
*/
public class ValidationHelper {
private static final Log logger = LogFactory.getLog(ValidationHelper.class);
private final Object model;
private final RequestContext requestContext;
@@ -101,6 +106,9 @@ public class ValidationHelper {
Method validateMethod = ReflectionUtils.findMethod(model.getClass(), methodName,
new Class[] { ValidationContext.class });
if (validateMethod != null) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking current state model validation method '" + methodName + "(ValidationContext)'");
}
ReflectionUtils.invokeMethod(validateMethod, model, new Object[] { new DefaultValidationContext(
requestContext, eventId, mappingResults) });
return true;
@@ -116,6 +124,9 @@ public class ValidationHelper {
if (validateMethod != null) {
MessageContextErrors errors = new MessageContextErrors(requestContext.getMessageContext(), modelName,
model, expressionParser, mappingResults);
if (logger.isDebugEnabled()) {
logger.debug("Invoking current state model validation method '" + methodName + "(Errors)'");
}
ReflectionUtils.invokeMethod(validateMethod, model, new Object[] { errors });
return true;
}
@@ -127,6 +138,9 @@ public class ValidationHelper {
Method validateMethod = ReflectionUtils.findMethod(model.getClass(), "validate",
new Class[] { ValidationContext.class });
if (validateMethod != null) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking default model validation method 'validate(ValidationContext)'");
}
ReflectionUtils.invokeMethod(validateMethod, model, new Object[] { new DefaultValidationContext(
requestContext, eventId, mappingResults) });
return true;
@@ -134,6 +148,9 @@ public class ValidationHelper {
// mvc 2 compatibility only
validateMethod = ReflectionUtils.findMethod(model.getClass(), "validate", new Class[] { Errors.class });
if (validateMethod != null) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking default model validation method 'validate(Errors)'");
}
MessageContextErrors errors = new MessageContextErrors(requestContext.getMessageContext(), modelName,
model, expressionParser, mappingResults);
ReflectionUtils.invokeMethod(validateMethod, model, new Object[] { errors });
@@ -164,6 +181,11 @@ public class ValidationHelper {
Method validateMethod = ReflectionUtils.findMethod(validator.getClass(), methodName, new Class[] {
model.getClass(), ValidationContext.class });
if (validateMethod != null) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking current state validator method '"
+ ClassUtils.getShortName(validator.getClass()) + "." + methodName + "("
+ ClassUtils.getShortName(model.getClass()) + ", ValidationContext)'");
}
ReflectionUtils.invokeMethod(validateMethod, validator, new Object[] { model,
new DefaultValidationContext(requestContext, eventId, mappingResults) });
return true;
@@ -172,6 +194,11 @@ public class ValidationHelper {
validateMethod = ReflectionUtils.findMethod(validator.getClass(), methodName, new Class[] { model.getClass(),
Errors.class });
if (validateMethod != null) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking current state validator method '"
+ ClassUtils.getShortName(validator.getClass()) + "." + methodName + "("
+ ClassUtils.getShortName(model.getClass()) + ", Errors)'");
}
MessageContextErrors errors = new MessageContextErrors(requestContext.getMessageContext(), modelName,
model, expressionParser, mappingResults);
ReflectionUtils.invokeMethod(validateMethod, validator, new Object[] { model, errors });
@@ -191,6 +218,9 @@ public class ValidationHelper {
private boolean invokeValidatorDefaultValidateMethod(Object model, Object validator) {
if (validator instanceof Validator) {
// supports existing validators
if (logger.isDebugEnabled()) {
logger.debug("Invoking Spring Validator '" + ClassUtils.getShortName(validator.getClass()) + "'");
}
MessageContextErrors errors = new MessageContextErrors(requestContext.getMessageContext(), modelName,
model, expressionParser, mappingResults);
((Validator) validator).validate(model, errors);
@@ -200,6 +230,10 @@ public class ValidationHelper {
Method validateMethod = ReflectionUtils.findMethod(validator.getClass(), "validate", new Class[] {
model.getClass(), ValidationContext.class });
if (validateMethod != null) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking default validator method '" + ClassUtils.getShortName(validator.getClass())
+ ".validate(" + ClassUtils.getShortName(model.getClass()) + ", ValidationContext)'");
}
ReflectionUtils.invokeMethod(validateMethod, validator, new Object[] { model,
new DefaultValidationContext(requestContext, eventId, mappingResults) });
return true;
@@ -208,6 +242,10 @@ public class ValidationHelper {
validateMethod = ReflectionUtils.findMethod(validator.getClass(), "validate", new Class[] { model.getClass(),
Errors.class });
if (validateMethod != null) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking default validator method '" + ClassUtils.getShortName(validator.getClass())
+ ".validate(" + ClassUtils.getShortName(model.getClass()) + ", Errors)'");
}
MessageContextErrors errors = new MessageContextErrors(requestContext.getMessageContext(), modelName,
model, expressionParser, mappingResults);
ReflectionUtils.invokeMethod(validateMethod, validator, new Object[] { model, errors });
@@ -215,5 +253,4 @@ public class ValidationHelper {
}
return false;
}
}

View File

@@ -1,11 +1,7 @@
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;
@@ -17,21 +13,16 @@ 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.ViewActionStateHolder;
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();
@@ -48,7 +39,7 @@ public class PortletMvcViewTests extends TestCase {
assertNotNull(request.getAttribute(ViewRendererServlet.MODEL_ATTRIBUTE));
}
public void testResumeEventModelBindingFieldMarkerFieldPresent() throws Exception {
public void testResumeEvent() throws Exception {
MockRequestContext context = new MockRequestContext();
context.putRequestParameter("_eventId", "submit");
context.putRequestParameter("booleanProperty", "true");
@@ -62,42 +53,15 @@ public class PortletMvcViewTests extends TestCase {
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);
org.springframework.web.servlet.View mvcView = (org.springframework.web.servlet.View) EasyMock
.createMock(org.springframework.web.servlet.View.class);
AbstractMvcView view = new PortletMvcView(mvcView, context);
view.setExpressionParser(DefaultExpressionParserFactory.getExpressionParser());
view.processUserEvent();
assertEquals(true, bindBean.getBooleanProperty());
MappingResultsHolder holder = (MappingResultsHolder) context.getFlashScope().get(
MappingResultsHolder.MAPPING_RESULTS_HOLDER_KEY);
ViewActionStateHolder holder = (ViewActionStateHolder) context.getFlashScope().get(ViewActionStateHolder.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;
}
}
}