From 104397fb91b00169144a6cf7162f6be84b619fd6 Mon Sep 17 00:00:00 2001 From: Andrey Litvitski Date: Wed, 25 Dec 2024 22:00:05 +0300 Subject: [PATCH 1/2] Add info contributor support for JDK 24's VirtualThreadSchedulerMXBean See gh-43594 --- .../boot/info/ProcessInfo.java | 64 +++++++++++++++++++ .../boot/info/ProcessInfoTests.java | 22 +++++++ 2 files changed, 86 insertions(+) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/info/ProcessInfo.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/info/ProcessInfo.java index 85f46e0266..b9ad4f64e3 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/info/ProcessInfo.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/info/ProcessInfo.java @@ -24,6 +24,7 @@ import java.lang.management.MemoryUsage; * Information about the process of the application. * * @author Jonatan Ivanov + * @author Andrey Litvitski * @since 3.3.0 */ public class ProcessInfo { @@ -72,6 +73,29 @@ public class ProcessInfo { return new MemoryInfo(); } + /** + * Virtual threads information for the process. These values provide details about the + * current state of virtual threads, including the number of mounted threads, queued threads, + * the parallelism level, and the thread pool size. + * @return an instance of {@link VirtualThreadsInfo} containing information about virtual threads, + * or {@code null} if the VirtualThreadSchedulerMXBean is not available. + * @since 3.5.0 + */ + public VirtualThreadsInfo getVirtualThreads() { + try { + Class mxBeanClass = Class.forName("jdk.management.VirtualThreadSchedulerMXBean"); + Object bean = ManagementFactory.getPlatformMXBean(mxBeanClass); + return new VirtualThreadsInfo( + (Integer) mxBeanClass.getMethod("getMountedVirtualThreadCount").invoke(bean), + (Long) mxBeanClass.getMethod("getQueuedVirtualThreadCount").invoke(bean), + (Integer) mxBeanClass.getMethod("getParallelism").invoke(bean), + (Integer) mxBeanClass.getMethod("getPoolSize").invoke(bean) + ); + } catch (ReflectiveOperationException e) { + return null; + } + } + public long getPid() { return this.pid; } @@ -84,6 +108,46 @@ public class ProcessInfo { return this.owner; } + /** + * Virtual threads information. + * + * @since 3.5.0 + */ + public static class VirtualThreadsInfo { + + private final int mounted; + + private final long queued; + + private final int parallelism; + + private final int poolSize; + + public VirtualThreadsInfo(int mounted, long queued, int parallelism, int poolSize) { + this.mounted = mounted; + this.queued = queued; + this.parallelism = parallelism; + this.poolSize = poolSize; + } + + public long getMounted() { + return this.mounted; + } + + public long getQueued() { + return this.queued; + } + + public int getParallelism() { + return this.parallelism; + } + + public int getPoolSize() { + return this.poolSize; + } + + } + /** * Memory information. * diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/info/ProcessInfoTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/info/ProcessInfoTests.java index 37ba3b0173..70507bccfc 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/info/ProcessInfoTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/info/ProcessInfoTests.java @@ -19,6 +19,8 @@ package org.springframework.boot.info; import org.junit.jupiter.api.Test; import org.springframework.boot.info.ProcessInfo.MemoryInfo.MemoryUsageInfo; +import org.springframework.boot.info.ProcessInfo.VirtualThreadsInfo; +import org.springframework.util.ClassUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -26,6 +28,7 @@ import static org.assertj.core.api.Assertions.assertThat; * Tests for {@link ProcessInfo}. * * @author Jonatan Ivanov + * @author Andrey Litvitski */ class ProcessInfoTests { @@ -54,4 +57,23 @@ class ProcessInfoTests { assertThat(nonHeapUsageInfo.getMax()).isEqualTo(-1); } + @Test + void virtualThreadsInfoIsNullWhenMXBeanIsNotAccessible() { + if (ClassUtils.isPresent("jdk.management.VirtualThreadSchedulerMXBean", null)) { + ProcessInfo processInfo = new ProcessInfo(); + + VirtualThreadsInfo virtualThreadsInfo = processInfo.getVirtualThreads(); + + assertThat(virtualThreadsInfo).isNotNull(); + assertThat(virtualThreadsInfo.getMounted()).isGreaterThanOrEqualTo(0); + assertThat(virtualThreadsInfo.getQueued()).isGreaterThanOrEqualTo(0); + assertThat(virtualThreadsInfo.getParallelism()).isGreaterThan(0); + assertThat(virtualThreadsInfo.getPoolSize()).isGreaterThan(0); + } else { + ProcessInfo processInfo = new ProcessInfo(); + + assertThat(processInfo.getVirtualThreads()).isNull(); + } + } + } From 821af70559a5f97d7e5d60f6d8a646392086d30e Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Mon, 13 Jan 2025 11:06:36 +0100 Subject: [PATCH 2/2] Polish "Add info contributor support for JDK 24's VirtualThreadSchedulerMXBean" See gh-43594 --- .../boot/info/ProcessInfo.java | 23 ++++++------ .../boot/info/ProcessInfoTests.java | 35 ++++++++++--------- 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/info/ProcessInfo.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/info/ProcessInfo.java index b9ad4f64e3..3d3d7aedda 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/info/ProcessInfo.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/info/ProcessInfo.java @@ -19,6 +19,7 @@ package org.springframework.boot.info; import java.lang.management.ManagementFactory; import java.lang.management.MemoryMXBean; import java.lang.management.MemoryUsage; +import java.lang.management.PlatformManagedObject; /** * Information about the process of the application. @@ -75,23 +76,25 @@ public class ProcessInfo { /** * Virtual threads information for the process. These values provide details about the - * current state of virtual threads, including the number of mounted threads, queued threads, - * the parallelism level, and the thread pool size. - * @return an instance of {@link VirtualThreadsInfo} containing information about virtual threads, - * or {@code null} if the VirtualThreadSchedulerMXBean is not available. + * current state of virtual threads, including the number of mounted threads, queued + * threads, the parallelism level, and the thread pool size. + * @return an instance of {@link VirtualThreadsInfo} containing information about + * virtual threads, or {@code null} if the VirtualThreadSchedulerMXBean is not + * available. * @since 3.5.0 */ + @SuppressWarnings("unchecked") public VirtualThreadsInfo getVirtualThreads() { try { - Class mxBeanClass = Class.forName("jdk.management.VirtualThreadSchedulerMXBean"); + Class mxBeanClass = (Class) Class + .forName("jdk.management.VirtualThreadSchedulerMXBean"); Object bean = ManagementFactory.getPlatformMXBean(mxBeanClass); - return new VirtualThreadsInfo( - (Integer) mxBeanClass.getMethod("getMountedVirtualThreadCount").invoke(bean), + return new VirtualThreadsInfo((Integer) mxBeanClass.getMethod("getMountedVirtualThreadCount").invoke(bean), (Long) mxBeanClass.getMethod("getQueuedVirtualThreadCount").invoke(bean), (Integer) mxBeanClass.getMethod("getParallelism").invoke(bean), - (Integer) mxBeanClass.getMethod("getPoolSize").invoke(bean) - ); - } catch (ReflectiveOperationException e) { + (Integer) mxBeanClass.getMethod("getPoolSize").invoke(bean)); + } + catch (ReflectiveOperationException ex) { return null; } } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/info/ProcessInfoTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/info/ProcessInfoTests.java index 70507bccfc..fb2558aa75 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/info/ProcessInfoTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/info/ProcessInfoTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2024 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,10 +17,11 @@ package org.springframework.boot.info; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledForJreRange; +import org.junit.jupiter.api.condition.JRE; import org.springframework.boot.info.ProcessInfo.MemoryInfo.MemoryUsageInfo; import org.springframework.boot.info.ProcessInfo.VirtualThreadsInfo; -import org.springframework.util.ClassUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -58,22 +59,22 @@ class ProcessInfoTests { } @Test - void virtualThreadsInfoIsNullWhenMXBeanIsNotAccessible() { - if (ClassUtils.isPresent("jdk.management.VirtualThreadSchedulerMXBean", null)) { - ProcessInfo processInfo = new ProcessInfo(); + @EnabledForJreRange(min = JRE.JAVA_24) + void virtualThreadsInfoIfAvailable() { + ProcessInfo processInfo = new ProcessInfo(); + VirtualThreadsInfo virtualThreadsInfo = processInfo.getVirtualThreads(); + assertThat(virtualThreadsInfo).isNotNull(); + assertThat(virtualThreadsInfo.getMounted()).isGreaterThanOrEqualTo(0); + assertThat(virtualThreadsInfo.getQueued()).isGreaterThanOrEqualTo(0); + assertThat(virtualThreadsInfo.getParallelism()).isGreaterThan(0); + assertThat(virtualThreadsInfo.getPoolSize()).isGreaterThanOrEqualTo(0); + } - VirtualThreadsInfo virtualThreadsInfo = processInfo.getVirtualThreads(); - - assertThat(virtualThreadsInfo).isNotNull(); - assertThat(virtualThreadsInfo.getMounted()).isGreaterThanOrEqualTo(0); - assertThat(virtualThreadsInfo.getQueued()).isGreaterThanOrEqualTo(0); - assertThat(virtualThreadsInfo.getParallelism()).isGreaterThan(0); - assertThat(virtualThreadsInfo.getPoolSize()).isGreaterThan(0); - } else { - ProcessInfo processInfo = new ProcessInfo(); - - assertThat(processInfo.getVirtualThreads()).isNull(); - } + @Test + @EnabledForJreRange(max = JRE.JAVA_23) + void virtualThreadsInfoIfNotAvailable() { + ProcessInfo processInfo = new ProcessInfo(); + assertThat(processInfo.getVirtualThreads()).isNull(); } }