One of the newer features introduced in JAX-RS 2.0 is the support for validating resource classes with Bean Validation Bean Validation support comes in Jersey 2.x by adding jersey-bean-validation module to the class-path. The following sample shows how to use Bean Validation with JA ]]> Type:Expresso,Blended,Chocolate,Brewed Size: Small, Medium, Large, X Large Preference: Regular, Non Fat, Soy Milk, Whipped CreamThe following code shows how the Coffee resource looks

 /** * * A simple JAX-RS resource which demonstrates Bean Validation * @author Bhakti Mehta * */ @Path("v1/coffees") public class CoffeesResource { @Context javax.ws.rs.core.UriInfo uriInfo; @GET @Path("{order}") @Produces(MediaType.APPLICATION_XML) @NotNull(message="Coffee does not exist for the order id requested") public Coffee getCoffee( @PathParam("order") int order) { return CoffeeService.getCoffee(order); } @POST @Consumes(MediaType.APPLICATION_XML) @Produces(MediaType.APPLICATION_XML) @ValidateOnExecution public Response addCoffee(@Valid Coffee coffee) { int order = CoffeeService.addCoffee(coffee); return Response.created(uriInfo.getAbsolutePath()) .entity("" + order + "").build(); } } 

As you can see in the preceeding snippet there is a GET and POST method. The @GET annotated method getCoffee() checks if a coffee exists for the PathParam order and returns it else returns null. There is an @NotNull annotation for the method to provide a message incase there is no coffee by the order specified The @POST annotated addCoffee() method adds a new order for the coffee. Notice the @Validannotation for the request parameter coffee. It indicates to validate the object coffee The next snippet shows how the Coffee object looks like.

 @XmlRootElement public class Coffee { @VerifyValue(Type.class) private String type; @VerifyValue(Size.class) private String size; @NotNull private String name; private double price; private int order; ....///getters and setters } 

I have two Enum Size and Type associated with the Coffee. Each of the enums are annotated with @VerifyEnum() annotation. The @VerifyValueis a custom constraint I introduced to validate any Enum object. It will be covered below. The following snippet shows how the Size and Type enums.

 public enum Size { Small("S"), Medium("M"), Large("L"), ExtraLarge("XL"); private String value; private Size(String v) { this.value = v; } public String getValue() { return value; } } 
 public enum Type { Expresso, Brewed, Blended, Chocolate ; } 

The following code shows how I have a custom bean validation constraint called VerifyValuewhich verifies if the value for the Enum is valid

 @Retention(RUNTIME) @Target({FIELD, METHOD}) @Documented @Constraint(validatedBy = VerifyValueValidator.class) public @interface VerifyValue { String message() default "Value specified is not valid"; Class[] groups() default {}; Class[] payload() default {}; Class> value(); } 

Here is the implementation for the VerifyValueValidator

 /** * Implementation for the user-defined constraint annotation @VerifyValue * This is a general purpose validator which verifies the value for any enum * If an Enum object has a getValue() method it will validate based on the value of the Enum * else will use the EnumConstant * * @author Bhakti Mehta */ public class VerifyValueValidator implements ConstraintValidator { Class> enumClass; public void initialize(final VerifyValue enumObject) { enumClass = enumObject.value(); } public boolean isValid(final Object myval, final ConstraintValidatorContext constraintValidatorContext) { if ((myval != null) && (enumClass != null)) { Enum[] enumValues = enumClass.getEnumConstants(); Object enumValue = null; for (Enum enumerable : enumValues) { if (myval.equals(enumerable.toString()) ) return true; enumValue = getEnumValue(enumerable); if ((enumValue != null) && (myval.toString().equals(enumValue.toString()))) { return true; } } } return false; } /** * Invokes the getValue() method for enum if present * @param enumerable The Enum object * @return returns the value of enum from getValue() or * enum constant */ private Object getEnumValue(Enum enumerable) { try { for (Method method: enumerable.getClass().getDeclaredMethods()) { if (method.getName().equals("getValue")) { return method.invoke(enumerable); } } } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return null; } 

The isValid() method looks for all the Enum objects if its value matches that specified in the request and returns true else returns false. There is no straightforward method to get an enum from a value specified so I have implemented the getEnumValue() method. The EnumSet will return the EnumConstants but not an Enum based on its value.The getEnumValue() method uses reflection to invoke the getValue()method for the Enum. There can be further optimization to cache the enum based on the value in a HashMap so future references can be looked up from the map first. Here is the Application class which has the following properties set

 public Map getProperties() { Map properties = new HashMap() ; properties.put(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true); return properties; } 

The ServerProperties.BV_SEND_ERROR_IN_RESPONSE is a Bean Validation (JSR-349) support customization property that will allow the validation errors to be sent back in the response Complete sample can be downloaded from https://github.com/bhakti-mehta/samples/tree/master/jax-rs-bean-validation

Running and testing the sample

Install GlassfFish 4.0 Build the sample using mvn install Deploy the war to GlassFish

 Using curl to the URL http://localhost:8080/jax-rs-bean-validation/v1/coffees curl -X POST -d @test.xml http://localhost:8080/jax-rs-bean-validation/v1/coffees --header "Content-Type:application/xml" 

This will give you an order id for the coffee created. Here is the sample input for the POST request in the test.xml file <coffee> <type>Expresso</type> <size>XL</size> <name>Cafe Latte</name> <price>6.50</price> </coffee>

 Send a GET request to http://localhost:8080/jax-rs-bean-validation/v1/coffees/ You will get a validation error incase there is no coffee with the order specified 

You can send some invalid values in the Type or the Size and see the validation errors.

Jersey 1.1.x and Bean Validation

In Jersey 1.x (RI for JAX-RS 1.1) Bean Validation is not supported by default. With Jersey 1.x I have enabled bean validation for resource parameters by using Aspect oriented Programming by implementing a MethodInterceptorThis feature enables us to write code that is executed each time a matching method is invoked.

 /** * Intercepts resource methods before they are invoked to check for * parameters annotated with @Valid annotation * and validates the method arguments * * Incase the argument is not valid an appropriate exception with the error * message underlying the cause of constraint violation errors is thrown * @author Bhakti Mehta * */ public class ValidationInterceptor implements MethodInterceptor { private final Validator validator public static IsValidable isValidable() { return new IsValidable(); } public ValidationInterceptor() { validator = Validation.buildDefaultValidatorFactory().getValidator(); } @Override public Object invoke(MethodInvocation invocation) throws Throwable { Method method = invocation.getMethod(); Object argument = null; Annotation[][] parameterAnnotations = method.getParameterAnnotations(); if (parameterAnnotations != null) { for (int i = 0; i < parameterAnnotations.length; i++) { Annotation[] annotationsArray = parameterAnnotations[i]; for(Annotation annotation : annotationsArray){ if (annotation instanceof Valid) { argument = invocation.getArguments()[i]; } } } } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("\n"); if (argument != null ) { Set> violations = validator.validate(argument); if (!violations.isEmpty()) { for(ConstraintViolation violation : violations) { stringBuilder.append(violation.getMessage()).append("\n"); } throw new ConstraintViolationException( String.format("Error when validating method %s due to %s", method.getName(), stringBuilder.toString()) ,violations); } } return invocation.proceed(); } } class IsValidable extends AbstractMatcher { @Override public boolean matches(Method method) { Class[] interfaces = method.getDeclaringClass().getInterfaces(); if (interfaces.length > 0) { Class resource = interfaces[0]; try { Method resourceMethod = resource.getMethod(method.getName(), method.getParameterTypes()); return Modifier.isPublic(resourceMethod.getModifiers()); } catch (NoSuchMethodException nsme) { return false; } } else { return false; } } } 

The above snippet shows the implementation of a MethodInterceptor On the invoke() method we get an instance of the MethodInvocation object. As you can see in the above snippet we get the Method from the invocation.getMethod() call and iterate through the parameter annotations for the method and check if there is an @Valid annotation If a param has an annotation with @Valid we get the argument from the methodInvocation using invocation.getArguments()[i] Incase the argument is not Null we call the validator.validate(argument) For all the ConstraintViolations we throw a ConstraintViolationException with the list of violations Finally we call through the underlying method by calling invocation.proceed()The following code shows how to inject the ValidationInterceptor so it works with Jersey 1.1.x

 public class MyConfigurator extends JerseyServletModule { @Override protected void configureServlets() { MethodInterceptor validationInterceptor = new ValidationInterceptor(); requestInjection(validationInterceptor); bindInterceptor(Matchers.any(), ValidationInterceptor.isValidable(), validationInterceptor); 

The above code shows how we inject interceptors. Another solution provided which can be useful is using ResourceFilterFactory as mentioned here However in my case I needed to know the arguments of the method and validate them and I found the AOP method interceptorworked well.

Summary

This blog showed how to use Bean Validation with JAX-RS 2.0. A complete sample is available for download from https://github.com/bhakti-mehta/samples/tree/master/jax-rs-bean-validation Additionally for people who cannot upgrade to JAX-RS 2.0 we covered how to support Bean Validation using two approaches 1. AOP with method interceptors 2. Using RequestFilterFactory and RequestFilters Hope this was helpful.