Showing posts with label bean validation. Show all posts
Showing posts with label bean validation. Show all posts

Tuesday, November 6, 2018

Building Enterprise Java applications, the Spring way

I think it is fair to say that Java EE has gained pretty bad reputation among Java developers. Despite the fact that it has certainly improved on all fronts over the years, even changed home to Eclipse Foundation to become Jakarta EE, its bitter taste is still quite strong. On the other side we have Spring Framework (or to reflect the reality better, a full-fledged Spring Platform): brilliant, lightweight, fast, innovative and hyper-productive Java EE replacement. So why to bother with Java EE?

We are going to answer this question by showing how easy it is to build modern Java applications using most of Java EE specs. And the key ingredient to succeed here is Eclipse Microprofile: enterprise Java in the age of microservices.

The application we are going to build is RESTful web API to manage people, as simple as that. The standard way to build RESTful web services in Java is by using JAX-RS 2.1 (JSR-370). Consequently, CDI 2.0 (JSR-365) is going to take care of dependency injection whereas JPA 2.0 (JSR-317) is going to cover the data access layer. And certainly, Bean Validation 2.0 (JSR-380) is helping us to deal with input verification.

The only non-Java EE specification we would be relying on is OpenAPI v3.0 which helps to provide the usable description of our RESTful web APIs. With that, let us get started with the PersonEntity domain model (omitting getters and setters as not very relevant details):

@Entity
@Table(name = "people")
public class PersonEntity {
    @Id @Column(length = 256) 
    private String email;

    @Column(nullable = false, length = 256, name = "first_name")
    private String firstName;

    @Column(nullable = false, length = 256, name = "last_name")
    private String lastName;

    @Version
    private Long version;
}

It just has the absolute minimum set of properties. The JPA repository is pretty straightforward and implements typical set of CRUD methods.

@ApplicationScoped
@EntityManagerConfig(qualifier = PeopleDb.class)
public class PeopleJpaRepository implements PeopleRepository {
    @Inject @PeopleDb private EntityManager em;

    @Override
    @Transactional(readOnly = true)
    public Optional<PersonEntity> findByEmail(String email) {
        final CriteriaBuilder cb = em.getCriteriaBuilder();
    
        final CriteriaQuery<PersonEntity> query = cb.createQuery(PersonEntity.class);
        final Root<PersonEntity> root = query.from(PersonEntity.class);
        query.where(cb.equal(root.get(PersonEntity_.email), email));
        
        try {
            final PersonEntity entity = em.createQuery(query).getSingleResult();
            return Optional.of(entity);
        } catch (final NoResultException ex) {
            return Optional.empty();
        }
    }

    @Override
    @Transactional
    public PersonEntity saveOrUpdate(String email, String firstName, String lastName) {
        final PersonEntity entity = new PersonEntity(email, firstName, lastName);
        em.persist(entity);
        return entity;
    }

    @Override
    @Transactional(readOnly = true)
    public Collection<PersonEntity> findAll() {
        final CriteriaBuilder cb = em.getCriteriaBuilder();
        final CriteriaQuery<PersonEntity> query = cb.createQuery(PersonEntity.class);
        query.from(PersonEntity.class);
        return em.createQuery(query).getResultList();
    }

    @Override
    @Transactional
    public Optional<PersonEntity> deleteByEmail(String email) {
        return findByEmail(email)
            .map(entity -> {
                em.remove(entity);
                return entity;
            });
    }
}

The transaction management (namely, the @Transactional annotation) needs some explanation. In the typical Java EE application, the container runtime is responsible for managing the transactions. Since we don't want to onboard the application container but stay lean, we could have used EntityManager to start / commit / rollback transactions. It would certainly work out but pollute the code with the boilerplate. Arguably, the better option is to use Apache DeltaSpike CDI extensions for declarative transaction management (this is where @Transactional and @EntityManagerConfig annotations are coming from). The snippet below illustrates how it is being integrated.

@ApplicationScoped
public class PersistenceConfig {
    @PersistenceUnit(unitName = "peopledb")
    private EntityManagerFactory entityManagerFactory;

    @Produces @PeopleDb @TransactionScoped
    public EntityManager create() {
        return this.entityManagerFactory.createEntityManager();
    }

    public void dispose(@Disposes @PeopleDb EntityManager entityManager) {
        if (entityManager.isOpen()) {
            entityManager.close();
        }
    }
}

Awesome, the hardest part is already behind! The Person data transfer object and the service layer are coming next.

public class Person {
    @NotNull private String email;
    @NotNull private String firstName;
    @NotNull private String lastName;
}

Honestly, for the sake of keeping the example application as small as possible we could skip the service layer altogether and go to the repository directly. But this is, in general, not a very good practice so let us introduce PeopleServiceImpl anyway.

@ApplicationScoped
public class PeopleServiceImpl implements PeopleService {
    @Inject private PeopleRepository repository;

    @Override
    public Optional<Person> findByEmail(String email) {
        return repository
            .findByEmail(email)
            .map(this::toPerson);
    }

    @Override
    public Person add(Person person) {
        return toPerson(repository.saveOrUpdate(person.getEmail(), person.getFirstName(), person.getLastName()));
    }

    @Override
    public Collection<Person> getAll() {
        return repository
            .findAll()
            .stream()
            .map(this::toPerson)
            .collect(Collectors.toList());
    }

    @Override
    public Optional<Person> remove(String email) {
        return repository
            .deleteByEmail(email)
            .map(this::toPerson);
    }
    
    private Person toPerson(PersonEntity entity) {
        return new Person(entity.getEmail(), entity.getFirstName(), entity.getLastName());
    }
}

The only part left is the definition of the JAX-RS application and resources.

@Dependent
@ApplicationPath("api")
@OpenAPIDefinition(
    info = @Info(
        title = "People Management Web APIs", 
        version = "1.0.0", 
        license = @License(
            name = "Apache License", 
            url = "https://www.apache.org/licenses/LICENSE-2.0"
        )
    )
)
public class PeopleApplication extends Application {
}

Not much to say, as simple as it could possibly be. The JAX-RS resource implementation is a bit more interesting though (the OpenAPI annotations are taking most of the place).

@ApplicationScoped
@Path( "/people" ) 
@Tag(name = "people")
public class PeopleResource {
    @Inject private PeopleService service;
    
    @Produces(MediaType.APPLICATION_JSON)
    @GET
    @Operation(
        description = "List all people", 
        responses = {
            @ApiResponse(
                content = @Content(array = @ArraySchema(schema = @Schema(implementation = Person.class))),
                responseCode = "200"
            )
        }
    )
    public Collection<Person> getPeople() {
        return service.getAll();
    }

    @Produces(MediaType.APPLICATION_JSON)
    @Path("/{email}")
    @GET
    @Operation(
        description = "Find person by e-mail", 
        responses = {
            @ApiResponse(
                content = @Content(schema = @Schema(implementation = Person.class)), 
                responseCode = "200"
            ),
            @ApiResponse(
                responseCode = "404", 
                description = "Person with such e-mail doesn't exists"
            )
        }
    )
    public Person findPerson(@Parameter(description = "E-Mail address to lookup for", required = true) @PathParam("email") final String email) {
        return service
            .findByEmail(email)
            .orElseThrow(() -> new NotFoundException("Person with such e-mail doesn't exists"));
    }

    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    @POST
    @Operation(
        description = "Create new person",
        requestBody = @RequestBody(
            content = @Content(schema = @Schema(implementation = Person.class)),
        ), 
        responses = {
            @ApiResponse(
                 content = @Content(schema = @Schema(implementation = Person.class)),
                 headers = @Header(name = "Location"),
                 responseCode = "201"
            ),
            @ApiResponse(
                responseCode = "409", 
                description = "Person with such e-mail already exists"
            )
        }
    )
    public Response addPerson(@Context final UriInfo uriInfo,
            @Parameter(description = "Person", required = true) @Valid Person payload) {

        final Person person = service.add(payload);
        return Response
             .created(uriInfo.getRequestUriBuilder().path(person.getEmail()).build())
             .entity(person)
             .build();
    }
    
    @Path("/{email}")
    @DELETE
    @Operation(
        description = "Delete existing person",
        responses = {
            @ApiResponse(
                responseCode = "204",
                description = "Person has been deleted"
            ),
            @ApiResponse(
                responseCode = "404", 
                description = "Person with such e-mail doesn't exists"
            )
        }
    )
    public Response deletePerson(@Parameter(description = "E-Mail address to lookup for", required = true ) @PathParam("email") final String email) {
        return service
            .remove(email)
            .map(r -> Response.noContent().build())
            .orElseThrow(() -> new NotFoundException("Person with such e-mail doesn't exists"));
    }
}

And with that, we are done! But how could we assemble and wire all these pieces together? Here is the time for Microprofile to enter the stage. There are many implementations to chose from, the one we are going to use in this post is Project Hammock. The only thing we have to do is to specify the CDI 2.0, JAX-RS 2.1 and JPA 2.0 implementations we would like to use, which translates to Weld, Apache CXF, and OpenJPA respectively (expressed through the Project Hammock dependencies). Let us take a look on the Apache Maven pom.xml file.

<properties>
    <deltaspike.version>1.8.1</deltaspike.version>
    <hammock.version>2.1</hammock.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.apache.deltaspike.modules</groupId>
        <artifactId>deltaspike-jpa-module-api</artifactId>
        <version>${deltaspike.version}</version>
        <scope>compile</scope>
    </dependency>

    <dependency>
        <groupId>org.apache.deltaspike.modules</groupId>
        <artifactId>deltaspike-jpa-module-impl</artifactId>
        <version>${deltaspike.version}</version>
        <scope>runtime</scope>
    </dependency>

    <dependency>
        <groupId>ws.ament.hammock</groupId>
        <artifactId>dist-microprofile</artifactId>
        <version>${hammock.version}</version>
    </dependency>

    <dependency>
        <groupId>ws.ament.hammock</groupId>
        <artifactId>jpa-openjpa</artifactId>
        <version>${hammock.version}</version>
    </dependency>

    <dependency>
        <groupId>ws.ament.hammock</groupId>
        <artifactId>util-beanvalidation</artifactId>
        <version>${hammock.version}</version>
    </dependency>

    <dependency>
        <groupId>ws.ament.hammock</groupId>
        <artifactId>util-flyway</artifactId>
        <version>${hammock.version}</version>
    </dependency>

    <dependency>
        <groupId>ws.ament.hammock</groupId>
        <artifactId>swagger</artifactId>
        <version>${hammock.version}</version>
    </dependency>
</dependencies>

Without further ado, let us build and run the application right away (if you are curious what relational datastore the application is using, it is H2 with the database configured in-memory).

> mvn clean package
> java -jar target/eclipse-microprofile-hammock-0.0.1-SNAPSHOT-capsule.jar 

The best way to ensure that our people management RESTful web APIs are fully functional is to send a couple of requests to it:

>  curl -X POST http://localhost:10900/api/people -H "Content-Type: application\json" \
     -d '{"email": "a@b.com", "firstName": "John", "lastName": "Smith"}'

HTTP/1.1 201 Created
Location: http://localhost:10900/api/people/a@b.com
Content-Type: application/json

{
    "firstName":"John","
    "lastName":"Smith",
    "email":"a@b.com"
}

What about making sure the Bean Validation is working fine? To trigger that, let us send the partially prepared request.

>  curl  --X POST http://localhost:10900/api/people -H "Content-Type: application\json" \
     -d '{"firstName": "John", "lastName": "Smith"}'

HTTP/1.1 400 Bad Request
Content-Length: 0

The OpenAPI specification and pre-bundled Swagger UI distribution are also available at http://localhost:10900/index.html?url=http://localhost:10900/api/openapi.json.

So far so good but fairly speaking we have not talked about testing our application at all. How hard it would be to come up with the integration test for, let say, the scenario of adding a person? It turns out that the frameworks around testing Java EE applications have improved a lot. In particular, it is exceptionally easy to accomplish with Arquillian test framework (along with beloved JUnit and REST Assured). One real example is worth thousand words.

@RunWith(Arquillian.class)
@EnableRandomWebServerPort
public class PeopleApiTest {
    @ArquillianResource private URI uri;
    
    @Deployment
    public static JavaArchive createArchive() {
        return ShrinkWrap
            .create(JavaArchive.class)
            .addClasses(PeopleResource.class, PeopleApplication.class)
            .addClasses(PeopleServiceImpl.class, PeopleJpaRepository.class, PersistenceConfig.class)
            .addPackages(true, "org.apache.deltaspike");
    }
            
    @Test
    public void shouldAddNewPerson() throws Exception {
        final Person person = new Person("a@b.com", "John", "Smith");
        
        given()
            .contentType(ContentType.JSON)
            .body(person)
            .post(uri + "/api/people")
            .then()
            .assertThat()
            .statusCode(201)
            .body("email", equalTo("a@b.com"))
            .body("firstName", equalTo("John"))
            .body("lastName", equalTo("Smith"));
    }
}

Amazing, isn't it? It is actually a lot of fun to develop modern Java EE applications, someone may say, the Spring way! And in fact, the parallels with Spring are not coincidental since it was inspiring, is inspiring and undoubtedly is going to continue inspire a lot of innovations in the Java EE ecosystem.

How the future is looking like? I think, by all means bright, both for Jakarta EE and Eclipse Microprofile. The latter just approached the version 2.0 with tons of new specifications included, oriented to address the needs of the microservice architectures. It is awesome to witness these transformations happening.

The complete source of the project is available on Github.

Thursday, February 25, 2016

Your JAX-RS APIs were not born equal: using dynamic features

This time we are going to talk a little bit about JAX-RS 2.0 APIs and touch on one very interesting aspect of the specification: dynamic features and how they are useful.

Traditionally, when JAX-RS 2.0 APIs are configured and deployed (using Application class, bootstrapped from servlet or created through RuntimeDelegate), there is an option to register additional providers and features. The great examples of those could be bean validation (JSR 349) or Java API for JSON processing (JSR-353) support. Those providers and features are going to be applied to all JAX-RS 2.0 resources and in most use cases this is a desired behavior. However, from time to time there is a need to enable a particular provider or feature only for some resources, leaving others unaffected. This is exactly the use case where dynamic features are going to help us a lot.

For this post we are going to use the latest version 3.1.5 of excellent Apache CXF framework but dynamic features are part of the JAX-RS 2.0 specification and are supported by most (if not all) of the implementations.

Let us consider a very simple JAX-RS 2.0 API to manage people, with a single method to handle HTTP GET requests. Let us assume this is a version 1 of the API and although the @Range annotation is specified for the count query parameter, its support was never implemented and it is present in the code for documentation purposes only.

@Path("/v1/people")
public class PeopleRestService {
    @Produces( { MediaType.APPLICATION_JSON } )
    @GET
    public List<Person> getAll(@Range(min = 1, max = 10) @QueryParam("count") int count) {
        return Collections.nCopies(count, new Person("a@b.com", "A", "B"));
    }
}

In this case, passing an invalid value for the count query parameter is going to result in Internal Server Error. Let us make sure this is exactly what is happening:

$ curl -i http://localhost:8080/rest/api/v1/people?count=-1

HTTP/1.1 500 Server Error
Cache-Control: must-revalidate,no-cache,no-store
Content-Type: text/html;charset=iso-8859-1
Content-Length: 377
Connection: close
Server: Jetty(9.3.7.v20160115)

After some time we realized the issues with this API and decided to implement the proper validation mechanism in place, using the Bean Validation 1.1 integration with JAX-RS 2.0. However, we made a decision to create version 2 of the API and to keep version 1 untouched as its clients do not expect any other HTTP status codes except 200 and 500 to be returned (unfortunately, in real life it happens more often than not).

There are couple of different approaches to implement such per-API customization, but probably the most simple one is by introducing a dedicated annotation, for example @EnableBeanValidation, and annotating JAX-RS 2.0 resource class with it:

@Path("/v2/people")
@EnableBeanValidation
public class ValidatingPeopleRestService {
    @Produces( { MediaType.APPLICATION_JSON } )
    @GET
    public @Valid List<Person> getAll(@Range(min = 1, max = 10) @QueryParam("count") int count) {
        return Collections.nCopies(count, new Person("a@b.com", "A", "B"));
    }
}

To enable Bean Validation 1.1 for all the JAX-RS 2.0 APIs annotated with @EnableBeanValidation we are going to create a dynamic feature class, BeanValidationDynamicFeature:

@Provider
public class BeanValidationDynamicFeature implements DynamicFeature {
    private final JAXRSBeanValidationInInterceptor inInterceptor;
    private final JAXRSBeanValidationOutInterceptor outInterceptor;
    
    public BeanValidationDynamicFeature(final BeanValidationProvider provider) {
        this.inInterceptor = new JAXRSBeanValidationInInterceptor();
        this.inInterceptor.setProvider(provider);
        
        this.outInterceptor = new JAXRSBeanValidationOutInterceptor();
        this.outInterceptor.setProvider(provider);
    }
    
    @Override
    public void configure(final ResourceInfo resourceInfo, final FeatureContext context) {
        if (resourceInfo.getResourceClass().getAnnotation(EnableBeanValidation.class) != null) {
            context.register(inInterceptor);
            context.register(outInterceptor);
        }
    }
}
Its job is pretty simple, just register JAXRSBeanValidationInInterceptor and JAXRSBeanValidationOutInterceptor interceptor instances as additional providers for JAX-RS 2.0 APIs in question. One minor but important note though: exception mappers are not supported by dynamic features, at least with respect to Apache CXF implementation, and should be registered as a regular providers (along with dynamic features themselves), for example:

@Bean @DependsOn("cxf")
public Server jaxRsServer() {
    final JAXRSServerFactoryBean factory = 
        RuntimeDelegate.getInstance().createEndpoint( 
            jaxRsApiApplication(), 
            JAXRSServerFactoryBean.class 
        );
        
    factory.setServiceBean(validatingPeopleRestService());
    factory.setServiceBean(peopleRestService());
    factory.setProvider(new JacksonJsonProvider());
    factory.setProvider(new BeanValidationDynamicFeature(new BeanValidationProvider()));
    factory.setProvider(new ValidationExceptionMapper());
        
    return factory.create();
}

@Bean 
public JaxRsApiApplication jaxRsApiApplication() {
    return new JaxRsApiApplication();
}
    
@Bean 
public ValidatingPeopleRestService validatingPeopleRestService() {
    return new ValidatingPeopleRestService();
}
    
@Bean 
public PeopleRestService peopleRestService() {
    return new PeopleRestService();
}

That is basically all we have to do. Once the BeanValidationDynamicFeature is registered (in this case using JAXRSServerFactoryBean), it is going to be applied to all matching service beans. Let us make sure that for version 2 of our people management API the proper out of the box validation is triggered:

$ curl -i http://localhost:8080/rest/api/v2/people?count=-1

HTTP/1.1 400 Bad Request
Content-Length: 0
Server: Jetty(9.3.7.v20160115)

This time the response is different, indicating that invalid input has been submitted by the client (straight result of Bean Validation 1.1 in action): Bad Request.

Hopefully, dynamic features are going to be yet another useful tool in your toolbox. The example we have covered here is somewhat imaginary but it is very easy to use dynamic features with security, tracing, logging, profiling, ... Moreover, dynamic features can be applied even on a particular resource methods, allowing fined-grained control over your APIs.

The complete project source is available on Github.

Thursday, March 27, 2014

Apache CXF 3.0: JAX-RS 2.0 and Bean Validation 1.1 finally together

The upcoming release 3.0 (currently in milestone 2 phase) of the great Apache CXF framework is bringing a lot of interesting and useful features, getting closer to deliver full-fledged JAX-RS 2.0 support. One of those features, a long-awaited by many of us, is the support of Bean Validation 1.1: easy and concise model to add validation capabilities to your REST services layer.

In this blog post we are going to look how to configure Bean Validation 1.1 in your Apache CXF projects and discuss some interesting use cases. To keep this post reasonably short and focused, we will not discuss the Bean Validation 1.1 itself but concentrate more on integration with JAX-RS 2.0 resources (some of the bean validation basics we have already covered in the older posts).

At the moment, Hibernate Validator is the de-facto reference implementation of Bean Validation 1.1 specification, with the latest version being 5.1.0.Final and as such it will be the validation provider of our choice (Apache BVal project at the moment supports only Bean Validation 1.0). It is worth to mention that Apache CXF is agnostic to implementation and will work equally well either with Hibernate Validator or Apache BVal once released.

We are going to build a very simple application to manage people. Our model consists of one single class named Person.

package com.example.model;

import javax.validation.constraints.NotNull;

import org.hibernate.validator.constraints.Email;

public class Person {
    @NotNull @Email private String email;
    @NotNull private String firstName;
    @NotNull private String lastName;
        
    public Person() {
    }

    public Person( final String email ) {
        this.email = email;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail( final String email ) {
        this.email = email;
    }
    
    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setFirstName( final String firstName ) {
        this.firstName = firstName;
    }
    
    public void setLastName( final String lastName ) {
        this.lastName = lastName;
    }        
}

From snippet above we can see that Person class imposes couple of restrictions on its properties: all of them should not be null. Additionally, email property should contain a valid e-mail address (which will be validated by Hibernate Validator-specific constraint @Email). Pretty simple.

Now, let us take a look on JAX-RS 2.0 resources with validation constraints. The skeleton of the PeopleRestService class is bind to /people URL path and is shown below.

package com.example.rs;

import java.util.Collection;

import javax.inject.Inject;
import javax.validation.Valid;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;

import org.hibernate.validator.constraints.Length;

import com.example.model.Person;
import com.example.services.PeopleService;

@Path( "/people" ) 
public class PeopleRestService {
    @Inject private PeopleService peopleService;

    // REST methods here
}

It should look very familiar, nothing new. The first method we are going to add and decorate with validation constraints is getPerson, which will look up a person by its e-mail address.

@Produces( { MediaType.APPLICATION_JSON } )
@Path( "/{email}" )
@GET
public @Valid Person getPerson( 
        @Length( min = 5, max = 255 ) @PathParam( "email" ) final String email ) {
    return peopleService.getByEmail( email );
}

There are a couple of differences from traditional JAX-RS 2.0 method declaration. Firstly, we would like the e-mail address (email path parameter) to be at least 5 characters long (but no more than 255 characters) which is imposed by @Length( min = 5, max = 255 ) annotation. Secondly, we would like to ensure that only valid person is returned by this method so we annotated the method's return value with @Valid annotation. The effect of @Valid is very interesting: the person's instance in question will be checked against all validation constraints declared by its class (Person).

At the moment, Bean Validation 1.1 is not active by default in your Apache CXF projects so if you run your application and call this REST endpoint, all validation constraints will be simply ignored. The good news are that it is very easy to activate Bean Validation 1.1 as it requires only three components to be added to your usual configuration (please check out this feature documentation for more details and advanced configuration):

  • JAXRSBeanValidationInInterceptor in-inteceptor: performs validation of the input parameters of JAX-RS 2.0 resource methods
  • JAXRSBeanValidationOutInterceptor out-inteceptor: performs validation of return values of JAX-RS 2.0 resource methods
  • ValidationExceptionMapper exception mapper: maps the validation violations to HTTP status codes. As per specification, all input parameters validation violations result into 400 Bad Request error. Respectively, all return values validation violations result into 500 Internal Server Error error. At the moment, the ValidationExceptionMapper does not include additional information into response (as it may violate application protocol) but it could be easily extended to provide more details about validation errors.
The AppConfig class shows off one of the ways to wire up all the required components together using RuntimeDelegate and JAXRSServerFactoryBean (the XML-based configuration is also supported).

package com.example.config;

import java.util.Arrays;

import javax.ws.rs.ext.RuntimeDelegate;

import org.apache.cxf.bus.spring.SpringBus;
import org.apache.cxf.endpoint.Server;
import org.apache.cxf.interceptor.Interceptor;
import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
import org.apache.cxf.jaxrs.validation.JAXRSBeanValidationInInterceptor;
import org.apache.cxf.jaxrs.validation.JAXRSBeanValidationOutInterceptor;
import org.apache.cxf.jaxrs.validation.ValidationExceptionMapper;
import org.apache.cxf.message.Message;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import com.example.rs.JaxRsApiApplication;
import com.example.rs.PeopleRestService;
import com.example.services.PeopleService;
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;

@Configuration
public class AppConfig {    
    @Bean( destroyMethod = "shutdown" )
    public SpringBus cxf() {
        return new SpringBus();
    }

    @Bean @DependsOn( "cxf" )
    public Server jaxRsServer() {
        final JAXRSServerFactoryBean factory = 
            RuntimeDelegate.getInstance().createEndpoint( 
                jaxRsApiApplication(), 
                JAXRSServerFactoryBean.class 
            );
        factory.setServiceBeans( Arrays.< Object >asList( peopleRestService() ) );
        factory.setAddress( factory.getAddress() );
        factory.setInInterceptors( 
            Arrays.< Interceptor< ? extends Message > >asList( 
                new JAXRSBeanValidationInInterceptor()
            ) 
        );
        factory.setOutInterceptors( 
            Arrays.< Interceptor< ? extends Message > >asList( 
                new JAXRSBeanValidationOutInterceptor() 
            ) 
        );
        factory.setProviders( 
            Arrays.asList( 
                new ValidationExceptionMapper(), 
                new JacksonJsonProvider() 
            ) 
        );

        return factory.create();
    }
    
    @Bean 
    public JaxRsApiApplication jaxRsApiApplication() {
        return new JaxRsApiApplication();
    }
    
    @Bean 
    public PeopleRestService peopleRestService() {
        return new PeopleRestService();
    }

    @Bean 
    public PeopleService peopleService() {
        return new PeopleService();
    }
}

All in/out interceptors and exception mapper are injected. Great, let us build the project and run the server to validate the Bean Validation 1.1 is active and works as expected.

mvn clean package
java -jar target/jaxrs-2.0-validation-0.0.1-SNAPSHOT.jar 

Now, if we issue a REST request with short (or invalid) e-mail address a@b, the server should return 400 Bad Request. Let us validate that.

> curl http://localhost:8080/rest/api/people/a@b -i

HTTP/1.1 400 Bad Request
Date: Wed, 26 Mar 2014 00:11:59 GMT
Content-Length: 0
Server: Jetty(9.1.z-SNAPSHOT)

Excellent! To be completely sure, we can check server console output and find there the validation exception of type ConstraintViolationException and its stacktrace. Plus, the last line provides the details what went wrong: PeopleRestService.getPerson.arg0: length must be between 5 and 255 (please notice, because argument names are not currently available on JVM after compilation, they are replaced by placeholders like arg0, arg1, ...).

WARNING: Interceptor for {http://rs.example.com/}PeopleRestService has thrown exception, unwinding now
javax.validation.ConstraintViolationException
        at org.apache.cxf.validation.BeanValidationProvider.validateParameters(BeanValidationProvider.java:119)
        at org.apache.cxf.validation.BeanValidationInInterceptor.handleValidation(BeanValidationInInterceptor.java:59)
        at org.apache.cxf.validation.AbstractValidationInterceptor.handleMessage(AbstractValidationInterceptor.java:73)
        at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:307)
        at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121)
        at org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:240)
        at org.apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.java:223)
        at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:197)
        at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:149)
        at org.apache.cxf.transport.servlet.CXFNonSpringServlet.invoke(CXFNonSpringServlet.java:167)
        at org.apache.cxf.transport.servlet.AbstractHTTPServlet.handleRequest(AbstractHTTPServlet.java:286)
        at org.apache.cxf.transport.servlet.AbstractHTTPServlet.doGet(AbstractHTTPServlet.java:211)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:687)
        at org.apache.cxf.transport.servlet.AbstractHTTPServlet.service(AbstractHTTPServlet.java:262)
        at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:711)
        at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:552)
        at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1112)
        at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:479)
        at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1046)
        at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
        at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97)
        at org.eclipse.jetty.server.Server.handle(Server.java:462)
        at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:281)
        at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:232)
        at org.eclipse.jetty.io.AbstractConnection$1.run(AbstractConnection.java:505)
        at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:607)
        at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:536)
        at java.lang.Thread.run(Unknown Source)

Mar 25, 2014 8:11:59 PM org.apache.cxf.jaxrs.validation.ValidationExceptionMapper toResponse
WARNING: PeopleRestService.getPerson.arg0: length must be between 5 and 255

Moving on, we are going to add two more REST methods to demonstrate the collections and Response validation in action.

@Produces( { MediaType.APPLICATION_JSON } )
@GET
public @Valid Collection< Person > getPeople( 
        @Min( 1 ) @QueryParam( "count" ) @DefaultValue( "1" ) final int count ) {
    return peopleService.getPeople( count );
}

The @Valid annotation on collection of objects will ensure that every single object in collection is valid. The count parameter is also constrained to have the minimum value 1 by @Min( 1 ) annotation (the @DefaultValue is taken into account if the query parameter is not specified). Let us on purpose add the person without first and last names set so the resulting collection will contain at least one person instance which should not pass the validation process.

> curl http://localhost:8080/rest/api/people -X POST -id "email=a@b3.com"

With that, the call of getPeople REST method should return 500 Internal Server Error. Let us check that is the case.

> curl -i http://localhost:8080/rest/api/people?count=10

HTTP/1.1 500 Server Error
Date: Wed, 26 Mar 2014 01:28:58 GMT
Content-Length: 0
Server: Jetty(9.1.z-SNAPSHOT)

Looking into server console output, the hint what is wrong is right there.

Mar 25, 2014 9:28:58 PM org.apache.cxf.jaxrs.validation.ValidationExceptionMapper toResponse
WARNING: PeopleRestService.getPeople.[0].firstName: may not be null
Mar 25, 2014 9:28:58 PM org.apache.cxf.jaxrs.validation.ValidationExceptionMapper toResponse
WARNING: PeopleRestService.getPeople.[0].lastName: may not be null

And finally, yet another example, this time with generic Response object.

@Valid
@Produces( { MediaType.APPLICATION_JSON  } )
@POST
public Response addPerson( @Context final UriInfo uriInfo,
        @NotNull @Length( min = 5, max = 255 ) @FormParam( "email" ) final String email, 
        @FormParam( "firstName" ) final String firstName, 
        @FormParam( "lastName" ) final String lastName ) {        
    final Person person = peopleService.addPerson( email, firstName, lastName );
    return Response.created( uriInfo.getRequestUriBuilder().path( email ).build() )
        .entity( person ).build();
}

The last example is a bit tricky: the Response class is part of JAX-RS 2.0 API and has no validation constraints defined. As such, imposing any validation rules on the instance of this class will not trigger any violations. But Apache CXF tries its best and performs a simple but useful trick: instead of Response instance, the response's entity will be validated instead. We can easy verify that by trying to create a person without first and last names set: the expected result should be 500 Internal Server Error.

> curl http://localhost:8080/rest/api/people -X POST -id "email=a@b3.com"

HTTP/1.1 500 Server Error
Date: Wed, 26 Mar 2014 01:13:06 GMT
Content-Length: 0
Server: Jetty(9.1.z-SNAPSHOT)

And server console output is more verbose:

Mar 25, 2014 9:13:06 PM org.apache.cxf.jaxrs.validation.ValidationExceptionMapper toResponse
WARNING: PeopleRestService.addPerson.<return value>.firstName: may not be null
Mar 25, 2014 9:13:06 PM org.apache.cxf.jaxrs.validation.ValidationExceptionMapper toResponse
WARNING: PeopleRestService.addPerson.<return value>.lastName: may not be null

Nice! In this blog post we have just touched a bit the a topic of how Bean Validation 1.1 may make your Apache CXF projects better by providing such a rich and extensible declarative validation support. Definitely give it a try!

A complete project is available on GitHub.

Saturday, April 2, 2011

Force your beans validation using JSR 303 and Hibernate Validator 4.1.0

In my list of most loved features JSR 303 is somewhere among top ones. Not only because it's extremely useful, but also because it has simple design and extensibility. Hibernate project actively uses validation techniques for a long time as part of Hibernate Validator subproject, recently with JSR 303 implementation support.

Today I would like to share some basic uses cases of JSR 303 annotations and programmatic validation support. Let us start with first one.

1. Assume you are expecting Person object to be passed to you business logic layer, just typical Java bean with few properties.
package org.example;

public class Person {
    private String firstName;
    private String lastName;
    
    public void setFirstName( String firstName ) {
        this.firstName = firstName;
    }
 
    public String getFirstName() {
        return firstName;
    }

    public void setLastName( String lastName ) {
        this.lastName = lastName;
    }

    public String getLastName() {
        return lastName;
    }   
}
Further, let us assume that according to business logic firstName and lastName properties are required. There are many ways to enforce such kind of constraints but we will use JSR 303 bean validation and Hibernate Validator.
package org.example;

import org.hibernate.validator.constraints.NotBlank;

public class Person {
    @NotBlank private String firstName;
    @NotBlank private String lastName;
    
    ...  
}
The question arises who will actually perform validation? The most straightforward solution is to do it using utility class.
package org.example;

import java.util.HashSet;
import java.util.Set;

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

public class ValidatorUtil {
    private final ValidatorFactory factory;
 
    public ValidatorUtil() {
        factory = Validation.buildDefaultValidatorFactory();
    }
  
    public< T > void validate( final T instance ) {  
        final Validator validator = factory.getValidator();
  
        final Set< ConstraintViolation< T > > violations = validator
            .validate( instance, Default.class );

        if( !violations.isEmpty() ) {
            final Set< ConstraintViolation< ? > > constraints = 
                new HashSet< ConstraintViolation< ? > >( violations.size() );

            for ( final ConstraintViolation< ? > violation: violations ) {
                constraints.add( violation );
            }
   
            throw new ConstraintViolationException( constraints );
        }
    }
}
Having such a class validation is easy:
final Person person = new Person();
new ValidatorUtil().validate( person );
ConstraintViolationException exception will be thrown in case of any validation errors. Let us move on to more complicated examples.

2. Assume you are expecting Department object to be passed to you business logic containing at least one (valid!) person. Let me skip the details and present the final solution here.
package org.example;

import java.util.Collection;

import javax.validation.Valid;

import org.hibernate.validator.constraints.NotEmpty;

public class Department {
    @NotEmpty @Valid private Collection< Person > persons;

    public void setPersons( Collection< Person > persons ) {
        this.persons = persons;
    }

    public Collection< Person > getPersons() {
        return persons;
    }
}
The validation above ensures that Department contains at least one Person instance and each Person's instance is itself valid.

There are just a lot of other use case out there which Hibernate Validator is able to handle out of the box: @Min, @Man, @Email, @Url, @Range, @Length, @Pattern, ... This post is just a starting point ...