You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@kylin.apache.org by GitBox <gi...@apache.org> on 2018/09/30 05:34:17 UTC

[GitHub] shaofengshi closed pull request #276: KYLIN-3605 upgrade hadoop-common and zookeeper version to fix securit…

shaofengshi closed pull request #276: KYLIN-3605 upgrade hadoop-common and zookeeper version to fix securit…
URL: https://github.com/apache/kylin/pull/276
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/LICENSE b/LICENSE
index a237c61e69..26e015cf79 100644
--- a/LICENSE
+++ b/LICENSE
@@ -265,5 +265,8 @@ RocksDB is dual-licensed under both the GPLv2 and Apache 2.0 License.You may
 select, at your option, one of the above-listed licenses.
 https://github.com/facebook/rocksdb/blob/master/LICENSE.Apache
 
-
-
+==============================================================================
+For HadoopMetrics2Reporter:
+==============================================================================
+HadoopMetrics2Reporter is licensed under Apache 2.0 License.
+https://github.com/joshelser/dropwizard-hadoop-metrics2
diff --git a/core-common/pom.xml b/core-common/pom.xml
index 3b5f7fc16b..594e39b39b 100644
--- a/core-common/pom.xml
+++ b/core-common/pom.xml
@@ -86,11 +86,6 @@
             <version>${dropwizard.version}</version>
         </dependency>
 
-        <dependency>
-            <groupId>com.github.joshelser</groupId>
-            <artifactId>dropwizard-metrics-hadoop-metrics2-reporter</artifactId>
-            <version>0.1.2</version>
-        </dependency>
         <dependency>
             <groupId>commons-dbcp</groupId>
             <artifactId>commons-dbcp</artifactId>
@@ -101,5 +96,12 @@
             <artifactId>mysql-connector-java</artifactId>
             <scope>provided</scope>
         </dependency>
+
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <version>${mockito.version}</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>
diff --git a/core-common/src/main/java/org/apache/kylin/common/metrics/metrics2/HadoopMetrics2Reporter.java b/core-common/src/main/java/org/apache/kylin/common/metrics/metrics2/HadoopMetrics2Reporter.java
new file mode 100644
index 0000000000..c0e48a6b7e
--- /dev/null
+++ b/core-common/src/main/java/org/apache/kylin/common/metrics/metrics2/HadoopMetrics2Reporter.java
@@ -0,0 +1,495 @@
+/*
+ * 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.kylin.common.metrics.metrics2;
+
+import java.text.SimpleDateFormat;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.concurrent.TimeUnit;
+
+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 org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+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;
+
+/**
+ * Modified from https://github.com/joshelser/dropwizard-hadoop-metrics2, Copyright by Josh Elser
+ *
+ * A {@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 = "";
+    @SuppressWarnings("rawtypes")
+    private static final SortedMap<String, Gauge> EMPTY_GAUGE_MAP = Collections
+            .unmodifiableSortedMap(new TreeMap<String, Gauge>());
+    private static final SortedMap<String, Meter> EMPTY_METER_MAP = Collections
+            .unmodifiableSortedMap(new TreeMap<String, Meter>());
+    private static final SortedMap<String, Timer> EMPTY_TIMER_MAP = Collections
+            .unmodifiableSortedMap(new TreeMap<String, Timer>());
+    private static final SortedMap<String, Counter> EMPTY_COUNTER_MAP = Collections
+            .unmodifiableSortedMap(new TreeMap<String, Counter>());
+    private static final SortedMap<String, Histogram> EMPTY_HISTOGRAM_MAP = Collections
+            .unmodifiableSortedMap(new TreeMap<String, Histogram>());
+
+    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;
+
+    // TODO Adding to the queues and removing from them are now guarded by explicit synchronization
+    // so these don't need to be safe for concurrency anymore.
+    @SuppressWarnings("rawtypes")
+    private SortedMap<String, Gauge> dropwizardGauges;
+    private SortedMap<String, Counter> dropwizardCounters;
+    private SortedMap<String, Histogram> dropwizardHistograms;
+    private SortedMap<String, Meter> dropwizardMeters;
+    private SortedMap<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;
+
+        // These could really be Collection.emptyMap(), but this makes testing a bit easier.
+        this.dropwizardGauges = EMPTY_GAUGE_MAP;
+        this.dropwizardCounters = EMPTY_COUNTER_MAP;
+        this.dropwizardHistograms = EMPTY_HISTOGRAM_MAP;
+        this.dropwizardMeters = EMPTY_METER_MAP;
+        this.dropwizardTimers = EMPTY_TIMER_MAP;
+
+        // 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);
+        }
+
+        // Synchronizing here ensures that the dropwizard metrics collection side is excluded from executing
+        // at the same time we are pulling elements from the queues.
+        synchronized (this) {
+            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) {
+        try {
+            // Pass through the gauges
+            @SuppressWarnings("rawtypes")
+            Iterator<Entry<String, Gauge>> gaugeIterator = dropwizardGauges.entrySet().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.trace("Ignoring Gauge ({}) with unhandled type: {}", gauge.getKey(), o.getClass());
+                }
+            }
+        } finally {
+            dropwizardGauges = EMPTY_GAUGE_MAP;
+        }
+
+        try {
+            // Pass through the counters
+            Iterator<Entry<String, Counter>> counterIterator = dropwizardCounters.entrySet().iterator();
+            while (counterIterator.hasNext()) {
+                Entry<String, Counter> counter = counterIterator.next();
+                MetricsInfo info = Interns.info(counter.getKey(), EMPTY_STRING);
+                builder.addCounter(info, counter.getValue().getCount());
+            }
+        } finally {
+            dropwizardCounters = EMPTY_COUNTER_MAP;
+        }
+
+        try {
+            // Pass through the histograms
+            Iterator<Entry<String, Histogram>> histogramIterator = dropwizardHistograms.entrySet().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());
+            }
+        } finally {
+            dropwizardHistograms = EMPTY_HISTOGRAM_MAP;
+        }
+
+        try {
+            // Pass through the meter values
+            Iterator<Entry<String, Meter>> meterIterator = dropwizardMeters.entrySet().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());
+            }
+        } finally {
+            dropwizardMeters = EMPTY_METER_MAP;
+        }
+
+        try {
+            // Pass through the timers (meter + histogram)
+            Iterator<Entry<String, Timer>> timerIterator = dropwizardTimers.entrySet().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);
+            }
+        } finally {
+            dropwizardTimers = EMPTY_TIMER_MAP;
+        }
+
+        // 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) {
+        // ScheduledReporter is synchronizing on `this`, so we don't have to worry about concurrent
+        // invocations of reporter causing trouble. All of the maps provided to us are umodifiable
+        // copies of the actual metric objects. We can just hold onto them.
+        dropwizardGauges = gauges;
+        dropwizardCounters = counters;
+        dropwizardHistograms = histograms;
+        dropwizardMeters = meters;
+        dropwizardTimers = timers;
+    }
+
+    @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")
+    SortedMap<String, Gauge> getDropwizardGauges() {
+        return dropwizardGauges;
+    }
+
+    void setDropwizardGauges(@SuppressWarnings("rawtypes") SortedMap<String, Gauge> gauges) {
+        this.dropwizardGauges = Objects.requireNonNull(gauges);
+    }
+
+    SortedMap<String, Counter> getDropwizardCounters() {
+        return dropwizardCounters;
+    }
+
+    void setDropwizardCounters(SortedMap<String, Counter> counters) {
+        this.dropwizardCounters = counters;
+    }
+
+    SortedMap<String, Histogram> getDropwizardHistograms() {
+        return dropwizardHistograms;
+    }
+
+    void setDropwizardHistograms(SortedMap<String, Histogram> histograms) {
+        this.dropwizardHistograms = histograms;
+    }
+
+    SortedMap<String, Meter> getDropwizardMeters() {
+        return dropwizardMeters;
+    }
+
+    void setDropwizardMeters(SortedMap<String, Meter> meters) {
+        this.dropwizardMeters = meters;
+    }
+
+    SortedMap<String, Timer> getDropwizardTimers() {
+        return dropwizardTimers;
+    }
+
+    void setDropwizardTimers(SortedMap<String, Timer> timers) {
+        this.dropwizardTimers = timers;
+    }
+
+    protected void printQueueDebugMessage() {
+        StringBuilder sb = new StringBuilder(64);
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss", Locale.ROOT);
+        sb.append(sdf.format(new Date())).append(" ================================\n");
+        sb.append("\n  Dropwizard gauges size: ").append(getDropwizardGauges().size());
+        sb.append("\n  Dropwizard counters size: ").append(getDropwizardCounters().size());
+        sb.append("\n  Dropwizard histograms size: ").append(getDropwizardHistograms().size());
+        sb.append("\n  Dropwizard meters size: ").append(getDropwizardMeters().size());
+        sb.append("\n  Dropwizard timers size: ").append(getDropwizardTimers().size()).append("\n");
+        System.out.println(sb.toString());
+    }
+}
diff --git a/core-common/src/main/java/org/apache/kylin/common/metrics/metrics2/Metrics2Reporter.java b/core-common/src/main/java/org/apache/kylin/common/metrics/metrics2/Metrics2Reporter.java
index e1da0562ff..83dab81752 100644
--- a/core-common/src/main/java/org/apache/kylin/common/metrics/metrics2/Metrics2Reporter.java
+++ b/core-common/src/main/java/org/apache/kylin/common/metrics/metrics2/Metrics2Reporter.java
@@ -24,7 +24,6 @@
 import org.apache.kylin.common.KylinConfig;
 
 import com.codahale.metrics.MetricRegistry;
-import com.github.joshelser.dropwizard.metrics.hadoop.HadoopMetrics2Reporter;
 
 /**
  * A wrapper around Codahale HadoopMetrics2Reporter to make it a pluggable/configurable Kylin Metrics reporter.
diff --git a/core-common/src/test/java/org/apache/kylin/common/metrics/metrics2/HadoopMetrics2ReporterTest.java b/core-common/src/test/java/org/apache/kylin/common/metrics/metrics2/HadoopMetrics2ReporterTest.java
new file mode 100644
index 0000000000..75752bdd48
--- /dev/null
+++ b/core-common/src/test/java/org/apache/kylin/common/metrics/metrics2/HadoopMetrics2ReporterTest.java
@@ -0,0 +1,415 @@
+/*
+ * 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.kylin.common.metrics.metrics2;
+
+import com.codahale.metrics.Counter;
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.Histogram;
+import com.codahale.metrics.Meter;
+import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.Snapshot;
+import com.codahale.metrics.Timer;
+
+import org.apache.hadoop.metrics2.MetricsCollector;
+import org.apache.hadoop.metrics2.MetricsRecordBuilder;
+import org.apache.hadoop.metrics2.MetricsSystem;
+import org.apache.hadoop.metrics2.lib.Interns;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.util.Collections;
+import java.util.TreeMap;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Modified from https://github.com/joshelser/dropwizard-hadoop-metrics2, Copyright by Josh Elser
+ * Tests for {@link HadoopMetrics2Reporter}.
+ */
+public class HadoopMetrics2ReporterTest {
+
+    private MetricRegistry mockRegistry;
+    private MetricsSystem mockMetricsSystem;
+    private String recordName = "myserver";
+    private HadoopMetrics2Reporter metrics2Reporter;
+
+    @Before
+    public void setup() {
+        mockRegistry = mock(MetricRegistry.class);
+        mockMetricsSystem = mock(MetricsSystem.class);
+
+        recordName = "myserver";
+        metrics2Reporter = HadoopMetrics2Reporter.forRegistry(mockRegistry).convertDurationsTo(TimeUnit.MILLISECONDS)
+                .convertRatesTo(TimeUnit.SECONDS).build(mockMetricsSystem, "MyServer", "My Cool Server", recordName);
+    }
+
+    private void verifyRecordBuilderUnits(MetricsRecordBuilder recordBuilder) {
+        verify(recordBuilder).tag(HadoopMetrics2Reporter.RATE_UNIT_LABEL, metrics2Reporter.getRateUnit());
+        verify(recordBuilder).tag(HadoopMetrics2Reporter.DURATION_UNIT_LABEL, metrics2Reporter.getDurationUnit());
+    }
+
+    @Test
+    public void testBuilderDefaults() {
+        HadoopMetrics2Reporter.Builder builder = HadoopMetrics2Reporter.forRegistry(mockRegistry);
+
+        final String jmxContext = "MyJmxContext;sub=Foo";
+        final String desc = "Description";
+        final String recordName = "Metrics";
+
+        HadoopMetrics2Reporter reporter = builder.build(mockMetricsSystem, jmxContext, desc, recordName);
+
+        assertEquals(mockMetricsSystem, reporter.getMetrics2System());
+        // The Context "tag", not the jmx context
+        assertEquals(null, reporter.getContext());
+        assertEquals(recordName, reporter.getRecordName());
+    }
+
+    @Test
+    public void testGaugeReporting() {
+        final AtomicLong gaugeValue = new AtomicLong(0L);
+        @SuppressWarnings("rawtypes")
+        final Gauge gauge = new Gauge<Long>() {
+            @Override
+            public Long getValue() {
+                return gaugeValue.get();
+            }
+        };
+
+        @SuppressWarnings("rawtypes")
+        TreeMap<String, Gauge> gauges = new TreeMap<>();
+        gauges.put("my_gauge", gauge);
+        // Add the metrics objects to the internal "queues" by hand
+        metrics2Reporter.setDropwizardGauges(gauges);
+
+        // Set some values
+        gaugeValue.set(5L);
+
+        MetricsCollector collector = mock(MetricsCollector.class);
+        MetricsRecordBuilder recordBuilder = mock(MetricsRecordBuilder.class);
+
+        Mockito.when(collector.addRecord(recordName)).thenReturn(recordBuilder);
+
+        // Make sure a value of 5 gets reported
+        metrics2Reporter.getMetrics(collector, true);
+
+        verify(recordBuilder).addGauge(Interns.info("my_gauge", ""), gaugeValue.get());
+        verifyRecordBuilderUnits(recordBuilder);
+
+        // Should not be the same instance we gave before. Our map should have gotten swapped out.
+        assertTrue("Should not be the same map instance after collection",
+                gauges != metrics2Reporter.getDropwizardGauges());
+    }
+
+    @Test
+    public void testCounterReporting() {
+        final Counter counter = new Counter();
+
+        TreeMap<String, Counter> counters = new TreeMap<>();
+        counters.put("my_counter", counter);
+        // Add the metrics objects to the internal "queues" by hand
+        metrics2Reporter.setDropwizardCounters(counters);
+
+        // Set some values
+        counter.inc(5L);
+
+        MetricsCollector collector = mock(MetricsCollector.class);
+        MetricsRecordBuilder recordBuilder = mock(MetricsRecordBuilder.class);
+
+        Mockito.when(collector.addRecord(recordName)).thenReturn(recordBuilder);
+
+        metrics2Reporter.getMetrics(collector, true);
+
+        verify(recordBuilder).addCounter(Interns.info("my_counter", ""), 5L);
+        verifyRecordBuilderUnits(recordBuilder);
+
+        // Should not be the same instance we gave before. Our map should have gotten swapped out.
+        assertTrue("Should not be the same map instance after collection",
+                counters != metrics2Reporter.getDropwizardCounters());
+    }
+
+    @Test
+    public void testHistogramReporting() {
+        final String metricName = "my_histogram";
+        final Histogram histogram = mock(Histogram.class);
+        final Snapshot snapshot = mock(Snapshot.class);
+
+        long count = 10L;
+        double percentile75 = 75;
+        double percentile95 = 95;
+        double percentile98 = 98;
+        double percentile99 = 99;
+        double percentile999 = 999;
+        double median = 50;
+        double mean = 60;
+        long min = 1L;
+        long max = 100L;
+        double stddev = 10;
+
+        when(snapshot.get75thPercentile()).thenReturn(percentile75);
+        when(snapshot.get95thPercentile()).thenReturn(percentile95);
+        when(snapshot.get98thPercentile()).thenReturn(percentile98);
+        when(snapshot.get99thPercentile()).thenReturn(percentile99);
+        when(snapshot.get999thPercentile()).thenReturn(percentile999);
+        when(snapshot.getMedian()).thenReturn(median);
+        when(snapshot.getMean()).thenReturn(mean);
+        when(snapshot.getMin()).thenReturn(min);
+        when(snapshot.getMax()).thenReturn(max);
+        when(snapshot.getStdDev()).thenReturn(stddev);
+
+        when(histogram.getCount()).thenReturn(count);
+        when(histogram.getSnapshot()).thenReturn(snapshot);
+
+        MetricsCollector collector = mock(MetricsCollector.class);
+        MetricsRecordBuilder recordBuilder = mock(MetricsRecordBuilder.class);
+
+        Mockito.when(collector.addRecord(recordName)).thenReturn(recordBuilder);
+
+        TreeMap<String, Histogram> histograms = new TreeMap<>();
+        histograms.put(metricName, histogram);
+        // Add the metrics objects to the internal "queues" by hand
+        metrics2Reporter.setDropwizardHistograms(histograms);
+
+        metrics2Reporter.getMetrics(collector, true);
+
+        verify(recordBuilder).addGauge(Interns.info(metricName + "_max", ""), metrics2Reporter.convertDuration(max));
+        verify(recordBuilder).addGauge(Interns.info(metricName + "_min", ""), metrics2Reporter.convertDuration(min));
+        verify(recordBuilder).addGauge(Interns.info(metricName + "_median", ""),
+                metrics2Reporter.convertDuration(median));
+        verify(recordBuilder).addGauge(Interns.info(metricName + "_count", ""), count);
+        verify(recordBuilder).addGauge(Interns.info(metricName + "_stddev", ""),
+                metrics2Reporter.convertDuration(stddev));
+
+        verify(recordBuilder).addGauge(Interns.info(metricName + "_75thpercentile", ""),
+                metrics2Reporter.convertDuration(percentile75));
+        verify(recordBuilder).addGauge(Interns.info(metricName + "_95thpercentile", ""),
+                metrics2Reporter.convertDuration(percentile95));
+        verify(recordBuilder).addGauge(Interns.info(metricName + "_98thpercentile", ""),
+                metrics2Reporter.convertDuration(percentile98));
+        verify(recordBuilder).addGauge(Interns.info(metricName + "_99thpercentile", ""),
+                metrics2Reporter.convertDuration(percentile99));
+        verify(recordBuilder).addGauge(Interns.info(metricName + "_999thpercentile", ""),
+                metrics2Reporter.convertDuration(percentile999));
+
+        verifyRecordBuilderUnits(recordBuilder);
+
+        // Should not be the same instance we gave before. Our map should have gotten swapped out.
+        assertTrue("Should not be the same map instance after collection",
+                histograms != metrics2Reporter.getDropwizardHistograms());
+    }
+
+    @Test
+    public void testTimerReporting() {
+        final String metricName = "my_timer";
+        final Timer timer = mock(Timer.class);
+        final Snapshot snapshot = mock(Snapshot.class);
+
+        TreeMap<String, Timer> timers = new TreeMap<>();
+        timers.put(metricName, timer);
+        // Add the metrics objects to the internal "queues" by hand
+        metrics2Reporter.setDropwizardTimers(timers);
+
+        long count = 10L;
+        double meanRate = 1.0;
+        double oneMinRate = 2.0;
+        double fiveMinRate = 5.0;
+        double fifteenMinRate = 10.0;
+
+        when(timer.getCount()).thenReturn(count);
+        when(timer.getMeanRate()).thenReturn(meanRate);
+        when(timer.getOneMinuteRate()).thenReturn(oneMinRate);
+        when(timer.getFiveMinuteRate()).thenReturn(fiveMinRate);
+        when(timer.getFifteenMinuteRate()).thenReturn(fifteenMinRate);
+        when(timer.getSnapshot()).thenReturn(snapshot);
+
+        double percentile75 = 75;
+        double percentile95 = 95;
+        double percentile98 = 98;
+        double percentile99 = 99;
+        double percentile999 = 999;
+        double median = 50;
+        double mean = 60;
+        long min = 1L;
+        long max = 100L;
+        double stddev = 10;
+
+        when(snapshot.get75thPercentile()).thenReturn(percentile75);
+        when(snapshot.get95thPercentile()).thenReturn(percentile95);
+        when(snapshot.get98thPercentile()).thenReturn(percentile98);
+        when(snapshot.get99thPercentile()).thenReturn(percentile99);
+        when(snapshot.get999thPercentile()).thenReturn(percentile999);
+        when(snapshot.getMedian()).thenReturn(median);
+        when(snapshot.getMean()).thenReturn(mean);
+        when(snapshot.getMin()).thenReturn(min);
+        when(snapshot.getMax()).thenReturn(max);
+        when(snapshot.getStdDev()).thenReturn(stddev);
+
+        MetricsCollector collector = mock(MetricsCollector.class);
+        MetricsRecordBuilder recordBuilder = mock(MetricsRecordBuilder.class);
+
+        Mockito.when(collector.addRecord(recordName)).thenReturn(recordBuilder);
+
+        metrics2Reporter.getMetrics(collector, true);
+
+        // We get the count from the meter and histogram
+        verify(recordBuilder).addGauge(Interns.info(metricName + "_count", ""), count);
+
+        // Verify the rates
+        verify(recordBuilder).addGauge(Interns.info(metricName + "_mean_rate", ""),
+                metrics2Reporter.convertRate(meanRate));
+        verify(recordBuilder).addGauge(Interns.info(metricName + "_1min_rate", ""),
+                metrics2Reporter.convertRate(oneMinRate));
+        verify(recordBuilder).addGauge(Interns.info(metricName + "_5min_rate", ""),
+                metrics2Reporter.convertRate(fiveMinRate));
+        verify(recordBuilder).addGauge(Interns.info(metricName + "_15min_rate", ""),
+                metrics2Reporter.convertRate(fifteenMinRate));
+
+        // Verify the histogram
+        verify(recordBuilder).addGauge(Interns.info(metricName + "_max", ""), metrics2Reporter.convertDuration(max));
+        verify(recordBuilder).addGauge(Interns.info(metricName + "_min", ""), metrics2Reporter.convertDuration(min));
+        verify(recordBuilder).addGauge(Interns.info(metricName + "_median", ""),
+                metrics2Reporter.convertDuration(median));
+        verify(recordBuilder).addGauge(Interns.info(metricName + "_stddev", ""),
+                metrics2Reporter.convertDuration(stddev));
+
+        verify(recordBuilder).addGauge(Interns.info(metricName + "_75thpercentile", ""),
+                metrics2Reporter.convertDuration(percentile75));
+        verify(recordBuilder).addGauge(Interns.info(metricName + "_95thpercentile", ""),
+                metrics2Reporter.convertDuration(percentile95));
+        verify(recordBuilder).addGauge(Interns.info(metricName + "_98thpercentile", ""),
+                metrics2Reporter.convertDuration(percentile98));
+        verify(recordBuilder).addGauge(Interns.info(metricName + "_99thpercentile", ""),
+                metrics2Reporter.convertDuration(percentile99));
+        verify(recordBuilder).addGauge(Interns.info(metricName + "_999thpercentile", ""),
+                metrics2Reporter.convertDuration(percentile999));
+
+        verifyRecordBuilderUnits(recordBuilder);
+
+        // Should not be the same instance we gave before. Our map should have gotten swapped out.
+        assertTrue("Should not be the same map instance after collection",
+                timers != metrics2Reporter.getDropwizardTimers());
+    }
+
+    @Test
+    public void testMeterReporting() {
+        final String metricName = "my_meter";
+        final Meter meter = mock(Meter.class);
+
+        TreeMap<String, Meter> meters = new TreeMap<>();
+        meters.put(metricName, meter);
+        // Add the metrics objects to the internal "queues" by hand
+        metrics2Reporter.setDropwizardMeters(meters);
+
+        // Set some values
+        long count = 10L;
+        double meanRate = 1.0;
+        double oneMinRate = 2.0;
+        double fiveMinRate = 5.0;
+        double fifteenMinRate = 10.0;
+
+        when(meter.getCount()).thenReturn(count);
+        when(meter.getMeanRate()).thenReturn(meanRate);
+        when(meter.getOneMinuteRate()).thenReturn(oneMinRate);
+        when(meter.getFiveMinuteRate()).thenReturn(fiveMinRate);
+        when(meter.getFifteenMinuteRate()).thenReturn(fifteenMinRate);
+
+        MetricsCollector collector = mock(MetricsCollector.class);
+        MetricsRecordBuilder recordBuilder = mock(MetricsRecordBuilder.class);
+
+        Mockito.when(collector.addRecord(recordName)).thenReturn(recordBuilder);
+
+        metrics2Reporter.getMetrics(collector, true);
+
+        // Verify the rates
+        verify(recordBuilder).addGauge(Interns.info(metricName + "_count", ""), count);
+        verify(recordBuilder).addGauge(Interns.info(metricName + "_mean_rate", ""),
+                metrics2Reporter.convertRate(meanRate));
+        verify(recordBuilder).addGauge(Interns.info(metricName + "_1min_rate", ""),
+                metrics2Reporter.convertRate(oneMinRate));
+        verify(recordBuilder).addGauge(Interns.info(metricName + "_5min_rate", ""),
+                metrics2Reporter.convertRate(fiveMinRate));
+        verify(recordBuilder).addGauge(Interns.info(metricName + "_15min_rate", ""),
+                metrics2Reporter.convertRate(fifteenMinRate));
+
+        // Verify the units
+        verifyRecordBuilderUnits(recordBuilder);
+
+        // Should not be the same instance we gave before. Our map should have gotten swapped out.
+        assertTrue("Should not be the same map instance after collection",
+                meters != metrics2Reporter.getDropwizardMeters());
+    }
+
+    @SuppressWarnings("rawtypes")
+    @Test
+    public void metrics2CycleIsNonDestructive() {
+        metrics2Reporter.setDropwizardCounters(Collections.unmodifiableSortedMap(new TreeMap<String, Counter>()));
+        metrics2Reporter.setDropwizardGauges(Collections.unmodifiableSortedMap(new TreeMap<String, Gauge>()));
+        metrics2Reporter.setDropwizardHistograms(Collections.unmodifiableSortedMap(new TreeMap<String, Histogram>()));
+        metrics2Reporter.setDropwizardMeters(Collections.unmodifiableSortedMap(new TreeMap<String, Meter>()));
+        metrics2Reporter.setDropwizardTimers(Collections.unmodifiableSortedMap(new TreeMap<String, Timer>()));
+
+        MetricsCollector collector = mock(MetricsCollector.class);
+        MetricsRecordBuilder recordBuilder = mock(MetricsRecordBuilder.class);
+
+        Mockito.when(collector.addRecord(recordName)).thenReturn(recordBuilder);
+
+        metrics2Reporter.getMetrics(collector, true);
+    }
+
+    @SuppressWarnings("rawtypes")
+    @Test
+    public void cachedMetricsAreClearedAfterCycle() {
+        // After we perform a metrics2 reporting cycle, the maps should be reset to avoid double-reporting
+        TreeMap<String, Counter> counters = new TreeMap<>();
+        TreeMap<String, Gauge> gauges = new TreeMap<>();
+        TreeMap<String, Histogram> histograms = new TreeMap<>();
+        TreeMap<String, Meter> meters = new TreeMap<>();
+        TreeMap<String, Timer> timers = new TreeMap<>();
+
+        metrics2Reporter.setDropwizardCounters(counters);
+        metrics2Reporter.setDropwizardGauges(gauges);
+        metrics2Reporter.setDropwizardHistograms(histograms);
+        metrics2Reporter.setDropwizardMeters(meters);
+        metrics2Reporter.setDropwizardTimers(timers);
+
+        MetricsCollector collector = mock(MetricsCollector.class);
+        MetricsRecordBuilder recordBuilder = mock(MetricsRecordBuilder.class);
+
+        Mockito.when(collector.addRecord(recordName)).thenReturn(recordBuilder);
+
+        metrics2Reporter.getMetrics(collector, true);
+
+        assertTrue(counters != metrics2Reporter.getDropwizardCounters());
+        assertEquals(0, metrics2Reporter.getDropwizardCounters().size());
+        assertTrue(gauges != metrics2Reporter.getDropwizardGauges());
+        assertEquals(0, metrics2Reporter.getDropwizardGauges().size());
+        assertTrue(histograms != metrics2Reporter.getDropwizardHistograms());
+        assertEquals(0, metrics2Reporter.getDropwizardHistograms().size());
+        assertTrue(meters != metrics2Reporter.getDropwizardMeters());
+        assertEquals(0, metrics2Reporter.getDropwizardMeters().size());
+        assertTrue(timers != metrics2Reporter.getDropwizardTimers());
+        assertEquals(0, metrics2Reporter.getDropwizardTimers().size());
+    }
+}
diff --git a/core-common/src/test/java/org/apache/kylin/common/metrics/metrics2/StandaloneExample.java b/core-common/src/test/java/org/apache/kylin/common/metrics/metrics2/StandaloneExample.java
new file mode 100644
index 0000000000..fabfdab713
--- /dev/null
+++ b/core-common/src/test/java/org/apache/kylin/common/metrics/metrics2/StandaloneExample.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.kylin.common.metrics.metrics2;
+
+import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.configuration.SubsetConfiguration;
+import org.apache.hadoop.metrics2.MetricsSystem;
+import org.apache.hadoop.metrics2.lib.DefaultMetricsSystem;
+import org.apache.hadoop.metrics2.sink.FileSink;
+
+import com.codahale.metrics.ConsoleReporter;
+import com.codahale.metrics.MetricRegistry;
+
+/**
+ * Modified from https://github.com/joshelser/dropwizard-hadoop-metrics2, Copyright by Josh Elser
+ *
+ * A little utility to try to simulate "real-life" scenarios. Doesn't actually assert anything yet
+ * so it requires human interaction.
+ */
+public class StandaloneExample {
+
+    public static void main(String[] args) throws Exception {
+        final MetricRegistry metrics = new MetricRegistry();
+
+        final HadoopMetrics2Reporter metrics2Reporter = HadoopMetrics2Reporter.forRegistry(metrics).build(
+                DefaultMetricsSystem.initialize("StandaloneTest"), // The application-level name
+                "Test", // Component name
+                "Test", // Component description
+                "Test"); // Name for each metric record
+        final ConsoleReporter consoleReporter = ConsoleReporter.forRegistry(metrics).build();
+
+        MetricsSystem metrics2 = DefaultMetricsSystem.instance();
+        // Writes to stdout without a filename configuration
+        // Will be invoked every 10seconds by default
+        FileSink sink = new FileSink();
+        metrics2.register("filesink", "filesink", sink);
+        sink.init(new SubsetConfiguration(null, null) {
+            public String getString(String key) {
+                if (key.equals("filename")) {
+                    return null;
+                }
+                return super.getString(key);
+            }
+        });
+
+        // How often should the dropwizard reporter be invoked
+        metrics2Reporter.start(500, TimeUnit.MILLISECONDS);
+        // How often will the dropwziard metrics be logged to the console
+        consoleReporter.start(2, TimeUnit.SECONDS);
+
+        generateMetrics(metrics, 5000, 25, TimeUnit.MILLISECONDS, metrics2Reporter, 10);
+    }
+
+    /**
+     * Runs a number of threads which generate metrics.
+     */
+    public static void generateMetrics(final MetricRegistry metrics, final long metricsToGenerate, final int period,
+            final TimeUnit periodTimeUnit, HadoopMetrics2Reporter metrics2Reporter, int numThreads) throws Exception {
+        final ScheduledExecutorService pool = Executors.newScheduledThreadPool(numThreads);
+        final CountDownLatch latch = new CountDownLatch(numThreads);
+
+        for (int i = 0; i < numThreads; i++) {
+            final int id = i;
+            final int halfPeriod = (period / 2);
+            Runnable task = new Runnable() {
+                private long executions = 0;
+                final Random r = new Random();
+
+                @Override
+                public void run() {
+                    if (executions >= metricsToGenerate) {
+                        return;
+                    }
+                    metrics.counter("foo counter thread" + id).inc();
+                    executions++;
+                    if (executions < metricsToGenerate) {
+                        pool.schedule(this, period + r.nextInt(halfPeriod), periodTimeUnit);
+                    } else {
+                        latch.countDown();
+                    }
+                }
+            };
+            pool.schedule(task, period, periodTimeUnit);
+        }
+
+        while (!latch.await(2, TimeUnit.SECONDS)) {
+            metrics2Reporter.printQueueDebugMessage();
+        }
+
+        pool.shutdown();
+        pool.awaitTermination(5000, TimeUnit.SECONDS);
+    }
+}
diff --git a/pom.xml b/pom.xml
index 5c3b2cb979..f7d50ebd37 100644
--- a/pom.xml
+++ b/pom.xml
@@ -40,7 +40,7 @@
 
   <properties>
     <!-- General Properties -->
-    <javaVersion>1.7</javaVersion>
+    <javaVersion>1.8</javaVersion>
     <maven-model.version>3.3.9</maven-model.version>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
@@ -76,11 +76,11 @@
     <avatica.version>1.10.0</avatica.version>
 
     <!-- Hadoop Common deps, keep compatible with hadoop2.version -->
-    <zookeeper.version>3.4.12</zookeeper.version>
+    <zookeeper.version>3.4.13</zookeeper.version>
     <curator.version>2.12.0</curator.version>
     <jsr305.version>3.0.1</jsr305.version>
     <guava.version>14.0</guava.version>
-    <jsch.version>0.1.53</jsch.version>
+    <jsch.version>0.1.54</jsch.version>
     <commons-cli.version>1.2</commons-cli.version>
     <commons-lang.version>2.6</commons-lang.version>
     <commons-io.version>2.4</commons-io.version>
@@ -107,7 +107,7 @@
     <commons-lang3.version>3.4</commons-lang3.version>
     <commons-email.version>1.5</commons-email.version>
     <commons-validator.version>1.4.0</commons-validator.version>
-    <commons-compress>1.17</commons-compress>
+    <commons-compress>1.18</commons-compress>
 
     <!-- Utility -->
     <log4j.version>1.2.17</log4j.version>


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services