Merge pull request #28136 from jonatan-ivanov

* pr/28136:
  Polish "Add Java InfoContributor"
  Add Java InfoContributor

Closes gh-28136
This commit is contained in:
Stephane Nicoll
2021-10-05 15:13:43 +02:00
7 changed files with 342 additions and 71 deletions

View File

@@ -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();
}
}

View File

@@ -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<String, InfoContributor> 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<String, InfoContributor> 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<String, InfoContributor> 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<String, InfoContributor> beans = this.context.getBeansOfType(InfoContributor.class);
assertThat(beans).containsKeys("gitInfoContributor");
Map<String, Object> content = invokeContributor(
this.context.getBean("gitInfoContributor", InfoContributor.class));
Object git = content.get("git");
assertThat(git).isInstanceOf(Map.class);
Map<String, Object> gitInfo = (Map<String, Object>) git;
assertThat(gitInfo).containsOnlyKeys("branch", "commit");
this.contextRunner.withUserConfiguration(GitPropertiesConfiguration.class).run((context) -> {
assertThat(context).hasSingleBean(GitInfoContributor.class);
Map<String, Object> content = invokeContributor(context.getBean(GitInfoContributor.class));
Object git = content.get("git");
assertThat(git).isInstanceOf(Map.class);
Map<String, Object> gitInfo = (Map<String, Object>) git;
assertThat(gitInfo).containsOnlyKeys("branch", "commit");
});
}
@SuppressWarnings("unchecked")
@Test
void gitPropertiesFullMode() {
load(GitPropertiesConfiguration.class, "management.info.git.mode=full");
Map<String, Object> content = invokeContributor(
this.context.getBean("gitInfoContributor", InfoContributor.class));
Object git = content.get("git");
assertThat(git).isInstanceOf(Map.class);
Map<String, Object> gitInfo = (Map<String, Object>) 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<String, Object> content = invokeContributor(context.getBean(GitInfoContributor.class));
Object git = content.get("git");
assertThat(git).isInstanceOf(Map.class);
Map<String, Object> gitInfo = (Map<String, Object>) 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<String, InfoContributor> beans = this.context.getBeansOfType(InfoContributor.class);
assertThat(beans).containsKeys("buildInfoContributor");
Map<String, Object> content = invokeContributor(
this.context.getBean("buildInfoContributor", InfoContributor.class));
Object build = content.get("build");
assertThat(build).isInstanceOf(Map.class);
Map<String, Object> buildInfo = (Map<String, Object>) 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<String, Object> content = invokeContributor(context.getBean(BuildInfoContributor.class));
Object build = content.get("build");
assertThat(build).isInstanceOf(Map.class);
Map<String, Object> buildInfo = (Map<String, Object>) 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<String, Object> content = invokeContributor(context.getBean(JavaInfoContributor.class));
assertThat(content).containsKey("java");
assertThat(content.get("java")).isInstanceOf(JavaInfo.class);
});
}
private Map<String, Object> 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 {

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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 "<<howto#howto.build.generate-info,how to generate build information>>" 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.

View File

@@ -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;
}
}
}

View File

@@ -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"));
});
}
}