Skip to content

Commit 898f7e0

Browse files
Mark Millerjoschi
authored andcommitted
FEAT: Jersey: check impl method for annotations before interface method
Metrics annotations (@timed, @metered, etc) don’t allow annotating a resource on the *implementing* method, only on the *defining* (interface) method (or more accurately, the method corresponding to the @path definition, more or less). This patch enhances dropwizard-metrics to extract metrics annotations more intelligently, preferring the annotation on the implementation (and falling back to the interface / definitionMethod if the implementation annotation is absent).
1 parent add6411 commit 898f7e0

File tree

3 files changed

+69
-12
lines changed

3 files changed

+69
-12
lines changed

metrics-jersey2/src/main/java/com/codahale/metrics/jersey2/InstrumentedResourceMethodApplicationListener.java

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -405,14 +405,33 @@ private <T extends Annotation> T getClassLevelAnnotation(final Resource resource
405405
return annotation;
406406
}
407407

408+
/**
409+
* @param method the method to examine for the annotation
410+
* @param annotation the annotation class (e.g. Timed, Metered, etc)
411+
* @return the annotation (of type {@code T})from the given method, or null.
412+
* @param <T> the type of annotation to return
413+
*/
414+
private static <T extends Annotation> T findAnnotation(final ResourceMethod method, final Class<T> annotation) {
415+
// In code-generation situations, developers define their API (via OpenAPI or gRPC or ___), and JAX-RS
416+
// interfaces are generated for the defined services (which define @Path, @Produces, etc).
417+
// The developer is then responsible for implementing those service interfaces.
418+
// For fetching metrics annotations, we presume that any annotation on the implementing method should
419+
// take precedence over the interface method, as the developer controls the former more directly.
420+
T result = method.getInvocable().getHandlingMethod().getAnnotation(annotation);
421+
if (result == null) {
422+
result = method.getInvocable().getDefinitionMethod().getAnnotation(annotation);
423+
}
424+
return result;
425+
}
426+
408427
private void registerTimedAnnotations(final ResourceMethod method, final Timed classLevelTimed) {
409428
final Method definitionMethod = method.getInvocable().getDefinitionMethod();
410429
if (classLevelTimed != null) {
411430
registerTimers(method, definitionMethod, classLevelTimed);
412431
return;
413432
}
414433

415-
final Timed annotation = definitionMethod.getAnnotation(Timed.class);
434+
final Timed annotation = findAnnotation(method, Timed.class);
416435
if (annotation != null) {
417436
registerTimers(method, definitionMethod, annotation);
418437
}
@@ -434,7 +453,7 @@ private void registerMeteredAnnotations(final ResourceMethod method, final Meter
434453
meters.putIfAbsent(definitionMethod, meterMetric(metrics, method, classLevelMetered));
435454
return;
436455
}
437-
final Metered annotation = definitionMethod.getAnnotation(Metered.class);
456+
final Metered annotation = findAnnotation(method, Metered.class);
438457

439458
if (annotation != null) {
440459
meters.putIfAbsent(definitionMethod, meterMetric(metrics, method, annotation));
@@ -448,7 +467,7 @@ private void registerExceptionMeteredAnnotations(final ResourceMethod method, fi
448467
exceptionMeters.putIfAbsent(definitionMethod, new ExceptionMeterMetric(metrics, method, classLevelExceptionMetered));
449468
return;
450469
}
451-
final ExceptionMetered annotation = definitionMethod.getAnnotation(ExceptionMetered.class);
470+
final ExceptionMetered annotation = findAnnotation(method, ExceptionMetered.class);
452471

453472
if (annotation != null) {
454473
exceptionMeters.putIfAbsent(definitionMethod, new ExceptionMeterMetric(metrics, method, annotation));
@@ -462,7 +481,7 @@ private void registerResponseMeteredAnnotations(final ResourceMethod method, fin
462481
responseMeters.putIfAbsent(definitionMethod, new ResponseMeterMetric(metrics, method, classLevelResponseMetered));
463482
return;
464483
}
465-
final ResponseMetered annotation = definitionMethod.getAnnotation(ResponseMetered.class);
484+
final ResponseMetered annotation = findAnnotation(method, ResponseMetered.class);
466485

467486
if (annotation != null) {
468487
responseMeters.putIfAbsent(definitionMethod, new ResponseMeterMetric(metrics, method, annotation));

metrics-jersey3/src/main/java/com/codahale/metrics/jersey3/InstrumentedResourceMethodApplicationListener.java

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -406,14 +406,33 @@ private <T extends Annotation> T getClassLevelAnnotation(final Resource resource
406406
return annotation;
407407
}
408408

409+
/**
410+
* @param method the method to examine for the annotation
411+
* @param annotation the annotation class (e.g. Timed, Metered, etc)
412+
* @return the annotation (of type {@code T})from the given method, or null.
413+
* @param <T> the type of annotation to return
414+
*/
415+
private static <T extends Annotation> T findAnnotation(final ResourceMethod method, final Class<T> annotation) {
416+
// In code-generation situations, developers define their API (via OpenAPI or gRPC or ___), and JAX-RS
417+
// interfaces are generated for the defined services (which define @Path, @Produces, etc).
418+
// The developer is then responsible for implementing those service interfaces.
419+
// For fetching metrics annotations, we presume that any annotation on the implementing method should
420+
// take precedence over the interface method, as the developer controls the former more directly.
421+
T result = method.getInvocable().getHandlingMethod().getAnnotation(annotation);
422+
if (result == null) {
423+
result = method.getInvocable().getDefinitionMethod().getAnnotation(annotation);
424+
}
425+
return result;
426+
}
427+
409428
private void registerTimedAnnotations(final ResourceMethod method, final Timed classLevelTimed) {
410429
final Method definitionMethod = method.getInvocable().getDefinitionMethod();
411430
if (classLevelTimed != null) {
412431
registerTimers(method, definitionMethod, classLevelTimed);
413432
return;
414433
}
415434

416-
final Timed annotation = definitionMethod.getAnnotation(Timed.class);
435+
final Timed annotation = findAnnotation(method, Timed.class);
417436
if (annotation != null) {
418437
registerTimers(method, definitionMethod, annotation);
419438
}
@@ -435,7 +454,7 @@ private void registerMeteredAnnotations(final ResourceMethod method, final Meter
435454
meters.putIfAbsent(definitionMethod, meterMetric(metrics, method, classLevelMetered));
436455
return;
437456
}
438-
final Metered annotation = definitionMethod.getAnnotation(Metered.class);
457+
final Metered annotation = findAnnotation(method, Metered.class);
439458

440459
if (annotation != null) {
441460
meters.putIfAbsent(definitionMethod, meterMetric(metrics, method, annotation));
@@ -449,7 +468,7 @@ private void registerExceptionMeteredAnnotations(final ResourceMethod method, fi
449468
exceptionMeters.putIfAbsent(definitionMethod, new ExceptionMeterMetric(metrics, method, classLevelExceptionMetered));
450469
return;
451470
}
452-
final ExceptionMetered annotation = definitionMethod.getAnnotation(ExceptionMetered.class);
471+
final ExceptionMetered annotation = findAnnotation(method, ExceptionMetered.class);
453472

454473
if (annotation != null) {
455474
exceptionMeters.putIfAbsent(definitionMethod, new ExceptionMeterMetric(metrics, method, annotation));
@@ -463,7 +482,7 @@ private void registerResponseMeteredAnnotations(final ResourceMethod method, fin
463482
responseMeters.putIfAbsent(definitionMethod, new ResponseMeterMetric(metrics, method, classLevelResponseMetered));
464483
return;
465484
}
466-
final ResponseMetered annotation = definitionMethod.getAnnotation(ResponseMetered.class);
485+
final ResponseMetered annotation = findAnnotation(method, ResponseMetered.class);
467486

468487
if (annotation != null) {
469488
responseMeters.putIfAbsent(definitionMethod, new ResponseMeterMetric(metrics, method, annotation));

metrics-jersey31/src/main/java/io/dropwizard/metrics/jersey31/InstrumentedResourceMethodApplicationListener.java

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -405,14 +405,33 @@ private <T extends Annotation> T getClassLevelAnnotation(final Resource resource
405405
return annotation;
406406
}
407407

408+
/**
409+
* @param method the method to examine for the annotation
410+
* @param annotation the annotation class (e.g. Timed, Metered, etc)
411+
* @return the annotation (of type {@code T})from the given method, or null.
412+
* @param <T> the type of annotation to return
413+
*/
414+
private static <T extends Annotation> T findAnnotation(final ResourceMethod method, final Class<T> annotation) {
415+
// In code-generation situations, developers define their API (via OpenAPI or gRPC or ___), and JAX-RS
416+
// interfaces are generated for the defined services (which define @Path, @Produces, etc).
417+
// The developer is then responsible for implementing those service interfaces.
418+
// For fetching metrics annotations, we presume that any annotation on the implementing method should
419+
// take precedence over the interface method, as the developer controls the former more directly.
420+
T result = method.getInvocable().getHandlingMethod().getAnnotation(annotation);
421+
if (result == null) {
422+
result = method.getInvocable().getDefinitionMethod().getAnnotation(annotation);
423+
}
424+
return result;
425+
}
426+
408427
private void registerTimedAnnotations(final ResourceMethod method, final Timed classLevelTimed) {
409428
final Method definitionMethod = method.getInvocable().getDefinitionMethod();
410429
if (classLevelTimed != null) {
411430
registerTimers(method, definitionMethod, classLevelTimed);
412431
return;
413432
}
414433

415-
final Timed annotation = definitionMethod.getAnnotation(Timed.class);
434+
final Timed annotation = findAnnotation(method, Timed.class);
416435
if (annotation != null) {
417436
registerTimers(method, definitionMethod, annotation);
418437
}
@@ -434,7 +453,7 @@ private void registerMeteredAnnotations(final ResourceMethod method, final Meter
434453
meters.putIfAbsent(definitionMethod, meterMetric(metrics, method, classLevelMetered));
435454
return;
436455
}
437-
final Metered annotation = definitionMethod.getAnnotation(Metered.class);
456+
final Metered annotation = findAnnotation(method, Metered.class);
438457

439458
if (annotation != null) {
440459
meters.putIfAbsent(definitionMethod, meterMetric(metrics, method, annotation));
@@ -448,7 +467,7 @@ private void registerExceptionMeteredAnnotations(final ResourceMethod method, fi
448467
exceptionMeters.putIfAbsent(definitionMethod, new ExceptionMeterMetric(metrics, method, classLevelExceptionMetered));
449468
return;
450469
}
451-
final ExceptionMetered annotation = definitionMethod.getAnnotation(ExceptionMetered.class);
470+
final ExceptionMetered annotation = findAnnotation(method, ExceptionMetered.class);
452471

453472
if (annotation != null) {
454473
exceptionMeters.putIfAbsent(definitionMethod, new ExceptionMeterMetric(metrics, method, annotation));
@@ -462,7 +481,7 @@ private void registerResponseMeteredAnnotations(final ResourceMethod method, fin
462481
responseMeters.putIfAbsent(definitionMethod, new ResponseMeterMetric(metrics, method, classLevelResponseMetered));
463482
return;
464483
}
465-
final ResponseMetered annotation = definitionMethod.getAnnotation(ResponseMetered.class);
484+
final ResponseMetered annotation = findAnnotation(method, ResponseMetered.class);
466485

467486
if (annotation != null) {
468487
responseMeters.putIfAbsent(definitionMethod, new ResponseMeterMetric(metrics, method, annotation));

0 commit comments

Comments
 (0)