You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ab...@apache.org on 2017/04/18 15:15:17 UTC

lucene-solr:branch_6x: SOLR-10477: Port some of the SolrMetricReporter enhancements from master to 6.x.

Repository: lucene-solr
Updated Branches:
  refs/heads/branch_6x 46012784d -> 3641497ba


SOLR-10477: Port some of the SolrMetricReporter enhancements from master to 6.x.


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/3641497b
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/3641497b
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/3641497b

Branch: refs/heads/branch_6x
Commit: 3641497ba5da5999ff68ab58d9c19a59c287bac1
Parents: 4601278
Author: Andrzej Bialecki <ab...@apache.org>
Authored: Tue Apr 18 17:14:45 2017 +0200
Committer: Andrzej Bialecki <ab...@apache.org>
Committed: Tue Apr 18 17:14:45 2017 +0200

----------------------------------------------------------------------
 solr/CHANGES.txt                                |   4 +
 .../org/apache/solr/core/SolrXmlConfig.java     |  16 +-
 .../solr/handler/admin/MetricsHandler.java      |  40 +++-
 .../solr/handler/admin/SystemInfoHandler.java   |  68 +------
 .../solr/metrics/AltBufferPoolMetricSet.java    |  47 +++++
 .../solr/metrics/OperatingSystemMetricSet.java  |  66 +-----
 .../apache/solr/metrics/SolrMetricManager.java  | 199 ++++++++++++++++++-
 .../apache/solr/metrics/SolrMetricReporter.java |  12 ++
 .../metrics/reporters/JmxObjectNameFactory.java |  19 +-
 .../metrics/reporters/ReporterClientCache.java  |  84 ++++++++
 .../metrics/reporters/SolrGangliaReporter.java  |  48 ++++-
 .../metrics/reporters/SolrGraphiteReporter.java |  46 ++++-
 .../solr/metrics/reporters/SolrJmxReporter.java | 132 +++++++++---
 .../metrics/reporters/SolrSlf4jReporter.java    |  28 ++-
 .../apache/solr/servlet/SolrDispatchFilter.java |   9 +-
 .../org/apache/solr/util/stats/MetricUtils.java |  82 ++++++++
 .../solr/cloud/ReplicationFactorTest.java       |   9 -
 .../solr/handler/admin/MetricsHandlerTest.java  |  17 ++
 .../handler/admin/SystemInfoHandlerTest.java    |   8 +-
 .../org/apache/solr/metrics/JvmMetricsTest.java |  53 ++++-
 .../solr/metrics/SolrMetricReporterTest.java    |   1 +
 .../reporters/SolrGangliaReporterTest.java      |   2 +-
 .../metrics/reporters/SolrJmxReporterTest.java  |  58 +++++-
 .../reporters/SolrSlf4jReporterTest.java        |   2 +-
 .../java/org/apache/solr/SolrTestCaseJ4.java    |  14 ++
 .../cloud/AbstractFullDistribZkTestBase.java    |   9 -
 26 files changed, 835 insertions(+), 238 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/3641497b/solr/CHANGES.txt
----------------------------------------------------------------------
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index d57f16e..87d1c54 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -161,6 +161,10 @@ Other Changes
 
 * SOLR-10151: Use monotonically incrementing counter for doc ids in TestRecovery. (Peter Szantai-Kis, Mano Kovacs via Mark Miller)
 
+* SOLR-10477: Improvements to metric reporters and API: support for "regex" parameter in /admin/metrics,
+  "enabled" flag in reporter configurations, correct handling of "serviceUrl" in SolrJmxReporter, better handling
+  of service clients for JMX, Ganglia and Graphite reporters. (ab)
+
 ==================  6.5.1 ==================
 
 Bug Fixes

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/3641497b/solr/core/src/java/org/apache/solr/core/SolrXmlConfig.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/core/SolrXmlConfig.java b/solr/core/src/java/org/apache/solr/core/SolrXmlConfig.java
index 2db5be2..919e90e 100644
--- a/solr/core/src/java/org/apache/solr/core/SolrXmlConfig.java
+++ b/solr/core/src/java/org/apache/solr/core/SolrXmlConfig.java
@@ -25,7 +25,9 @@ import java.lang.invoke.MethodHandles;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
@@ -470,13 +472,15 @@ public class SolrXmlConfig {
 
   private static PluginInfo[] getMetricReporterPluginInfos(Config config) {
     NodeList nodes = (NodeList) config.evaluate("solr/metrics/reporter", XPathConstants.NODESET);
-    if (nodes == null || nodes.getLength() == 0)
-      return new PluginInfo[0];
-    PluginInfo[] configs = new PluginInfo[nodes.getLength()];
-    for (int i = 0; i < nodes.getLength(); i++) {
-      configs[i] = new PluginInfo(nodes.item(i), "SolrMetricReporter", true, true);
+    List<PluginInfo> configs = new ArrayList<>();
+    if (nodes != null && nodes.getLength() > 0) {
+      for (int i = 0; i < nodes.getLength(); i++) {
+        // we don't require class in order to support predefined replica and node reporter classes
+        PluginInfo info = new PluginInfo(nodes.item(i), "SolrMetricReporter", true, false);
+        configs.add(info);
+      }
     }
-    return configs;
+    return configs.toArray(new PluginInfo[configs.size()]);
   }
   private static PluginInfo getTransientCoreCacheFactoryPluginInfo(Config config) {
     Node node = config.getNode("solr/transientCoreCacheFactory", false);

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/3641497b/solr/core/src/java/org/apache/solr/handler/admin/MetricsHandler.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/MetricsHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/MetricsHandler.java
index 4dc86d9..2b92c02 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/MetricsHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/MetricsHandler.java
@@ -19,6 +19,7 @@ package org.apache.solr.handler.admin;
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.EnumSet;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -52,6 +53,13 @@ public class MetricsHandler extends RequestHandlerBase implements PermissionName
   final SolrMetricManager metricManager;
 
   public static final String COMPACT_PARAM = "compact";
+  public static final String PREFIX_PARAM = "prefix";
+  public static final String REGEX_PARAM = "regex";
+  public static final String REGISTRY_PARAM = "registry";
+  public static final String GROUP_PARAM = "group";
+  public static final String TYPE_PARAM = "type";
+
+  public static final String ALL = "all";
 
   public MetricsHandler() {
     this.container = null;
@@ -90,23 +98,32 @@ public class MetricsHandler extends RequestHandlerBase implements PermissionName
   }
 
   private MetricFilter parseMustMatchFilter(SolrQueryRequest req) {
-    String[] prefixes = req.getParams().getParams("prefix");
-    MetricFilter mustMatchFilter;
+    String[] prefixes = req.getParams().getParams(PREFIX_PARAM);
+    MetricFilter prefixFilter = null;
     if (prefixes != null && prefixes.length > 0) {
       Set<String> prefixSet = new HashSet<>();
       for (String prefix : prefixes) {
         prefixSet.addAll(StrUtils.splitSmart(prefix, ','));
       }
-      mustMatchFilter = new SolrMetricManager.PrefixFilter((String[])prefixSet.toArray(new String[prefixSet.size()]));
-    } else  {
+      prefixFilter = new SolrMetricManager.PrefixFilter((String[])prefixSet.toArray(new String[prefixSet.size()]));
+    }
+    String[] regexes = req.getParams().getParams(REGEX_PARAM);
+    MetricFilter regexFilter = null;
+    if (regexes != null && regexes.length > 0) {
+      regexFilter = new SolrMetricManager.RegexFilter(regexes);
+    }
+    MetricFilter mustMatchFilter;
+    if (prefixFilter == null && regexFilter == null) {
       mustMatchFilter = MetricFilter.ALL;
+    } else {
+      mustMatchFilter = new SolrMetricManager.OrFilter(prefixFilter, regexFilter);
     }
     return mustMatchFilter;
   }
 
   private Set<String> parseRegistries(SolrQueryRequest req) {
-    String[] groupStr = req.getParams().getParams("group");
-    String[] registryStr = req.getParams().getParams("registry");
+    String[] groupStr = req.getParams().getParams(GROUP_PARAM);
+    String[] registryStr = req.getParams().getParams(REGISTRY_PARAM);
     if ((groupStr == null || groupStr.length == 0) && (registryStr == null || registryStr.length == 0)) {
       // return all registries
       return container.getMetricManager().registryNames();
@@ -118,7 +135,7 @@ public class MetricsHandler extends RequestHandlerBase implements PermissionName
       for (String g : groupStr) {
         List<String> split = StrUtils.splitSmart(g, ',');
         for (String s : split) {
-          if (s.trim().equals("all")) {
+          if (s.trim().equals(ALL)) {
             allRegistries = true;
             break;
           }
@@ -137,7 +154,7 @@ public class MetricsHandler extends RequestHandlerBase implements PermissionName
       for (String r : registryStr) {
         List<String> split = StrUtils.splitSmart(r, ',');
         for (String s : split) {
-          if (s.trim().equals("all")) {
+          if (s.trim().equals(ALL)) {
             allRegistries = true;
             break;
           }
@@ -161,7 +178,7 @@ public class MetricsHandler extends RequestHandlerBase implements PermissionName
   }
 
   private List<MetricType> parseMetricTypes(SolrQueryRequest req) {
-    String[] typeStr = req.getParams().getParams("type");
+    String[] typeStr = req.getParams().getParams(TYPE_PARAM);
     List<String> types = Collections.emptyList();
     if (typeStr != null && typeStr.length > 0)  {
       types = new ArrayList<>();
@@ -176,7 +193,8 @@ public class MetricsHandler extends RequestHandlerBase implements PermissionName
         metricTypes = types.stream().map(String::trim).map(MetricType::valueOf).collect(Collectors.toList());
       }
     } catch (IllegalArgumentException e) {
-      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Invalid metric type in: " + types + " specified. Must be one of (all, meter, timer, histogram, counter, gauge)", e);
+      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Invalid metric type in: " + types +
+          " specified. Must be one of " + MetricType.SUPPORTED_TYPES_MSG, e);
     }
     return metricTypes;
   }
@@ -199,6 +217,8 @@ public class MetricsHandler extends RequestHandlerBase implements PermissionName
     gauge(Gauge.class),
     all(null);
 
+    public static final String SUPPORTED_TYPES_MSG = EnumSet.allOf(MetricType.class).toString();
+
     private final Class klass;
 
     MetricType(Class klass) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/3641497b/solr/core/src/java/org/apache/solr/handler/admin/SystemInfoHandler.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/SystemInfoHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/SystemInfoHandler.java
index 8b74491..3a676da 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/SystemInfoHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/SystemInfoHandler.java
@@ -16,10 +16,6 @@
  */
 package org.apache.solr.handler.admin;
 
-import java.beans.BeanInfo;
-import java.beans.IntrospectionException;
-import java.beans.Introspector;
-import java.beans.PropertyDescriptor;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
@@ -27,23 +23,20 @@ import java.io.InputStreamReader;
 import java.lang.invoke.MethodHandles;
 import java.lang.management.ManagementFactory;
 import java.lang.management.OperatingSystemMXBean;
-import java.lang.management.PlatformManagedObject;
 import java.lang.management.RuntimeMXBean;
-import java.lang.reflect.InvocationTargetException;
 import java.net.InetAddress;
 import java.nio.charset.Charset;
 import java.text.DecimalFormat;
 import java.text.DecimalFormatSymbols;
-import java.util.Arrays;
 import java.util.Date;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
 
+import com.codahale.metrics.Gauge;
 import org.apache.commons.io.IOUtils;
 import org.apache.lucene.LucenePackage;
 import org.apache.lucene.util.Constants;
-import org.apache.solr.common.util.NamedList;
 import org.apache.solr.common.util.SimpleOrderedMap;
 import org.apache.solr.core.CoreContainer;
 import org.apache.solr.core.SolrCore;
@@ -53,6 +46,7 @@ import org.apache.solr.response.SolrQueryResponse;
 import org.apache.solr.schema.IndexSchema;
 import org.apache.solr.util.RTimer;
 import org.apache.solr.util.RedactionUtils;
+import org.apache.solr.util.stats.MetricUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -207,29 +201,13 @@ public class SystemInfoHandler extends RequestHandlerBase
     
     OperatingSystemMXBean os = ManagementFactory.getOperatingSystemMXBean();
     info.add(NAME, os.getName()); // add at least this one
-    try {
-      // add remaining ones dynamically using Java Beans API
-      addMXBeanProperties(os, OperatingSystemMXBean.class, info);
-    } catch (IntrospectionException | ReflectiveOperationException e) {
-      log.warn("Unable to fetch properties of OperatingSystemMXBean.", e);
-    }
-
-    // There are some additional beans we want to add (not available on all JVMs):
-    for (String clazz : Arrays.asList(
-        "com.sun.management.OperatingSystemMXBean",
-        "com.sun.management.UnixOperatingSystemMXBean", 
-        "com.ibm.lang.management.OperatingSystemMXBean"
-    )) {
-      try {
-        final Class<? extends PlatformManagedObject> intf = Class.forName(clazz)
-            .asSubclass(PlatformManagedObject.class);
-        addMXBeanProperties(os, intf, info);
-      } catch (ClassNotFoundException e) {
-        // ignore
-      } catch (IntrospectionException | ReflectiveOperationException e) {
-        log.warn("Unable to fetch properties of JVM-specific OperatingSystemMXBean.", e);
+    // add remaining ones dynamically using Java Beans API
+    // also those from JVM implementation-specific classes
+    MetricUtils.addMXBeanMetrics(os, MetricUtils.OS_MXBEAN_CLASSES, null, (name, metric) -> {
+      if (info.get(name) == null) {
+        info.add(name, ((Gauge) metric).getValue());
       }
-    }
+    });
 
     // Try some command line things:
     try { 
@@ -242,35 +220,7 @@ public class SystemInfoHandler extends RequestHandlerBase
     } 
     return info;
   }
-  
-  /**
-   * Add all bean properties of a {@link PlatformManagedObject} to the given {@link NamedList}.
-   * <p>
-   * If you are running a OpenJDK/Oracle JVM, there are nice properties in:
-   * {@code com.sun.management.UnixOperatingSystemMXBean} and
-   * {@code com.sun.management.OperatingSystemMXBean}
-   */
-  static <T extends PlatformManagedObject> void addMXBeanProperties(T obj, Class<? extends T> intf, NamedList<Object> info)
-      throws IntrospectionException, ReflectiveOperationException {
-    if (intf.isInstance(obj)) {
-      final BeanInfo beanInfo = Introspector.getBeanInfo(intf, intf.getSuperclass(), Introspector.IGNORE_ALL_BEANINFO);
-      for (final PropertyDescriptor desc : beanInfo.getPropertyDescriptors()) {
-        final String name = desc.getName();
-        if (info.get(name) == null) {
-          try {
-            final Object v = desc.getReadMethod().invoke(obj);
-            if(v != null) {
-              info.add(name, v);
-            }
-          } catch (InvocationTargetException ite) {
-            // ignore (some properties throw UOE)
-          }
-        }
-      }
-    }
-  }
-  
-  
+
   /**
    * Utility function to execute a function
    */

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/3641497b/solr/core/src/java/org/apache/solr/metrics/AltBufferPoolMetricSet.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/metrics/AltBufferPoolMetricSet.java b/solr/core/src/java/org/apache/solr/metrics/AltBufferPoolMetricSet.java
new file mode 100644
index 0000000..ccd7564
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/metrics/AltBufferPoolMetricSet.java
@@ -0,0 +1,47 @@
+/*
+ * 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.solr.metrics;
+
+import java.lang.management.BufferPoolMXBean;
+import java.lang.management.ManagementFactory;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.Metric;
+import com.codahale.metrics.MetricSet;
+
+/**
+ * This is an alternative implementation of {@link com.codahale.metrics.jvm.BufferPoolMetricSet} that
+ * doesn't need an MBean server.
+ */
+public class AltBufferPoolMetricSet implements MetricSet {
+
+  @Override
+  public Map<String, Metric> getMetrics() {
+    final Map<String, Metric> metrics = new HashMap<>();
+    List<BufferPoolMXBean> pools = ManagementFactory.getPlatformMXBeans(BufferPoolMXBean.class);
+    for (final BufferPoolMXBean pool : pools) {
+      String name = pool.getName();
+      metrics.put(name + ".Count", (Gauge<Long>)() -> pool.getCount());
+      metrics.put(name + ".MemoryUsed", (Gauge<Long>)() -> pool.getMemoryUsed());
+      metrics.put(name + ".TotalCapacity", (Gauge<Long>)() -> pool.getTotalCapacity());
+    }
+    return metrics;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/3641497b/solr/core/src/java/org/apache/solr/metrics/OperatingSystemMetricSet.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/metrics/OperatingSystemMetricSet.java b/solr/core/src/java/org/apache/solr/metrics/OperatingSystemMetricSet.java
index 34ef5d1..21957eb 100644
--- a/solr/core/src/java/org/apache/solr/metrics/OperatingSystemMetricSet.java
+++ b/solr/core/src/java/org/apache/solr/metrics/OperatingSystemMetricSet.java
@@ -16,77 +16,31 @@
  */
 package org.apache.solr.metrics;
 
-import javax.management.JMException;
-import javax.management.MBeanAttributeInfo;
-import javax.management.MBeanInfo;
-import javax.management.MBeanServer;
-import javax.management.ObjectName;
-import java.lang.invoke.MethodHandles;
+import java.lang.management.ManagementFactory;
+import java.lang.management.OperatingSystemMXBean;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Map;
-import java.util.Set;
 
-import com.codahale.metrics.JmxAttributeGauge;
 import com.codahale.metrics.Metric;
 import com.codahale.metrics.MetricSet;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.apache.solr.util.stats.MetricUtils;
 
 /**
  * This is an extended replacement for {@link com.codahale.metrics.jvm.FileDescriptorRatioGauge}
- * - that class uses reflection and doesn't work under Java 9. We can also get much more
- * information about OS environment once we have to go through MBeanServer anyway.
+ * - that class uses reflection and doesn't work under Java 9. This implementation tries to retrieve
+ * bean properties from known implementations of {@link java.lang.management.OperatingSystemMXBean}.
  */
 public class OperatingSystemMetricSet implements MetricSet {
-  private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
-
-  /** Metric names - these correspond to known numeric MBean attributes. Depending on the OS and
-   * Java implementation only some of them may be actually present.
-   */
-  public static final String[] METRICS = {
-      "AvailableProcessors",
-      "CommittedVirtualMemorySize",
-      "FreePhysicalMemorySize",
-      "FreeSwapSpaceSize",
-      "MaxFileDescriptorCount",
-      "OpenFileDescriptorCount",
-      "ProcessCpuLoad",
-      "ProcessCpuTime",
-      "SystemLoadAverage",
-      "TotalPhysicalMemorySize",
-      "TotalSwapSpaceSize"
-  };
-
-  private final MBeanServer mBeanServer;
-
-  public OperatingSystemMetricSet(MBeanServer mBeanServer) {
-    this.mBeanServer = mBeanServer;
-  }
 
   @Override
   public Map<String, Metric> getMetrics() {
     final Map<String, Metric> metrics = new HashMap<>();
-
-    try {
-      final ObjectName on = new ObjectName("java.lang:type=OperatingSystem");
-      // verify that it exists
-      MBeanInfo info = mBeanServer.getMBeanInfo(on);
-      // collect valid attributes
-      Set<String> attributes = new HashSet<>();
-      for (MBeanAttributeInfo ai : info.getAttributes()) {
-        attributes.add(ai.getName());
-      }
-      for (String metric : METRICS) {
-        // verify that an attribute exists before attempting to add it
-        if (attributes.contains(metric)) {
-          metrics.put(metric, new JmxAttributeGauge(mBeanServer, on, metric));
-        }
+    OperatingSystemMXBean os = ManagementFactory.getOperatingSystemMXBean();
+    MetricUtils.addMXBeanMetrics(os, MetricUtils.OS_MXBEAN_CLASSES, null, (k, v) -> {
+      if (!metrics.containsKey(k)) {
+        metrics.put(k, v);
       }
-    } catch (JMException ignored) {
-      log.debug("Unable to load OperatingSystem MBean", ignored);
-    }
-
+    });
     return metrics;
   }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/3641497b/solr/core/src/java/org/apache/solr/metrics/SolrMetricManager.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/metrics/SolrMetricManager.java b/solr/core/src/java/org/apache/solr/metrics/SolrMetricManager.java
index cdc1adf..a1505e4 100644
--- a/solr/core/src/java/org/apache/solr/metrics/SolrMetricManager.java
+++ b/solr/core/src/java/org/apache/solr/metrics/SolrMetricManager.java
@@ -18,9 +18,13 @@ package org.apache.solr.metrics;
 
 import java.io.IOException;
 import java.lang.invoke.MethodHandles;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
@@ -29,6 +33,9 @@ import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+import java.util.stream.Collectors;
 
 import com.codahale.metrics.Counter;
 import com.codahale.metrics.Gauge;
@@ -92,10 +99,10 @@ public class SolrMetricManager {
 
   /**
    * An implementation of {@link MetricFilter} that selects metrics
-   * with names that start with a prefix.
+   * with names that start with one of prefixes.
    */
   public static class PrefixFilter implements MetricFilter {
-    private final String[] prefixes;
+    private final Set<String> prefixes = new HashSet<>();
     private final Set<String> matched = new HashSet<>();
     private boolean allMatch = false;
 
@@ -107,8 +114,18 @@ public class SolrMetricManager {
      */
     public PrefixFilter(String... prefixes) {
       Objects.requireNonNull(prefixes);
-      this.prefixes = prefixes;
-      if (prefixes.length == 0) {
+      if (prefixes.length > 0) {
+        this.prefixes.addAll(Arrays.asList(prefixes));
+      }
+      if (this.prefixes.isEmpty()) {
+        allMatch = true;
+      }
+    }
+
+    public PrefixFilter(Collection<String> prefixes) {
+      Objects.requireNonNull(prefixes);
+      this.prefixes.addAll(prefixes);
+      if (this.prefixes.isEmpty()) {
         allMatch = true;
       }
     }
@@ -142,6 +159,145 @@ public class SolrMetricManager {
     public void reset() {
       matched.clear();
     }
+
+    @Override
+    public String toString() {
+      return "PrefixFilter{" +
+          "prefixes=" + prefixes +
+          '}';
+    }
+  }
+
+  /**
+   * An implementation of {@link MetricFilter} that selects metrics
+   * with names that match regular expression patterns.
+   */
+  public static class RegexFilter implements MetricFilter {
+    private final Set<Pattern> compiledPatterns = new HashSet<>();
+    private final Set<String> matched = new HashSet<>();
+    private boolean allMatch = false;
+
+    /**
+     * Create a filter that uses the provided prefix.
+     * @param patterns regex patterns to use, must not be null. If empty then any
+     *               name will match, if not empty then match on any pattern will
+     *                 succeed (logical OR).
+     */
+    public RegexFilter(String... patterns) throws PatternSyntaxException {
+      this(patterns != null ? Arrays.asList(patterns) : Collections.emptyList());
+    }
+
+    public RegexFilter(Collection<String> patterns) throws PatternSyntaxException {
+      Objects.requireNonNull(patterns);
+      if (patterns.isEmpty()) {
+        allMatch = true;
+        return;
+      }
+      patterns.forEach(p -> {
+        Pattern pattern = Pattern.compile(p);
+        compiledPatterns.add(pattern);
+      });
+      if (patterns.isEmpty()) {
+        allMatch = true;
+      }
+    }
+
+    @Override
+    public boolean matches(String name, Metric metric) {
+      if (allMatch) {
+        matched.add(name);
+        return true;
+      }
+      for (Pattern p : compiledPatterns) {
+        if (p.matcher(name).matches()) {
+          matched.add(name);
+          return true;
+        }
+      }
+      return false;
+    }
+
+    /**
+     * Return the set of names that matched this filter.
+     * @return matching names
+     */
+    public Set<String> getMatched() {
+      return Collections.unmodifiableSet(matched);
+    }
+
+    /**
+     * Clear the set of names that matched.
+     */
+    public void reset() {
+      matched.clear();
+    }
+
+    @Override
+    public String toString() {
+      return "RegexFilter{" +
+          "compiledPatterns=" + compiledPatterns +
+          '}';
+    }
+  }
+
+  public static class OrFilter implements MetricFilter {
+    List<MetricFilter> filters = new ArrayList<>();
+
+    public OrFilter(Collection<MetricFilter> filters) {
+      if (filters != null) {
+        this.filters.addAll(filters);
+      }
+    }
+
+    public OrFilter(MetricFilter... filters) {
+      if (filters != null) {
+        for (MetricFilter filter : filters) {
+          if (filter != null) {
+            this.filters.add(filter);
+          }
+        }
+      }
+    }
+
+    @Override
+    public boolean matches(String s, Metric metric) {
+      for (MetricFilter filter : filters) {
+        if (filter.matches(s, metric)) {
+          return true;
+        }
+      }
+      return false;
+    }
+  }
+
+  public static class AndFilter implements MetricFilter {
+    List<MetricFilter> filters = new ArrayList<>();
+
+    public AndFilter(Collection<MetricFilter> filters) {
+      if (filters != null) {
+        this.filters.addAll(filters);
+      }
+    }
+
+    public AndFilter(MetricFilter... filters) {
+      if (filters != null) {
+        for (MetricFilter filter : filters) {
+          if (filter != null) {
+            this.filters.add(filter);
+          }
+        }
+      }
+    }
+
+    @Override
+    public boolean matches(String s, Metric metric) {
+      for (MetricFilter filter : filters) {
+        if (!filter.matches(s, metric)) {
+          return false;
+        }
+      }
+      return true;
+    }
   }
 
   /**
@@ -151,7 +307,40 @@ public class SolrMetricManager {
     Set<String> set = new HashSet<>();
     set.addAll(registries.keySet());
     set.addAll(SharedMetricRegistries.names());
-    return Collections.unmodifiableSet(set);
+    return set;
+  }
+
+  /**
+   * Return set of existing registry names that match a regex pattern
+   * @param patterns regex patterns. NOTE: users need to make sure that patterns that
+   *                 don't start with a wildcard use the full registry name starting with
+   *                 {@link #REGISTRY_NAME_PREFIX}
+   * @return set of existing registry names where at least one pattern matched.
+   */
+  public Set<String> registryNames(String... patterns) throws PatternSyntaxException {
+    if (patterns == null || patterns.length == 0) {
+      return registryNames();
+    }
+    List<Pattern> compiled = new ArrayList<>();
+    for (String pattern : patterns) {
+      compiled.add(Pattern.compile(pattern));
+    }
+    return registryNames((Pattern[])compiled.toArray(new Pattern[compiled.size()]));
+  }
+
+  public Set<String> registryNames(Pattern... patterns) {
+    Set<String> allNames = registryNames();
+    if (patterns == null || patterns.length == 0) {
+      return allNames;
+    }
+    return allNames.stream().filter(s -> {
+      for (Pattern p : patterns) {
+        if (p.matcher(s).matches()) {
+          return true;
+        }
+      }
+      return false;
+    }).collect(Collectors.toSet());
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/3641497b/solr/core/src/java/org/apache/solr/metrics/SolrMetricReporter.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/metrics/SolrMetricReporter.java b/solr/core/src/java/org/apache/solr/metrics/SolrMetricReporter.java
index ff2d3fc..9ad15d0 100644
--- a/solr/core/src/java/org/apache/solr/metrics/SolrMetricReporter.java
+++ b/solr/core/src/java/org/apache/solr/metrics/SolrMetricReporter.java
@@ -30,6 +30,7 @@ public abstract class SolrMetricReporter implements Closeable, PluginInfoInitial
   protected final String registryName;
   protected final SolrMetricManager metricManager;
   protected PluginInfo pluginInfo;
+  protected boolean enabled = true;
 
   /**
    * Create a reporter for metrics managed in a named registry.
@@ -58,6 +59,17 @@ public abstract class SolrMetricReporter implements Closeable, PluginInfoInitial
   }
 
   /**
+   * Enable reporting, defaults to true. Implementations should check this flag in
+   * {@link #validate()} and accordingly enable or disable reporting.
+   * @param enabled enable, defaults to true when null or not set.
+   */
+  public void setEnabled(Boolean enabled) {
+    if (enabled != null) {
+      this.enabled = enabled;
+    }
+  }
+
+  /**
    * Get the effective {@link PluginInfo} instance that was used for
    * initialization of this plugin.
    * @return plugin info, or null if not yet initialized.

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/3641497b/solr/core/src/java/org/apache/solr/metrics/reporters/JmxObjectNameFactory.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/metrics/reporters/JmxObjectNameFactory.java b/solr/core/src/java/org/apache/solr/metrics/reporters/JmxObjectNameFactory.java
index 4df5257..c9c9439 100644
--- a/solr/core/src/java/org/apache/solr/metrics/reporters/JmxObjectNameFactory.java
+++ b/solr/core/src/java/org/apache/solr/metrics/reporters/JmxObjectNameFactory.java
@@ -41,9 +41,9 @@ public class JmxObjectNameFactory implements ObjectNameFactory {
    * @param additionalProperties additional properties as key, value pairs.
    */
   public JmxObjectNameFactory(String reporterName, String domain, String... additionalProperties) {
-    this.reporterName = reporterName;
+    this.reporterName = reporterName.replaceAll(":", "_");
     this.domain = domain;
-    this.subdomains = domain.split("\\.");
+    this.subdomains = domain.replaceAll(":", "_").split("\\.");
     if (additionalProperties != null && (additionalProperties.length % 2) != 0) {
       throw new IllegalArgumentException("additionalProperties length must be even: " + Arrays.toString(additionalProperties));
     }
@@ -60,7 +60,8 @@ public class JmxObjectNameFactory implements ObjectNameFactory {
   @Override
   public ObjectName createName(String type, String currentDomain, String name) {
     SolrMetricInfo metricInfo = SolrMetricInfo.of(name);
-
+    String safeName = metricInfo != null ? metricInfo.name : name;
+    safeName = safeName.replaceAll(":", "_");
     // It turns out that ObjectName(String) mostly preserves key ordering
     // as specified in the constructor (except for the 'type' key that ends
     // up at top level) - unlike ObjectName(String, Map) constructor
@@ -83,7 +84,7 @@ public class JmxObjectNameFactory implements ObjectNameFactory {
         }
         sb.append(','); // separate from other properties
       } else {
-        sb.append(currentDomain);
+        sb.append(currentDomain.replaceAll(":", "_"));
         sb.append(':');
       }
     } else {
@@ -96,18 +97,20 @@ public class JmxObjectNameFactory implements ObjectNameFactory {
     if (metricInfo != null) {
       sb.append("category=");
       sb.append(metricInfo.category.toString());
-      sb.append(",scope=");
-      sb.append(metricInfo.scope);
+      if (metricInfo.scope != null) {
+        sb.append(",scope=");
+        sb.append(metricInfo.scope);
+      }
       // we could also split by type, but don't call it 'type' :)
       // if (type != null) {
       //   sb.append(",class=");
       //   sb.append(type);
       // }
       sb.append(",name=");
-      sb.append(metricInfo.name);
+      sb.append(safeName);
     } else {
       // make dotted names into hierarchies
-      String[] path = name.split("\\.");
+      String[] path = safeName.split("\\.");
       for (int i = 0; i < path.length - 1; i++) {
         if (i > 0) {
           sb.append(',');

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/3641497b/solr/core/src/java/org/apache/solr/metrics/reporters/ReporterClientCache.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/metrics/reporters/ReporterClientCache.java b/solr/core/src/java/org/apache/solr/metrics/reporters/ReporterClientCache.java
new file mode 100644
index 0000000..e8ad1fa
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/metrics/reporters/ReporterClientCache.java
@@ -0,0 +1,84 @@
+/*
+ * 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.solr.metrics.reporters;
+
+import java.io.Closeable;
+import java.lang.invoke.MethodHandles;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Simple cache for reusable service clients used by some implementations of
+ * {@link org.apache.solr.metrics.SolrMetricReporter}.
+ */
+public class ReporterClientCache<T> implements Closeable {
+  private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+  private final Map<String, T> cache = new ConcurrentHashMap<>();
+
+  /**
+   * Provide an instance of service client.
+   * @param <T> formal type
+   */
+  public interface ClientProvider<T> {
+    /**
+     * Get an instance of a service client. It's not specified that each time this
+     * method is invoked a new client instance should be returned.
+     * @return client instance
+     * @throws Exception when client creation encountered an error.
+     */
+    T get() throws Exception;
+  }
+
+  /**
+   * Get existing or register a new client.
+   * @param id client id
+   * @param clientProvider provider of new client instances
+   */
+  public synchronized T getOrCreate(String id, ClientProvider<T> clientProvider) {
+    T item = cache.get(id);
+    if (item == null) {
+      try {
+        item = clientProvider.get();
+        cache.put(id, item);
+      } catch (Exception e) {
+        LOG.warn("Error providing a new client for id=" + id, e);
+        item = null;
+      }
+    }
+    return item;
+  }
+
+  /**
+   * Empty this cache, and close all clients that are {@link Closeable}.
+   */
+  public void close() {
+    for (T client : cache.values()) {
+      if (client instanceof Closeable) {
+        try {
+          ((Closeable)client).close();
+        } catch (Exception e) {
+          LOG.warn("Error closing client " + client + ", ignoring...", e);
+        }
+      }
+    }
+    cache.clear();
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/3641497b/solr/core/src/java/org/apache/solr/metrics/reporters/SolrGangliaReporter.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/metrics/reporters/SolrGangliaReporter.java b/solr/core/src/java/org/apache/solr/metrics/reporters/SolrGangliaReporter.java
index 45561e5..142ddd8 100644
--- a/solr/core/src/java/org/apache/solr/metrics/reporters/SolrGangliaReporter.java
+++ b/solr/core/src/java/org/apache/solr/metrics/reporters/SolrGangliaReporter.java
@@ -17,6 +17,9 @@
 package org.apache.solr.metrics.reporters;
 
 import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.TimeUnit;
 
 import com.codahale.metrics.MetricFilter;
@@ -24,21 +27,26 @@ import com.codahale.metrics.ganglia.GangliaReporter;
 import info.ganglia.gmetric4j.gmetric.GMetric;
 import org.apache.solr.metrics.SolrMetricManager;
 import org.apache.solr.metrics.SolrMetricReporter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  *
  */
 public class SolrGangliaReporter extends SolrMetricReporter {
+  private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
   private String host = null;
   private int port = -1;
   private boolean multicast;
   private int period = 60;
   private String instancePrefix = null;
-  private String filterPrefix = null;
+  private List<String> filters = new ArrayList<>();
   private boolean testing;
   private GangliaReporter reporter;
 
+  private static final ReporterClientCache<GMetric> serviceRegistry = new ReporterClientCache<>();
+
   // for unit tests
   GMetric ganglia = null;
 
@@ -65,10 +73,24 @@ public class SolrGangliaReporter extends SolrMetricReporter {
     this.instancePrefix = prefix;
   }
 
-  public void setFilter(String filter) {
-    this.filterPrefix = filter;
+  /**
+   * Report only metrics with names matching any of the prefix filters.
+   * @param filters list of 0 or more prefixes. If the list is empty then
+   *                all names will match.
+   */
+  public void setFilter(List<String> filters) {
+    if (filters == null || filters.isEmpty()) {
+      return;
+    }
+    this.filters.addAll(filters);
   }
 
+  // due to vagaries of SolrPluginUtils.invokeSetters we need this too
+  public void setFilter(String filter) {
+    if (filter != null && !filter.isEmpty()) {
+      this.filters.add(filter);
+    }
+  }
 
   public void setPeriod(int period) {
     this.period = period;
@@ -89,6 +111,10 @@ public class SolrGangliaReporter extends SolrMetricReporter {
 
   @Override
   protected void validate() throws IllegalStateException {
+    if (!enabled) {
+      log.info("Reporter disabled for registry " + registryName);
+      return;
+    }
     if (host == null) {
       throw new IllegalStateException("Init argument 'host' must be set to a valid Ganglia server name.");
     }
@@ -106,12 +132,12 @@ public class SolrGangliaReporter extends SolrMetricReporter {
   //this is a separate method for unit tests
   void start() {
     if (!testing) {
-      try {
-        ganglia = new GMetric(host, port,
-            multicast ? GMetric.UDPAddressingMode.MULTICAST : GMetric.UDPAddressingMode.UNICAST,
-            1);
-      } catch (IOException ioe) {
-        throw new IllegalStateException("Exception connecting to Ganglia", ioe);
+      String id = host + ":" + port + ":" + multicast;
+      ganglia = serviceRegistry.getOrCreate(id, () -> new GMetric(host, port,
+          multicast ? GMetric.UDPAddressingMode.MULTICAST : GMetric.UDPAddressingMode.UNICAST,
+          1));
+      if (ganglia == null) {
+        return;
       }
     }
     if (instancePrefix == null) {
@@ -125,8 +151,8 @@ public class SolrGangliaReporter extends SolrMetricReporter {
         .convertDurationsTo(TimeUnit.MILLISECONDS)
         .prefixedWith(instancePrefix);
     MetricFilter filter;
-    if (filterPrefix != null) {
-      filter = new SolrMetricManager.PrefixFilter(filterPrefix);
+    if (!filters.isEmpty()) {
+      filter = new SolrMetricManager.PrefixFilter(filters);
     } else {
       filter = MetricFilter.ALL;
     }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/3641497b/solr/core/src/java/org/apache/solr/metrics/reporters/SolrGraphiteReporter.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/metrics/reporters/SolrGraphiteReporter.java b/solr/core/src/java/org/apache/solr/metrics/reporters/SolrGraphiteReporter.java
index 8565ce8..d5b7a20 100644
--- a/solr/core/src/java/org/apache/solr/metrics/reporters/SolrGraphiteReporter.java
+++ b/solr/core/src/java/org/apache/solr/metrics/reporters/SolrGraphiteReporter.java
@@ -18,6 +18,8 @@ package org.apache.solr.metrics.reporters;
 
 import java.io.IOException;
 import java.lang.invoke.MethodHandles;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.TimeUnit;
 
 import com.codahale.metrics.MetricFilter;
@@ -41,9 +43,11 @@ public class SolrGraphiteReporter extends SolrMetricReporter {
   private int period = 60;
   private boolean pickled = false;
   private String instancePrefix = null;
-  private String filterPrefix = null;
+  private List<String> filters = new ArrayList<>();
   private GraphiteReporter reporter = null;
 
+  private static final ReporterClientCache<GraphiteSender> serviceRegistry = new ReporterClientCache<>();
+
   /**
    * Create a Graphite reporter for metrics managed in a named registry.
    *
@@ -67,10 +71,25 @@ public class SolrGraphiteReporter extends SolrMetricReporter {
     this.instancePrefix = prefix;
   }
 
+  /**
+   * Report only metrics with names matching any of the prefix filters.
+   * @param filters list of 0 or more prefixes. If the list is empty then
+   *                all names will match.
+   */
+  public void setFilter(List<String> filters) {
+    if (filters == null || filters.isEmpty()) {
+      return;
+    }
+    this.filters.addAll(filters);
+  }
+
   public void setFilter(String filter) {
-    this.filterPrefix = filter;
+    if (filter != null && !filter.isEmpty()) {
+      this.filters.add(filter);
+    }
   }
 
+
   public void setPickled(boolean pickled) {
     this.pickled = pickled;
   }
@@ -81,6 +100,10 @@ public class SolrGraphiteReporter extends SolrMetricReporter {
 
   @Override
   protected void validate() throws IllegalStateException {
+    if (!enabled) {
+      log.info("Reporter disabled for registry " + registryName);
+      return;
+    }
     if (host == null) {
       throw new IllegalStateException("Init argument 'host' must be set to a valid Graphite server name.");
     }
@@ -93,12 +116,15 @@ public class SolrGraphiteReporter extends SolrMetricReporter {
     if (period < 1) {
       throw new IllegalStateException("Init argument 'period' is in time unit 'seconds' and must be at least 1.");
     }
-    final GraphiteSender graphite;
-    if (pickled) {
-      graphite = new PickledGraphite(host, port);
-    } else {
-      graphite = new Graphite(host, port);
-    }
+    GraphiteSender graphite;
+    String id = host + ":" + port + ":" + pickled;
+    graphite = serviceRegistry.getOrCreate(id, () -> {
+      if (pickled) {
+        return new PickledGraphite(host, port);
+      } else {
+        return new Graphite(host, port);
+      }
+    });
     if (instancePrefix == null) {
       instancePrefix = registryName;
     } else {
@@ -110,8 +136,8 @@ public class SolrGraphiteReporter extends SolrMetricReporter {
         .convertRatesTo(TimeUnit.SECONDS)
         .convertDurationsTo(TimeUnit.MILLISECONDS);
     MetricFilter filter;
-    if (filterPrefix != null) {
-      filter = new SolrMetricManager.PrefixFilter(filterPrefix);
+    if (!filters.isEmpty()) {
+      filter = new SolrMetricManager.PrefixFilter(filters);
     } else {
       filter = MetricFilter.ALL;
     }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/3641497b/solr/core/src/java/org/apache/solr/metrics/reporters/SolrJmxReporter.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/metrics/reporters/SolrJmxReporter.java b/solr/core/src/java/org/apache/solr/metrics/reporters/SolrJmxReporter.java
index 0e78eee..e6ce124 100644
--- a/solr/core/src/java/org/apache/solr/metrics/reporters/SolrJmxReporter.java
+++ b/solr/core/src/java/org/apache/solr/metrics/reporters/SolrJmxReporter.java
@@ -18,12 +18,14 @@ package org.apache.solr.metrics.reporters;
 
 import javax.management.MBeanServer;
 
-import java.io.IOException;
 import java.lang.invoke.MethodHandles;
-import java.lang.management.ManagementFactory;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Locale;
 
 import com.codahale.metrics.JmxReporter;
+import com.codahale.metrics.MetricFilter;
+import com.codahale.metrics.MetricRegistry;
 import org.apache.solr.core.PluginInfo;
 import org.apache.solr.metrics.SolrMetricManager;
 import org.apache.solr.metrics.SolrMetricReporter;
@@ -34,16 +36,23 @@ import org.slf4j.LoggerFactory;
 /**
  * A {@link SolrMetricReporter} that finds (or creates) a MBeanServer from
  * the given configuration and registers metrics to it with JMX.
+ * <p>NOTE: {@link JmxReporter} that this class uses exports only newly added metrics (it doesn't
+ * process already existing metrics in a registry)</p>
  */
 public class SolrJmxReporter extends SolrMetricReporter {
 
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
+  private static final ReporterClientCache<MBeanServer> serviceRegistry = new ReporterClientCache<>();
+
   private String domain;
   private String agentId;
   private String serviceUrl;
+  private String rootName;
+  private List<String> filters = new ArrayList<>();
 
   private JmxReporter reporter;
+  private MetricRegistry registry;
   private MBeanServer mBeanServer;
 
   /**
@@ -57,7 +66,7 @@ public class SolrJmxReporter extends SolrMetricReporter {
   }
 
   /**
-   * Initializes the reporter by finding (or creating) a MBeanServer
+   * Initializes the reporter by finding an MBeanServer
    * and registering the metricManager's metric registry.
    *
    * @param pluginInfo the configuration for the reporter
@@ -65,44 +74,56 @@ public class SolrJmxReporter extends SolrMetricReporter {
   @Override
   public synchronized void init(PluginInfo pluginInfo) {
     super.init(pluginInfo);
-
+    if (!enabled) {
+      log.info("Reporter disabled for registry " + registryName);
+      return;
+    }
+    log.debug("Initializing for registry " + registryName);
     if (serviceUrl != null && agentId != null) {
-      ManagementFactory.getPlatformMBeanServer(); // Ensure at least one MBeanServer is available.
       mBeanServer = JmxUtil.findFirstMBeanServer();
-      log.warn("No more than one of serviceUrl(%s) and agentId(%s) should be configured, using first MBeanServer instead of configuration.",
+      log.warn("No more than one of serviceUrl({}) and agentId({}) should be configured, using first MBeanServer instead of configuration.",
           serviceUrl, agentId, mBeanServer);
-    }
-    else if (serviceUrl != null) {
-      try {
-        mBeanServer = JmxUtil.findMBeanServerForServiceUrl(serviceUrl);
-      } catch (IOException e) {
-        log.warn("findMBeanServerForServiceUrl(%s) exception: %s", serviceUrl, e);
-        mBeanServer = null;
-      }
-    }
-    else if (agentId != null) {
+    } else if (serviceUrl != null) {
+      // reuse existing services
+      mBeanServer = serviceRegistry.getOrCreate(serviceUrl, () -> JmxUtil.findMBeanServerForServiceUrl(serviceUrl));
+    } else if (agentId != null) {
       mBeanServer = JmxUtil.findMBeanServerForAgentId(agentId);
     } else {
-      ManagementFactory.getPlatformMBeanServer(); // Ensure at least one MBeanServer is available.
       mBeanServer = JmxUtil.findFirstMBeanServer();
-      log.warn("No serviceUrl or agentId was configured, using first MBeanServer.", mBeanServer);
+      log.debug("No serviceUrl or agentId was configured, using first MBeanServer: " + mBeanServer);
     }
 
     if (mBeanServer == null) {
-      log.warn("No JMX server found. Not exposing Solr metrics.");
+      log.warn("No JMX server found. Not exposing Solr metrics via JMX.");
       return;
     }
 
-    JmxObjectNameFactory jmxObjectNameFactory = new JmxObjectNameFactory(pluginInfo.name, domain);
+    if (domain == null || domain.isEmpty()) {
+      domain = registryName;
+    }
+    String fullDomain = domain;
+    if (rootName != null && !rootName.isEmpty()) {
+      fullDomain = rootName + "." + domain;
+    }
+    JmxObjectNameFactory jmxObjectNameFactory = new JmxObjectNameFactory(pluginInfo.name, fullDomain);
+    registry = metricManager.registry(registryName);
+    MetricFilter filter;
+    if (filters.isEmpty()) {
+      filter = MetricFilter.ALL;
+    } else {
+      // apply also prefix filters
+      filter = new SolrMetricManager.PrefixFilter(filters);
+    }
 
-    reporter = JmxReporter.forRegistry(metricManager.registry(registryName))
+    reporter = JmxReporter.forRegistry(registry)
                           .registerWith(mBeanServer)
-                          .inDomain(domain)
+                          .inDomain(fullDomain)
+                          .filter(filter)
                           .createsObjectNamesWith(jmxObjectNameFactory)
                           .build();
     reporter.start();
 
-    log.info("JMX monitoring enabled at server: " + mBeanServer);
+    log.info("JMX monitoring for '" + fullDomain + "' (registry '" + registryName + "') enabled at server: " + mBeanServer);
   }
 
   /**
@@ -127,9 +148,19 @@ public class SolrJmxReporter extends SolrMetricReporter {
     // Nothing to validate
   }
 
+
+  /**
+   * Set root name of the JMX hierarchy for this reporter. Default (null or empty) is none, ie.
+   * the hierarchy will start from the domain name.
+   * @param rootName root name of the JMX name hierarchy, or null or empty for default.
+   */
+  public void setRootName(String rootName) {
+    this.rootName = rootName;
+  }
+
   /**
    * Sets the domain with which MBeans are published. If none is set,
-   * the domain defaults to the name of the core.
+   * the domain defaults to the name of the registry.
    *
    * @param domain the domain
    */
@@ -162,7 +193,46 @@ public class SolrJmxReporter extends SolrMetricReporter {
   }
 
   /**
-   * Retrieves the reporter's MBeanServer.
+   * Return configured agentId or null.
+   */
+  public String getAgentId() {
+    return agentId;
+  }
+
+  /**
+   * Return configured serviceUrl or null.
+   */
+  public String getServiceUrl() {
+    return serviceUrl;
+  }
+
+  /**
+   * Return configured domain or null.
+   */
+  public String getDomain() {
+    return domain;
+  }
+
+  /**
+   * Report only metrics with names matching any of the prefix filters.
+   * @param filters list of 0 or more prefixes. If the list is empty then
+   *                all names will match.
+   */
+  public void setFilter(List<String> filters) {
+    if (filters == null || filters.isEmpty()) {
+      return;
+    }
+    this.filters.addAll(filters);
+  }
+
+  public void setFilter(String filter) {
+    if (filter != null && !filter.isEmpty()) {
+      this.filters.add(filter);
+    }
+  }
+
+  /**
+   * Return the reporter's MBeanServer.
    *
    * @return the reporter's MBeanServer
    */
@@ -170,10 +240,18 @@ public class SolrJmxReporter extends SolrMetricReporter {
     return mBeanServer;
   }
 
+  /**
+   * For unit tests.
+   * @return true if this reporter is actively reporting metrics to JMX.
+   */
+  public boolean isActive() {
+    return reporter != null;
+  }
+
   @Override
   public String toString() {
-    return String.format(Locale.ENGLISH, "[%s@%s: domain = %s, service url = %s, agent id = %s]",
-        getClass().getName(), Integer.toHexString(hashCode()), domain, serviceUrl, agentId);
+    return String.format(Locale.ENGLISH, "[%s@%s: rootName = %s, domain = %s, service url = %s, agent id = %s]",
+        getClass().getName(), Integer.toHexString(hashCode()), rootName, domain, serviceUrl, agentId);
   }
 
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/3641497b/solr/core/src/java/org/apache/solr/metrics/reporters/SolrSlf4jReporter.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/metrics/reporters/SolrSlf4jReporter.java b/solr/core/src/java/org/apache/solr/metrics/reporters/SolrSlf4jReporter.java
index 817dda1..c5dbc00 100644
--- a/solr/core/src/java/org/apache/solr/metrics/reporters/SolrSlf4jReporter.java
+++ b/solr/core/src/java/org/apache/solr/metrics/reporters/SolrSlf4jReporter.java
@@ -18,6 +18,8 @@ package org.apache.solr.metrics.reporters;
 
 import java.io.IOException;
 import java.lang.invoke.MethodHandles;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.TimeUnit;
 
 import com.codahale.metrics.MetricFilter;
@@ -47,7 +49,7 @@ public class SolrSlf4jReporter extends SolrMetricReporter {
   private int period = 60;
   private String instancePrefix = null;
   private String logger = null;
-  private String filterPrefix = null;
+  private List<String> filters = new ArrayList<>();
   private Slf4jReporter reporter;
 
   /**
@@ -65,8 +67,22 @@ public class SolrSlf4jReporter extends SolrMetricReporter {
     this.instancePrefix = prefix;
   }
 
+  /**
+   * Report only metrics with names matching any of the prefix filters.
+   * @param filters list of 0 or more prefixes. If the list is empty then
+   *                all names will match.
+   */
+  public void setFilter(List<String> filters) {
+    if (filters == null || filters.isEmpty()) {
+      return;
+    }
+    this.filters.addAll(filters);
+  }
+
   public void setFilter(String filter) {
-    this.filterPrefix = filter;
+    if (filter != null && !filter.isEmpty()) {
+      this.filters.add(filter);
+    }
   }
 
   public void setLogger(String logger) {
@@ -79,6 +95,10 @@ public class SolrSlf4jReporter extends SolrMetricReporter {
 
   @Override
   protected void validate() throws IllegalStateException {
+    if (!enabled) {
+      log.info("Reporter disabled for registry " + registryName);
+      return;
+    }
     if (period < 1) {
       throw new IllegalStateException("Init argument 'period' is in time unit 'seconds' and must be at least 1.");
     }
@@ -93,8 +113,8 @@ public class SolrSlf4jReporter extends SolrMetricReporter {
         .convertDurationsTo(TimeUnit.MILLISECONDS);
 
     MetricFilter filter;
-    if (filterPrefix != null) {
-      filter = new SolrMetricManager.PrefixFilter(filterPrefix);
+    if (!filters.isEmpty()) {
+      filter = new SolrMetricManager.PrefixFilter(filters);
     } else {
       filter = MetricFilter.ALL;
     }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/3641497b/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java b/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java
index ff0db9b..98656af 100644
--- a/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java
+++ b/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java
@@ -16,7 +16,6 @@
  */
 package org.apache.solr.servlet;
 
-import javax.management.MBeanServer;
 import javax.servlet.FilterChain;
 import javax.servlet.FilterConfig;
 import javax.servlet.ServletException;
@@ -34,7 +33,6 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.lang.invoke.MethodHandles;
-import java.lang.management.ManagementFactory;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.time.Instant;
@@ -47,7 +45,6 @@ import java.util.concurrent.atomic.AtomicReference;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import com.codahale.metrics.jvm.BufferPoolMetricSet;
 import com.codahale.metrics.jvm.ClassLoadingGaugeSet;
 import com.codahale.metrics.jvm.GarbageCollectorMetricSet;
 import com.codahale.metrics.jvm.MemoryUsageGaugeSet;
@@ -69,6 +66,7 @@ import org.apache.solr.core.SolrCore;
 import org.apache.solr.core.SolrInfoMBean;
 import org.apache.solr.core.SolrResourceLoader;
 import org.apache.solr.core.SolrXmlConfig;
+import org.apache.solr.metrics.AltBufferPoolMetricSet;
 import org.apache.solr.metrics.OperatingSystemMetricSet;
 import org.apache.solr.metrics.SolrMetricManager;
 import org.apache.solr.request.SolrRequestInfo;
@@ -185,13 +183,12 @@ public class SolrDispatchFilter extends BaseSolrFilter {
   }
 
   private void setupJvmMetrics()  {
-    MBeanServer platformMBeanServer = ManagementFactory.getPlatformMBeanServer();
     SolrMetricManager metricManager = cores.getMetricManager();
     try {
       String registry = SolrMetricManager.getRegistryName(SolrInfoMBean.Group.jvm);
-      metricManager.registerAll(registry, new BufferPoolMetricSet(platformMBeanServer), true, "buffers");
+      metricManager.registerAll(registry, new AltBufferPoolMetricSet(), true, "buffers");
       metricManager.registerAll(registry, new ClassLoadingGaugeSet(), true, "classes");
-      metricManager.registerAll(registry, new OperatingSystemMetricSet(platformMBeanServer), true, "os");
+      metricManager.registerAll(registry, new OperatingSystemMetricSet(), true, "os");
       metricManager.registerAll(registry, new GarbageCollectorMetricSet(), true, "gc");
       metricManager.registerAll(registry, new MemoryUsageGaugeSet(), true, "memory");
       metricManager.registerAll(registry, new ThreadStatesGaugeSet(), true, "threads"); // todo should we use CachedThreadStatesGaugeSet instead?

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/3641497b/solr/core/src/java/org/apache/solr/util/stats/MetricUtils.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/util/stats/MetricUtils.java b/solr/core/src/java/org/apache/solr/util/stats/MetricUtils.java
index dbb4ff4..2f32e49 100644
--- a/solr/core/src/java/org/apache/solr/util/stats/MetricUtils.java
+++ b/solr/core/src/java/org/apache/solr/util/stats/MetricUtils.java
@@ -16,7 +16,14 @@
  */
 package org.apache.solr.util.stats;
 
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
 import java.lang.invoke.MethodHandles;
+import java.lang.management.OperatingSystemMXBean;
+import java.lang.management.PlatformManagedObject;
+import java.lang.reflect.InvocationTargetException;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -248,4 +255,79 @@ public class MetricUtils {
   public static ExecutorService instrumentedExecutorService(ExecutorService delegate, MetricRegistry metricRegistry, String scope)  {
     return new InstrumentedExecutorService(delegate, metricRegistry, scope);
   }
+
+  /**
+   * Creates a set of metrics (gauges) that correspond to available bean properties for the provided MXBean.
+   * @param obj an instance of MXBean
+   * @param intf MXBean interface, one of {@link PlatformManagedObject}-s
+   * @param consumer consumer for created names and metrics
+   * @param <T> formal type
+   */
+  public static <T extends PlatformManagedObject> void addMXBeanMetrics(T obj, Class<? extends T> intf,
+      String prefix, BiConsumer<String, Metric> consumer) {
+    if (intf.isInstance(obj)) {
+      BeanInfo beanInfo;
+      try {
+        beanInfo = Introspector.getBeanInfo(intf, intf.getSuperclass(), Introspector.IGNORE_ALL_BEANINFO);
+      } catch (IntrospectionException e) {
+        LOG.warn("Unable to fetch properties of MXBean " + obj.getClass().getName());
+        return;
+      }
+      for (final PropertyDescriptor desc : beanInfo.getPropertyDescriptors()) {
+        final String name = desc.getName();
+        // test if it works at all
+        try {
+          desc.getReadMethod().invoke(obj);
+          // worked - consume it
+          final Gauge<?> gauge = () -> {
+            try {
+              return desc.getReadMethod().invoke(obj);
+            } catch (InvocationTargetException ite) {
+              // ignore (some properties throw UOE)
+              return null;
+            } catch (IllegalAccessException e) {
+              return null;
+            }
+          };
+          String metricName = MetricRegistry.name(prefix, name);
+          consumer.accept(metricName, gauge);
+        } catch (Exception e) {
+          // didn't work, skip it...
+        }
+      }
+    }
+  }
+
+  /**
+   * These are well-known implementations of {@link java.lang.management.OperatingSystemMXBean}.
+   * Some of them provide additional useful properties beyond those declared by the interface.
+   */
+  public static String[] OS_MXBEAN_CLASSES = new String[] {
+      OperatingSystemMXBean.class.getName(),
+      "com.sun.management.OperatingSystemMXBean",
+      "com.sun.management.UnixOperatingSystemMXBean",
+      "com.ibm.lang.management.OperatingSystemMXBean"
+  };
+
+  /**
+   * Creates a set of metrics (gauges) that correspond to available bean properties for the provided MXBean.
+   * @param obj an instance of MXBean
+   * @param interfaces interfaces that it may implement. Each interface will be tried in turn, and only
+   *                   if it exists and if it contains unique properties then they will be added as metrics.
+   * @param prefix optional prefix for metric names
+   * @param consumer consumer for created names and metrics
+   * @param <T> formal type
+   */
+  public static <T extends PlatformManagedObject> void addMXBeanMetrics(T obj, String[] interfaces,
+      String prefix, BiConsumer<String, Metric> consumer) {
+    for (String clazz : interfaces) {
+      try {
+        final Class<? extends PlatformManagedObject> intf = Class.forName(clazz)
+            .asSubclass(PlatformManagedObject.class);
+        MetricUtils.addMXBeanMetrics(obj, intf, null, consumer);
+      } catch (ClassNotFoundException e) {
+        // ignore
+      }
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/3641497b/solr/core/src/test/org/apache/solr/cloud/ReplicationFactorTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/cloud/ReplicationFactorTest.java b/solr/core/src/test/org/apache/solr/cloud/ReplicationFactorTest.java
index 9441e3f..9100eee 100644
--- a/solr/core/src/test/org/apache/solr/cloud/ReplicationFactorTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/ReplicationFactorTest.java
@@ -18,7 +18,6 @@ package org.apache.solr.cloud;
 
 import java.io.File;
 import java.lang.invoke.MethodHandles;
-import java.net.ServerSocket;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
@@ -71,14 +70,6 @@ public class ReplicationFactorTest extends AbstractFullDistribZkTestBase {
     return createProxiedJetty(solrHome, dataDir, shardList, solrConfigOverride, schemaOverride);
   }
   
-  protected int getNextAvailablePort() throws Exception {    
-    int port = -1;
-    try (ServerSocket s = new ServerSocket(0)) {
-      port = s.getLocalPort();
-    }
-    return port;
-  }
-
   @Test
   public void test() throws Exception {
     log.info("replication factor test running");

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/3641497b/solr/core/src/test/org/apache/solr/handler/admin/MetricsHandlerTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/handler/admin/MetricsHandlerTest.java b/solr/core/src/test/org/apache/solr/handler/admin/MetricsHandlerTest.java
index 2f84997..1a209be 100644
--- a/solr/core/src/test/org/apache/solr/handler/admin/MetricsHandlerTest.java
+++ b/solr/core/src/test/org/apache/solr/handler/admin/MetricsHandlerTest.java
@@ -17,6 +17,7 @@
 
 package org.apache.solr.handler.admin;
 
+import java.lang.management.ManagementFactory;
 import java.util.Map;
 
 import org.apache.solr.SolrTestCaseJ4;
@@ -33,6 +34,11 @@ import org.junit.Test;
 public class MetricsHandlerTest extends SolrTestCaseJ4 {
   @BeforeClass
   public static void beforeClass() throws Exception {
+    // this is needed to enable default SolrJmxReporter in TestHarness
+    // which is then initialized for solr.jetty and solr.jvm so that they
+    // show up in MetricsHandler output
+    ManagementFactory.getPlatformMBeanServer();
+
     initCore("solrconfig.xml", "schema.xml");
   }
 
@@ -136,6 +142,17 @@ public class MetricsHandlerTest extends SolrTestCaseJ4 {
     assertNotNull(values.get("CONTAINER.threadPool.coreLoadExecutor.completed"));
 
     resp = new SolrQueryResponse();
+    handler.handleRequestBody(req(CommonParams.QT, "/admin/metrics", CommonParams.WT, "json", "prefix", "CONTAINER.cores", "regex", "C.*thread.*completed"), resp);
+    values = resp.getValues();
+    assertNotNull(values.get("metrics"));
+    values = (NamedList) values.get("metrics");
+    assertNotNull(values.get("solr.node"));
+    values = (NamedList) values.get("solr.node");
+    assertEquals(5, values.size());
+    assertNotNull(values.get("CONTAINER.threadPool.coreContainerWorkExecutor.completed"));
+    assertNotNull(values.get("CONTAINER.threadPool.coreLoadExecutor.completed"));
+
+    resp = new SolrQueryResponse();
     handler.handleRequestBody(req(CommonParams.QT, "/admin/metrics", CommonParams.WT, "json", "group", "jvm", "prefix", "CONTAINER.cores"), resp);
     values = resp.getValues();
     assertNotNull(values.get("metrics"));

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/3641497b/solr/core/src/test/org/apache/solr/handler/admin/SystemInfoHandlerTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/handler/admin/SystemInfoHandlerTest.java b/solr/core/src/test/org/apache/solr/handler/admin/SystemInfoHandlerTest.java
index c961a55..2e20dc8 100644
--- a/solr/core/src/test/org/apache/solr/handler/admin/SystemInfoHandlerTest.java
+++ b/solr/core/src/test/org/apache/solr/handler/admin/SystemInfoHandlerTest.java
@@ -20,8 +20,10 @@ import java.lang.management.ManagementFactory;
 import java.lang.management.OperatingSystemMXBean;
 import java.util.Arrays;
 
+import com.codahale.metrics.Gauge;
 import org.apache.lucene.util.LuceneTestCase;
 import org.apache.solr.common.util.SimpleOrderedMap;
+import org.apache.solr.util.stats.MetricUtils;
 
 
 public class SystemInfoHandlerTest extends LuceneTestCase {
@@ -36,9 +38,11 @@ public class SystemInfoHandlerTest extends LuceneTestCase {
     info.add( "version", os.getVersion() );
     info.add( "arch", os.getArch() );
 
-    // make another using addMXBeanProperties() 
+    // make another using MetricUtils.addMXBeanMetrics()
     SimpleOrderedMap<Object> info2 = new SimpleOrderedMap<>();
-    SystemInfoHandler.addMXBeanProperties( os, OperatingSystemMXBean.class, info2 );
+    MetricUtils.addMXBeanMetrics( os, OperatingSystemMXBean.class, null, (k, v) -> {
+      info2.add(k, ((Gauge)v).getValue());
+    } );
 
     // make sure they got the same thing
     for (String p : Arrays.asList("name", "version", "arch")) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/3641497b/solr/core/src/test/org/apache/solr/metrics/JvmMetricsTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/metrics/JvmMetricsTest.java b/solr/core/src/test/org/apache/solr/metrics/JvmMetricsTest.java
index 72adc68..a656f84 100644
--- a/solr/core/src/test/org/apache/solr/metrics/JvmMetricsTest.java
+++ b/solr/core/src/test/org/apache/solr/metrics/JvmMetricsTest.java
@@ -16,8 +16,6 @@
  */
 package org.apache.solr.metrics;
 
-import javax.management.MBeanServer;
-import java.lang.management.ManagementFactory;
 import java.util.Map;
 
 import com.codahale.metrics.Gauge;
@@ -31,26 +29,63 @@ import org.junit.Test;
  */
 public class JvmMetricsTest extends SolrJettyTestBase {
 
+  static final String[] STRING_OS_METRICS = {
+      "arch",
+      "name",
+      "version"
+  };
+  static final String[] NUMERIC_OS_METRICS = {
+      "availableProcessors",
+      "systemLoadAverage"
+  };
+
+  static final String[] BUFFER_METRICS = {
+      "direct.Count",
+      "direct.MemoryUsed",
+      "direct.TotalCapacity",
+      "mapped.Count",
+      "mapped.MemoryUsed",
+      "mapped.TotalCapacity"
+  };
+
   @BeforeClass
   public static void beforeTest() throws Exception {
     createJetty(legacyExampleCollection1SolrHome());
   }
 
   @Test
-  public void testOperatingSystemMetricsSet() throws Exception {
-    MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
-    OperatingSystemMetricSet set = new OperatingSystemMetricSet(mBeanServer);
+  public void testOperatingSystemMetricSet() throws Exception {
+    OperatingSystemMetricSet set = new OperatingSystemMetricSet();
     Map<String, Metric> metrics = set.getMetrics();
     assertTrue(metrics.size() > 0);
-    for (String metric : OperatingSystemMetricSet.METRICS) {
+    for (String metric : NUMERIC_OS_METRICS) {
       Gauge<?> gauge = (Gauge<?>)metrics.get(metric);
-      if (gauge == null || gauge.getValue() == null) { // some are optional depending on OS
-        continue;
-      }
+      assertNotNull(metric, gauge);
       double value = ((Number)gauge.getValue()).doubleValue();
       // SystemLoadAverage on Windows may be -1.0
       assertTrue("unexpected value of " + metric + ": " + value, value >= 0 || value == -1.0);
     }
+    for (String metric : STRING_OS_METRICS) {
+      Gauge<?> gauge = (Gauge<?>)metrics.get(metric);
+      assertNotNull(metric, gauge);
+      String value = (String)gauge.getValue();
+      assertNotNull(value);
+      assertFalse(value.isEmpty());
+    }
+  }
+
+  @Test
+  public void testAltBufferPoolMetricSet() throws Exception {
+    AltBufferPoolMetricSet set = new AltBufferPoolMetricSet();
+    Map<String, Metric> metrics = set.getMetrics();
+    assertTrue(metrics.size() > 0);
+    for (String name : BUFFER_METRICS) {
+      assertNotNull(name, metrics.get(name));
+      Object g = metrics.get(name);
+      assertTrue(g instanceof Gauge);
+      Object v = ((Gauge)g).getValue();
+      assertTrue(v instanceof Long);
+    }
   }
 
   @Test

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/3641497b/solr/core/src/test/org/apache/solr/metrics/SolrMetricReporterTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/metrics/SolrMetricReporterTest.java b/solr/core/src/test/org/apache/solr/metrics/SolrMetricReporterTest.java
index b275919..f3359cc 100644
--- a/solr/core/src/test/org/apache/solr/metrics/SolrMetricReporterTest.java
+++ b/solr/core/src/test/org/apache/solr/metrics/SolrMetricReporterTest.java
@@ -42,6 +42,7 @@ public class SolrMetricReporterTest extends LuceneTestCase {
     Map<String, Object> attrs = new HashMap<>();
     attrs.put(FieldType.CLASS_NAME, MockMetricReporter.class.getName());
     attrs.put(CoreAdminParams.NAME, TestUtil.randomUnicodeString(random));
+    attrs.put("enabled", random.nextBoolean());
 
     boolean shouldDefineConfigurable = random.nextBoolean();
     String configurable = TestUtil.randomUnicodeString(random);

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/3641497b/solr/core/src/test/org/apache/solr/metrics/reporters/SolrGangliaReporterTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/metrics/reporters/SolrGangliaReporterTest.java b/solr/core/src/test/org/apache/solr/metrics/reporters/SolrGangliaReporterTest.java
index c50ff3c..eca414c 100644
--- a/solr/core/src/test/org/apache/solr/metrics/reporters/SolrGangliaReporterTest.java
+++ b/solr/core/src/test/org/apache/solr/metrics/reporters/SolrGangliaReporterTest.java
@@ -64,7 +64,7 @@ public class SolrGangliaReporterTest extends SolrTestCaseJ4 {
     h.coreName = DEFAULT_TEST_CORENAME;
     SolrMetricManager metricManager = cc.getMetricManager();
     Map<String, SolrMetricReporter> reporters = metricManager.getReporters("solr.node");
-    assertEquals(1, reporters.size());
+    assertTrue(reporters.toString(), reporters.size() >= 1);
     SolrMetricReporter reporter = reporters.get("test");
     assertNotNull(reporter);
     assertTrue(reporter instanceof SolrGangliaReporter);

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/3641497b/solr/core/src/test/org/apache/solr/metrics/reporters/SolrJmxReporterTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/metrics/reporters/SolrJmxReporterTest.java b/solr/core/src/test/org/apache/solr/metrics/reporters/SolrJmxReporterTest.java
index 333876f..5d69714 100644
--- a/solr/core/src/test/org/apache/solr/metrics/reporters/SolrJmxReporterTest.java
+++ b/solr/core/src/test/org/apache/solr/metrics/reporters/SolrJmxReporterTest.java
@@ -20,6 +20,7 @@ import javax.management.MBeanServer;
 import javax.management.ObjectInstance;
 import javax.management.ObjectName;
 
+import java.rmi.registry.LocateRegistry;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Random;
@@ -40,12 +41,15 @@ import org.apache.solr.metrics.SolrMetricTestUtils;
 import org.apache.solr.schema.FieldType;
 import org.junit.After;
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
 public class SolrJmxReporterTest extends SolrTestCaseJ4 {
 
   private static final int MAX_ITERATIONS = 20;
 
+  private static int jmxPort;
+
   private String domain;
 
   private SolrCoreMetricManager coreMetricManager;
@@ -53,6 +57,14 @@ public class SolrJmxReporterTest extends SolrTestCaseJ4 {
   private SolrJmxReporter reporter;
   private MBeanServer mBeanServer;
   private String reporterName;
+  private String rootName;
+
+  @BeforeClass
+  public static void init() throws Exception {
+    jmxPort = getNextAvailablePort();
+    assertFalse(jmxPort == -1);
+    LocateRegistry.createRegistry(jmxPort);
+  }
 
   @Before
   public void beforeTest() throws Exception {
@@ -60,11 +72,13 @@ public class SolrJmxReporterTest extends SolrTestCaseJ4 {
 
     final SolrCore core = h.getCore();
     domain = core.getName();
+    rootName = TestUtil.randomSimpleString(random(), 1, 10);
 
     coreMetricManager = core.getCoreMetricManager();
     metricManager = core.getCoreContainer().getMetricManager();
-    PluginInfo pluginInfo = createReporterPluginInfo();
-    metricManager.loadReporter(coreMetricManager.getRegistryName(), coreMetricManager.getCore().getResourceLoader(), pluginInfo);
+    PluginInfo pluginInfo = createReporterPluginInfo(rootName, true);
+    metricManager.loadReporter(coreMetricManager.getRegistryName(), coreMetricManager.getCore().getResourceLoader(),
+        pluginInfo);
 
     Map<String, SolrMetricReporter> reporters = metricManager.getReporters(coreMetricManager.getRegistryName());
     assertTrue("reporters.size should be > 0, but was + " + reporters.size(), reporters.size() > 0);
@@ -77,7 +91,7 @@ public class SolrJmxReporterTest extends SolrTestCaseJ4 {
     assertNotNull("MBean server not found.", mBeanServer);
   }
 
-  private PluginInfo createReporterPluginInfo() {
+  private PluginInfo createReporterPluginInfo(String rootName, boolean enabled) {
     Random random = random();
     String className = SolrJmxReporter.class.getName();
     String reporterName = TestUtil.randomSimpleString(random, 1, 10);
@@ -85,6 +99,9 @@ public class SolrJmxReporterTest extends SolrTestCaseJ4 {
     Map<String, Object> attrs = new HashMap<>();
     attrs.put(FieldType.CLASS_NAME, className);
     attrs.put(CoreAdminParams.NAME, reporterName);
+    attrs.put("rootName", rootName);
+    attrs.put("enabled", enabled);
+    attrs.put("serviceUrl", "service:jmx:rmi:///jndi/rmi://localhost:" + jmxPort + "/solrjmx");
 
     boolean shouldOverrideDomain = random.nextBoolean();
     if (shouldOverrideDomain) {
@@ -143,8 +160,9 @@ public class SolrJmxReporterTest extends SolrTestCaseJ4 {
             reporterName.equals(o.getObjectName().getKeyProperty("reporter"))).count());
 
     h.getCoreContainer().reload(h.getCore().getName());
-    PluginInfo pluginInfo = createReporterPluginInfo();
-    metricManager.loadReporter(coreMetricManager.getRegistryName(), coreMetricManager.getCore().getResourceLoader(), pluginInfo);
+    PluginInfo pluginInfo = createReporterPluginInfo(rootName, true);
+    metricManager.loadReporter(coreMetricManager.getRegistryName(), coreMetricManager.getCore().getResourceLoader(),
+        pluginInfo);
     coreMetricManager.registerMetricProducer(scope, producer);
 
     objects = mBeanServer.queryMBeans(null, null);
@@ -153,4 +171,34 @@ public class SolrJmxReporterTest extends SolrTestCaseJ4 {
             pluginInfo.name.equals(o.getObjectName().getKeyProperty("reporter"))).count());
   }
 
+  @Test
+  public void testEnabled() throws Exception {
+    String root1 = TestUtil.randomSimpleString(random(), 1, 10);
+    PluginInfo pluginInfo1 = createReporterPluginInfo(root1, true);
+    metricManager.loadReporter(coreMetricManager.getRegistryName(), coreMetricManager.getCore().getResourceLoader(),
+        pluginInfo1);
+
+    String root2 = TestUtil.randomSimpleString(random(), 1, 10);
+    assertFalse(root2.equals(root1));
+    PluginInfo pluginInfo2 = createReporterPluginInfo(root2, false);
+    metricManager.loadReporter(coreMetricManager.getRegistryName(), coreMetricManager.getCore().getResourceLoader(),
+        pluginInfo2);
+
+    Map<String, SolrMetricReporter> reporters = metricManager.getReporters(coreMetricManager.getRegistryName());
+    assertTrue(reporters.containsKey(pluginInfo1.name));
+    assertTrue(reporters.containsKey(pluginInfo2.name));
+
+    String scope = SolrMetricTestUtils.getRandomScope(random(), true);
+    SolrInfoMBean.Category category = SolrMetricTestUtils.getRandomCategory(random(), true);
+    Map<String, Counter> metrics = SolrMetricTestUtils.getRandomMetrics(random(), true);
+    SolrMetricProducer producer = SolrMetricTestUtils.getProducerOf(metricManager, category, scope, metrics);
+    coreMetricManager.registerMetricProducer(scope, producer);
+    Set<ObjectInstance> objects = mBeanServer.queryMBeans(null, null);
+    assertEquals(metrics.size(), objects.stream().
+        filter(o -> scope.equals(o.getObjectName().getKeyProperty("scope")) &&
+            root1.equals(o.getObjectName().getDomain())).count());
+    assertEquals(0, objects.stream().
+        filter(o -> scope.equals(o.getObjectName().getKeyProperty("scope")) &&
+            root2.equals(o.getObjectName().getDomain())).count());
+  }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/3641497b/solr/core/src/test/org/apache/solr/metrics/reporters/SolrSlf4jReporterTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/metrics/reporters/SolrSlf4jReporterTest.java b/solr/core/src/test/org/apache/solr/metrics/reporters/SolrSlf4jReporterTest.java
index 47bf8e7..a8f3343 100644
--- a/solr/core/src/test/org/apache/solr/metrics/reporters/SolrSlf4jReporterTest.java
+++ b/solr/core/src/test/org/apache/solr/metrics/reporters/SolrSlf4jReporterTest.java
@@ -57,7 +57,7 @@ public class SolrSlf4jReporterTest extends SolrTestCaseJ4 {
     h.coreName = DEFAULT_TEST_CORENAME;
     SolrMetricManager metricManager = cc.getMetricManager();
     Map<String, SolrMetricReporter> reporters = metricManager.getReporters("solr.node");
-    assertEquals(2, reporters.size());
+    assertTrue(reporters.toString(), reporters.size() >= 2);
     SolrMetricReporter reporter = reporters.get("test1");
     assertNotNull(reporter);
     assertTrue(reporter instanceof SolrSlf4jReporter);

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/3641497b/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java
----------------------------------------------------------------------
diff --git a/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java b/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java
index edf28df..ed6a115 100644
--- a/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java
+++ b/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java
@@ -33,6 +33,7 @@ import java.lang.annotation.Target;
 import java.lang.invoke.MethodHandles;
 import java.lang.reflect.Method;
 import java.net.MalformedURLException;
+import java.net.ServerSocket;
 import java.net.URL;
 import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
@@ -801,6 +802,19 @@ public abstract class SolrTestCaseJ4 extends LuceneTestCase {
     configString = schemaString = null;
   }
 
+  /**
+   * Find next available local port.
+   * @return available port number or -1 if none could be found
+   * @throws Exception on IO errors
+   */
+  protected static int getNextAvailablePort() throws Exception {
+    int port = -1;
+    try (ServerSocket s = new ServerSocket(0)) {
+      port = s.getLocalPort();
+    }
+    return port;
+  }
+
 
   /** Validates an update XML String is successful
    */

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/3641497b/solr/test-framework/src/java/org/apache/solr/cloud/AbstractFullDistribZkTestBase.java
----------------------------------------------------------------------
diff --git a/solr/test-framework/src/java/org/apache/solr/cloud/AbstractFullDistribZkTestBase.java b/solr/test-framework/src/java/org/apache/solr/cloud/AbstractFullDistribZkTestBase.java
index 2a8d453..8dba82d 100644
--- a/solr/test-framework/src/java/org/apache/solr/cloud/AbstractFullDistribZkTestBase.java
+++ b/solr/test-framework/src/java/org/apache/solr/cloud/AbstractFullDistribZkTestBase.java
@@ -19,7 +19,6 @@ package org.apache.solr.cloud;
 import java.io.File;
 import java.io.IOException;
 import java.lang.invoke.MethodHandles;
-import java.net.ServerSocket;
 import java.net.URI;
 import java.net.URL;
 import java.nio.file.Path;
@@ -593,14 +592,6 @@ public abstract class AbstractFullDistribZkTestBase extends AbstractDistribZkTes
     return proxy;
   }
 
-  protected int getNextAvailablePort() throws Exception {
-    int port = -1;
-    try (ServerSocket s = new ServerSocket(0)) {
-      port = s.getLocalPort();
-    }
-    return port;
-  }
-
   private File getRelativeSolrHomePath(File solrHome) {
     final Path solrHomePath = solrHome.toPath();
     final Path curDirPath = new File("").getAbsoluteFile().toPath();