diff --git a/plugins/oval/src/main/java/org/apache/struts2/oval/interceptor/OValValidationInterceptor.java b/plugins/oval/src/main/java/org/apache/struts2/oval/interceptor/OValValidationInterceptor.java index fd6848be5..8a8d2f74d 100644 --- a/plugins/oval/src/main/java/org/apache/struts2/oval/interceptor/OValValidationInterceptor.java +++ b/plugins/oval/src/main/java/org/apache/struts2/oval/interceptor/OValValidationInterceptor.java @@ -24,6 +24,7 @@ import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor; import com.opensymphony.xwork2.interceptor.PrefixMethodInvocationUtil; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.ActionProxy; +import com.opensymphony.xwork2.ModelDriven; import com.opensymphony.xwork2.Validateable; import com.opensymphony.xwork2.inject.Inject; import com.opensymphony.xwork2.validator.ValidatorContext; @@ -38,6 +39,9 @@ import net.sf.oval.context.FieldContext; import net.sf.oval.context.OValContext; import net.sf.oval.context.MethodReturnValueContext; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.lang.reflect.Method; import java.lang.reflect.Field; @@ -167,7 +171,11 @@ public class OValValidationInterceptor extends MethodFilterInterceptor { //perform validation List violations = validator.validate(action); - if (violations != null) { + addValidationErrors(violations.toArray(new ConstraintViolation[0]), action, valueStack, null); + } + + private void addValidationErrors(ConstraintViolation[] violations, Object action, ValueStack valueStack, String parentFieldname) { + if (violations != null) { ValidatorContext validatorContext = new DelegatingValidatorContext(action); for (ConstraintViolation violation : violations) { //translate message @@ -182,15 +190,34 @@ public class OValValidationInterceptor extends MethodFilterInterceptor { valueStack.pop(); } - if (isActionError(violation)) + if (isActionError(violation)) { + LOG.debug("Adding action error '#0'", message); validatorContext.addActionError(message); - else { + } else { ValidationError validationError = buildValidationError(violation, message); - validatorContext.addFieldError(validationError.getFieldName(), validationError.getMessage()); + + // build field name + String fieldName = validationError.getFieldName(); + if (parentFieldname != null) { + fieldName = parentFieldname + "." + fieldName; + } + + LOG.debug("Adding field error [#0] with message '#1'", fieldName, validationError.getMessage()); + validatorContext.addFieldError(fieldName, validationError.getMessage()); + + // don't add "model." prefix to fields of model in model driven action + if ((action instanceof ModelDriven) && "model".equals(fieldName)) { + fieldName = null; + } + + // add violations of member object fields + addValidationErrors(violation.getCauses(), action, valueStack, fieldName); } } } - } + } + + /** diff --git a/plugins/oval/src/test/java/org/apache/struts2/oval/interceptor/MemberObject.java b/plugins/oval/src/test/java/org/apache/struts2/oval/interceptor/MemberObject.java new file mode 100644 index 000000000..b57bf9210 --- /dev/null +++ b/plugins/oval/src/test/java/org/apache/struts2/oval/interceptor/MemberObject.java @@ -0,0 +1,18 @@ +package org.apache.struts2.oval.interceptor; + +import net.sf.oval.constraint.AssertValid; + +import org.apache.struts2.oval.interceptor.domain.Person; + +import com.opensymphony.xwork2.ActionSupport; + +public class MemberObject extends ActionSupport { + + @AssertValid + private Person person = new Person(); + + public Person getPerson() { + return person; + } + +} diff --git a/plugins/oval/src/test/java/org/apache/struts2/oval/interceptor/ModelDrivenAction.java b/plugins/oval/src/test/java/org/apache/struts2/oval/interceptor/ModelDrivenAction.java new file mode 100644 index 000000000..81596f21d --- /dev/null +++ b/plugins/oval/src/test/java/org/apache/struts2/oval/interceptor/ModelDrivenAction.java @@ -0,0 +1,19 @@ +package org.apache.struts2.oval.interceptor; + +import net.sf.oval.constraint.AssertValid; + +import org.apache.struts2.oval.interceptor.domain.Person; + +import com.opensymphony.xwork2.ActionSupport; +import com.opensymphony.xwork2.ModelDriven; + +public class ModelDrivenAction extends ActionSupport implements ModelDriven { + + @AssertValid + private Person model = new Person(); + + public Person getModel() { + return model; + } + +} diff --git a/plugins/oval/src/test/java/org/apache/struts2/oval/interceptor/OValValidationInterceptorTest.java b/plugins/oval/src/test/java/org/apache/struts2/oval/interceptor/OValValidationInterceptorTest.java index c1ba7abb8..014b5720b 100644 --- a/plugins/oval/src/test/java/org/apache/struts2/oval/interceptor/OValValidationInterceptorTest.java +++ b/plugins/oval/src/test/java/org/apache/struts2/oval/interceptor/OValValidationInterceptorTest.java @@ -82,7 +82,7 @@ public class OValValidationInterceptorTest extends XWorkTestCase { public void testSlashesInNameWithWildcardsHitsCache() throws Exception { ActionProxy baseActionProxy = actionProxyFactory.createActionProxy("oval", "simpleFieldsXML/test", null, null); baseActionProxy.execute(); - + ActionProxy baseActionProxy2 = actionProxyFactory.createActionProxy("oval", "simpleFieldsXML/test2", null, null); baseActionProxy2.execute(); @@ -247,6 +247,40 @@ public class OValValidationInterceptorTest extends XWorkTestCase { } + public void testModelDrivenAction() throws Exception { + ActionProxy baseActionProxy = actionProxyFactory.createActionProxy("oval", "modelDrivenAction", null, null); + ModelDrivenAction action = (ModelDrivenAction) baseActionProxy.getAction(); + action.getModel().setName(null); + action.getModel().setEmail(null); + action.getModel().getAddress().setStreet("short"); + baseActionProxy.execute(); + + Map> fieldErrors = ((ValidationAware) baseActionProxy.getAction()).getFieldErrors(); + assertNotNull(fieldErrors); + assertEquals(5, fieldErrors.size()); // 5: as there will be field errors for 'model' and 'address' themselves + assertValue(fieldErrors, "name", Arrays.asList("name cannot be null")); + assertValue(fieldErrors, "email", Arrays.asList("email cannot be null")); + assertValue(fieldErrors, "address.street", Arrays.asList("street cannot be smaller than 7 characters")); + + } + + public void testMemberObject() throws Exception { + ActionProxy baseActionProxy = actionProxyFactory.createActionProxy("oval", "memberObject", null, null); + MemberObject action = (MemberObject) baseActionProxy.getAction(); + action.getPerson().setName(null); + action.getPerson().setEmail(null); + action.getPerson().getAddress().setStreet("short"); + baseActionProxy.execute(); + + Map> fieldErrors = ((ValidationAware) baseActionProxy.getAction()).getFieldErrors(); + assertNotNull(fieldErrors); + assertEquals(5, fieldErrors.size()); // 5: as there will be field errors for 'person' and 'person.address' themselves + assertValue(fieldErrors, "person.name", Arrays.asList("name cannot be null")); + assertValue(fieldErrors, "person.email", Arrays.asList("email cannot be null")); + assertValue(fieldErrors, "person.address.street", Arrays.asList("street cannot be smaller than 7 characters")); + } + + private void assertValue(Map> map, String key, List expectedValues) { assertNotNull(map); assertNotNull(key); diff --git a/plugins/oval/src/test/java/org/apache/struts2/oval/interceptor/domain/Address.java b/plugins/oval/src/test/java/org/apache/struts2/oval/interceptor/domain/Address.java new file mode 100644 index 000000000..8a0f98afd --- /dev/null +++ b/plugins/oval/src/test/java/org/apache/struts2/oval/interceptor/domain/Address.java @@ -0,0 +1,18 @@ +package org.apache.struts2.oval.interceptor.domain; + +import net.sf.oval.constraint.MinLength; + +public class Address { + + @MinLength(value=7) + private String street; + + public void setStreet(String street) { + this.street = street; + } + + public String getStreet() { + return street; + } + +} diff --git a/plugins/oval/src/test/java/org/apache/struts2/oval/interceptor/domain/Person.java b/plugins/oval/src/test/java/org/apache/struts2/oval/interceptor/domain/Person.java new file mode 100644 index 000000000..4811d5f02 --- /dev/null +++ b/plugins/oval/src/test/java/org/apache/struts2/oval/interceptor/domain/Person.java @@ -0,0 +1,43 @@ +package org.apache.struts2.oval.interceptor.domain; + +import javax.persistence.Column; + +import net.sf.oval.constraint.AssertValid; +import net.sf.oval.constraint.NotNull; + +public class Person { + + @Column(nullable=false) + private String name; + + @NotNull + private String email; + + @AssertValid + private Address address = new Address(); + + public void setEmail(String email) { + this.email = email; + } + + public String getEmail() { + return email; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setAddress(Address address) { + this.address = address; + } + + public Address getAddress() { + return address; + } + +} diff --git a/plugins/oval/src/test/resources/oval-test.xml b/plugins/oval/src/test/resources/oval-test.xml index 128d3813a..15a5d498f 100644 --- a/plugins/oval/src/test/resources/oval-test.xml +++ b/plugins/oval/src/test/resources/oval-test.xml @@ -7,7 +7,7 @@ - + @@ -82,5 +82,17 @@ + + + true + + + + + + true + + +