diff --git a/geode-core/src/main/java/org/apache/geode/internal/stats50/VMStats50.java b/geode-core/src/main/java/org/apache/geode/internal/stats50/VMStats50.java index a2d25dadeb0b..b223ab3ecbcf 100644 --- a/geode-core/src/main/java/org/apache/geode/internal/stats50/VMStats50.java +++ b/geode-core/src/main/java/org/apache/geode/internal/stats50/VMStats50.java @@ -20,10 +20,8 @@ import java.lang.management.MemoryMXBean; import java.lang.management.MemoryPoolMXBean; import java.lang.management.MemoryUsage; -import java.lang.management.OperatingSystemMXBean; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; -import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -31,6 +29,8 @@ import java.util.List; import java.util.Map; +import com.sun.management.OperatingSystemMXBean; +import com.sun.management.UnixOperatingSystemMXBean; import org.apache.logging.log4j.Logger; import org.apache.geode.StatisticDescriptor; @@ -41,7 +41,6 @@ import org.apache.geode.SystemFailure; import org.apache.geode.annotations.Immutable; import org.apache.geode.annotations.internal.MakeNotStatic; -import org.apache.geode.internal.classloader.ClassPathLoader; import org.apache.geode.internal.statistics.StatisticsTypeFactoryImpl; import org.apache.geode.internal.statistics.VMStatsContract; import org.apache.geode.logging.internal.log4j.api.LogService; @@ -61,20 +60,24 @@ public class VMStats50 implements VMStatsContract { private static final ClassLoadingMXBean clBean; @Immutable private static final MemoryMXBean memBean; - @Immutable - private static final OperatingSystemMXBean osBean; + /** - * This is actually an instance of UnixOperatingSystemMXBean but this class is not available on - * Windows so needed to make this a runtime check. + * Platform-specific OperatingSystemMXBean providing extended metrics beyond the standard + * java.lang.management.OperatingSystemMXBean. This interface is in the exported + * com.sun.management package and provides processCpuTime and other platform metrics. + * Available on all platforms. */ @Immutable - private static final Object unixBean; - @Immutable - private static final Method getMaxFileDescriptorCount; - @Immutable - private static final Method getOpenFileDescriptorCount; + private static final OperatingSystemMXBean platformOsBean; + + /** + * Unix-specific OperatingSystemMXBean providing file descriptor metrics. + * Only available on Unix-like platforms (Linux, macOS, Solaris, etc.). + * Gracefully null on Windows and other non-Unix platforms. + */ @Immutable - private static final Method getProcessCpuTime; + private static final UnixOperatingSystemMXBean unixOsBean; + @Immutable private static final ThreadMXBean threadBean; @@ -150,52 +153,53 @@ public class VMStats50 implements VMStatsContract { static { clBean = ManagementFactory.getClassLoadingMXBean(); memBean = ManagementFactory.getMemoryMXBean(); - osBean = ManagementFactory.getOperatingSystemMXBean(); - { - Method m1 = null; - Method m2 = null; - Method m3 = null; - Object bean = null; - try { - Class c = - ClassPathLoader.getLatest().forName("com.sun.management.UnixOperatingSystemMXBean"); - if (c.isInstance(osBean)) { - m1 = c.getMethod("getMaxFileDescriptorCount"); - m2 = c.getMethod("getOpenFileDescriptorCount"); - bean = osBean; - } else { - // leave them null - } - // Always set ProcessCpuTime - m3 = osBean.getClass().getMethod("getProcessCpuTime"); - if (m3 != null) { - m3.setAccessible(true); - } - } catch (VirtualMachineError err) { - SystemFailure.initiateFailure(err); - // If this ever returns, rethrow the error. We're poisoned - // now, so don't let this thread continue. - throw err; - } catch (Throwable ex) { - // Whenever you catch Error or Throwable, you must also - // catch VirtualMachineError (see above). However, there is - // _still_ a possibility that you are dealing with a cascading - // error condition, so you also need to check to see if the JVM - // is still usable: - logger.warn(ex.getMessage()); - SystemFailure.checkFailure(); - // must be on a platform that does not support unix mxbean - bean = null; - m1 = null; - m2 = null; - m3 = null; - } finally { - unixBean = bean; - getMaxFileDescriptorCount = m1; - getOpenFileDescriptorCount = m2; - getProcessCpuTime = m3; + + // Initialize platform-specific MXBeans using direct interface casting. + // This approach eliminates the need for reflection and --add-opens flags. + // The com.sun.management package is exported by jdk.management module, + // making these interfaces accessible without module violations. + OperatingSystemMXBean tempPlatformBean = null; + UnixOperatingSystemMXBean tempUnixBean = null; + + try { + // Get the standard OperatingSystemMXBean + java.lang.management.OperatingSystemMXBean stdOsBean = + ManagementFactory.getOperatingSystemMXBean(); + + // Cast to com.sun.management.OperatingSystemMXBean for extended metrics + // This interface is in the exported com.sun.management package + if (stdOsBean instanceof OperatingSystemMXBean) { + tempPlatformBean = (OperatingSystemMXBean) stdOsBean; } + + // Check for Unix-specific interface + // This is only available on Unix-like platforms (Linux, macOS, Solaris) + if (stdOsBean instanceof UnixOperatingSystemMXBean) { + tempUnixBean = (UnixOperatingSystemMXBean) stdOsBean; + } + } catch (VirtualMachineError err) { + SystemFailure.initiateFailure(err); + // If this ever returns, rethrow the error. We're poisoned + // now, so don't let this thread continue. + throw err; + } catch (Throwable ex) { + // Whenever you catch Error or Throwable, you must also + // catch VirtualMachineError (see above). However, there is + // _still_ a possibility that you are dealing with a cascading + // error condition, so you also need to check to see if the JVM + // is still usable: + logger.warn( + "Unable to access platform OperatingSystemMXBean for statistics collection. " + + "This affects monitoring metrics but does not impact core Geode functionality: {}", + ex.getMessage()); + SystemFailure.checkFailure(); + tempPlatformBean = null; + tempUnixBean = null; + } finally { + platformOsBean = tempPlatformBean; + unixOsBean = tempUnixBean; } + threadBean = ManagementFactory.getThreadMXBean(); if (THREAD_STATS_ENABLED) { if (threadBean.isThreadCpuTimeSupported()) { @@ -242,7 +246,7 @@ public class VMStats50 implements VMStatsContract { true)); sds.add(f.createLongCounter("processCpuTime", "CPU timed used by the process in nanoseconds.", "nanoseconds")); - if (unixBean != null) { + if (unixOsBean != null) { sds.add(f.createLongGauge("fdLimit", "Maximum number of file descriptors", "fds", true)); sds.add(f.createLongGauge("fdsOpen", "Current number of open file descriptors", "fds")); } @@ -260,7 +264,7 @@ public class VMStats50 implements VMStatsContract { totalMemoryId = vmType.nameToId("totalMemory"); maxMemoryId = vmType.nameToId("maxMemory"); processCpuTimeId = vmType.nameToId("processCpuTime"); - if (unixBean != null) { + if (unixOsBean != null) { unix_fdLimitId = vmType.nameToId("fdLimit"); unix_fdsOpenId = vmType.nameToId("fdsOpen"); } else { @@ -585,7 +589,9 @@ private void refreshGC() { public void refresh() { Runtime rt = Runtime.getRuntime(); vmStats.setInt(pendingFinalizationCountId, memBean.getObjectPendingFinalizationCount()); - vmStats.setInt(cpusId, osBean.getAvailableProcessors()); + if (platformOsBean != null) { + vmStats.setInt(cpusId, platformOsBean.getAvailableProcessors()); + } vmStats.setInt(threadsId, threadBean.getThreadCount()); vmStats.setInt(daemonThreadsId, threadBean.getDaemonThreadCount()); vmStats.setInt(peakThreadsId, threadBean.getPeakThreadCount()); @@ -596,32 +602,38 @@ public void refresh() { vmStats.setLong(totalMemoryId, rt.totalMemory()); vmStats.setLong(maxMemoryId, rt.maxMemory()); - // Compute processCpuTime separately, if not accessible ignore - try { - if (getProcessCpuTime != null) { - Object v = getProcessCpuTime.invoke(osBean); - vmStats.setLong(processCpuTimeId, (Long) v); + // Collect process CPU time using public com.sun.management API. + // No reflection or setAccessible() required - this is a properly + // exported interface method from the jdk.management module. + if (platformOsBean != null) { + try { + long cpuTime = platformOsBean.getProcessCpuTime(); + vmStats.setLong(processCpuTimeId, cpuTime); + } catch (VirtualMachineError err) { + SystemFailure.initiateFailure(err); + // If this ever returns, rethrow the error. We're poisoned + // now, so don't let this thread continue. + throw err; + } catch (Throwable ex) { + // Whenever you catch Error or Throwable, you must also + // catch VirtualMachineError (see above). However, there is + // _still_ a possibility that you are dealing with a cascading + // error condition, so you also need to check to see if the JVM + // is still usable: + SystemFailure.checkFailure(); } - } catch (VirtualMachineError err) { - SystemFailure.initiateFailure(err); - // If this ever returns, rethrow the error. We're poisoned - // now, so don't let this thread continue. - throw err; - } catch (Throwable ex) { - // Whenever you catch Error or Throwable, you must also - // catch VirtualMachineError (see above). However, there is - // _still_ a possibility that you are dealing with a cascading - // error condition, so you also need to check to see if the JVM - // is still usable: - SystemFailure.checkFailure(); } - if (unixBean != null) { + // Collect Unix-specific file descriptor metrics. + // This interface is only implemented on Unix-like platforms; + // gracefully null on Windows. + if (unixOsBean != null) { try { - Object v = getMaxFileDescriptorCount.invoke(unixBean); - vmStats.setLong(unix_fdLimitId, (Long) v); - v = getOpenFileDescriptorCount.invoke(unixBean); - vmStats.setLong(unix_fdsOpenId, (Long) v); + long maxFd = unixOsBean.getMaxFileDescriptorCount(); + vmStats.setLong(unix_fdLimitId, maxFd); + + long openFd = unixOsBean.getOpenFileDescriptorCount(); + vmStats.setLong(unix_fdsOpenId, openFd); } catch (VirtualMachineError err) { SystemFailure.initiateFailure(err); // If this ever returns, rethrow the error. We're poisoned diff --git a/geode-gfsh/src/main/java/org/apache/geode/management/internal/cli/commands/MemberJvmOptions.java b/geode-gfsh/src/main/java/org/apache/geode/management/internal/cli/commands/MemberJvmOptions.java index 4021db507d62..fd9116cedeff 100644 --- a/geode-gfsh/src/main/java/org/apache/geode/management/internal/cli/commands/MemberJvmOptions.java +++ b/geode-gfsh/src/main/java/org/apache/geode/management/internal/cli/commands/MemberJvmOptions.java @@ -26,8 +26,8 @@ import java.util.Collections; import java.util.List; +import org.apache.geode.distributed.internal.deadlock.UnsafeThreadLocal; import org.apache.geode.internal.offheap.AddressableMemoryManager; -import org.apache.geode.internal.stats50.VMStats50; import org.apache.geode.unsafe.internal.com.sun.jmx.remote.security.MBeanServerAccessController; public class MemberJvmOptions { @@ -38,18 +38,17 @@ public class MemberJvmOptions { private static final String COM_SUN_JMX_REMOTE_SECURITY_EXPORT = "--add-exports=java.management/com.sun.jmx.remote.security=ALL-UNNAMED"; /** - * open needed by {@link AddressableMemoryManager} + * open needed by {@link UnsafeThreadLocal} */ - private static final String JAVA_NIO_OPEN = "--add-opens=java.base/java.nio=ALL-UNNAMED"; + private static final String JAVA_LANG_OPEN = "--add-opens=java.base/java.lang=ALL-UNNAMED"; /** - * open needed by {@link VMStats50} + * open needed by {@link AddressableMemoryManager} */ - private static final String COM_SUN_MANAGEMENT_INTERNAL_OPEN = - "--add-opens=jdk.management/com.sun.management.internal=ALL-UNNAMED"; + private static final String JAVA_NIO_OPEN = "--add-opens=java.base/java.nio=ALL-UNNAMED"; static final List JAVA_11_OPTIONS = Arrays.asList( COM_SUN_JMX_REMOTE_SECURITY_EXPORT, - COM_SUN_MANAGEMENT_INTERNAL_OPEN, + JAVA_LANG_OPEN, JAVA_NIO_OPEN); public static List getMemberJvmOptions() {