diff --git a/src/main/java/com/ryantenney/metrics/spring/AbstractMetricMethodInterceptor.java b/src/main/java/com/ryantenney/metrics/spring/AbstractMetricMethodInterceptor.java index 3ea83162..48db7d23 100644 --- a/src/main/java/com/ryantenney/metrics/spring/AbstractMetricMethodInterceptor.java +++ b/src/main/java/com/ryantenney/metrics/spring/AbstractMetricMethodInterceptor.java @@ -52,9 +52,23 @@ abstract class AbstractMetricMethodInterceptor implemen ReflectionUtils.doWithMethods(targetClass, this, methodFilter); } + private AnnotationMetricPair getAnnotationMetricPair(MethodInvocation invocation) { + final Method method = invocation.getMethod(); + AnnotationMetricPair annotationMetricPair = metrics.get(MethodKey.forMethod(method)); + if (annotationMetricPair != null) { + return annotationMetricPair; + } + for (MethodKey methodKey: metrics.keySet()) { + if (methodKey.isCompatibleWith(invocation)) { + return metrics.get(methodKey); + } + } + return null; + } + @Override public Object invoke(MethodInvocation invocation) throws Throwable { - final AnnotationMetricPair annotationMetricPair = metrics.get(MethodKey.forMethod(invocation.getMethod())); + final AnnotationMetricPair annotationMetricPair = getAnnotationMetricPair(invocation); if (annotationMetricPair != null) { return invoke(invocation, annotationMetricPair.getMeter(), annotationMetricPair.getAnnotation()); } diff --git a/src/main/java/com/ryantenney/metrics/spring/MethodKey.java b/src/main/java/com/ryantenney/metrics/spring/MethodKey.java index b4f94378..0c25a126 100644 --- a/src/main/java/com/ryantenney/metrics/spring/MethodKey.java +++ b/src/main/java/com/ryantenney/metrics/spring/MethodKey.java @@ -18,6 +18,8 @@ import java.lang.reflect.Method; import java.util.Arrays; +import org.aopalliance.intercept.MethodInvocation; + class MethodKey { private final String name; @@ -36,6 +38,32 @@ private MethodKey(Method method) { this.hashCode = computeHashCode(); } + public boolean isCompatibleWith(MethodInvocation invocation) { + final Method invokedMethod = invocation.getMethod(); + final Object[] invokedParameters = invocation.getArguments(); + + if (!name.equals(invokedMethod.getName())) { + return false; + } + if (!invokedMethod.getReturnType().isAssignableFrom(returnType)) { + return false; + } + if (invokedParameters.length != parameterTypes.length) { + return false; + } + for (int i=0; i invokedParameterClass = invokedParameters[i].getClass(); + Class parameterClass = parameterTypes[i]; + if (!parameterClass.isAssignableFrom(invokedParameterClass)) { + return false; + } + } + return true; + } + @Override public int hashCode() { return hashCode; diff --git a/src/test/java/com/ryantenney/metrics/spring/MeteredClassImpementsInterfaceTest.java b/src/test/java/com/ryantenney/metrics/spring/MeteredClassImpementsInterfaceTest.java index 6a5d227d..5402f683 100644 --- a/src/test/java/com/ryantenney/metrics/spring/MeteredClassImpementsInterfaceTest.java +++ b/src/test/java/com/ryantenney/metrics/spring/MeteredClassImpementsInterfaceTest.java @@ -38,22 +38,23 @@ /** * Purpose of test: - * Verify that calling a method from a class implementing an interface + * Verify that calling a method from a class implementing an interface * but annotated at the class level doesn't throw an NPE * Also verifies that it does register metrics. */ public class MeteredClassImpementsInterfaceTest { - MeteredClassInterface meteredClass; + MeteredClassInterface meteredClass; ClassPathXmlApplicationContext ctx; MetricRegistry metricRegistry; + @SuppressWarnings("unchecked") @Before public void init() { this.ctx = new ClassPathXmlApplicationContext("classpath:metered-interface-impl.xml"); this.metricRegistry = this.ctx.getBean(MetricRegistry.class); - this.meteredClass = (MeteredClassInterface) this.ctx.getBean("metered-class-interface"); + this.meteredClass = (MeteredClassInterface) this.ctx.getBean("metered-class-interface"); } @After @@ -68,19 +69,19 @@ public void noMetricsRegistered() { @Test public void testMeteredClassInterface() { - MeteredClassInterface mi = ctx.getBean(MeteredClassInterface.class); - Assert.assertNotNull("Expected to be able to get MeteredInterface by interface and not by class.", mi); + Assert.assertNotNull("Expected to be able to get MeteredInterface by interface and not by class.", + ctx.getBean(MeteredClassInterface.class)); } @Test(expected = NoSuchBeanDefinitionException.class) public void testMeteredInterfaceImpl() { - MeteredClassInterface mc = ctx.getBean(MeteredClassImpl.class); + MeteredClassInterface mc = ctx.getBean(MeteredClassImpl.class); Assert.assertNull("Expected to be unable to get MeteredInterfaceImpl by class.", mc); } @Test public void testTimedMethod() { - ctx.getBean(MeteredClassInterface.class).timedMethod(); + meteredClass.timedMethod(null); Assert.assertFalse("Metrics should be registered", this.metricRegistry.getTimers().isEmpty()); } @@ -112,8 +113,7 @@ public void timedMethod() throws Throwable { Timer timedMethod = forTimedMethod(metricRegistry, MeteredClassImpl.class, "timedMethod"); assertEquals(0, timedMethod.getCount()); - - meteredClass.timedMethod(); + meteredClass.timedMethod(new BogusClassBExtension()); assertEquals(1, timedMethod.getCount()); } @@ -143,37 +143,38 @@ public void run() { assertEquals(0, countedMethod.getCount()); } - public interface MeteredClassInterface { + public interface MeteredClassInterface { - public void timedMethod(); + public A timedMethod(B arg); - public void meteredMethod(); + public A meteredMethod(); - public void countedMethod(Runnable runnable); + public A countedMethod(Runnable runnable); - public void exceptionMeteredMethod() throws Throwable; + public A exceptionMeteredMethod() throws Throwable; } - public static class MeteredClassImpl implements MeteredClassInterface { + public static class MeteredClassImpl implements MeteredClassInterface { @Override @Timed - public void timedMethod() {} + public BogusClassA timedMethod(BogusClassB arg) {return new BogusClassA();} @Override @Metered - public void meteredMethod() {} + public BogusClassA meteredMethod() {return null; } @Override @Counted - public void countedMethod(Runnable runnable) { + public BogusClassA countedMethod(Runnable runnable) { if (runnable != null) runnable.run(); + return null; } @Override @ExceptionMetered - public void exceptionMeteredMethod() throws Throwable { + public BogusClassA exceptionMeteredMethod() throws Throwable { throw new BogusException(); } @@ -182,4 +183,9 @@ public void exceptionMeteredMethod() throws Throwable { @SuppressWarnings("serial") public static class BogusException extends Throwable {} + public static class BogusClassA {} + public static class BogusClassAExtension extends BogusClassA {} + public static class BogusClassB {} + public static class BogusClassBExtension extends BogusClassB {} + } diff --git a/src/test/java/com/ryantenney/metrics/spring/TestUtil.java b/src/test/java/com/ryantenney/metrics/spring/TestUtil.java index d09c3148..1c03278a 100755 --- a/src/test/java/com/ryantenney/metrics/spring/TestUtil.java +++ b/src/test/java/com/ryantenney/metrics/spring/TestUtil.java @@ -160,7 +160,7 @@ private static Method findMethod(Class clazz, String methodName) { log.info("Looking for method {}.{}", clazz, methodName); List methodsFound = new ArrayList(); for (Method method : clazz.getDeclaredMethods()) { - if (method.getName().equals(methodName)) { + if (method.getName().equals(methodName) && !method.isBridge()) { methodsFound.add(method); } }