[GERONIMO-6782] implement specification v3.0-RC2
diff --git a/geronimo-metrics-common/src/main/java/org/apache/geronimo/microprofile/metrics/common/RegistryImpl.java b/geronimo-metrics-common/src/main/java/org/apache/geronimo/microprofile/metrics/common/RegistryImpl.java
index 9aa2d29..6da5a7c 100644
--- a/geronimo-metrics-common/src/main/java/org/apache/geronimo/microprofile/metrics/common/RegistryImpl.java
+++ b/geronimo-metrics-common/src/main/java/org/apache/geronimo/microprofile/metrics/common/RegistryImpl.java
@@ -41,6 +41,8 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Function;
+import java.util.function.Supplier;
import static java.util.stream.Collectors.toCollection;
import static java.util.stream.Collectors.toMap;
@@ -49,10 +51,20 @@
private static final Tag[] NO_TAG = new Tag[0];
private final Type type;
+ private final Tag[] globalTags;
private final ConcurrentMap<MetricID, Holder<? extends Metric>> metrics = new ConcurrentHashMap<>();
public RegistryImpl(final Type type) {
+ this(type, new Tag[0]);
+ }
+
+ public RegistryImpl(final Type type, final Tag[] globalTags) {
this.type = type;
+ this.globalTags = globalTags;
+ }
+
+ public Tag[] getGlobalTags() {
+ return globalTags;
}
@Override
@@ -63,12 +75,9 @@
@Override
public <T extends Metric> T register(final Metadata metadata, final T metric, final Tag... tags) throws IllegalArgumentException {
final MetricID metricID = new MetricID(metadata.getName(), tags);
- final Holder<? extends Metric> holder = metrics.putIfAbsent(
+ final Holder<? extends Metric> present = metrics.putIfAbsent(
metricID, new Holder<>(metric, metadata, metricID));
- if (holder != null && !metadata.isReusable() && !holder.metadata.isReusable()) {
- throw new IllegalArgumentException("'" + metadata.getName() + "' metric already exists and is not reusable");
- }
- return metric;
+ return present != null ? (T) present.metric : metric;
}
@Override
@@ -123,15 +132,11 @@
Holder<? extends Metric> holder = metrics.get(metricID);
if (holder == null) {
holder = new Holder<>(new CounterImpl(
- metadata.getUnit().orElse(MetricUnits.NONE)), metadata, metricID);
+ metadata.getUnit() == null ? MetricUnits.NONE : metadata.getUnit()), enforceType(metadata, MetricType.COUNTER), metricID);
final Holder<? extends Metric> existing = metrics.putIfAbsent(holder.metricID, holder);
if (existing != null) {
holder = existing;
}
- } else if (!metadata.isReusable()) {
- throw new IllegalArgumentException("Metric " + metadata.getName() + " already exists and is not set as reusable");
- } else if (!holder.metadata.isReusable()) {
- throw new IllegalArgumentException("Metric " + metadata.getName() + " already exists and was not set as reusable");
}
if (!Counter.class.isInstance(holder.metric)) {
throw new IllegalArgumentException(holder.metric + " is not a counter");
@@ -170,43 +175,80 @@
Holder<? extends Metric> holder = metrics.get(metricID);
if (holder == null) {
holder = new Holder<>(new ConcurrentGaugeImpl(
- metadata.getUnit().orElse(MetricUnits.NONE)), metadata, metricID);
+ metadata.getUnit() == null ? MetricUnits.NONE : metadata.getUnit()), enforceType(metadata, MetricType.CONCURRENT_GAUGE), metricID);
final Holder<? extends Metric> existing = metrics.putIfAbsent(holder.metricID, holder);
if (existing != null) {
holder = existing;
}
- } else if (!metadata.isReusable()) {
- throw new IllegalArgumentException("Metric " + metadata.getName() + " already exists and is not set as reusable");
- } else if (!holder.metadata.isReusable()) {
- throw new IllegalArgumentException("Metric " + metadata.getName() + " already exists and was not set as reusable");
}
if (!ConcurrentGauge.class.isInstance(holder.metric)) {
- throw new IllegalArgumentException(holder.metric + " is not a counter");
+ throw new IllegalArgumentException(holder.metric + " is not a concurrent gauge");
}
return ConcurrentGauge.class.cast(holder.metric);
}
@Override
+ public <T, R extends Number> Gauge<R> gauge(final String name, final T object, final Function<T, R> func, Tag... tags) {
+ return gauge(new MetricID(name, tags), () -> func.apply(object));
+ }
+
+ @Override
+ public <T, R extends Number> Gauge<R> gauge(final MetricID metricID, final T object, final Function<T, R> func) {
+ return gauge(metricID, () -> func.apply(object));
+ }
+
+ @Override
+ public <T, R extends Number> Gauge<R> gauge(final Metadata metadata, final T object, final Function<T, R> func, final Tag... tags) {
+ final MetricID metricID = new MetricID(metadata.getName(), tags);
+ Holder<? extends Metric> holder = metrics.get(metricID);
+ if (holder == null) {
+ holder = new Holder<>(new SimpleGaugeImpl<>(() -> func.apply(object)), enforceType(metadata, MetricType.GAUGE), metricID);
+ final Holder<? extends Metric> existing = metrics.putIfAbsent(holder.metricID, holder);
+ if (existing != null) {
+ holder = existing;
+ }
+ }
+ if (!Gauge.class.isInstance(holder.metric)) {
+ throw new IllegalArgumentException(holder.metric + " is not a gauge");
+ }
+ return Gauge.class.cast(holder.metric);
+ }
+
+ @Override
+ public <T extends Number> Gauge<T> gauge(final String name, final Supplier<T> supplier, final Tag... tags) {
+ return gauge(new MetricID(name, tags), supplier);
+ }
+
+ @Override
+ public <T extends Number> Gauge<T> gauge(final MetricID metricID, final Supplier<T> supplier) {
+ Holder<? extends Metric> holder = metrics.get(metricID);
+ if (holder == null) {
+ holder = new Holder<>(
+ new SimpleGaugeImpl<>(supplier),
+ Metadata.builder().withName(metricID.getName()).withType(MetricType.GAUGE).build(),
+ metricID);
+ final Holder<? extends Metric> existing = metrics.putIfAbsent(holder.metricID, holder);
+ if (existing != null) {
+ holder = existing;
+ }
+ }
+ if (!Gauge.class.isInstance(holder.metric)) {
+ throw new IllegalArgumentException(holder.metric + " is not a gauge");
+ }
+ return Gauge.class.cast(holder.metric);
+ }
+
+ @Override
+ public <T extends Number> Gauge<T> gauge(final Metadata metadata, final Supplier<T> supplier, final Tag... tags) {
+ return register(metadata, new SimpleGaugeImpl<>(supplier), tags);
+ }
+
+ @Override
public Gauge<?> getGauge(MetricID metricID) {
return getMetric(metricID, Gauge.class);
}
@Override
- public Gauge<?> gauge(String name, Gauge<?> gauge) {
- return gauge(new MetricID(name), gauge);
- }
-
- @Override
- public Gauge<?> gauge(final String name, final Gauge<?> gauge, final Tag...tags) {
- return register(Metadata.builder().reusable().withName(name).withType(MetricType.GAUGE).build(), gauge, tags);
- }
-
- @Override
- public Gauge<?> gauge(final MetricID metricID, final Gauge<?> gauge) {
- return gauge(metricID.getName(), gauge, metricID.getTagsAsArray());
- }
-
- @Override
public Histogram getHistogram(MetricID metricID) {
return this.getMetric(metricID, Histogram.class);
}
@@ -236,15 +278,11 @@
final MetricID metricID = new MetricID(metadata.getName(), tags);
Holder<? extends Metric> holder = metrics.get(metricID);
if (holder == null) {
- holder = new Holder<>(new HistogramImpl(metadata.getUnit().orElse(MetricUnits.NONE)), metadata, metricID);
+ holder = new Holder<>(new HistogramImpl(metadata.getUnit() == null ? MetricUnits.NONE : metadata.getUnit()), enforceType(metadata, MetricType.HISTOGRAM), metricID);
final Holder<? extends Metric> existing = metrics.putIfAbsent(metricID, holder);
if (existing != null) {
holder = existing;
}
- } else if (!metadata.isReusable()) {
- throw new IllegalArgumentException("Metric " + metadata.getName() + " already exists and is not set as reusable");
- } else if (!holder.metadata.isReusable()) {
- throw new IllegalArgumentException("Metric " + metadata.getName() + " already exists and was not set as reusable");
}
if (!Histogram.class.isInstance(holder.metric)) {
throw new IllegalArgumentException(holder.metric + " is not a histogram");
@@ -282,15 +320,11 @@
final MetricID metricID = new MetricID(metadata.getName(), tags);
Holder<? extends Metric> holder = metrics.get(metricID);
if (holder == null) {
- holder = new Holder<>(new MeterImpl(metadata.getUnit().orElse(MetricUnits.NONE)), metadata, metricID);
+ holder = new Holder<>(new MeterImpl(metadata.getUnit() == null ? MetricUnits.NONE : metadata.getUnit()), enforceType(metadata, MetricType.METERED), metricID);
final Holder<? extends Metric> existing = metrics.putIfAbsent(metricID, holder);
if (existing != null) {
holder = existing;
}
- } else if (!metadata.isReusable()) {
- throw new IllegalArgumentException("Metric " + metadata.getName() + " already exists and is not set as reusable");
- } else if (!holder.metadata.isReusable()) {
- throw new IllegalArgumentException("Metric " + metadata.getName() + " already exists and was not set as reusable");
}
if (!Meter.class.isInstance(holder.metric)) {
throw new IllegalArgumentException(holder.metric + " is not a meter");
@@ -314,7 +348,7 @@
}
@Override
- public SimpleTimer simpleTimer(String name) {
+ public SimpleTimer simpleTimer(final String name) {
return simpleTimer(new MetricID(name));
}
@@ -328,15 +362,11 @@
final MetricID metricID = new MetricID(metadata.getName(), tags);
Holder<? extends Metric> holder = metrics.get(metricID);
if (holder == null) {
- holder = new Holder<>(new SimpleTimerImpl(metadata.getUnit().orElse(MetricUnits.NONE)), metadata, metricID);
+ holder = new Holder<>(new SimpleTimerImpl(metadata.getUnit() == null ? MetricUnits.NONE : metadata.getUnit()), enforceType(metadata, MetricType.SIMPLE_TIMER), metricID);
final Holder<? extends Metric> existing = metrics.putIfAbsent(metricID, holder);
if (existing != null) {
holder = existing;
}
- } else if (!metadata.isReusable()) {
- throw new IllegalArgumentException("Metric " + metadata.getName() + " already exists and is not set as reusable");
- } else if (!holder.metadata.isReusable()) {
- throw new IllegalArgumentException("Metric " + metadata.getName() + " already exists and was not set as reusable");
}
if (!SimpleTimer.class.isInstance(holder.metric)) {
throw new IllegalArgumentException(holder.metric + " is not a timer");
@@ -374,15 +404,11 @@
final MetricID metricID = new MetricID(metadata.getName(), tags);
Holder<? extends Metric> holder = metrics.get(metricID);
if (holder == null) {
- holder = new Holder<>(new TimerImpl(metadata.getUnit().orElse(MetricUnits.NONE)), metadata, metricID);
+ holder = new Holder<>(new TimerImpl(metadata.getUnit() == null ? MetricUnits.NONE : metadata.getUnit()), enforceType(metadata, MetricType.TIMER), metricID);
final Holder<? extends Metric> existing = metrics.putIfAbsent(metricID, holder);
if (existing != null) {
holder = existing;
}
- } else if (!metadata.isReusable()) {
- throw new IllegalArgumentException("Metric " + metadata.getName() + " already exists and is not set as reusable");
- } else if (!holder.metadata.isReusable()) {
- throw new IllegalArgumentException("Metric " + metadata.getName() + " already exists and was not set as reusable");
}
if (!Timer.class.isInstance(holder.metric)) {
throw new IllegalArgumentException(holder.metric + " is not a timer");
@@ -397,9 +423,9 @@
}
@Override
- public <T extends Metric> T getMetric(MetricID metricID, Class<T> asType) {
+ public <T extends Metric> T getMetric(final MetricID metricID, final Class<T> asType) {
try {
- return asType.cast(this.getMetric(metricID));
+ return asType.cast(getMetric(metricID));
} catch (ClassCastException e) {
throw new IllegalArgumentException(metricID + " was not of expected type " + asType, e);
}
@@ -542,6 +568,13 @@
return type;
}
+ private Metadata enforceType(final Metadata metadata, final MetricType type) {
+ if (metadata.getTypeRaw() == null || !type.equals(metadata.getTypeRaw())) {
+ return Metadata.builder(metadata).withType(type).build();
+ }
+ return metadata;
+ }
+
private <T extends Metric> SortedMap<MetricID, T> filterByType(final MetricFilter filter, final Class<T> type) {
return metrics.entrySet().stream()
.filter(it -> type.isInstance(it.getValue().metric))
diff --git a/geronimo-metrics-common/src/main/java/org/apache/geronimo/microprofile/metrics/common/SimpleGaugeImpl.java b/geronimo-metrics-common/src/main/java/org/apache/geronimo/microprofile/metrics/common/SimpleGaugeImpl.java
new file mode 100644
index 0000000..61816ff
--- /dev/null
+++ b/geronimo-metrics-common/src/main/java/org/apache/geronimo/microprofile/metrics/common/SimpleGaugeImpl.java
@@ -0,0 +1,34 @@
+/*
+ * 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.geronimo.microprofile.metrics.common;
+
+import org.eclipse.microprofile.metrics.Gauge;
+
+import java.util.function.Supplier;
+
+public class SimpleGaugeImpl<T> implements Gauge<T> {
+ private final Supplier<T> supplier;
+
+ public SimpleGaugeImpl(final Supplier<T> supplier) {
+ this.supplier = supplier;
+ }
+
+ @Override
+ public T getValue() {
+ return supplier.get();
+ }
+}
diff --git a/geronimo-metrics-common/src/main/java/org/apache/geronimo/microprofile/metrics/common/jaxrs/MetricsEndpoints.java b/geronimo-metrics-common/src/main/java/org/apache/geronimo/microprofile/metrics/common/jaxrs/MetricsEndpoints.java
index 6e38041..6fb088c 100644
--- a/geronimo-metrics-common/src/main/java/org/apache/geronimo/microprofile/metrics/common/jaxrs/MetricsEndpoints.java
+++ b/geronimo-metrics-common/src/main/java/org/apache/geronimo/microprofile/metrics/common/jaxrs/MetricsEndpoints.java
@@ -16,36 +16,7 @@
*/
package org.apache.geronimo.microprofile.metrics.common.jaxrs;
-import static java.util.Collections.emptyMap;
-import static java.util.Collections.singletonMap;
-import static java.util.Optional.ofNullable;
-import static java.util.function.Function.identity;
-import static java.util.stream.Collectors.joining;
-import static java.util.stream.Collectors.toMap;
-
-import java.time.Duration;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
-import java.util.TreeMap;
-import java.util.function.Function;
-import java.util.regex.Pattern;
-import java.util.stream.Stream;
-
-import javax.json.JsonValue;
-import javax.ws.rs.GET;
-import javax.ws.rs.OPTIONS;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
-import javax.ws.rs.Produces;
-import javax.ws.rs.WebApplicationException;
-import javax.ws.rs.core.Context;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.SecurityContext;
-import javax.ws.rs.core.UriInfo;
-
+import org.apache.geronimo.microprofile.metrics.common.RegistryImpl;
import org.apache.geronimo.microprofile.metrics.common.prometheus.PrometheusFormatter;
import org.eclipse.microprofile.metrics.ConcurrentGauge;
import org.eclipse.microprofile.metrics.Counter;
@@ -59,8 +30,37 @@
import org.eclipse.microprofile.metrics.MetricRegistry;
import org.eclipse.microprofile.metrics.SimpleTimer;
import org.eclipse.microprofile.metrics.Snapshot;
+import org.eclipse.microprofile.metrics.Tag;
import org.eclipse.microprofile.metrics.Timer;
+import javax.json.JsonValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.OPTIONS;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.SecurityContext;
+import javax.ws.rs.core.UriInfo;
+import java.time.Duration;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Objects;
+import java.util.TreeMap;
+import java.util.function.Function;
+import java.util.regex.Pattern;
+import java.util.stream.Stream;
+
+import static java.util.Collections.emptyMap;
+import static java.util.Collections.singletonMap;
+import static java.util.Optional.ofNullable;
+import static java.util.function.Function.identity;
+import static java.util.stream.Collectors.joining;
+import static java.util.stream.Collectors.toMap;
+
@Path("metrics")
public class MetricsEndpoints {
private final Pattern semicolon = Pattern.compile(";");
@@ -68,6 +68,7 @@
private MetricRegistry baseRegistry;
private MetricRegistry vendorRegistry;
private MetricRegistry applicationRegistry;
+ private Tag[] globalTags = new Tag[0]; // ensure forgetting to call init() is tolerated for backward compatibility
private SecurityValidator securityValidator = new SecurityValidator() {
{
@@ -77,6 +78,16 @@
private PrometheusFormatter prometheus = new PrometheusFormatter().enableOverriding();
+ protected void init() {
+ globalTags = Stream.of(baseRegistry, vendorRegistry, applicationRegistry)
+ .filter(RegistryImpl.class::isInstance)
+ .map(RegistryImpl.class::cast)
+ .findFirst()
+ .map(RegistryImpl::getGlobalTags)
+ .orElseGet(() -> new Tag[0]);
+ prometheus.withGlobalTags(globalTags);
+ }
+
public void setBaseRegistry(final MetricRegistry baseRegistry) {
this.baseRegistry = baseRegistry;
}
@@ -197,7 +208,8 @@
private MetricID findMetricId(final MetricRegistry metricRegistry, final Metadata value) {
final Map<MetricID, Metric> metrics = metricRegistry.getMetrics();
- final MetricID directKey = new MetricID(value.getName());
+ final MetricID directKey = RegistryImpl.class.isInstance(metricRegistry) && RegistryImpl.class.cast(metricRegistry).getGlobalTags().length > 0 ?
+ new MetricID(value.getName(), RegistryImpl.class.cast(metricRegistry).getGlobalTags()) : new MetricID(value.getName());
if (metrics.containsKey(directKey)) {
return directKey;
}
@@ -225,7 +237,8 @@
private <T> Map<String, T> singleEntry(final String id, final MetricRegistry metricRegistry,
final Function<Metric, T> metricMapper) {
- final MetricID key = new MetricID(id);
+ final MetricID key = RegistryImpl.class.isInstance(metricRegistry) && RegistryImpl.class.cast(metricRegistry).getGlobalTags().length > 0 ?
+ new MetricID(id, RegistryImpl.class.cast(metricRegistry).getGlobalTags()) : new MetricID(id);
final Map<MetricID, Metric> metrics = metricRegistry.getMetrics();
return ofNullable(metrics.get(key)) // try first without any tag (fast access)
.map(metric -> singletonMap(id + formatTags(key), metricMapper.apply(metric)))
@@ -235,7 +248,7 @@
}
private Meta mapMeta(final Metadata value, final MetricID metricID) {
- return ofNullable(value).map(v -> new Meta(value, metricID)).orElse(null);
+ return ofNullable(value).map(v -> new Meta(value, metricID, globalTags)).orElse(null);
}
private Object map(final Metric metric) {
@@ -339,18 +352,22 @@
}
private String formatTags(final MetricID id) {
- return id.getTags().isEmpty() ? "" : (';' + id.getTagsAsList().stream()
+ return id.getTags().isEmpty() && globalTags.length == 0 ? "" : (';' +
+ Stream.concat(id.getTagsAsList().stream(), Stream.of(globalTags))
.map(e -> e.getTagName() + "=" + semicolon.matcher(e.getTagValue()).replaceAll("_"))
+ .distinct()
.collect(joining(";")));
}
public static class Meta {
private final Metadata value;
private final MetricID metricID;
+ private final Tag[] globalTags;
- private Meta(final Metadata value, final MetricID metricID) {
+ private Meta(final Metadata value, final MetricID metricID, final Tag[] globalTags) {
this.value = value;
this.metricID = metricID;
+ this.globalTags = globalTags;
}
public String getName() {
@@ -362,7 +379,7 @@
}
public String getDescription() {
- return value.getDescription().orElse(null);
+ return value.getDescription();
}
public String getType() {
@@ -374,15 +391,15 @@
}
public String getUnit() {
- return value.getUnit().orElse(null);
- }
-
- public boolean isReusable() {
- return value.isReusable();
+ return value.getUnit();
}
public String getTags() { // not sure why tck expect it, sounds worse than native getTags for clients (array of key/values)
- return metricID.getTags().entrySet().stream().map(e -> e.getKey() + '=' + e.getValue()).collect(joining(","));
+ return Stream.concat(
+ metricID.getTags().entrySet().stream().map(e -> e.getKey() + '=' + e.getValue()),
+ Stream.of(globalTags).map(e -> e.getTagName() + '=' + e.getTagValue()))
+ .distinct()
+ .collect(joining(","));
}
}
}
diff --git a/geronimo-metrics-common/src/main/java/org/apache/geronimo/microprofile/metrics/common/prometheus/PrometheusFormatter.java b/geronimo-metrics-common/src/main/java/org/apache/geronimo/microprofile/metrics/common/prometheus/PrometheusFormatter.java
index ddd0e8f..60fb345 100644
--- a/geronimo-metrics-common/src/main/java/org/apache/geronimo/microprofile/metrics/common/prometheus/PrometheusFormatter.java
+++ b/geronimo-metrics-common/src/main/java/org/apache/geronimo/microprofile/metrics/common/prometheus/PrometheusFormatter.java
@@ -60,6 +60,7 @@
protected final Set<Object> validUnits;
protected final Map<String, String> keyMapping = new HashMap<>();
protected Predicate<String> prefixFilter = null;
+ protected Tag[] globalTags;
public PrometheusFormatter() {
validUnits = Stream.of(MetricUnits.class.getDeclaredFields())
@@ -80,6 +81,11 @@
return this;
}
+ public PrometheusFormatter withGlobalTags(final Tag[] globalTags) {
+ this.globalTags = globalTags;
+ return this;
+ }
+
public PrometheusFormatter enableOverriding() {
try (final InputStream source = Thread.currentThread().getContextClassLoader()
.getResourceAsStream("META-INF/geronimo-metrics/prometheus-mapping.properties")) {
@@ -130,7 +136,7 @@
})
.filter(it -> prefixFilter == null || prefixFilter.test(it.prometheusKey))
.map(entry -> {
- final List<Tag> tagsAsList = entry.metricID.getTagsAsList();
+ final List<Tag> tagsAsList = getTags(entry);
switch (entry.metadata.getTypeRaw()) {
case COUNTER: {
String key = toPrometheusKey(entry.metadata);
@@ -181,6 +187,14 @@
.collect(StringBuilder::new, StringBuilder::append, StringBuilder::append);
}
+ private List<Tag> getTags(final Entry entry) {
+ return globalTags == null || globalTags.length == 0 ?
+ entry.metricID.getTagsAsList() :
+ Stream.concat(entry.metricID.getTagsAsList().stream(), Stream.of(globalTags))
+ .distinct()
+ .collect(toList());
+ }
+
private StringBuilder histogram(final String registryKey, final Entry entry, final List<Tag> tagsAsList, final String keyBase, final String keyUnit, final Histogram histogram) {
final String type = entry.metadata == null ? null : entry.metadata.getType();
return new StringBuilder()
@@ -273,7 +287,7 @@
}
private String toUnitSuffix(final Metadata metadata, final boolean enforceValid) {
- final String unit = enforceValid ? getValidUnit(metadata) : metadata.getUnit().orElse(MetricUnits.NONE);
+ final String unit = enforceValid ? getValidUnit(metadata) : (metadata.getUnit() == null ? MetricUnits.NONE : metadata.getUnit());
return MetricUnits.NONE.equalsIgnoreCase(unit) || (enforceValid && !validUnits.contains(unit)) ? "" : ("_" + toPrometheusUnit(unit));
}
@@ -293,7 +307,7 @@
}
private String getValidUnit(final Metadata metadata) {
- final String unit = metadata.getUnit().orElse(MetricUnits.NONE);
+ final String unit = metadata.getUnit() == null ? MetricUnits.NONE : metadata.getUnit();
// for tck, we dont really want to prevent the user to add new units
// we should likely just check it exists in MetricUnits constant but it is too restrictive
if (unit.startsWith("jelly")) {
diff --git a/geronimo-metrics-extensions/geronimo-metrics-extension-common/src/main/java/org/apache/geronimo/microprofile/metrics/extension/common/MicroprofileMetricsAdapter.java b/geronimo-metrics-extensions/geronimo-metrics-extension-common/src/main/java/org/apache/geronimo/microprofile/metrics/extension/common/MicroprofileMetricsAdapter.java
index a8e46e8..6845461 100644
--- a/geronimo-metrics-extensions/geronimo-metrics-extension-common/src/main/java/org/apache/geronimo/microprofile/metrics/extension/common/MicroprofileMetricsAdapter.java
+++ b/geronimo-metrics-extensions/geronimo-metrics-extension-common/src/main/java/org/apache/geronimo/microprofile/metrics/extension/common/MicroprofileMetricsAdapter.java
@@ -40,7 +40,6 @@
.withDescription(def.getDescription())
.withType(GAUGE)
.withUnit(def.getUnit())
- .reusable(true)
.build();
try {
registry.register(metadata, (Gauge<Double>) () -> def.getEvaluator()
diff --git a/geronimo-metrics/pom.xml b/geronimo-metrics/pom.xml
index 4ea48a7..9ba3b92 100644
--- a/geronimo-metrics/pom.xml
+++ b/geronimo-metrics/pom.xml
@@ -27,8 +27,9 @@
<name>Geronimo Metrics :: Impl</name>
<properties>
- <geronimo-metrics.Automatic-Module-Name>org.apache.geronimo.microprofile.metrics
- </geronimo-metrics.Automatic-Module-Name>
+ <geronimo-metrics.Automatic-Module-Name>org.apache.geronimo.microprofile.metrics</geronimo-metrics.Automatic-Module-Name>
+
+ <tck.forkCount>1</tck.forkCount>
</properties>
<dependencies>
@@ -77,6 +78,18 @@
<version>${spec.version}</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>${junit.version}</version>
+ <scope>test</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest-core</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
</dependencies>
<build>
@@ -85,7 +98,7 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
- <forkCount>1</forkCount>
+ <forkCount>${tck.forkCount}</forkCount>
<runOrder>alphabetical</runOrder>
<dependenciesToScan>
<dependency>org.eclipse.microprofile.metrics:microprofile-metrics-api-tck</dependency>
diff --git a/geronimo-metrics/src/main/java/org/apache/geronimo/microprofile/metrics/cdi/ConcurrentGaugeInterceptor.java b/geronimo-metrics/src/main/java/org/apache/geronimo/microprofile/metrics/cdi/ConcurrentGaugeInterceptor.java
index fe7147c..6a6a2c3 100644
--- a/geronimo-metrics/src/main/java/org/apache/geronimo/microprofile/metrics/cdi/ConcurrentGaugeInterceptor.java
+++ b/geronimo-metrics/src/main/java/org/apache/geronimo/microprofile/metrics/cdi/ConcurrentGaugeInterceptor.java
@@ -104,7 +104,7 @@
.orElse(""));
final ConcurrentGauge gauge = registry.getConcurrentGauge(
- new MetricID(name, concurrentGauge == null ? new Tag[0] : extension.createTags(concurrentGauge.tags())));
+ new MetricID(name, extension.createTags(concurrentGauge == null ? new String[0] : concurrentGauge.tags())));
if (gauge == null) {
throw new IllegalStateException("No counter with name [" + name + "] found in registry [" + registry + "]");
}
diff --git a/geronimo-metrics/src/main/java/org/apache/geronimo/microprofile/metrics/cdi/CountedInterceptor.java b/geronimo-metrics/src/main/java/org/apache/geronimo/microprofile/metrics/cdi/CountedInterceptor.java
index 7357071..42fbfae 100644
--- a/geronimo-metrics/src/main/java/org/apache/geronimo/microprofile/metrics/cdi/CountedInterceptor.java
+++ b/geronimo-metrics/src/main/java/org/apache/geronimo/microprofile/metrics/cdi/CountedInterceptor.java
@@ -99,7 +99,7 @@
ofNullable(extension.getAnnotation(type, Counted.class)).map(Counted::name).orElse(""));
final Counter counter = registry.getCounter(
- new MetricID(name, counted == null ? new Tag[0] : extension.createTags(counted.tags())));
+ new MetricID(name, extension.createTags(counted == null ? new String[0] : counted.tags())));
if (counter == null) {
throw new IllegalStateException("No counter with name [" + name + "] found in registry [" + registry + "]");
}
diff --git a/geronimo-metrics/src/main/java/org/apache/geronimo/microprofile/metrics/cdi/MeteredInterceptor.java b/geronimo-metrics/src/main/java/org/apache/geronimo/microprofile/metrics/cdi/MeteredInterceptor.java
index bb56b77..d53fe7f 100644
--- a/geronimo-metrics/src/main/java/org/apache/geronimo/microprofile/metrics/cdi/MeteredInterceptor.java
+++ b/geronimo-metrics/src/main/java/org/apache/geronimo/microprofile/metrics/cdi/MeteredInterceptor.java
@@ -95,7 +95,7 @@
ofNullable(extension.getAnnotation(type, Metered.class)).map(Metered::name).orElse(""));
meter = registry.getMeter(
- new MetricID(name, metered == null ? new Tag[0] : extension.createTags(metered.tags())));
+ new MetricID(name, extension.createTags(metered == null ? new String[0] : metered.tags())));
if (meter == null) {
throw new IllegalStateException("No meter with name [" + name + "] found in registry [" + registry + "]");
}
diff --git a/geronimo-metrics/src/main/java/org/apache/geronimo/microprofile/metrics/cdi/MetricsExtension.java b/geronimo-metrics/src/main/java/org/apache/geronimo/microprofile/metrics/cdi/MetricsExtension.java
index 49a843d..dc65f39 100644
--- a/geronimo-metrics/src/main/java/org/apache/geronimo/microprofile/metrics/cdi/MetricsExtension.java
+++ b/geronimo-metrics/src/main/java/org/apache/geronimo/microprofile/metrics/cdi/MetricsExtension.java
@@ -19,6 +19,8 @@
import org.apache.geronimo.microprofile.metrics.common.BaseMetrics;
import org.apache.geronimo.microprofile.metrics.common.GaugeImpl;
import org.apache.geronimo.microprofile.metrics.common.RegistryImpl;
+import org.eclipse.microprofile.config.Config;
+import org.eclipse.microprofile.config.ConfigProvider;
import org.eclipse.microprofile.metrics.Counter;
import org.eclipse.microprofile.metrics.Gauge;
import org.eclipse.microprofile.metrics.Histogram;
@@ -85,17 +87,15 @@
public class MetricsExtension implements Extension { // must not explicitly depend on jaxrs since it is dropped in nojaxrs bundle
private static final Tag[] NO_TAG = new Tag[0];
- private final MetricRegistry applicationRegistry = new RegistryImpl(MetricRegistry.Type.APPLICATION);
- private final MetricRegistry baseRegistry = new RegistryImpl(MetricRegistry.Type.BASE);
- private final MetricRegistry vendorRegistry = new RegistryImpl(MetricRegistry.Type.VENDOR);
+ private MetricRegistry applicationRegistry;
+ private MetricRegistry baseRegistry;
+ private MetricRegistry vendorRegistry;
private final Map<MetricID, Metadata> registrations = new HashMap<>();
private final Map<MetricID, Function<BeanManager, Gauge<?>>> gaugeFactories = new HashMap<>();
private final Collection<Runnable> producersRegistrations = new ArrayList<>();
private final Collection<CreationalContext<?>> creationalContexts = new ArrayList<>();
- private Map<String, String> environmentalTags;
-
void vetoEndpointIfNotActivated(@Observes final ProcessAnnotatedType<?> processAnnotatedType) {
// default is secured so deploy
final String name = processAnnotatedType.getAnnotatedType().getJavaClass().getName();
@@ -113,6 +113,11 @@
}
void letOtherExtensionsUseRegistries(@Observes final BeforeBeanDiscovery beforeBeanDiscovery, final BeanManager beanManager) {
+ final Tag[] globalTags = OptionalConfig.findTags();
+ applicationRegistry = new RegistryImpl(MetricRegistry.Type.APPLICATION, globalTags);
+ baseRegistry = new RegistryImpl(MetricRegistry.Type.BASE, globalTags);
+ vendorRegistry = new RegistryImpl(MetricRegistry.Type.VENDOR, globalTags);
+
beforeBeanDiscovery.addQualifier(RegistryType.class);
beanManager.fireEvent(applicationRegistry);
beanManager.fireEvent(applicationRegistry, new RegistryTypeImpl(MetricRegistry.Type.APPLICATION));
@@ -125,9 +130,6 @@
final String name = method.getAnnotated().getJavaMember().getName();
return name.equals("name") || name.equals("tags");
}).forEach(method -> method.remove(a -> a.annotationType() == Nonbinding.class));
-
- // we must update @Metrics with that if it exists for the cdi resolution ot match since we make it binding
- environmentalTags = new MetricID("foo").getTags();
}
void onMetric(@Observes final ProcessProducerField<? extends Metric, ?> processProducerField, final BeanManager beanManager) {
@@ -185,9 +187,9 @@
.withUnit(config.unit())
.build();
final MetricID id = new MetricID(name, createTags(config.tags()));
- addRegistration(metadata, id, true);
+ addRegistration(metadata, id);
- if (!name.equals(config.name()) || !environmentalTags.isEmpty()) {
+ if (!name.equals(config.name())) {
final Annotation[] newQualifiers = Stream.concat(metricInjectionPointProcessEvent.getInjectionPoint().getQualifiers().stream()
.filter(it -> it.annotationType() != org.eclipse.microprofile.metrics.annotation.Metric.class),
Stream.of(new MetricImpl(metadata, id)))
@@ -199,7 +201,7 @@
final String name = MetricRegistry.name(injectionPoint.getMember().getDeclaringClass(), injectionPoint.getMember().getName());
final Metadata metadata = Metadata.builder().withName(name).withType(type).build();
final MetricID metricID = new MetricID(name);
- addRegistration(metadata, metricID, true);
+ addRegistration(metadata, metricID);
// ensure the injection uses the qualifier since we'll not register it without
final Annotation[] newQualifiers = Stream.concat(metricInjectionPointProcessEvent.getInjectionPoint().getQualifiers().stream()
@@ -211,22 +213,6 @@
}
}
- private void addRegistration(final Metadata metadata, final MetricID id, final boolean reusable) {
- final Metadata existing = registrations.putIfAbsent(id, metadata);
- if (existing != null) {
- if (!reusable && !metadata.isReusable()) {
- throw new IllegalArgumentException(id.getName() + " is not set as reusable but is referenced twice");
- }
- }
- }
-
- public Tag[] createTags(final String[] tags) {
- return Stream.of(tags).filter(it -> it.contains("=")).map(it -> {
- final int sep = it.indexOf("=");
- return new Tag(it.substring(0, sep), it.substring(sep + 1));
- }).toArray(Tag[]::new);
- }
-
void findInterceptorMetrics(@Observes @WithAnnotations({
Counted.class,
SimplyTimed.class,
@@ -330,6 +316,21 @@
creationalContexts.forEach(CreationalContext::release);
}
+ private void addRegistration(final Metadata metadata, final MetricID id) {
+ registrations.putIfAbsent(id, metadata);
+ }
+
+ public Tag[] createTags(final String[] tags) {
+ return toTags(tags);
+ }
+
+ private static Tag[] toTags(final String[] tags) {
+ return Stream.of(tags).filter(it -> it.contains("=")).map(it -> {
+ final int sep = it.indexOf("=");
+ return new Tag(it.substring(0, sep), it.substring(sep + 1));
+ }).toArray(Tag[]::new);
+ }
+
private void doRegisterMetric(final AnnotatedType<?> annotatedType, final Class<?> javaClass, final AnnotatedCallable<?> method) {
final Member javaMember = method.getJavaMember();
@@ -347,7 +348,7 @@
.withUnit(counted.unit())
.build();
final MetricID metricID = new MetricID(name, createTags(counted.tags()));
- addRegistration(metadata, metricID, counted.reusable() || !isMethod);
+ addRegistration(metadata, metricID);
}
final ConcurrentGauge concurrentGauge = ofNullable(method.getAnnotation(ConcurrentGauge.class)).orElseGet(() ->
@@ -364,7 +365,7 @@
.withUnit(concurrentGauge.unit())
.build();
final MetricID metricID = new MetricID(name, createTags(concurrentGauge.tags()));
- addRegistration(metadata, metricID, concurrentGauge.reusable() || !isMethod);
+ addRegistration(metadata, metricID);
}
final Timed timed = ofNullable(method.getAnnotation(Timed.class)).orElseGet(() -> getAnnotation(annotatedType, Timed.class));
@@ -380,7 +381,7 @@
.withUnit(timed.unit())
.build();
final MetricID metricID = new MetricID(name, createTags(timed.tags()));
- addRegistration(metadata, metricID, timed.reusable() || !isMethod);
+ addRegistration(metadata, metricID);
}
final SimplyTimed simplyTimed = ofNullable(method.getAnnotation(SimplyTimed.class)).orElseGet(() -> getAnnotation(annotatedType, SimplyTimed.class));
@@ -396,7 +397,7 @@
.withUnit(simplyTimed.unit())
.build();
final MetricID metricID = new MetricID(name, createTags(simplyTimed.tags()));
- addRegistration(metadata, metricID, simplyTimed.reusable() || !isMethod);
+ addRegistration(metadata, metricID);
}
final Metered metered = ofNullable(method.getAnnotation(Metered.class))
@@ -413,7 +414,7 @@
.withUnit(metered.unit())
.build();
final MetricID metricID = new MetricID(name, createTags(metered.tags()));
- addRegistration(metadata, metricID, metered.reusable() || !isMethod);
+ addRegistration(metadata, metricID);
}
final org.eclipse.microprofile.metrics.annotation.Gauge gauge = ofNullable(method.getAnnotation(org.eclipse.microprofile.metrics.annotation.Gauge.class))
@@ -430,7 +431,7 @@
.withUnit(gauge.unit())
.build();
final MetricID metricID = new MetricID(name, createTags(gauge.tags()));
- addRegistration(metadata, metricID, false);
+ addRegistration(metadata, metricID);
gaugeFactories.put(metricID, beanManager -> {
final Object reference = getInstance(javaClass, beanManager);
final Method mtd = Method.class.cast(javaMember);
@@ -464,7 +465,7 @@
.withUnit(config.unit())
.build();
final MetricID id = new MetricID(name, createTags(config.tags()));
- addRegistration(metadata, id, true);
+ addRegistration(metadata, id);
return metadata;
}
@@ -521,16 +522,20 @@
final Annotation qualifier,
final Object instance,
final boolean addDefaultQualifier) {
- final BeanConfigurator<Object> base = afterBeanDiscovery.addBean()
+ final BeanConfigurator<Object> configurator = afterBeanDiscovery.addBean()
.id(MetricsExtension.class.getName() + ":" + type.getName() + ":" + idSuffix)
.beanClass(type)
- .types(type, Object.class)
.scope(Dependent.class) // avoid proxies, tck use assertEquals(proxy, registry.get(xxx))
.createWith(c -> instance);
- if (addDefaultQualifier) {
- base.qualifiers(qualifier, Default.Literal.INSTANCE, Any.Literal.INSTANCE);
+ if (MetricRegistry.class == type) {
+ configurator.types(MetricRegistry.class, RegistryImpl.class, Objects.class);
} else {
- base.qualifiers(qualifier, Any.Literal.INSTANCE);
+ configurator.types(type, Object.class);
+ }
+ if (addDefaultQualifier) {
+ configurator.qualifiers(qualifier, Default.Literal.INSTANCE, Any.Literal.INSTANCE);
+ } else {
+ configurator.qualifiers(qualifier, Any.Literal.INSTANCE);
}
}
@@ -556,6 +561,7 @@
this.metadata = metadata;
this.tags = metricID.getTags().entrySet().stream()
.map(e -> e.getKey() + "=" + e.getValue())
+ .distinct()
.toArray(String[]::new);
}
@@ -586,12 +592,12 @@
@Override
public String description() {
- return metadata.getDescription().orElse("");
+ return metadata.getDescription();
}
@Override
public String unit() {
- return metadata.getUnit().orElse("");
+ return metadata.getUnit();
}
}
@@ -613,4 +619,21 @@
return RegistryType.class;
}
}
+
+ private static final class OptionalConfig {
+ private OptionalConfig() {
+ // no-op
+ }
+
+ public static Tag[] findTags() {
+ try {
+ final Config config = ConfigProvider.getConfig();
+ return config.getOptionalValue("mp.metrics.tags", String.class)
+ .map(it -> toTags(it.split(",")))
+ .orElseGet(() -> new Tag[0]);
+ } catch (final IllegalStateException | ExceptionInInitializerError | NoClassDefFoundError t) {
+ return new Tag[0];
+ }
+ }
+ }
}
diff --git a/geronimo-metrics/src/main/java/org/apache/geronimo/microprofile/metrics/cdi/SimplyTimedInterceptor.java b/geronimo-metrics/src/main/java/org/apache/geronimo/microprofile/metrics/cdi/SimplyTimedInterceptor.java
index b97e8d7..ecea25b 100644
--- a/geronimo-metrics/src/main/java/org/apache/geronimo/microprofile/metrics/cdi/SimplyTimedInterceptor.java
+++ b/geronimo-metrics/src/main/java/org/apache/geronimo/microprofile/metrics/cdi/SimplyTimedInterceptor.java
@@ -90,7 +90,7 @@
Modifier.isAbstract(executable.getDeclaringClass().getModifiers()) ? type.getJavaClass() : executable.getDeclaringClass(),
executable, timed == null ? null : timed.name(), timed != null && timed.absolute(),
ofNullable(extension.getAnnotation(type, SimplyTimed.class)).map(SimplyTimed::name).orElse(""));
- timer = registry.getSimpleTimer(new MetricID(name, timed == null ? new Tag[0] : extension.createTags(timed.tags())));
+ timer = registry.getSimpleTimer(new MetricID(name, extension.createTags(timed == null ? new String[0] : timed.tags())));
if (timer == null) {
throw new IllegalStateException("No timer with name [" + name + "] found in registry [" + registry + "]");
}
diff --git a/geronimo-metrics/src/main/java/org/apache/geronimo/microprofile/metrics/cdi/TimedInterceptor.java b/geronimo-metrics/src/main/java/org/apache/geronimo/microprofile/metrics/cdi/TimedInterceptor.java
index d736fc1..17b7619 100644
--- a/geronimo-metrics/src/main/java/org/apache/geronimo/microprofile/metrics/cdi/TimedInterceptor.java
+++ b/geronimo-metrics/src/main/java/org/apache/geronimo/microprofile/metrics/cdi/TimedInterceptor.java
@@ -91,7 +91,7 @@
Modifier.isAbstract(executable.getDeclaringClass().getModifiers()) ? type.getJavaClass() : executable.getDeclaringClass(),
executable, timed == null ? null : timed.name(), timed != null && timed.absolute(),
ofNullable(extension.getAnnotation(type, Timed.class)).map(Timed::name).orElse(""));
- timer = registry.getTimer(new MetricID(name, timed == null ? new Tag[0] : extension.createTags(timed.tags())));
+ timer = registry.getTimer(new MetricID(name, extension.createTags(timed == null ? new String[0] : timed.tags())));
if (timer == null) {
throw new IllegalStateException("No timer with name [" + name + "] found in registry [" + registry + "]");
}
diff --git a/geronimo-metrics/src/main/java/org/apache/geronimo/microprofile/metrics/jaxrs/CdiMetricsEndpoints.java b/geronimo-metrics/src/main/java/org/apache/geronimo/microprofile/metrics/jaxrs/CdiMetricsEndpoints.java
index 62b9284..74ec9a6 100644
--- a/geronimo-metrics/src/main/java/org/apache/geronimo/microprofile/metrics/jaxrs/CdiMetricsEndpoints.java
+++ b/geronimo-metrics/src/main/java/org/apache/geronimo/microprofile/metrics/jaxrs/CdiMetricsEndpoints.java
@@ -47,5 +47,6 @@
setApplicationRegistry(applicationRegistry);
setBaseRegistry(baseRegistry);
setVendorRegistry(vendorRegistry);
+ super.init();
}
}
diff --git a/geronimo-metrics/src/test/java/org/apache/geronimo/microprofile/metrics/test/ArquillianSetup.java b/geronimo-metrics/src/test/java/org/apache/geronimo/microprofile/metrics/test/ArquillianSetup.java
index a0b6499..e1d300d 100644
--- a/geronimo-metrics/src/test/java/org/apache/geronimo/microprofile/metrics/test/ArquillianSetup.java
+++ b/geronimo-metrics/src/test/java/org/apache/geronimo/microprofile/metrics/test/ArquillianSetup.java
@@ -17,10 +17,10 @@
package org.apache.geronimo.microprofile.metrics.test;
import org.apache.catalina.Context;
+import org.apache.geronimo.microprofile.metrics.cdi.MetricsExtension;
import org.apache.meecrowave.Meecrowave;
import org.apache.meecrowave.arquillian.MeecrowaveContainer;
import org.eclipse.microprofile.metrics.MetricID;
-import org.eclipse.microprofile.metrics.Tag;
import org.eclipse.microprofile.metrics.annotation.Metric;
import org.jboss.arquillian.container.spi.client.container.DeployableContainer;
import org.jboss.arquillian.container.spi.context.annotation.ContainerScoped;
@@ -128,7 +128,9 @@
thread.setContextClassLoader(appClassLoaderInstanceProducer.get());
try {
final CDI<Object> cdi = CDI.current();
- final Annotation[] qualifiers = Stream.of(p.getAnnotations()).filter(it -> cdi.getBeanManager().isQualifier(it.annotationType())).toArray(Annotation[]::new);
+ final Annotation[] qualifiers = Stream.of(p.getAnnotations())
+ .filter(it -> cdi.getBeanManager().isQualifier(it.annotationType()))
+ .toArray(Annotation[]::new);
return cdi.select(p.getType(), fixQualifiers(qualifiers)).get();
} catch (final RuntimeException re) {
re.printStackTrace(); // easier to debug when some test fail since TCK inject metrics as params
@@ -141,14 +143,17 @@
}
private Annotation[] fixQualifiers(final Annotation[] qualifiers) {
+ final MetricsExtension metricsExtension = CDI.current().select(MetricsExtension.class).get();
return Stream.of(qualifiers)
.map(it -> {
if (Metric.class == it.annotationType()) { // we make tags and name binding so ensure it uses the right values
final Metric delegate = Metric.class.cast(it);
- return new MetricLiteral(delegate, new MetricID(delegate.name(), Stream.of(delegate.tags()).filter(tag -> tag.contains("=")).map(tag -> {
- final int sep = tag.indexOf("=");
- return new Tag(tag.substring(0, sep), tag.substring(sep + 1));
- }).toArray(Tag[]::new)).getTagsAsList().stream().map(t -> t.getTagName() + '=' + t.getTagValue()).toArray(String[]::new));
+ final String[] tags = new MetricID(delegate.name(), metricsExtension.createTags(delegate.tags()))
+ .getTagsAsList().stream()
+ .map(t -> t.getTagName() + '=' + t.getTagValue())
+ .distinct()
+ .toArray(String[]::new);
+ return new MetricLiteral(delegate, tags);
}
return it;
})
diff --git a/pom.xml b/pom.xml
index 4370c42..6ed5dd9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -42,10 +42,11 @@
</scm>
<properties>
- <spec.version>3.0-M2</spec.version>
+ <spec.version>3.0-RC2</spec.version>
<arquillian.version>1.1.13.Final</arquillian.version>
<meecrowave.version>1.2.8</meecrowave.version>
<owb.version>2.0.16</owb.version>
+ <junit.version>4.13</junit.version>
</properties>
<modules>
@@ -101,7 +102,7 @@
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
- <version>4.12</version>
+ <version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>