Java annotations: pass value of annotation attribute to another annotation - java

I have interface Resource and several classes implementing it, for example Audio, Video... Further, I have created custom annotation MyAnnotation with Class type param:
#MyAnnotation(type = Audio.class)
class Audio {
...
}
#MyAnnotation(type = Video.class)
class Video{
...
}
In some other place in code I have to use Interface Resource as a returned type:
public class Operations<T extends Resource> {
....
#OtherAnnotation(type = Audio.class (if audio), type = Video.class (if video) )
T getResource();
....
}
The question is how to appropriatelly annotate annotation #OtherAnnotation depending of what kind of Resource type will be returned ?

What you are asking is for dynamic values for annotation attributes.
However annotations can only be set at compile time which is the reason why their values can only be compile time constants. You may only read them at runtime.
There was a similar question in which someone tried to generate the annotation value , it's answer explains why there is no way to dynamically generate a value used in annotation in a bit more detail. In that question there was an attempt to use a final class variable generated with a static method.
There are annotation processors which offer a bit more flexibility by handling placeholders. However i don't think this fits your case, as you want the dynamic values at runtime.
This answer refers to spring's use of the expression language for the Value annotation in which the placeholder (#Value("#{systemProperties.dbName})") gets overrided with the data from one of the property sources defined ( example in spring boot )
In any case, you will have to rethink your architecture a bit.

Related

Class parameter name for default type in spring-kafka listener

I'm attempting to change the default type value for kafka listener with property "spring.json.value.default.type=" using my own annotation in spring-kafka. Currently, it's possible to overwrite it with following values:
properties="spring.json.value.default.type=com.package.class" which is canonical name of class.
I've made an annotation that sets the following value:
#MyAnnotation(topic = Topics.BUILD_CONFIG_CREATED, defaultType = ConstantsClass.TYPE_HEADER + "prz.student.finger.kafkaBSC.MyObjectDTO")
Is there any way to avoid hard typing the class name?
I would like to implement the option to use the following code(just giving the class that was imported):
#MyAnnotation(topic = Topics.BUILD_CONFIG_CREATED, defaultType = MyObjectDTO.class)
The closest to I've got is adding in my annotation:
#AliasFor(annotation = KafkaListener.class, attribute = "properties")
String defaultType() default headerType()+dtoType().getCanonicalName().toString();
String headerType() default "spring.json.value.default.type=";
Unfortunately, the constraints regarding the compilation time values for class in annotation blocks me from implementing it. Is there any way to inject the cannonical name without hard typing it, or any other way to implement this?
The properties property can contain SpEL (see its Javadocs).
Something like #{#someBean.type.name}; where someBean is a bean with a method public Class<?> getType().

How can I make fields in Object as required or do field validation?

Let's assume I have a class created by using jsonschemapojo2 as so
public class a{
private String b;
public void setB(String b){this.b = b}
public void getB(){return b}
}
I used this class to parse a result from mongodb into an object:
a = {b : "1"}
The new requirement I have for my application is that an exception must be thrown when the field b is null, empty string or it's not present in the object. I can't use annotations at the class level as the class is created from the jsonschemapojo2. Is there any way to specify the required field annotation at the schema level?. Otherwise how would I create a proxy method that validates the object? should I use reflection or is there a smarter way to do it in springboot?
Yes, bean validation allows to define the constraints in xml file. You can find many examples at Hibernate website.
You can use either one from bellow options to define the constraints -
i) By the programmatic API More Details
ii) By XML : More details
You can’t enable or disable constraints therefore you would have to redefine the metadata at every runtime, which is not a good idea.

Creating and using annotation in java

I am reviewing open source spring projects. I am confused about the use of annotations around here. I want to ask to clarify this.
#Target(ElementType.METHOD)
#Retention(RUNTIME)
#Bean
public #interface Merge {
#AliasFor("targetRef")
String value() default "";
#AliasFor("value")
String targetRef() default "";
Placement placement() default Placement.APPEND;
int position() default 0;
Class<MergeBeanStatusProvider> statusProvider() default MergeBeanStatusProvider.class;
boolean early() default false;
}
An annotation has been created here named Merge. It has different parameters and default values.
#Configuration
public class LocalConfiguration {
#Merge(targetRef = "mergedList", early = true)
public List<String> blLocalMerge() {
return Arrays.asList("local-config1", "local-config2");
}
}
And this is usage of #Merge annotation in any class I choosed randomly.
When I examined the code, I could not find any class related to the implementation of Merge annotation. By the way, this problem I'm having isn't just about this annotation. Almost all the annotations I have examined are used without being implemented in any way.
I think I will understand the others if we start from this annotation.
What does this anotation do? What kind of message does it give to the place where it is used. How does the application understand what that annotation does in runtime without being implemented anywhere.
Thanks.
Annotations don't have implementations. They are processed by external classes or tools depending on the RetentionPolicy. In this case, the Merge annotation has Runtime retention so it will be available via reflection once the class is loaded. At runtime any interested party (in this case I assume the Spring Framework) can use getAnnotations on your LocalConfiguration class to detect the Merge annotation and take whatever action that needs to be taken. The possibilities are really up to the framework that defined the annotation. A lot of Spring injection works like this with annotations but they are also used by many other frameworks such as Hibernate, Jersey, etc. The main idea is that annotations act as markers on specific code points to be used by an external entity at a later point.

Java Hibernate field access property or property based access

i am developing some improvements over a legacy system have some quite time
i have a class like this
class MyPersistentClazz
{
private String aTPlace;
public void setATPlace(.......){......}//yes mistyping
#Column(name="atPlaceOrder")
public String getATPlace(){return aTPlace;}
}
they usually load this class using this methods
final MyPersistentClazz clazz = (MyPersistentClazz)session.createCriteria(MyPersistentClazz.class).add(idEq(id)).uniqueResult();
and using load and get methods and works OK.
but the problem arise when i use projections.
final Projection p=Projections.projectionList().add(Projections.property("d.aTPlace"),"aTPlace");
throws
Exception in thread "main" org.hibernate.QueryException: could not resolve property:
my question is...
when using projections i think Hibernate is calling the setter of each property is this assertion OK?
when using criteria.uniqueResult or load or get Hibernate use individual field property access?
or why works with some and not work with others with the same setter?
we are using only annotations not XML.
thanks a lot.
How Hibernate works with your bean depends on how you annotated it. If you annotate instance variables then Hibernate will use direct injection and bypass your Set methods. Otherwise, it will use your Set methods.
Could it be that it is incorrectly converting your property name to a Set method name? Try changing the property name to something simpler (without that series of capital letters), and ensure that the case of the property in your projection is correct.

Java Annotations values provided in dynamic manner

I want to provide annotations with some values generated by some methods.
I tried this so far:
public #interface MyInterface {
String aString();
}
#MyInterface(aString = MyClass.GENERIC_GENERATED_NAME)
public class MyClass {
static final String GENERIC_GENERATED_NAME = MyClass.generateName(MyClass.class);
public static final String generateName(final Class<?> c) {
return c.getClass().getName();
}
}
Thought GENERIC_GENERATED_NAME is static final, it complains that
The value for annotation attribute MyInterface.aString must be a constant expression
So how to achieve this ?
There is no way to dynamically generate a string used in an annotation. The compiler evaluates annotation metadata for RetentionPolicy.RUNTIME annotations at compile time, but GENERIC_GENERATED_NAME isn't known until runtime. And you can't use generated values for annotations that are RetentionPolicy.SOURCE because they are discarded after compile time, so those generated values would never be known.
The solution is to use an annotated method instead. Call that method (with reflection) to get the dynamic value.
From the user's perspective we'd have:
#MyInterface
public class MyClass {
#MyName
public String generateName() {
return MyClass.class.getName();
}
}
The annotation itself would be defined as
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface #MyName {
}
Implementing the lookup for both of these annotations is rather straight-forward.
// as looked up by #MyInterface
Class<?> clazz;
Method[] methods = clazz.getDeclaredMethods();
if (methods.length != 1) {
// error
}
Method method = methods[0];
if (!method.isAnnotationPresent(MyName.class)) {
// error as well
}
// This works if the class has a public empty constructor
// (otherwise, get constructor & use setAccessible(true))
Object instance = clazz.newInstance();
// the dynamic value is here:
String name = (String) method.invoke(instance);
There is no way to modify the properties of an annotation dynamically like others said. Still if you want to achieve that, there are two ways to do this.
Assign an expression to the property in the annotation and process that expression whenever you retrieve the annotation. In your case your annotation can be
#MyInterface(aString = "objectA.doSomething(args1, args2)")
When you read that, you can process the string and make the method invocation and retrieve the value. Spring does that by SPEL (Spring expression language). This is resource intensive and the cpu cycles are wasted every time we want to process the expression. If you are using spring, you can hook in a beanPostProcessor and process the expression once and store the result somewhere. (Either a global properties object or in a map which can be retrieved anywhere).
This is a hacky way of doing what we want. Java stores a private variable which maintains a map of annotations on the class/field/method. You can use reflection and get hold of that map. So while processing the annotation for the first time, we resolve the expression and find the actual value. Then we create an annotation object of the required type. We can put the newly created annotation with the actual value (which is constant) on the property of the annotation and override the actual annotation in the retrieved map.
The way jdk stores the annotation map is java version dependent and is not reliable since it is not exposed for use (it is private).
You can find a reference implementation here.
https://rationaleemotions.wordpress.com/2016/05/27/changing-annotation-values-at-runtime/
P.S: I haven't tried and tested the second method.

Categories