From hibernate-commits at lists.jboss.org Tue Apr 20 11:28:20 2010 Content-Type: multipart/mixed; boundary="===============3749827415519430030==" MIME-Version: 1.0 From: hibernate-commits at lists.jboss.org To: hibernate-commits at lists.jboss.org Subject: [hibernate-commits] Hibernate SVN: r19251 - in validator/trunk/hibernate-validator/src: main/java/org/hibernate/validator/constraints and 5 other directories. Date: Tue, 20 Apr 2010 11:28:19 -0400 Message-ID: <201004201528.o3KFSJ8b032679@svn01.web.mwc.hst.phx2.redhat.com> --===============3749827415519430030== Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Author: hardy.ferentschik Date: 2010-04-20 11:28:18 -0400 (Tue, 20 Apr 2010) New Revision: 19251 Added: validator/trunk/hibernate-validator/src/main/java/org/hibernate/validato= r/constraints/ScriptAssert.java validator/trunk/hibernate-validator/src/main/java/org/hibernate/validato= r/constraints/impl/ScriptAssertValidator.java validator/trunk/hibernate-validator/src/main/java/org/hibernate/validato= r/constraints/impl/scriptassert/ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validato= r/constraints/impl/scriptassert/ScriptEvaluator.java validator/trunk/hibernate-validator/src/main/java/org/hibernate/validato= r/constraints/impl/scriptassert/ScriptEvaluatorFactory.java validator/trunk/hibernate-validator/src/main/java/org/hibernate/validato= r/constraints/impl/scriptassert/package.html validator/trunk/hibernate-validator/src/test/java/org/hibernate/validato= r/constraints/impl/ScriptAssertValidatorTest.java Removed: validator/trunk/hibernate-validator/src/main/docbook/en-US/modules/defin= econstraints.xml Modified: validator/trunk/hibernate-validator/src/main/docbook/en-US/modules/using= validator.xml validator/trunk/hibernate-validator/src/main/java/org/hibernate/validato= r/util/annotationfactory/AnnotationDescriptor.java validator/trunk/hibernate-validator/src/main/java/org/hibernate/validato= r/util/annotationfactory/AnnotationProxy.java validator/trunk/hibernate-validator/src/main/resources/org/hibernate/val= idator/ValidationMessages.properties validator/trunk/hibernate-validator/src/main/resources/org/hibernate/val= idator/ValidationMessages_de.properties Log: HV-292 - Provide a constraint annotation @ScriptAssert Deleted: validator/trunk/hibernate-validator/src/main/docbook/en-US/modules= /defineconstraints.xml =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- validator/trunk/hibernate-validator/src/main/docbook/en-US/modules/defi= neconstraints.xml 2010-04-20 15:22:58 UTC (rev 19250) +++ validator/trunk/hibernate-validator/src/main/docbook/en-US/modules/defi= neconstraints.xml 2010-04-20 15:28:18 UTC (rev 19251) @@ -1,487 +0,0 @@ - - - - - - Defining constraints - -
- What is a constraint? - - A constraint is a rule that a given element (field, property or - bean) has to comply to. The rule semantic is expressed by an annotatio= n. A - constraint usually has some attributes used to parameterize the - constraints limits. The constraint applies to the annotated - element. -
- -
- Built in constraints - - Hibernate Validator comes with some built-in constraints, which - covers most basic data checks. As we'll see later, you're not limited = to - them, you can literally in a minute write your own constraints. - - - Built-in constraints - - - - - - - Annotation - - Apply on - - Runtime checking - - Hibernate Metadata impact - - - - - - @Length(min=3D, max=3D) - - property (String) - - check if the string length match the range - - Column length will be set to max - - - - @Max(value=3D) - - property (numeric or string representation of a - numeric) - - check if the value is less than or equals to max - - Add a check constraint on the column - - - - @Min(value=3D) - - property (numeric or string representation of a - numeric) - - check if the value is more than or equals to min - - Add a check constraint on the column - - - - @NotNull - - property - - check if the value is not null - - Column(s) are not null - - - - @NotEmpty - - property - - check if the string is not null nor empty. Check if the - connection is not null nor empty - - Column(s) are not null (for String) - - - - @Past - - property (date or calendar) - - check if the date is in the past - - Add a check constraint on the column - - - - @Future - - property (date or calendar) - - check if the date is in the future - - none - - - - @Pattern(regex=3D"regexp", flag=3D) or @Patterns( - {@Pattern(...)} ) - - property (string) - - check if the property match the regular expression give= n a - match flag (see java.util.regex.Pattern - ) - - none - - - - @Range(min=3D, max=3D) - - property (numeric or string representation of a - numeric) - - check if the value is between min and max - (included) - - Add a check constraint on the column - - - - @Size(min=3D, max=3D) - - property (array, collection, map) - - check if the element size is between min and max - (included) - - none - - - - @AssertFalse - - property - - check that the method evaluates to false (useful for - constraints expressed in code rather than annotations) - - none - - - - @AssertTrue - - property - - check that the method evaluates to true (useful for - constraints expressed in code rather than annotations) - - none - - - - @Valid - - property (object) - - perform validation recursively on the associated object= . If - the object is a Collection or an array, the elements are valid= ated - recursively. If the object is a Map, the value elements are - validated recursively. - - none - - - - @Email - - property (String) - - check whether the string is conform to the email address - specification - - none - - - - @CreditCardNumber - - property (String) - - check whether the string is a well formated credit card - number (derivative of the Luhn algorithm) - - none - - - - @Digits - - property (numeric or string representation of a - numeric) - - check whether the property is a number having up to - integerDigits integer digits and - fractionalDigits fractonal digits - - define column precision and scale - - - - @EAN - - property (string) - - check whether the string is a properly formated EAN or - UPC-A code - - none - - - -
-
- -
- Error messages - - Hibernate Validator comes with a default set of error messages - translated in about ten languages (if yours is not part of it, please = sent - us a patch). You can override those messages by creating a - ValidatorMessages.properties or ( - ValidatorMessages_loc.properties ) and override t= he - needed keys. You can even add your own additional set of messages while - writing your validator annotations. If Hibernate Validator cannot reso= lve - a key from your resourceBundle nor from ValidatorMessage, it falls bac= k to - the default built-in values. - - Alternatively you can provide a - ResourceBundle while checking programmatically = the - validation rules on a bean or if you want a completly different - interpolation mechanism, you can provide an implementation of - org.hibernate.validator.MessageInterpolator (check = the - JavaDoc for more informations). -
- -
- Writing your own constraints - - Extending the set of built-in constraints is extremely easy. Any - constraint consists of two pieces: the constraint - descriptor (the annotation) and the constraint - validator (the implementation class). Here is a - simple user-defined descriptor: - - @ValidatorClass(Capita= lizedValidator.class) -(a)Target(METHOD) -(a)Retention(RUNTIME) -(a)Documented -public @interface Capitalized { - CapitalizeType type() default Capitalize.FIRST; - String message() default "has incorrect capitalization" -} - - type is a parameter describing how the proper= ty - should to be capitalized. This is a user parameter fully dependant on = the - annotation business. - - message is the default string used to describe - the constraint violation and is mandatory. You can hard code the strin= g or - you can externalize part/all of it through the Java ResourceBundle - mechanism. Parameters values are going to be injected inside the messa= ge - when the {parameter} string is found (in our example - Capitalization is not {type} would generate - Capitalization is not FIRST ), externalizing the wh= ole - string in ValidatorMessages.properties is conside= red - good practice. See - . - - @ValidatorClass(Capita= lizedValidator.class) -(a)Target(METHOD) -(a)Retention(RUNTIME) -(a)Documented -public @interface Capitalized { - CapitalizeType type() default Capitalize.FIRST; - String message() default "{validator.capitalized}"; -} - - -#in ValidatorMessages.properties -validator.capitalized =3D Capitalization is not {type} - - - As you can see the {} notation is recursive. - - To link a descriptor to its validator implementation, we use the - @ValidatorClass meta-annotation. The validator class - parameter must name a class which implements - Validator<ConstraintAnnotation> . - - We now have to implement the validator (ie. the rule checking - implementation). A validation implementation can check the value of th= e a - property (by implementing PropertyConstraint ) and/= or - can modify the hibernate mapping metadata to express the constraint at= the - database level (by implementing - PersistentClassConstraint ) - - public class Capitaliz= edValidator - implements Validator<Capitalized>, PropertyConstraint { - private CapitalizeType type; - - //part of the Validator<Annotation> contract, - //allows to get and use the annotation values - public void initialize(Capitalized parameters) { - type =3D parameters.type(); - } - - //part of the property constraint contract - public boolean isValid(Object value) { - if (value=3D=3Dnull) return true; - if ( !(value instanceof String) ) return false; - String string =3D (String) value; - if (type =3D=3D CapitalizeType.ALL) { - return string.equals( string.toUpperCase() ); - } - else { - String first =3D string.substring(0,1); - return first.equals( first.toUpperCase(); - } - } -} - - The isValid() method should return false if t= he - constraint has been violated. For more examples, refer to the built-in - validator implementations. - - We only have seen property level validation, but you can write a - Bean level validation annotation. Instead of receiving the return inst= ance - of a property, the bean itself will be passed to the validator. To - activate the validation checking, just annotated the bean itself inste= ad. - A small sample can be found in the unit test suite. - - If your constraint can be applied multiple times (with different - parameters) on the same property or type, you can use the following - annotation form: - - @Target(METHOD) -(a)Retention(RUNTIME) -(a)Documented -public @interface Patterns { - Pattern[] value(); -} - -(a)Target(METHOD) -(a)Retention(RUNTIME) -(a)Documented -(a)ValidatorClass(PatternValidator.class) -public @interface Pattern { - String regexp(); -} - - Basically an annotation containing the value attribute as an arr= ay - of validator annotations. -
- -
- Annotating your domain model - - Since you are already familiar with annotations now, the syntax - should be very familiar - - public class Address { - private String line1; - private String line2; - private String zip; - private String state; - private String country; - private long id; - - // a not null string of 20 characters maximum - @Length(max=3D20) - @NotNull - public String getCountry() { - return country; - } - - // a non null string - @NotNull - public String getLine1() { - return line1; - } - - //no constraint - public String getLine2() { - return line2; - } - - // a not null string of 3 characters maximum - @Length(max=3D3) @NotNull - public String getState() { - return state; - } - - // a not null numeric string of 5 characters maximum - // if the string is longer, the message will - //be searched in the resource bundle at key 'long' - @Length(max=3D5, message=3D"{long}") - @Pattern(regex=3D"[0-9]+") - @NotNull - public String getZip() { - return zip; - } - - // should always be true - @AssertTrue - public boolean isValid() { - return true; - } - - // a numeric between 1 and 2000 - @Id @Min(1) - @Range(max=3D2000) - public long getId() { - return id; - } -} - - While the example only shows public property validation, you can - also annotate fields of any kind of visibility - - @MyBeanConstraint(max= =3D45 -public class Dog { - @AssertTrue private boolean isMale; - @NotNull protected String getName() { ... }; - ... -} - - You can also annotate interfaces. Hibernate Validator will check= all - superclasses and interfaces extended or implemented by a given bean to - read the appropriate validator annotations. - - public interface Named= { - @NotNull String getName(); - ... -} - -public class Dog implements Named { - - @AssertTrue private boolean isMale; - - public String getName() { ... }; - -} - - - The name property will be checked for nullity when the Dog bean = is - validated. -
-
Modified: validator/trunk/hibernate-validator/src/main/docbook/en-US/module= s/usingvalidator.xml =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- validator/trunk/hibernate-validator/src/main/docbook/en-US/modules/usin= gvalidator.xml 2010-04-20 15:22:58 UTC (rev 19250) +++ validator/trunk/hibernate-validator/src/main/docbook/en-US/modules/usin= gvalidator.xml 2010-04-20 15:28:18 UTC (rev 19251) @@ -1182,7 +1182,7 @@ int, long and the respective wrappers of the primitive types. = - Check whether the annotated value is higher than or equ= al + Checks whether the annotated value is higher than or eq= ual to the specified minimum. = Add a check constraint on the column. @@ -1224,7 +1224,7 @@ field/property. Supported types are String, Collection,= Map and arrays. = - Check whether is annoated element is not + Check whether the annotated element is not null nor empty. = none @@ -1264,8 +1264,9 @@ = field/property. Needs to be a string. = - Check if the annotated string match the regular express= ion - regex. + Checks if the annotated string matches the regular + expression regex considering the given = flag + match. = none @@ -1305,6 +1306,27 @@ = + @ScriptAssert(lang=3D, script=3D, alias=3D) + + no + + type + + Checks whether the given script can successfully be + evaluated against the annotated element. In order to use this + constraint, an implementation of the Java Scripting API as def= ined + by JSR 223 ("Scripting for the JavaTM + Platform") must part of the class path. This is automatically = the + case when running on Java 6. For older Java versions, the JSR = 223 + RI can be added manually to the class path.The expressions to = be + evaluated can be written in any scripting or expression langua= ge, + for which a JSR 223 compatible engine can be found in the class + path. + + none + + + @URL(protocol=3D, host=3D, port=3D) = no @@ -1326,10 +1348,13 @@ = yes = - field/property + field/property. Any non-primitive types are + supported. = - Perform validation recursively on the associated - object. + Performs validation recursively on the associated objec= t. If + the object is a collection or an array, the elements are valid= ated + recursively. If the object is a map, the value elements are + validated recursively. = none Added: validator/trunk/hibernate-validator/src/main/java/org/hibernate/vali= dator/constraints/ScriptAssert.java =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validat= or/constraints/ScriptAssert.java (rev 0) +++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validat= or/constraints/ScriptAssert.java 2010-04-20 15:28:18 UTC (rev 19251) @@ -0,0 +1,139 @@ +// $Id$ +/* +* JBoss, Home of Professional Open Source +* Copyright 2010, Red Hat, Inc. and/or its affiliates, and individual cont= ributors +* by the @authors tag. See the copyright.txt in the distribution for a +* full listing of individual contributors. +* +* 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.hibernate.validator.constraints; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import javax.script.ScriptContext; +import javax.script.ScriptEngineManager; +import javax.validation.Constraint; +import javax.validation.ConstraintDeclarationException; +import javax.validation.Payload; + +import org.hibernate.validator.constraints.impl.ScriptAssertValidator; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + *

+ * A class-level constraint, that evaluates a script expression against the + * annotated element. This constraint can be used to implement validation + * routines, that depend on multiple attributes of the annotated element. + *

+ *

+ * For evaluation of expressions the Java Scripting API as defined by JSR 223 + * ("Scripting for the JavaTM Platform") is used. Therefore an + * implementation of that API must part of the class path. This is automat= ically + * the case when running on Java 6. For older Java versions, the JSR 223 R= I can + * be added manually to the class path. + *

+ * The expressions to be evaluated can be written in any scripting or expr= ession + * language, for which a JSR 223 compatible engine can be found in the cla= ss + * path. The following listing shows an example using the JavaScript engin= e, + * which comes with Java 6:

+ *

+ *

+ * @ScriptAssert(lang =3D "javascript", script =3D "_t=
his.startDate.before(_this.endDate)")
+ * public class CalendarEvent {
+ * 

+ * private Date startDate; + *

+ * private Date endDate; + *

+ * //... + *

+ * } + *

+ *

+ * Using a real expression language in conjunction with a shorter object a= lias allows + * for very compact expressions: + *

+ *
+ * @ScriptAssert(lang =3D "jexl", script =3D "_.startD=
ate < _.endDate", alias =3D "_")
+ * public class CalendarEvent {
+ * 

+ * private Date startDate; + *

+ * private Date endDate; + *

+ * //... + *

+ * } + *

+ *

+ * Accepts any type. + *

+ * + * @author Gunnar Morling + */ +(a)Target({ TYPE }) +(a)Retention(RUNTIME) +(a)Constraint(validatedBy =3D ScriptAssertValidator.class) +(a)Documented +public @interface ScriptAssert { + + String message() default "{org.hibernate.validator.constraints.ScriptAsse= rt.message}"; + + Class[] groups() default { }; + + Class[] payload() default { }; + + /** + * @return The name of the script language used by this constraint as + * expected by the JSR 223 {@link ScriptEngineManager}. A + * {@link ConstraintDeclarationException} will be thrown upon scr= ipt + * evaluation, if no engine for the given language could be found. + */ + String lang(); + + /** + * @return The script to be executed. The script must return + * Boolean.TRUE, if the annotated element could + * successfully be validated, otherwise Boolean.FALSE. + * Returning null or any type other than Boolean will cause a + * {@link ConstraintDeclarationException} upon validation. Any + * exception occurring during script evaluation will be wrapped i= nto + * a ConstraintDeclarationException, too. Within the script, the + * validated object can be accessed from the {@link ScriptContext + * script context} using the name specified in the + * alias attribute. + */ + String script(); + + /** + * @return The name, under which the annotated element shall be registered + * within the script context. Defaults to "_this". + */ + String alias() default "_this"; + + /** + * Defines several @ScriptAssert annotations on the same element. + * + * @author Gunnar Morling + * @see ScriptAssert + */ + @Target({ TYPE }) + @Retention(RUNTIME) + @Documented + public @interface List { + ScriptAssert[] value(); + } +} Property changes on: validator/trunk/hibernate-validator/src/main/java/org/= hibernate/validator/constraints/ScriptAssert.java ___________________________________________________________________ Name: svn:keywords + Id Added: validator/trunk/hibernate-validator/src/main/java/org/hibernate/vali= dator/constraints/impl/ScriptAssertValidator.java =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validat= or/constraints/impl/ScriptAssertValidator.java (rev= 0) +++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validat= or/constraints/impl/ScriptAssertValidator.java 2010-04-20 15:28:18 UTC (rev= 19251) @@ -0,0 +1,69 @@ +// $Id$ +/* +* JBoss, Home of Professional Open Source +* Copyright 2010, Red Hat, Inc. and/or its affiliates, and individual cont= ributors +* by the @authors tag. See the copyright.txt in the distribution for a +* full listing of individual contributors. +* +* 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.hibernate.validator.constraints.impl; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +import org.hibernate.validator.constraints.ScriptAssert; +import org.hibernate.validator.constraints.impl.scriptassert.ScriptEvaluat= or; +import org.hibernate.validator.constraints.impl.scriptassert.ScriptEvaluat= orFactory; + +/** + * Validator for the {@link ScriptAssert} constraint annotation. + * + * @author Gunnar Morling. + */ +public class ScriptAssertValidator implements ConstraintValidator { + + private String script; + + private String languageName; + + private String alias; + + public void initialize(ScriptAssert constraintAnnotation) { + + validateParameters( constraintAnnotation ); + + this.script =3D constraintAnnotation.script(); + this.languageName =3D constraintAnnotation.lang(); + this.alias =3D constraintAnnotation.alias(); + } + + public boolean isValid(Object value, ConstraintValidatorContext constrain= tValidatorContext) { + + ScriptEvaluator scriptEvaluator =3D ScriptEvaluatorFactory.getInstance() + .getScriptEvaluatorByLanguageName( languageName ); + + return scriptEvaluator.evaluate( script, value, alias ); + } + + private void validateParameters(ScriptAssert constraintAnnotation) { + + if ( constraintAnnotation.script().isEmpty() ) { + throw new IllegalArgumentException( "The parameter \"script\" must not = be empty." ); + } + if ( constraintAnnotation.lang().isEmpty() ) { + throw new IllegalArgumentException( "The parameter \"lang\" must not be= empty." ); + } + if ( constraintAnnotation.alias().isEmpty() ) { + throw new IllegalArgumentException( "The parameter \"alias\" must not b= e empty." ); + } + } +} Property changes on: validator/trunk/hibernate-validator/src/main/java/org/= hibernate/validator/constraints/impl/ScriptAssertValidator.java ___________________________________________________________________ Name: svn:keywords + Id Added: validator/trunk/hibernate-validator/src/main/java/org/hibernate/vali= dator/constraints/impl/scriptassert/ScriptEvaluator.java =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validat= or/constraints/impl/scriptassert/ScriptEvaluator.java = (rev 0) +++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validat= or/constraints/impl/scriptassert/ScriptEvaluator.java 2010-04-20 15:28:18 U= TC (rev 19251) @@ -0,0 +1,108 @@ +// $Id$ +/* +* JBoss, Home of Professional Open Source +* Copyright 2010, Red Hat, Inc. and/or its affiliates, and individual cont= ributors +* by the @authors tag. See the copyright.txt in the distribution for a +* full listing of individual contributors. +* +* 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.hibernate.validator.constraints.impl.scriptassert; + +import javax.script.ScriptEngine; +import javax.script.ScriptException; +import javax.validation.ConstraintDeclarationException; + +/** + * A wrapper around JSR 223 {@link ScriptEngine}s. This class is thread-sa= fe. + * + * @author Gunnar Morling + */ +public class ScriptEvaluator { + + private final ScriptEngine engine; + + /** + * Creates a new script executor. + * + * @param engine The engine to be wrapped. + */ + public ScriptEvaluator(ScriptEngine engine) { + this.engine =3D engine; + } + + /** + * Makes the given object available in then engine-scoped script context = and executes the given script. + * The execution of the script happens either synchronized or unsynchroni= zed, depending on the engine's + * threading abilities. + * + * @param script The script to be executed. + * @param obj The object to be put into the context. + * @param objectAlias The name under which the given object shall be put = into the context. + * + * @return The script's result. + * + * @throws ConstraintDeclarationException In case of any errors during sc= ript execution or if the script + * returned null or another type t= han Boolean. + */ + public boolean evaluate(String script, Object obj, String objectAlias) { + + if ( engineAllowsParallelAccessFromMultipleThreads() ) { + return doEvaluate( script, obj, objectAlias ); + } + else { + synchronized ( engine ) { + return doEvaluate( script, obj, objectAlias ); + } + } + } + + private boolean doEvaluate(String script, Object obj, String objectAlias)= { + + engine.put( objectAlias, obj ); + + Object evaluationResult; + + try { + evaluationResult =3D engine.eval( script ); + } + catch ( ScriptException e ) { + throw new ConstraintDeclarationException( + "Error during execution of script \"" + script + "\" occured.", e + ); + } + + if ( evaluationResult =3D=3D null ) { + throw new ConstraintDeclarationException( "Script \"" + script + "\" re= turned null, but must return either true or false." ); + } + + if ( !( evaluationResult instanceof Boolean ) ) { + throw new ConstraintDeclarationException( + "Script \"" + script + "\" returned " + evaluationResult + " (of type= " + evaluationResult.getClass() + .getCanonicalName() + "), but must return either true or false." + ); + } + + return Boolean.TRUE.equals( evaluationResult ); + } + + /** + * Checks, whether the given engine is thread-safe or not. + * + * @return True, if the given engine is thread-safe, false otherwise. + */ + private boolean engineAllowsParallelAccessFromMultipleThreads() { + + String threadingType =3D ( String ) engine.getFactory().getParameter( "T= HREADING" ); + + return "THREAD-ISOLATED".equals( threadingType ) || "STATELESS".equals( = threadingType ); + } +} Property changes on: validator/trunk/hibernate-validator/src/main/java/org/= hibernate/validator/constraints/impl/scriptassert/ScriptEvaluator.java ___________________________________________________________________ Name: svn:keywords + Id Added: validator/trunk/hibernate-validator/src/main/java/org/hibernate/vali= dator/constraints/impl/scriptassert/ScriptEvaluatorFactory.java =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validat= or/constraints/impl/scriptassert/ScriptEvaluatorFactory.java = (rev 0) +++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validat= or/constraints/impl/scriptassert/ScriptEvaluatorFactory.java 2010-04-20 15:= 28:18 UTC (rev 19251) @@ -0,0 +1,105 @@ +// $Id$ +/* +* JBoss, Home of Professional Open Source +* Copyright 2010, Red Hat, Inc. and/or its affiliates, and individual cont= ributors +* by the @authors tag. See the copyright.txt in the distribution for a +* full listing of individual contributors. +* +* 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.hibernate.validator.constraints.impl.scriptassert; + +import java.lang.ref.Reference; +import java.lang.ref.SoftReference; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.validation.ConstraintDeclarationException; + +/** + * Factory responsible for the creation of {@link ScriptEvaluator}s. This + * class is thread-safe. + * + * @author Gunnar Morling + */ +public class ScriptEvaluatorFactory { + + /** + * A reference with an instance of this factory. Allows the factory to be= reused several times, but can be GC'ed if required. + */ + private static Reference INSTANCE =3D new SoftRef= erence( new ScriptEvaluatorFactory() ); + + /** + * A cache of script executors (keyed by language name). + */ + private ConcurrentMap scriptExecutorCache =3D ne= w ConcurrentHashMap(); + + private ScriptEvaluatorFactory() { + } + + /** + * Retrieves an instance of this factory. + * + * @return A script evaluator factory. Never null. + */ + public static synchronized ScriptEvaluatorFactory getInstance() { + + ScriptEvaluatorFactory theValue =3D INSTANCE.get(); + + if ( theValue =3D=3D null ) { + theValue =3D new ScriptEvaluatorFactory(); + INSTANCE =3D new SoftReference( theValue ); + } + + return theValue; + } + + /** + * Retrieves a script executor for the given language. + * + * @param languageName The name of a scripting language as expected by {@= link ScriptEngineManager#getEngineByName(String)}. + * + * @return A script executor for the given language. Never null. + * + * @throws ConstraintDeclarationException In case no JSR 223 compatible e= ngine for the given language could be found. + */ + public ScriptEvaluator getScriptEvaluatorByLanguageName(String languageNa= me) { + + if ( !scriptExecutorCache.containsKey( languageName ) ) { + + ScriptEvaluator scriptExecutor =3D createNewScriptEvaluator( languageNa= me ); + scriptExecutorCache.putIfAbsent( languageName, scriptExecutor ); + } + + return scriptExecutorCache.get( languageName ); + } + + /** + * Creates a new script executor for the given language. + * + * @param languageName A JSR 223 language name. + * + * @return A newly created script executor for the given language. + */ + private ScriptEvaluator createNewScriptEvaluator(String languageName) { + + ScriptEngine engine =3D new ScriptEngineManager().getEngineByName( langu= ageName ); + + if ( engine =3D=3D null ) { + throw new ConstraintDeclarationException( + "No JSR 223 script engine found for language \"" + languageName + "\"= ." + ); + } + + return new ScriptEvaluator( engine ); + } +} Property changes on: validator/trunk/hibernate-validator/src/main/java/org/= hibernate/validator/constraints/impl/scriptassert/ScriptEvaluatorFactory.ja= va ___________________________________________________________________ Name: svn:keywords + Id Added: validator/trunk/hibernate-validator/src/main/java/org/hibernate/vali= dator/constraints/impl/scriptassert/package.html =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validat= or/constraints/impl/scriptassert/package.html (rev = 0) +++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validat= or/constraints/impl/scriptassert/package.html 2010-04-20 15:28:18 UTC (rev = 19251) @@ -0,0 +1,26 @@ + + + + + + +This package contains classes related to the evaluation of the @ScriptAsse= rt constraint. + + Modified: validator/trunk/hibernate-validator/src/main/java/org/hibernate/v= alidator/util/annotationfactory/AnnotationDescriptor.java =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validat= or/util/annotationfactory/AnnotationDescriptor.java 2010-04-20 15:22:58 UTC= (rev 19250) +++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validat= or/util/annotationfactory/AnnotationDescriptor.java 2010-04-20 15:28:18 UTC= (rev 19251) @@ -1,7 +1,7 @@ // $Id$ /* * JBoss, Home of Professional Open Source -* Copyright 2009, Red Hat, Inc. and/or its affiliates, and individual cont= ributors +* Copyright 2010, Red Hat, Inc. and/or its affiliates, and individual cont= ributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * @@ -39,6 +39,31 @@ = private final Map elements =3D new HashMap(); = + /** + * Returns a new descriptor for the given annotation type. + * = + * @param The type of the annotation. + * @param annotationType The annotation's class. + * = + * @return A new descriptor for the given annotation type. + */ + public static AnnotationDescriptor getInstance(= Class annotationType) { + return new AnnotationDescriptor(annotationType); + } + = + /** + * Returns a new descriptor for the given annotation type. + * = + * @param The type of the annotation. + * @param annotationType The annotation's class. + * @param elements A map with attribute values for the annotation to be c= reated. + * = + * @return A new descriptor for the given annotation type. + */ + public static AnnotationDescriptor getInstance(= Class annotationType, Map elements) { + return new AnnotationDescriptor(annotationType, elements); + } + = public AnnotationDescriptor(Class annotationType) { this.type =3D annotationType; } Modified: validator/trunk/hibernate-validator/src/main/java/org/hibernate/v= alidator/util/annotationfactory/AnnotationProxy.java =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validat= or/util/annotationfactory/AnnotationProxy.java 2010-04-20 15:22:58 UTC (rev= 19250) +++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validat= or/util/annotationfactory/AnnotationProxy.java 2010-04-20 15:28:18 UTC (rev= 19251) @@ -1,7 +1,7 @@ // $Id$ /* * JBoss, Home of Professional Open Source -* Copyright 2009, Red Hat, Inc. and/or its affiliates, and individual cont= ributors +* Copyright 2010, Red Hat, Inc. and/or its affiliates, and individual cont= ributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * @@ -89,7 +89,7 @@ } } if ( processedValuesFromDescriptor !=3D descriptor.numberOfElements() ) { - throw new RuntimeException( "Trying to instantiate " + annotationType += " with unknown paramters." ); + throw new RuntimeException( "Trying to instantiate " + annotationType += " with unknown parameters." ); } return result; } Modified: validator/trunk/hibernate-validator/src/main/resources/org/hibern= ate/validator/ValidationMessages.properties =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- validator/trunk/hibernate-validator/src/main/resources/org/hibernate/va= lidator/ValidationMessages.properties 2010-04-20 15:22:58 UTC (rev 19250) +++ validator/trunk/hibernate-validator/src/main/resources/org/hibernate/va= lidator/ValidationMessages.properties 2010-04-20 15:28:18 UTC (rev 19251) @@ -19,5 +19,5 @@ org.hibernate.validator.constraints.NotEmpty.message=3Dmay not be empty org.hibernate.validator.constraints.Range.message=3Dmust be between {min} = and {max} org.hibernate.validator.constraints.URL.message=3Dmust be a valid URL -org.hibernate.validator.constraints.CreditCardNumber.message=3DInvalid cre= dit card number - +org.hibernate.validator.constraints.CreditCardNumber.message=3Dinvalid cre= dit card number +org.hibernate.validator.constraints.ScriptAssert.message=3Dscript expressi= on "{script}" didn't evaluate to true Modified: validator/trunk/hibernate-validator/src/main/resources/org/hibern= ate/validator/ValidationMessages_de.properties =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- validator/trunk/hibernate-validator/src/main/resources/org/hibernate/va= lidator/ValidationMessages_de.properties 2010-04-20 15:22:58 UTC (rev 19250) +++ validator/trunk/hibernate-validator/src/main/resources/org/hibernate/va= lidator/ValidationMessages_de.properties 2010-04-20 15:28:18 UTC (rev 19251) @@ -18,4 +18,5 @@ org.hibernate.validator.constraints.NotEmpty.message=3Dkann nicht leer sein org.hibernate.validator.constraints.Range.message=3Dmuss zwischen {min} un= d {max} liegen org.hibernate.validator.constraints.URL.message=3Dmuss eine g\u00FCltige U= RL sein -org.hibernate.validator.constraints.CreditCardNumber.message=3DUng\u00FClt= ige Kreditkartennummer \ No newline at end of file +org.hibernate.validator.constraints.CreditCardNumber.message=3Dung\u00FClt= ige Kreditkartennummer +org.hibernate.validator.constraints.ScriptAssert.message=3DSkriptausdruck = "{script}" muss true zur\u00FCckliefern Added: validator/trunk/hibernate-validator/src/test/java/org/hibernate/vali= dator/constraints/impl/ScriptAssertValidatorTest.java =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- validator/trunk/hibernate-validator/src/test/java/org/hibernate/validat= or/constraints/impl/ScriptAssertValidatorTest.java = (rev 0) +++ validator/trunk/hibernate-validator/src/test/java/org/hibernate/validat= or/constraints/impl/ScriptAssertValidatorTest.java 2010-04-20 15:28:18 UTC = (rev 19251) @@ -0,0 +1,202 @@ +// $Id$ +/* +* JBoss, Home of Professional Open Source +* Copyright 2010, Red Hat, Inc. and/or its affiliates, and individual cont= ributors +* by the @authors tag. See the copyright.txt in the distribution for a +* full listing of individual contributors. +* +* 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.hibernate.validator.constraints.impl; + +import java.util.Date; +import java.util.GregorianCalendar; +import javax.validation.ConstraintDeclarationException; +import javax.validation.ConstraintValidator; + +import org.testng.annotations.Test; + +import org.hibernate.validator.constraints.ScriptAssert; +import org.hibernate.validator.util.annotationfactory.AnnotationDescriptor; +import org.hibernate.validator.util.annotationfactory.AnnotationFactory; + +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +/** + * Unit test for {@link ScriptAssertValidator}. + * + * @author Gunnar Morling + */ +public class ScriptAssertValidatorTest { + + @Test + public void scriptEvaluatesToTrue() throws Exception { + + ConstraintValidator validator =3D getInitializedVa= lidator( "javascript", "true" ); + + assertTrue( validator.isValid( new Object(), null ) ); + } + + @Test + public void scriptEvaluatesToFalse() throws Exception { + + ConstraintValidator validator =3D getInitializedVa= lidator( "javascript", "false" ); + + assertFalse( validator.isValid( new Object(), null ) ); + } + + @Test + public void scriptExpressionReferencingAnnotatedObject() throws Exception= { + + ConstraintValidator validator =3D getInitializedVa= lidator( + "javascript", "_this.startDate.before(_this.endDate)" + ); + + Date startDate =3D new GregorianCalendar( 2009, 8, 20 ).getTime(); + Date endDate =3D new GregorianCalendar( 2009, 8, 21 ).getTime(); + + assertTrue( validator.isValid( new CalendarEvent( startDate, endDate ), = null ) ); + assertFalse( validator.isValid( new CalendarEvent( endDate, startDate ),= null ) ); + } + + @Test + public void scriptExpressionUsingCustomizedAlias() throws Exception { + + ConstraintValidator validator =3D getInitializedVa= lidator( + "javascript", "_.startDate.before(_.endDate)", "_" + ); + + Date startDate =3D new GregorianCalendar( 2009, 8, 20 ).getTime(); + Date endDate =3D new GregorianCalendar( 2009, 8, 21 ).getTime(); + + assertFalse( validator.isValid( new CalendarEvent( endDate, startDate ),= null ) ); + } + + @Test(expectedExceptions =3D IllegalArgumentException.class) + public void emptyLanguageNameRaisesException() throws Exception { + + getInitializedValidator( "", "script" ); + } + + @Test(expectedExceptions =3D IllegalArgumentException.class) + public void emptyScriptRaisesException() throws Exception { + + getInitializedValidator( "lang", "" ); + } + + @Test(expectedExceptions =3D IllegalArgumentException.class) + public void emptyAliasRaisesException() throws Exception { + + getInitializedValidator( "lang", "script", "" ); + } + + @Test(expectedExceptions =3D ConstraintDeclarationException.class) + public void unknownLanguageNameRaisesException() throws Exception { + + ConstraintValidator validator =3D getInitializedVa= lidator( "foo", "script" ); + + validator.isValid( new Object(), null ); + } + + @Test(expectedExceptions =3D ConstraintDeclarationException.class) + public void illegalScriptExpressionRaisesException() throws Exception { + + ConstraintValidator validator =3D getInitializedVa= lidator( "javascript", "foo" ); + + validator.isValid( new Object(), null ); + } + + @Test(expectedExceptions =3D ConstraintDeclarationException.class) + public void scriptExpressionReturningNullRaisesException() throws Excepti= on { + + ConstraintValidator validator =3D getInitializedVa= lidator( "javascript", "null" ); + + validator.isValid( new Object(), null ); + } + + @Test(expectedExceptions =3D ConstraintDeclarationException.class) + public void scriptExpressionReturningNoBooleanRaisesException() throws Ex= ception { + + ConstraintValidator validator =3D getInitializedVa= lidator( + "javascript", "new java.util.Date()" + ); + + validator.isValid( new Object(), null ); + } + + /** + * Returns a {@link ScriptAssertValidator} initialized with a {@link Scri= ptAssert} with the given values. + */ + private ConstraintValidator getInitializedValidator= (String lang, String script, String name) { + + ConstraintValidator validator =3D new ScriptAssert= Validator(); + validator.initialize( getScriptAssert( lang, script, name ) ); + + return validator; + } + + /** + * Returns a {@link ScriptAssertValidator} initialized with a {@link Scri= ptAssert} with the given values. + */ + private ConstraintValidator getInitializedValidator= (String lang, String script) { + + ConstraintValidator validator =3D new ScriptAssert= Validator(); + validator.initialize( getScriptAssert( lang, script, null ) ); + + return validator; + } + + /** + * Returns a {@link ScriptAssert} initialized with the given values. + */ + private ScriptAssert getScriptAssert(String lang, String script, String n= ame) { + + AnnotationDescriptor descriptor =3D AnnotationDescriptor.g= etInstance( ScriptAssert.class ); + + descriptor.setValue( "lang", lang ); + descriptor.setValue( "script", script ); + if ( name !=3D null ) { + descriptor.setValue( "alias", name ); + } + + return AnnotationFactory.create( descriptor ); + } + + /** + * An exemplary model class used in tests. + * + * @author Gunnar Morling + */ + private static class CalendarEvent { + + private Date startDate; + + private Date endDate; + + public CalendarEvent(Date startDate, Date endDate) { + + this.startDate =3D startDate; + this.endDate =3D endDate; + } + + @SuppressWarnings("unused") + public Date getStartDate() { + return startDate; + } + + @SuppressWarnings("unused") + public Date getEndDate() { + return endDate; + } + + } +} Property changes on: validator/trunk/hibernate-validator/src/test/java/org/= hibernate/validator/constraints/impl/ScriptAssertValidatorTest.java ___________________________________________________________________ Name: svn:keywords + Id --===============3749827415519430030==--