Java 8 Repeatable annotation - java

Trying to figure out how to get along with Java 8 Repeatable annotations support.
Following :
https://blog.idrsolutions.com/2015/03/java-8-repeating-annotation-explained-in-5-minutes/
it works flawlessly.
But if I'm modifying the example and add just a #Manufacturer on the Car class I'm unable to read that single annotation. So if there's only 1 occurrence of the repeated annotation it can't be read.
So having:
#Manufacturer("Range Rover")
public class Car {
}
Manufacturer[] a = Car.class.getAnnotationsByType(Manufacturer.class );
the size here will be 0
and
Cars cars = Car.class.getAnnotation(Cars.class);
for(Manufacturer car: cars.value())
System.out.println(car.value());
here there will be a NPE on the cars..
Why's that ?

You try to get an annotation of type Cars and not of type Manufacturer.
The following solution worked:
Manufacturer[] annotations = Car.class.getAnnotationsByType(Manufacturer.class);
for (Manufacturer annotation : annotations) {
System.out.println(annotation.name());
}
You should always pass the type of the annotation to getAnnotation or getAnnotationsByType and not the type of the class itself.
Hope it helpt you

So the problem is that the Manufacturer annotation doesn't have a RetentionPolicy declared only the container annotation (Cars).
So adding
#Retention(RetentionPolicy.RUNTIME)
also on the Manufacturer annotation will read the annotation in a container based /single based manner (in a manner of speaking)
Thanks Sean, at least you answer gave me a clue...

Related

How to build custom #Projections for spring-rest?

spring-data-rest makes it possible to expose #Entity domain objects directly and even provide a DTO projection as follows:
#Projection(name = "personDTO", types = { Person.class })
public interface PersonDTO {
#Value("#{target.firstName} #{target.lastName}") //SPeL
String getFullName();
}
Question: what if I want to construct only some of the dto fields myself? Eg having some kind of condition on the firstname field, and fill it based on this either the one or other way. Is that possible?
Spring mentions a example, but unfortunately it's not complete:
https://spring.io/blog/2014/05/21/what-s-new-in-spring-data-dijkstra
#Projection(name = "summary", types = Order.class)
interface OrderSummary {
#Value("#{#shop.calculateTotal(target)}")
Money getTotal();
}
Here the logic is exported to #shop.calulcateTotal(), BUT they don't tell in the example how this #shop bean is injected here. I assume this is a #Service, but don't know how to get it in.
Says right below the example you posted.
https://spring.io/blog/2014/05/21/what-s-new-in-spring-data-dijkstra
For advanced use cases you can even equip the projection methods with #Value to return the result of a SpEL expression to the marshaller. In our sample here, we invoke a method on a Spring bean named shop and hand the proxy target instance to it to calculate the order total, which could consider rebates, taxes etc.
Since your projections are already managed by spring, you don't really need to inject it. Spring magic takes care of it for you.

Is it possible to omit some annotation attributes but not all?

In java tutorials - Annotations part, question 3 (https://docs.oracle.com/javase/tutorial/java/annotations/QandE/questions.html), an annotation are expected to be used as below:
#Meal("breakfast", mainDish="cereal")
I tried to define the annotation as below but it does not allow the above usage.
public #interface Meal {
String value();
String mainDish();
}
Is it possible to omit the first attribute name as the question suggested?
No, the shortcut only works if you specify the value attribute and nothing else.
Otherwise you must explicitly write value=, that is the correct version would be #Meal(value = "breakfast", mainDish = "cereal")

Make Java annotation act differently depending on field annotated

In Java, is there a way to change the behaviour of an annotation depending on the type of the annotated field?
I know that annotation presence is supposed to be tested by code. Not the opposite. But the case is rather particular: this is a Jackson 2.0 « inside » annotation which gather a list of annotations. We use it to define the field name (#JsonProperty) and the field serializing policies (#JsonSerialize).
The serialisation policies must be adapted to the annotated field. And, because we are talking of a framework, one unique annotation is far better than two separate ones.
#Retention(RUNTIME)
#JacksonAnnotationsInside.
#JsonProperty("_id")
#JsonSerialize(using=IdSerializer.class)
#JsonDeserialize(using=IdDeserializer.class)
public #interface Id {}
Some cases need to turn the serializers down, that's the point. In the following example, the String must be processed by the de/serializers, ObjectId don't. Both need to be renamed _id by the #JsonProperty.
public class Car {
#Id String id
}
public class Bus {
#Id ObjectId id
}
Any clues?

Turning one annotation into many annotations with AspectJ

I have discovered a pattern in my JPA mappings that I would like to codify. A simple example follows:
#OneToMany(fetch=FetchType.EAGER)
#Sort(type=SortType.NATURAL)
private SortedSet<Item> items;
I would like to create a single annotation called SortedOneToMany that I can apply to the above set:
public #interface SortedOneToMany {
FetchType fetch() default EAGER;
SortType sort() default NATURAL;
Class comparator() default void.class;
}
I have written the following aspect to "attach" the JPA annotations whenever it sees my annotation:
public aspect SortedOneToManyAspect {
declare #field: #SortedOneToMany * * : #OneToMany(fetch=FetchType.EAGER);
declare #field: #SortedOneToMany * * : #Sort(type=SortType.NATURAL);
}
But I don't know how can I access the values of the SortedOneToMany annotation parameters and use them when defining the OneToMany and Sort annotations. There may be cases where I want to change one of the default values like so:
#SortedOneToMany(sort=SortType.COMPARATOR,comparator=ItemComparator.class)
private SortedSet<Item> items;
So how can I pass the annotation values from SortedOneToMany to the Sort annotation?
I received this answer from Andy Clement on the aspectj-users mailing list:
Hi,
I'm afraid you can't do that with AspectJ right now, you can't pass a
piece of the matched information to the new annotation. I can perhaps
imagine some hypothetical syntax:
declare #field:
#SortedOneToMany(sort=SortType.COMPARATOR,comparator={1}) * * :
#Sort(type=SortType.COMPARATOR,comparator={1});
which would seem to achieve what you want.
Maybe raise an enhancement request for it:
https://bugs.eclipse.org/bugs/enter_bug.cgi?product=AspectJ
sorry I don't have better news.
cheers
Andy
I created a ticket for the issue in case anyone wants to follow the progress: https://bugs.eclipse.org/bugs/show_bug.cgi?id=345515

Annotations of Annotations in Java 5/6

I am setting up Hibernate Caching and want to cache certain entities in different regions. For example, some entities can be "stale" for up to 5 minutes, some for an hour, and some (like lookups) won't change for weeks. To facilitate easy config of regions in my code, I'm trying the following:
I created an annotation named #LookupCache (and #DailyCache etc)
#Cache(region = "lookups", usage = CacheConcurrencyStrategy.READ_ONLY)
#Retention(RetentionPolicy.RUNTIME)
public #interface LookupCache {}
And I am adding that annotation to my Hibernate/JPA entity:
#LookupCache
public class Course {}
This way, I can easily change the region or attributes of the #LookupCache without having to change the annotation params of every class.
However, the cache loader doesn't pick up this inherited #Cache notation. How do I get the #LookupCache annotation to inherit the annotations that are applied to it?
Update: To clarify, the #Cache annotation is a built-in hibernate annotation used by second-level caches like EHCache. I can't modify the #Cache annotation to make it inheritable by other annotations (my client doesn't want to maintain a special fork of hibernate). This is probably my only option though.
Here is a simple example that shows how to retrieve details of the #Cache annotation applied to your #LookupCache annotation:
Course c = new Course();
LookupCache lookupCache = c.getClass().getAnnotation(LookupCache.class);
Cache cache = lookupCache.annotationType().getAnnotation(Cache.class);
System.err.println("region " + cache.region());
System.err.println("usage " + cache.usage());
You just need to make sure that #Cache annotation also has #Retention(RetentionPolicy.RUNTIME)

Categories

Resources