package com.signalfx.codahale.reporter;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.yammer.metrics.core.Metric;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.signalfx.metrics.protobuf.SignalFxProtocolBuffers;
/**
*
* Implementation of MetricMetadata
*
*/
public class MetricMetadataImpl implements MetricMetadata {
private final ConcurrentMap<Metric, Metadata> metaDataCollection;
public MetricMetadataImpl() {
// This map must be thread safe
metaDataCollection = new ConcurrentHashMap<Metric, Metadata>();
}
public Map<String, String> getTags(Metric metric) {
Metadata existingMetaData = metaDataCollection.get(metric);
if (existingMetaData == null) {
return Collections.emptyMap();
} else {
return Collections.unmodifiableMap(existingMetaData.tags);
}
}
@Override
public Optional<SignalFxProtocolBuffers.MetricType> getMetricType(Metric metric) {
Metadata existingMetaData = metaDataCollection.get(metric);
if (existingMetaData == null || existingMetaData.metricType == null) {
return Optional.absent();
} else {
return Optional.of(existingMetaData.metricType);
}
}
@Override
public <M extends Metric> Tagger<M> tagMetric(M metric) {
return forMetric(metric);
}
@Override
public <M extends Metric> Tagger<M> forMetric(M metric) {
if (metaDataCollection.containsKey(metric)) {
return new TaggerImpl<M>(metric, metaDataCollection.get(metric));
} else {
synchronized (this) {
if (metaDataCollection.containsKey(metric)) {
return new TaggerImpl<M>(metric, metaDataCollection.get(metric));
}
Metadata thisMetricsMetadata = new Metadata();
Metadata oldMetaData = metaDataCollection.put(metric, thisMetricsMetadata);
Preconditions
.checkArgument(oldMetaData == null, "Concurrency issue adding metadata");
return new TaggerImpl<M>(metric, thisMetricsMetadata);
}
}
}
private static abstract class TaggerBaseImpl<M extends Metric, T extends TaggerBase<M, T>>
implements TaggerBase<M, T>{
protected final Metadata thisMetricsMetadata;
@Override
public T withDimension(String key, String value) {
thisMetricsMetadata.tags.put(key, value);
return (T) this;
}
TaggerBaseImpl(Metadata thisMetricsMetadata) {
this.thisMetricsMetadata = thisMetricsMetadata;
}
public T withSourceName(String sourceName) {
thisMetricsMetadata.tags.put(SOURCE, sourceName);
return (T) this;
}
public T withMetricName(String metricName) {
thisMetricsMetadata.tags.put(METRIC, metricName);
return (T) this;
}
public T withMetricType(
SignalFxProtocolBuffers.MetricType metricType) {
thisMetricsMetadata.metricType = metricType;
return (T) this;
}
}
private static final class TaggerImpl<M extends Metric> extends TaggerBaseImpl<M, Tagger<M>>
implements Tagger<M> {
private final M metric;
TaggerImpl(M metric, Metadata thisMetricsMetadata) {
super(thisMetricsMetadata);
this.metric = metric;
}
@Override public M metric() {
return metric;
}
}
private static final class Metadata {
private final Map<String, String> tags;
private SignalFxProtocolBuffers.MetricType metricType;
private Metadata() {
tags = new ConcurrentHashMap<String, String>(6);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Metadata)) {
return false;
}
Metadata metadata = (Metadata) o;
if (metricType != metadata.metricType) {
return false;
}
if (tags != null ? !tags.equals(metadata.tags) : metadata.tags != null) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = tags != null ? tags.hashCode() : 0;
result = 31 * result + (metricType != null ? metricType.hashCode() : 0);
return result;
}
}
}