Dynamic expression variables for validation messages in Spring MessageSource - java

I am using Hibernate Validator and a MessageSource to localize violation messages.
class FooValidator implements ConstraintValidator<ValidFoo, Foo>
#Override
public void initialize(ValidTransitions constraintAnnotation) {
}
#Override
public boolean isValid(Foo foo, ConstraintValidatorContext context) {
//some calculations
String result = calculations.toString();
HibernateConstraintValidatorContext hibernateContext = context.unwrap(HibernateConstraintValidatorContext.class);
hibernateContext.disableDefaultConstraintViolation();
hibernateContext.addExpressionVariable("invalidResult", result)
.buildConstraintViolationWithTemplate("Invalid field: ${invalidResult}")
.addConstraintViolation();
return false;
}
}
I want to have something like this in messages.properties:
ValidFoo.foo=Localized invalid field message, field: ${invalidResult}
But I can't find a way to inject dynamic parameters from my FooValidator to message property.

Named parameters and expression variables can be resolved only by Hibernate.
Spring uses MessageFormat.
See AbstractMessageSource.getMessageInternal for details.

Related

Access annotation attributes from custom oval annotation

Is it possible, when using custom oval annotation and custom class for check, to access the annotation and retrieve the used annotation attributes ?
Reference for oval: https://sebthom.github.io/oval/USERGUIDE.html#custom-constraint-annotations
Minimal example
Lets assume we have class Foo.
It has two annotated fields.
Each time, the annotation has a different myValue – a and b.
class Foo {
#CustomAnnotation(myValue = "a")
public String first;
#CustomAnnotation(myValue = "b")
public String second;
}
This is the annotation.
It is noted that a check should be performed using MyCheck.class, also setting some default value for myValue.
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
#Constraint(checkWith = MyCheck.class)
public #interface CustomAnnotation {
String myValue() default "";
}
Now we want to use oval to validate this field.
Most importantly, we want to extract the value a or b from the annotation's myValue and use it inside our validation logic.
public class MyCheck extends AbstractAnnotationCheck<CustomAnnotation> {
#Override
public boolean isSatisfied(Object validatedObject, Object valueToValidate, OValContext context,
Validator validator) throws OValException {
// how to get the value of `myValue`, which is `a` or `b` or empty string as default
}
}
What I have tried and failed:
validatedObject is Foo.class. You can easily get its fields and annotations. However, there is no way to differentiate between the two annotations.
valueToValidate is in this case String value – what first or second holds.
context not useful, you can get compile time type from it, which is String
validator not useful ?
After some digging in the superclass I have found that you can override method
configure
This method gets as the only parameter the annotation that is currently being checked at the field.
You can then read the myValue.
public class MyCheck extends AbstractAnnotationCheck<CustomAnnotation> {
private String myValue;
#Override
public void configure(CustomAnnotation customAnnotation) {
super.configure(customAnnotation);
this.myValue = customAnnotation.myValue();
}
#Override
public boolean isSatisfied(Object validatedObject, Object valueToValidate, OValContext context,
Validator validator) throws OValException {
if (myValue.equals("a")) {}
else if (myValue.equals("b")){}
else {}
}

Custom PropertyPlaceholderConfigurer doesn't resolve embedded property values

I have a Spring 4.3 custom PropertyPlaceholderConfigurer that does extra processing on values read from the properties files before injection:
public class MyPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer{
#Override
protected String convertPropertyValue(final String originalValue) { ... }
#Override
protected String resolveSystemProperty(final String key) {
return convertPropertyValue(super.resolveSystemProperty(key));
}
}
My problem is that bean fields that are annotated for injection with property values, eg. #Value("${some_property}"), are in fact injected with the raw value read from the property source without my custom processing.
After some debugging I found that I should have also overiden com.ec.survey.security.SafePasswordPropertyPlaceholderConfigurer#resolvePlaceholder

Can a CDI Bean's method return a managed Bean it created?

i have the following setup:
#Applicationscoped
#Transactional(txtype.Requires_new)
Public class querybean {
#Inject ExternalSysrltem externalSystemProxy;
Public Handle gethandleByKey(String key) {
return new Handle(/*do external Systems Query, returns an ExternalHandle Object*/)
}
Public static class Handle {
ExternalHandle eh;
/*protected so that User of class cannot Instantiate it otherwise that by getHandleByKey()*/
Protected Handle(ExternalHandle arg) {
This.eh = arg;
}
Public String getHandleInfo() {
Return This.eh.getName() + "/" + this.eh.getState()..;
/*generally wrap the ExternallHandle with businesslogic to hide direct access to the complex ExternalService's Interface*/
}
}
}
Can I get Handle to be a Managed Bean that can be annotated with #Transactional and still create it in the getHandleByKey Method at Runtime by querying the external System?
A static inner class can be a bean according the the spec.
In your example it is not a bean due to its constructor.
As said in comments you could use a producer, but a produced bean can't be intercepted (with #Transaction here)
If you want to keep your pattern, you'll have to create a very complex extension since it should work at low level to ensure interceptor will be activated.
I suggest that you go for something simpler by deporting your ExternalHandle resolution in Handle Bean, allowing you to use a String to construct it.
First create a qualifier with a non binding member to transmit information to your constructor.
#Target({TYPE, METHOD, PARAMETER, FIELD})
#Retention(RUNTIME)
#Documented
#Qualifier
public #interface Keyed {
#Nonbinding
String key();
}
Then create a literal for your annotation to allow creation of an annotation instance with a given key value.
public class KeyedLiteral extends AnnotationLiteral<Keyed> implements Keyed {
private final String key;
public KeyedLiteral(String key) {
this.key = key;
}
#Override
public String key() {
return key;
}
}
Using programmatic lookup and InjectionPoint to transmit your key value. Your code will be like:
#Applicationscoped
#Transactional(txtype.Requires_new)
Public class querybean {
#Inject
#Any
Instance<Handle> handles;
Public Handle gethandleByKey(String key) {
return instances.select(new KeyedLiteral(key)).get()
}
#Dependent
#Transactional
#Keyed("") //enforce the presence of the annotation for the constructor
Public static class Handle {
ExternalHandle eh;
// needed to make the bean proxyable (mandatory for the interceptor bound))
Protected Handle() {}
#Inject
Protected Handle(InjectionPoint ip, ExternalSysrltem externalSystem) {
String key=ip.getAnnotated().getAnnotation(Keyed.class).key();
eh = /*do external Systems Query, returns an ExternalHandle Object from key and externalSystem*/
}
Public String getHandleInfo() {
Return This.eh.getName() + "/" + this.eh.getState()..;
/*generally wrap the ExternallHandle with businesslogic to hide direct access to the complex ExternalService's Interface*/
}
}
}

How customize spring validator?

I want to write custom validator with complex logic that depends on some properties that must be set in starup. It is possible to write component-scan tag in spring configuration file, but how to set other properties in class or spring validater are used as stateless classess?
public class CustomValidator implements Validator{
private Map<String,Integer> parameters;
public boolean supports(Class clazz) {
return ObjectToValidate.class.equals(clazz);
}
public void validate(Object obj, Errors e) {
ObjectToValidate object = (ObjectToValidate) obj;
switch (parameters.get(object.getIntegerProperty())) {
case 1:
//validation algorithm 1;
break;
case 2:
//validation algorithm 2;
break;
//etc.
}
}
public void setParameters(Map<String,Integer> parameters){
this.parameters = parameters;
}
}
You should set up in your app context a org.springframework.validation.beanvalidation.LocalValidatorFactoryBean.
It will inject your validators with every #Autowired property they need. If you don't want to use autowiring, you should add them to your app context as beans.
Give a look here: http://static.springsource.org/spring/docs/3.0.0.RC3/reference/html/ch05s07.html
Everything is explained.
Stefano

How can I validate a field as required depending on another field's value in SEAM?

I'm trying to create a simple custom validator for my project, and I can't seem to find a way of getting seam to validate things conditionally.
Here's what I've got:
A helper/backing bean (that is NOT an entity)
#RequiredIfSelected
public class AdSiteHelper {
private Date start;
private Date end;
private boolean selected;
/* getters and setters implied */
}
What I need is for "start" and "end" to be required if and only if selected is true.
I tried creating a custom validator at the TYPE target, but seam doesn't seem to want to pick it up and validate it. (Maybe because it's not an entity?)
here's the general idea of my custom annotation for starters:
#ValidatorClass(RequiredIfSelectedValidator.class)
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
public #interface RequiredIfSelected {
String message();
}
public class RequiredIfSelectedValidator implements Validator<RequiredIfSelected>, Serializable {
public boolean isValid(Object value) {
AdSiteHelper ash = (AdSiteHelper) value;
return !ash.isSelected() || (ash.getStart() && ash.getEnd());
}
public void initialize(RequiredIfSelected parameters) { }
}
I had a similar problem covered by this post. If your Bean holding these values is always the same then you could just load the current instance of it into your Validator with
//Assuming you have the #Name annotation populated on your Bean and a Scope of CONVERSATION or higher
AdSiteHelper helper = (AdSiteHelper)Component.getInstance("adSiteHelper");
Also as you're using Seam your validators don't need to be so complex. You don't need an interface and it can be as simple as
#Name("requiredIfSelectedValidator")
#Validator
public class RequiredIfSelectedValidator implements javax.faces.validator.Validator {
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
//do stuff
}
}

Categories