SLING-5966 - Add Gauge support to metrics
git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1770122 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/main/java/org/apache/sling/commons/metrics/Gauge.java b/src/main/java/org/apache/sling/commons/metrics/Gauge.java
new file mode 100644
index 0000000..ac723a6
--- /dev/null
+++ b/src/main/java/org/apache/sling/commons/metrics/Gauge.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.sling.commons.metrics;
+
+import aQute.bnd.annotation.ConsumerType;
+
+/**
+ * A gauge metric is an instantaneous reading of a particular value. To instrument a queue's depth,
+ * for example:<br>
+ * <pre><code>
+ * final Queue<String> queue = new ConcurrentLinkedQueue<String>();
+ * final Gauge<Integer> queueDepth = new Gauge<Integer>() {
+ * public Integer getValue() {
+ * return queue.size();
+ * }
+ * };
+ * </code></pre>
+ *
+ * <p> A Gauge instance should be registered with OSGi ServiceRegistry with {@code Gauge#NAME} set
+ * to Gauge name. Then the Gauge instance would be registered with MetricService via whiteboard
+ * pattern
+ *
+ * @param <T> the type of the metric's value
+ */
+@ConsumerType
+public interface Gauge<T> {
+ /**
+ * Service property name which determines the name of the Gauge
+ */
+ String NAME = "name";
+ /**
+ * Returns the metric's current value.
+ *
+ * @return the metric's current value
+ */
+ T getValue();
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/commons/metrics/internal/GaugeManager.java b/src/main/java/org/apache/sling/commons/metrics/internal/GaugeManager.java
new file mode 100644
index 0000000..bc48066
--- /dev/null
+++ b/src/main/java/org/apache/sling/commons/metrics/internal/GaugeManager.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.sling.commons.metrics.internal;
+
+import java.io.Closeable;
+import java.util.Collections;
+
+import com.codahale.metrics.MetricRegistry;
+import org.apache.sling.commons.metrics.Gauge;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class GaugeManager implements ServiceTrackerCustomizer<Gauge, GaugeManager.GaugeImpl>, Closeable {
+ private final Logger log = LoggerFactory.getLogger(getClass());
+ private final MetricRegistry registry;
+ private final BundleMetricsMapper mapper;
+ private final BundleContext bundleContext;
+ private ServiceTracker<Gauge, GaugeImpl> tracker;
+
+ public GaugeManager(BundleContext context, MetricRegistry registry, BundleMetricsMapper mapper) {
+ this.registry = registry;
+ this.mapper = mapper;
+ this.bundleContext = context;
+ this.tracker = new ServiceTracker<>(context, Gauge.class, this);
+ tracker.open();
+ }
+
+ //~-------------------------------------< ServiceTrackerCustomizer >
+
+ @Override
+ public GaugeImpl addingService(ServiceReference<Gauge> reference) {
+ String name = (String) reference.getProperty(Gauge.NAME);
+ if (name == null){
+ log.warn("A {} service is registered without [{}] property. This Gauge would not be " +
+ "registered with MetricsRegistry", reference, Gauge.NAME);
+ return null;
+ }
+
+ Gauge gauge = bundleContext.getService(reference);
+ GaugeImpl gaugeImpl = new GaugeImpl(name, gauge);
+ register(reference, gaugeImpl);
+ return gaugeImpl;
+ }
+
+ @Override
+ public void modifiedService(ServiceReference<Gauge> reference, GaugeImpl service) {
+ String name = (String) reference.getProperty(Gauge.NAME);
+ if (name == null){
+ return;
+ }
+
+ if (!name.equals(service.name)){
+ unregister(service);
+ service.name = name;
+ register(reference, service);
+ }
+ }
+
+ @Override
+ public void removedService(ServiceReference<Gauge> reference, GaugeImpl service) {
+ unregister(service);
+ }
+
+ //~------------------------------------< Closeable >
+
+ @Override
+ public void close() {
+ tracker.close();
+ }
+
+ //~-------------------------------------< Internal >
+
+ private void unregister(GaugeImpl service) {
+ mapper.unregister(Collections.singleton(service.name));
+ }
+
+ private void register(ServiceReference<Gauge> reference, GaugeImpl gaugeImpl) {
+ mapper.addMapping(gaugeImpl.name, reference.getBundle());
+ registry.register(gaugeImpl.name, gaugeImpl);
+ }
+
+ //~--------------------------------------< GaugeImpl >
+
+ public static class GaugeImpl implements com.codahale.metrics.Gauge {
+ String name;
+ final Gauge gauge;
+
+ public GaugeImpl(String name, Gauge gauge) {
+ this.name = name;
+ this.gauge = gauge;
+ }
+
+ @Override
+ public Object getValue() {
+ return gauge.getValue();
+ }
+ }
+}
diff --git a/src/main/java/org/apache/sling/commons/metrics/internal/MetricsServiceImpl.java b/src/main/java/org/apache/sling/commons/metrics/internal/MetricsServiceImpl.java
index 03efcee..1bc6514 100644
--- a/src/main/java/org/apache/sling/commons/metrics/internal/MetricsServiceImpl.java
+++ b/src/main/java/org/apache/sling/commons/metrics/internal/MetricsServiceImpl.java
@@ -54,6 +54,7 @@
private final ConcurrentMap<String, Metric> metrics = new ConcurrentHashMap<>();
private final MetricRegistry registry = new MetricRegistry();
private final BundleMetricsMapper metricsMapper = new BundleMetricsMapper(registry);
+ private GaugeManager gaugeManager;
@Reference(cardinality = ReferenceCardinality.OPTIONAL_UNARY)
private MBeanServer server;
@@ -64,6 +65,8 @@
private void activate(BundleContext context, Map<String, Object> config) {
enableJMXReporter();
+ gaugeManager = new GaugeManager(context, registry, metricsMapper);
+
final Dictionary<String, String> svcProps = new Hashtable<>();
svcProps.put(Constants.SERVICE_DESCRIPTION, "Apache Sling Metrics Service");
svcProps.put(Constants.SERVICE_VENDOR, "The Apache Software Foundation");
@@ -84,6 +87,8 @@
}
regs.clear();
+ gaugeManager.close();
+
metrics.clear();
if (reporter != null) {
diff --git a/src/main/java/org/apache/sling/commons/metrics/package-info.java b/src/main/java/org/apache/sling/commons/metrics/package-info.java
index aa5623b..5b24541 100644
--- a/src/main/java/org/apache/sling/commons/metrics/package-info.java
+++ b/src/main/java/org/apache/sling/commons/metrics/package-info.java
@@ -22,7 +22,7 @@
*
* @version 1.0
*/
-@Version("1.0")
+@Version("1.1.0")
@Export(optional = "provide:=true")
package org.apache.sling.commons.metrics;
diff --git a/src/test/java/org/apache/sling/commons/metrics/internal/MetricServiceTest.java b/src/test/java/org/apache/sling/commons/metrics/internal/MetricServiceTest.java
index 72a8e02..a8af34a 100644
--- a/src/test/java/org/apache/sling/commons/metrics/internal/MetricServiceTest.java
+++ b/src/test/java/org/apache/sling/commons/metrics/internal/MetricServiceTest.java
@@ -31,20 +31,25 @@
import com.codahale.metrics.JmxReporter;
import com.codahale.metrics.MetricRegistry;
import org.apache.sling.commons.metrics.Counter;
+import org.apache.sling.commons.metrics.Gauge;
import org.apache.sling.commons.metrics.Histogram;
import org.apache.sling.commons.metrics.Meter;
import org.apache.sling.commons.metrics.MetricsService;
import org.apache.sling.commons.metrics.Timer;
+import org.apache.sling.testing.mock.osgi.MapUtil;
import org.apache.sling.testing.mock.osgi.MockOsgi;
import org.apache.sling.testing.mock.osgi.junit.OsgiContext;
import org.junit.After;
import org.junit.Rule;
import org.junit.Test;
+import org.osgi.framework.ServiceRegistration;
import static org.apache.sling.commons.metrics.internal.BundleMetricsMapper.JMX_TYPE_METRICS;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
@@ -145,6 +150,19 @@
}
+ @Test
+ public void gaugeRegistration() throws Exception{
+ activate();
+ ServiceRegistration<Gauge> reg = context.bundleContext().registerService(Gauge.class, new TestGauge(42),
+ MapUtil.toDictionary(Gauge.NAME, "foo"));
+
+ assertTrue(getRegistry().getGauges().containsKey("foo"));
+ assertEquals(42, getRegistry().getGauges().get("foo").getValue());
+
+ reg.unregister();
+ assertFalse(getRegistry().getGauges().containsKey("foo"));
+ }
+
private MetricRegistry getRegistry(){
return context.getService(MetricRegistry.class);
}
@@ -153,4 +171,17 @@
MockOsgi.activate(service, context.bundleContext(), Collections.<String, Object>emptyMap());
}
+ private static class TestGauge implements Gauge {
+ int value;
+
+ public TestGauge(int value){
+ this.value = value;
+ }
+
+ @Override
+ public Object getValue() {
+ return value;
+ }
+ }
+
}