Sunday, February 22, 2009

Bookmark and Share

JSR 303 ("Bean Validation") specifies a fairly simple to use bootstrapping API for the creation of Validator instances:

1
2
3
4
...
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
...

To make things even easier, one can use a dependency injection container such as the Spring container to obtain Validator instances. That way you don't have to cope with the ValidatorFactory in your application logic but rather can have Validator instances simply injected. All you have to do for that is to create a Spring application context, that defines two Spring beans validatorFactory and validator as follows:

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
<beans 
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

    <bean id="validatorFactory" class="javax.validation.Validation"
        factory-method="buildDefaultValidatorFactory" />

    <bean id="validator" factory-bean="validatorFactory"
        factory-method="getValidator" />
</beans>

The validationFactory bean is created by instructing Spring to call the static factory method Validation.buildDefaultValidatorFactory(). The validator bean in turn is created by letting Spring call the getValidator() method on the validatorFactory bean.

As we don't specify any scope for the two beans explicitely, the default scope Singleton will be used. That way the Spring container will instantiate both beans exactly once and return these instances whenever requested. We are fine with that, as the Bean Validation specification states, that ValidatorFactory and Validator implementations have to be thread-safe.

The validator bean can now be injected into any other bean we create. Using Spring's test framework we can have the comfort of dependency injection also in JUnit tests. Let's try it out by using an injected Validator to validate instances of the exemplary Car class from my introductory post on the Bean Validation API:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package de.gmorling.bvspring;

import static org.junit.Assert.*;

import java.util.Set;

import javax.annotation.Resource;
import javax.validation.ConstraintViolation;
import javax.validation.Validator;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class CarTest {

    @Resource
    private Validator validator;

    @Test
    public void licensePlateTooShort() {

        Car car = new Car("Morris", "D", 4);

        Set<ConstraintViolation<Car>> constraintViolations = validator
                .validate(car);

        assertEquals(1, constraintViolations.size());
        assertEquals("size must be between 2 and 14", constraintViolations
                .iterator().next().getInterpolatedMessage());
    }

    @Test
    public void carIsValid() {

        Car car = new Car("DD-AB-123");

        Set<ConstraintViolation<Car>> constraintViolations = validator
                .validate(car);

        assertEquals(0, constraintViolations.size());
    }

}

With JUnit's @RunWith annotation we declare that the test runner SpringJUnit4ClassRunner shall be used to run the test, which enables Spring to inject dependencies into the test class.

The @ContextConfiguration stems from Spring's test framework and specifies the application context to be used. As we don't provide any name, Spring will use the default name, which is de/gmorling/bvspring/CarTest-context.xml in our case.

The @Resource annotation finally is defined by JSR 250 ("Common Annotations for the JavaTM Platform") and is used here to signal, that the validator field shall be populated by Spring. Spring processes the @Resource annotation following by-name semantics, meaning that a Spring bean with the name "validator" will be searched in the application context and injected into the field.

One final word on a version incompatibility between Spring and JUnit I noticed. Using JUnit 4.5 gave me the following error:

1
java.lang.NoSuchMethodError: org.junit.runner.notification.RunNotifier.testAborted(Lorg/junit/runner/Description;Ljava/lang/Throwable;)

So be sure to use JUnit 4.4. A complete Maven project with this post's source files can be downloaded here.

Monday, February 16, 2009

Bookmark and Share

DBUnit is a great tool when it comes to the management of test data. When doing data driven unit tests, database tables have to be populated with predefined records, that can be accessed by the code under test. DBUnit allows the specification of such test records by the means of data set files as in the following example:

1
2
3
4
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
    <location num="6" addr="Webster Street" date="2009-02-16"/>
</dataset>

The data that can be inserted with such data set files is pretty static by nature. DBUnit offers a bit of dynamic with its ReplacementDataSet, which allows to replace custom-defined placeholders (e.g. [SYSDATE]) with other values (e.g. new Date()). But I am aware of no possibility to use more flexible expressions to insert things like "now - 14 days" into a date field for instance.

So I set out to create a data set implementation, that allows to use scripting language expressions in its fields. Using this ScriptableDataSet, data set files like the following can be loaded and processed:

1
2
3
4
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
    <location num="jruby:12/2" addr="jruby:'Webster Street'.reverse" date="jruby:DateTime::now() - 14"/>
</dataset>

Having the power of a scripting language at your hands any dynamic expressions thinkable can be used in the fields of a data set – also "now - 14 days" is no problem any more.

How does it work? The scriptable data set leverages the scripting language integration API as defined by JSR 223 ("Scripting for the Java Platform"). An implementation of that API is part of Java 6, but it generally can be used with previous Java versions as well.

To use a certain language in a data set, a JSR 223 compatible engine for that language has to be added to the class path. Loads of such engines can be found at the scripting project at java.net. Having added the engine for your preferred scripting language (or by using the Rhino engine for JavaScript, which is delivered with the Java 6 JDK), a ScriptableDataSet can be created as follows:

1
2
3
4
5
6
7
8
List<Class<? extends ScriptInvocationHandler>> handlers = 
    new ArrayList<Class<? extends ScriptInvocationHandler>>();
handlers.add(TestInvocationHandler.class);

IDataSet dataSet = 
    new ScriptableDataSet(
        new FlatXmlDataSet(new File("dataset.xml")),
        new ScriptableDataSetConfig("jruby", "jruby:", handlers));

Sporting DBUnit's decorator scheme, a ScriptableDataSet is created wrapping another IDataSet. Additionally a ScriptableDataSetConfig object has to be provided, that specifies

  • the name of the scripting language to be used as understood by the JSR 223 ScriptEngineManager ("jruby")
  • a prefix that shall precede all fields containing scripts in that language ("jruby:")
  • an optional list of ScriptInvocationHandlers, that can be used to pre-process (e.g. to add common imports) and post-process scripts (e.g. to convert results into data types understood by DBUnit)

So if you like the idea of the scriptable data set, don't hesistate and get your hands on it at its git repo over at github. Do you think, it is useful at all, and if so, which scenarios for its usage could you think of?

Saturday, February 14, 2009

Bookmark and Share

One of the features new to Java 6 is its built-in support for scripting languages (an introductory article on that topic can be found here), which enables you to load and execute programs written in a scripting language directly from within your Java program.

The scripting support in Java 6 is realized by providing an implementation of JSR 223 ("Scripting for the Java Platform"). While JavaScript is directly supported by the JDK itself, any other scripting language can be integrated as well by simply adding a JSR 223 compatible scripting engine to the class path.

The first place to look for JSR 223 scripting engines is https://scripting.dev.java.net/, where engines for Ruby, Python, Groovy and a lot of other languages can be found.

To give it a try, I wanted to include the JRuby engine into a Maven based project. That engine can be found in the Maven repo at java.net, but unfortunetaly its pom.xml is somewhat defective, as it contains the dependency script-api:javax.script, which neither exists in the java.net repository nor any other one I am aware of.

But when running on Java 6, it isn't required at all – as the scripting API is part of the JDK. So I excluded the dependency in the pom.xml of my own project, which reads as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<project 
    xmlns="http://maven.apache.org/POM/4.0.0" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>org.gm</groupId>
    <artifactId>jruby-scripting</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>jruby-scripting</name>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.5</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.jruby</groupId>
            <artifactId>jruby</artifactId>
            <version>1.1.6</version>
        </dependency>
        <dependency>
            <groupId>com.sun.script.jruby</groupId>
            <artifactId>jruby-engine</artifactId>
            <version>1.1.6</version>
            <exclusions>
                <exclusion>
                    <groupId>script-api</groupId>
                    <artifactId>javax.script</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <repositories>
        <repository>
            <id>maven2-repository.dev.java.net</id>
            <name>Java.net Repository for Maven 2</name>
            <url>http://download.java.net/maven/2/</url>
        </repository>
    </repositories>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>RELEASE</version>
                <configuration>
                    <source>1.5</source>
                    <target>1.5</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Having fixed that, we can try the scripting API by evaluating a simple Ruby script, that receives a variable provided by the hosting Java program and returns the obligatory String "Hello, jruby!" ;-):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package org.gm.jrubyscripting;

import static org.junit.Assert.*;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

import org.junit.Test;

public class HelloJRuby {

    @Test
    public void helloJRuby() throws Exception {

        String engineName = "jruby";

        ScriptEngineManager manager = new ScriptEngineManager();

        ScriptEngine jRubyEngine = manager.getEngineByName(engineName);
        assertNotNull(jRubyEngine);

        jRubyEngine.put("engine", engineName);

        assertEquals(
            "Hello, jruby!",
            jRubyEngine.eval("return 'Hello, ' + $engine + '!'"));
    }
}

But what to do, if you are not running on Java 6? After some more searching I finally managed to find the missing dependency in the repo of the Mule project, but with groupId and artifactId interchanged.

From there it can be added to the project, for example using a separate Maven build profile to be activated on JDK versions < 1.6:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
<profiles>
    <profile>
        <activation>
            <jdk>[1.3,1.6)</jdk>
        </activation> 
        <dependencies>
            <dependency>
                <groupId>javax.script</groupId>
                <artifactId>script-api</artifactId>
                <version>1.0</version>
            </dependency>
        </dependencies>
        <repositories>
            <repository>
                <id>dist.codehaus.org/mule</id>
                <name>Mule Repository for Maven 2</name>
                <url>http://dist.codehaus.org/mule/dependencies/maven2/</url>
            </repository>
        </repositories>
    </profile>
</profiles>
...

Thursday, February 12, 2009

Bookmark and Share

Today the first Alpha version of Hibernate Validator 4 was released. HV 4 is the reference implementation of JSR 303 (Bean Validation), about which I wrote recently here and here.

Why am I reporting on the Alpha release? Well, first of all it's a great step for JSR 303 – and I'm personally involved.

I contributed to the reference guide contained in the distribution. Right now the guide contains only a "Getting started" section, but I am planning to deliver more chapters within the next weeks. Any feedback would be highly appreciated.

Saturday, February 7, 2009

Bookmark and Share

I am so happy – this week my first article ever in a printed publication appeared. It's named "Web Services: Auf die Plätze, fertig, los!" and is published in edition 3/09 of the German-speaking journal Java Magazin.

The article (written by my colleague Ralf Ebert and me), is a tutorial, that shows how to get started with building contract-first web services using JAX-WS and Maven. We take the reader by the hand and show from scratch, how to create a new Maven project, how to write an XML schema for the service and how to run the web service using the Jetty plugin for Maven.

To make things easier, we don't make the reader write the WSDL for the service by hand, but rather provide a Maven plugin, that creates the WSDL from the XSD with the request/response types relying on some naming conventions. That greatly simplifies things (as writing WSDLs is pretty time-consuming and error prone), while keeping full control over the WSDL, being the big advantage of the contract-first approach for creating web services.

I will show in detail, how to make use of that plugin – named Maven WSDL builder plugin – in a subsequent post.

Having a basic examplary service up and running, the article continues by showing how to perform schema validation of web service requests and responses. Finally we explain how to implement cross-cutting concerns using handler chains.

So if you ever wanted to get started with contract-first web services the real simple way, don't hesistate and get your copy of Java Magazin now and let us know your opinion on the article.

Sunday, February 1, 2009

Bookmark and Share

In my last post I explained how to get the JSR 303 reference implementation up and running and showed some of the very basic features of the Bean Validation API.

Today I want to delve a bit more into the details of the JSR by showing how to create custom constraint annotations and how to make use of the concept of validation groups.

Creating custom constraint annotations

Though the JSR defines a whole bunch of standard constraint annotations such as @NotNull, @Size, @Min or @AssertTrue (btw. raise your voice in the JSR feedback forum, if you want to have more standard annotations included), there will always be validation requirements, for which these standard annotations won't suffice.

Being aware of that problem, the specification authors laid out the API in an expansible manner, that allows users to define their custom constraint annotations.

To try that out, lets create a constraint annotation, that ensures, that the licensePlate field of the Car entity from my previous post always contains an upper-case String. First we have to define the annotation itself. If you've never designed an annotation before, this may look a bit scary, but actually it's not very hard:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package org.gm.beanvalidation.constraints;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import javax.validation.Constraint;

import org.gm.beanvalidation.validators.UpperCaseValidator;

@Target( { METHOD, FIELD, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = UpperCaseValidator.class)
@Documented
public @interface UpperCase {

    String message() default "{validator.uppercase}";

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

}

An annotation type is defined using the @interface keyword. All attributes of an annotation type are declared in a method-like manner. The specification of the Bean Validation API demands, that any constraint annotation defines

  • an attribute "message" that returns the default key for creating error messages in case the constraint is violated
  • an attribute "groups" that allows the specification of validation groups, to which this constraint belongs (see section "Using validation groups" for further details). This must default to an empty array.

In addition we annotate the annotation type with a couple of so-called meta annotations:

  • @Target({ METHOD, FIELD, ANNOTATION_TYPE }): Says, that methods, fields and annotation declarations may be annotated with @UpperCase (but not type declarations e.g.)
  • @Retention(RUNTIME): Specifies, that annotations of this type will be available at runtime by the means of reflection
  • @Constraint(validatedBy = UpperCaseValidator.class): Specifies the validator to be used to validate elements annotated with @UpperCase
  • Documented: Says, that the use of @UpperCase will be contained in the JavaDoc of elements annotated with it

Implementing the constraint validator

Next, we need to implement a constraint validator, that is able to validate elements with the @UpperCase annotation. To do so, we implement the interface ConstraintValidator as shown below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package org.gm.beanvalidation.validators;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

import org.gm.beanvalidation.constraints.UpperCase;

public class UpperCaseValidator implements ConstraintValidator<UpperCase, String> {

    public void initialize(UpperCase constraintAnnotation) {
        //nothing to do
    }

    public boolean isValid(String object,
        ConstraintValidatorContext constraintContext) {

        if (object == null)
            return true;

        return object.equals(object.toUpperCase());
    }

}

The ConstraintValidator interface specifies two type parameters, which we set in our implementation. The first specifies the annotation type to be validated by a ConstraintValidator (in our example UpperCase), the second the type of elements, which the validator can handle (here String).

The implementation of the validator is straightforward. The initialize() method gives us access to any attributes of the annotation (such as the min/max fields in case of the Size annotatation), but as @UpperCase doesn't define any attributes, we have nothing to do here.

What's interesting for us, is the isValid() method. On line 20 we implement the logic, that determines, whether a given object is valid according to the @UpperCase annotation or not.

Note, that the specification recommends, that null values should be declared to be valid. If null is not a valid value for an element, it should be annotated with @NotNull explicitely. We could use the passed-in ConstraintValidatorContext to raise any custom validation errors, but as we are fine with the default behavior, we can ignore that parameter.

Specifying the error message

Finally we need to specify the error message, that shall be used, in case the @UpperCase constraint is violated. To do so, we create a file named ValidationMessages.properties under src/main/resources with the following content:

1
validator.uppercase=String must be upper-case.

If a validation error occurs, the validation runtime will use the default value, that we specified for the message attribute of the @UpperCase annotation to look up the error message in this file.

Now that our first custom constraint is completed, we can use it in the Car class to specify that the licensePlate field shall only contain upper case Strings:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package org.gm.beanvalidation;

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

import org.gm.beanvalidation.constraints.UpperCase;

public class Car {

    @NotNull
    private String manufacturer;

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

    public Car(String manufacturer, String licencePlate) {

        super();
        this.manufacturer = manufacturer;
        this.licensePlate = licencePlate;
    }

    //getters and setters ...

Let's demonstrate in a little test that the @UpperCase constraint is properly validated:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package org.gm.beanvalidation;

import static org.junit.Assert.*;

import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;

import org.junit.BeforeClass;
import org.junit.Test;

public class CarTest {

    private static Validator validator;

    @BeforeClass
    public static void setUp() {
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        validator = factory.getValidator();
    }

    @Test
    public void testLicensePlateNotUpperCase() {

        Car car = new Car("Audi", "dd-ab-123");

        Set<ConstraintViolation<Car>> constraintViolations =
            validator.validate(car);
        assertEquals(1, constraintViolations.size());
        assertEquals(
            "String must be upper-case.", 
            constraintViolations.iterator().next().getInterpolatedMessage());
    }

    @Test
    public void testCarIsValid() {

        Car car = new Car("Audi", "DD-AB-123");

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

Compound constraints

Looking at the license plate field of the Car class, we see three constraint annotations already. In complexer scenarios, where even more constraints could be applied to one element, this might become a bit confusing easily.

Furthermore, if we had a licensePlate field in another class, we would have to copy all constraint declarations to the other class as well, violating the DRY principle.

Luckily, the Bean Validation API comes to the rescue by allowing the creation of compound constraints. So let's compose a new constraint annotation @ValidLicensePlate, that comprises the constraints @NotNull, @Size and @UpperCase:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package org.gm.beanvalidation.constraints;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

import org.gm.beanvalidation.validators.ValidLicensePlateValidator;

@NotNull
@Size(min = 2, max = 14)
@UpperCase
@Target( { METHOD, FIELD, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = ValidLicensePlateValidator.class)
@Documented
public @interface ValidLicensePlate {

    String message() default "{validator.validlicenseplate}";

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

}

To do so, we just annotate the constraint declaration with its comprising constraints (btw. that's exactly why we allowed annotation types as target for the @UpperCase annotation). The validator class for @ValidLicensePlate doesn't add any additional validation logic, so we skip it for now.

Using the new compound constraint at the licensePlate field now is fully equivalent to the previous version, where we declared the three constraints directly at the field itself:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package org.gm.beanvalidation;

import org.gm.beanvalidation.constraints.ValidLicensePlate;

public class Car {

    // ...

    @ValidLicensePlate
    private String licensePlate;

    // ...

}

Using validation groups

Now, let's imagine another custom constraint annotation, @NotStolen, which could be annotated at the licensePlate field, too. Upon the validation of @NotStolen an external service shall be called, that checks whether a given license plate is stolen or not.

Calling this service would be rather expensive (in terms of processing time, but possibly also in terms of money, as we might be charged for each call by the service provider), so we are interested in calling the service not more than neccessary.

Especially we don't want the service to be called, if a Car instance is not even valid with respect to the @NotNull constraint at the manufacturer field or the @ValidLicensePlate constraint, e.g. due to an input form not completely filled.

To meet this requirement, we can leverage the concept of validation groups. Each constraint at the fields of an object to be validated can be part of one or more such validation group, and instead of validating all constraints at once, we validate one group after the other, stopping, if one group couldn't be validated successfully.

Validation groups are defined by creating simple marker interfaces. Let's see, how this might look in our Car class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package org.gm.beanvalidation;

import javax.validation.constraints.NotNull;

import org.gm.beanvalidation.constraints.NotStolen;
import org.gm.beanvalidation.constraints.ValidLicensePlate;

public class Car {

    @NotNull
    private String manufacturer;

    @ValidLicensePlate
    @NotStolen(groups = Extended.class)
    private String licensePlate;

    /**
     * Used as validation group.
     */
    public interface Extended {
    }

    // ...

}

The @NotStolen constraint is part of the Extended validation group. On the contrary, the @NotNull and the @ValidLicensePlate constraints belong to the Default group (which is specified by the JSR 303 API), as no special validation group is specified for them.

We can use the validation group concept now in a sample class, that checks the validity of a given Car instance:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package org.gm.beanvalidation;

import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import javax.validation.groups.Default;

import org.gm.beanvalidation.Car.Extended;

public class CarValidityChecker {

    public boolean checkCarValidity(Car car) {

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

        Set<ConstraintViolation<Car>> constraintViolations = validator
                .validate(car, Default.class);

        if (!constraintViolations.isEmpty())
            return false;

        constraintViolations = validator.validate(car, Extended.class);

        return constraintViolations.isEmpty();
    }

}

At first, only the constraints of the Default group are validated. If they are violated, processing stops here. Only if all constraints of the Default group are met, the Extended group is validated, issuing the expensive external service call required for the validation of the @NotStolen constraint.

Conclusion

That concludes my exploration of the JSR 303 (Bean Validation) API for now. I showed how to create custom constraint annotations, how to make use of constraint composition and finally how to leverage the concept of validation groups.

Of course that's not all to be said about the Bean Validation API. Features that I didn't mention during my journey through the JSR's API include class-level constraints, the proposed validation of method parameters and return values and the external declaration of constraints by the means of an XML descriptor.

JSR 303 remains in Public Review status for another couple of days. So you still have the chance to give feedback to the specification's authors. In my opinion the JSR looks really promising and I hope to see it soon in Final status, neatly integrated with the JSF 2.0 and JPA 2.0 APIs.