Bean Validation is by design static but sometimes you need to be able to make constraints evolving in time: a high/low limit can change depending on business requirements for instance.

Most of the time, it makes you write it in the business layer which makes sense when it is advanced constraints. But for simple checks - like “is this number higher than this one” - it would be nice to keep Bean Validation model.


Let’s see how CDI makes it possible, and how it opens doors to even more!

Creating the constraint

The marker/annotation

To illustrate this we will use a “prefix” constraint. This will simply check a String is prefixed with a configured value.

A constraint is an annotation which can hold a static configuration and a validation which implements the validation logic. Here is our marker annotation:

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = Prefixed.PrefixedValidator.class /*we’ll do it soon*/)
public @interface Prefixed {
   String message() default "{com.github.rmannibucau.bval.Prefixed.message}";
   Class<?>[] groups() default {};
   Class<? extends Payload>[] payload() default {};
}

Nothing special there, our annotation defines the standard BVal methods:

  • Message: the message reported in violations when the value is not valid (retrieved from a ValidationMessages[_locale].properties in the classpath, the value in {} is the key in this file).
  • Groups: the marker used to filter validations (only validate a subpart of the data)
  • Payload: allow to enrich the violation with some contextual data (this is rarely used in practise)

In a standard constraint we would add a String value()method to define the prefix we want to validate. Of course we will not do it, to be able to configure it somewhere else.

The validator

You have probably noticed that we referenced a validator Prefixed.PrefixedValidator, it is now time to implement it.

Note: in this case I implemented it as an inner class of the annotation, but you can use a normal class in another file if desired. The implementation is simple and relies on BVal 1.1 CDI integration:

@ApplicationScoped
public class PrefixedValidator implements ConstraintValidator<Prefixed, String> {
   @Inject
   private PrefixConfiguration configuration;

   @Override
   public void initialize(final Prefixed constraintAnnotation) {
       // here we don't read a static config from the annotation
   }

   @Override
   public boolean isValid(final String value, final ConstraintValidatorContext context) {
       return value != null && value.startsWith(configuration.getPrefix());
   }
}

So, nothing complicated: we just get the validation config from an injection.

Note: some people will prefer implementing the valid() method in a CDI bean and just invoke it to keep the validation logic framework agnostic. For this post it doesn’t change anything, but be sure to keep your code design consistent.

Make it dynamic

The original goal was to be able to change the validation depending on some external factors. In our sample, we will change the prefix but in real life it could be a minimum price (non premium users can buy until 100$, but this week’s offer will exceptionally be 120$).

Since the configuration is a CDI bean and we just need to ensure that the reference is shared by all injection points and bla bla bla… In brief: ensure you configuration is @ApplicationScoped. This way, you can inject your configuration in a servlet, JAX-RS endpoint or JSF controller, to change the prefix value (“admin GUI” idea).

Here is a sample of a JAX-RS admin endpoint:

import javax.annotation.security.RolesAllowed;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

import static javax.ws.rs.core.MediaType.APPLICATION_JSON;

@RolesAllowed("admin")
@Path("constraint")
@ApplicationScoped
@Produces(APPLICATION_JSON)
@Consumes(APPLICATION_JSON)
public class PrefixResource {
   @Inject
   private PrefixConfiguration prefix;

   @GET
   @Path("prefix")
   public Value getPrefix() {
       return new Value(prefix.getPrefix());
   }

   @POST
   @Path("prefix")
   public Value postPrefix(final Value newValue) {
       prefix.setPrefix(newValue.getValue());
       return getPrefix();
   }

   public static class Value {
       private String value;

       public Value() {
           // no-op
       }

       public Value(final String val) {
           value = val;
       }

       public void setValue(final String value) {
           this.value = value;
       }

       public String getValue() {
           return value;
       }
   }
}

There is no bval needed there: this is basically a front layer for the prefix configuration we have. Note that @RolesAllowed("admin") is not handled by default and needs to configure either CXF support for it or a custom interceptor to support it. But this is not the topic of this article.

This is the nice effect of this solution: you can influence bval validations from outside bval framework. It finally makes your validation more business oriented and less technical.

Going further

So now we have a dynamic bean validation constraint, we rely on CDI to retrieve the configuration and update it, so what’s missing? Some performance improvement.

I didn’t yet share PrefixConfiguration implementation but it can use a simple implementation like:

import javax.enterprise.context.ApplicationScoped;

// can be produced with owner or use deltaspike config
// or any custom config reader integrated with CDI
@ApplicationScoped
public class PrefixConfiguration {
   // @Inject @ConfigProperty(name = "...")
   private String prefix;

   public String getPrefix() {
       return prefix;
   }

   public void setPrefix(final String prefix) {
       this.prefix = prefix;
   }
}

In such a case, we are entirely in memory and we are globally done. However, the configuration of a real application is often in a database (whatever it means RDBMS or NoSQL). This also means that reading the configuration can be slow compared to the validation requirement.

That’s what we can enhance easily using JCache.
Since our stack is based on CDI, we can use JCache integration. We mainly need it on getPrefix() but we can ensure we evict the cache data on writes (i.e. setPrefix()):

@ApplicationScoped
public class PrefixConfiguration {
   // @Inject @ConfigProperty(name = "...") or use @PersistenceContext...
   private String prefix;

   @CacheResult
   public String getPrefix() {
       return prefix;
   }

   @CacheRemove
   public void setPrefix(final String prefix) {
       this.prefix = prefix;
   }
}

That’s what we would like to write but this is not doing what we expect. Why? Beause we need to tell JCache that the getter and setter are using the same cache (default uses method’s qualified name, so both are not cache at the same place) and that both use the same key (default rely on parameters and we don’t have the same parameters for both methods).

To do so, we will use the qualified name of the class as cache name, and use @CacheDefaults  to avoid setting it on both methods. We will then write a custom CacheKeyGenerator which will only provide a single key. This key will ensure that we always match the same entry.

Note: you can do a bit better! Create a key generator that matches a constant string. This would allow to put all constraints in the same cache and share the same eviction but the caching will be by entry (the string value). This is not the main topic once again, so let’s be simple.

Here is the full code:

import javax.cache.annotation.CacheDefaults;
import javax.cache.annotation.CacheKeyGenerator;
import javax.cache.annotation.CacheKeyInvocationContext;
import javax.cache.annotation.CacheRemove;
import javax.cache.annotation.CacheResult;
import javax.cache.annotation.GeneratedCacheKey;
import javax.enterprise.context.ApplicationScoped;
import java.lang.annotation.Annotation;

// can be produced with owner or use deltaspike config
// or any custom config reader integrated with CDI
@ApplicationScoped
@CacheDefaults(cacheName = "com.github.rmannibucau.bval.PrefixConfiguration", cacheKeyGenerator = PrefixConfiguration.ConstantKeyGenerator.class)
public class PrefixConfiguration {
   // @Inject @ConfigProperty(name = "...")
   private String prefix;

   @CacheResult
   public String getPrefix() {
       return prefix;
   }

   @CacheRemove
   public void setPrefix(final String prefix) {
       this.prefix = prefix;
   }

   @ApplicationScoped // constant key since get and set should use the same and we have a single value
   public static class ConstantKeyGenerator implements CacheKeyGenerator {
       private static final GeneratedCacheKey KEY = new ConstantKey();

       @Override
       public GeneratedCacheKey generateCacheKey(final CacheKeyInvocationContext<? extends Annotation> cacheKeyInvocationContext) {
           return KEY;
       }

       public static class ConstantKey implements GeneratedCacheKey {
           @Override
           public int hashCode() {
               return 0; // single key so hash is a constant
           }

           @Override
           public boolean equals(final Object obj) { // no param so type is enough
               return GeneratedCacheKey.class.isInstance(obj);
           }
       }
   }
}

Side note: as usual when I use nested classes, you can split it into several files.

With that decoration, our prefix will be cached, and even if it depends on the cache configuration, the validation shouldn’t suffer from a configuration retrieved from a database, which allows us to abstract business constraints a bit more.

The final word

Even if bean validation shouldn’t be used a lot for business validation in general, it is today a good candidate for validation, simply relying upon dynamic values.

It enables to not change the programming model and add a layer in your pipeline just because you need the constraint to be dynamic.

About the design of the proposed sample, a lot of improvements could be done but the link between technologies is there and should be easy to apply to your case.

The code has been tested on TomEE 7.0.0 but should run on all EE 7 servers supporting JCache or adding JCache if not provided out of the box.

Finally and to go further, don’t forget that Bean Validation has been integrated with JAX-RS too. Thereby, this technique can be used to change, at runtime, the constraints of payloads too!

From the same author:

In the same category: