I want to decorate existing objects so that method calls are automatically validated. I already managed to delegate method call to an interceptor that calls Hibernate validator and so far it works fine:
public class HibernateBeanValidator implements BeanValidator{
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
@Override
public <T> T addMethodValidation(T object) {
ExecutableValidator executableValidator = factory.getValidator().forExecutables();
Class<? extends T> dynamicType = (Class<? extends T>)new ByteBuddy()
.subclass(object.getClass())
.method(isPublic()).intercept(MethodDelegation.to(new ValidationInterceptor(object, executableValidator)).andThen(SuperMethodCall.INSTANCE))
.make()
.load(getClass().getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
try {
T validatedObject = dynamicType.newInstance();
return validatedObject;
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
public static class ValidationInterceptor {
private final Object validatedObject;
private final ExecutableValidator executableValidator;
public <T> ValidationInterceptor(T object, ExecutableValidator executableValidator) {
this.validatedObject = object;
this.executableValidator = executableValidator;
}
public void validate(@Origin Method method, @AllArguments Object[] arguments)
throws Exception {
Set<ConstraintViolation<Object>> constraintViolations = executableValidator.validateParameters(validatedObject, method, arguments);
if(! constraintViolations.isEmpty()) {
throw new ValidationException(constraintViolations);
}
}
}
}
What I would like to improve is to bind method calls only to methods that have at least one parameter annotated with a constraint annotation, such as:
class Echo {
public String repeat(@NotNull String word) { /* should bind validation here */
return word;
}
public String notAnnotated(String word) { /* should not bind validation */
return word;
}
}
How could I specify an ElementMatcher in Byte Buddy so that it would bind only to methods with parameters annotated with annotations that are annotated with @Constraint, such as @NotNull (taken from javax.validation.constraints):
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = { })
public @interface NotNull {
String message() default "{javax.validation.constraints.NotNull.message}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
/**
* Defines several {@link NotNull} annotations on the same element.
*
* @see javax.validation.constraints.NotNull
*/
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
@interface List {
NotNull[] value();
}
}