Passing dynamic parameters to an annotation - java

I wonder if there is a possiblity to pass dynamically values to an annotation attribute.
I know that annotation are not designed to be modified but I'm using Hibernate Filters and condition to be put are not static in my case.
I think that the only solution is to use librairies whose aim is to read and modify byte code such as Javassist or ASM but it would be too much better if there is another solution.
ps: The difficulty in my case is that I should modify annotations (attribute's value) but librairies I mentioned above allow to create not to edit that's why I'm wondering for another solution
Thanks in advance

I don't know if it integrates nicely with your frameworks, but i would like to suggest the following:
Create an annotation which receives a Class that implements the validation rule
Create an interface which the annotation can receive
Create an implementation for the interface which has the logic for your rule
Add the annotations to your model class
Create an annotation processor which applies the validation for each annotated field
I wrote the following example in Groovy, but using standard Java libs and idiomatic Java. Warn me if anything is unreadable:
import java.lang.annotation.*
// Our Rule interface
interface Rule<T> { boolean isValid(T t) }
// Here is the annotation which can receive a Rule class
#Retention(RetentionPolicy.RUNTIME)
#interface Validation { Class<? extends Rule> value() }
// An implementation of our Rule, in this case, for a Person's name
class NameRule implements Rule<Person> {
PersonDAO dao = new PersonDAO()
boolean isValid(Person person) {
Integer mode = dao.getNameValidationMode()
if (mode == 1) { // Don't hardcode numbers; use enums
return person.name ==~ "[A-Z]{1}[a-z ]{2,25}" // regex matching
} else if (mode == 2) {
return person.name ==~ "[a-zA-Z]{1,25}"
}
}
}
After these declarations, the usage:
// Our model with an annotated field
class Person {
#Validation(NameRule.class)
String name
}
// Here we are mocking a database select to get the rule save in the database
// Don't use hardcoded numbers, stick to a enum or anything else
class PersonDAO { Integer getNameValidationMode() { return 1 } }
The processing of the annotations:
// Here we get each annotation and process it against the object
class AnnotationProcessor {
String validate(Person person) {
def annotatedFields = person.class.declaredFields.findAll { it.annotations.size() > 0 }
for (field in annotatedFields) {
for (annotation in field.annotations) {
Rule rule = annotation.value().newInstance()
if (! rule.isValid(person)) {
return "Error: name is not valid"
}
else {
return "Valid"
}
}
}
}
}
And tests:
// These two must pass
assert new AnnotationProcessor().validate(
new Person(name: "spongebob squarepants") ) == "Error: name is not valid"
assert new AnnotationProcessor().validate(
new Person(name: "John doe") ) == "Valid"
Also, take a look at GContracts, it provides some interesting validation-through-annotations model.

Annotation parameters are hard coded constants in the classfile. So the only way to change them is to generate a new classfile.
Unfortunately, I'm not familiar with Hibernate, so I can't suggest the best option in your specific case.

Related

jqwik strategies for combining arbitraries

(Context: my background in property-based testing is mostly from scala's scalacheck library, the use of some of the types and annotations in jqwik feels a bit different and there are a couple paradigms I can't quite get the hang of yet.)
I'm not sure how best to combine existing Arbitrary definitions for primitive types to produce a re-usable (needed for more than a single #Property or test class) Arbitrary that is built atop other Aribtrary definitions I've defined.
Given this is probably much clearer with an illustration:
// assume this has a builder pattern or all-args constructor.
// consider this is some sort of core domain object that I need in various
// test suites
public class MyComplexClass {
private final String id; // positive-integer shaped
private final String recordId; // uuid-shaped
private final String creatorId; // positive-integer shaped
private final String editorId; // positive-integer shaped
private final String nonce; // uuid-shaped
private final String payload; // random string
}
My instinct is to define Aribrary<String> that produces UUID-like strings and another that produces positive integer strings, something like:
public class MyArbitraries {
public Arbitrary<String> arbUuidString() {
return Combinators.combine(
Arbitraries.longs(), Arbitraries.longs(), Arbitraries.of(Set.of('8', '9', 'a', 'b')))
.as((l1, l2, y) -> {
StringBuilder b = new StringBuilder(new UUID(l1, l2).toString());
b.setCharAt(14, '4');
b.setCharAt(19, y);
return UUID.fromString(b.toString());
});
}
public Arbitrary<String> arbNumericIdString() {
return Arbitraries.shorts().map(Math::abs).map(i -> "" + i);
}
}
But then I'm not sure the best way to utilize these to produce an Arbitrary< MyComplexClass>. I'd want something like:
public class MyDomain extends DomainContextBase {
#Provider
public Arbitrary<MyComplexClass> arbMyComplexClass() {
return Builders.withBuilder(MyComplexClass::newBuilder)
// best way to reference these?!
.use(arbNumericIdString()).in(MyComplexClass.Builder::setId)
.use(arbUuidString()).in(MyComplexClass.Builder::setCreatorId)
// etc.
.build(MyComplexClass.Builder::build);
}
}
My understanding here is:
I cannot use #ForAll to 'inject' or provide these Arbitraries as ForAll is only supported in #Property-annotated methods
I cannot use #Domain here for similar reasons
I can't really use ArbitrarySupplier or similar as there is no obvious 'type' here, it's mostly just a bunch of strings
Is the best option to just create static Arbitrary<String> functions and call them directly?
One initial comment: #ForAll also works in methods annotated with #Provide and in domains. Here's a simple example:
class MyDomain extends DomainContextBase {
#Provide
public Arbitrary<String> strings(#ForAll("lengths") int length) {
return Arbitraries.strings().alpha().ofLength(length);
}
#Provide
public Arbitrary<Integer> lengths() {
return Arbitraries.integers().between(3, 10);
}
// Will not be used by strings() method
#Provide
public Arbitrary<Integer> negatives() {
return Arbitraries.integers().between(-100, -10);
}
}
class MyProperties {
#Property(tries = 101)
#Domain(MyDomain.class)
public void printOutAlphaStringsWithLength3to10(#ForAll String stringsFromDomain) {
System.out.println(stringsFromDomain);
}
}
Maybe the confusing thing is that the string reference in #ForAll("myString") is only evaluated locally (the class itself, superclasses and containing classes).
This is by purpose, in order to prevent string-based reference magic;
having to fall back to strings in the first place - since method refs cannot be used in Java annotations - is already bad enough.
As for your concrete question:
Is the best option to just create static Arbitrary functions and call them directly?
I consider that a "good enough" approach for sharing generators within a single domain or when you have several related domains that inherit from a common superclass.
When you want to share generators across unrelated domains, you'll have to either:
Use type-based resolution: Introduce value types for things like RecordId, UUIDString etc. Then you can use domains (or registered ArbitraryProviders to generate based on type.
Introduce annotations to mark different variants of the same type.
You can then check the annotation in your provider method or arbitrary provider. Here's an example:
class MyNumbers extends DomainContextBase {
#Provide
Arbitrary<Integer> numbers() {
return Arbitraries.integers().between(0, 255);
}
}
#Domain(MyNumbers.class)
class MyDomain extends DomainContextBase {
#Target({ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
#interface Name {}
#Target({ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
#interface HexNumber {}
#Provide
public Arbitrary<String> names(TypeUsage targetType) {
if (targetType.isAnnotated(Name.class)) {
return Arbitraries.strings().alpha().ofLength(5);
}
return null;
}
#Provide
public Arbitrary<String> numbers(TypeUsage targetType) {
if (targetType.isAnnotated(HexNumber.class)) {
return Arbitraries.defaultFor(Integer.class).map(Integer::toHexString);
}
return null;
}
}
#Property(tries = 101)
#Domain(MyDomain.class)
public void generateNamesAndHexNumbers(
#ForAll #MyDomain.Name String aName,
#ForAll #MyDomain.HexNumber String aHexNumber
) {
System.out.println(aName);
System.out.println(aHexNumber);
}
This examples also shows how one domain (MyNumbers) can be used
in another domain (MyDomain) through annotating the domain implementation class and either having a parameter being injected or use
Arbitraries.defaultFor(TypeProvidedInOtherDomain.class).
But maybe there's a useful feature for sharing arbitraries missing in jqwik.
The jqwik team's happy about any suggestion.

Custom setter to be used in MapStruct

I have been looking through the MapStruct documentation without any success.
I am implementing a mapping between my Domain classes and my DTO classes; using MapStruct. In my domain, I do not want to use Setters for my fields because, we know that today Setters are bad (for many reasons, but that's not the topic of my question).
However when I want to convert ItemDto into Item, I got the following message :
Error:(17, 21) java: Property "name" has no write accessor in my.example.Item.
However my class Item has a Business method void changeName(String newName) that I would like to use in my Mapper.
The code of my Mapper is :
#Mapper
public interface MyMapper {
#Mapping(source="nameDto", target = "name")
Item map(ItemDto dto);
}
My question is quite simple : how to specify StructMap to use changeName as write accessor ?
Thanks for your help.
In order to implement something like that you would have to write your own custom AccessorNamingStrategy.
If your domain objects follow the same pattern changeXXX then a simple implementation can look like:
public class CustomAccessorNamingStrategy extends DefaultAccessorNamingStrategy {
#Override
public boolean isSetterMethod(ExecutableElement method) {
String methodName = method.getSimpleName().toString();
return methodName.startsWith( "change" ) && methodName.length() > 6;
}
#Override
public String getPropertyName(ExecutableElement getterOrSetterMethod) {
String methodName = getterOrSetterMethod.getSimpleName().toString();
if ( methodName.startsWith( "change") {
return IntrospectorUtils.decapitalize( methodName.substring( 6 );
}
return super.getPropertyName( getterOrSetterMethod );
}
}
You can of course adapt the CustomAccessorNamingStrategy to fit your needs. Keep in mind that this would be used for all objects. Also the ItemDto.
More information about it can be found here in the MapStruct documentation.

How to properly override JacksonAnnotationIntrospector._findAnnotation to replace an annotation of the element

I am trying to create some classes serializable by Jackson. I want to annotate some elements with standard Jackson annotation (let's consider JsonIgnore for this example) but I want them to have effect only in my specific mapper.
So I decided to create my own annotations like standard ones (e. g. MyJsonIgnore) and process them only in an annotation introspector used by my mapper. I've found overridable method _findAnnotation. Javadoc says the following:
...overridable that sub-classes may, if they choose to,
mangle actual access block access ("hide" annotations)
or perhaps change it.
I've found a way to block some annotations (however it involves overriding _hasAnnotation, not _findAnnotation) but I am completely stuck with changing annotations.
Here is some minimal example of what I am trying to do:
object Mappers {
/**
* Same as JsonIgnore but mapper-specific
*/
annotation class MyJsonIgnore(val value: Boolean = true)
private class MyIntrospector : JacksonAnnotationIntrospector() {
override fun <A : Annotation> _findAnnotation(
annotated: Annotated,
annoClass: Class<A>
): A {
if (annoClass == JsonIgnore::class.java && _hasAnnotation(annotated, MyJsonIgnore::class.java)) {
val mji = _findAnnotation(annotated, MyJsonIgnore::class.java)
return JsonIgnore(mji.value) // Does not compile, type mismatch
// return JsonIgnore(mji.value) as A // Does not compile, annotation class cannot be instantiated, same as Java, see below
}
return super._findAnnotation(annotated, annoClass)
}
}
fun myMapper(): ObjectMapper {
return ObjectMapper().setAnnotationIntrospector(MyIntrospector())
}
}
I also cannot do it with Java:
public class Mappers {
/**
* Same as JsonIgnore but mapper-specific
*/
public #interface MyJsonIgnore {
boolean value() default true;
}
private static class MyIntrospector extends JacksonAnnotationIntrospector {
#Override
protected <A extends Annotation> A _findAnnotation(Annotated annotated,
Class<A> annoClass) {
if (annoClass == JsonIgnore.class && _hasAnnotation(annotated, MyJsonIgnore.class)) {
MyJsonIgnore mji = _findAnnotation(annotated, MyJsonIgnore.class);
return new JsonIgnore(mji.value()); // Does not compile, JsonIgnore is abstract
}
return super._findAnnotation(annotated, annoClass);
}
}
static ObjectMapper myMapper() {
return new ObjectMapper().setAnnotationIntrospector(new MyIntrospector())
}
}
So what is the supposed way to change annotations by overriding this method? Is there any? Is my approach right or should I do it other way?
The main problem here is that you can't instantiate the annotation class. There is one solution though: you could store one annotation as a member of another annotation like this:
#Retention(AnnotationRetention.RUNTIME) // don't forget
#Target(AnnotationTarget.FIELD) // these annotations
annotation class MyJsonIgnore(val value: Boolean = true, val jsonIgnore: JsonIgnore = JsonIgnore())
So MyJsonIgnore will have an instantiated JsonIgnore inside. And then you can use it in your AnnotationIntrospector:
private class MyIntrospector : JacksonAnnotationIntrospector() {
override fun <A : Annotation> _findAnnotation(
annotated: Annotated,
annoClass: Class<A>
): A? {
if (annoClass == JsonIgnore::class.java && _hasAnnotation(annotated, MyJsonIgnore::class.java)) {
val mji = _findAnnotation(annotated, MyJsonIgnore::class.java)
if (mji?.value == true) {
return mji.jsonIgnore as A // this cast should be safe because we have checked
// the annotation class
}
}
return super._findAnnotation(annotated, annoClass)
}
}
I've tested this with the following class
class Test {
#MyJsonIgnore
val ignoreMe = "IGNORE"
val field = "NOT IGNORE"
}
and method
fun main() {
println(Mappers.myMapper().writeValueAsString(Test()))
println(ObjectMapper().writeValueAsString(Test()))
}
and the output was
{"field":"NOT IGNORE"}
{"ignoreMe":"IGNORE","field":"NOT IGNORE"}
So here are my further thoughts. Kirill Simonov's answer is right and typesafe (an alternative would be to create an annotation instance using Kotlin reflection but it is not typesafe).
Here are some problems with my original code and thoughts about the original approach:
You should consistently override _hasAnnotation and _getAnnotation
You cannot be sure that _getAnnotation will be called after _hasAnnotation check. You cannot be sure which of them will be used to check your replaced annotation (#JsonIgnore in my case) without looking into JacksonAnnotationIntrospector code. It seems that overriding them consistently would be a good practice. So we should also add the following override to our class if we want to use this approach:
override fun <A : Annotation> _hasAnnotation(
annotated: Annotated,
annoClass: Class<A>
): Boolean {
if (annoClass == JsonIgnore::class.java && _hasAnnotation(annotated, MyJsonIgnore::class.java)) {
return true
}
return super._hasAnnotation(annotated, annoClass)
}
_getAnnotation return type should be nullable
This was correctly fixed by Kirill but was not explicitly pointed out. _getAnnotation can and will return null sometimes.
You (probably) cannot have one magic MyConditional annotation.
Kirill's answer may encourage you to create something like a conditional annotation for all the jackson annotation which could be used as follows:
#MyConditional([
JsonIgnore, JsonProperty("propertyName")
])
You just cannot have polymorphic annotation parameter. You would have to create My* for every Jackson annotation you need and for annotation with parameters it's not as neat as with #MyJsonIgnore.
You can try to make a repeatable annotation which would be applied like below and instantiated using reflection.
#MyConditional(
clazz = JsonProperty::class.java,
args = [
// Ah, you probably cannot have an array of any possible args here, forget it.
]
)
_hasAnnotation and _getAnnotation are not the only ways which JacksonAnnotationIntrospector uses to get or check for annotations
After using similar approach to create conditional #JsonProperty annotation I've noticed that it does not work for enum elements. After some debugging I found out that findEnumValues method uses java.lang.reflect.Field::getAnnotation directly (due to "various reasons" mentioned in deprecated findEnumValue). If you want your conditional annotation to work you should override (at least) findEnumValues.
Be careful with ObjectMapper::setAnnotationIntrospector
Well, its Javadoc explicitly says it: be careful. It replaces the whole annotation introspector of your mapper removing all the added (chained) by modules Introspectors. It did not appear in my code in the question (it was for the sake of creating minimal example) but actually I've accidentally broke deserialization with KotlinModule. You may want to consider implementing JacksonModule and appending your introspector to existing ones.
Consider another approach: implementing functionality-specific methods in NopAnnotationIntrospector.
At the end I ended up with this approach (mostly because of 4.). I needed to override findEnumValues and hasIgnoreMarker and it was enough for me. It involved a bit of copy-paste code from JacksonAnnotationMapper but unless you have to make a lot of the annotation conditional it might work (in any case implementing it involves a lot of boilerplate code). This way it's likely that you really want to chain this introspector, not to set it.

Dynamic access of method based on external field in Spring boot

We have multiple external variables in application.yml in spring boot application and i want to access this variable from my java code and based on the field value I want to redirect the call to various functions.
Example:
String externalVariable1 abc;
String externalVariable2 xyz;
method: if(string == abc) {
call function1; }
else {
call function2; }
Now problem here is there might be further addition to external variable in furture, I want to write robust method which should be adaptable to future addition to external variable without changing my core code. i might add the functionality as part of helper methods.
All I can think of reflection way, Can you guys help me with better approach given i am using spring boot application.
Don't do reflection for this. Instead, wrap function1/function2 into some kind of strategy object:
interface Strategy {
void doStuff();
}
class Function1 implements Strategy {
void doStuff() {
function1();
}
}
class Function2 implements Strategy {
void doStuff() {
function2();
}
}
Then, register all of these with some factory-style class:
class StrategyFactory {
private Strategy defaultStrategy = new Function2();
Map<String, Strategy> strategies = ....
strategies.put("abc", new Function1());
...
Strategy getStrategy(String key) {
return strategies.getOrDefault(key, defaultStrategy);
}
}
Finally, use it:
factory.getStrategy(valueFromYaml).doStuff();
Make key a more complex object than just String if you need to accommodate for more complicated scenarios, or use a more sophisticated way of selecting a strategy than a map lookup.
If you don't know the available strategies before runtime (e.g. if the configuration for these comes from a DB or files) keep only the class name of the Strategy implementation in a map:
Map<String, String> strategyClassNames = ...;
strategy.put(keyFromDB, valueFromDB);
...
and use it by:
Class<? extends Strategy> strategy = Class.forName(strategyClassNames.get(key));
strategy.newInstance().doStuff();

Shared methods between enums

I want to refactor an emun in two new enums, but I don't like to copy/paste the enum methods in all new enums.
enum EmailType {
REMINDER_ADMIN('reminderForAdmin')
REMINDER_PRODUCTION('reminderForProduction')
REMINDER_MANAGEMENT('reminderForManagement')
REMINDER_CUSTOMER('reminderForCustomer')
private final propertiesIdentifier
String getTemplate(type) {
...
}
String getFrom(type) {
...
}
String getTo(type) {
...
}
String getBcc(type) {
...
}
...
}
It's possible to implements only one time the methods and use in several enums?
enum EmailTypeAdministration {
REMINDER_ADMIN('reminderForAdmin')
REMINDER_PRODUCTION('reminderForProduction')
...
}
enum EmailTypeClients {
REMINDER_MANAGEMENT('reminderForManagement')
REMINDER_CUSTOMER('reminderForCustomer')
...
}
As my old friend Clippy would say, "it looks like you're using Groovy". If so, you can use a mixin to add the common methods to both enums.
// This class holds the methods that will be mixed-in to the enums
class EnumUtils {
String getTemplate(type) {
"template" + type
}
String getFrom(type) {
}
String getTo(type) {
}
String getBcc(type) {
}
}
// This annotation adds the common methods to the enum
#Mixin(EnumUtils)
enum EmailTypeAdministration {
REMINDER_ADMIN('reminderForAdmin'),
REMINDER_PRODUCTION('reminderForProduction')
EmailTypeAdministration(str) {}
}
// This annotation adds the common methods to the enum
#Mixin(EnumUtils)
enum EmailTypeClients {
REMINDER_MANAGEMENT('reminderForManagement'),
REMINDER_CUSTOMER('reminderForCustomer')
EmailTypeClients(str) {}
}
// Quick test to prove the methods exist and return the expected values
EmailTypeAdministration emailTypeAdmin = EmailTypeAdministration.REMINDER_ADMIN
assert 'templateParam' == emailTypeAdmin.getTemplate('Param')
You can run the code above in the Groovy console to prove it works as advertised
Enums cannot extend any other class since all enums automatically extend class named Enum. So, your only option is to delegate the methods implementation to separate utility. This may be relevant if the implementation is not trivial (more than one line). Otherwise delegation does not give you serious benefits.
Other possibility is to extend Enum manually but I be ready to write verbose code like valueOf(), values() etc., so I am not sure you really need this.
EDIT:
Take a look on my article about Hierarchical Enums. It can probably help you too.
Finally the Mixin solution don't works because #Mixin annotation only works with classes instead of enums.
I use a similar approach with delegate. Delegate transformation works fine! This code can be improve to use EnumUtils with factory or singleton pattern.
class EnumUtils {
String getTemplate(type) {
"template" + type
}
String getFrom(type) {
}
String getTo(type) {
}
String getBcc(type) {
}
}
enum EmailTypeAdministration {
REMINDER_ADMIN('reminderForAdmin'),
REMINDER_PRODUCTION('reminderForProduction')
#Delegate EnumUtils enumUtils = new EnumUtils()
EmailTypeAdministration(str) {}
}
enum EmailTypeClients {
REMINDER_MANAGEMENT('reminderForManagement'),
REMINDER_CUSTOMER('reminderForCustomer')
#Delegate EnumUtils enumUtils = new EnumUtils()
EmailTypeClients(str) {}
}
EmailTypeAdministration emailTypeAdmin = EmailTypeAdministration.REMINDER_ADMIN
assert 'templateParam' == emailTypeAdmin.getTemplate('Param')
The Enum type can't do that but you can use Groovy Mixins or a factory with an interface:
In the enums, just define the constants. All enums must implement a common marker interface.
Create a factory which accepts the marker interface and contains the getters.
The factory approach allows you to move the configuration (like templates, email addresses) into a config file which the factory reads at startup.
Lesson: Don't put configuration into enums. Enums are constants. Configuration changes.

Categories