You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@bval.apache.org by ro...@apache.org on 2010/05/05 10:58:59 UTC
svn commit: r941207 - in /incubator/bval/trunk/bval-jsr303/src:
main/java/org/apache/bval/jsr303/ main/java/org/apache/bval/jsr303/util/
test/java/org/apache/bval/constraints/ test/java/org/apache/bval/jsr303/
Author: romanstumm
Date: Wed May 5 08:58:58 2010
New Revision: 941207
URL: http://svn.apache.org/viewvc?rev=941207&view=rev
Log:
BVAL-37 Ensure constraint definitions are valid - from Carlos Varla
Added:
incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/ConstraintDefinitionValidator.java
Modified:
incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/AppendValidationToBuilder.java
incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/Jsr303MetaBeanFactory.java
incubator/bval/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/Password.java
incubator/bval/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ConstraintDefinitionsTest.java
Modified: incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/AppendValidationToBuilder.java
URL: http://svn.apache.org/viewvc/incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/AppendValidationToBuilder.java?rev=941207&r1=941206&r2=941207&view=diff
==============================================================================
--- incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/AppendValidationToBuilder.java (original)
+++ incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/AppendValidationToBuilder.java Wed May 5 08:58:58 2010
@@ -61,14 +61,14 @@ public class AppendValidationToBuilder e
/**
* @return The set of groups from the parent constraint.
*/
- public Set<?> getInheritedGroups() {
+ public Set<Class<?>> getInheritedGroups() {
return builder.getConstraintValidation().getGroups();
}
/**
* @return The set of payloads from the parent constraint.
*/
- public Set<?> getInheritedPayload() {
+ public Set<Class<? extends Payload>> getInheritedPayload() {
return builder.getConstraintValidation().getPayload();
}
Modified: incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/Jsr303MetaBeanFactory.java
URL: http://svn.apache.org/viewvc/incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/Jsr303MetaBeanFactory.java?rev=941207&r1=941206&r2=941207&view=diff
==============================================================================
--- incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/Jsr303MetaBeanFactory.java (original)
+++ incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/Jsr303MetaBeanFactory.java Wed May 5 08:58:58 2010
@@ -21,6 +21,7 @@ package org.apache.bval.jsr303;
import org.apache.bval.MetaBeanFactory;
import org.apache.bval.jsr303.groups.Group;
+import org.apache.bval.jsr303.util.ConstraintDefinitionValidator;
import org.apache.bval.jsr303.util.SecureActions;
import org.apache.bval.jsr303.util.TypeUtils;
import org.apache.bval.jsr303.xml.MetaConstraint;
@@ -304,6 +305,7 @@ public class Jsr303MetaBeanFactory imple
*/
Constraint vcAnno = annotation.annotationType().getAnnotation(Constraint.class);
if (vcAnno != null) {
+ ConstraintDefinitionValidator.validateConstraintDefinition(annotation);
Class<? extends ConstraintValidator<?, ?>>[] validatorClasses;
validatorClasses = findConstraintValidatorClasses(annotation, vcAnno);
return applyConstraint(annotation, validatorClasses, prop, owner, access,
Added: incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/ConstraintDefinitionValidator.java
URL: http://svn.apache.org/viewvc/incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/ConstraintDefinitionValidator.java?rev=941207&view=auto
==============================================================================
--- incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/ConstraintDefinitionValidator.java (added)
+++ incubator/bval/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/ConstraintDefinitionValidator.java Wed May 5 08:58:58 2010
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.bval.jsr303.util;
+
+import javax.validation.Constraint;
+import javax.validation.ConstraintDefinitionException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.Locale;
+
+/**
+ * Internal validator that ensures the correct definition of constraint
+ * annotations.
+ *
+ * @author Carlos Vara
+ */
+public class ConstraintDefinitionValidator {
+
+ /**
+ * Ensures that the constraint definition is valid.
+ *
+ * @param annotation
+ * An annotation which is annotated with {@link Constraint}.
+ * @throws ConstraintDefinitionException
+ * In case the constraint is invalid.
+ */
+ public static void validateConstraintDefinition(Annotation annotation) {
+ validGroups(annotation);
+ validPayload(annotation);
+ validMessage(annotation);
+ validAttributes(annotation);
+ }
+
+ /**
+ * Check that the annotation:
+ * <ul>
+ * <li>Has a groups() method.</li>
+ * <li>Whose default value is an empty Class[] array.</li>
+ * </ul>
+ *
+ * @param annotation
+ * The annotation to check.
+ */
+ private static void validGroups(Annotation annotation) {
+ // Ensure that it has a groups() method...
+ Method groupsMethod = SecureActions.getMethod(annotation.annotationType(), "groups");
+ if ( groupsMethod == null ) {
+ throw new ConstraintDefinitionException("Constraint definition " + annotation + " has no groups() method");
+ }
+
+ // ...whose default value is an empty array
+ Object defaultGroupsValue = groupsMethod.getDefaultValue();
+ if ( defaultGroupsValue instanceof Class<?>[] ) {
+ if ( ((Class[]) defaultGroupsValue).length != 0 ) {
+ throw new ConstraintDefinitionException("Default value for groups() must be an empty array");
+ }
+ }
+ else {
+ throw new ConstraintDefinitionException("Return type for groups() must be of type Class<?>[]");
+ }
+ }
+
+ /**
+ * Check that the annotation:
+ * <ul>
+ * <li>Has a payload() method.</li>
+ * <li>Whose default value is an empty Class[] array.</li>
+ * </ul>
+ *
+ * @param annotation
+ * The annotation to check.
+ */
+ private static void validPayload(Annotation annotation) {
+ // Ensure that it has a payload() method...
+ Method payloadMethod = SecureActions.getMethod(annotation.annotationType(), "payload");
+ if ( payloadMethod == null ) {
+ throw new ConstraintDefinitionException("Constraint definition " + annotation + " has no payload() method");
+ }
+
+ // ...whose default value is an empty array
+ Object defaultPayloadValue = payloadMethod.getDefaultValue();
+ if ( defaultPayloadValue instanceof Class<?>[] ) {
+ if ( ((Class[]) defaultPayloadValue).length != 0 ) {
+ throw new ConstraintDefinitionException("Default value for payload() must be an empty array");
+ }
+ }
+ else {
+ throw new ConstraintDefinitionException("Return type for payload() must be of type Class<? extends Payload>[]");
+ }
+ }
+
+ /**
+ * Check that the annotation:
+ * <ul>
+ * <li>Has a message() method.</li>
+ * <li>Whose default value is a {@link String}.</li>
+ * </ul>
+ *
+ * @param annotation
+ * The annotation to check.
+ */
+ private static void validMessage(Annotation annotation) {
+ // Ensure that it has a message() method...
+ Method messageMethod = SecureActions.getMethod(annotation.annotationType(), "message");
+ if ( messageMethod == null ) {
+ throw new ConstraintDefinitionException("Constraint definition " + annotation + " has no message() method");
+ }
+
+ // ...whose default value is a String
+ Object defaultMessageValue = messageMethod.getDefaultValue();
+ if ( !(defaultMessageValue instanceof String) ) {
+ throw new ConstraintDefinitionException("Return type for message() must be of type String");
+ }
+ }
+
+ /**
+ * Check that the annotation has no methods that start with "valid".
+ *
+ * @param annotation
+ * The annotation to check.
+ */
+ private static void validAttributes(Annotation annotation) {
+ Method[] methods = SecureActions.getDeclaredMethods(annotation.annotationType());
+ for ( Method method : methods ) {
+ // Currently case insensitive, the spec is unclear about this
+ if ( method.getName().toLowerCase(Locale.ENGLISH).startsWith("valid") ) {
+ throw new ConstraintDefinitionException("A constraint annotation cannot have methods which start with 'valid'");
+ }
+ }
+ }
+
+}
Modified: incubator/bval/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/Password.java
URL: http://svn.apache.org/viewvc/incubator/bval/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/Password.java?rev=941207&r1=941206&r2=941207&view=diff
==============================================================================
--- incubator/bval/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/Password.java (original)
+++ incubator/bval/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/Password.java Wed May 5 08:58:58 2010
@@ -22,10 +22,8 @@ import javax.validation.Constraint;
import javax.validation.Payload;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
-
-import org.apache.bval.constraints.NotEmpty;
-
import java.lang.annotation.Retention;
+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
@@ -41,7 +39,7 @@ import static java.lang.annotation.Reten
@Constraint(validatedBy = {})
// test that Password is validated although only combined constraints exists, no own implementation
public @interface Password {
- String[] groups() default {};
+ Class<?>[] groups() default {};
String message() default "Wrong password";
Modified: incubator/bval/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ConstraintDefinitionsTest.java
URL: http://svn.apache.org/viewvc/incubator/bval/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ConstraintDefinitionsTest.java?rev=941207&r1=941206&r2=941207&view=diff
==============================================================================
--- incubator/bval/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ConstraintDefinitionsTest.java (original)
+++ incubator/bval/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ConstraintDefinitionsTest.java Wed May 5 08:58:58 2010
@@ -20,10 +20,9 @@ package org.apache.bval.jsr303;
import junit.framework.Assert;
import junit.framework.TestCase;
+import org.apache.bval.constraints.NotNullValidator;
-import javax.validation.Validation;
-import javax.validation.Validator;
-import javax.validation.ValidatorFactory;
+import javax.validation.*;
import javax.validation.constraints.Min;
import javax.validation.metadata.BeanDescriptor;
import javax.validation.metadata.ConstraintDescriptor;
@@ -71,6 +70,99 @@ public class ConstraintDefinitionsTest e
}
}
+ /**
+ * Checks that a {@link ConstraintDefinitionException} is thrown when
+ * parsing a constraint definition with no <code>groups()</code> method.
+ */
+ public void testNoGroupsConstraint() {
+ try {
+ getValidator().validate(new NoGroups());
+ fail("No exception thrown when parsing a constraint definition with no groups() method");
+ } catch (ConstraintDefinitionException e) {
+ // correct
+ }
+ }
+
+ /**
+ * Checks that a {@link ConstraintDefinitionException} is thrown when
+ * parsing a constraint definition with an invalid <code>groups()</code>
+ * method.
+ */
+ public void testInvalidDefaultGroupsConstraint() {
+ try {
+ getValidator().validate(new InvalidGroups());
+ fail("No exception thrown when parsing a constraint definition with a groups() method does not return Class[]");
+ } catch (ConstraintDefinitionException e) {
+ // correct
+ }
+ }
+
+ /**
+ * Checks that a {@link ConstraintDefinitionException} is thrown when
+ * parsing a constraint definition with no <code>payload()</code> method.
+ */
+ public void testNoPayloadConstraint() {
+ try {
+ getValidator().validate(new NoPayload());
+ fail("No exception thrown when parsing a constraint definition with no payload() method");
+ } catch (ConstraintDefinitionException e) {
+ // correct
+ }
+ }
+
+ /**
+ * Checks that a {@link ConstraintDefinitionException} is thrown when
+ * parsing a constraint definition with an invalid <code>payload()</code>
+ * method.
+ */
+ public void testInvalidDefaultPayloadConstraint() {
+ try {
+ getValidator().validate(new InvalidPayload());
+ fail("No exception thrown when parsing a constraint definition with a payload() method does not return an empty array");
+ } catch (ConstraintDefinitionException e) {
+ // correct
+ }
+ }
+
+ /**
+ * Checks that a {@link ConstraintDefinitionException} is thrown when
+ * parsing a constraint definition with no <code>message()</code> method.
+ */
+ public void testNoMessageConstraint() {
+ try {
+ getValidator().validate(new NoMessage());
+ fail("No exception thrown when parsing a constraint definition with no payload() method");
+ } catch (ConstraintDefinitionException e) {
+ // correct
+ }
+ }
+
+ /**
+ * Checks that a {@link ConstraintDefinitionException} is thrown when
+ * parsing a constraint definition with an invalid <code>message()</code>
+ * method.
+ */
+ public void testInvalidDefaultMessageConstraint() {
+ try {
+ getValidator().validate(new InvalidMessage());
+ fail("No exception thrown when parsing a constraint definition with a message() method does not return a String");
+ } catch (ConstraintDefinitionException e) {
+ // correct
+ }
+ }
+
+ /**
+ * Checks that a {@link ConstraintDefinitionException} is thrown when
+ * parsing a constraint definition with a method starting with 'valid'.
+ */
+ public void testInvalidAttributeConstraint() {
+ try {
+ getValidator().validate(new InvalidAttribute());
+ fail("No exception thrown when parsing a constraint definition with a method starting with 'valid'");
+ } catch (ConstraintDefinitionException e) {
+ // correct
+ }
+ }
public static class Person {
@MinList({
@@ -80,11 +172,115 @@ public class ConstraintDefinitionsTest e
public Integer age;
}
+ @Target({ METHOD, FIELD, ANNOTATION_TYPE })
+ @Retention(RUNTIME)
+ @Documented
+ public static @interface MinList {
+ Min[] value();
+ }
+
+ public static class NoGroups {
+ @NoGroupsConstraint
+ public String prop;
+ }
+
+ @Target({ METHOD, FIELD, ANNOTATION_TYPE })
+ @Retention(RUNTIME)
+ @Documented
+ @Constraint(validatedBy = {NotNullValidator.class})
+ public static @interface NoGroupsConstraint {
+ String message() default "def msg";
+ Class<? extends Payload>[] payload() default {};
+ }
+
+ public static class InvalidGroups {
+ @InvalidGroupsConstraint
+ public String prop;
+ }
+
+ @Target({ METHOD, FIELD, ANNOTATION_TYPE })
+ @Retention(RUNTIME)
+ @Documented
+ @Constraint(validatedBy = {NotNullValidator.class})
+ public static @interface InvalidGroupsConstraint {
+ String message() default "def msg";
+ String[] groups() default { "Group1" };
+ Class<? extends Payload>[] payload() default {};
+ }
+
+ public static class NoPayload {
+ @NoPayloadConstraint
+ public String prop;
+ }
+
+ @Target({ METHOD, FIELD, ANNOTATION_TYPE })
+ @Retention(RUNTIME)
+ @Documented
+ @Constraint(validatedBy = {NotNullValidator.class})
+ public static @interface NoPayloadConstraint {
+ String message() default "def msg";
+ String[] groups() default {};
+ }
+
+ public static class InvalidPayload {
+ @InvalidPayloadConstraint
+ public String prop;
+ }
+
+ @Target({ METHOD, FIELD, ANNOTATION_TYPE })
+ @Retention(RUNTIME)
+ @Documented
+ @Constraint(validatedBy = {NotNullValidator.class})
+ public static @interface InvalidPayloadConstraint {
+ String message() default "def msg";
+ String[] groups() default {};
+ Class<? extends Payload>[] payload() default {Payload1.class};
+ public static class Payload1 implements Payload {
+ }
+ }
+
+ public static class NoMessage {
+ @NoMessageConstraint
+ public String prop;
+ }
+
+ @Target({ METHOD, FIELD, ANNOTATION_TYPE })
+ @Retention(RUNTIME)
+ @Documented
+ @Constraint(validatedBy = {NotNullValidator.class})
+ public static @interface NoMessageConstraint {
+ String[] groups() default {};
+ Class<? extends Payload>[] payload() default {};
+ }
+
+ public static class InvalidMessage {
+ @InvalidMessageConstraint(message=2)
+ public String prop;
+ }
+
+ @Target({ METHOD, FIELD, ANNOTATION_TYPE })
+ @Retention(RUNTIME)
+ @Documented
+ @Constraint(validatedBy = {NotNullValidator.class})
+ public static @interface InvalidMessageConstraint {
+ int message();
+ String[] groups() default {};
+ Class<? extends Payload>[] payload() default {};
+ }
+
+ public static class InvalidAttribute {
+ @InvalidAttributeConstraint
+ public String prop;
+ }
+
+ @Target({ METHOD, FIELD, ANNOTATION_TYPE })
+ @Retention(RUNTIME)
+ @Documented
+ @Constraint(validatedBy = {NotNullValidator.class})
+ public static @interface InvalidAttributeConstraint {
+ String message() default "def msg";
+ String[] groups() default {};
+ Class<? extends Payload>[] payload() default {};
+ String validValue() default "1";
+ }
}
-
-@Target({ METHOD, FIELD, ANNOTATION_TYPE })
-@Retention(RUNTIME)
-@Documented
-@interface MinList {
- Min[] value();
-}
\ No newline at end of file