Let's consider the following example, I'm creating a custom bean validation constraint by means of a new annotation type:
#Target( { METHOD, FIELD, ANNOTATION_TYPE })
#Retention(RUNTIME)
#Constraint(validatedBy = MyAbstractOrInterfaceValidator.class)
#Documented
public #interface MyAnnotation {
String message() default "{}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
SomeClass value();
}
My question is: can MyBastractOrInterfaceValidator class be an abstract class or an interface? How can I control which implementation of that interface or abstract class is used to validate an element the annotation is placed on?
No, that's not possible. If you want to hide your validator implementations from the public constraint definitions, you have the following options:
Provide a constraint mapping XML file and bundle it with your distribution. Users of the constraint will have to add the file to their validation.xml, though.
If you are using a DI solution such as CDI or Spring, provide just a very slim validator implementation as part of your public API (possibly as an inner class of the annotation) and obtain the actual implementation via dependency injection
If you are on Hibernate Validator 5.2 (currently under development), constraint validators can be registered via a META-INF/services file, which would be exactly what you are after; You even can plug in a custom constraint definition contributor for implementing other lookup strategies
See http://docs.oracle.com/javaee/7/api/javax/validation/Constraint.html
First, note that validatedBy is a Class<? extends ConstraintValidator<?,?>>[], that's an array, not a single class.
Second, there's no reason to use an interface or abstract class, because that class needs to be instantiated.
But, if you want to change the validator's implementation in the runtime, try using this:
public class MyValidator implements ConstraintValidator<MyAnnotation, String> {
//store the Class information in a static variable
private static Class<? extends ConstraintValidator<MyAnnotation, String>> implementationClass = MyValidatorOne.class;
//and change it by an accessor
public static void setImplementationClass(Class<? extends ConstraintValidator<MyAnnotation, String>> implClass) {
this.implementationClass = implClass;
}
//this delegate will do all the job
private final ConstraintValidator<MyAnnotation, String> implementation;
public MyValidator() {
implementation = implementationClass.newInstance();
}
#Override
void initialize(MyAnnotation constraintAnnotation) {
implementation.initialize(constraintAnnotation);
}
#Override
boolean isValid(T value, ConstraintValidatorContext context) {
return implementation.isValid(value, context);
}
}
somewhere in the code:
MyValidator.setImplementationClass(MyValidatorTwo.class);
But there's one problem. Most probably an instance of each validator is created once in the runtime for a single class - on the first validation call on object of that class. Implementation change will only take effect if done before that.
Other way is to store implementationClass values in external class, like java.util.Properties or a class which picks available implementations with some priority.
Related
My current situation:
I want to inject the following class into my application:
public interface IConfigAccessor<T extends IConfig> {
...
}
ConfigAccessors are a proxy-objects, created dynamically at runtime. The creation of these object works as follows:
public class ConfigFactory implements IConfigFactory {
private final IConfigUpdater updater;
#Inject
public ConfigFactory(IConfigUpdater updater) {
this.updater = updater;
}
#Override
public <T extends IConfig> IConfigAccessor<T> register(final String configKey, final Class<T> configClass) {
ConfigCache<T> configCache = new ConfigCache<>(new SomeOtherThings(), configKey, configClass);
updater.register(configCache);
return new ConfigAccessor<>(configCache, configKey, configClass);
}
}
As you can see, to create these objects, I need to inject the ConfigUpdater and other depdencies. This means, that guice needs to be fully configured already.
To get the instance out of Guice, I use the following code:
IConfigFactory configClient = injector.getInstance(IConfigFactory.class);
IConfigAccessor<ConcreteConfig> accessor = configClient.register("key", ConcreteConfig.class)
How I want to inject them via Guice:
Currently, I can get the requried objects, but I have to manually pass them around in my application.
Instead, what I want to have is the following:
public class SomeClass {
#Inject
public SomeClass(#Config(configKey="key") IConfigAccessor<ConcreteConfig> accessor) {
// hurray!
}
}
What's the correct approach/technology to get this working?
After a lot of research, I'm feeling a bit lost on how to approach this topic. There are a lot of different things Guice offers, including simple Providers, custom Listeners which scan classes and identify custom annotations, FactoryModuleBuilders and more.
My problem is quite specific, and I'm not sure which of these things to use and how to get it working. I'm not even sure if this is even possible with Guice?
Edit: What I have so far
I have the following annotation which I want to use inside constructor paramters:
#Target({ ElementType.FIELD, ElementType.PARAMETER })
#Retention(RetentionPolicy.RUNTIME)
public #interface InjectConfig {
String configKey();
}
Inside the module, I can bind a provider to IConfigAccessor (with the above annotation) as such:
bind(IConfigAccessor.class).annotatedWith(InjectConfig.class)
.toProvider(new ConfigProvider<>());
However, there are two problems whith this:
The provider cannot provide IConfigAccessor. To create such an instance, the provider would need an IConfigUpdater, but since I use 'new' for the provider, I can't inject it.
Inside the provider, there is no way to find out about the configKey used in the Annotation.
Second approach:
Let's assume that I already know all configurations and configKeys I want to inject during startup. In this case, I could loop over all possible configKeys and have the following binding:
String configKey = "some key";
final Class<? extends IConfig> configClass =...;
bind(IConfigAccessor.class).annotatedWith(Names.named(configKey))
.toProvider(new ConfigProvider<>(configKey, configClass));
However, problem (1) still resides: The provider cannot get an IConfigUpdater instance.
The main problem here is that you cannot use the value of the annotation in the injection. There is another question which covers this part:
Guice inject based on annotation value
Instead of binding a provider instance, you should bind the provider class, and get the class by injecting a typeliteral.
That way, your config factory can look like that:
public class ConfigFactory<T extends IConfig> implements IConfigFactory {
#Inject private final IConfigUpdater updater;
#Inject private TypeLiteral<T> type;
#Override
public IConfigAccessor<T> register(final String configKey) {
Class<T> configClass = (Class<T>)type.getRawType();
ConfigCache<T> configCache = new ConfigCache<>(new SomeOtherThings(), configKey, configClass);
updater.register(configCache);
return new ConfigAccessor<>(configCache, configKey, configClass);
}
}
And then SomeClass:
public class SomeClass {
#Inject
public SomeClass(ConfigFactory<ConcreteConfig> accessor) {
ConcreteConfig config = accessor.register("key");
}
}
Since SomeClass needs to know "key" anyway, this is not too much a change information-wise. The downside is that the SomeClass API now gets a factory instead of the concrete config.
[EDIT]
And here is someone who actually did inject annotated values using custom injection.
I have List of Objects that I need to run some validation on
#KeyValid
#Valid
protected List<KeyValue> keyValues;
and I have a the following annotation created for it:
#Target({ElementType.TYPE, ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = KeyValidator.class)
public #interface KeyValid{
String message() default "invalid_parameter_default_message";
String[] checks() default {};
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
And this is my validator:
public class KeyValidator implements ConstraintValidator<KeyValid, KeyValue> {
#Override
public void initialize(KeyValid keyValid) {
}
#Override
public boolean isValid(KeyValue keyValue, ConstraintValidatorContext constraintValidatorContext) {
return true;
}
}
I had read somewhere that collections can be validated in bulk if the list or map or set is annotated by custom constraint then all of the elements of the collection call the validator but the above code throws the following error
javax.validation.UnexpectedTypeException: HV000030: No validator could be found for constraint 'com.util.Validators.KeyValid' validating type 'java.util.List<com.model.KeyValue>'. Check configuration for 'keyValue'
Your constraint would get the actual List passed not its elements. If you are using Java 8 and the latest version of Hibernate Validator, you can use type argument constraints. You just have to make sure to also add ElementType.TYPE_USE to #Target in your constraint. Type argument constraints are not yet official part of Bean Validation, but will be in the next version of it (BV 2.0).
You would have something like this:
protected List<#KeyValid KeyValue> keyValues;
Alternatively, could you not put the #KeyValid constraint as class level constraint on KeyValue?
I'm working with bean validations and I'm searching for a possibility to set a default group of my own bean validation annotation.
I have something (working) like this:
Application.class (calling validate on MyBean)
public class Application {
public static void main(String[] args) {
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Set<ConstraintViolation<MyBean>> violations =
validator.validate(new MyBean(), SecondStep.class);
}
}
MyBean.class (the bean itself; here is what I want to prevent)
public class MyBean {
// I don't want to write this "groups" attribute every time, because it's very clear,
// that this should only be validated for the second step, isn't it?
#RequiredBySecondStep(groups=SecondStep.class)
private Object myField;
}
RequiredBySecondStep.class (the bean validation annotation)
#Documented
#Target(FIELD)
#Retention(RUNTIME)
#Constraint(validatedBy = RequiredBySecondStepValidator.class)
public #interface RequiredBySecondStep {
String message() default "may not be null on the second step";
Class<?>[] groups() default {}; // <-- here I want to set SecondStep.class
Class<? extends Payload>[] payload() default {};
}
RequiredBySecondStepValidator.class (an implemented constraint validator)
public class RequiredBySecondStepValidator implements ConstraintValidator<RequiredBySecondStep, Object> {
public void initialize(RequiredBySecondStep constraintAnnotation) {
}
public boolean isValid(Object object, ConstraintValidatorContext constraintContext) {
return object != null;
}
}
SecondStep.class (the bean validation group)
public interface SecondStep {
}
Unfortunately, it's not possible by specification, to set the default group in the RequiredBySecondStep annotation like this:
Class<?>[] groups() default SecondStep.class;
// and using just the following in the bean:
#RequiredBySecondStep
private Object myField;
This will result in a RuntimeException:
javax.validation.ConstraintDefinitionException: Default value for
groups() must be an empty array
Furthermore, there is not only a SecondStep. There are probably 5 different groups which I want to annotate directly with a #RequiredByFirstStep or #RequiredByFifthStep.
Is there a good way to implement this?
I think you got it all a bit wrong. There is indeed to way to do what you want and that's because the aspect of constraints and their validation via a ConstraintValidator is orthogonal to groups and groups sequences. Per design a constraint (annotation and its validator) should be independent of the group getting validated. Even if you would get this to work, it would not be portable constraints. Personally, I would re-think what you want to achieve. #RequiredByFirstStep does not tell you what the requirement is. You should develop constraints which imply what they are valiating (a string length, not null, etc), when or better in which condition they are executed is a then controlled by group interfaces.
I have a list of lets say Car objects. Each car has a miles member.
I need to validate (using Hibernate Validator) that at least one car in my list has a not null miles member. The best solution would be an annotation that would apply to all the elements of a collection, but would validate in the context of the whole collection.
I have two questions:
Is there already an annotation for this (I do not know of any)?
If there is no annotation for this, is there a way to create an generic annotation?
I thought of specifiyng the name of the field that has to be not null at least for one element in the list, then I can apply this not only for Car classes.
public class VechicleTransport {
#AtLeastOneNotNull( fieldName = "miles" )
private List<Car> carList;
}
public class Car {
private Double miles;
....
}
AFAIK there is no such annotation, You need to define custom constraint annotations and define your validation logic inside it.
Like
Defining custom constraint annotation AtLeastOneNotNull
#Target({ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Constraint(validatedBy=AtLeastOneNotNullValidator.class)
public #interface AtLeastOneNotNull{
String message() default "Your error message";
Class<!--?-->[] groups() default {};
Class<!--? extends Payload-->[] payload() default {};
}
Defining validator for custom annotation.
public class AtLeastOneNotNullValidator implements ConstraintValidator<AtLeastOneNotNull, object=""> {
#Override
public void initialize(AtLeastOneNotNull constraint) {
}
#Override
public boolean isValid(Object target, ConstraintValidatorContext context) {
// Add logic to check if atleast one element have one field
}
}
Link for more details
I need to create a custom constraint annotation which can access the value of another field of my bean. I'll use this annotation to validate the field because it depends on the value of the other but the way I define it the compiler says "The value for annotation attribute" of my field "must be a constant expression".
I've defined it in this way:
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy=EqualsFieldValidator.class)
#Documented
public #interface EqualsField {
public String field();
String message() default "{com.myCom.annotations.EqualsField.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class EqualsFieldValidator implements ConstraintValidator<EqualsField, String>{
private EqualsField equalsField;
#Override
public void initialize(EqualsField equalsField) {
this.equalsField = equalsField;
}
#Override
public boolean isValid(String thisField, ConstraintValidatorContext arg1) {
//my validation
}
}
and in my bean I want something like this:
public class MyBean{
private String field1;
#EqualsField(field=field1)
private String field2;
}
Is there any way to define the annotation so the field value can be a variable?
Thanks
The easiest thing to do is take one step back: the constraint/validator you have written works on a field-level, but what you want to enforce is a cross-field dependency i.e. a class-level constraint.
Rewrite your constraint and validator to work at the class level (i.e. the annotation will go on the class, not on the field). That way you'll get access to the entire class. In your isValid(..) method, simply do a get on both the fields, compare, and return appropriately.
As the compiler said annotations must be constant (i.e. you can determine the value at compile time.) Now If I'm guessing correctly it looks like you are using this annotation to denote that the values of those fields should be equal when run through the equals field validator. One approach you could take is using reflection. Instead of trying to annotate with the value, annotate with the field name instead
public class MyBean{
private String field1;
#EqualsField("field1")
private String field2;
}
Then in your validator you can read the name of the field and use reflection to access it
Object o = object.getClass().getDeclaredField(annotationValue).get(object);
o == object.(field with annotation) OR
o.equals(object.(field with annotation));
Depending on what you are trying to do you may need to add in logic based on the field type, but still the same general principle.
Check out this previous question, has multiple solutions for cross-field validation: Cross field validation with Hibernate Validator (JSR 303)