SlideShare ist ein Scribd-Unternehmen logo
1 von 54
Bean Validation mit JSR-303
Türsteher für Bohnen
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 2
Holisticon wurde 2006 gegründet.
Das operative Geschäft in
Deutschland haben wir zum
1. Januar 2007 aufgenommen.
Wir sind in Großraum Hamburg vertreten.
Wir beraten Konzerne und große mittelständische Unternehmen.
Wir sind ein Tochterunternehmen der schwedischen
Sigma-Gruppe.
Holisticon AG
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 3
Unser Weltbild
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 4
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
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 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
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 7
EJB +
JSF 2
EJB +
RCP
Spring +
JSF 2Spring MVC
Übliche Architekturen
SWT
JFace
View JSF Page
JSF ControllerController Integration
Serviceschnittstellen
Session BeansSpring Beans
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 8
Übliche Architekturen
Spring MVC Lifecycle Controller
Submitted Form
Data Binding
Validation
Business Logic
View Resolver
Model and View
Dispatcher
Servlet
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 9
Übliche Architekturen
Spring MVC Validatoren (klassischer Ansatz)
public class ZipCodeValidator implements Validator {
boolean supports(Class clazz) {
return String.class.equals(clazz);
}
public void validate(Object obj, Errors e) {
ValidationUtils.rejectIfEmpty(e, “zipcode", “zipcode.empty");
String z = (String) obj;
if (z.length() != 5) {
e.rejectValue(”zipcode", ”wrong.size”);
}
}
}
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 10
Übliche Architekturen
JSF2 Validatoren (klassischer Ansatz)
Restore
View
Apply
Request
Values
Process
Validation
Update
Model
Values
Invoke
Application
Render
Response
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 11
Übliche Architekturen
JSF2 Validatoren (klassischer Ansatz)
<h:inputText … >
<f:validator validatorId="ZipcodeValidator" />
</h:inputText>
@FacesValidator("ZipcodeValidator")
public class ZipcodeValidator implements Validator {
public void validate(FacesContext ctx, UIComponent component,
Object value) throws ValidatorException {
if (value instanceof String) {
String zipCode = (String) value;
if (zipCode == null || zipCode.length() != 5) {
throw new ValidatorException(new FacesMessage(
FacesMessage.SEVERITY_ERROR, "Wrong zip code", null));
}
}
}
}
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 12
Übliche Architekturen
JFace Data Binding Validatoren (klassischer Ansatz)
SWT/JFace
Listeners
Property Change
Listeners
Model-to-Target
Update Strategy
Target-to-Model
Update Strategy
Observable
Target
Observable
Model ValidatorenValidatoren
Konverter
Konverter
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 13
Übliche Architekturen
JFace Data Binding Validatoren (klassischer Ansatz)
public class ZipCodeComposite extends Composite {
public void createBinding(DataBindingContext dbc) {
IObservableValue observableModel = …
ISWTObservableValue observableTarget = …
UpdateValueStrategy t2m = new UpdateValueStrategy();
t2m.setAfterConvertValidator(new IValidator() {
public IStatus validate(Object value) {
if ((String) value == null || ((String) value).length() != 5) {
return ValidationStatus.error("Wrong zip code");
}
return ValidationStatus.ok();
}
});
dbc.bindValue(observableTarget, observableModel, t2m, null);
}
}
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 14
Übliche Architekturen
JPA Hooks (klassischer Ansatz)
@ExcludeDefaultInterceptors
public class PersistenceValidationListener {
@PrePersist
@PreUpdate
@PreRemove
public void validateMe(Object entity) {
if (entity instanceof Address) {
String zipCode = ((Address)entity).getZip();
if (zipCode == null || zipCode.length() != 5) {
throw new IllegalArgumentException(“Wrong zip code”);
}
}
}
}
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 15
Übliche Architekturen
Validatoren für jedes
 Framework
 Schicht
 System
Validierung als Teil der Anwendungslogik
Bean Validation JSR-303
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 17
Bean Validation
Idee
 Validierungsregeln an Daten
 Offener JCP Standard (JSR-303), Teil von JEE6
 Open Source Implementierung
 Definiert eine Menge von Constraints
 Definiert eine Validation API
Standard Constraint Annotationen
public class ZipCode {
@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
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 18
Bean Validation
@Valid
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 19
Bean Validation
Validation API
public class ValidatorHelper {
private static Validator validator =
Validation.buildDefaultValidatorFactory().getValidator();
public static void validate(Object object) {
Set<ConstraintViolation<Object>> violations = validator.validate(object);
if (violations.size() > 0) {
StringBuffer buf = new StringBuffer("constraint violations in")
.append(object.getClass().getName() + "[");
for (ConstraintViolation<Object> violation : violations) {
buf.append(""").append(violation.getPropertyPath()).append("" ")
.append(violation.getMessage()).append(" (invalidValue="")
.append(violation.getInvalidValue()).append(""); ");
}
throw new IllegalArgumentException(buf.append("]").toString());
}
}
}
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 20
Bean Validation
Eigene Constraints
@Target({ FIELD, PARAMETER })
@Retention(RUNTIME)
@Constraint(validatedBy = {})
@Pattern(regexp = "[0-9]*")
@Size(min = 5, max = 5)
@ReportAsSingleViolation
public @interface ZipcodeConstraint {
String message() default "{Zipcode.invalid_zipcode}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 21
Bean Validation
Eigener Validator
@Constraint(validatedBy = {ZipcodeValidator.class})
public @interface ZipcodeConstraint {
// …
}
public class ZipcodeValidator implements
ConstraintValidator<ZipcodeConstraint, String> {
private String message;
public void initialize(ZipcodeConstraint annotation) {
message = annotation.message();
}
public boolean isValid(String value, ConstraintValidatorContext ctx) {
if (value.length() != 5) {
ctx.buildConstraintViolationWithTemplate(message)
.addConstraintViolation();
return false;
}
return true;
}
}
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 22
Bean Validation
Validation Groups, Constraint List
public class ZipCode {
@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;
}
public class ValidatorHelper {
public static void validate(Class<?>[] groups, Object object) {
Set<ConstraintViolation<Object>> violations =
validator.validate(object, groups);
// …
}
}
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 23
Bean Validation
JSF 2
 Zero-config Standardvalidator
JPA
 Mittels JPA Hooks
 Spezielle Graph-Traverser
Domänenspezifische Datentypen
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 25
Domänenspezifische Typen
/**
* Domain specific type
*/
public class Customer {
@Valid
@NotNull
private Name name;
@Enumerated(EnumType.STRING)
@NotNull
private Gender gender;
// …
}
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 26
Domänenspezifische Typen
/**
* Domain specific type
*/
public class Customer {
// …
@Valid
@NotNull
private Address postalAddress;
@Valid
@NotNull
private Address billingAddress;
// …
}
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 27
Domänenspezifische Typen
public class Address {
private String addressLine1;
private String addressLine2;
@Valid
private ZipCode zip;
@Valid
private City city;
}
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 28
Domänenspezifische Typen
public class ZipCode {
@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;
// …
}
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 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
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 30
Domänenspezifische Typen
UI
Business Services
O/R Mapping
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 31
Domänenspezifische Typen
Domänenspezifische Typen mit JSR-303?
 Konvertierung
 Validierung
Spring MVC Integration
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 33
Spring MVC
Property Editor Support (Konverter)
public class ZipCodeBinder extends PropertyEditorSupport {
public String getAsText() {
return (getValue() == null ? "" : ((ZipCode) getValue()).getContent());
}
public void setAsText(final String text) throws IllegalArgumentException {
if (text != null && !text.isEmpty()) {
ZipCode zipCode = new ZipCode(text);
setValue(zipCode);
} else {
setValue(null);
}
}
}
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 34
Spring MVC
Controller
@RequestMapping(value="/customer.html", method = RequestMethod.PUT)
public String processForm(@Valid Customer customer, BindingResult result,
Map<String, Object> model) {
if (result.hasErrors()) {
// […]
return "form";
}
Customer savedCustomer = customerService.createCustomer(customer);
// […]
model.put("customer", savedCustomer);
return "form";
}
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 35
Spring MVC
View
<form:form method="put" action="customer.html" commandName="customer">
<fieldset>
<form:label path="postalAddress.zip„>PLZ:</form:label>
<form:input path="postalAddress.zip" />
<form:errors path="postalAddress.zip" />
<input type="submit" value="Create" />
</fieldset>
</form:form>
JSF2 Integration
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 37
JSF Integration
Apache MyFaces Extensions Validator (ExtVal)
Konverter
@FacesConverter(forClass=ZipCode.class)
public class ZipcodeConverter implements Converter {
public Object getAsObject(FacesContext ctx, UIComponent comp, String value) {
if (value == null)
return null;
return new ZipCode(value);
}
public String getAsString(FacesContext ctx, UIComponent comp, Object value) {
return ((ZipCode)value).getContent();
}
}
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 38
JSF Integration
Demo
JFace Data Binding Integration
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 40
JFace Data Binding
Binding
public class ZipCodeComposite extends Composite {
public void createBinding(DataBindingContext dbc) {
// […]
UpdateValueStrategy t2m = new UpdateValueStrategy();
UpdateValueStrategy m2t = new UpdateValueStrategy();
m2t.setConverter(new ZipCode2StringConverter());
t2m.setConverter(new String2ZipCodeConverter());
t2m.setAfterConvertValidator(new BeanValidator());
dbc.bindValue(observableTarget, observableModel, t2m, m2t);
}
}
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 41
JFace Data Binding
IConverter
public class ZipCode2StringConverter implements IConverter {
public Object getToType() {
return String.class;
}
public Object getFromType() {
return ZipCode.class;
}
public Object convert(Object fromObject) {
ZipCode zipCode = (ZipCode) fromObject;
if (zipCode == null) {
return null;
}
return zipCode.getContent();
}
}
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 42
JFace Data Binding
Validator
public class BeanValidator implements IValidator {
private ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
public IStatus validate(Object value) {
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()));
}
return new MultiStatus(Activator.PLUGIN_ID, IStatus.ERROR, statusList
.toArray(new IStatus[statusList.size()]), "Validation errors", null);
}
return ValidationStatus.ok();
}
}
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 43
Domänenspezifische Typen
Erkenntnisse
 Wiederverwendbarkeit
 Semantische Typsicherheit
 Validierungslogikkapselung
 Trennung der Verantwortlichkeit
 Spezielle Konverter
 Konverter verletzen die Kapselungsgrenzen
Service Boundary Validation
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 45
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
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 46
Service Boundary Validation
JNDI /
DI Resolver
Bean
Wrapper
Bean
InterceptorClient
Business
Interface
doBusiness
doBusiness
validateParams
validateResult
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 47
Service Boundary Validation
EJB Interceptor
public class ServiceValidationInterceptor {
@AroundInvoke
public Object validate(InvocationContext context) throws Exception {
Method method = context.getMethod();
Object[] args = context.getParameters();
validateParams(args, method);
Object result = context.proceed();
if (result != null) {
validateResult(result);
}
return result;
}
}
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 48
Service Boundary Validation
Spring Aspekt
public class ServiceValidationAspect {
public void validateArguments(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
…
}
public void validateResult(JoinPoint joinPoint, Object retVal) { … }
}
<beans[…] >
<bean id="bvAspect" class="….aspect.ServiceValidationAspect" />
<aop:config>
<aop:pointcut id="bv" expression="execution(* *.*(..))" />
<aop:aspect ref="bvAspect">
<aop:before pointcut-ref="bv" method="validateArguments" />
<aop:after-returning pointcut-ref="bv" returning="retVal"
method="validateResult" />
</aop:aspect>
</aop:config>
</beans>
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 49
Service Boundary Validation
Was ist mit Gruppenvalidierung?
public interface Search extends ValidationGroup {
}
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidationAdvice {
Class<? extends ValidationGroup>[] value();
}
public interface CustomerService {
public List<Customer> getAllCustomers();
@ValidationAdvice(Search.class)
public List<Customer> searchCustomer(CustomerSearchParameters criteria);
}
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 50
Service Boundary Validation
Gruppenvalidierung
public void validateArguments(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
Class<?> groups = new Class<?>[] { Default.class };
ValidationAdvice advice = method.getAnnotation(ValidationAdvice.class);
if (advice != null) {
groups = advice.value();
}
ValidatorHelper.requireValidated(groups, args);
}
Offene Punkte
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 52
Offene Punkte und Ausblick
JSR-303 Bean Validation ist praxistauglich!
Sinnvolle Ergänzung: Apache MyFaces ExtVal!
Domänenspezifische Typen zur Kapselung!
Was ist mit Konvertern?
Conditional/Cross Validation mit EL?
Was ist mit JAX-WS/JAX-B?
Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 53
Vielen Dank!
http://blog.holisticon.de/

Weitere ähnliche Inhalte

Empfohlen

AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfAI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfmarketingartwork
 
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024Neil Kimberley
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)contently
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024Albert Qian
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsKurio // The Social Media Age(ncy)
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Search Engine Journal
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summarySpeakerHub
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next Tessa Mero
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentLily Ray
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best PracticesVit Horky
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project managementMindGenius
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...RachelPearson36
 
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Applitools
 
12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at WorkGetSmarter
 

Empfohlen (20)

AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfAI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
 
Skeleton Culture Code
Skeleton Culture CodeSkeleton Culture Code
Skeleton Culture Code
 
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie Insights
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search Intent
 
How to have difficult conversations
How to have difficult conversations How to have difficult conversations
How to have difficult conversations
 
Introduction to Data Science
Introduction to Data ScienceIntroduction to Data Science
Introduction to Data Science
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best Practices
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project management
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
 
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
 
12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work
 
ChatGPT webinar slides
ChatGPT webinar slidesChatGPT webinar slides
ChatGPT webinar slides
 
More than Just Lines on a Map: Best Practices for U.S Bike Routes
More than Just Lines on a Map: Best Practices for U.S Bike RoutesMore than Just Lines on a Map: Best Practices for U.S Bike Routes
More than Just Lines on a Map: Best Practices for U.S Bike Routes
 

Türsteher für Bohnen

  • 1. Bean Validation mit JSR-303 Türsteher für Bohnen
  • 2. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 2 Holisticon wurde 2006 gegründet. Das operative Geschäft in Deutschland haben wir zum 1. Januar 2007 aufgenommen. Wir sind in Großraum Hamburg vertreten. Wir beraten Konzerne und große mittelständische Unternehmen. Wir sind ein Tochterunternehmen der schwedischen Sigma-Gruppe. Holisticon AG
  • 3. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 3 Unser Weltbild
  • 4. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 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. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 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. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 7 EJB + JSF 2 EJB + RCP Spring + JSF 2Spring MVC Übliche Architekturen SWT JFace View JSF Page JSF ControllerController Integration Serviceschnittstellen Session BeansSpring Beans
  • 8. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 8 Übliche Architekturen Spring MVC Lifecycle Controller Submitted Form Data Binding Validation Business Logic View Resolver Model and View Dispatcher Servlet
  • 9. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 9 Übliche Architekturen Spring MVC Validatoren (klassischer Ansatz) public class ZipCodeValidator implements Validator { boolean supports(Class clazz) { return String.class.equals(clazz); } public void validate(Object obj, Errors e) { ValidationUtils.rejectIfEmpty(e, “zipcode", “zipcode.empty"); String z = (String) obj; if (z.length() != 5) { e.rejectValue(”zipcode", ”wrong.size”); } } }
  • 10. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 10 Übliche Architekturen JSF2 Validatoren (klassischer Ansatz) Restore View Apply Request Values Process Validation Update Model Values Invoke Application Render Response
  • 11. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 11 Übliche Architekturen JSF2 Validatoren (klassischer Ansatz) <h:inputText … > <f:validator validatorId="ZipcodeValidator" /> </h:inputText> @FacesValidator("ZipcodeValidator") public class ZipcodeValidator implements Validator { public void validate(FacesContext ctx, UIComponent component, Object value) throws ValidatorException { if (value instanceof String) { String zipCode = (String) value; if (zipCode == null || zipCode.length() != 5) { throw new ValidatorException(new FacesMessage( FacesMessage.SEVERITY_ERROR, "Wrong zip code", null)); } } } }
  • 12. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 12 Übliche Architekturen JFace Data Binding Validatoren (klassischer Ansatz) SWT/JFace Listeners Property Change Listeners Model-to-Target Update Strategy Target-to-Model Update Strategy Observable Target Observable Model ValidatorenValidatoren Konverter Konverter
  • 13. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 13 Übliche Architekturen JFace Data Binding Validatoren (klassischer Ansatz) public class ZipCodeComposite extends Composite { public void createBinding(DataBindingContext dbc) { IObservableValue observableModel = … ISWTObservableValue observableTarget = … UpdateValueStrategy t2m = new UpdateValueStrategy(); t2m.setAfterConvertValidator(new IValidator() { public IStatus validate(Object value) { if ((String) value == null || ((String) value).length() != 5) { return ValidationStatus.error("Wrong zip code"); } return ValidationStatus.ok(); } }); dbc.bindValue(observableTarget, observableModel, t2m, null); } }
  • 14. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 14 Übliche Architekturen JPA Hooks (klassischer Ansatz) @ExcludeDefaultInterceptors public class PersistenceValidationListener { @PrePersist @PreUpdate @PreRemove public void validateMe(Object entity) { if (entity instanceof Address) { String zipCode = ((Address)entity).getZip(); if (zipCode == null || zipCode.length() != 5) { throw new IllegalArgumentException(“Wrong zip code”); } } } }
  • 15. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 15 Übliche Architekturen Validatoren für jedes  Framework  Schicht  System Validierung als Teil der Anwendungslogik
  • 17. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 17 Bean Validation Idee  Validierungsregeln an Daten  Offener JCP Standard (JSR-303), Teil von JEE6  Open Source Implementierung  Definiert eine Menge von Constraints  Definiert eine Validation API Standard Constraint Annotationen public class ZipCode { @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
  • 18. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 18 Bean Validation @Valid
  • 19. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 19 Bean Validation Validation API public class ValidatorHelper { private static Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); public static void validate(Object object) { Set<ConstraintViolation<Object>> violations = validator.validate(object); if (violations.size() > 0) { StringBuffer buf = new StringBuffer("constraint violations in") .append(object.getClass().getName() + "["); for (ConstraintViolation<Object> violation : violations) { buf.append(""").append(violation.getPropertyPath()).append("" ") .append(violation.getMessage()).append(" (invalidValue="") .append(violation.getInvalidValue()).append(""); "); } throw new IllegalArgumentException(buf.append("]").toString()); } } }
  • 20. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 20 Bean Validation Eigene Constraints @Target({ FIELD, PARAMETER }) @Retention(RUNTIME) @Constraint(validatedBy = {}) @Pattern(regexp = "[0-9]*") @Size(min = 5, max = 5) @ReportAsSingleViolation public @interface ZipcodeConstraint { String message() default "{Zipcode.invalid_zipcode}"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
  • 21. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 21 Bean Validation Eigener Validator @Constraint(validatedBy = {ZipcodeValidator.class}) public @interface ZipcodeConstraint { // … } public class ZipcodeValidator implements ConstraintValidator<ZipcodeConstraint, String> { private String message; public void initialize(ZipcodeConstraint annotation) { message = annotation.message(); } public boolean isValid(String value, ConstraintValidatorContext ctx) { if (value.length() != 5) { ctx.buildConstraintViolationWithTemplate(message) .addConstraintViolation(); return false; } return true; } }
  • 22. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 22 Bean Validation Validation Groups, Constraint List public class ZipCode { @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; } public class ValidatorHelper { public static void validate(Class<?>[] groups, Object object) { Set<ConstraintViolation<Object>> violations = validator.validate(object, groups); // … } }
  • 23. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 23 Bean Validation JSF 2  Zero-config Standardvalidator JPA  Mittels JPA Hooks  Spezielle Graph-Traverser
  • 25. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 25 Domänenspezifische Typen /** * Domain specific type */ public class Customer { @Valid @NotNull private Name name; @Enumerated(EnumType.STRING) @NotNull private Gender gender; // … }
  • 26. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 26 Domänenspezifische Typen /** * Domain specific type */ public class Customer { // … @Valid @NotNull private Address postalAddress; @Valid @NotNull private Address billingAddress; // … }
  • 27. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 27 Domänenspezifische Typen public class Address { private String addressLine1; private String addressLine2; @Valid private ZipCode zip; @Valid private City city; }
  • 28. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 28 Domänenspezifische Typen public class ZipCode { @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. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 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. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 30 Domänenspezifische Typen UI Business Services O/R Mapping
  • 31. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 31 Domänenspezifische Typen Domänenspezifische Typen mit JSR-303?  Konvertierung  Validierung
  • 33. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 33 Spring MVC Property Editor Support (Konverter) public class ZipCodeBinder extends PropertyEditorSupport { public String getAsText() { return (getValue() == null ? "" : ((ZipCode) getValue()).getContent()); } public void setAsText(final String text) throws IllegalArgumentException { if (text != null && !text.isEmpty()) { ZipCode zipCode = new ZipCode(text); setValue(zipCode); } else { setValue(null); } } }
  • 34. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 34 Spring MVC Controller @RequestMapping(value="/customer.html", method = RequestMethod.PUT) public String processForm(@Valid Customer customer, BindingResult result, Map<String, Object> model) { if (result.hasErrors()) { // […] return "form"; } Customer savedCustomer = customerService.createCustomer(customer); // […] model.put("customer", savedCustomer); return "form"; }
  • 35. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 35 Spring MVC View <form:form method="put" action="customer.html" commandName="customer"> <fieldset> <form:label path="postalAddress.zip„>PLZ:</form:label> <form:input path="postalAddress.zip" /> <form:errors path="postalAddress.zip" /> <input type="submit" value="Create" /> </fieldset> </form:form>
  • 37. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 37 JSF Integration Apache MyFaces Extensions Validator (ExtVal) Konverter @FacesConverter(forClass=ZipCode.class) public class ZipcodeConverter implements Converter { public Object getAsObject(FacesContext ctx, UIComponent comp, String value) { if (value == null) return null; return new ZipCode(value); } public String getAsString(FacesContext ctx, UIComponent comp, Object value) { return ((ZipCode)value).getContent(); } }
  • 38. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 38 JSF Integration Demo
  • 39. JFace Data Binding Integration
  • 40. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 40 JFace Data Binding Binding public class ZipCodeComposite extends Composite { public void createBinding(DataBindingContext dbc) { // […] UpdateValueStrategy t2m = new UpdateValueStrategy(); UpdateValueStrategy m2t = new UpdateValueStrategy(); m2t.setConverter(new ZipCode2StringConverter()); t2m.setConverter(new String2ZipCodeConverter()); t2m.setAfterConvertValidator(new BeanValidator()); dbc.bindValue(observableTarget, observableModel, t2m, m2t); } }
  • 41. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 41 JFace Data Binding IConverter public class ZipCode2StringConverter implements IConverter { public Object getToType() { return String.class; } public Object getFromType() { return ZipCode.class; } public Object convert(Object fromObject) { ZipCode zipCode = (ZipCode) fromObject; if (zipCode == null) { return null; } return zipCode.getContent(); } }
  • 42. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 42 JFace Data Binding Validator public class BeanValidator implements IValidator { private ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); public IStatus validate(Object value) { 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())); } return new MultiStatus(Activator.PLUGIN_ID, IStatus.ERROR, statusList .toArray(new IStatus[statusList.size()]), "Validation errors", null); } return ValidationStatus.ok(); } }
  • 43. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 43 Domänenspezifische Typen Erkenntnisse  Wiederverwendbarkeit  Semantische Typsicherheit  Validierungslogikkapselung  Trennung der Verantwortlichkeit  Spezielle Konverter  Konverter verletzen die Kapselungsgrenzen
  • 45. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 45 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
  • 46. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 46 Service Boundary Validation JNDI / DI Resolver Bean Wrapper Bean InterceptorClient Business Interface doBusiness doBusiness validateParams validateResult
  • 47. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 47 Service Boundary Validation EJB Interceptor public class ServiceValidationInterceptor { @AroundInvoke public Object validate(InvocationContext context) throws Exception { Method method = context.getMethod(); Object[] args = context.getParameters(); validateParams(args, method); Object result = context.proceed(); if (result != null) { validateResult(result); } return result; } }
  • 48. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 48 Service Boundary Validation Spring Aspekt public class ServiceValidationAspect { public void validateArguments(JoinPoint joinPoint) { Object[] args = joinPoint.getArgs(); Method method = ((MethodSignature) joinPoint.getSignature()).getMethod(); … } public void validateResult(JoinPoint joinPoint, Object retVal) { … } } <beans[…] > <bean id="bvAspect" class="….aspect.ServiceValidationAspect" /> <aop:config> <aop:pointcut id="bv" expression="execution(* *.*(..))" /> <aop:aspect ref="bvAspect"> <aop:before pointcut-ref="bv" method="validateArguments" /> <aop:after-returning pointcut-ref="bv" returning="retVal" method="validateResult" /> </aop:aspect> </aop:config> </beans>
  • 49. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 49 Service Boundary Validation Was ist mit Gruppenvalidierung? public interface Search extends ValidationGroup { } @Target({ ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) public @interface ValidationAdvice { Class<? extends ValidationGroup>[] value(); } public interface CustomerService { public List<Customer> getAllCustomers(); @ValidationAdvice(Search.class) public List<Customer> searchCustomer(CustomerSearchParameters criteria); }
  • 50. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 50 Service Boundary Validation Gruppenvalidierung public void validateArguments(JoinPoint joinPoint) { Object[] args = joinPoint.getArgs(); Method method = ((MethodSignature) joinPoint.getSignature()).getMethod(); Class<?> groups = new Class<?>[] { Default.class }; ValidationAdvice advice = method.getAnnotation(ValidationAdvice.class); if (advice != null) { groups = advice.value(); } ValidatorHelper.requireValidated(groups, args); }
  • 52. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 52 Offene Punkte und Ausblick JSR-303 Bean Validation ist praxistauglich! Sinnvolle Ergänzung: Apache MyFaces ExtVal! Domänenspezifische Typen zur Kapselung! Was ist mit Konvertern? Conditional/Cross Validation mit EL? Was ist mit JAX-WS/JAX-B?
  • 53. Türsteher für Bohnen | Oliver Ochs, Simon Zambrovski | 53

Hinweis der Redaktion

  1. Übliche Architekturen Spring MVC Validatoren JSF Validatoren Jface Data Binding Validatoren Hibernate Hooks JSR-303 Grundidee Advanced : Custom, Compound, Groups Kontext JEE JPA : hooks JSF 2