SlideShare a Scribd company logo
1 of 53
Bean Validation mit JSR-303 Türsteher für Bohnen
Holisticon wurde 2006 gegründet. Das operative Geschäft inDeutschland haben wir zum1. Januar 2007 aufgenommen. Wir sind in Großraum Hamburg vertreten. Wir beraten Konzerne und große mittelständische Unternehmen. Wir sind ein Tochterunternehmen der schwedischenSigma-Gruppe. Holisticon AG
Unser Weltbild
Validierung in üblichen Architekturen  JSR-303 Domänenspezifische Datentypen Integration Spring MVC JSF2  JFace Data Binding Service Boundary Validation Offene Punkte Agenda
Validierung in üblichen Architekturen
Spezifika der Java Enterprise Welt Mehrschicht-Architekturen Bit-Schieber-Anwendung Benutzer-Interaktion Verwendung von (vielen verschiedenen) Frameworks Validierung Eingabevalidierung Design-by-Contract Daten-Integrität Übliche Architekturen
EJB + JSF 2 EJB + RCP Spring +  JSF 2 Spring MVC Übliche Architekturen SWT View JSF Page JFace JSF Controller Controller Integration Serviceschnittstellen Session Beans Spring Beans
Übliche Architekturen Spring MVC Lifecycle Dispatcher Servlet Controller Submitted Form Data Binding Validation Model and View Business Logic View Resolver
Übliche Architekturen Spring MVC Validatoren (klassischer Ansatz) publicclassZipCodeValidatorimplementsValidator{ booleansupports(Class clazz) { returnString.class.equals(clazz);   }   publicvoidvalidate(Objectobj, Errors e) { ValidationUtils.rejectIfEmpty(e, “zipcode", “zipcode.empty");     String z = (String) obj;     if (z.length() != 5) {  e.rejectValue(”zipcode", ”wrong.size”);     }  } }
Übliche Architekturen JSF2 Validatoren (klassischer Ansatz) Restore View Apply Request Values Process Validation Update Model  Values Invoke Application Render  Response
Übliche Architekturen JSF2 Validatoren (klassischer Ansatz) <h:inputText … >   <f:validator validatorId="ZipcodeValidator" /> </h:inputText> @FacesValidator("ZipcodeValidator") publicclassZipcodeValidatorimplementsValidator{ publicvoidvalidate(FacesContextctx, UIComponentcomponent, Objectvalue) throwsValidatorException { if (valueinstanceof String) {       String zipCode = (String) value; if (zipCode == null || zipCode.length() != 5) { thrownewValidatorException(newFacesMessage( FacesMessage.SEVERITY_ERROR, "Wrongzip code", null));       }     }   } }
Übliche Architekturen JFace Data Binding Validatoren (klassischer Ansatz) Observable SWT/JFace Listeners Target Konverter Model-to-Target Update Strategy Target-to-Model Update Strategy Observable Validatoren Validatoren Model Konverter Property Change Listeners
Übliche Architekturen JFace Data Binding Validatoren (klassischer Ansatz) publicclassZipCodeCompositeextends Composite { publicvoidcreateBinding(DataBindingContextdbc) { IObservableValueobservableModel = …  ISWTObservableValueobservableTarget = … UpdateValueStrategy t2m = newUpdateValueStrategy();     t2m.setAfterConvertValidator(newIValidator() { publicIStatusvalidate(Objectvalue) { if ((String) value == null || ((String) value).length() != 5) {           return ValidationStatus.error("Wrong zip code");         } returnValidationStatus.ok();       }     }); dbc.bindValue(observableTarget, observableModel, t2m, null);   } }
Übliche Architekturen JPA Hooks (klassischer Ansatz) @ExcludeDefaultInterceptors publicclassPersistenceValidationListener { @PrePersist   @PreUpdate   @PreRemove publicvoidvalidateMe(Objectentity) { if (entityinstanceofAddress) {       String zipCode = ((Address)entity).getZip(); if (zipCode == null || zipCode.length() != 5) {         throw new IllegalArgumentException(“Wrong zip code”);       }     }   } }
Übliche Architekturen Validatoren für jedes Framework Schicht System Validierung als Teil der Anwendungslogik
Bean Validation JSR-303
Bean Validation Idee Validierungsregeln an Daten Offener JCP Standard (JSR-303) Open Source Implementierung Definiert eine Menge von Constraints Definiert eine Validation API Standard Constraint Annotationen publicclassZipCode {   @Pattern(regexp = "[0-9]*")   @Size(min=5, max=5)   private String content; } @AssertFalse @AssertTrue @DecimalMin(value) @DecimalMax(calue) @Digits(integer, fraction) @Future @Min(value) @Max(value) @NotNull @Null @Past @Pattern(regexp, flags) @Size(min, max) @Valid
Bean Validation @Valid
Bean Validation Validation API publicclassValidatorHelper {   private staticValidatorvalidator = Validation.buildDefaultValidatorFactory().getValidator();   public static void validate(Object object) {     Set<ConstraintViolation<Object>> violations = validator.validate(object); if (violations.size() > 0) { StringBufferbuf = new StringBuffer("constraint violations in")        .append(object.getClass().getName() + "["); for (ConstraintViolation<Object> violation : violations) { buf.append("quot;").append(violation.getPropertyPath()).append("quot; ")          .append(violation.getMessage()).append(" (invalidValue=quot;")          .append(violation.getInvalidValue()).append("quot;); ");       } thrownewIllegalArgumentException(buf.append("]").toString());     }   } }
Bean Validation Eigene Constraints @Target({ FIELD, PARAMETER }) @Retention(RUNTIME) @Constraint(validatedBy = {}) @Pattern(regexp = "[0-9]*") @Size(min = 5, max = 5) @ReportAsSingleViolation public @interfaceZipcodeConstraint {   String message() default "{Zipcode.invalid_zipcode}";   Class<?>[] groups() default {};   Class<? extends Payload>[] payload() default {}; }
Bean Validation Eigener Validator @Constraint(validatedBy = {ZipcodeValidator.class}) public @interfaceZipcodeConstraint {   // … } publicclassZipcodeValidatorimplements ConstraintValidator<ZipcodeConstraint, String> {   private String message; publicvoidinitialize(ZipcodeConstraintannotation) { message = annotation.message();   }   public booleanisValid(String value, ConstraintValidatorContextctx) { if (value.length() != 5) { ctx.buildConstraintViolationWithTemplate(message)        .addConstraintViolation(); returnfalse;     } returntrue;   } }
Bean Validation Validation Groups, Constraint List publicclassZipCode {   @Pattern.List({     @Pattern(regexp = "[0-9]*"),     @Pattern(regexp = "[0-9*]*", groups = { Search.class })    }) @Size.List({     @Size(min=5, max=5),     @Size(min=1, max=5, groups = { Search.class })   })   @NotNull(groups = { Default.class, Search.class })   private String content; } publicclassValidatorHelper {   public static void validate(Class<?>[] groups, Object object) {   Set<ConstraintViolation<Object>> violations = validator.validate(object, groups);    // …   } }
Bean Validation JSF 2 Zero-configStandardvalidator JPA Mittels JPA Hooks Spezielle Graph-Traverser
Domänenspezifische Datentypen
Domänenspezifische Typen /**  * Domain specific type  */ publicclass Customer {   @Valid   @NotNull   private Name name;   @Enumerated(EnumType.STRING)   @NotNull   private Gender gender;   // … }
Domänenspezifische Typen /**  * Domain specific type  */ public class Customer {   // …   @Valid   @NotNull   private AddresspostalAddress;   @Valid   @NotNull   private AddressbillingAddress;   // … }
Domänenspezifische Typen public class Address {   private String addressLine1;   private String addressLine2;   @Valid   private ZipCode zip;   @Valid   private City city; }
Domänenspezifische Typen publicclassZipCode {   @Pattern.List({      @Pattern(regexp = "[0-9]*"),     @Pattern(regexp = "[0-9*]*",  groups = Search.class)    })   @Size(min=1, max=5)   @NotNull(groups = { Default.class, Search.class})   @Column(name = "zip")   private String content;   // … }
Domänenspezifische Typen Domänenspezifische  Datentypen Datencontainer mit Semantik Stellt schichtenübergreifende Typisierung sicher Konvertierung Konvertierung für UI (String <-> Object <-> String) Konvertierung für DB (O/R-Mapping)  Validierung Ist eng mit Format / Semantik der Daten verbunden Muss in die Frameworks integriert werden
Domänenspezifische  Typen UI Business Services O/R Mapping
Domänenspezifische  Typen Domänenspezifische  Typen mit JSR-303? Konvertierung Validierung
Spring MVC Integration
Spring MVC Property Editor Support (Konverter) publicclassZipCodeBinderextendsPropertyEditorSupport { public String getAsText() { return (getValue() == null ? "" : ((ZipCode) getValue()).getContent());   } publicvoidsetAsText(final String text) throwsIllegalArgumentException {     if (text != null && !text.isEmpty()) { ZipCodezipCode = new ZipCode(text); setValue(zipCode);     } else {       setValue(null);     }   } }
Spring MVC Controller @RequestMapping(value="/customer.html", method = RequestMethod.PUT) public String processForm(@Valid Customer customer, BindingResultresult, Map<String, Object> model) { if (result.hasErrors()) {     // […] return "form";   }   Customer savedCustomer = customerService.createCustomer(customer);   // […]   model.put("customer", savedCustomer); return "form"; }
Spring MVC View <form:formmethod="put" action="customer.html" commandName="customer">   <fieldset>     <form:labelpath="postalAddress.zip„>PLZ:</form:label>     <form:inputpath="postalAddress.zip" />     <form:errorspath="postalAddress.zip" />     <input type="submit" value="Create" />   </fieldset> </form:form>
JSF2 Integration
JSF Integration Apache MyFacesExtensionsValidator (ExtVal) Konverter @FacesConverter(forClass=ZipCode.class) publicclassZipcodeConverterimplementsConverter {   public Object getAsObject(FacesContextctx, UIComponent comp, String value) {     if (value == null)       return null; returnnewZipCode(value);   }   public String getAsString(FacesContextctx, UIComponent comp, Object value) { return ((ZipCode)value).getContent();   } }
JFace Data Binding Integration
JFace Data Binding Binding publicclassZipCodeCompositeextends Composite { publicvoidcreateBinding(DataBindingContextdbc) {     // […] UpdateValueStrategy t2m = newUpdateValueStrategy(); UpdateValueStrategy m2t = newUpdateValueStrategy(); m2t.setConverter(newZipCode2StringConverter());     t2m.setConverter(newString2ZipCodeConverter()); t2m.setAfterConvertValidator(newBeanValidator()); dbc.bindValue(observableTarget, observableModel, t2m, m2t);   } }
JFace Data Binding IConverter publicclass ZipCode2StringConverter implementsIConverter { publicObjectgetToType() { returnString.class;   } publicObjectgetFromType() { returnZipCode.class;   } publicObjectconvert(ObjectfromObject) { ZipCodezipCode = (ZipCode) fromObject; if (zipCode == null) { return null;     } returnzipCode.getContent();   } }
JFace Data Binding Validator public class BeanValidator implements IValidator {   private ValidatorFactoryfactory = Validation.buildDefaultValidatorFactory(); publicIStatusvalidate(Objectvalue) {     Set<ConstraintViolation<Object>> violations = factory.getValidator()      .validate(value, new Class<?>[] { Default.class }); if (violations.size() > 0) {       List<IStatus> statusList = new ArrayList<IStatus>(); for (ConstraintViolation<Object> cv : violations) {         statusList.add(ValidationStatus.error(cv.getMessage()));       } returnnewMultiStatus(Activator.PLUGIN_ID, IStatus.ERROR, statusList        .toArray(new IStatus[statusList.size()]), "Validation errors", null);     } returnValidationStatus.ok();   } }
Domänenspezifische  Typen Erkenntnisse Wiederverwendbarkeit Semantische Typsicherheit Validierungslogikkapselung Trennung der Verantwortlichkeit Spezielle Konverter Konverter verletzen die Kapselungsgrenzen
Service BoundaryValidation
Service Boundary Validation Motivation Richtlinien für Schnittstellendesign Design-by-Contract Transparenz für den Entwickler Grundprinzip Aspekt / Interceptor Validierung der Methodenparameter Validierung der Rückgabewerte
Service Boundary Validation JNDI / DI Resolver Bean Wrapper  Bean Interceptor Client Business Interface doBusiness validateParams doBusiness validateResult
Service Boundary Validation EJB Interceptor publicclassServiceValidationInterceptor {   @AroundInvoke publicObjectvalidate(InvocationContextcontext) throws Exception { Methodmethod = context.getMethod(); Object[] args = context.getParameters(); validateParams(args, method); Objectresult = context.proceed(); if (result != null) { validateResult(result);     } returnresult;   } }
Service Boundary Validation Spring Aspekt publicclassServiceValidationAspect { publicvoidvalidateArguments(JoinPointjoinPoint) { Object[] args = joinPoint.getArgs();     Method method = ((MethodSignature) joinPoint.getSignature()).getMethod(); …   } publicvoidvalidateResult(JoinPointjoinPoint, ObjectretVal) { … } } <beans[…] >   <beanid="bvAspect" class="….aspect.ServiceValidationAspect" />   <aop:config>     <aop:pointcutid="bv" expression="execution(* *.*(..))" />     <aop:aspectref="bvAspect">       <aop:beforepointcut-ref="bv" method="validateArguments" />       <aop:after-returning pointcut-ref="bv" returning="retVal"         method="validateResult" />     </aop:aspect>   </aop:config> </beans>
Service Boundary Validation Was ist mit Gruppenvalidierung? public interface Search extends ValidationGroup { } @Target({ ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) public @interfaceValidationAdvice {   Class<? extendsValidationGroup>[] value(); } publicinterfaceCustomerService { public List<Customer> getAllCustomers();   @ValidationAdvice(Search.class) public List<Customer> searchCustomer(CustomerSearchParameterscriteria); }
Service Boundary Validation Gruppenvalidierung publicvoidvalidateArguments(JoinPointjoinPoint) { Object[] args = joinPoint.getArgs();   Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();   Class<?> groups = new Class<?>[] { Default.class }; ValidationAdviceadvice = method.getAnnotation(ValidationAdvice.class); if (advice != null) { groups = advice.value();   } ValidatorHelper.requireValidated(groups, args); }
Offene Punkte
Offene Punkte und Ausblick JSR-303 Bean Validation ist praxistauglich! Sinnvolle Ergänzung: Apache MyFacesExtVal! Domänenspezifische Typen zur Kapselung! Was ist mit Konvertern? Conditional/Cross Validation mit EL? Was ist mit JAX-WS/JAX-B?
Vielen Dank! http://blog.holisticon.de/

More Related Content

Similar to Türsteher für Bohnen

No sql pour valtech tech days
No sql pour valtech tech daysNo sql pour valtech tech days
No sql pour valtech tech daysClaude Falguiere
 
ADO.NET Entity Framework 4
ADO.NET Entity Framework 4ADO.NET Entity Framework 4
ADO.NET Entity Framework 4Raffaele Fanizzi
 
CodeFest 2010. Желтов А. — Погружение в Internet Explorer 9 для разработчиков
CodeFest 2010. Желтов А. — Погружение в Internet Explorer 9 для разработчиковCodeFest 2010. Желтов А. — Погружение в Internet Explorer 9 для разработчиков
CodeFest 2010. Желтов А. — Погружение в Internet Explorer 9 для разработчиковCodeFest
 
Lecture exch2k7 21_10_08 - copy
Lecture exch2k7 21_10_08 - copyLecture exch2k7 21_10_08 - copy
Lecture exch2k7 21_10_08 - copyArcobaleno Nesta
 
Ria的强力后盾:rest+海量存储
Ria的强力后盾:rest+海量存储 Ria的强力后盾:rest+海量存储
Ria的强力后盾:rest+海量存储 zhen chen
 
N03 app engineseminar
N03 app engineseminarN03 app engineseminar
N03 app engineseminarSun-Jin Jang
 
Fatih BAZMAN CodeIgniter Sunumu
Fatih BAZMAN CodeIgniter SunumuFatih BAZMAN CodeIgniter Sunumu
Fatih BAZMAN CodeIgniter SunumuFatih Bazman
 
Entity+framework+
Entity+framework+Entity+framework+
Entity+framework+Rey zhang
 
Boostのあるプログラミング生活
Boostのあるプログラミング生活Boostのあるプログラミング生活
Boostのあるプログラミング生活Akira Takahashi
 
详细介绍什么是Java虚拟机(JVM)介绍资料
详细介绍什么是Java虚拟机(JVM)介绍资料详细介绍什么是Java虚拟机(JVM)介绍资料
详细介绍什么是Java虚拟机(JVM)介绍资料wensheng wei
 
Aspetos gerais de desenvolvimento web.
Aspetos gerais de desenvolvimento web.Aspetos gerais de desenvolvimento web.
Aspetos gerais de desenvolvimento web.Corcioli
 
C++模板与泛型编程
C++模板与泛型编程C++模板与泛型编程
C++模板与泛型编程deer hope
 
C++模板与泛型编程
C++模板与泛型编程C++模板与泛型编程
C++模板与泛型编程deer hope
 
IIS7.5概要 10月17日
IIS7.5概要 10月17日IIS7.5概要 10月17日
IIS7.5概要 10月17日hirookun
 
第2章 asp
第2章  asp第2章  asp
第2章 aspbillao
 
Software Testing und Qualitätssicherung
Software Testing und QualitätssicherungSoftware Testing und Qualitätssicherung
Software Testing und QualitätssicherungChristian Baranowski
 
Mini charla jquery
Mini charla jqueryMini charla jquery
Mini charla jquerylizardoceliz
 

Similar to Türsteher für Bohnen (20)

No sql pour valtech tech days
No sql pour valtech tech daysNo sql pour valtech tech days
No sql pour valtech tech days
 
ADO.NET Entity Framework 4
ADO.NET Entity Framework 4ADO.NET Entity Framework 4
ADO.NET Entity Framework 4
 
CodeFest 2010. Желтов А. — Погружение в Internet Explorer 9 для разработчиков
CodeFest 2010. Желтов А. — Погружение в Internet Explorer 9 для разработчиковCodeFest 2010. Желтов А. — Погружение в Internet Explorer 9 для разработчиков
CodeFest 2010. Желтов А. — Погружение в Internet Explorer 9 для разработчиков
 
MS Swit 2010
MS Swit 2010MS Swit 2010
MS Swit 2010
 
Lecture exch2k7 21_10_08 - copy
Lecture exch2k7 21_10_08 - copyLecture exch2k7 21_10_08 - copy
Lecture exch2k7 21_10_08 - copy
 
Ria的强力后盾:rest+海量存储
Ria的强力后盾:rest+海量存储 Ria的强力后盾:rest+海量存储
Ria的强力后盾:rest+海量存储
 
N03 app engineseminar
N03 app engineseminarN03 app engineseminar
N03 app engineseminar
 
Fatih BAZMAN CodeIgniter Sunumu
Fatih BAZMAN CodeIgniter SunumuFatih BAZMAN CodeIgniter Sunumu
Fatih BAZMAN CodeIgniter Sunumu
 
Entity+framework+
Entity+framework+Entity+framework+
Entity+framework+
 
Boostのあるプログラミング生活
Boostのあるプログラミング生活Boostのあるプログラミング生活
Boostのあるプログラミング生活
 
详细介绍什么是Java虚拟机(JVM)介绍资料
详细介绍什么是Java虚拟机(JVM)介绍资料详细介绍什么是Java虚拟机(JVM)介绍资料
详细介绍什么是Java虚拟机(JVM)介绍资料
 
Aspetos gerais de desenvolvimento web.
Aspetos gerais de desenvolvimento web.Aspetos gerais de desenvolvimento web.
Aspetos gerais de desenvolvimento web.
 
C++模板与泛型编程
C++模板与泛型编程C++模板与泛型编程
C++模板与泛型编程
 
BDD no mundo real
BDD no mundo realBDD no mundo real
BDD no mundo real
 
FISL11 2010 - Automação de Datacenters
FISL11 2010 - Automação de DatacentersFISL11 2010 - Automação de Datacenters
FISL11 2010 - Automação de Datacenters
 
C++模板与泛型编程
C++模板与泛型编程C++模板与泛型编程
C++模板与泛型编程
 
IIS7.5概要 10月17日
IIS7.5概要 10月17日IIS7.5概要 10月17日
IIS7.5概要 10月17日
 
第2章 asp
第2章  asp第2章  asp
第2章 asp
 
Software Testing und Qualitätssicherung
Software Testing und QualitätssicherungSoftware Testing und Qualitätssicherung
Software Testing und Qualitätssicherung
 
Mini charla jquery
Mini charla jqueryMini charla jquery
Mini charla jquery
 

Türsteher für Bohnen

  • 1. Bean Validation mit JSR-303 Türsteher für Bohnen
  • 2. Holisticon wurde 2006 gegründet. Das operative Geschäft inDeutschland haben wir zum1. Januar 2007 aufgenommen. Wir sind in Großraum Hamburg vertreten. Wir beraten Konzerne und große mittelständische Unternehmen. Wir sind ein Tochterunternehmen der schwedischenSigma-Gruppe. Holisticon AG
  • 4. Validierung in üblichen Architekturen JSR-303 Domänenspezifische Datentypen Integration Spring MVC JSF2 JFace Data Binding Service Boundary Validation Offene Punkte Agenda
  • 5. Validierung in üblichen Architekturen
  • 6. Spezifika der Java Enterprise Welt Mehrschicht-Architekturen Bit-Schieber-Anwendung Benutzer-Interaktion Verwendung von (vielen verschiedenen) Frameworks Validierung Eingabevalidierung Design-by-Contract Daten-Integrität Übliche Architekturen
  • 7. EJB + JSF 2 EJB + RCP Spring + JSF 2 Spring MVC Übliche Architekturen SWT View JSF Page JFace JSF Controller Controller Integration Serviceschnittstellen Session Beans Spring Beans
  • 8. Übliche Architekturen Spring MVC Lifecycle Dispatcher Servlet Controller Submitted Form Data Binding Validation Model and View Business Logic View Resolver
  • 9. Übliche Architekturen Spring MVC Validatoren (klassischer Ansatz) publicclassZipCodeValidatorimplementsValidator{ booleansupports(Class clazz) { returnString.class.equals(clazz); } publicvoidvalidate(Objectobj, Errors e) { ValidationUtils.rejectIfEmpty(e, “zipcode", “zipcode.empty"); String z = (String) obj; if (z.length() != 5) { e.rejectValue(”zipcode", ”wrong.size”); } } }
  • 10. Übliche Architekturen JSF2 Validatoren (klassischer Ansatz) Restore View Apply Request Values Process Validation Update Model Values Invoke Application Render Response
  • 11. Übliche Architekturen JSF2 Validatoren (klassischer Ansatz) <h:inputText … > <f:validator validatorId="ZipcodeValidator" /> </h:inputText> @FacesValidator("ZipcodeValidator") publicclassZipcodeValidatorimplementsValidator{ publicvoidvalidate(FacesContextctx, UIComponentcomponent, Objectvalue) throwsValidatorException { if (valueinstanceof String) { String zipCode = (String) value; if (zipCode == null || zipCode.length() != 5) { thrownewValidatorException(newFacesMessage( FacesMessage.SEVERITY_ERROR, "Wrongzip code", null)); } } } }
  • 12. Übliche Architekturen JFace Data Binding Validatoren (klassischer Ansatz) Observable SWT/JFace Listeners Target Konverter Model-to-Target Update Strategy Target-to-Model Update Strategy Observable Validatoren Validatoren Model Konverter Property Change Listeners
  • 13. Übliche Architekturen JFace Data Binding Validatoren (klassischer Ansatz) publicclassZipCodeCompositeextends Composite { publicvoidcreateBinding(DataBindingContextdbc) { IObservableValueobservableModel = … ISWTObservableValueobservableTarget = … UpdateValueStrategy t2m = newUpdateValueStrategy(); t2m.setAfterConvertValidator(newIValidator() { publicIStatusvalidate(Objectvalue) { if ((String) value == null || ((String) value).length() != 5) { return ValidationStatus.error("Wrong zip code"); } returnValidationStatus.ok(); } }); dbc.bindValue(observableTarget, observableModel, t2m, null); } }
  • 14. Übliche Architekturen JPA Hooks (klassischer Ansatz) @ExcludeDefaultInterceptors publicclassPersistenceValidationListener { @PrePersist @PreUpdate @PreRemove publicvoidvalidateMe(Objectentity) { if (entityinstanceofAddress) { String zipCode = ((Address)entity).getZip(); if (zipCode == null || zipCode.length() != 5) { throw new IllegalArgumentException(“Wrong zip code”); } } } }
  • 15. Übliche Architekturen Validatoren für jedes Framework Schicht System Validierung als Teil der Anwendungslogik
  • 17. Bean Validation Idee Validierungsregeln an Daten Offener JCP Standard (JSR-303) Open Source Implementierung Definiert eine Menge von Constraints Definiert eine Validation API Standard Constraint Annotationen publicclassZipCode { @Pattern(regexp = "[0-9]*") @Size(min=5, max=5) private String content; } @AssertFalse @AssertTrue @DecimalMin(value) @DecimalMax(calue) @Digits(integer, fraction) @Future @Min(value) @Max(value) @NotNull @Null @Past @Pattern(regexp, flags) @Size(min, max) @Valid
  • 19. Bean Validation Validation API publicclassValidatorHelper { private staticValidatorvalidator = Validation.buildDefaultValidatorFactory().getValidator(); public static void validate(Object object) { Set<ConstraintViolation<Object>> violations = validator.validate(object); if (violations.size() > 0) { StringBufferbuf = new StringBuffer("constraint violations in") .append(object.getClass().getName() + "["); for (ConstraintViolation<Object> violation : violations) { buf.append("quot;").append(violation.getPropertyPath()).append("quot; ") .append(violation.getMessage()).append(" (invalidValue=quot;") .append(violation.getInvalidValue()).append("quot;); "); } thrownewIllegalArgumentException(buf.append("]").toString()); } } }
  • 20. Bean Validation Eigene Constraints @Target({ FIELD, PARAMETER }) @Retention(RUNTIME) @Constraint(validatedBy = {}) @Pattern(regexp = "[0-9]*") @Size(min = 5, max = 5) @ReportAsSingleViolation public @interfaceZipcodeConstraint { String message() default "{Zipcode.invalid_zipcode}"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
  • 21. Bean Validation Eigener Validator @Constraint(validatedBy = {ZipcodeValidator.class}) public @interfaceZipcodeConstraint { // … } publicclassZipcodeValidatorimplements ConstraintValidator<ZipcodeConstraint, String> { private String message; publicvoidinitialize(ZipcodeConstraintannotation) { message = annotation.message(); } public booleanisValid(String value, ConstraintValidatorContextctx) { if (value.length() != 5) { ctx.buildConstraintViolationWithTemplate(message) .addConstraintViolation(); returnfalse; } returntrue; } }
  • 22. Bean Validation Validation Groups, Constraint List publicclassZipCode { @Pattern.List({ @Pattern(regexp = "[0-9]*"), @Pattern(regexp = "[0-9*]*", groups = { Search.class }) }) @Size.List({ @Size(min=5, max=5), @Size(min=1, max=5, groups = { Search.class }) }) @NotNull(groups = { Default.class, Search.class }) private String content; } publicclassValidatorHelper { public static void validate(Class<?>[] groups, Object object) { Set<ConstraintViolation<Object>> violations = validator.validate(object, groups); // … } }
  • 23. Bean Validation JSF 2 Zero-configStandardvalidator JPA Mittels JPA Hooks Spezielle Graph-Traverser
  • 25. Domänenspezifische Typen /** * Domain specific type */ publicclass Customer { @Valid @NotNull private Name name; @Enumerated(EnumType.STRING) @NotNull private Gender gender; // … }
  • 26. Domänenspezifische Typen /** * Domain specific type */ public class Customer { // … @Valid @NotNull private AddresspostalAddress; @Valid @NotNull private AddressbillingAddress; // … }
  • 27. Domänenspezifische Typen public class Address { private String addressLine1; private String addressLine2; @Valid private ZipCode zip; @Valid private City city; }
  • 28. Domänenspezifische Typen publicclassZipCode { @Pattern.List({ @Pattern(regexp = "[0-9]*"), @Pattern(regexp = "[0-9*]*", groups = Search.class) }) @Size(min=1, max=5) @NotNull(groups = { Default.class, Search.class}) @Column(name = "zip") private String content; // … }
  • 29. Domänenspezifische Typen Domänenspezifische Datentypen Datencontainer mit Semantik Stellt schichtenübergreifende Typisierung sicher Konvertierung Konvertierung für UI (String <-> Object <-> String) Konvertierung für DB (O/R-Mapping) Validierung Ist eng mit Format / Semantik der Daten verbunden Muss in die Frameworks integriert werden
  • 30. Domänenspezifische Typen UI Business Services O/R Mapping
  • 31. Domänenspezifische Typen Domänenspezifische Typen mit JSR-303? Konvertierung Validierung
  • 33. Spring MVC Property Editor Support (Konverter) publicclassZipCodeBinderextendsPropertyEditorSupport { public String getAsText() { return (getValue() == null ? "" : ((ZipCode) getValue()).getContent()); } publicvoidsetAsText(final String text) throwsIllegalArgumentException { if (text != null && !text.isEmpty()) { ZipCodezipCode = new ZipCode(text); setValue(zipCode); } else { setValue(null); } } }
  • 34. Spring MVC Controller @RequestMapping(value="/customer.html", method = RequestMethod.PUT) public String processForm(@Valid Customer customer, BindingResultresult, Map<String, Object> model) { if (result.hasErrors()) { // […] return "form"; } Customer savedCustomer = customerService.createCustomer(customer); // […] model.put("customer", savedCustomer); return "form"; }
  • 35. Spring MVC View <form:formmethod="put" action="customer.html" commandName="customer"> <fieldset> <form:labelpath="postalAddress.zip„>PLZ:</form:label> <form:inputpath="postalAddress.zip" /> <form:errorspath="postalAddress.zip" /> <input type="submit" value="Create" /> </fieldset> </form:form>
  • 37. JSF Integration Apache MyFacesExtensionsValidator (ExtVal) Konverter @FacesConverter(forClass=ZipCode.class) publicclassZipcodeConverterimplementsConverter { public Object getAsObject(FacesContextctx, UIComponent comp, String value) { if (value == null) return null; returnnewZipCode(value); } public String getAsString(FacesContextctx, UIComponent comp, Object value) { return ((ZipCode)value).getContent(); } }
  • 38. JFace Data Binding Integration
  • 39. JFace Data Binding Binding publicclassZipCodeCompositeextends Composite { publicvoidcreateBinding(DataBindingContextdbc) { // […] UpdateValueStrategy t2m = newUpdateValueStrategy(); UpdateValueStrategy m2t = newUpdateValueStrategy(); m2t.setConverter(newZipCode2StringConverter()); t2m.setConverter(newString2ZipCodeConverter()); t2m.setAfterConvertValidator(newBeanValidator()); dbc.bindValue(observableTarget, observableModel, t2m, m2t); } }
  • 40. JFace Data Binding IConverter publicclass ZipCode2StringConverter implementsIConverter { publicObjectgetToType() { returnString.class; } publicObjectgetFromType() { returnZipCode.class; } publicObjectconvert(ObjectfromObject) { ZipCodezipCode = (ZipCode) fromObject; if (zipCode == null) { return null; } returnzipCode.getContent(); } }
  • 41. JFace Data Binding Validator public class BeanValidator implements IValidator { private ValidatorFactoryfactory = Validation.buildDefaultValidatorFactory(); publicIStatusvalidate(Objectvalue) { Set<ConstraintViolation<Object>> violations = factory.getValidator() .validate(value, new Class<?>[] { Default.class }); if (violations.size() > 0) { List<IStatus> statusList = new ArrayList<IStatus>(); for (ConstraintViolation<Object> cv : violations) { statusList.add(ValidationStatus.error(cv.getMessage())); } returnnewMultiStatus(Activator.PLUGIN_ID, IStatus.ERROR, statusList .toArray(new IStatus[statusList.size()]), "Validation errors", null); } returnValidationStatus.ok(); } }
  • 42. Domänenspezifische Typen Erkenntnisse Wiederverwendbarkeit Semantische Typsicherheit Validierungslogikkapselung Trennung der Verantwortlichkeit Spezielle Konverter Konverter verletzen die Kapselungsgrenzen
  • 44. Service Boundary Validation Motivation Richtlinien für Schnittstellendesign Design-by-Contract Transparenz für den Entwickler Grundprinzip Aspekt / Interceptor Validierung der Methodenparameter Validierung der Rückgabewerte
  • 45. Service Boundary Validation JNDI / DI Resolver Bean Wrapper Bean Interceptor Client Business Interface doBusiness validateParams doBusiness validateResult
  • 46. Service Boundary Validation EJB Interceptor publicclassServiceValidationInterceptor { @AroundInvoke publicObjectvalidate(InvocationContextcontext) throws Exception { Methodmethod = context.getMethod(); Object[] args = context.getParameters(); validateParams(args, method); Objectresult = context.proceed(); if (result != null) { validateResult(result); } returnresult; } }
  • 47. Service Boundary Validation Spring Aspekt publicclassServiceValidationAspect { publicvoidvalidateArguments(JoinPointjoinPoint) { Object[] args = joinPoint.getArgs(); Method method = ((MethodSignature) joinPoint.getSignature()).getMethod(); … } publicvoidvalidateResult(JoinPointjoinPoint, ObjectretVal) { … } } <beans[…] > <beanid="bvAspect" class="….aspect.ServiceValidationAspect" /> <aop:config> <aop:pointcutid="bv" expression="execution(* *.*(..))" /> <aop:aspectref="bvAspect"> <aop:beforepointcut-ref="bv" method="validateArguments" /> <aop:after-returning pointcut-ref="bv" returning="retVal" method="validateResult" /> </aop:aspect> </aop:config> </beans>
  • 48. Service Boundary Validation Was ist mit Gruppenvalidierung? public interface Search extends ValidationGroup { } @Target({ ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) public @interfaceValidationAdvice { Class<? extendsValidationGroup>[] value(); } publicinterfaceCustomerService { public List<Customer> getAllCustomers(); @ValidationAdvice(Search.class) public List<Customer> searchCustomer(CustomerSearchParameterscriteria); }
  • 49. Service Boundary Validation Gruppenvalidierung publicvoidvalidateArguments(JoinPointjoinPoint) { Object[] args = joinPoint.getArgs(); Method method = ((MethodSignature) joinPoint.getSignature()).getMethod(); Class<?> groups = new Class<?>[] { Default.class }; ValidationAdviceadvice = method.getAnnotation(ValidationAdvice.class); if (advice != null) { groups = advice.value(); } ValidatorHelper.requireValidated(groups, args); }
  • 51. Offene Punkte und Ausblick JSR-303 Bean Validation ist praxistauglich! Sinnvolle Ergänzung: Apache MyFacesExtVal! Domänenspezifische Typen zur Kapselung! Was ist mit Konvertern? Conditional/Cross Validation mit EL? Was ist mit JAX-WS/JAX-B?
  • 52.