2016-07-28 11 views
1

Ich habe das gleiche Problem und versuchte Zero3-Lösung (Required @QueryParam in JAX-RS (and what to do in their absence)), aber in meinem Fall parameter.isAnnotationPresent(Required.class) immer false zurück.Pflicht @QueryParam in JAX-RS @Required Annotation nicht in ContainerRequestFilter funktioniert

Das ist mein Required Anmerkung:

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. 
} 

Ich versuchte es auch mit einem BeanParam Annotation und modifiziert, um die Filter dementsprechend aber dasselbe Ergebnis - immer null für isAnnotionPresen bekommen.

Ich benutze WildFly 9 (RESTeasy), die den Anforderungsfilter automatisch registriert.

Meine REST-Ressource wie folgt aussieht:

@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... 
} 

Ausführen des Debugger zeigt für parameter.declaredAnnotations zwei Einträge in der HashMap für BeanParam:

0 interface my.annotations.Required -> @my.annotations.Required() 
1 interface javax.ws.rs.BeanParam -> @javax.ws.rs.BeanParam() 

und für QueryParam:

0 interface my.annotations.Required -> @my.annotations.Required() 
1 interface javax.ws.rs.QueryParam -> @javax.ws.rs.QueryParam(value=mandant) 

Alle Hinweise willkommen - Danke!

+0

Scheint wie Bean-Validierung wäre für diesen Anwendungsfall besser geeignet. Schauen Sie sich die Dokumentation zu RESTEasy (Wildfly's jax-rs) an. Es gibt einen Abschnitt zur Bean-Validierung –

+1

Das wäre die letzte Option, weil ich an einer Multi-Tenant-Anwendung arbeite, in der wir viele REST-Services haben und jeder von ihnen den obligatorischen "mandant" -Parameter benötigt. Ich würde eine Filterlösung bevorzugen, die in Zero3s Lösung beschrieben ist (http://stackoverflow.com/questions/13968261/required-queryparam-in-axs-rs-and-what-to-do-in-the-absence/38639372 # 38639372) – raho

Antwort

0

Basierend auf den Informationen in Ihrer Frage und den zusätzlichen Informationen in den Kommentaren denke ich, ich habe eine grobe Vorstellung davon, was hier vor sich geht. Schauen wir uns die Original-Ausgabe starten:

parameter.isAnnotationPresent (Required.class) immer wieder zurückkehren false

Nun wollen wir bei der Umsetzung von AnnotatedElement#isAnnotationpresent(Class<? extends Annotation>) in Java einen Blick 8:

default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) { 
    return getAnnotation(annotationClass) != null; 
} 

Dies führt uns zur Implementierung von Parameter#getAnnotation(Class<T>):

public <T extends Annotation> T getAnnotation(Class<T> annotationClass) { 
    Objects.requireNonNull(annotationClass); 
    return annotationClass.cast(declaredAnnotations().get(annotationClass)); 
} 

Die Dokumentation von Object#cast(Object) heißt es:

@return das Objekt nach dem Gießen oder null, wenn obj

So null ist null wie declaredAnnotations().get(annotationClass) kehrt erscheint. Parameter#declaredAnnotations() gibt eine Map<Class<? extends Annotation>, Annotation> zurück, die eigentlich eine HashMap ist, die eine Zuordnung von Anmerkungsklassen zu Anmerkungsinstanzen für den fraglichen Parameter enthält.Die Dokumentation von Map#get(Object) heißt es:

@return den Wert, auf den der angegebenen Schlüssel zugeordnet ist, oder null, wenn diese Karte keine Zuordnung für den Schlüssel

enthält Dies steht im Einklang mit Ihrem Kommentar, dass die Staaten dass das Objekt Required.class nicht mit dem Klassenobjekt der Required-Annotation übereinstimmt (und nicht denselben Hashcode hat), die auf dem betreffenden Parameter vorhanden ist.

Dies führt uns zum Kern des Problems: Warum ist Ihre Annotation-Klasse nicht gleich selbst? Immerhin ist Object#equals(object) wie folgt umgesetzt:

public boolean equals(Object obj) { 
    return (this == obj); 
} 

Für diese Implementierung von equals(Object) für Class<T> Objekte zu arbeiten (was die Implementierung erbt), ist die Annahme, klar, dass es nur ein solches Klassenobjekt pro Klasse. Nach meinem Wissen ist dies auch der Fall beim Vergleich von Klassenobjekten, die vom selben Klassenlader geladen wurden. Dies wird wahrscheinlich irgendwo in der Java-Spezifikation oder irgendwo in einer Dokumentation zum Laden von Klassen angegeben. Ich weiß es nicht, aber nehmen wir an, dass so etwas funktioniert.

In komplexeren Anwendungen können Klassen jedoch von verschiedenen Klassenladeprogrammen auf den verschiedenen Ebenen Ihrer Anwendung geladen werden, wodurch die Singleton-Annahme ungültig wird und das Problem verursacht wird. Ich nehme an, dass der WildFly-Anwendungsserver Ihre Required-Annotation mit einem anderen Klassenlader geladen hat als der, den Sie später verwenden, um den Verweis auf das Objekt Required.class zu erhalten.

Ich weiß nicht viel über WildFly, aber ich weiß, dass Java-Anwendungsserver normalerweise auf einige ziemlich bürokratische Klassenlader-Hierarchien zum Laden von Klassen angewiesen sind (aus Sicherheitsgründen und anderen Gründen). Ich schlage vor, in die WildFly-Dokumentation zu schauen, die hoffentlich mehr Informationen darüber enthält.

TL; DR: Ich glaube, Ihr Required.class Objekt stellt eine andere Kopie Ihrer Required Annotationsklasse dar als die, die auf dem Parameter vorhanden ist. Stellen Sie sicher, dass derselbe Klassenlader verwendet wird, um beide zu laden.

+0

Danke für Ihre sehr detaillierte Erklärung. In Bezug auf den Hinweis, dass der gleiche Klassenlader verwendet werden sollte, habe ich meine Anwendung umstrukturiert und nun funktioniert 'isAnnotationPresent()' wie erwartet! Ich werde Ihren Kommentar als Antwort auf diese Frage markieren! – raho

0

Als Behelfslösung für nicht arbeit parameter.isAnnotationPresent(Required.class) in ContainerRequestFilter Ich verwende jetzt diese Methode:

private boolean isRequired(Parameter parameter) { 
    for (Annotation annotation : parameter.getDeclaredAnnotations()) { 
     if (Required.class.getName().equals(annotation.annotationType().getName())) { 
      return true; 
     } 
    } 
    return false; 
} 

eitherway ich frage mich, warum isAnnotionaPresent() funktioniert nicht?!

+1

Interessant. Machst du etwas witziges in deinem Anmerkungstyp? Wie überschreiben 'equals()' oder 'hashCode()'? Da Sie nach dem Namen der Annotationsklasse vergleichen (was natürlich keine sehr robuste Methode ist), nehme ich an, dass Sie Probleme haben, direkt mit 'equals()' zu vergleichen? Debugging-Tipp: Versuchen Sie, das Ergebnis von 'equals()' und 'hashcode()' neben den Namen, die Sie gerade vergleichen, auszudrucken und zu sehen, ob die Dinge richtig aussehen. – Zero3

+0

Hallo Zero3, meine Anmerkung sieht genauso aus wie das Ihre: '@Target (ElementType.PARAMETER) @Retention (RetentionPolicy.RUNTIME) @Documented public @interface Erforderlich { // Dies ist nur ein Marker Annotation, also nichts hier drin. } ' Die' hashCode() 'der verglichenen Namen sind gleich, also sollte in meiner Arbeit etwas stimmen ;-) – raho

+0

Hmm. Eigentlich meinte ich, dass Sie versuchen könnten, 'Required.class.hashcode()', 'annotation.annotationType(). Hashcode()' und 'Required.class.equals (annotation.annotationType())' zu drucken. Wenn diese nicht identisch sind wie erwartet, könnte dies die Ursache für Ihre Probleme sein. Eine andere Ursache könnte natürlich sein, wenn Sie fälschlicherweise eine falsche Request-Annotation in Ihre Endpunkt-Java-Datei importiert haben (Sie haben nicht das vollständige Codebeispiel in Ihrem ursprünglichen Post veröffentlicht, daher ist es schwierig, hier das vollständige Bild zu erhalten). – Zero3