polish of portlet fix
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 + "'";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user