Objektvalidierung mit dem
Bean Validation API




                  JUG Hamburg – 07.04.2010

                            Gunnar Morling
Gunnar Morling

 • Contributor bei Hibernate Validator
 • Blogger (http://www.gunnarmorling.de/)
 • Softwarearchitekt/Entwicklungsleiter bei der Otto Group




07.04.2010               Bean Validation API                 2/29
Validierung allerorten

 • String:
      – nicht null
      – gültige E-Mail-Adresse oder URL
      – gültige PLZ
 • Zahl:
      – zwischen 0 und 100
      – gültige Kreditkartennummer
 • GUI: Passwort_1 = Passwort_2
 • Rechungsdatum nicht null (aber nur dann, wenn Auftrag im
   Zustand “fakturiert”)




07.04.2010                  Bean Validation API          3/29
Das Problem...




 • Redundante Definition von Constraints
 • Mögliche Inkonsistenzen
 • Verschiedene Runtimes, verschiedene Validatoren


07.04.2010              Bean Validation API              4/29
... und die Lösung




 • Constraints an zentraler Stelle, eine Runtime
 • Realisierungsmöglichkeiten: programmatisch vs. deklarativ
 • Quelle für Constraints in angrenzende Systemen
   (JavaScript, DDL)
07.04.2010               Bean Validation API               5/29
“Hello, Bean Validation!”
 public class Car {

     @NotNull
     private String manufacturer;

     @NotNull
     @Size(min = 2, max = 14)
     private String licensePlate;

     @Min(value=2, message="Seat count must be 2 at least.")
     private int seatCount;
     
     public Car(String manufacturer, String licencePlate, int seatCount) {
         //...
     }
     //...
 }


 • Constraints: Annotationen mit Standard- (u.a. “message”)
   und spezifischen (z.B. “min”, “max”) Attributen


07.04.2010                    Bean Validation API                            6/29
JSR 303: “Bean Validation”

 • JSR 303:
      – Metamodell: Wie werden Constraints ausgedrückt?
      – API: Wie werden Constraints ausgewertet?
 • Spec Lead: Emmanuel Bernard (RedHat)
 • In allen Anwendungsschichten nutzbar
 • Teil von Java EE 6, aber auch unter SE nutzbar
 • Implementierungen:
      – Hibernate Validator (Referenzimplementierung, Teil von GF v3)
      – Agimatec Validation
      – Weitere geplant (z.B. OVal)




07.04.2010                  Bean Validation API                    7/29
Demo 1: Constraints definieren

 • Demo 1:
      – Felder
      – Properties
      – Vererbung




07.04.2010                Bean Validation API   8/29
Validierung von Objektgraphen
 public class Person {

     @NotNull
     @Size(min=3, max=50)
     private String name;

     //...
 }

 public class Car {

     private String manufacturer;
     private String licensePlate;
     private int seatCount;
     
     @NotNull
     private Person driver;

     //...
 }




07.04.2010                    Bean Validation API   9/29
Validierung von Objektgraphen
 public class Person {

     @NotNull
     @Size(min=3, max=50)
     private String name;

     //...
 }

 public class Car {

     private String manufacturer;
     private String licensePlate;
     private int seatCount;
     
     @NotNull
     @Valid
     private Person driver;

     //...
 }



07.04.2010                    Bean Validation API   9/29
Feldübergreifende Validierung

 • Validierung mitunter von mehreren Attributen einer Klasse
   abhängig:
 public class Car {

     private String manufacturer;
     private String licensePlate;

     @Min(value=2)
     private int seatCount;
     
     @NotNull
     @NotEmpty
     private List<Person> passengers;

     //...
 }




07.04.2010                    Bean Validation API         10/29
Feldübergreifende Validierung

 • Mittels Class-Level-Constraints Zugriff auf alle Attribute
   einer Klasse möglich:
 @PassengerCount
 public class Car {

     private String manufacturer;
     private String licensePlate;

     @Min(value=2)
     private int seatCount;
     
     @NotNull
     @NotEmpty
     private List<Person> passengers;

     //...
 }




07.04.2010                    Bean Validation API               10/29
Vordefinierte Constraints

 • Bestandteil von JSR 303
      –   @Null, @NotNull
      –   @AssertFalse, @AssertTrue
      –   @Past, @Future
      –   @Min, @Max, @Digits, @DecimalMin, @DecimalMax
      –   @Size, @Pattern
      –   @Valid
 • Zusätzlich in Hibernate Validator 4.0
      –   @Email
      –   @Length
      –   @NotEmpty
      –   @Range




07.04.2010                  Bean Validation API           11/29
Constraints auswerten

 • javax.validation.Validator:

 Car car = new Car(null, "DD­AB­123", 4);
     
 Validator validator = 
     Validation.buildDefaultValidatorFactory().getValidator();

 //Objekt validieren
 Set<ConstraintViolation<Car>> constraintViolations = validator.validate(car);
     
 //Property validieren
 constraintViolations = validator.validateProperty(car, "licensePlate");
     
 //Wert validieren
 constraintViolations = 
     validator.validateValue(Car.class, "licensePlate", "DD­AB­123");




07.04.2010                     Bean Validation API                       12/29
javax.validation.ConstraintViolation
Car car = new Car("Morris", "DD­AB­123", 1);

Validator validator = 
Validation.buildDefaultValidatorFactory().getValidator();

Set<ConstraintViolation<Car>> constraintViolations = validator.validate(car);
assertEquals(1, constraintViolations.size());

ConstraintViolation<Car> violation = constraintViolations.iterator().next();

assertEquals(car, violation.getRootBean());
assertEquals("seatCount", 
    violation.getPropertyPath().iterator().next().getName());

assertEquals(1, violation.getInvalidValue());
assertEquals("Seat count must be 2 at least.", violation.getMessage());

assertEquals(Min.class,
    violation.getConstraintDescriptor().getAnnotation().annotationType());




07.04.2010                    Bean Validation API                         13/29
Validierungsgruppen

 • Erfordernis, nur Teilmengen der Constraints zu prüfen:
      – Objektlebenszyklus
      – Wizards
      – Nutzerrolle
 • Repräsentation in Form von Interfaces (Vererbungsbe-
   ziehungen, typsicher):
 •
    public interface MyValidationGroup {}

 • Zuordnung von Constraints zu Gruppen:
      – Parameter “groups”
      – Ohne Zuordnung: javax.validation.groups.Default




07.04.2010                    Bean Validation API           14/29
Validierungsgruppen – Beispiel
public class Order {

    @NotNull @Size(min = 10, max = 10)
    @Pattern(regexp = "ON[0­9]*")
    private String orderNumber;

    @Valid @NotNull @Size(min = 1, max = 50)
    private List<OrderLine> orderLines;

    @NotNull @Past
    private Date orderDate;

    @NotNull @Past
    private Date invoiceDate;

    // ...
}




07.04.2010                      Bean Validation API   15/29
Validierungsgruppen – Beispiel
public class Order {

    public interface AfterProcessing {}

    @NotNull @Size(min = 10, max = 10) @Pattern(regexp = "ON[0­9]*")
    private String orderNumber;

    @Valid @NotNull @Size(min = 1, max = 50)
    private List<OrderLine> orderLines;

    @NotNull @Past
    private Date orderDate;

    @NotNull(groups=AfterProcessing.class) @Past(groups=AfterProcessing.class)
    private Date invoiceDate;
    
    // ...
}

Validator validator = ...;
Order order = ...;
validator.validate(order, AfterProcessing.class);



07.04.2010                     Bean Validation API                       15/29
@GroupSequence

 • Festlegung von Ausführungsreihenfolgen:
      – Prüfung sehr teuer (CPU, externe Services etc.)
      – Prüfung von Constraints erfordert validen Basiszustand
 @GroupSequence({Default.class, AfterProcessing.class})
 public interface Complete {}
 •
 • Festlegung der Default-Gruppe:
 @GroupSequence({Order.class, AfterProcessing.class})
 public class Order {

     // ...
 }




07.04.2010                    Bean Validation API                16/29
Eigene Constraints definieren

 • Die Standard-Constraints können um eigene erweitert
   werden, z.B.:
      – Gültige Postleitzahl
      – Quersumme einer Auftragsnummer mod 10 = 0
      – Event.startDate < Event.endDate
 • Was wird benötigt:
      – Constraint-Annotation
      – Validator(en)
      – Fehlertext
      ➔ Demo 2




07.04.2010                  Bean Validation API          17/29
Zusammengesetzte Constraints

 • Drei Constraints für Validierung des Nummernschilds
 • Andere Entitäten mit gleichem Attribut?
 @NotNull
 @Size(min = 2, max = 14)
 @CheckCase(CaseMode.UPPER)
 private String licensePlate;




07.04.2010                      Bean Validation API      18/29
Zusammengesetzte Constraints
 @NotNull
 @Size(min = 2, max = 14)
 @CheckCase(CaseMode.UPPER)
 private String licensePlate;

 @NotNull
 @Size(min = 2, max = 14)
 @CheckCase(CaseMode.UPPER)
 @Constraint(validatedBy={})
 @Target({ElementType.FIELD, ElementType.METHOD})
 @Retention(RetentionPolicy.RUNTIME)
 public @interface ValidLicensePlate {

     String message() default "{de.jughh.bv.ValidLicensePlate.message}";

     Class<?>[] groups() default {};

     Class<? extends Payload>[] payload() default {};

 }




07.04.2010                      Bean Validation API                        19/29
Zusammengesetzte Constraints
 @NotNull
 @Size(min = 2, max = 14)
 @CheckCase(CaseMode.UPPER)
 private String licensePlate;

 @NotNull
 @Size(min = 2, max = 14)
 @CheckCase(CaseMode.UPPER)
 @Constraint(validatedBy={})
 @Target({ElementType.FIELD, ElementType.METHOD})
 @Retention(RetentionPolicy.RUNTIME)
 public @interface ValidLicensePlate {

     String message() default "{de.jughh.bv.ValidLicensePlate.message}";

     Class<?>[] groups() default {};

     Class<? extends Payload>[] payload() default {};

 }

 @ValidLicensePlate
 private String licensePlate;


07.04.2010                      Bean Validation API                        19/29
Zusammengesetzte Constraints

 • Rückgabe einer einzigen Constraint-Verletzung per
   @ReportAsSingleViolation:

 @NotNull
 @Size(min = 2, max = 14)
 @CheckCase(CaseMode.UPPER)
 @ReportAsSingleViolation
 @Constraint(validatedBy={})
 @Target({ElementType.FIELD, ElementType.METHOD})
 @Retention(RetentionPolicy.RUNTIME)
 public @interface ValidLicensePlate {

     String message() default "{de.jughh.bv.ValidLicensePlate.message}";

     Class<?>[] groups() default {};

     Class<? extends Payload>[] payload() default {};

 }




07.04.2010                    Bean Validation API                          20/29
XML-Konfiguration: validation.xml
<?xml version="1.0" encoding="UTF­8"?>
<validation­config
  xmlns="http://jboss.org/xml/ns/javax/validation/configuration"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema­instance"
  xsi:schemaLocation="http://jboss.org/xml/ns/javax/validation/configuration 
                                          validation­configuration­1.0.xsd">
    
    <constraint­validator­factory>
      de.jughh.bv.MyConstraintValidatorFactory
    </constraint­validator­factory>
    <message­interpolator>
      de.jughh.bv.MyMessageInterpolator
    </message­interpolator>

    <constraint­mapping>
      META­INF/validation/car­constraints.xml
    </constraint­mapping>
    <constraint­mapping>...</constraint­mapping>

    <property name="de.jughh.bv.validation.logging">WARN</property>

</validation­config>


07.04.2010                    Bean Validation API                       21/29
XML-Konfiguration: Constraint-Mappings
 <?xml version="1.0" encoding="UTF­8"?>
 <constraint­mappings 
   xmlns="http://jboss.org/xml/ns/javax/validation/mapping"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema­instance"
   xsi:schemaLocation="http://jboss.org/xml/ns/javax/validation/mapping 
                                                validation­mapping­1.0.xsd">

   <default­package>de.jughh.bv.domain</default­package>

   <bean class="Car" ignore­annotations="false">
     <field name="licensePlate">
       <constraint annotation="javax.validation.constraint.Size">
         <message>Size of license plate is limited</message>
         <groups>
           <value>de.jughh.bv.LightValidation</value>
         </groups>
         <element name="max">10</element>
       </constraint>
     </field>
   </bean>

 </constraint­mappings>


07.04.2010                    Bean Validation API                        22/29
Bootstrapping
 //1. Default­Factory
 Validator validator = 
     Validation.buildDefaultValidatorFactory().getValidator();

 //2. Angepasste Factory
 Configuration<?> configuration = Validation.byDefaultProvider().configure();
     
 ValidatorFactory validatorFactory = configuration
     .messageInterpolator(
         new MyMessageInterpolator(
             configuration.getDefaultMessageInterpolator()))
     .constraintValidatorFactory(
         new MyConstraintValidatorFactory(
             configuration.getDefaultConstraintValidatorFactory()))
     .ignoreXmlConfiguration()
     .buildValidatorFactory();
     
 Validator validator = validatorFactory.getValidator();

 //3. Providerspezifische Konfiguration
 HibernateValidatorConfiguration configuration = 
     Validation.byProvider(HibernateValidator.class).configure();


07.04.2010                     Bean Validation API                       23/29
Integration mit anderen APIs

 • JPA 2
      – Validierung vor Insert, Update (& Delete)
      – Kein Nachladen aus der DB
      – Berücksichtigung von Constraints für DDL
 • JSF 2
      – Autom. Validierung aller gebundenen Properties
      – Wichtig: javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL = true
 • Java EE 6
      –   Injektion von ValidatorFactory und Validator:

     @Resource
     private ValidatorFactory validatorFactory;

     @Resource
     private Validator validator;



07.04.2010                      Bean Validation API                          24/29
07.04.2010   Bean Validation API   /29
Neues in Hibernate Validator 4.1

 • Annotation Processor: Constraintprüfung zur Compile-Zeit
 • Neue Constraints
      –   @CreditCardNumber
      –   @NotBlank
      –   @URL
      –   @ScriptAssert:
 •
 @ScriptAssert(lang = "jexl", script = "_this.startDate < _this.endDate")
 public class CalendarEvent {
 •
     private Date startDate;
 •
     private Date endDate;
 •
     //constructor, getter, setter ...
 } RBL
 •



07.04.2010                     Bean Validation API                          26/29
07.04.2010   Bean Validation API   /29
Fragen & Antworten




             ?!
07.04.2010   Bean Validation API       28/29
Referenzen

 • JSR 303: “Bean Validation”:
   http://jcp.org/en/jsr/detail?id=303
 • Hibernate Validator 4.0:
   http://www.hibernate.org/subprojects/validator.html
 • Hibernate Validator Reference Guide:
   http://docs.jboss.org/hibernate/stable/validator/reference/
   en/html/
 • Forum zu Hibernate Validator:
   https://forum.hibernate.org/viewforum.php?f=9
 • Musings of a programming Addict – Artikel zu BV:
   http://musingsofaprogrammingaddict.blogspot.com/search/
   label/Bean%20Validation



07.04.2010                Bean Validation API                29/29

Objektvalidierung mit dem Bean Validation Api