Enkel validering av Java-objekter med OVal

OVal oppsummerer seg selv slik:

OVal is a pragmatic and extensible general purpose validation framework for any kind of Java objects (not only JavaBeans) and allows you:
* to easily validate objects on demand,
* to specify constraints for class fields and getter methods,
* to validate objects based on certain EJB3 JPA annotations (namely all field annotations that require a not-null value),
* to configure constraints via annotations, POJOs and/or simple XML files,
* to express constraints using scripting languages such as Groovy, BeanShell, and JavaScript
* to easily create custom constraints, and
* to develop new constraint configuration mechanisms

When using AspectJ certain programming by contract (aka Design By Contract™ or DBC) features are available:
* specifying constraints for constructor parameters that are automatically checked when a constructor is called (preconditions),
* specifying constraints for method parameters that are automatically checked when a method is called (preconditions),
* requiring a certain object state before a method is called (preconditions)
* enforcing object validation after an object has been created (invariants),
* enforcing object validation before/after a method of an object is/has been called (invariants),
* specifying constrains for a method’s return value that are automatically checked after a method has been executed (postconditions),
* requiring a certain object state after a method is called (postconditions).

Vi har tatt ibruk dette rammeverket med stor suksess på prosjekt. Vi har holdt oss unna AspectJ-delen fordi det krever weaving av koden (hvilket gjør utviklingsprosessen tyngre), og fordi vi rett og slett ikke har behov for “Gaurd Clauses”.

Eksemplet nedenfor viser hvordan enkel validering av Java objekter kan gjøres hvor som helst når som helst.

public class User {
	@NotNull
	@NotEmpty
	@Length(max=32, errorCode="A10")
	String username;
 
	@Range(min = 1, max = 10, errorCode="A11")
	int luckyNumber;
 
	@AssertValid
	Name name;
 
	public User(String username, int luckyNumber, Name name) {
		this.username = username;
		this.luckyNumber = luckyNumber;
		this.name = name;
	}
}
public class Name {
 
	@NotNull
	@NotEmpty
	String firstname;
 
	@NotNull
	@NotEmpty
	String lastname;
 
	public Name(String firstname, String lastname) {
		this.firstname = firstname;
		this.lastname = lastname;
	}
}
public class UserRegistrationService {
	public String register(User user) {
		List<ConstraintViolation> violations = new Validator().validate(user);
		if (!violations.isEmpty()) {
			StringBuilder output = new StringBuilder("Registration failed due to these errors:\n");
			for (ConstraintViolation violation : violations) {
				output.append("\t").append(violation.getErrorCode()).append(" - ");
				output.append(violation.getMessage()).append("\n");
			}
			return output.toString();
		}
		return "Success!";
	}
	public static void main(String[] args) {
		User user = new User("johnsmith", 71, new Name("John", null));
 
		String registrationOutput = new UserRegistrationService().register(user);
 
		System.out.println(registrationOutput);
	}
}

Resultatet av denne kjøringen:

Registration failed due to these errors:
	A11 - com.example.business.User.luckyNumber is not in the range 1.0 through 10.0
	net.sf.oval.constraint.AssertValid - net.sf.oval.constraint.AssertValid.violated

Når som helst og hvor som helst kan du opprette et Validator-objekt. Dette objektet bruker du til å validere et objekt som er annotert opp med annoteringsregler. Resultatet er en liste med ConstrainViolations som du kan bruke til hva du vil. I dette eksemplet så har jeg bare skrevet ut feilkode og melding direkte i UserRegistrationService. På prosjektet vårt har vi dratt ut håndteringen i en egen klasse slik at vi loope igjennom alle ConstraintViolations og kaste fornuftige exceptions tilbake til klienten.

I eksemplet ovenfor får vi også en lite fornuftig melding fra Oval om at en @AssertValid-regel er blitt brutt. For at Oval skal validere en hel objektgraf må det settes på @AssertValid slik som er gjort på name-feltet i User-klassen. Dette resulterer i at en ConstraintViolation kan returnere en liste med årsaker til at valideringen feilet. I dette tilfellet feilet AssertValid pga @NotNull på lastname-feltet i Name-klassen. Dette må graves ut på en litt mer sofistikert måte, se ny og forbedret UserRegistrationService nedenfor:

public class UserRegistrationService {
 
	public String register(User user) {
		List<ConstraintViolation> violations = new Validator().validate(user);
		if (!violations.isEmpty()) {
			StringBuilder output = new StringBuilder("Registration failed due to these errors:\n");
			output.append(getAllCauses(violations));
			return output.toString();
		}
		return "Success!";
	}
 
	private String getAllCauses(List<ConstraintViolation> violations) {
		StringBuilder errorMessage = new StringBuilder();
		for (ConstraintViolation violation : violations) {
			if (errorMessage.length() != 0)
				errorMessage.append(",").append("\n");
			errorMessage.append(getCause(violation));
		}
		return errorMessage.toString();
	}
 
	private String getCause(ConstraintViolation violation) {
		if (violation.getCauses() == null) {
			return violation.getMessage();
		} else {
			StringBuilder errorMessage = new StringBuilder();
			for (ConstraintViolation v : violation.getCauses()) {
				if (errorMessage.length() != 0)
					errorMessage.append(",").append("\n");
				errorMessage.append(getCause(v));
			}
			return errorMessage.toString();
		}
	}
 
	public static void main(String[] args) {
		User user = new User("johnsmith", 71, new Name("John", null));
 
		String registrationOutput = new UserRegistrationService().register(user);
 
		System.out.println(registrationOutput);
	}
 
}

Resultatet av kjøringen:

Registration failed due to these errors:
com.example.business.User.luckyNumber is not in the range 1.0 through 10.0,
com.example.business.Name.lastname cannot be null

Det er verdt å merke seg at nåværende versjon av OVal ikke er kompatibel med JSR-303 (Bean Validation). Versjon 2 er planlagt å bli kompatibel. Hibernate Validator kan være verdt å ta en kikk på dersom du vil benytte JSR-303 annoteringene. Disse to rammeverkene tilbyr mye av det samme, men mitt inntrykk er allikevel at OVal er litt mer lettvekt enn Hibernate Validator.

Post a Comment

Your email is never shared. Required fields are marked *

*
*

Spam Protection by WP-SpamFree