You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by el...@apache.org on 2016/02/04 19:33:37 UTC

[2/3] calcite git commit: [CALCITE-642] Add an avatica-metrics API

http://git-wip-us.apache.org/repos/asf/calcite/blob/7e5710fc/avatica-metrics/src/main/java/org/apache/calcite/avatica/metrics/noop/NoopMetricsSystemConfiguration.java
----------------------------------------------------------------------
diff --git a/avatica-metrics/src/main/java/org/apache/calcite/avatica/metrics/noop/NoopMetricsSystemConfiguration.java b/avatica-metrics/src/main/java/org/apache/calcite/avatica/metrics/noop/NoopMetricsSystemConfiguration.java
new file mode 100644
index 0000000..42b3f24
--- /dev/null
+++ b/avatica-metrics/src/main/java/org/apache/calcite/avatica/metrics/noop/NoopMetricsSystemConfiguration.java
@@ -0,0 +1,40 @@
+/*
+ * 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.calcite.avatica.metrics.noop;
+
+import org.apache.calcite.avatica.metrics.MetricsSystemConfiguration;
+
+/**
+ * An empty configuration for the {@link NoopMetricsSystem}.
+ */
+public class NoopMetricsSystemConfiguration implements MetricsSystemConfiguration<Void> {
+
+  private static final NoopMetricsSystemConfiguration INSTANCE =
+      new NoopMetricsSystemConfiguration();
+
+  public static NoopMetricsSystemConfiguration getInstance() {
+    return INSTANCE;
+  }
+
+  private NoopMetricsSystemConfiguration() {}
+
+  @Override public Void get() {
+    return null;
+  }
+}
+
+// End NoopMetricsSystemConfiguration.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/7e5710fc/avatica-metrics/src/main/java/org/apache/calcite/avatica/metrics/noop/NoopMetricsSystemFactory.java
----------------------------------------------------------------------
diff --git a/avatica-metrics/src/main/java/org/apache/calcite/avatica/metrics/noop/NoopMetricsSystemFactory.java b/avatica-metrics/src/main/java/org/apache/calcite/avatica/metrics/noop/NoopMetricsSystemFactory.java
new file mode 100644
index 0000000..c15f978
--- /dev/null
+++ b/avatica-metrics/src/main/java/org/apache/calcite/avatica/metrics/noop/NoopMetricsSystemFactory.java
@@ -0,0 +1,35 @@
+/*
+ * 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.calcite.avatica.metrics.noop;
+
+import org.apache.calcite.avatica.metrics.MetricsSystemConfiguration;
+import org.apache.calcite.avatica.metrics.MetricsSystemFactory;
+
+/**
+ * A {@link MetricsSystemFactory} for the {@link NoopMetricsSystem}.
+ *
+ * No service file is provided for this implementation. It is the fallback implementation if
+ * no implementation or more than one implementation is found on the classpath.
+ */
+public class NoopMetricsSystemFactory implements MetricsSystemFactory {
+
+  @Override public NoopMetricsSystem create(MetricsSystemConfiguration<?> config) {
+    return NoopMetricsSystem.getInstance();
+  }
+}
+
+// End NoopMetricsSystemFactory.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/7e5710fc/avatica-metrics/src/main/java/org/apache/calcite/avatica/metrics/noop/NoopTimer.java
----------------------------------------------------------------------
diff --git a/avatica-metrics/src/main/java/org/apache/calcite/avatica/metrics/noop/NoopTimer.java b/avatica-metrics/src/main/java/org/apache/calcite/avatica/metrics/noop/NoopTimer.java
new file mode 100644
index 0000000..879da64
--- /dev/null
+++ b/avatica-metrics/src/main/java/org/apache/calcite/avatica/metrics/noop/NoopTimer.java
@@ -0,0 +1,41 @@
+/*
+ * 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.calcite.avatica.metrics.noop;
+
+import org.apache.calcite.avatica.metrics.Timer;
+import org.apache.calcite.avatica.metrics.Timer.Context;
+
+/**
+ * {@link Timer} which does nothing.
+ */
+public class NoopTimer implements Timer {
+
+  @Override public Context start() {
+    return new NoopContext();
+  }
+
+  /**
+   * {@link Context} which does nothing.
+   */
+  public class NoopContext implements Context {
+
+    @Override public void close() {}
+
+  }
+}
+
+// End NoopTimer.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/7e5710fc/avatica-metrics/src/main/java/org/apache/calcite/avatica/metrics/noop/package-info.java
----------------------------------------------------------------------
diff --git a/avatica-metrics/src/main/java/org/apache/calcite/avatica/metrics/noop/package-info.java b/avatica-metrics/src/main/java/org/apache/calcite/avatica/metrics/noop/package-info.java
new file mode 100644
index 0000000..826a655
--- /dev/null
+++ b/avatica-metrics/src/main/java/org/apache/calcite/avatica/metrics/noop/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+/**
+ * No-operation implementation for the Avatica Metrics framework.
+ */
+@PackageMarker
+package org.apache.calcite.avatica.metrics.noop;
+
+import org.apache.calcite.avatica.metrics.PackageMarker;
+
+// End package-info.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/7e5710fc/avatica-metrics/src/main/java/org/apache/calcite/avatica/metrics/package-info.java
----------------------------------------------------------------------
diff --git a/avatica-metrics/src/main/java/org/apache/calcite/avatica/metrics/package-info.java b/avatica-metrics/src/main/java/org/apache/calcite/avatica/metrics/package-info.java
new file mode 100644
index 0000000..efed28c
--- /dev/null
+++ b/avatica-metrics/src/main/java/org/apache/calcite/avatica/metrics/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+/**
+ * Metrics for the Avatica framework.
+ */
+@PackageMarker
+package org.apache.calcite.avatica.metrics;
+
+// End package-info.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/7e5710fc/avatica-metrics/src/test/java/org/apache/calcite/avatica/metrics/MetricsSystemLoaderTest.java
----------------------------------------------------------------------
diff --git a/avatica-metrics/src/test/java/org/apache/calcite/avatica/metrics/MetricsSystemLoaderTest.java b/avatica-metrics/src/test/java/org/apache/calcite/avatica/metrics/MetricsSystemLoaderTest.java
new file mode 100644
index 0000000..1c405ee
--- /dev/null
+++ b/avatica-metrics/src/test/java/org/apache/calcite/avatica/metrics/MetricsSystemLoaderTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.calcite.avatica.metrics;
+
+import org.apache.calcite.avatica.metrics.noop.NoopMetricsSystem;
+import org.apache.calcite.avatica.metrics.noop.NoopMetricsSystemConfiguration;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test class for {@link MetricsSystemLoader}.
+ */
+public class MetricsSystemLoaderTest {
+
+  @Test public void testSingleInstance() {
+    final List<MetricsSystemFactory> factories =
+        Collections.<MetricsSystemFactory>singletonList(new MarkedNoopMetricsSystemFactory());
+    MetricsSystemLoader loader = Mockito.mock(MetricsSystemLoader.class);
+
+    Mockito.when(loader.getFactories()).thenReturn(factories);
+    Mockito.when(loader._load(Mockito.any(MetricsSystemConfiguration.class))).thenCallRealMethod();
+
+    // One MetricsSystemFactory should return the MetricsSystem it creates
+    MetricsSystem system = loader._load(NoopMetricsSystemConfiguration.getInstance());
+    assertEquals(MarkedMetricsSystem.INSTANCE, system);
+  }
+
+  @Test public void testMultipleInstances() {
+    // The type of the factories doesn't matter (we can send duplicates for testing purposes)
+    final List<MetricsSystemFactory> factories =
+        Arrays.<MetricsSystemFactory>asList(new MarkedNoopMetricsSystemFactory(),
+            new MarkedNoopMetricsSystemFactory());
+    MetricsSystemLoader loader = Mockito.mock(MetricsSystemLoader.class);
+
+    Mockito.when(loader.getFactories()).thenReturn(factories);
+    Mockito.when(loader._load(Mockito.any(MetricsSystemConfiguration.class))).thenCallRealMethod();
+
+    // We had two factories loaded, therefore we'll fall back to the NoopMetricsSystem
+    MetricsSystem system = loader._load(NoopMetricsSystemConfiguration.getInstance());
+    assertEquals(NoopMetricsSystem.getInstance(), system);
+  }
+
+  @Test public void testNoInstances() {
+    // The type of the factories doesn't matter (we can send duplicates for testing purposes)
+    final List<MetricsSystemFactory> factories = Collections.emptyList();
+    MetricsSystemLoader loader = Mockito.mock(MetricsSystemLoader.class);
+
+    Mockito.when(loader.getFactories()).thenReturn(factories);
+    Mockito.when(loader._load(Mockito.any(MetricsSystemConfiguration.class))).thenCallRealMethod();
+
+    // We had no factories loaded, therefore we'll fall back to the NoopMetricsSystem
+    MetricsSystem system = loader._load(NoopMetricsSystemConfiguration.getInstance());
+    assertEquals(NoopMetricsSystem.getInstance(), system);
+  }
+
+  /**
+   * A test factory implementation which can return a recognized MetricsSystem implementation.
+   */
+  private static class MarkedNoopMetricsSystemFactory implements MetricsSystemFactory {
+    public MarkedMetricsSystem create(MetricsSystemConfiguration<?> config) {
+      return MarkedMetricsSystem.INSTANCE;
+    }
+  }
+
+  /**
+   * A metrics system implementation that is identifiable for testing.
+   */
+  private static class MarkedMetricsSystem implements MetricsSystem {
+    private static final MarkedMetricsSystem INSTANCE = new MarkedMetricsSystem();
+
+    private MarkedMetricsSystem() {}
+
+    @Override public Timer getTimer(String name) {
+      return null;
+    }
+
+    @Override public Histogram getHistogram(String name) {
+      return null;
+    }
+
+    @Override public Meter getMeter(String name) {
+      return null;
+    }
+
+    @Override public Counter getCounter(String name) {
+      return null;
+    }
+
+    @Override public <T> void register(String name, Gauge<T> gauge) {}
+  }
+}
+
+// End MetricsSystemLoaderTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/7e5710fc/avatica-metrics/src/test/java/org/apache/calcite/avatica/metrics/noop/NoopMetricsSystemFactoryTest.java
----------------------------------------------------------------------
diff --git a/avatica-metrics/src/test/java/org/apache/calcite/avatica/metrics/noop/NoopMetricsSystemFactoryTest.java b/avatica-metrics/src/test/java/org/apache/calcite/avatica/metrics/noop/NoopMetricsSystemFactoryTest.java
new file mode 100644
index 0000000..3060b2f
--- /dev/null
+++ b/avatica-metrics/src/test/java/org/apache/calcite/avatica/metrics/noop/NoopMetricsSystemFactoryTest.java
@@ -0,0 +1,37 @@
+/*
+ * 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.calcite.avatica.metrics.noop;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test class for {@link NoopMetricsSystemFactory}.
+ */
+public class NoopMetricsSystemFactoryTest {
+
+  @Test public void testSingleton() {
+    NoopMetricsSystemFactory factory = new NoopMetricsSystemFactory();
+    NoopMetricsSystemConfiguration config = NoopMetricsSystemConfiguration.getInstance();
+    assertTrue("The factory should only return one NoopMetricsSystem instance",
+        factory.create(config) == factory.create(config));
+  }
+
+}
+
+// End NoopMetricsSystemFactoryTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/7e5710fc/avatica-metrics/src/test/java/org/apache/calcite/avatica/metrics/noop/NoopMetricsSystemTest.java
----------------------------------------------------------------------
diff --git a/avatica-metrics/src/test/java/org/apache/calcite/avatica/metrics/noop/NoopMetricsSystemTest.java b/avatica-metrics/src/test/java/org/apache/calcite/avatica/metrics/noop/NoopMetricsSystemTest.java
new file mode 100644
index 0000000..cda453c
--- /dev/null
+++ b/avatica-metrics/src/test/java/org/apache/calcite/avatica/metrics/noop/NoopMetricsSystemTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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.calcite.avatica.metrics.noop;
+
+import org.apache.calcite.avatica.metrics.Counter;
+import org.apache.calcite.avatica.metrics.Gauge;
+import org.apache.calcite.avatica.metrics.Histogram;
+import org.apache.calcite.avatica.metrics.Meter;
+import org.apache.calcite.avatica.metrics.MetricsSystem;
+import org.apache.calcite.avatica.metrics.Timer;
+import org.apache.calcite.avatica.metrics.Timer.Context;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for {@link NoopMetricsSystem}.
+ */
+public class NoopMetricsSystemTest {
+
+  @Test public void testNoNulls() {
+    // The NOOP implementation should act as a real implementation, no "nulls" allowed.
+    MetricsSystem metrics = NoopMetricsSystem.getInstance();
+
+    Counter counter = metrics.getCounter("counter");
+    counter.decrement();
+    counter.increment();
+    counter.decrement(1L);
+    counter.increment(1L);
+
+    Histogram histogram = metrics.getHistogram("histogram");
+    histogram.update(1);
+    histogram.update(1L);
+
+    Timer timer = metrics.getTimer("timer");
+    Context context = timer.start();
+    context.close();
+
+    Meter meter = metrics.getMeter("meter");
+    meter.mark();
+    meter.mark(5L);
+
+    metrics.register("gauge", new Gauge<Long>() {
+      @Override public Long getValue() {
+        return 42L;
+      }
+    });
+  }
+
+  @Test public void testSingleton() {
+    assertTrue("Should be a singleton",
+        NoopMetricsSystem.getInstance() == NoopMetricsSystem.getInstance());
+  }
+}
+
+// End NoopMetricsSystemTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/7e5710fc/avatica-metrics/src/test/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/avatica-metrics/src/test/resources/log4j.properties b/avatica-metrics/src/test/resources/log4j.properties
new file mode 100644
index 0000000..834e2db
--- /dev/null
+++ b/avatica-metrics/src/test/resources/log4j.properties
@@ -0,0 +1,24 @@
+# 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.
+
+# Root logger is configured at INFO and is sent to A1
+log4j.rootLogger=INFO, A1
+
+# A1 goes to the console
+log4j.appender.A1=org.apache.log4j.ConsoleAppender
+
+# Set the pattern for each log message
+log4j.appender.A1.layout=org.apache.log4j.PatternLayout
+log4j.appender.A1.layout.ConversionPattern=%d [%t] %-5p - %m%n

http://git-wip-us.apache.org/repos/asf/calcite/blob/7e5710fc/avatica-server/pom.xml
----------------------------------------------------------------------
diff --git a/avatica-server/pom.xml b/avatica-server/pom.xml
index 2386243..9a896f5 100644
--- a/avatica-server/pom.xml
+++ b/avatica-server/pom.xml
@@ -25,7 +25,6 @@ limitations under the License.
 
   <artifactId>calcite-avatica-server</artifactId>
   <packaging>jar</packaging>
-  <version>1.7.0-SNAPSHOT</version>
   <name>Calcite Avatica Server</name>
   <description>JDBC server.</description>
 
@@ -108,6 +107,34 @@ limitations under the License.
   </dependencies>
 
   <build>
+    <pluginManagement>
+      <plugins>
+        <plugin>
+          <groupId>org.eclipse.m2e</groupId>
+          <artifactId>lifecycle-mapping</artifactId>
+          <version>1.0.0</version>
+          <configuration>
+            <lifecycleMappingMetadata>
+              <pluginExecutions>
+                <pluginExecution>
+                  <pluginExecutionFilter>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-checkstyle-plugin</artifactId>
+                    <versionRange>[2.12.1,)</versionRange>
+                    <goals>
+                      <goal>check</goal>
+                    </goals>
+                  </pluginExecutionFilter>
+                  <action>
+                    <ignore />
+                  </action>
+                </pluginExecution>
+              </pluginExecutions>
+            </lifecycleMappingMetadata>
+          </configuration>
+        </plugin>
+      </plugins>
+    </pluginManagement>
     <plugins>
       <!-- Parent module has the same plugin and does the work of
            generating -sources.jar for each project. But without the
@@ -143,6 +170,7 @@ limitations under the License.
               <failOnWarning>true</failOnWarning>
               <!-- ignore "unused but declared" warnings -->
               <ignoredUnusedDeclaredDependencies>
+                <ignoredUnusedDeclaredDependency>io.dropwizard.metrics:metrics-core</ignoredUnusedDeclaredDependency>
                 <ignoredUnusedDeclaredDependency>net.hydromatic:scott-data-hsqldb</ignoredUnusedDeclaredDependency>
                 <ignoredUnusedDeclaredDependency>org.hsqldb:hsqldb</ignoredUnusedDeclaredDependency>
                 <ignoredUnusedDeclaredDependency>org.slf4j:slf4j-api</ignoredUnusedDeclaredDependency>

http://git-wip-us.apache.org/repos/asf/calcite/blob/7e5710fc/avatica-server/src/main/java/org/apache/calcite/avatica/jdbc/JdbcMeta.java
----------------------------------------------------------------------
diff --git a/avatica-server/src/main/java/org/apache/calcite/avatica/jdbc/JdbcMeta.java b/avatica-server/src/main/java/org/apache/calcite/avatica/jdbc/JdbcMeta.java
index 8cac9f8..becb19d 100644
--- a/avatica-server/src/main/java/org/apache/calcite/avatica/jdbc/JdbcMeta.java
+++ b/avatica-server/src/main/java/org/apache/calcite/avatica/jdbc/JdbcMeta.java
@@ -28,6 +28,9 @@ import org.apache.calcite.avatica.NoSuchConnectionException;
 import org.apache.calcite.avatica.NoSuchStatementException;
 import org.apache.calcite.avatica.QueryState;
 import org.apache.calcite.avatica.SqlType;
+import org.apache.calcite.avatica.metrics.Gauge;
+import org.apache.calcite.avatica.metrics.MetricsSystem;
+import org.apache.calcite.avatica.metrics.noop.NoopMetricsSystem;
 import org.apache.calcite.avatica.remote.TypedValue;
 
 import com.google.common.cache.Cache;
@@ -38,6 +41,8 @@ import com.google.common.cache.RemovalNotification;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import static org.apache.calcite.avatica.remote.MetricsHelper.concat;
+
 import java.lang.reflect.InvocationTargetException;
 import java.sql.Connection;
 import java.sql.DatabaseMetaData;
@@ -55,6 +60,7 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Properties;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -86,6 +92,7 @@ public class JdbcMeta implements Meta {
   private final Properties info;
   private final Cache<String, Connection> connectionCache;
   private final Cache<Integer, StatementInfo> statementCache;
+  private final MetricsSystem metrics;
 
   /**
    * Creates a JdbcMeta.
@@ -116,6 +123,10 @@ public class JdbcMeta implements Meta {
     });
   }
 
+  public JdbcMeta(String url, Properties info) throws SQLException {
+    this(url, info, NoopMetricsSystem.getInstance());
+  }
+
   /**
    * Creates a JdbcMeta.
    *
@@ -125,9 +136,11 @@ public class JdbcMeta implements Meta {
    * connection arguments; normally at least a "user" and
    * "password" property should be included
    */
-  public JdbcMeta(String url, Properties info) throws SQLException {
+  public JdbcMeta(String url, Properties info, MetricsSystem metrics)
+      throws SQLException {
     this.url = url;
     this.info = info;
+    this.metrics = Objects.requireNonNull(metrics);
 
     int concurrencyLevel = Integer.parseInt(
         info.getProperty(ConnectionCacheSettings.CONCURRENCY_LEVEL.key(),
@@ -177,6 +190,19 @@ public class JdbcMeta implements Meta {
         .build();
 
     LOG.debug("instantiated statement cache: {}", statementCache.stats());
+
+    // Register some metrics
+    this.metrics.register(concat(JdbcMeta.class, "ConnectionCacheSize"), new Gauge<Long>() {
+      @Override public Long getValue() {
+        return connectionCache.size();
+      }
+    });
+
+    this.metrics.register(concat(JdbcMeta.class, "StatementCacheSize"), new Gauge<Long>() {
+      @Override public Long getValue() {
+        return statementCache.size();
+      }
+    });
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/calcite/blob/7e5710fc/avatica-server/src/main/java/org/apache/calcite/avatica/server/AvaticaJsonHandler.java
----------------------------------------------------------------------
diff --git a/avatica-server/src/main/java/org/apache/calcite/avatica/server/AvaticaJsonHandler.java b/avatica-server/src/main/java/org/apache/calcite/avatica/server/AvaticaJsonHandler.java
index 250c1d5..703a2c3 100644
--- a/avatica-server/src/main/java/org/apache/calcite/avatica/server/AvaticaJsonHandler.java
+++ b/avatica-server/src/main/java/org/apache/calcite/avatica/server/AvaticaJsonHandler.java
@@ -17,6 +17,10 @@
 package org.apache.calcite.avatica.server;
 
 import org.apache.calcite.avatica.AvaticaUtils;
+import org.apache.calcite.avatica.metrics.MetricsSystem;
+import org.apache.calcite.avatica.metrics.Timer;
+import org.apache.calcite.avatica.metrics.Timer.Context;
+import org.apache.calcite.avatica.metrics.noop.NoopMetricsSystem;
 import org.apache.calcite.avatica.remote.Handler.HandlerResponse;
 import org.apache.calcite.avatica.remote.JsonHandler;
 import org.apache.calcite.avatica.remote.Service;
@@ -28,6 +32,8 @@ import org.eclipse.jetty.server.handler.AbstractHandler;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import static org.apache.calcite.avatica.remote.MetricsHelper.concat;
+
 import java.io.IOException;
 import java.util.Objects;
 
@@ -39,41 +45,56 @@ import javax.servlet.http.HttpServletResponse;
 /**
  * Jetty handler that executes Avatica JSON request-responses.
  */
-public class AvaticaJsonHandler extends AbstractHandler implements AvaticaHandler {
+public class AvaticaJsonHandler extends AbstractHandler implements MetricsAwareAvaticaHandler {
   private static final Logger LOG = LoggerFactory.getLogger(AvaticaJsonHandler.class);
 
   final Service service;
   final JsonHandler jsonHandler;
 
+  final MetricsSystem metrics;
+  final Timer requestTimer;
+
   public AvaticaJsonHandler(Service service) {
+    this(service, NoopMetricsSystem.getInstance());
+  }
+
+  public AvaticaJsonHandler(Service service, MetricsSystem metrics) {
     this.service = Objects.requireNonNull(service);
-    this.jsonHandler = new JsonHandler(service);
+    this.metrics = Objects.requireNonNull(metrics);
+    // Avatica doesn't have a Guava dependency
+    this.jsonHandler = new JsonHandler(service, this.metrics);
+
+    // Metrics
+    this.requestTimer = this.metrics.getTimer(
+        concat(AvaticaJsonHandler.class, MetricsAwareAvaticaHandler.REQUEST_TIMER_NAME));
   }
 
   public void handle(String target, Request baseRequest,
       HttpServletRequest request, HttpServletResponse response)
       throws IOException, ServletException {
-    response.setContentType("application/json;charset=utf-8");
-    response.setStatus(HttpServletResponse.SC_OK);
-    if (request.getMethod().equals("POST")) {
-      // First look for a request in the header, then look in the body.
-      // The latter allows very large requests without hitting HTTP 413.
-      String rawRequest = request.getHeader("request");
-      if (rawRequest == null) {
-        try (ServletInputStream inputStream = request.getInputStream()) {
-          rawRequest = AvaticaUtils.readFully(inputStream);
+    try (final Context ctx = requestTimer.start()) {
+      response.setContentType("application/json;charset=utf-8");
+      response.setStatus(HttpServletResponse.SC_OK);
+      if (request.getMethod().equals("POST")) {
+        // First look for a request in the header, then look in the body.
+        // The latter allows very large requests without hitting HTTP 413.
+        String rawRequest = request.getHeader("request");
+        if (rawRequest == null) {
+          try (ServletInputStream inputStream = request.getInputStream()) {
+            rawRequest = AvaticaUtils.readFully(inputStream);
+          }
         }
+        final String jsonRequest =
+            new String(rawRequest.getBytes("ISO-8859-1"), "UTF-8");
+        LOG.trace("request: {}", jsonRequest);
+
+        final HandlerResponse<String> jsonResponse = jsonHandler.apply(jsonRequest);
+        LOG.trace("response: {}", jsonResponse);
+        baseRequest.setHandled(true);
+        // Set the status code and write out the response.
+        response.setStatus(jsonResponse.getStatusCode());
+        response.getWriter().println(jsonResponse.getResponse());
       }
-      final String jsonRequest =
-          new String(rawRequest.getBytes("ISO-8859-1"), "UTF-8");
-      LOG.trace("request: {}", jsonRequest);
-
-      final HandlerResponse<String> jsonResponse = jsonHandler.apply(jsonRequest);
-      LOG.trace("response: {}", jsonResponse);
-      baseRequest.setHandled(true);
-      // Set the status code and write out the response.
-      response.setStatus(jsonResponse.getStatusCode());
-      response.getWriter().println(jsonResponse.getResponse());
     }
   }
 
@@ -83,6 +104,10 @@ public class AvaticaJsonHandler extends AbstractHandler implements AvaticaHandle
     // Also add it to the handler to include with exceptions
     jsonHandler.setRpcMetadata(metadata);
   }
+
+  @Override public MetricsSystem getMetrics() {
+    return metrics;
+  }
 }
 
 // End AvaticaJsonHandler.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/7e5710fc/avatica-server/src/main/java/org/apache/calcite/avatica/server/AvaticaProtobufHandler.java
----------------------------------------------------------------------
diff --git a/avatica-server/src/main/java/org/apache/calcite/avatica/server/AvaticaProtobufHandler.java b/avatica-server/src/main/java/org/apache/calcite/avatica/server/AvaticaProtobufHandler.java
index e44de00..aeebad7 100644
--- a/avatica-server/src/main/java/org/apache/calcite/avatica/server/AvaticaProtobufHandler.java
+++ b/avatica-server/src/main/java/org/apache/calcite/avatica/server/AvaticaProtobufHandler.java
@@ -17,7 +17,12 @@
 package org.apache.calcite.avatica.server;
 
 import org.apache.calcite.avatica.AvaticaUtils;
+import org.apache.calcite.avatica.metrics.MetricsSystem;
+import org.apache.calcite.avatica.metrics.Timer;
+import org.apache.calcite.avatica.metrics.Timer.Context;
+import org.apache.calcite.avatica.metrics.noop.NoopMetricsSystem;
 import org.apache.calcite.avatica.remote.Handler.HandlerResponse;
+import org.apache.calcite.avatica.remote.MetricsHelper;
 import org.apache.calcite.avatica.remote.ProtobufHandler;
 import org.apache.calcite.avatica.remote.ProtobufTranslation;
 import org.apache.calcite.avatica.remote.ProtobufTranslationImpl;
@@ -41,35 +46,48 @@ import javax.servlet.http.HttpServletResponse;
 /**
  * Jetty handler that executes Avatica JSON request-responses.
  */
-public class AvaticaProtobufHandler extends AbstractHandler implements AvaticaHandler {
+public class AvaticaProtobufHandler extends AbstractHandler implements MetricsAwareAvaticaHandler {
   private static final Logger LOG = LoggerFactory.getLogger(AvaticaJsonHandler.class);
 
   private final Service service;
   private final ProtobufHandler pbHandler;
   private final ProtobufTranslation protobufTranslation;
+  private final MetricsSystem metrics;
+  private final Timer requestTimer;
 
   public AvaticaProtobufHandler(Service service) {
+    this(service, NoopMetricsSystem.getInstance());
+  }
+
+  public AvaticaProtobufHandler(Service service, MetricsSystem metrics) {
     this.protobufTranslation = new ProtobufTranslationImpl();
     this.service = Objects.requireNonNull(service);
-    this.pbHandler = new ProtobufHandler(service, protobufTranslation);
+    this.metrics = Objects.requireNonNull(metrics);
+    this.pbHandler = new ProtobufHandler(service, protobufTranslation, metrics);
+
+    this.requestTimer = this.metrics.getTimer(
+        MetricsHelper.concat(AvaticaProtobufHandler.class,
+            MetricsAwareAvaticaHandler.REQUEST_TIMER_NAME));
   }
 
   public void handle(String target, Request baseRequest,
       HttpServletRequest request, HttpServletResponse response)
       throws IOException, ServletException {
-    response.setContentType("application/octet-stream;charset=utf-8");
-    response.setStatus(HttpServletResponse.SC_OK);
-    if (request.getMethod().equals("POST")) {
-      byte[] requestBytes;
-      try (ServletInputStream inputStream = request.getInputStream()) {
-        requestBytes = AvaticaUtils.readFullyToBytes(inputStream);
-      }
+    try (final Context ctx = this.requestTimer.start()) {
+      response.setContentType("application/octet-stream;charset=utf-8");
+      response.setStatus(HttpServletResponse.SC_OK);
+      if (request.getMethod().equals("POST")) {
+        byte[] requestBytes;
+        try (ServletInputStream inputStream = request.getInputStream()) {
+          requestBytes = AvaticaUtils.readFullyToBytes(inputStream);
+        }
 
-      HandlerResponse<byte[]> handlerResponse = pbHandler.apply(requestBytes);
+        HandlerResponse<byte[]> handlerResponse = pbHandler.apply(requestBytes);
 
-      baseRequest.setHandled(true);
-      response.setStatus(handlerResponse.getStatusCode());
-      response.getOutputStream().write(handlerResponse.getResponse());
+        baseRequest.setHandled(true);
+        response.setStatus(handlerResponse.getStatusCode());
+        response.getOutputStream().write(handlerResponse.getResponse());
+      }
     }
   }
 
@@ -79,6 +97,11 @@ public class AvaticaProtobufHandler extends AbstractHandler implements AvaticaHa
     // Also add it to the handler to include with exceptions
     pbHandler.setRpcMetadata(metadata);
   }
+
+  @Override public MetricsSystem getMetrics() {
+    return this.metrics;
+  }
+
 }
 
 // End AvaticaProtobufHandler.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/7e5710fc/avatica-server/src/main/java/org/apache/calcite/avatica/server/DelegatingAvaticaHandler.java
----------------------------------------------------------------------
diff --git a/avatica-server/src/main/java/org/apache/calcite/avatica/server/DelegatingAvaticaHandler.java b/avatica-server/src/main/java/org/apache/calcite/avatica/server/DelegatingAvaticaHandler.java
index 223fba7..a574985 100644
--- a/avatica-server/src/main/java/org/apache/calcite/avatica/server/DelegatingAvaticaHandler.java
+++ b/avatica-server/src/main/java/org/apache/calcite/avatica/server/DelegatingAvaticaHandler.java
@@ -37,6 +37,9 @@ import javax.servlet.http.HttpServletResponse;
  *
  * <p>This implementation provides a no-op implementation for
  * {@link #setServerRpcMetadata(org.apache.calcite.avatica.remote.Service.RpcMetadataResponse)}.
+ *
+ * Does not implement {@link MetricsAwareAvaticaHandler} as this implementation is only presented
+ * for backwards compatibility.
  */
 public class DelegatingAvaticaHandler implements AvaticaHandler {
   private static final Logger LOG = LoggerFactory.getLogger(DelegatingAvaticaHandler.class);

http://git-wip-us.apache.org/repos/asf/calcite/blob/7e5710fc/avatica-server/src/main/java/org/apache/calcite/avatica/server/HandlerFactory.java
----------------------------------------------------------------------
diff --git a/avatica-server/src/main/java/org/apache/calcite/avatica/server/HandlerFactory.java b/avatica-server/src/main/java/org/apache/calcite/avatica/server/HandlerFactory.java
index 724626d..b1fcb40 100644
--- a/avatica-server/src/main/java/org/apache/calcite/avatica/server/HandlerFactory.java
+++ b/avatica-server/src/main/java/org/apache/calcite/avatica/server/HandlerFactory.java
@@ -16,33 +16,100 @@
  */
 package org.apache.calcite.avatica.server;
 
+import org.apache.calcite.avatica.metrics.MetricsSystem;
+import org.apache.calcite.avatica.metrics.MetricsSystemConfiguration;
+import org.apache.calcite.avatica.metrics.MetricsSystemFactory;
+import org.apache.calcite.avatica.metrics.MetricsSystemLoader;
+import org.apache.calcite.avatica.metrics.noop.NoopMetricsSystem;
+import org.apache.calcite.avatica.metrics.noop.NoopMetricsSystemConfiguration;
 import org.apache.calcite.avatica.remote.Driver;
 import org.apache.calcite.avatica.remote.Service;
 
 import org.eclipse.jetty.server.Handler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.ServiceLoader;
 
 /**
  * Factory that instantiates the desired implementation, typically differing on the method
  * used to serialize messages, for use in the Avatica server.
  */
 public class HandlerFactory {
+  private static final Logger LOG = LoggerFactory.getLogger(HandlerFactory.class);
 
   /**
-   * The desired implementation for the given serialization method.
+   * Constructs the desired implementation for the given serialization method with metrics.
    *
-   * @param serialization The desired message serialization
+   * @param service The underlying {@link Service}.
+   * @param serialization The desired message serialization.
+   * @return The {@link Handler}.
    */
   public Handler getHandler(Service service, Driver.Serialization serialization) {
+    return getHandler(service, serialization, NoopMetricsSystemConfiguration.getInstance());
+  }
+
+  /**
+   * Constructs the desired implementation for the given serialization method with metrics.
+   *
+   * @param service The underlying {@link Service}.
+   * @param serialization The desired message serialization.
+   * @param metricsConfig Configuration for the {@link MetricsSystem}.
+   * @return The {@link Handler}.
+   */
+  public Handler getHandler(Service service, Driver.Serialization serialization,
+      MetricsSystemConfiguration<?> metricsConfig) {
+    MetricsSystem metrics = MetricsSystemLoader.load(Objects.requireNonNull(metricsConfig));
+
     switch (serialization) {
     case JSON:
-      return new AvaticaJsonHandler(service);
+      return new AvaticaJsonHandler(service, metrics);
     case PROTOBUF:
-      return new AvaticaProtobufHandler(service);
+      return new AvaticaProtobufHandler(service, metrics);
     default:
       throw new IllegalArgumentException("Unknown Avatica handler for " + serialization.name());
     }
   }
 
+  /**
+   * Load a {@link MetricsSystem} using ServiceLoader to create a {@link MetricsSystemFactory}.
+   *
+   * @param config State to pass to the factory for initialization.
+   * @return A {@link MetricsSystem} instance.
+   */
+  MetricsSystem loadMetricsSystem(MetricsSystemConfiguration<?> config) {
+    ServiceLoader<MetricsSystemFactory> loader = ServiceLoader.load(MetricsSystemFactory.class);
+    List<MetricsSystemFactory> availableFactories = new ArrayList<>();
+    for (MetricsSystemFactory factory : loader) {
+      availableFactories.add(factory);
+    }
+
+    if (1 == availableFactories.size()) {
+      // One and only one instance -- what we want
+      MetricsSystemFactory factory = availableFactories.get(0);
+      LOG.info("Loaded MetricsSystem {}", factory.getClass());
+      return factory.create(config);
+    } else if (availableFactories.isEmpty()) {
+      // None-provided default to no metrics
+      LOG.info("No metrics implementation available on classpath. Using No-op implementation");
+      return NoopMetricsSystem.getInstance();
+    } else {
+      // Tell the user they're doing something wrong, and choose the first impl.
+      StringBuilder sb = new StringBuilder();
+      for (MetricsSystemFactory factory : availableFactories) {
+        if (sb.length() > 0) {
+          sb.append(", ");
+        }
+        sb.append(factory.getClass());
+      }
+      LOG.warn("Found multiple MetricsSystemFactory implementations: {}."
+          + " Using No-op implementation", sb);
+      return NoopMetricsSystem.getInstance();
+    }
+  }
 }
 
 // End HandlerFactory.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/7e5710fc/avatica-server/src/main/java/org/apache/calcite/avatica/server/MetricsAwareAvaticaHandler.java
----------------------------------------------------------------------
diff --git a/avatica-server/src/main/java/org/apache/calcite/avatica/server/MetricsAwareAvaticaHandler.java b/avatica-server/src/main/java/org/apache/calcite/avatica/server/MetricsAwareAvaticaHandler.java
new file mode 100644
index 0000000..0914dbd
--- /dev/null
+++ b/avatica-server/src/main/java/org/apache/calcite/avatica/server/MetricsAwareAvaticaHandler.java
@@ -0,0 +1,43 @@
+/*
+ * 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.calcite.avatica.server;
+
+import org.apache.calcite.avatica.metrics.MetricsSystem;
+
+/**
+ * An {@link AvaticaHandler} that is capable of collecting metrics.
+ */
+public interface MetricsAwareAvaticaHandler extends AvaticaHandler {
+
+  /**
+   * General prefix for all metrics in a handler.
+   */
+  String HANDLER_PREFIX = "Handler.";
+
+  /**
+   * Name for timing requests from users
+   */
+  String REQUEST_TIMER_NAME = HANDLER_PREFIX + "RequestTimings";
+
+  /**
+   * @return An instance of the {@link MetricsSystem} for this AvaticaHandler.
+   */
+  MetricsSystem getMetrics();
+
+}
+
+// End MetricsAwareAvaticaHandler.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/7e5710fc/avatica/pom.xml
----------------------------------------------------------------------
diff --git a/avatica/pom.xml b/avatica/pom.xml
index 7e229ea..cf7d875 100644
--- a/avatica/pom.xml
+++ b/avatica/pom.xml
@@ -25,7 +25,6 @@ limitations under the License.
 
   <artifactId>calcite-avatica</artifactId>
   <packaging>jar</packaging>
-  <version>1.7.0-SNAPSHOT</version>
   <name>Calcite Avatica</name>
   <description>JDBC driver framework.</description>
 
@@ -37,6 +36,10 @@ limitations under the License.
     <!-- Make sure that there are no dependencies on other calcite modules,
          or on libraries other than Jackson. -->
     <dependency>
+      <groupId>org.apache.calcite</groupId>
+      <artifactId>calcite-avatica-metrics</artifactId>
+    </dependency>
+    <dependency>
       <groupId>com.fasterxml.jackson.core</groupId>
       <artifactId>jackson-core</artifactId>
     </dependency>
@@ -79,6 +82,34 @@ limitations under the License.
   </dependencies>
 
   <build>
+    <pluginManagement>
+      <plugins>
+        <plugin>
+          <groupId>org.eclipse.m2e</groupId>
+          <artifactId>lifecycle-mapping</artifactId>
+          <version>1.0.0</version>
+          <configuration>
+            <lifecycleMappingMetadata>
+              <pluginExecutions>
+                <pluginExecution>
+                  <pluginExecutionFilter>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-checkstyle-plugin</artifactId>
+                    <versionRange>[2.12.1,)</versionRange>
+                    <goals>
+                      <goal>check</goal>
+                    </goals>
+                  </pluginExecutionFilter>
+                  <action>
+                    <ignore />
+                  </action>
+                </pluginExecution>
+              </pluginExecutions>
+            </lifecycleMappingMetadata>
+          </configuration>
+        </plugin>
+      </plugins>
+    </pluginManagement>
     <plugins>
       <plugin>
         <artifactId>maven-dependency-plugin</artifactId>

http://git-wip-us.apache.org/repos/asf/calcite/blob/7e5710fc/avatica/src/main/java/org/apache/calcite/avatica/remote/Handler.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/remote/Handler.java b/avatica/src/main/java/org/apache/calcite/avatica/remote/Handler.java
index e3ad07f..30d026c 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/remote/Handler.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/remote/Handler.java
@@ -28,6 +28,7 @@ import java.util.Objects;
 public interface Handler<T> {
   int HTTP_OK = 200;
   int HTTP_INTERNAL_SERVER_ERROR = 500;
+  String HANDLER_SERIALIZATION_METRICS_NAME = "Handler.Serialization";
 
   /**
    * Struct that encapsulates the context of the result of a request to Avatica.

http://git-wip-us.apache.org/repos/asf/calcite/blob/7e5710fc/avatica/src/main/java/org/apache/calcite/avatica/remote/JsonHandler.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/remote/JsonHandler.java b/avatica/src/main/java/org/apache/calcite/avatica/remote/JsonHandler.java
index 5afc760..fd57078 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/remote/JsonHandler.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/remote/JsonHandler.java
@@ -16,6 +16,9 @@
  */
 package org.apache.calcite.avatica.remote;
 
+import org.apache.calcite.avatica.metrics.MetricsSystem;
+import org.apache.calcite.avatica.metrics.Timer;
+import org.apache.calcite.avatica.metrics.Timer.Context;
 import org.apache.calcite.avatica.remote.Service.Request;
 import org.apache.calcite.avatica.remote.Service.Response;
 
@@ -35,8 +38,14 @@ public class JsonHandler extends AbstractHandler<String> {
 
   protected static final ObjectMapper MAPPER = JsonService.MAPPER;
 
-  public JsonHandler(Service service) {
+  final MetricsSystem metrics;
+  final Timer serializationTimer;
+
+  public JsonHandler(Service service, MetricsSystem metrics) {
     super(service);
+    this.metrics = metrics;
+    this.serializationTimer = this.metrics.getTimer(
+        MetricsHelper.concat(JsonHandler.class, HANDLER_SERIALIZATION_METRICS_NAME));
   }
 
   public HandlerResponse<String> apply(String jsonRequest) {
@@ -44,7 +53,9 @@ public class JsonHandler extends AbstractHandler<String> {
   }
 
   @Override Request decode(String request) throws IOException {
-    return MAPPER.readValue(request, Service.Request.class);
+    try (final Context ctx = serializationTimer.start()) {
+      return MAPPER.readValue(request, Service.Request.class);
+    }
   }
 
   /**
@@ -54,9 +65,11 @@ public class JsonHandler extends AbstractHandler<String> {
    * @return A JSON string.
    */
   @Override String encode(Response response) throws IOException {
-    final StringWriter w = new StringWriter();
-    MAPPER.writeValue(w, response);
-    return w.toString();
+    try (final Context ctx = serializationTimer.start()) {
+      final StringWriter w = new StringWriter();
+      MAPPER.writeValue(w, response);
+      return w.toString();
+    }
   }
 }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/7e5710fc/avatica/src/main/java/org/apache/calcite/avatica/remote/MetricsHelper.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/remote/MetricsHelper.java b/avatica/src/main/java/org/apache/calcite/avatica/remote/MetricsHelper.java
new file mode 100644
index 0000000..2561b29
--- /dev/null
+++ b/avatica/src/main/java/org/apache/calcite/avatica/remote/MetricsHelper.java
@@ -0,0 +1,36 @@
+/*
+ * 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.calcite.avatica.remote;
+
+/**
+ * A utility class to encapsulate common logic in use of metrics implementation.
+ */
+public class MetricsHelper {
+
+  private static final String PERIOD = ".";
+
+  private MetricsHelper() {}
+
+  public static String concat(Class<?> clz, String name) {
+    StringBuilder sb = new StringBuilder();
+    sb.append(clz.getName());
+    return sb.append(PERIOD).append(name).toString();
+  }
+
+}
+
+// End MetricsHelper.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/7e5710fc/avatica/src/main/java/org/apache/calcite/avatica/remote/ProtobufHandler.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/remote/ProtobufHandler.java b/avatica/src/main/java/org/apache/calcite/avatica/remote/ProtobufHandler.java
index d77b52d..89e380e 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/remote/ProtobufHandler.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/remote/ProtobufHandler.java
@@ -16,6 +16,9 @@
  */
 package org.apache.calcite.avatica.remote;
 
+import org.apache.calcite.avatica.metrics.MetricsSystem;
+import org.apache.calcite.avatica.metrics.Timer;
+import org.apache.calcite.avatica.metrics.Timer.Context;
 import org.apache.calcite.avatica.remote.Service.Response;
 
 import java.io.IOException;
@@ -28,10 +31,15 @@ import java.io.IOException;
 public class ProtobufHandler extends AbstractHandler<byte[]> {
 
   private final ProtobufTranslation translation;
+  private final MetricsSystem metrics;
+  private final Timer serializationTimer;
 
-  public ProtobufHandler(Service service, ProtobufTranslation translation) {
+  public ProtobufHandler(Service service, ProtobufTranslation translation, MetricsSystem metrics) {
     super(service);
     this.translation = translation;
+    this.metrics = metrics;
+    this.serializationTimer = this.metrics.getTimer(
+        MetricsHelper.concat(ProtobufHandler.class, HANDLER_SERIALIZATION_METRICS_NAME));
   }
 
   @Override public HandlerResponse<byte[]> apply(byte[] requestBytes) {
@@ -39,11 +47,15 @@ public class ProtobufHandler extends AbstractHandler<byte[]> {
   }
 
   @Override Service.Request decode(byte[] serializedRequest) throws IOException {
-    return translation.parseRequest(serializedRequest);
+    try (final Context ctx = serializationTimer.start()) {
+      return translation.parseRequest(serializedRequest);
+    }
   }
 
   @Override byte[] encode(Response response) throws IOException {
-    return translation.serializeResponse(response);
+    try (final Context ctx = serializationTimer.start()) {
+      return translation.serializeResponse(response);
+    }
   }
 }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/7e5710fc/avatica/src/test/java/org/apache/calcite/avatica/metrics/MetricsHelperTest.java
----------------------------------------------------------------------
diff --git a/avatica/src/test/java/org/apache/calcite/avatica/metrics/MetricsHelperTest.java b/avatica/src/test/java/org/apache/calcite/avatica/metrics/MetricsHelperTest.java
new file mode 100644
index 0000000..c85312d
--- /dev/null
+++ b/avatica/src/test/java/org/apache/calcite/avatica/metrics/MetricsHelperTest.java
@@ -0,0 +1,42 @@
+/*
+ * 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.calcite.avatica.metrics;
+
+import org.apache.calcite.avatica.remote.MetricsHelper;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test class for {@link MetricsHelper}.
+ */
+public class MetricsHelperTest {
+
+  @Test(expected = NullPointerException.class) public void testNullConcat() {
+    MetricsHelper.concat(null, "foo");
+  }
+
+  @Test public void testConcat() {
+    String suffix = "suffix";
+    String finalName = getClass().getName() + "." + suffix;
+    assertEquals(finalName, MetricsHelper.concat(getClass(), suffix));
+  }
+
+}
+
+// End MetricsHelperTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/7e5710fc/avatica/src/test/java/org/apache/calcite/avatica/remote/ProtobufHandlerTest.java
----------------------------------------------------------------------
diff --git a/avatica/src/test/java/org/apache/calcite/avatica/remote/ProtobufHandlerTest.java b/avatica/src/test/java/org/apache/calcite/avatica/remote/ProtobufHandlerTest.java
index a10c7dc..e7c442c 100644
--- a/avatica/src/test/java/org/apache/calcite/avatica/remote/ProtobufHandlerTest.java
+++ b/avatica/src/test/java/org/apache/calcite/avatica/remote/ProtobufHandlerTest.java
@@ -18,6 +18,7 @@ package org.apache.calcite.avatica.remote;
 
 import org.apache.calcite.avatica.Meta;
 import org.apache.calcite.avatica.Meta.Frame;
+import org.apache.calcite.avatica.metrics.noop.NoopMetricsSystem;
 import org.apache.calcite.avatica.proto.Common;
 import org.apache.calcite.avatica.proto.Common.ColumnValue;
 import org.apache.calcite.avatica.proto.Requests;
@@ -58,7 +59,7 @@ public class ProtobufHandlerTest {
     translation = Mockito.mock(ProtobufTranslation.class);
 
     // Real objects
-    handler = new ProtobufHandler(service, translation);
+    handler = new ProtobufHandler(service, translation, NoopMetricsSystem.getInstance());
   }
 
   @Test

http://git-wip-us.apache.org/repos/asf/calcite/blob/7e5710fc/avatica/src/test/java/org/apache/calcite/avatica/test/JsonHandlerTest.java
----------------------------------------------------------------------
diff --git a/avatica/src/test/java/org/apache/calcite/avatica/test/JsonHandlerTest.java b/avatica/src/test/java/org/apache/calcite/avatica/test/JsonHandlerTest.java
index 4445126..57cf60a 100644
--- a/avatica/src/test/java/org/apache/calcite/avatica/test/JsonHandlerTest.java
+++ b/avatica/src/test/java/org/apache/calcite/avatica/test/JsonHandlerTest.java
@@ -20,6 +20,7 @@ import org.apache.calcite.avatica.AvaticaParameter;
 import org.apache.calcite.avatica.ColumnMetaData;
 import org.apache.calcite.avatica.Meta;
 import org.apache.calcite.avatica.Meta.CursorFactory;
+import org.apache.calcite.avatica.metrics.noop.NoopMetricsSystem;
 import org.apache.calcite.avatica.remote.JsonHandler;
 import org.apache.calcite.avatica.remote.JsonService;
 import org.apache.calcite.avatica.remote.LocalJsonService;
@@ -167,7 +168,7 @@ public class JsonHandlerTest {
     final List<TypedValue> expectedParameterValues = new ArrayList<>();
     final Service service = new ParameterValuesCheckingService(expectedParameterValues);
     final JsonService jsonService = new LocalJsonService(service);
-    final JsonHandler jsonHandler = new JsonHandler(jsonService);
+    final JsonHandler jsonHandler = new JsonHandler(jsonService, NoopMetricsSystem.getInstance());
 
     final List<TypedValue> parameterValues = Arrays.asList(
         TypedValue.create("NUMBER", new BigDecimal("123")),

http://git-wip-us.apache.org/repos/asf/calcite/blob/7e5710fc/dropwizard-metrics2/pom.xml
----------------------------------------------------------------------
diff --git a/dropwizard-metrics2/pom.xml b/dropwizard-metrics2/pom.xml
new file mode 100644
index 0000000..bcb7033
--- /dev/null
+++ b/dropwizard-metrics2/pom.xml
@@ -0,0 +1,145 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.calcite</groupId>
+    <artifactId>calcite</artifactId>
+    <version>1.7.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>calcite-dropwizard-hadoop-metrics2</artifactId>
+  <packaging>jar</packaging>
+  <name>Calcite Dropwizard Reporter for Hadoop Metrics2</name>
+  <description>A Dropwizard Metrics reporter which also acts as a Hadoop Metrics2 MetricsSource.</description>
+
+  <properties>
+    <top.dir>${project.basedir}/..</top.dir>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>io.dropwizard.metrics</groupId>
+      <artifactId>metrics-core</artifactId>
+      <version>${dropwizard-metrics3.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.hadoop</groupId>
+      <artifactId>hadoop-common</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-all</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <pluginManagement>
+      <plugins>
+        <plugin>
+          <groupId>org.eclipse.m2e</groupId>
+          <artifactId>lifecycle-mapping</artifactId>
+          <version>1.0.0</version>
+          <configuration>
+            <lifecycleMappingMetadata>
+              <pluginExecutions>
+                <pluginExecution>
+                  <pluginExecutionFilter>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-checkstyle-plugin</artifactId>
+                    <versionRange>[2.12.1,)</versionRange>
+                    <goals>
+                      <goal>check</goal>
+                    </goals>
+                  </pluginExecutionFilter>
+                  <action>
+                    <ignore />
+                  </action>
+                </pluginExecution>
+              </pluginExecutions>
+            </lifecycleMappingMetadata>
+          </configuration>
+        </plugin>
+      </plugins>
+    </pluginManagement>
+    <plugins>
+      <plugin>
+        <artifactId>maven-dependency-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>analyze</id>
+            <goals>
+              <goal>analyze-only</goal>
+            </goals>
+            <configuration>
+              <failOnWarning>true</failOnWarning>
+              <!-- ignore "unused but declared" warnings -->
+              <ignoredUnusedDeclaredDependencies>
+                <ignoredUnusedDeclaredDependency>org.slf4j:slf4j-log4j12</ignoredUnusedDeclaredDependency>
+              </ignoredUnusedDeclaredDependencies>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <!-- Parent module has the same plugin and does the work of
+           generating -sources.jar for each project. But without the
+           plugin declared here, IDEs don't know the sources are
+           available. -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <phase>verify</phase>
+            <goals>
+              <goal>jar-no-fork</goal>
+              <goal>test-jar-no-fork</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <artifactId>maven-remote-resources-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>non-root-resources</id>
+            <goals>
+              <goal>process</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/calcite/blob/7e5710fc/dropwizard-metrics2/src/main/java/org/apache/calcite/dropwizard/metrics/hadoop/HadoopMetrics2Reporter.java
----------------------------------------------------------------------
diff --git a/dropwizard-metrics2/src/main/java/org/apache/calcite/dropwizard/metrics/hadoop/HadoopMetrics2Reporter.java b/dropwizard-metrics2/src/main/java/org/apache/calcite/dropwizard/metrics/hadoop/HadoopMetrics2Reporter.java
new file mode 100644
index 0000000..89a49eb
--- /dev/null
+++ b/dropwizard-metrics2/src/main/java/org/apache/calcite/dropwizard/metrics/hadoop/HadoopMetrics2Reporter.java
@@ -0,0 +1,451 @@
+/*
+ * 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.calcite.dropwizard.metrics.hadoop;
+
+import org.apache.hadoop.metrics2.MetricsCollector;
+import org.apache.hadoop.metrics2.MetricsInfo;
+import org.apache.hadoop.metrics2.MetricsRecordBuilder;
+import org.apache.hadoop.metrics2.MetricsSource;
+import org.apache.hadoop.metrics2.MetricsSystem;
+import org.apache.hadoop.metrics2.lib.Interns;
+import org.apache.hadoop.metrics2.lib.MetricsRegistry;
+
+import com.codahale.metrics.Counter;
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.Histogram;
+import com.codahale.metrics.Meter;
+import com.codahale.metrics.MetricFilter;
+import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.ScheduledReporter;
+import com.codahale.metrics.Snapshot;
+import com.codahale.metrics.Timer;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Iterator;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.SortedMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Dropwizard-Metrics {@link com.codahale.metrics.Reporter} which also acts as a Hadoop Metrics2
+ * {@link MetricsSource}. Configure it like other Reporters.
+ *
+ * <pre>
+ * final HadoopMetrics2Reporter metrics2Reporter = HadoopMetrics2Reporter.forRegistry(metrics)
+ *     .build(DefaultMetricsSystem.initialize("Phoenix"), // The application-level name
+ *            "QueryServer", // Component name
+ *            "Phoenix Query Server", // Component description
+ *            "General"); // Name for each metric record
+ * metrics2Reporter.start(30, TimeUnit.SECONDS);
+ * </pre>
+ */
+public class HadoopMetrics2Reporter extends ScheduledReporter implements MetricsSource {
+  private static final Logger LOG = LoggerFactory.getLogger(HadoopMetrics2Reporter.class);
+  private static final String EMPTY_STRING = "";
+
+  public static final MetricsInfo RATE_UNIT_LABEL =
+      Interns.info("rate_unit", "The unit of measure for rate metrics");
+  public static final MetricsInfo DURATION_UNIT_LABEL =
+      Interns.info("duration_unit", "The unit of measure of duration metrics");
+
+  /**
+   * Returns a new {@link Builder} for {@link HadoopMetrics2Reporter}.
+   *
+   * @param registry the registry to report
+   * @return a {@link Builder} instance for a {@link HadoopMetrics2Reporter}
+   */
+  public static Builder forRegistry(MetricRegistry registry) {
+    return new Builder(registry);
+  }
+
+  /**
+   * A builder to create {@link HadoopMetrics2Reporter} instances.
+   */
+  public static class Builder {
+    private final MetricRegistry registry;
+    private MetricFilter filter;
+    private TimeUnit rateUnit;
+    private TimeUnit durationUnit;
+    private String recordContext;
+
+    private Builder(MetricRegistry registry) {
+      this.registry = registry;
+      this.filter = MetricFilter.ALL;
+      this.rateUnit = TimeUnit.SECONDS;
+      this.durationUnit = TimeUnit.MILLISECONDS;
+    }
+
+    /**
+     * Convert rates to the given time unit. Defaults to {@link TimeUnit#SECONDS}.
+     *
+     * @param rateUnit a unit of time
+     * @return {@code this}
+     */
+    public Builder convertRatesTo(TimeUnit rateUnit) {
+      this.rateUnit = Objects.requireNonNull(rateUnit);
+      return this;
+    }
+
+    /**
+     * Convert durations to the given time unit. Defaults to {@link TimeUnit#MILLISECONDS}.
+     *
+     * @param durationUnit a unit of time
+     * @return {@code this}
+     */
+    public Builder convertDurationsTo(TimeUnit durationUnit) {
+      this.durationUnit = Objects.requireNonNull(durationUnit);
+      return this;
+    }
+
+    /**
+     * Only report metrics which match the given filter. Defaults to {@link MetricFilter#ALL}.
+     *
+     * @param filter a {@link MetricFilter}
+     * @return {@code this}
+     */
+    public Builder filter(MetricFilter filter) {
+      this.filter = Objects.requireNonNull(filter);
+      return this;
+    }
+
+    /**
+     * A "context" name that will be added as a tag on each emitted metric record. Defaults to
+     * no "context" attribute on each record.
+     *
+     * @param recordContext The "context" tag
+     * @return {@code this}
+     */
+    public Builder recordContext(String recordContext) {
+      this.recordContext = Objects.requireNonNull(recordContext);
+      return this;
+    }
+
+    /**
+     * Builds a {@link HadoopMetrics2Reporter} with the given properties, making metrics available
+     * to the Hadoop Metrics2 framework (any configured {@link MetricsSource}s.
+     *
+     * @param metrics2System The Hadoop Metrics2 system instance.
+     * @param jmxContext The JMX "path", e.g. {@code "MyServer,sub=Requests"}.
+     * @param description A description these metrics.
+     * @param recordName A suffix included on each record to identify it.
+     *
+     * @return a {@link HadoopMetrics2Reporter}
+     */
+    public HadoopMetrics2Reporter build(MetricsSystem metrics2System, String jmxContext,
+        String description, String recordName) {
+      return new HadoopMetrics2Reporter(registry,
+          rateUnit,
+          durationUnit,
+          filter,
+          metrics2System,
+          Objects.requireNonNull(jmxContext),
+          description,
+          recordName,
+          recordContext);
+    }
+  }
+
+  private final MetricsRegistry metrics2Registry;
+  private final MetricsSystem metrics2System;
+  private final String recordName;
+  private final String context;
+
+  @SuppressWarnings("rawtypes")
+  private final ConcurrentLinkedQueue<Entry<String, Gauge>> dropwizardGauges;
+  private final ConcurrentLinkedQueue<Entry<String, Counter>> dropwizardCounters;
+  private final ConcurrentLinkedQueue<Entry<String, Histogram>> dropwizardHistograms;
+  private final ConcurrentLinkedQueue<Entry<String, Meter>> dropwizardMeters;
+  private final ConcurrentLinkedQueue<Entry<String, Timer>> dropwizardTimers;
+
+  private HadoopMetrics2Reporter(MetricRegistry registry, TimeUnit rateUnit, TimeUnit durationUnit,
+      MetricFilter filter, MetricsSystem metrics2System, String jmxContext, String description,
+      String recordName, String context) {
+    super(registry, "hadoop-metrics2-reporter", filter, rateUnit, durationUnit);
+    this.metrics2Registry = new MetricsRegistry(Interns.info(jmxContext, description));
+    this.metrics2System = metrics2System;
+    this.recordName = recordName;
+    this.context = context;
+
+    this.dropwizardGauges = new ConcurrentLinkedQueue<>();
+    this.dropwizardCounters = new ConcurrentLinkedQueue<>();
+    this.dropwizardHistograms = new ConcurrentLinkedQueue<>();
+    this.dropwizardMeters = new ConcurrentLinkedQueue<>();
+    this.dropwizardTimers = new ConcurrentLinkedQueue<>();
+
+    // Register this source with the Metrics2 system.
+    // Make sure this is the last thing done as getMetrics() can be called at any time after.
+    this.metrics2System.register(Objects.requireNonNull(jmxContext),
+        Objects.requireNonNull(description), this);
+  }
+
+  @Override public void getMetrics(MetricsCollector collector, boolean all) {
+    MetricsRecordBuilder builder = collector.addRecord(recordName);
+    if (null != context) {
+      builder.setContext(context);
+    }
+
+    snapshotAllMetrics(builder);
+
+    metrics2Registry.snapshot(builder, all);
+  }
+
+  /**
+   * Consumes the current metrics collected by dropwizard and adds them to the {@code builder}.
+   *
+   * @param builder A record builder
+   */
+  void snapshotAllMetrics(MetricsRecordBuilder builder) {
+    // Pass through the gauges
+    @SuppressWarnings("rawtypes")
+    Iterator<Entry<String, Gauge>> gaugeIterator = dropwizardGauges.iterator();
+    while (gaugeIterator.hasNext()) {
+      @SuppressWarnings("rawtypes")
+      Entry<String, Gauge> gauge = gaugeIterator.next();
+      final MetricsInfo info = Interns.info(gauge.getKey(), EMPTY_STRING);
+      final Object o = gauge.getValue().getValue();
+
+      // Figure out which gauge types metrics2 supports and call the right method
+      if (o instanceof Integer) {
+        builder.addGauge(info, (int) o);
+      } else if (o instanceof Long) {
+        builder.addGauge(info, (long) o);
+      } else if (o instanceof Float) {
+        builder.addGauge(info, (float) o);
+      } else if (o instanceof Double) {
+        builder.addGauge(info, (double) o);
+      } else {
+        LOG.info("Ignoring Gauge ({}) with unhandled type: {}", gauge.getKey(), o.getClass());
+      }
+
+      gaugeIterator.remove();
+    }
+
+    // Pass through the counters
+    Iterator<Entry<String, Counter>> counterIterator = dropwizardCounters.iterator();
+    while (counterIterator.hasNext()) {
+      Entry<String, Counter> counter = counterIterator.next();
+      MetricsInfo info = Interns.info(counter.getKey(), EMPTY_STRING);
+      LOG.info("Adding counter {} {}", info, counter.getValue().getCount());
+      builder.addCounter(info, counter.getValue().getCount());
+      counterIterator.remove();
+    }
+
+    // Pass through the histograms
+    Iterator<Entry<String, Histogram>> histogramIterator = dropwizardHistograms.iterator();
+    while (histogramIterator.hasNext()) {
+      final Entry<String, Histogram> entry = histogramIterator.next();
+      final String name = entry.getKey();
+      final Histogram histogram = entry.getValue();
+
+      addSnapshot(builder, name, EMPTY_STRING, histogram.getSnapshot(), histogram.getCount());
+
+      histogramIterator.remove();
+    }
+
+    // Pass through the meter values
+    Iterator<Entry<String, Meter>> meterIterator = dropwizardMeters.iterator();
+    while (meterIterator.hasNext()) {
+      final Entry<String, Meter> meterEntry = meterIterator.next();
+      final String name = meterEntry.getKey();
+      final Meter meter = meterEntry.getValue();
+
+      addMeter(builder, name, EMPTY_STRING, meter.getCount(), meter.getMeanRate(),
+          meter.getOneMinuteRate(), meter.getFiveMinuteRate(), meter.getFifteenMinuteRate());
+
+      meterIterator.remove();
+    }
+
+    // Pass through the timers (meter + histogram)
+    Iterator<Entry<String, Timer>> timerIterator = dropwizardTimers.iterator();
+    while (timerIterator.hasNext()) {
+      final Entry<String, Timer> timerEntry = timerIterator.next();
+      final String name = timerEntry.getKey();
+      final Timer timer = timerEntry.getValue();
+      final Snapshot snapshot = timer.getSnapshot();
+
+      // Add the meter info (mean rate and rate over time windows)
+      addMeter(builder, name, EMPTY_STRING, timer.getCount(), timer.getMeanRate(),
+          timer.getOneMinuteRate(), timer.getFiveMinuteRate(), timer.getFifteenMinuteRate());
+
+      // Count was already added via the meter
+      addSnapshot(builder, name, EMPTY_STRING, snapshot);
+
+      timerIterator.remove();
+    }
+
+    // Add in metadata about what the units the reported metrics are displayed using.
+    builder.tag(RATE_UNIT_LABEL, getRateUnit());
+    builder.tag(DURATION_UNIT_LABEL, getDurationUnit());
+  }
+
+  /**
+   * Add Dropwizard-Metrics rate information to a Hadoop-Metrics2 record builder, converting the
+   * rates to the appropriate unit.
+   *
+   * @param builder A Hadoop-Metrics2 record builder.
+   * @param name A base name for this record.
+   * @param desc A description for the record.
+   * @param count The number of measured events.
+   * @param meanRate The average measured rate.
+   * @param oneMinuteRate The measured rate over the past minute.
+   * @param fiveMinuteRate The measured rate over the past five minutes
+   * @param fifteenMinuteRate The measured rate over the past fifteen minutes.
+   */
+  private void addMeter(MetricsRecordBuilder builder, String name, String desc, long count,
+      double meanRate, double oneMinuteRate, double fiveMinuteRate, double fifteenMinuteRate) {
+    builder.addGauge(Interns.info(name + "_count", EMPTY_STRING), count);
+    builder.addGauge(Interns.info(name + "_mean_rate", EMPTY_STRING), convertRate(meanRate));
+    builder.addGauge(Interns.info(name + "_1min_rate", EMPTY_STRING), convertRate(oneMinuteRate));
+    builder.addGauge(Interns.info(name + "_5min_rate", EMPTY_STRING), convertRate(fiveMinuteRate));
+    builder.addGauge(Interns.info(name + "_15min_rate", EMPTY_STRING),
+        convertRate(fifteenMinuteRate));
+  }
+
+  /**
+   * Add Dropwizard-Metrics value-distribution data to a Hadoop-Metrics2 record building, converting
+   * the durations to the appropriate unit.
+   *
+   * @param builder A Hadoop-Metrics2 record builder.
+   * @param name A base name for this record.
+   * @param desc A description for this record.
+   * @param snapshot The distribution of measured values.
+   * @param count The number of values which were measured.
+   */
+  private void addSnapshot(MetricsRecordBuilder builder, String name, String desc,
+      Snapshot snapshot, long count) {
+    builder.addGauge(Interns.info(name + "_count", desc), count);
+    addSnapshot(builder, name, desc, snapshot);
+  }
+
+  /**
+   * Add Dropwizard-Metrics value-distribution data to a Hadoop-Metrics2 record building, converting
+   * the durations to the appropriate unit.
+   *
+   * @param builder A Hadoop-Metrics2 record builder.
+   * @param name A base name for this record.
+   * @param desc A description for this record.
+   * @param snapshot The distribution of measured values.
+   */
+  private void addSnapshot(MetricsRecordBuilder builder, String name, String desc,
+      Snapshot snapshot) {
+    builder.addGauge(Interns.info(name + "_mean", desc), convertDuration(snapshot.getMean()));
+    builder.addGauge(Interns.info(name + "_min", desc), convertDuration(snapshot.getMin()));
+    builder.addGauge(Interns.info(name + "_max", desc), convertDuration(snapshot.getMax()));
+    builder.addGauge(Interns.info(name + "_median", desc), convertDuration(snapshot.getMedian()));
+    builder.addGauge(Interns.info(name + "_stddev", desc), convertDuration(snapshot.getStdDev()));
+
+    builder.addGauge(Interns.info(name + "_75thpercentile", desc),
+        convertDuration(snapshot.get75thPercentile()));
+    builder.addGauge(Interns.info(name + "_95thpercentile", desc),
+        convertDuration(snapshot.get95thPercentile()));
+    builder.addGauge(Interns.info(name + "_98thpercentile", desc),
+        convertDuration(snapshot.get98thPercentile()));
+    builder.addGauge(Interns.info(name + "_99thpercentile", desc),
+        convertDuration(snapshot.get99thPercentile()));
+    builder.addGauge(Interns.info(name + "_999thpercentile", desc),
+        convertDuration(snapshot.get999thPercentile()));
+  }
+
+  @SuppressWarnings("rawtypes")
+  @Override public void report(SortedMap<String, Gauge> gauges, SortedMap<String, Counter> counters,
+      SortedMap<String, Histogram> histograms, SortedMap<String, Meter> meters,
+      SortedMap<String, Timer> timers) {
+    for (Entry<String, Gauge> gauge : gauges.entrySet()) {
+      dropwizardGauges.add(gauge);
+    }
+
+    for (Entry<String, Counter> counter : counters.entrySet()) {
+      dropwizardCounters.add(counter);
+    }
+
+    for (Entry<String, Histogram> histogram : histograms.entrySet()) {
+      dropwizardHistograms.add(histogram);
+    }
+
+    for (Entry<String, Meter> meter : meters.entrySet()) {
+      dropwizardMeters.add(meter);
+    }
+
+    for (Entry<String, Timer> timer : timers.entrySet()) {
+      dropwizardTimers.add(timer);
+    }
+  }
+
+  @Override protected String getRateUnit() {
+    // Make it "events per rate_unit" to be accurate.
+    return "events/" + super.getRateUnit();
+  }
+
+  @Override protected String getDurationUnit() {
+    // Make it visible to the tests
+    return super.getDurationUnit();
+  }
+
+  @Override protected double convertDuration(double duration) {
+    // Make it visible to the tests
+    return super.convertDuration(duration);
+  }
+
+  @Override protected double convertRate(double rate) {
+    // Make it visible to the tests
+    return super.convertRate(rate);
+  }
+
+  // Getters visible for testing
+
+  MetricsRegistry getMetrics2Registry() {
+    return metrics2Registry;
+  }
+
+  MetricsSystem getMetrics2System() {
+    return metrics2System;
+  }
+
+  String getRecordName() {
+    return recordName;
+  }
+
+  String getContext() {
+    return context;
+  }
+
+  @SuppressWarnings("rawtypes") ConcurrentLinkedQueue<Entry<String, Gauge>> getDropwizardGauges() {
+    return dropwizardGauges;
+  }
+
+  ConcurrentLinkedQueue<Entry<String, Counter>> getDropwizardCounters() {
+    return dropwizardCounters;
+  }
+
+  ConcurrentLinkedQueue<Entry<String, Histogram>> getDropwizardHistograms() {
+    return dropwizardHistograms;
+  }
+
+  ConcurrentLinkedQueue<Entry<String, Meter>> getDropwizardMeters() {
+    return dropwizardMeters;
+  }
+
+  ConcurrentLinkedQueue<Entry<String, Timer>> getDropwizardTimers() {
+    return dropwizardTimers;
+  }
+}
+
+// End HadoopMetrics2Reporter.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/7e5710fc/dropwizard-metrics2/src/main/java/org/apache/calcite/dropwizard/metrics/hadoop/PackageMarker.java
----------------------------------------------------------------------
diff --git a/dropwizard-metrics2/src/main/java/org/apache/calcite/dropwizard/metrics/hadoop/PackageMarker.java b/dropwizard-metrics2/src/main/java/org/apache/calcite/dropwizard/metrics/hadoop/PackageMarker.java
new file mode 100644
index 0000000..40a9b4f
--- /dev/null
+++ b/dropwizard-metrics2/src/main/java/org/apache/calcite/dropwizard/metrics/hadoop/PackageMarker.java
@@ -0,0 +1,37 @@
+/*
+ * 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.calcite.dropwizard.metrics.hadoop;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This is a dummy annotation that forces javac to produce output for
+ * otherwise empty package-info.java.
+ *
+ * <p>The result is maven-compiler-plugin can properly identify the scope of
+ * changed files
+ *
+ * <p>See more details in
+ * <a href="https://jira.codehaus.org/browse/MCOMPILER-205">
+ *   maven-compiler-plugin: incremental compilation broken</a>
+ */
+@Retention(RetentionPolicy.SOURCE)
+public @interface PackageMarker {
+}
+
+// End PackageMarker.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/7e5710fc/dropwizard-metrics2/src/main/java/org/apache/calcite/dropwizard/metrics/hadoop/package-info.java
----------------------------------------------------------------------
diff --git a/dropwizard-metrics2/src/main/java/org/apache/calcite/dropwizard/metrics/hadoop/package-info.java b/dropwizard-metrics2/src/main/java/org/apache/calcite/dropwizard/metrics/hadoop/package-info.java
new file mode 100644
index 0000000..a842ff9
--- /dev/null
+++ b/dropwizard-metrics2/src/main/java/org/apache/calcite/dropwizard/metrics/hadoop/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+/**
+ * Hadoop Metrics2 MetricsSource for Dropwizard-Metrics.
+ */
+@PackageMarker
+package org.apache.calcite.dropwizard.metrics.hadoop;
+
+// End package-info.java