diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/info/InfoContributorAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/info/InfoContributorAutoConfiguration.java index 92ffca446b..cde1f0cf84 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/info/InfoContributorAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/info/InfoContributorAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2021 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. @@ -20,6 +20,7 @@ import org.springframework.boot.actuate.info.BuildInfoContributor; import org.springframework.boot.actuate.info.EnvironmentInfoContributor; import org.springframework.boot.actuate.info.GitInfoContributor; import org.springframework.boot.actuate.info.InfoContributor; +import org.springframework.boot.actuate.info.JavaInfoContributor; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -40,6 +41,7 @@ import org.springframework.core.env.ConfigurableEnvironment; * * @author Meang Akira Tanaka * @author Stephane Nicoll + * @author Jonatan Ivanov * @since 2.0.0 */ @Configuration(proxyBeanMethods = false) @@ -77,4 +79,11 @@ public class InfoContributorAutoConfiguration { return new BuildInfoContributor(buildProperties); } + @Bean + @ConditionalOnEnabledInfoContributor("java") + @Order(DEFAULT_ORDER) + public JavaInfoContributor javaInfoContributor() { + return new JavaInfoContributor(); + } + } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/info/InfoContributorAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/info/InfoContributorAutoConfigurationTests.java index efa37a54b0..e4001ed7fb 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/info/InfoContributorAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/info/InfoContributorAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2021 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. @@ -19,17 +19,19 @@ package org.springframework.boot.actuate.autoconfigure.info; import java.util.Map; import java.util.Properties; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.springframework.boot.actuate.info.BuildInfoContributor; +import org.springframework.boot.actuate.info.EnvironmentInfoContributor; import org.springframework.boot.actuate.info.GitInfoContributor; import org.springframework.boot.actuate.info.Info; import org.springframework.boot.actuate.info.InfoContributor; +import org.springframework.boot.actuate.info.JavaInfoContributor; +import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.info.BuildProperties; import org.springframework.boot.info.GitProperties; -import org.springframework.boot.test.util.TestPropertyValues; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.boot.info.JavaInfo; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -39,94 +41,117 @@ import static org.assertj.core.api.Assertions.assertThat; * Tests for {@link InfoContributorAutoConfiguration}. * * @author Stephane Nicoll + * @author Jonatan Ivanov */ class InfoContributorAutoConfigurationTests { - private AnnotationConfigApplicationContext context; - - @AfterEach - void close() { - if (this.context != null) { - this.context.close(); - } - } + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(InfoContributorAutoConfiguration.class)); @Test void disableEnvContributor() { - load("management.info.env.enabled:false"); - Map beans = this.context.getBeansOfType(InfoContributor.class); - assertThat(beans).hasSize(0); + this.contextRunner.withPropertyValues("management.info.env.enabled=false") + .run((context) -> assertThat(context).doesNotHaveBean(EnvironmentInfoContributor.class)); + } + + @Test + void disableJavaContributor() { + this.contextRunner.withPropertyValues("management.info.java.enabled=false") + .run((context) -> assertThat(context).doesNotHaveBean(JavaInfoContributor.class)); + } + + @Test + void defaultInfoContributorsEnabled() { + this.contextRunner.run((context) -> { + assertThat(context).hasSingleBean(EnvironmentInfoContributor.class) + .hasSingleBean(JavaInfoContributor.class); + assertThat(context.getBeansOfType(InfoContributor.class)).hasSize(2); + }); } @Test void defaultInfoContributorsDisabled() { - load("management.info.defaults.enabled:false"); - Map beans = this.context.getBeansOfType(InfoContributor.class); - assertThat(beans).hasSize(0); + this.contextRunner.withPropertyValues("management.info.defaults.enabled=false") + .run((context) -> assertThat(context).doesNotHaveBean(InfoContributor.class)); } @Test void defaultInfoContributorsDisabledWithCustomOne() { - load(CustomInfoContributorConfiguration.class, "management.info.defaults.enabled:false"); - Map beans = this.context.getBeansOfType(InfoContributor.class); - assertThat(beans).hasSize(1); - assertThat(this.context.getBean("customInfoContributor")).isSameAs(beans.values().iterator().next()); + this.contextRunner.withPropertyValues("management.info.defaults.enabled=false") + .withUserConfiguration(CustomInfoContributorConfiguration.class).run((context) -> { + assertThat(context).hasSingleBean(InfoContributor.class); + assertThat(context.getBean(InfoContributor.class)) + .isSameAs(context.getBean("customInfoContributor")); + }); } @SuppressWarnings("unchecked") @Test void gitPropertiesDefaultMode() { - load(GitPropertiesConfiguration.class); - Map beans = this.context.getBeansOfType(InfoContributor.class); - assertThat(beans).containsKeys("gitInfoContributor"); - Map content = invokeContributor( - this.context.getBean("gitInfoContributor", InfoContributor.class)); - Object git = content.get("git"); - assertThat(git).isInstanceOf(Map.class); - Map gitInfo = (Map) git; - assertThat(gitInfo).containsOnlyKeys("branch", "commit"); + this.contextRunner.withUserConfiguration(GitPropertiesConfiguration.class).run((context) -> { + assertThat(context).hasSingleBean(GitInfoContributor.class); + Map content = invokeContributor(context.getBean(GitInfoContributor.class)); + Object git = content.get("git"); + assertThat(git).isInstanceOf(Map.class); + Map gitInfo = (Map) git; + assertThat(gitInfo).containsOnlyKeys("branch", "commit"); + }); } @SuppressWarnings("unchecked") @Test void gitPropertiesFullMode() { - load(GitPropertiesConfiguration.class, "management.info.git.mode=full"); - Map content = invokeContributor( - this.context.getBean("gitInfoContributor", InfoContributor.class)); - Object git = content.get("git"); - assertThat(git).isInstanceOf(Map.class); - Map gitInfo = (Map) git; - assertThat(gitInfo).containsOnlyKeys("branch", "commit", "foo"); - assertThat(gitInfo.get("foo")).isEqualTo("bar"); + this.contextRunner.withPropertyValues("management.info.git.mode=full") + .withUserConfiguration(GitPropertiesConfiguration.class).run((context) -> { + assertThat(context).hasSingleBean(GitInfoContributor.class); + Map content = invokeContributor(context.getBean(GitInfoContributor.class)); + Object git = content.get("git"); + assertThat(git).isInstanceOf(Map.class); + Map gitInfo = (Map) git; + assertThat(gitInfo).containsOnlyKeys("branch", "commit", "foo"); + assertThat(gitInfo.get("foo")).isEqualTo("bar"); + }); } @Test void customGitInfoContributor() { - load(CustomGitInfoContributorConfiguration.class); - assertThat(this.context.getBean(GitInfoContributor.class)) - .isSameAs(this.context.getBean("customGitInfoContributor")); + this.contextRunner.withUserConfiguration(CustomGitInfoContributorConfiguration.class).run((context) -> { + assertThat(context).hasSingleBean(GitInfoContributor.class); + assertThat(context.getBean(GitInfoContributor.class)).isSameAs(context.getBean("customGitInfoContributor")); + }); } @SuppressWarnings("unchecked") @Test void buildProperties() { - load(BuildPropertiesConfiguration.class); - Map beans = this.context.getBeansOfType(InfoContributor.class); - assertThat(beans).containsKeys("buildInfoContributor"); - Map content = invokeContributor( - this.context.getBean("buildInfoContributor", InfoContributor.class)); - Object build = content.get("build"); - assertThat(build).isInstanceOf(Map.class); - Map buildInfo = (Map) build; - assertThat(buildInfo).containsOnlyKeys("group", "artifact", "foo"); - assertThat(buildInfo.get("foo")).isEqualTo("bar"); + this.contextRunner.withUserConfiguration(BuildPropertiesConfiguration.class).run((context) -> { + assertThat(context).hasSingleBean(BuildInfoContributor.class); + Map content = invokeContributor(context.getBean(BuildInfoContributor.class)); + Object build = content.get("build"); + assertThat(build).isInstanceOf(Map.class); + Map buildInfo = (Map) build; + assertThat(buildInfo).containsOnlyKeys("group", "artifact", "foo"); + assertThat(buildInfo.get("foo")).isEqualTo("bar"); + }); } @Test void customBuildInfoContributor() { - load(CustomBuildInfoContributorConfiguration.class); - assertThat(this.context.getBean(BuildInfoContributor.class)) - .isSameAs(this.context.getBean("customBuildInfoContributor")); + this.contextRunner.withUserConfiguration(CustomBuildInfoContributorConfiguration.class).run((context) -> { + assertThat(context).hasSingleBean(BuildInfoContributor.class); + assertThat(context.getBean(BuildInfoContributor.class)) + .isSameAs(context.getBean("customBuildInfoContributor")); + }); + } + + @Test + void javaInfoContributor() { + this.contextRunner.run((context) -> { + assertThat(context).hasSingleBean(JavaInfoContributor.class); + Map content = invokeContributor(context.getBean(JavaInfoContributor.class)); + assertThat(content).containsKey("java"); + assertThat(content.get("java")).isInstanceOf(JavaInfo.class); + }); } private Map invokeContributor(InfoContributor contributor) { @@ -135,21 +160,6 @@ class InfoContributorAutoConfigurationTests { return builder.build().getDetails(); } - private void load(String... environment) { - load(null, environment); - } - - private void load(Class config, String... environment) { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); - if (config != null) { - context.register(config); - } - context.register(InfoContributorAutoConfiguration.class); - TestPropertyValues.of(environment).applyTo(context); - context.refresh(); - this.context = context; - } - @Configuration(proxyBeanMethods = false) static class GitPropertiesConfiguration { diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/info/JavaInfoContributor.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/info/JavaInfoContributor.java new file mode 100644 index 0000000000..480f05ff3e --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/info/JavaInfoContributor.java @@ -0,0 +1,41 @@ +/* + * Copyright 2012-2021 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. + * You may obtain a copy of the License at + * + * https://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.springframework.boot.actuate.info; + +import org.springframework.boot.actuate.info.Info.Builder; +import org.springframework.boot.info.JavaInfo; + +/** + * An {@link InfoContributor} that exposes {@link JavaInfo}. + * + * @author Jonatan Ivanov + * @since 2.6.0 + */ +public class JavaInfoContributor implements InfoContributor { + + private final JavaInfo javaInfo; + + public JavaInfoContributor() { + this.javaInfo = new JavaInfo(); + } + + @Override + public void contribute(Builder builder) { + builder.withDetail("java", this.javaInfo); + } + +} diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/info/JavaInfoContributorTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/info/JavaInfoContributorTests.java new file mode 100644 index 0000000000..f431ccd90e --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/info/JavaInfoContributorTests.java @@ -0,0 +1,41 @@ +/* + * Copyright 2012-2021 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. + * You may obtain a copy of the License at + * + * https://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.springframework.boot.actuate.info; + +import org.junit.jupiter.api.Test; + +import org.springframework.boot.info.JavaInfo; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link JavaInfoContributor} + * + * @author Jonatan Ivanov + */ +class JavaInfoContributorTests { + + @Test + void javaInfoShouldBeAdded() { + JavaInfoContributor javaInfoContributor = new JavaInfoContributor(); + Info.Builder builder = new Info.Builder(); + javaInfoContributor.contribute(builder); + Info info = builder.build(); + assertThat(info.getDetails().get("java")).isInstanceOf(JavaInfo.class); + } + +} diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/endpoints.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/endpoints.adoc index 488b212cfd..b193560506 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/endpoints.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/endpoints.adoc @@ -1157,6 +1157,9 @@ When appropriate, Spring auto-configures the following `InfoContributor` beans: | {spring-boot-actuator-module-code}/info/BuildInfoContributor.java[`BuildInfoContributor`] | Exposes build information if a `META-INF/build-info.properties` file is available. + +| {spring-boot-actuator-module-code}/info/JavaInfoContributor.java[`JavaInfoContributor`] +| Exposes Java runtime information under the `java` key. |=== TIP: You can disable them all by setting the configprop:management.info.defaults.enabled[] property. @@ -1240,6 +1243,12 @@ See "<>" for +[[actuator.endpoints.info.java-information]] +==== Java Information +The `info` endpoint publishes information about your Java runtime environment, see {spring-boot-module-api}/info/JavaInfo.html[`JavaInfo`] for more details. + + + [[actuator.endpoints.info.writing-custom-info-contributors]] ==== Writing Custom InfoContributors To provide custom application information, you can register Spring beans that implement the {spring-boot-actuator-module-code}/info/InfoContributor.java[`InfoContributor`] interface. diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/info/JavaInfo.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/info/JavaInfo.java new file mode 100644 index 0000000000..75851490f6 --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/info/JavaInfo.java @@ -0,0 +1,114 @@ +/* + * Copyright 2012-2021 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. + * You may obtain a copy of the License at + * + * https://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.springframework.boot.info; + +/** + * Information about the Java environment the application is running in. + * + * @author Jonatan Ivanov + * @author Stephane Nicoll + * @since 2.6.0 + */ +public class JavaInfo { + + private final String vendor; + + private final String version; + + private final JavaRuntimeEnvironmentInfo runtime; + + private final JavaVirtualMachineInfo jvm; + + public JavaInfo() { + this.vendor = System.getProperty("java.vendor"); + this.version = System.getProperty("java.version"); + this.runtime = new JavaRuntimeEnvironmentInfo(); + this.jvm = new JavaVirtualMachineInfo(); + } + + public String getVendor() { + return this.vendor; + } + + public String getVersion() { + return this.version; + } + + public JavaRuntimeEnvironmentInfo getRuntime() { + return this.runtime; + } + + public JavaVirtualMachineInfo getJvm() { + return this.jvm; + } + + /** + * Information about the Java Runtime Environment the application is running in. + */ + public static class JavaRuntimeEnvironmentInfo { + + private final String name; + + private final String version; + + public JavaRuntimeEnvironmentInfo() { + this.name = System.getProperty("java.runtime.name"); + this.version = System.getProperty("java.runtime.version"); + } + + public String getName() { + return this.name; + } + + public String getVersion() { + return this.version; + } + + } + + /** + * Information about the Java Virtual Machine) the application is running in. + */ + public static class JavaVirtualMachineInfo { + + private final String name; + + private final String vendor; + + private final String version; + + public JavaVirtualMachineInfo() { + this.name = System.getProperty("java.vm.name"); + this.vendor = System.getProperty("java.vm.vendor"); + this.version = System.getProperty("java.vm.version"); + } + + public String getName() { + return this.name; + } + + public String getVendor() { + return this.vendor; + } + + public String getVersion() { + return this.version; + } + + } + +} diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/info/JavaInfoTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/info/JavaInfoTests.java new file mode 100644 index 0000000000..421809c563 --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/info/JavaInfoTests.java @@ -0,0 +1,47 @@ +/* + * Copyright 2012-2021 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. + * You may obtain a copy of the License at + * + * https://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.springframework.boot.info; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link JavaInfo}. + * + * @author Jonatan Ivanov + * @author Stephane Nicoll + */ +class JavaInfoTests { + + @Test + void javaInfoIsAvailable() { + JavaInfo javaInfo = new JavaInfo(); + assertThat(javaInfo.getVendor()).isEqualTo(System.getProperty("java.vendor")); + assertThat(javaInfo.getVersion()).isEqualTo(System.getProperty("java.version")); + assertThat(javaInfo.getRuntime()).satisfies((jreInfo) -> { + assertThat(jreInfo.getName()).isEqualTo(System.getProperty("java.runtime.name")); + assertThat(jreInfo.getVersion()).isEqualTo(System.getProperty("java.runtime.version")); + }); + assertThat(javaInfo.getJvm()).satisfies((jvmInfo) -> { + assertThat(jvmInfo.getName()).isEqualTo(System.getProperty("java.vm.name")); + assertThat(jvmInfo.getVendor()).isEqualTo(System.getProperty("java.vm.vendor")); + assertThat(jvmInfo.getVersion()).isEqualTo(System.getProperty("java.vm.version")); + }); + } + +}