I'm exploring annotations and came to a point where some annotations seems to have a hierarchy among them.
I'm using annotations to generate code in the background for Cards. There are different Card types (thus different code and annotations) but there are certain elements that are common among them like a name.
#Target(value = {ElementType.TYPE})
public #interface Move extends Page{
String method1();
String method2();
}
And this would be the common Annotation:
#Target(value = {ElementType.TYPE})
public #interface Page{
String method3();
}
In the example above I would expect Move to inherit method3 but I get a warning saying that extends is not valid with annotations. I was trying to have an Annotation extends a common base one but that doesn't work. Is that even possible or is just a design issue?
You can annotate your annotation with a base annotation instead of inheritance. This is used in Spring framework.
To give an example
#Target(value = {ElementType.ANNOTATION_TYPE})
public #interface Vehicle {
}
#Target(value = {ElementType.TYPE})
#Vehicle
public #interface Car {
}
#Car
class Foo {
}
You can then check if a class is annotated with Vehicle using Spring's AnnotationUtils:
Vehicle vehicleAnnotation = AnnotationUtils.findAnnotation (Foo.class, Vehicle.class);
boolean isAnnotated = vehicleAnnotation != null;
This method is implemented as:
public static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType) {
return findAnnotation(clazz, annotationType, new HashSet<Annotation>());
}
#SuppressWarnings("unchecked")
private static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType, Set<Annotation> visited) {
try {
Annotation[] anns = clazz.getDeclaredAnnotations();
for (Annotation ann : anns) {
if (ann.annotationType() == annotationType) {
return (A) ann;
}
}
for (Annotation ann : anns) {
if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) {
A annotation = findAnnotation(ann.annotationType(), annotationType, visited);
if (annotation != null) {
return annotation;
}
}
}
}
catch (Exception ex) {
handleIntrospectionFailure(clazz, ex);
return null;
}
for (Class<?> ifc : clazz.getInterfaces()) {
A annotation = findAnnotation(ifc, annotationType, visited);
if (annotation != null) {
return annotation;
}
}
Class<?> superclass = clazz.getSuperclass();
if (superclass == null || Object.class == superclass) {
return null;
}
return findAnnotation(superclass, annotationType, visited);
}
AnnotationUtils also contains additional methods for searching for annotations on methods and other annotated elements. The Spring class is also powerful enough to search through bridged methods, proxies, and other corner-cases, particularly those encountered in Spring.
Unfortunately, no. Apparently it has something to do with programs that read the annotations on a class without loading them all the way. See Why is it not possible to extend annotations in Java?
However, types do inherit the annotations of their superclass if those annotations are #Inherited.
Also, unless you need those methods to interact, you could just stack the annotations on your class:
#Move
#Page
public class myAwesomeClass {}
Is there some reason that wouldn't work for you?
In addition to Grygoriys answer of annotating annotations.
You can check e.g. methods for containing a #Qualifier annotation (or an annotation annotated with #Qualifier) by this loop:
for (Annotation a : method.getAnnotations()) {
if (a.annotationType().isAnnotationPresent(Qualifier.class)) {
System.out.println("found #Qualifier annotation");//found annotation having Qualifier annotation itself
}
}
What you're basically doing, is to get all annotations present on the method and of those annotations you get their types and check those types if they're annotated with #Qualifier. Your annotation needs to be Target.Annotation_type enabled as well to get this working.
Check out https://github.com/blindpirate/annotation-magic , which is a library I developed when I had the same question.
#interface Animal {
boolean fluffy() default false;
String name() default "";
}
#Extends(Animal.class)
#Animal(fluffy = true)
#interface Pet {
String name();
}
#Extends(Pet.class)
#interface Cat {
#AliasFor("name")
String value();
}
#Extends(Pet.class)
#interface Dog {
String name();
}
#interface Rat {
#AliasFor(target = Animal.class, value = "name")
String value();
}
#Cat("Tom")
class MyClass {
#Dog(name = "Spike")
#Rat("Jerry")
public void foo() {
}
}
Pet petAnnotation = AnnotationMagic.getOneAnnotationOnClassOrNull(MyClass.class, Pet.class);
assertEquals("Tom", petAnnotation.name());
assertTrue(AnnotationMagic.instanceOf(petAnnotation, Animal.class));
Animal animalAnnotation = AnnotationMagic.getOneAnnotationOnClassOrNull(MyClass.class, Animal.class);
assertTrue(animalAnnotation.fluffy());
Method fooMethod = MyClass.class.getMethod("foo");
List<Animal> animalAnnotations = AnnotationMagic.getAnnotationsOnMethod(fooMethod, Animal.class);
assertEquals(Arrays.asList("Spike", "Jerry"), animalAnnotations.stream().map(Animal::name).collect(toList()));
Related
I have an annotation which marks classes that contain an inner class which implements a named interface.
Here's an example of how this annotation is used:
public interface Implementable {}
#Deserializable(Implementable.class)
public class ImplementableFactory {
public static Implementable getImplementable() {
return new Impl();
}
private class Impl implements Implementable {}
}
And here's the annotation itself:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#SupportedSourceVersion(SourceVersion.RELEASE_8)
public #interface Deserializable {
Class value();
}
I'd like to do some annotation processing to ensure this contract. I've created an annotation processor class for that purpose:
public class DeserializableProcessor extends AbstractProcessor {
#Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for(Element element : roundEnv.getElementsAnnotatedWith(Deserializable.class)){
TypeMirror expected = getDeserializableValue(element);
if (expected != null) {
Boolean found = false;
for (Element enclosed : element.getEnclosedElements()) {
if (enclosed.getKind().equals(ElementKind.CLASS)) {
//This next bit doesn't compile.
//I'm looking for the same functionality.
if (expected.isAssignableFrom(enclosed)) {
found = true;
break;
}
}
}
if (!found) {
String message = String.format("Classes marked with the Deserializable annotation must contain an inner class with implements the value of the annotation. %s does not contain a class which implements %s.",
element.getSimpleName().toString(),
expected.toString());
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, message);
}
}
}
return true;
}
private TypeMirror getDeserializableValue(Element element) {
...
}
}
How can I achieve similar functionality to Class::isAssignableFrom within reflection possible via annotation processing?
This can be done with the aid of AbstractProcessor's protected processingEnvironment. It exposes an implementation of TypeUtils, a utility class that enables a lot of reflection functionality.
if (processingEnv.getTypeUtils().isAssignable(enclosed.asType(), expected)) {
found = true;
break;
}
I want to decorate existing objects so that method calls are automatically validated. I already managed to delegate method call to an interceptor that calls Hibernate validator and so far it works fine:
public class HibernateBeanValidator implements BeanValidator{
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
#Override
public <T> T addMethodValidation(T object) {
ExecutableValidator executableValidator = factory.getValidator().forExecutables();
Class<? extends T> dynamicType = (Class<? extends T>)new ByteBuddy()
.subclass(object.getClass())
.method(isPublic()).intercept(MethodDelegation.to(new ValidationInterceptor(object, executableValidator)).andThen(SuperMethodCall.INSTANCE))
.make()
.load(getClass().getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
try {
T validatedObject = dynamicType.newInstance();
return validatedObject;
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
public static class ValidationInterceptor {
private final Object validatedObject;
private final ExecutableValidator executableValidator;
public <T> ValidationInterceptor(T object, ExecutableValidator executableValidator) {
this.validatedObject = object;
this.executableValidator = executableValidator;
}
public void validate(#Origin Method method, #AllArguments Object[] arguments)
throws Exception {
Set<ConstraintViolation<Object>> constraintViolations = executableValidator.validateParameters(validatedObject, method, arguments);
if(! constraintViolations.isEmpty()) {
throw new ValidationException(constraintViolations);
}
}
}
}
What I would like to improve is to bind method calls only to methods that have at least one parameter annotated with a constraint annotation, such as:
class Echo {
public String repeat(#NotNull String word) { /* should bind validation here */
return word;
}
public String notAnnotated(String word) { /* should not bind validation */
return word;
}
}
How could I specify an ElementMatcher in Byte Buddy so that it would bind only to methods with parameters annotated with annotations that are annotated with #Constraint, such as #NotNull (taken from javax.validation.constraints):
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
#Retention(RUNTIME)
#Documented
#Constraint(validatedBy = { })
public #interface NotNull {
String message() default "{javax.validation.constraints.NotNull.message}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
/**
* Defines several {#link NotNull} annotations on the same element.
*
* #see javax.validation.constraints.NotNull
*/
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
#Retention(RUNTIME)
#Documented
#interface List {
NotNull[] value();
}
}
Your problem can be solved by implementing a custom ElementMatcher which is used to identify methods to be intercepted. Currently, you are using the predefined isPublic() interceptor which does not consider annotations but only the public modifier of a method. As the predefined annotations can be chained, you can build a suitable matcher as follows:
isPublic().and(hasParameter(hasAnnotation(nameStartsWith("javax."))))
Of course, you can simply implement your own matchers without using the predfined ones.
Actually instead of just checking for an annotation out of the javax.validation.constraints namespace, it is probably better to use the Bean Validation meta data API. Constraints do not need to come from this namespace, but can also originate from Hibernate Validator (org.hibernate.validator.constraints) or be a custom constraint. A possible implementation of ElementMatcher which makes use of the meta data API could look like this:
public static class BeanValidationMatcher implements ElementMatcher {
private static final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
#Override
public boolean matches(Object target) {
// handle different descriptors and potentially use generic MethodDescription
if ( !( target instanceof MethodDescription.ForLoadedMethod ) ) {
return false;
}
MethodDescription.ForLoadedMethod methodDescription = (MethodDescription.ForLoadedMethod) target;
Method method = methodDescription.getLoadedMethod();
boolean isGetter = ReflectionHelper.isGetterMethod( method );
boolean needsValidation;
BeanDescriptor beanDescriptor = validator.getConstraintsForClass( method.getDeclaringClass() );
if ( isGetter ) {
needsValidation = isGetterConstrained( method, beanDescriptor );
}
else {
needsValidation = isNonGetterConstrained( method, beanDescriptor );
}
return needsValidation;
}
private boolean isNonGetterConstrained(Method method, BeanDescriptor beanDescriptor) {
return beanDescriptor.getConstraintsForMethod( method.getName(), method.getParameterTypes() ) != null;
}
private boolean isGetterConstrained(Method method, BeanDescriptor beanDescriptor) {
String propertyName = ReflectionHelper.getPropertyName( method );
PropertyDescriptor propertyDescriptor = beanDescriptor.getConstraintsForProperty( propertyName );
return propertyDescriptor != null && propertyDescriptor.findConstraints()
.declaredOn( ElementType.METHOD )
.hasConstraints();
}
}
I have 3 different kinds of custom annotations. Assume those are Annotation1,Annotation2,Annotation3.
I was applied these 3 annotations to some of the fields in my class. Now I am extracting/getting all the fields which are assigned these 3 annotations. So for that I wrote a method like
public List<Field> getAnnotation1Fields(Annotation1 argAnnotation1){
// Code to extract those fields...
}
So for my 3 annotations I need to write 3 different methods like
public List<Field> getAnnotation2Fields(Annotation2 argAnnotation2){
// Code to extract those fields...
}
public List<Field> getAnnotation3Fields(Annotation3 argAnnotation3){
// Code to extract those fields...
}
In the above methods the Extraction logic is same but the parameter type is different (Argument). Here my question is how can I call these three methods on single annotation...? So that one common method can be called for any type of annotation (Including our custom annotations).
Use method generics - You can define a variable type parameter for methods, as well as classes, like so:
public <T> List<Field> getAnnotationFields(T argAnnotation) {
// Get fields with annotation type T
}
And then you can call it as easily as:
Annotation3 thingy = ...;
getAnnotationFields(thingy); // Extracts fields for Annotation3
public List<Field> getAnnotatedFields(java.lang.annotation.Annotation annotation)
or
public List<Field> getAnnotatedFields(Class<? extends Annotation> annotationType)
depending on what you have instance (first) or type (second).
You could do it with this simple method:
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
public static List<Field> getAnnotatedFields(Class<?> clazz, Class<? extends Annotation> annotationType) {
Field[] declaredFields = clazz.getDeclaredFields();
List<Field> annotatedFields = new LinkedList<>();
for(Field field : declaredFields) {
if(field.getAnnotation(annotationType) != null)
annotatedFields.add(field);
}
return annotatedFields;
}
Usage example:
getAnnotatedFields(TestClass.class, Deprecated.class);
I hope that’s closer to your needs:
static <A extends Annotation> Map<String,A> getField2Annotation(
Class<?> declaringClass, Class<A> annotationType) {
Field[] fields=declaringClass.getDeclaredFields();
Map<String, A> map=Collections.emptyMap();
for(Field f:fields) {
A anno=f.getAnnotation(annotationType);
if(anno!=null) {
if(map.isEmpty()) map=new HashMap<String, A>();
map.put(f.getName(), anno);
}
}
return map;
}
Then you can do something like this:
public class Example {
#Retention(RetentionPolicy.RUNTIME)
#interface Foo { String value() default "bar"; }
#Foo static String X;
#Foo("baz") String Y;
public static void main(String[] args) {
Map<String, Foo> map = getField2Annotation(Example.class, Foo.class);
for(Map.Entry<String,Foo> e:map.entrySet()) {
System.out.println(e.getKey()+": "+e.getValue().value());
}
}
}
I've created an inherited field type annotation that is placed on private properties in an abstract superclass.
#Inherited
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface Lifecycle{
Type type();
String name() default "";
public enum Type{
DISCRIMINATOR,INITIAL,MUTABLE
}
}
I'm calling a method from the subclass which seeks to gather all the annotations of this type (inherited or otherwise) and return them in a list.
public static <T extends Annotation> List<T> getAnnotation(final Class c, final Class<T> ann) {
return getAnnotation(c, ann, new ArrayList<T>());
}
public static <T extends Annotation> List<T> getAnnotation(final Class c, final Class<T> ann, List<T> aList) {
Field[] fields = c.getFields();
for (int i = 0; i < fields.length; i++) {
Field myField = fields[i];
myField.setAccessible(true);
T found = myField.getAnnotation(ann);
if (found != null) {
aList.add(found);
}
}
if (!c.getSuperclass().equals(Object.class)) {
return getAnnotation(c.getSuperclass(), ann, aList);
} else {
return aList;
}
}
For some reason unbeknownst to me this doesn't work. All fields both inherited and not are definitely found. Likewise all classes in the inheritance structure are traversed, but for some reason, myField.getAnnotation(ann); is always null.
I'm kind of at a loss - I don't understand why if I can properly retrieve the field (as well as get and set it's value, e.g. not a security thing) that I can't see it's annotation.
Instead of this one:
Field[] fields = c.getFields();
try this:
Field[] fields = c.getDeclaredFields();
If this doesn't work, try to iterate over declared annotations:
for(Annotation annotation : field[i].getDeclaredAnnotations()) {
...
}
I'm developing a Java enterprise application, currently doing Java EE security stuff to restrict access for particular functions to specific users. I configured the application server and everything, and now I'm using the RolesAllowed-annotation to secure the methods:
#Documented
#Retention (RUNTIME)
#Target({TYPE, METHOD})
public #interface RolesAllowed {
String[] value();
}
When I use the annotation like this, it works fine:
#RolesAllowed("STUDENT")
public void update(User p) { ... }
But this is not what I want, as I have to use a String here, refactoring becomes hard, and typos can happen. So instead of using a String, I would like to use an Enum value as a parameter for this annotation. The Enum looks like this:
public enum RoleType {
STUDENT("STUDENT"),
TEACHER("TEACHER"),
DEANERY("DEANERY");
private final String label;
private RoleType(String label) {
this.label = label;
}
public String toString() {
return this.label;
}
}
So I tried to use the Enum as a parameter like this:
#RolesAllowed(RoleType.DEANERY.name())
public void update(User p) { ... }
But then I get the following compiler error, although Enum.name just returns a String (which is always constant, isn't it?).
The value for annotation attribute RolesAllowed.value must be a constant expression`
The next thing I tried was to add an additional final String to my Enum:
public enum RoleType {
...
public static final String STUDENT_ROLE = STUDENT.toString();
...
}
But this also doesn't work as a parameter, resulting in the same compiler error:
// The value for annotation attribute RolesAllowed.value must be a constant expression
#RolesAllowed(RoleType.STUDENT_ROLE)
How can I achieve the behavior I want? I even implemented my own interceptor to use my own annotations, which is beautiful, but far too much lines of code for a little problem like this.
DISCLAIMER
This question was originally a Scala question. I found out that Scala is not the source of the problem, so I first try to do this in Java.
How about this?
public enum RoleType {
STUDENT(Names.STUDENT),
TEACHER(Names.TEACHER),
DEANERY(Names.DEANERY);
public class Names{
public static final String STUDENT = "Student";
public static final String TEACHER = "Teacher";
public static final String DEANERY = "Deanery";
}
private final String label;
private RoleType(String label) {
this.label = label;
}
public String toString() {
return this.label;
}
}
And in annotation you can use it like
#RolesAllowed(RoleType.Names.DEANERY)
public void update(User p) { ... }
One little concern is, for any modification, we need to change in two places. But since they are in same file, its quite unlikely to be missed. In return, we are getting the benefit of not using raw strings and avoiding the sophisticated mechanism.
Or this sounds totally stupid? :)
I don't think your approach of using enums is going to work. I found that the compiler error went away if I changed the STUDENT_ROLE field in your final example to a constant string, as opposed to an expression:
public enum RoleType {
...
public static final String STUDENT_ROLE = "STUDENT";
...
}
However, this then means that the enum values wouldn't be used anywhere, because you'd be using the string constants in annotations instead.
It seems to me that you'd be better off if your RoleType class contained nothing more than a bunch of static final String constants.
To see why your code wasn't compiling, I had a look into the Java Language Specification (JLS). The JLS for annotations states that for an annotation with a parameter of type T and value V,
if T is a primitive type or String, V is a constant expression.
A constant expression includes, amongst other things,
Qualified names of the form TypeName . Identifier that refer to constant variables
and a constant variable is defined as
a variable, of primitive type or type String, that is final and initialized with a compile-time constant expression
Here's a solution using an additional interface and a meta-annotation. I've included a utility class to help do the reflection to get the role types from a set of annotations, and a little test for it:
/**
* empty interface which must be implemented by enums participating in
* annotations of "type" #RolesAllowed.
*/
public interface RoleType {
public String toString();
}
/** meta annotation to be applied to annotations that have enum values implementing RoleType.
* the value() method should return an array of objects assignable to RoleType*.
*/
#Retention(RetentionPolicy.RUNTIME)
#Target({ANNOTATION_TYPE})
public #interface RolesAllowed {
/* deliberately empty */
}
#RolesAllowed
#Retention(RetentionPolicy.RUNTIME)
#Target({TYPE, METHOD})
public #interface AcademicRolesAllowed {
public AcademicRoleType[] value();
}
public enum AcademicRoleType implements RoleType {
STUDENT, TEACHER, DEANERY;
#Override
public String toString() {
return name();
}
}
public class RolesAllowedUtil {
/** get the array of allowed RoleTypes for a given class **/
public static List<RoleType> getRoleTypesAllowedFromAnnotations(
Annotation[] annotations) {
List<RoleType> roleTypesAllowed = new ArrayList<RoleType>();
for (Annotation annotation : annotations) {
if (annotation.annotationType().isAnnotationPresent(
RolesAllowed.class)) {
RoleType[] roleTypes = getRoleTypesFromAnnotation(annotation);
if (roleTypes != null)
for (RoleType roleType : roleTypes)
roleTypesAllowed.add(roleType);
}
}
return roleTypesAllowed;
}
public static RoleType[] getRoleTypesFromAnnotation(Annotation annotation) {
Method[] methods = annotation.annotationType().getMethods();
for (Method method : methods) {
String name = method.getName();
Class<?> returnType = method.getReturnType();
Class<?> componentType = returnType.getComponentType();
if (name.equals("value") && returnType.isArray()
&& RoleType.class.isAssignableFrom(componentType)) {
RoleType[] features;
try {
features = (RoleType[]) (method.invoke(annotation,
new Object[] {}));
} catch (Exception e) {
throw new RuntimeException(
"Error executing value() method in "
+ annotation.getClass().getCanonicalName(),
e);
}
return features;
}
}
throw new RuntimeException(
"No value() method returning a RoleType[] type "
+ "was found in annotation "
+ annotation.getClass().getCanonicalName());
}
}
public class RoleTypeTest {
#AcademicRolesAllowed({DEANERY})
public class DeaneryDemo {
}
#Test
public void testDeanery() {
List<RoleType> roleTypes = RolesAllowedUtil.getRoleTypesAllowedFromAnnotations(DeaneryDemo.class.getAnnotations());
assertEquals(1, roleTypes.size());
}
}
I solved this by using Lombok annotation FieldNameConstants :
#FieldNameConstants(onlyExplicitlyIncluded = true)
public enum EnumBasedRole {
#FieldNameConstants.Include ADMIN,
#FieldNameConstants.Include EDITOR,
#FieldNameConstants.Include READER;
}
Next you can use it as follow :
#RestController
#RequestMapping("admin")
#RolesAllowed(EnumBasedRole.Fields.ADMIN)
public class MySecuredController {
#PostMapping("user")
public void deleteUser(...) {
...
}
}
I solved this problem by adding an annotation #RoleTypesAllowed and adding a metadata source. This works really well if there is just one enum type that needs to be supported. For multiple enum types, see anomolos's post.
In the below RoleType is my role enum.
#Documented
#Target({ElementType.TYPE, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
public #interface RoleTypesAllowed {
RoleType[] value();
}
Then I added the following metadata source to spring...
#Slf4j
public class CemsRolesAllowedMethodSecurityMetadataSource
extends AbstractFallbackMethodSecurityMetadataSource {
protected Collection<ConfigAttribute> findAttributes(Class<?> clazz) {
return this.processAnnotations(clazz.getAnnotations());
}
protected Collection<ConfigAttribute> findAttributes(Method method, Class<?> targetClass) {
return this.processAnnotations(AnnotationUtils.getAnnotations(method));
}
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
private List<ConfigAttribute> processAnnotations(Annotation[] annotations) {
if (annotations != null && annotations.length != 0) {
List<ConfigAttribute> attributes = new ArrayList();
for (Annotation a : annotations) {
if (a instanceof RoleTypesAllowed) {
RoleTypesAllowed ra = (RoleTypesAllowed) a;
RoleType[] alloweds = ra.value();
for (RoleType allowed : alloweds) {
String defaultedAllowed = new RoleTypeGrantedAuthority(allowed).getAuthority();
log.trace("Added role attribute: {}", defaultedAllowed);
attributes.add(new SecurityConfig(defaultedAllowed));
}
return attributes;
}
}
}
return null;
}
}