Skip to content
This repository has been archived by the owner on Jun 3, 2024. It is now read-only.

Add @Metric and deprecate @InjectMetric #58

Merged
merged 1 commit into from
Dec 15, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import java.lang.annotation.Target;

/**
* Deprecated, use @Metric instead.
*
* An annotation requesting that a metric be injected
* <p/>
* Given a field like this:
Expand All @@ -32,6 +34,7 @@
* A meter for the defining class with the name {@code someTimer} will be created. It will be up to the user
* to mark the meter. This annotation can be used on fields of type Meter, Timer, Counter, and Histogram.
*/
@Deprecated
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface InjectMetric {
Expand Down
50 changes: 50 additions & 0 deletions src/main/java/com/ryantenney/metrics/annotation/Metric.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* Copyright (C) 2012 Ryan W Tenney ([email protected])
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.ryantenney.metrics.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* An annotation requesting that a metric be injected
* <p/>
* Given a field like this:
* <pre><code>
* \@Metric
* public Meter someTimer;
* </code></pre>
* <p/>
* A meter for the defining class with the name {@code someTimer} will be created. It will be up to the user
* to mark the meter. This annotation can be used on fields of type Meter, Timer, Counter, and Histogram.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Metric {

/**
* The name of the metric.
*/
String name() default "";

/**
* If {@code true}, use the given name an as absolute name. If {@code false}, use the given name
* relative to the annotated class.
*/
boolean absolute() default false;

}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import com.codahale.metrics.Timer;
import com.ryantenney.metrics.annotation.InjectMetric;

@SuppressWarnings("deprecation")
class InjectMetricAnnotationBeanPostProcessor implements BeanPostProcessor, Ordered {

private static final Logger LOG = LoggerFactory.getLogger(InjectMetricAnnotationBeanPostProcessor.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/**
* Copyright (C) 2012 Ryan W Tenney ([email protected])
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.ryantenney.metrics.spring;

import java.lang.reflect.Field;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.FieldCallback;

import com.codahale.metrics.Counter;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
import com.ryantenney.metrics.annotation.Metric;

class MetricAnnotationBeanPostProcessor implements BeanPostProcessor, Ordered {

private static final Logger LOG = LoggerFactory.getLogger(MetricAnnotationBeanPostProcessor.class);

private static final AnnotationFilter FILTER = new AnnotationFilter(Metric.class);

private final MetricRegistry metrics;

public MetricAnnotationBeanPostProcessor(final MetricRegistry metrics) {
this.metrics = metrics;
}

@Override
public Object postProcessBeforeInitialization(final Object bean, String beanName) {
final Class<?> targetClass = AopUtils.getTargetClass(bean);

ReflectionUtils.doWithFields(targetClass, new FieldCallback() {
@Override
public void doWith(Field field) throws IllegalAccessException {
final Metric annotation = field.getAnnotation(Metric.class);
final String metricName = Util.forMetricField(targetClass, field, annotation);

final Class<?> type = field.getType();
if (!com.codahale.metrics.Metric.class.isAssignableFrom(type)) {
throw new IllegalArgumentException("Field " + targetClass.getCanonicalName() + "."
+ field.getName() + " must be a subtype of " + com.codahale.metrics.Metric.class.getCanonicalName());
}

ReflectionUtils.makeAccessible(field);

// Get the value of the field annotated with @Metric
com.codahale.metrics.Metric metric = (com.codahale.metrics.Metric) ReflectionUtils.getField(field, bean);

if (metric == null) {
// If null, create a metric of the appropriate type and inject it
metric = getMetric(metrics, type, metricName);
ReflectionUtils.setField(field, bean, metric);
LOG.debug("Injected metric {} for field {}.{}", metricName, targetClass.getCanonicalName(), field.getName());
}
else {
// If non-null, register that instance of the metric
try {
// Attempt to register that instance of the metric
metrics.register(metricName, metric);
LOG.debug("Registered metric {} for field {}.{}", metricName, targetClass.getCanonicalName(), field.getName());
}
catch (IllegalArgumentException ex1) {
// A metric is already registered under that name
// (Cannot determine the cause without parsing the Exception's message)
try {
metric = getMetric(metrics, type, metricName);
ReflectionUtils.setField(field, bean, metric);
LOG.debug("Injected metric {} for field {}.{}", metricName, targetClass.getCanonicalName(), field.getName());
}
catch (IllegalArgumentException ex2) {
// A metric of a different type is already registered under that name
throw new IllegalArgumentException("Error injecting metric for field "
+ targetClass.getCanonicalName() + "." + field.getName(), ex2);
}
}
}
}
}, FILTER);

return bean;
}

private com.codahale.metrics.Metric getMetric(MetricRegistry metricRegistry, Class<?> type, String metricName) {
com.codahale.metrics.Metric metric;
if (Meter.class == type) {
metric = metrics.meter(metricName);
}
else if (Timer.class == type) {
metric = metrics.timer(metricName);
}
else if (Counter.class == type) {
metric = metrics.counter(metricName);
}
else if (Histogram.class == type) {
metric = metrics.histogram(metricName);
}
else {
throw new IllegalArgumentException("Invalid @Metric type " + type.getCanonicalName());
}
return metric;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
return bean;
}

@Override
public int getOrder() {
return LOWEST_PRECEDENCE - 2;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ public static CachedGaugeAnnotationBeanPostProcessor cachedGauge(final MetricReg
return new CachedGaugeAnnotationBeanPostProcessor(metricRegistry);
}

public static MetricAnnotationBeanPostProcessor metric(final MetricRegistry metricRegistry) {
return new MetricAnnotationBeanPostProcessor(metricRegistry);
}

@Deprecated
public static InjectMetricAnnotationBeanPostProcessor injectMetric(final MetricRegistry metricRegistry) {
return new InjectMetricAnnotationBeanPostProcessor(metricRegistry);
}
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/com/ryantenney/metrics/spring/Util.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@
import com.ryantenney.metrics.annotation.CachedGauge;
import com.ryantenney.metrics.annotation.Counted;
import com.ryantenney.metrics.annotation.InjectMetric;
import com.ryantenney.metrics.annotation.Metric;

@SuppressWarnings("deprecation")
class Util {

private Util() {
Expand Down Expand Up @@ -60,6 +62,10 @@ static String forInjectMetricField(Class<?> klass, Member member, InjectMetric a
return chooseName(annotation.name(), annotation.absolute(), klass, member);
}

static String forMetricField(Class<?> klass, Member member, Metric annotation) {
return chooseName(annotation.name(), annotation.absolute(), klass, member);
}

static String chooseName(String explicitName, boolean absolute, Class<?> klass, Member member, String... suffixes) {
if (explicitName != null && !explicitName.isEmpty()) {
if (absolute) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ public BeanDefinition parse(Element element, ParserContext parserContext) {
.setFactoryMethod("cachedGauge")
.addConstructorArgReference(metricsBeanName));

registerComponent(parserContext,
build(MetricsBeanPostProcessorFactory.class, source, ROLE_INFRASTRUCTURE)
.setFactoryMethod("metric")
.addConstructorArgReference(metricsBeanName));

registerComponent(parserContext,
build(MetricsBeanPostProcessorFactory.class, source, ROLE_INFRASTRUCTURE)
.setFactoryMethod("injectMetric")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,13 @@ public BeanPostProcessor cachedGaugeAnnotationBeanPostProcessor() {

@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public BeanPostProcessor metricAnnotationBeanPostProcessor() {
return MetricsBeanPostProcessorFactory.metric(getMetricRegistry());
}

@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@SuppressWarnings("deprecation")
public BeanPostProcessor injectMetricAnnotationBeanPostProcessor() {
return MetricsBeanPostProcessorFactory.injectMetric(getMetricRegistry());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:injected-metrics.xml")
@SuppressWarnings("deprecation")
public class InjectMetricTest {

@Autowired
Expand Down
107 changes: 107 additions & 0 deletions src/test/java/com/ryantenney/metrics/spring/MetricAnnotationTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/**
* Copyright (C) 2012 Ryan W Tenney ([email protected])
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.ryantenney.metrics.spring;

import static com.ryantenney.metrics.spring.TestUtil.forMetricField;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.codahale.metrics.Counter;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
import com.codahale.metrics.UniformReservoir;
import com.ryantenney.metrics.annotation.Metric;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:metric-annotation.xml")
public class MetricAnnotationTest {

@Autowired
@Qualifier("target1")
MetricAnnotationTest.Target target;

@Autowired
@Qualifier("target2")
MetricAnnotationTest.Target target2;

@Autowired
MetricRegistry metricRegistry;

@Test
public void targetIsNotNull() throws Exception {
assertNotNull(target);
assertNotNull(target2);

assertNotNull(target.theNameForTheMeter);
assertNotNull(target2.theNameForTheMeter);
Meter meter = (Meter) forMetricField(metricRegistry, MetricAnnotationTest.Target.class, "theNameForTheMeter");
assertSame(target.theNameForTheMeter, meter);
assertSame(target2.theNameForTheMeter, meter);

assertNotNull(target.timer);
assertNotNull(target2.timer);
Timer timer = (Timer) forMetricField(metricRegistry, MetricAnnotationTest.Target.class, "timer");
assertSame(target.timer, timer);
assertSame(target2.timer, timer);

assertNotNull(target.counter);
assertNotNull(target2.counter);
Counter ctr = (Counter) forMetricField(metricRegistry, MetricAnnotationTest.Target.class, "counter");
assertSame(target.counter, ctr);
assertSame(target2.counter, ctr);

assertNotNull(target.histogram);
assertNotNull(target2.histogram);
Histogram hist = (Histogram) forMetricField(metricRegistry, MetricAnnotationTest.Target.class, "histogram");
assertSame(target.histogram, hist);
assertSame(target2.histogram, hist);

assertNotNull(target.uniformHistogram);
assertNotNull(target2.uniformHistogram);
Histogram uniHist = (Histogram) forMetricField(metricRegistry, MetricAnnotationTest.Target.class, "uniformHistogram");
assertSame(target.uniformHistogram, uniHist);
assertSame(target2.uniformHistogram, uniHist);
}

public static class Target {

@Metric
public Meter theNameForTheMeter;

@Metric(name = "theNameForTheTimer")
private Timer timer;

@Metric(name = "group.type.counter", absolute = true)
Counter counter;

@Metric
Histogram histogram;

@Metric
Histogram uniformHistogram = new Histogram(new UniformReservoir());

}

}
1 change: 1 addition & 0 deletions src/test/java/com/ryantenney/metrics/spring/TestSuite.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
MeteredClassImpementsInterfaceTest.class,
MeteredClassTest.class,
MeteredInterfaceTest.class,
MetricAnnotationTest.class,
ProxyTargetClassTest.class,
RegistryTest.class,
ReporterTest.class,
Expand Down
Loading