Reputation: 321
I have the same problem and tried Zero3's solution (Required @QueryParam in JAX-RS (and what to do in their absence)), but in my case parameter.isAnnotationPresent(Required.class)
always return false
.
This is my Required annotation:
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Required {
// This is just a marker annotation, so nothing in here.
}
I also tried it with a BeanParam
annotation and modified the filter accordingly, but same result - always get null
for isAnnotionPresen
.
I'm using WildFly 9 (RESTeasy) which automatically registers the request filter.
My REST resource looks like this:
@GET
@Path("/{type}/{id}")
public Response getAllByTypeAndId(@Required @BeanParam RequiredQueryParams requiredQueryParams,
@Required @QueryParam("mandant") String mandant,
@PathParam("type") String type,
@PathParam("id") Long id) {
...doSomething...
}
Running the debugger shows for parameter.declaredAnnotations two entries in the HashMap for BeanParam
:
0 interface my.annotations.Required -> @my.annotations.Required()
1 interface javax.ws.rs.BeanParam -> @javax.ws.rs.BeanParam()
and for QueryParam
:
0 interface my.annotations.Required -> @my.annotations.Required()
1 interface javax.ws.rs.QueryParam -> @javax.ws.rs.QueryParam(value=mandant)
Any hints welcome - Thank you!
Upvotes: 1
Views: 3392
Reputation: 661
Based on the information in your question and the additional information in the comments, I think I have a rough idea of what is going on here. Let's start at the original issue:
parameter.isAnnotationPresent(Required.class) always return
false
Now let's take a look at the implementation of AnnotatedElement#isAnnotationpresent(Class<? extends Annotation>)
in Java 8:
default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
return getAnnotation(annotationClass) != null;
}
This leads us to the implementation of Parameter#getAnnotation(Class<T>)
:
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
Objects.requireNonNull(annotationClass);
return annotationClass.cast(declaredAnnotations().get(annotationClass));
}
The documentation of Object#cast(Object)
states that:
@return the object after casting, or null if obj is null
So it appears like declaredAnnotations().get(annotationClass)
returns null. Parameter#declaredAnnotations()
returns a Map<Class<? extends Annotation>, Annotation>
, which is actually a HashMap
, containing a mapping of annotation classes to annotation instances for the parameter in question. The documentation of Map#get(Object)
states that:
@return the value to which the specified key is mapped, or null if this map contains no mapping for the key
This is consistent with your comment that states that your Required.class
object does not equal (nor have the same hashcode as) the class object of the Required
annotation present on the parameter in question.
This leads us to the core of the issue: Why is your annotation class not equal to itself? After all, Object#equals(object)
is implemented like this:
public boolean equals(Object obj) {
return (this == obj);
}
For this implementation of equals(Object)
to work for Class<T>
objects (which inherits the implementation), the assumption is clearly that there is only one such class object per class. To my knowledge, this is also the case when comparing class objects loaded by the same class loader. This is probably specified somewhere in the Java specification or maybe in some class loading documentation somewhere. I don't know, but let's assume that this is how things work.
However, in more advanced applications, classes might be loaded by different class loaders at the various layers of your application, invalidating the singleton assumption and in turn causing the issue you are experiencing. So my guess is that the WildFly application server loaded your Required
annotation using a different class loader than the one you later use to obtain the reference to the Required.class
object.
I don't know much about WildFly, but I do know that Java applications servers usually rely on some quite bureaucratic class loader hierarchies for loading classes (for security, and other reasons). I suggest looking into the WildFly documentation which hopefully has some more information about this.
TL;DR: I think your Required.class
object represents a different copy of your Required
annotation class than the one present on the parameter. Make sure that the same class loader is used to load both.
Upvotes: 1
Reputation: 321
As a work-around for non-working parameter.isAnnotationPresent(Required.class)
in ContainerRequestFilter
I'm using now this method:
private boolean isRequired(Parameter parameter) {
for (Annotation annotation : parameter.getDeclaredAnnotations()) {
if (Required.class.getName().equals(annotation.annotationType().getName())) {
return true;
}
}
return false;
}
eitherway i'm wondering why isAnnotionaPresent() does not work ?!
Upvotes: 0