feat: Allow users to register the same Meter multiple times without exception (#2017)

* feat: Allow register same metric multiple times

* add more test

* remove redundant test
diff --git a/impl_core/src/main/java/io/opencensus/implcore/metrics/DerivedDoubleCumulativeImpl.java b/impl_core/src/main/java/io/opencensus/implcore/metrics/DerivedDoubleCumulativeImpl.java
index 441afa1..3d0966f 100644
--- a/impl_core/src/main/java/io/opencensus/implcore/metrics/DerivedDoubleCumulativeImpl.java
+++ b/impl_core/src/main/java/io/opencensus/implcore/metrics/DerivedDoubleCumulativeImpl.java
@@ -127,6 +127,11 @@
     registeredPoints = Collections.<List<LabelValue>, PointWithFunction<?>>emptyMap();
   }
 
+  @Override
+  public MetricDescriptor getMetricDescriptor() {
+    return metricDescriptor;
+  }
+
   @javax.annotation.Nullable
   @Override
   public Metric getMetric(Clock clock) {
diff --git a/impl_core/src/main/java/io/opencensus/implcore/metrics/DerivedDoubleGaugeImpl.java b/impl_core/src/main/java/io/opencensus/implcore/metrics/DerivedDoubleGaugeImpl.java
index 58b7727..3c63a60 100644
--- a/impl_core/src/main/java/io/opencensus/implcore/metrics/DerivedDoubleGaugeImpl.java
+++ b/impl_core/src/main/java/io/opencensus/implcore/metrics/DerivedDoubleGaugeImpl.java
@@ -121,6 +121,11 @@
     registeredPoints = Collections.<List<LabelValue>, PointWithFunction<?>>emptyMap();
   }
 
+  @Override
+  public MetricDescriptor getMetricDescriptor() {
+    return metricDescriptor;
+  }
+
   @javax.annotation.Nullable
   @Override
   public Metric getMetric(Clock clock) {
diff --git a/impl_core/src/main/java/io/opencensus/implcore/metrics/DerivedLongCumulativeImpl.java b/impl_core/src/main/java/io/opencensus/implcore/metrics/DerivedLongCumulativeImpl.java
index 28134fb..08e627a 100644
--- a/impl_core/src/main/java/io/opencensus/implcore/metrics/DerivedLongCumulativeImpl.java
+++ b/impl_core/src/main/java/io/opencensus/implcore/metrics/DerivedLongCumulativeImpl.java
@@ -127,6 +127,11 @@
     registeredPoints = Collections.<List<LabelValue>, PointWithFunction<?>>emptyMap();
   }
 
+  @Override
+  public MetricDescriptor getMetricDescriptor() {
+    return metricDescriptor;
+  }
+
   @javax.annotation.Nullable
   @Override
   public Metric getMetric(Clock clock) {
diff --git a/impl_core/src/main/java/io/opencensus/implcore/metrics/DerivedLongGaugeImpl.java b/impl_core/src/main/java/io/opencensus/implcore/metrics/DerivedLongGaugeImpl.java
index fa4562e..8a27c59 100644
--- a/impl_core/src/main/java/io/opencensus/implcore/metrics/DerivedLongGaugeImpl.java
+++ b/impl_core/src/main/java/io/opencensus/implcore/metrics/DerivedLongGaugeImpl.java
@@ -121,6 +121,11 @@
     registeredPoints = Collections.<List<LabelValue>, PointWithFunction<?>>emptyMap();
   }
 
+  @Override
+  public MetricDescriptor getMetricDescriptor() {
+    return metricDescriptor;
+  }
+
   @javax.annotation.Nullable
   @Override
   public Metric getMetric(Clock clock) {
diff --git a/impl_core/src/main/java/io/opencensus/implcore/metrics/DoubleCumulativeImpl.java b/impl_core/src/main/java/io/opencensus/implcore/metrics/DoubleCumulativeImpl.java
index 3317844..5f4f7c6 100644
--- a/impl_core/src/main/java/io/opencensus/implcore/metrics/DoubleCumulativeImpl.java
+++ b/impl_core/src/main/java/io/opencensus/implcore/metrics/DoubleCumulativeImpl.java
@@ -125,6 +125,11 @@
     registeredPoints = Collections.<List<LabelValue>, PointImpl>emptyMap();
   }
 
+  @Override
+  public MetricDescriptor getMetricDescriptor() {
+    return metricDescriptor;
+  }
+
   private synchronized DoublePoint registerTimeSeries(List<LabelValue> labelValues) {
     PointImpl existingPoint = registeredPoints.get(labelValues);
     if (existingPoint != null) {
diff --git a/impl_core/src/main/java/io/opencensus/implcore/metrics/DoubleGaugeImpl.java b/impl_core/src/main/java/io/opencensus/implcore/metrics/DoubleGaugeImpl.java
index fc60abd..87f17ab 100644
--- a/impl_core/src/main/java/io/opencensus/implcore/metrics/DoubleGaugeImpl.java
+++ b/impl_core/src/main/java/io/opencensus/implcore/metrics/DoubleGaugeImpl.java
@@ -121,6 +121,11 @@
     registeredPoints = Collections.<List<LabelValue>, PointImpl>emptyMap();
   }
 
+  @Override
+  public MetricDescriptor getMetricDescriptor() {
+    return metricDescriptor;
+  }
+
   private synchronized DoublePoint registerTimeSeries(List<LabelValue> labelValues) {
     PointImpl existingPoint = registeredPoints.get(labelValues);
     if (existingPoint != null) {
diff --git a/impl_core/src/main/java/io/opencensus/implcore/metrics/LongCumulativeImpl.java b/impl_core/src/main/java/io/opencensus/implcore/metrics/LongCumulativeImpl.java
index 503d037..5b7ea4a 100644
--- a/impl_core/src/main/java/io/opencensus/implcore/metrics/LongCumulativeImpl.java
+++ b/impl_core/src/main/java/io/opencensus/implcore/metrics/LongCumulativeImpl.java
@@ -125,6 +125,11 @@
     registeredPoints = Collections.<List<LabelValue>, PointImpl>emptyMap();
   }
 
+  @Override
+  public MetricDescriptor getMetricDescriptor() {
+    return metricDescriptor;
+  }
+
   private synchronized LongPoint registerTimeSeries(List<LabelValue> labelValues) {
     PointImpl existingPoint = registeredPoints.get(labelValues);
     if (existingPoint != null) {
diff --git a/impl_core/src/main/java/io/opencensus/implcore/metrics/LongGaugeImpl.java b/impl_core/src/main/java/io/opencensus/implcore/metrics/LongGaugeImpl.java
index c6045d7..7d64953 100644
--- a/impl_core/src/main/java/io/opencensus/implcore/metrics/LongGaugeImpl.java
+++ b/impl_core/src/main/java/io/opencensus/implcore/metrics/LongGaugeImpl.java
@@ -121,6 +121,11 @@
     registeredPoints = Collections.<List<LabelValue>, PointImpl>emptyMap();
   }
 
+  @Override
+  public MetricDescriptor getMetricDescriptor() {
+    return metricDescriptor;
+  }
+
   private synchronized LongPoint registerTimeSeries(List<LabelValue> labelValues) {
     PointImpl existingPoint = registeredPoints.get(labelValues);
     if (existingPoint != null) {
diff --git a/impl_core/src/main/java/io/opencensus/implcore/metrics/Meter.java b/impl_core/src/main/java/io/opencensus/implcore/metrics/Meter.java
index f5a8dc8..8a256a3 100644
--- a/impl_core/src/main/java/io/opencensus/implcore/metrics/Meter.java
+++ b/impl_core/src/main/java/io/opencensus/implcore/metrics/Meter.java
@@ -18,6 +18,7 @@
 
 import io.opencensus.common.Clock;
 import io.opencensus.metrics.export.Metric;
+import io.opencensus.metrics.export.MetricDescriptor;
 import javax.annotation.Nullable;
 
 interface Meter {
@@ -31,4 +32,11 @@
    */
   @Nullable
   Metric getMetric(Clock clock);
+
+  /**
+   * Provides a {@link io.opencensus.metrics.export.MetricDescriptor}.
+   *
+   * @return a {@code MetricDescriptor}.
+   */
+  MetricDescriptor getMetricDescriptor();
 }
diff --git a/impl_core/src/main/java/io/opencensus/implcore/metrics/MetricRegistryImpl.java b/impl_core/src/main/java/io/opencensus/implcore/metrics/MetricRegistryImpl.java
index 5e193a2..862cfe9 100644
--- a/impl_core/src/main/java/io/opencensus/implcore/metrics/MetricRegistryImpl.java
+++ b/impl_core/src/main/java/io/opencensus/implcore/metrics/MetricRegistryImpl.java
@@ -59,8 +59,7 @@
             options.getUnit(),
             options.getLabelKeys(),
             options.getConstantLabels());
-    registeredMeters.registerMeter(name, longGaugeMetric);
-    return longGaugeMetric;
+    return (LongGauge) registeredMeters.registerMeter(name, longGaugeMetric);
   }
 
   @Override
@@ -72,8 +71,7 @@
             options.getUnit(),
             options.getLabelKeys(),
             options.getConstantLabels());
-    registeredMeters.registerMeter(name, doubleGaugeMetric);
-    return doubleGaugeMetric;
+    return (DoubleGauge) registeredMeters.registerMeter(name, doubleGaugeMetric);
   }
 
   @Override
@@ -85,8 +83,7 @@
             options.getUnit(),
             options.getLabelKeys(),
             options.getConstantLabels());
-    registeredMeters.registerMeter(name, derivedLongGauge);
-    return derivedLongGauge;
+    return (DerivedLongGauge) registeredMeters.registerMeter(name, derivedLongGauge);
   }
 
   @Override
@@ -98,8 +95,7 @@
             options.getUnit(),
             options.getLabelKeys(),
             options.getConstantLabels());
-    registeredMeters.registerMeter(name, derivedDoubleGauge);
-    return derivedDoubleGauge;
+    return (DerivedDoubleGauge) registeredMeters.registerMeter(name, derivedDoubleGauge);
   }
 
   @Override
@@ -112,8 +108,7 @@
             options.getLabelKeys(),
             options.getConstantLabels(),
             clock.now());
-    registeredMeters.registerMeter(name, longCumulativeMetric);
-    return longCumulativeMetric;
+    return (LongCumulative) registeredMeters.registerMeter(name, longCumulativeMetric);
   }
 
   @Override
@@ -126,8 +121,7 @@
             options.getLabelKeys(),
             options.getConstantLabels(),
             clock.now());
-    registeredMeters.registerMeter(name, longCumulativeMetric);
-    return longCumulativeMetric;
+    return (DoubleCumulative) registeredMeters.registerMeter(name, longCumulativeMetric);
   }
 
   @Override
@@ -140,8 +134,7 @@
             options.getLabelKeys(),
             options.getConstantLabels(),
             clock.now());
-    registeredMeters.registerMeter(name, derivedLongCumulative);
-    return derivedLongCumulative;
+    return (DerivedLongCumulative) registeredMeters.registerMeter(name, derivedLongCumulative);
   }
 
   @Override
@@ -154,8 +147,7 @@
             options.getLabelKeys(),
             options.getConstantLabels(),
             clock.now());
-    registeredMeters.registerMeter(name, derivedDoubleCumulative);
-    return derivedDoubleCumulative;
+    return (DerivedDoubleCumulative) registeredMeters.registerMeter(name, derivedDoubleCumulative);
   }
 
   private static final class RegisteredMeters {
@@ -165,17 +157,21 @@
       return registeredMeters;
     }
 
-    private synchronized void registerMeter(String meterName, Meter meter) {
+    private synchronized Meter registerMeter(String meterName, Meter meter) {
       Meter existingMeter = registeredMeters.get(meterName);
       if (existingMeter != null) {
-        // TODO(mayurkale): Allow users to register the same Meter multiple times without exception.
-        throw new IllegalArgumentException(
-            "A different metric with the same name already registered.");
+        if (!existingMeter.getMetricDescriptor().equals(meter.getMetricDescriptor())) {
+          throw new IllegalArgumentException(
+              "A different metric with the same name already registered.");
+        } else {
+          return existingMeter;
+        }
       }
 
       Map<String, Meter> registeredMetersCopy = new LinkedHashMap<String, Meter>(registeredMeters);
       registeredMetersCopy.put(meterName, meter);
       registeredMeters = Collections.unmodifiableMap(registeredMetersCopy);
+      return meter;
     }
   }
 
diff --git a/impl_core/src/test/java/io/opencensus/implcore/metrics/MetricRegistryImplTest.java b/impl_core/src/test/java/io/opencensus/implcore/metrics/MetricRegistryImplTest.java
index 005194a..ff9c5c8 100644
--- a/impl_core/src/test/java/io/opencensus/implcore/metrics/MetricRegistryImplTest.java
+++ b/impl_core/src/test/java/io/opencensus/implcore/metrics/MetricRegistryImplTest.java
@@ -244,10 +244,47 @@
   }
 
   @Test
-  public void registerDifferentMetricSameName() {
+  public void shouldReturnSameObjectOnMultipleRegisterCall() {
+    LongGauge longGauge = metricRegistry.addLongGauge(NAME, METRIC_OPTIONS);
+    LongPoint longPoint = longGauge.getOrCreateTimeSeries(LABEL_VALUES);
+    longPoint.set(200);
+
+    LongGauge longGauge1 = metricRegistry.addLongGauge(NAME, METRIC_OPTIONS);
+    LongPoint longPoint1 =
+        longGauge1.getOrCreateTimeSeries(Collections.singletonList(LABEL_VALUE_2));
+    longPoint1.set(300);
+
+    assertThat(longGauge).isEqualTo(longGauge1);
+    Collection<Metric> metricCollections = metricRegistry.getMetricProducer().getMetrics();
+    assertThat(metricCollections.size()).isEqualTo(1);
+    assertThat(metricCollections)
+        .containsExactly(
+            Metric.create(
+                LONG_METRIC_DESCRIPTOR,
+                Arrays.asList(
+                    TimeSeries.createWithOnePoint(
+                        Arrays.asList(LABEL_VALUE, LABEL_VALUE_2),
+                        Point.create(Value.longValue(200), TEST_TIME),
+                        null),
+                    TimeSeries.createWithOnePoint(
+                        Arrays.asList(LABEL_VALUE_2, LABEL_VALUE_2),
+                        Point.create(Value.longValue(300), TEST_TIME),
+                        null))));
+  }
+
+  @Test
+  public void shouldThrowWhenRegisterExistingMetricWithDiffType() {
     metricRegistry.addLongGauge(NAME, DESCRIPTION, UNIT, LABEL_KEYS);
     thrown.expect(IllegalArgumentException.class);
     thrown.expectMessage("A different metric with the same name already registered.");
     metricRegistry.addDoubleGauge(NAME, DESCRIPTION, UNIT, LABEL_KEYS);
   }
+
+  @Test
+  public void shouldThrowWhenRegisterExistingMetricWithDiffDesc() {
+    metricRegistry.addLongGauge(NAME, DESCRIPTION, UNIT, LABEL_KEYS);
+    thrown.expect(IllegalArgumentException.class);
+    thrown.expectMessage("A different metric with the same name already registered.");
+    metricRegistry.addLongGauge(NAME, "duplicate", UNIT, LABEL_KEYS);
+  }
 }