ProcMetricModule: Use reflection to avoid direct reference to com.sun classes

The current metric module is dependent on com.sun internal classes that
are not available in the JRE from other providers such as IBM, resulting
in ClassNotFound exceptions when initializing the CPU usage metric.

Refactor the initialization to dynamically cast to the first available
class, either the one from Sun or the one from IBM.

Bug: Issue 4637
Change-Id: Ib14f001dfe6a144853032a0c6a3e249b59daf9de
diff --git a/gerrit-server/src/main/java/com/google/gerrit/metrics/proc/OperatingSystemMXBeanProvider.java b/gerrit-server/src/main/java/com/google/gerrit/metrics/proc/OperatingSystemMXBeanProvider.java
new file mode 100644
index 0000000..696a687
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/metrics/proc/OperatingSystemMXBeanProvider.java
@@ -0,0 +1,81 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed 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 com.google.gerrit.metrics.proc;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.OperatingSystemMXBean;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class OperatingSystemMXBeanProvider {
+  private static final Logger log =
+      LoggerFactory.getLogger(OperatingSystemMXBeanProvider.class);
+
+  private final OperatingSystemMXBean sys;
+  private final Method getProcessCpuTime;
+  private final Method getOpenFileDescriptorCount;
+
+  static class Factory {
+    static OperatingSystemMXBeanProvider create() {
+      OperatingSystemMXBean sys = ManagementFactory.getOperatingSystemMXBean();
+      for (String name :
+          Arrays.asList(
+              "com.sun.management.UnixOperatingSystemMXBean",
+              "com.ibm.lang.management.UnixOperatingSystemMXBean")) {
+        try {
+          Class.forName(name);
+          return new OperatingSystemMXBeanProvider(sys, name);
+        } catch (ReflectiveOperationException e) {
+          log.debug("No implementation for " + name);
+        }
+      }
+      log.warn("No implementation of UnixOperatingSystemMXBean found");
+      return null;
+    }
+  }
+
+  private OperatingSystemMXBeanProvider(OperatingSystemMXBean sys, String name)
+      throws ReflectiveOperationException {
+    checkState(Class.forName(name).isInstance(sys));
+    this.sys = sys;
+    getProcessCpuTime =
+        sys.getClass().getMethod("getProcessCpuTime", new Class[] {});
+    getProcessCpuTime.setAccessible(true);
+    getOpenFileDescriptorCount =
+        sys.getClass().getMethod("getOpenFileDescriptorCount", new Class[] {});
+    getOpenFileDescriptorCount.setAccessible(true);
+  }
+
+  public long getProcessCpuTime() {
+    try {
+      return (long) getProcessCpuTime.invoke(sys, new Object[] {});
+    } catch (ReflectiveOperationException e) {
+      return -1;
+    }
+  }
+
+  public long getOpenFileDescriptorCount() {
+    try {
+      return (long) getOpenFileDescriptorCount.invoke(sys, new Object[] {});
+    } catch (ReflectiveOperationException e) {
+      return -1;
+    }
+  }
+}
\ No newline at end of file
diff --git a/gerrit-server/src/main/java/com/google/gerrit/metrics/proc/ProcMetricModule.java b/gerrit-server/src/main/java/com/google/gerrit/metrics/proc/ProcMetricModule.java
index 53b860c..e05afd1 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/metrics/proc/ProcMetricModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/metrics/proc/ProcMetricModule.java
@@ -25,10 +25,6 @@
 import com.google.gerrit.metrics.Description.Units;
 import com.google.gerrit.metrics.Field;
 import com.google.gerrit.metrics.MetricMaker;
-
-import com.sun.management.OperatingSystemMXBean;
-import com.sun.management.UnixOperatingSystemMXBean;
-
 import java.lang.management.GarbageCollectorMXBean;
 import java.lang.management.ManagementFactory;
 import java.lang.management.MemoryMXBean;
@@ -36,7 +32,6 @@
 import java.lang.management.ThreadMXBean;
 import java.util.concurrent.TimeUnit;
 
-@SuppressWarnings("restriction")
 public class ProcMetricModule extends MetricModule {
   @Override
   protected void configure(MetricMaker metrics) {
@@ -77,9 +72,14 @@
   }
 
   private void procCpuUsage(MetricMaker metrics) {
-    final OperatingSystemMXBean sys =
-        (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
-    if (sys.getProcessCpuTime() != -1) {
+    final OperatingSystemMXBeanProvider provider =
+        OperatingSystemMXBeanProvider.Factory.create();
+
+    if (provider == null) {
+      return;
+    }
+
+    if (provider.getProcessCpuTime() != -1) {
       metrics.newCallbackMetric(
           "proc/cpu/usage",
           Double.class,
@@ -89,26 +89,24 @@
           new Supplier<Double>() {
             @Override
             public Double get() {
-              return sys.getProcessCpuTime() / 1e9;
+              return provider.getProcessCpuTime() / 1e9;
             }
           });
     }
-    if (sys instanceof UnixOperatingSystemMXBean) {
-      final UnixOperatingSystemMXBean unix = (UnixOperatingSystemMXBean) sys;
-      if (unix.getOpenFileDescriptorCount() != -1) {
-        metrics.newCallbackMetric(
-            "proc/num_open_fds",
-            Long.class,
-            new Description("Number of open file descriptors")
-              .setGauge()
-              .setUnit("fds"),
-            new Supplier<Long>() {
-              @Override
-              public Long get() {
-                return unix.getOpenFileDescriptorCount();
-              }
-            });
-      }
+
+    if (provider.getOpenFileDescriptorCount() != -1) {
+      metrics.newCallbackMetric(
+          "proc/num_open_fds",
+          Long.class,
+          new Description("Number of open file descriptors")
+            .setGauge()
+            .setUnit("fds"),
+          new Supplier<Long>() {
+            @Override
+            public Long get() {
+              return provider.getOpenFileDescriptorCount();
+            }
+          });
     }
   }