diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/condition/ConditionsReportEndpoint.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/condition/ConditionsReportEndpoint.java index 6d22777325..7dbb50b82d 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/condition/ConditionsReportEndpoint.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/condition/ConditionsReportEndpoint.java @@ -17,6 +17,7 @@ package org.springframework.boot.actuate.autoconfigure.condition; import java.util.ArrayList; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -32,6 +33,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionEvaluationRepor import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport.ConditionAndOutcome; import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport.ConditionAndOutcomes; import org.springframework.boot.autoconfigure.condition.ConditionOutcome; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Condition; import org.springframework.util.ClassUtils; import org.springframework.util.LinkedMultiValueMap; @@ -50,24 +53,58 @@ import org.springframework.util.StringUtils; @Endpoint(id = "conditions") public class ConditionsReportEndpoint { - private final ConditionEvaluationReport conditionEvaluationReport; + private final ConfigurableApplicationContext context; - public ConditionsReportEndpoint(ConditionEvaluationReport conditionEvaluationReport) { - this.conditionEvaluationReport = conditionEvaluationReport; + public ConditionsReportEndpoint(ConfigurableApplicationContext context) { + this.context = context; } @ReadOperation - public Report getEvaluationReport() { - return new Report(this.conditionEvaluationReport); + public ApplicationConditionEvaluation applicationConditionEvaluation() { + Map contextConditionEvaluations = new HashMap<>(); + ConfigurableApplicationContext target = this.context; + while (target != null) { + contextConditionEvaluations.put(target.getId(), + new ContextConditionEvaluation(target)); + target = getConfigurableParent(target); + } + return new ApplicationConditionEvaluation(contextConditionEvaluations); + } + + private ConfigurableApplicationContext getConfigurableParent( + ConfigurableApplicationContext context) { + ApplicationContext parent = context.getParent(); + if (parent instanceof ConfigurableApplicationContext) { + return (ConfigurableApplicationContext) parent; + } + return null; } /** - * Adapts {@link ConditionEvaluationReport} to a JSON friendly structure. + * A description of an application's condition evaluation, primarily intended for + * serialization to JSON. + */ + public static final class ApplicationConditionEvaluation { + + private final Map contexts; + + private ApplicationConditionEvaluation( + Map contexts) { + this.contexts = contexts; + } + + public Map getContexts() { + return this.contexts; + } + + } + + /** + * A description of an application context's condition evaluation, primarily intended + * for serialization to JSON. */ - @JsonPropertyOrder({ "positiveMatches", "negativeMatches", "exclusions", - "unconditionalClasses" }) @JsonInclude(Include.NON_EMPTY) - public static class Report { + public static final class ContextConditionEvaluation { private final MultiValueMap positiveMatches; @@ -77,9 +114,11 @@ public class ConditionsReportEndpoint { private final Set unconditionalClasses; - private final Report parent; + private final String parentId; - public Report(ConditionEvaluationReport report) { + public ContextConditionEvaluation(ConfigurableApplicationContext context) { + ConditionEvaluationReport report = ConditionEvaluationReport + .get(context.getBeanFactory()); this.positiveMatches = new LinkedMultiValueMap<>(); this.negativeMatches = new LinkedHashMap<>(); this.exclusions = report.getExclusions(); @@ -93,8 +132,8 @@ public class ConditionsReportEndpoint { add(this.negativeMatches, entry.getKey(), entry.getValue()); } } - boolean hasParent = report.getParent() != null; - this.parent = (hasParent ? new Report(report.getParent()) : null); + this.parentId = context.getParent() == null ? null + : context.getParent().getId(); } private void add(Map map, String source, @@ -127,8 +166,8 @@ public class ConditionsReportEndpoint { return this.unconditionalClasses; } - public Report getParent() { - return this.parent; + public String getParentId() { + return this.parentId; } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/condition/ConditionsReportEndpointAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/condition/ConditionsReportEndpointAutoConfiguration.java index 34e7d9c3f0..52ce19a106 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/condition/ConditionsReportEndpointAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/condition/ConditionsReportEndpointAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -18,7 +18,6 @@ package org.springframework.boot.actuate.autoconfigure.condition; import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.SearchStrategy; import org.springframework.context.ConfigurableApplicationContext; @@ -35,19 +34,12 @@ import org.springframework.context.annotation.Configuration; @Configuration public class ConditionsReportEndpointAutoConfiguration { - private ConfigurableApplicationContext context; - - public ConditionsReportEndpointAutoConfiguration( - ConfigurableApplicationContext context) { - this.context = context; - } - @Bean @ConditionalOnMissingBean(search = SearchStrategy.CURRENT) @ConditionalOnEnabledEndpoint - public ConditionsReportEndpoint conditionsReportEndpoint() { - return new ConditionsReportEndpoint( - ConditionEvaluationReport.get(this.context.getBeanFactory())); + public ConditionsReportEndpoint conditionsReportEndpoint( + ConfigurableApplicationContext context) { + return new ConditionsReportEndpoint(context); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/flyway/FlywayEndpointAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/flyway/FlywayEndpointAutoConfiguration.java index 8f0a9fbcf2..609117746f 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/flyway/FlywayEndpointAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/flyway/FlywayEndpointAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -16,8 +16,6 @@ package org.springframework.boot.actuate.autoconfigure.flyway; -import java.util.Map; - import org.flywaydb.core.Flyway; import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint; @@ -28,6 +26,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -46,8 +45,8 @@ public class FlywayEndpointAutoConfiguration { @ConditionalOnBean(Flyway.class) @ConditionalOnMissingBean @ConditionalOnEnabledEndpoint - public FlywayEndpoint flywayEndpoint(Map flywayBeans) { - return new FlywayEndpoint(flywayBeans); + public FlywayEndpoint flywayEndpoint(ApplicationContext context) { + return new FlywayEndpoint(context); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/liquibase/LiquibaseEndpointAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/liquibase/LiquibaseEndpointAutoConfiguration.java index 59f6cb08b2..67859757b0 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/liquibase/LiquibaseEndpointAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/liquibase/LiquibaseEndpointAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -16,8 +16,6 @@ package org.springframework.boot.actuate.autoconfigure.liquibase; -import java.util.Map; - import liquibase.integration.spring.SpringLiquibase; import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint; @@ -28,6 +26,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -46,9 +45,8 @@ public class LiquibaseEndpointAutoConfiguration { @ConditionalOnBean(SpringLiquibase.class) @ConditionalOnMissingBean @ConditionalOnEnabledEndpoint - public LiquibaseEndpoint liquibaseEndpoint( - Map liquibaseBeans) { - return new LiquibaseEndpoint(liquibaseBeans); + public LiquibaseEndpoint liquibaseEndpoint(ApplicationContext context) { + return new LiquibaseEndpoint(context); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/condition/ConditionsReportEndpointTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/condition/ConditionsReportEndpointTests.java index 546c92e5c5..e9859429b6 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/condition/ConditionsReportEndpointTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/condition/ConditionsReportEndpointTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -23,7 +23,7 @@ import javax.annotation.PostConstruct; import org.junit.Test; -import org.springframework.boot.actuate.autoconfigure.condition.ConditionsReportEndpoint.Report; +import org.springframework.boot.actuate.autoconfigure.condition.ConditionsReportEndpoint.ContextConditionEvaluation; import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport; import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -49,8 +49,10 @@ public class ConditionsReportEndpointTests { public void invoke() { new ApplicationContextRunner().withUserConfiguration(Config.class) .run((context) -> { - Report report = context.getBean(ConditionsReportEndpoint.class) - .getEvaluationReport(); + ContextConditionEvaluation report = context + .getBean(ConditionsReportEndpoint.class) + .applicationConditionEvaluation().getContexts() + .get(context.getId()); assertThat(report.getPositiveMatches()).isEmpty(); assertThat(report.getNegativeMatches()).containsKey("a"); assertThat(report.getUnconditionalClasses()).contains("b"); @@ -79,9 +81,8 @@ public class ConditionsReportEndpointTests { } @Bean - public ConditionsReportEndpoint endpoint( - ConditionEvaluationReport report) { - return new ConditionsReportEndpoint(report); + public ConditionsReportEndpoint endpoint() { + return new ConditionsReportEndpoint(this.context); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/context/properties/ConfigurationPropertiesReportEndpointAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/context/properties/ConfigurationPropertiesReportEndpointAutoConfigurationTests.java index 827620ac26..5ec5b06a2d 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/context/properties/ConfigurationPropertiesReportEndpointAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/context/properties/ConfigurationPropertiesReportEndpointAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -21,7 +21,7 @@ import java.util.Map; import org.junit.Test; import org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint; -import org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint.ConfigurationPropertiesDescriptor; +import org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint.ApplicationConfigurationProperties; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -73,10 +73,11 @@ public class ConfigurationPropertiesReportEndpointAutoConfigurationTests { .hasSingleBean(ConfigurationPropertiesReportEndpoint.class); ConfigurationPropertiesReportEndpoint endpoint = context .getBean(ConfigurationPropertiesReportEndpoint.class); - ConfigurationPropertiesDescriptor properties = endpoint + ApplicationConfigurationProperties properties = endpoint .configurationProperties(); - Map nestedProperties = properties.getBeans() - .get("testProperties").getProperties(); + Map nestedProperties = properties.getContexts() + .get(context.getId()).getBeans().get("testProperties") + .getProperties(); assertThat(nestedProperties).isNotNull(); assertThat(nestedProperties.get("dbPassword")).isEqualTo(dbPassword); assertThat(nestedProperties.get("myTestProperty")).isEqualTo(myTestProperty); diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/AbstractEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/AbstractEndpointDocumentationTests.java index 0c9f42f991..2ff5eb4234 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/AbstractEndpointDocumentationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/AbstractEndpointDocumentationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -46,12 +46,16 @@ import org.springframework.restdocs.JUnitRestDocumentation; import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation; import org.springframework.restdocs.operation.preprocess.ContentModifyingOperationPreprocessor; import org.springframework.restdocs.operation.preprocess.OperationPreprocessor; +import org.springframework.restdocs.payload.FieldDescriptor; +import org.springframework.restdocs.payload.JsonFieldType; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.util.StringUtils; import org.springframework.web.context.WebApplicationContext; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; + /** * Abstract base class for tests that generate endpoint documentation using Spring REST * Docs. @@ -79,6 +83,10 @@ public abstract class AbstractEndpointDocumentationTests { .build(); } + protected WebApplicationContext getApplicationContext() { + return this.applicationContext; + } + protected String describeEnumValues(Class> enumType) { return StringUtils .collectionToCommaDelimitedString(Stream.of(enumType.getEnumConstants()) @@ -86,23 +94,35 @@ public abstract class AbstractEndpointDocumentationTests { .collect(Collectors.toList())); } - protected OperationPreprocessor limit(String key) { - return limit(key, (candidate) -> true); + protected OperationPreprocessor limit(String... keys) { + return limit((candidate) -> true, keys); } @SuppressWarnings("unchecked") - protected OperationPreprocessor limit(String key, Predicate filter) { + protected OperationPreprocessor limit(Predicate filter, String... keys) { return new ContentModifyingOperationPreprocessor((content, mediaType) -> { ObjectMapper objectMapper = new ObjectMapper() .enable(SerializationFeature.INDENT_OUTPUT); try { Map payload = objectMapper.readValue(content, Map.class); - Object entry = payload.get(key); - if (entry instanceof Map) { - payload.put(key, select((Map) entry, filter)); + Object target = payload; + Map parent = null; + for (String key : keys) { + if (target instanceof Map) { + parent = (Map) target; + target = parent.get(key); + } + else { + throw new IllegalStateException(); + } + } + if (target instanceof Map) { + parent.put(keys[keys.length - 1], + select((Map) target, filter)); } else { - payload.put(key, select((List) entry, filter)); + parent.put(keys[keys.length - 1], + select((List) target, filter)); } return objectMapper.writeValueAsBytes(payload); } @@ -112,6 +132,12 @@ public abstract class AbstractEndpointDocumentationTests { }); } + protected FieldDescriptor parentIdField() { + return fieldWithPath("contexts.*.parentId") + .description("Id of the parent application context, if any.").optional() + .type(JsonFieldType.STRING); + } + @SuppressWarnings("unchecked") private Map select(Map candidates, Predicate filter) { diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/BeansEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/BeansEndpointDocumentationTests.java index 536d69569e..96d9fc1fc3 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/BeansEndpointDocumentationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/BeansEndpointDocumentationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -30,7 +30,6 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.restdocs.payload.FieldDescriptor; -import org.springframework.restdocs.payload.JsonFieldType; import org.springframework.restdocs.payload.ResponseFieldsSnippet; import org.springframework.util.CollectionUtils; @@ -38,7 +37,6 @@ import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.docu import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; -import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -61,17 +59,16 @@ public class BeansEndpointDocumentationTests extends AbstractEndpointDocumentati .optional(), fieldWithPath("dependencies").description("Names of any dependencies.")); ResponseFieldsSnippet responseFields = responseFields( - fieldWithPath("contextId").description("ID of the application context."), - fieldWithPath("beans.*") + fieldWithPath("contexts") + .description("Application contexts keyed by id."), + parentIdField(), + fieldWithPath("contexts.*.beans") .description("Beans in the application context keyed by name.")) - .andWithPrefix("beans.*.", beanFields) - .and(subsectionWithPath("parent") - .description("Beans in the parent application " - + "context, if any.") - .type(JsonFieldType.OBJECT).optional()); + .andWithPrefix("contexts.*.beans.*.", beanFields); this.mockMvc.perform(get("/actuator/beans")).andExpect(status().isOk()) .andDo(document("beans", - preprocessResponse(limit("beans", this::isIndependentBean)), + preprocessResponse(limit(this::isIndependentBean, "contexts", + getApplicationContext().getId(), "beans")), responseFields)); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ConditionsReportEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ConditionsReportEndpointDocumentationTests.java index 28f343c52c..e7afb97f2c 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ConditionsReportEndpointDocumentationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ConditionsReportEndpointDocumentationTests.java @@ -17,21 +17,23 @@ package org.springframework.boot.actuate.autoconfigure.endpoint.web.documentation; import java.util.Arrays; +import java.util.List; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.boot.actuate.autoconfigure.condition.ConditionsReportEndpoint; import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport; import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.restdocs.JUnitRestDocumentation; import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation; +import org.springframework.restdocs.payload.FieldDescriptor; import org.springframework.restdocs.payload.JsonFieldType; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; @@ -70,37 +72,46 @@ public class ConditionsReportEndpointDocumentationTests @Test public void conditions() throws Exception { + List positiveMatchFields = Arrays.asList( + fieldWithPath("").description( + "Classes and methods with conditions that were " + "matched."), + fieldWithPath(".*.[].condition").description("Name of the condition."), + fieldWithPath(".*.[].message") + .description("Details of why the condition was matched.")); + List negativeMatchFields = Arrays.asList( + fieldWithPath("").description("Classes and methods with conditions that " + + "were not matched."), + fieldWithPath(".*.notMatched") + .description("Conditions that were matched."), + fieldWithPath(".*.notMatched.[].condition") + .description("Name of the condition."), + fieldWithPath(".*.notMatched.[].message").description( + "Details of why the condition was" + " not matched."), + fieldWithPath(".*.matched").description("Conditions that were matched."), + fieldWithPath(".*.matched.[].condition") + .description("Name of the condition.").type(JsonFieldType.STRING) + .optional(), + fieldWithPath(".*.matched.[].message") + .description("Details of why the condition was matched.") + .type(JsonFieldType.STRING).optional()); + FieldDescriptor unconditionalClassesField = fieldWithPath( + "contexts.*.unconditionalClasses").description( + "Names of unconditional auto-configuration classes if any."); this.mockMvc.perform(get("/actuator/conditions")).andExpect(status().isOk()) .andDo(MockMvcRestDocumentation.document("conditions", - preprocessResponse(limit("positiveMatches"), - limit("negativeMatches")), - responseFields( - fieldWithPath("positiveMatches").description( - "Classes and methods with conditions that were matched."), - fieldWithPath("positiveMatches.*.[].condition") - .description("Name of the condition."), - fieldWithPath("positiveMatches.*.[].message").description( - "Details of why the condition was matched."), - fieldWithPath("negativeMatches").description( - "Classes and methods with conditions that were not matched."), - fieldWithPath("negativeMatches.*.notMatched") - .description("Conditions that were matched."), - fieldWithPath("negativeMatches.*.notMatched.[].condition") - .description("Name of the condition."), - fieldWithPath("negativeMatches.*.notMatched.[].message") - .description( - "Details of why the condition was not matched."), - fieldWithPath("negativeMatches.*.matched") - .description("Conditions that were matched."), - fieldWithPath("negativeMatches.*.matched.[].condition") - .description("Name of the condition.") - .type(JsonFieldType.STRING).optional(), - fieldWithPath("negativeMatches.*.matched.[].message") - .description( - "Details of why the condition was matched.") - .type(JsonFieldType.STRING).optional(), - fieldWithPath("unconditionalClasses").description( - "Names of unconditional auto-configuration classes, if any.")))); + preprocessResponse( + limit("contexts", getApplicationContext().getId(), + "positiveMatches"), + limit("contexts", getApplicationContext().getId(), + "negativeMatches")), + responseFields(fieldWithPath("contexts") + .description("Application contexts keyed by id.")) + .andWithPrefix("contexts.*.positiveMatches", + positiveMatchFields) + .andWithPrefix("contexts.*.negativeMatches", + negativeMatchFields) + .and(unconditionalClassesField, + parentIdField()))); } @Configuration @@ -109,12 +120,12 @@ public class ConditionsReportEndpointDocumentationTests @Bean public ConditionsReportEndpoint autoConfigurationReportEndpoint( - ConfigurableListableBeanFactory beanFactory) { + ConfigurableApplicationContext context) { ConditionEvaluationReport conditionEvaluationReport = ConditionEvaluationReport - .get(beanFactory); + .get(context.getBeanFactory()); conditionEvaluationReport.recordEvaluationCandidates( Arrays.asList(PropertyPlaceholderAutoConfiguration.class.getName())); - return new ConditionsReportEndpoint(conditionEvaluationReport); + return new ConditionsReportEndpoint(context); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ConfigurationPropertiesReportEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ConfigurationPropertiesReportEndpointDocumentationTests.java index 8b5e350c4d..c80ee76dde 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ConfigurationPropertiesReportEndpointDocumentationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/ConfigurationPropertiesReportEndpointDocumentationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -23,7 +23,6 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation; -import org.springframework.restdocs.payload.JsonFieldType; import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; @@ -45,21 +44,19 @@ public class ConfigurationPropertiesReportEndpointDocumentationTests public void configProps() throws Exception { this.mockMvc.perform(get("/actuator/configprops")).andExpect(status().isOk()) .andDo(MockMvcRestDocumentation.document("configprops", - preprocessResponse(limit("beans")), + preprocessResponse(limit("contexts", + getApplicationContext().getId(), "beans")), responseFields( - fieldWithPath("contextId") - .description("ID of the application context."), - fieldWithPath("beans.*").description( + fieldWithPath("contexts") + .description("Application contexts keyed by id."), + fieldWithPath("contexts.*.beans.*").description( "`@ConfigurationProperties` beans keyed by bean name."), - fieldWithPath("beans.*.prefix").description( + fieldWithPath("contexts.*.beans.*.prefix").description( "Prefix applied to the names of the bean's properties."), - subsectionWithPath("beans.*.properties").description( - "Properties of the bean as name-value pairs."), - subsectionWithPath("parent") + subsectionWithPath("contexts.*.beans.*.properties") .description( - "`@ConfigurationProperties` beans in the parent " - + "context, if any.") - .optional().type(JsonFieldType.OBJECT)))); + "Properties of the bean as name-value pairs."), + parentIdField()))); } @Configuration diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/FlywayEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/FlywayEndpointDocumentationTests.java index e283c12a20..55a198d4df 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/FlywayEndpointDocumentationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/FlywayEndpointDocumentationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -18,9 +18,7 @@ package org.springframework.boot.actuate.autoconfigure.endpoint.web.documentatio import java.util.Arrays; import java.util.List; -import java.util.Map; -import org.flywaydb.core.Flyway; import org.flywaydb.core.api.MigrationState; import org.flywaydb.core.api.MigrationType; import org.junit.Test; @@ -29,6 +27,7 @@ import org.springframework.boot.actuate.flyway.FlywayEndpoint; import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration; import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -52,11 +51,16 @@ public class FlywayEndpointDocumentationTests extends AbstractEndpointDocumentat public void flyway() throws Exception { this.mockMvc.perform(get("/actuator/flyway")).andExpect(status().isOk()) .andDo(MockMvcRestDocumentation.document("flyway", - responseFields(fieldWithPath("*.migrations").description( + responseFields( + fieldWithPath("contexts") + .description("Application contexts keyed by id"), + fieldWithPath("contexts.*.flywayBeans.*.migrations").description( "Migrations performed by the Flyway instance, keyed by" - + " bean name.")).andWithPrefix( - "*.migrations.[].", - migrationFieldDescriptors()))); + + " Flyway bean name.")) + .andWithPrefix( + "contexts.*.flywayBeans.*.migrations.[].", + migrationFieldDescriptors()) + .and(parentIdField()))); } private List migrationFieldDescriptors() { @@ -100,8 +104,8 @@ public class FlywayEndpointDocumentationTests extends AbstractEndpointDocumentat static class TestConfiguration { @Bean - public FlywayEndpoint endpoint(Map flyways) { - return new FlywayEndpoint(flyways); + public FlywayEndpoint endpoint(ApplicationContext context) { + return new FlywayEndpoint(context); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/LiquibaseEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/LiquibaseEndpointDocumentationTests.java index 5001865d03..1056b603af 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/LiquibaseEndpointDocumentationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/LiquibaseEndpointDocumentationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -18,15 +18,14 @@ package org.springframework.boot.actuate.autoconfigure.endpoint.web.documentatio import java.util.Arrays; import java.util.List; -import java.util.Map; import liquibase.changelog.ChangeSet.ExecType; -import liquibase.integration.spring.SpringLiquibase; import org.junit.Test; import org.springframework.boot.actuate.liquibase.LiquibaseEndpoint; import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration; import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -48,12 +47,20 @@ public class LiquibaseEndpointDocumentationTests @Test public void liquibase() throws Exception { + FieldDescriptor changeSetsField = fieldWithPath( + "contexts.*.liquibaseBeans.*.changeSets") + .description("Change sets made by the Liquibase beans, keyed by " + + "bean name."); this.mockMvc.perform(get("/actuator/liquibase")).andExpect(status().isOk()) .andDo(MockMvcRestDocumentation.document("liquibase", - responseFields(fieldWithPath("*.changeSets").description( - "Change sets made by the Liquibase beans, keyed by " - + "bean name.")).andWithPrefix("*.changeSets[].", - getChangeSetFieldDescriptors()))); + responseFields( + fieldWithPath("contexts") + .description("Application contexts keyed by id"), + changeSetsField) + .andWithPrefix( + "contexts.*.liquibaseBeans.*.changeSets[].", + getChangeSetFieldDescriptors()) + .and(parentIdField()))); } private List getChangeSetFieldDescriptors() { @@ -86,8 +93,8 @@ public class LiquibaseEndpointDocumentationTests static class TestConfiguration { @Bean - public LiquibaseEndpoint endpoint(Map liquibases) { - return new LiquibaseEndpoint(liquibases); + public LiquibaseEndpoint endpoint(ApplicationContext context) { + return new LiquibaseEndpoint(context); } } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/beans/BeansEndpoint.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/beans/BeansEndpoint.java index 9d28b20c12..7fb7314a25 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/beans/BeansEndpoint.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/beans/BeansEndpoint.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -53,49 +53,73 @@ public class BeansEndpoint { } @ReadOperation - public ApplicationContextDescriptor beans() { - return ApplicationContextDescriptor.describing(this.context); + public ApplicationBeans beans() { + Map contexts = new HashMap<>(); + ConfigurableApplicationContext context = this.context; + while (context != null) { + contexts.put(context.getId(), ContextBeans.describing(context)); + context = getConfigurableParent(context); + } + return new ApplicationBeans(contexts); + } + + private static ConfigurableApplicationContext getConfigurableParent( + ConfigurableApplicationContext context) { + ApplicationContext parent = context.getParent(); + if (parent instanceof ConfigurableApplicationContext) { + return (ConfigurableApplicationContext) parent; + } + return null; + } + + /** + * A description of an application's beans, primarily intended for serialization to + * JSON. + */ + public static final class ApplicationBeans { + + private final Map contexts; + + private ApplicationBeans(Map contexts) { + this.contexts = contexts; + } + + public Map getContexts() { + return this.contexts; + } + } /** * A description of an application context, primarily intended for serialization to * JSON. */ - public static final class ApplicationContextDescriptor { - - private final String contextId; + public static final class ContextBeans { private final Map beans; - private final ApplicationContextDescriptor parent; + private final String parentId; - private ApplicationContextDescriptor(String contextId, - Map beans, ApplicationContextDescriptor parent) { - this.contextId = contextId; + private ContextBeans(Map beans, String parentId) { this.beans = beans; - this.parent = parent; + this.parentId = parentId; } - public String getContextId() { - return this.contextId; - } - - public ApplicationContextDescriptor getParent() { - return this.parent; + public String getParentId() { + return this.parentId; } public Map getBeans() { return this.beans; } - private static ApplicationContextDescriptor describing( - ConfigurableApplicationContext context) { + private static ContextBeans describing(ConfigurableApplicationContext context) { if (context == null) { return null; } - return new ApplicationContextDescriptor(context.getId(), - describeBeans(context.getBeanFactory()), - describing(getConfigurableParent(context))); + ConfigurableApplicationContext parent = getConfigurableParent(context); + return new ContextBeans(describeBeans(context.getBeanFactory()), + parent == null ? null : parent.getId()); } private static Map describeBeans( @@ -123,15 +147,6 @@ public class BeansEndpoint { && (!bd.isLazyInit() || bf.containsSingleton(beanName))); } - private static ConfigurableApplicationContext getConfigurableParent( - ConfigurableApplicationContext context) { - ApplicationContext parent = context.getParent(); - if (parent instanceof ConfigurableApplicationContext) { - return (ConfigurableApplicationContext) parent; - } - return null; - } - } /** diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpoint.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpoint.java index 818487264d..afc6b8356a 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpoint.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpoint.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -88,21 +88,25 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext } @ReadOperation - public ConfigurationPropertiesDescriptor configurationProperties() { + public ApplicationConfigurationProperties configurationProperties() { return extract(this.context); } - private ConfigurationPropertiesDescriptor extract(ApplicationContext context) { + private ApplicationConfigurationProperties extract(ApplicationContext context) { ObjectMapper mapper = new ObjectMapper(); configureObjectMapper(mapper); - return describeConfigurationProperties(context, mapper); + Map contextProperties = new HashMap<>(); + ApplicationContext target = context; + while (target != null) { + contextProperties.put(target.getId(), + describeConfigurationProperties(target, mapper)); + target = target.getParent(); + } + return new ApplicationConfigurationProperties(contextProperties); } - private ConfigurationPropertiesDescriptor describeConfigurationProperties( + private ContextConfigurationProperties describeConfigurationProperties( ApplicationContext context, ObjectMapper mapper) { - if (context == null) { - return null; - } ConfigurationBeanFactoryMetaData beanFactoryMetaData = getBeanFactoryMetaData( context); Map beans = getConfigurationPropertiesBeans(context, @@ -115,8 +119,8 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext beanDescriptors.put(beanName, new ConfigurationPropertiesBeanDescriptor( prefix, sanitize(prefix, safeSerialize(mapper, bean, prefix)))); } - return new ConfigurationPropertiesDescriptor(context.getId(), beanDescriptors, - describeConfigurationProperties(context.getParent(), mapper)); + return new ContextConfigurationProperties(beanDescriptors, + context.getParent() == null ? null : context.getParent().getId()); } private ConfigurationBeanFactoryMetaData getBeanFactoryMetaData( @@ -389,36 +393,48 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext } + /** + * A description of an application's {@link ConfigurationProperties} beans. Primarily + * intended for serialization to JSON. + */ + public static final class ApplicationConfigurationProperties { + + private final Map contexts; + + private ApplicationConfigurationProperties( + Map contexts) { + this.contexts = contexts; + } + + public Map getContexts() { + return this.contexts; + } + + } + /** * A description of an application context's {@link ConfigurationProperties} beans. * Primarily intended for serialization to JSON. */ - public static final class ConfigurationPropertiesDescriptor { - - private final String contextId; + public static final class ContextConfigurationProperties { private final Map beans; - private final ConfigurationPropertiesDescriptor parent; + private final String parentId; - private ConfigurationPropertiesDescriptor(String contextId, + private ContextConfigurationProperties( Map beans, - ConfigurationPropertiesDescriptor parent) { - this.contextId = contextId; + String parentId) { this.beans = beans; - this.parent = parent; - } - - public String getContextId() { - return this.contextId; + this.parentId = parentId; } public Map getBeans() { return this.beans; } - public ConfigurationPropertiesDescriptor getParent() { - return this.parent; + public String getParentId() { + return this.parentId; } } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/flyway/FlywayEndpoint.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/flyway/FlywayEndpoint.java index 37c7d4ae85..23881a9b66 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/flyway/FlywayEndpoint.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/flyway/FlywayEndpoint.java @@ -30,7 +30,7 @@ import org.flywaydb.core.api.MigrationType; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; -import org.springframework.util.Assert; +import org.springframework.context.ApplicationContext; /** * {@link Endpoint} to expose flyway info. @@ -43,34 +43,85 @@ import org.springframework.util.Assert; @Endpoint(id = "flyway") public class FlywayEndpoint { - private final Map flywayBeans; + private final ApplicationContext context; - public FlywayEndpoint(Map flywayBeans) { - Assert.notEmpty(flywayBeans, "FlywayBeans must be specified"); - this.flywayBeans = flywayBeans; + public FlywayEndpoint(ApplicationContext context) { + this.context = context; } @ReadOperation - public Map flywayReports() { - Map reports = new HashMap<>(); - this.flywayBeans.forEach((name, flyway) -> reports.put(name, - new FlywayReport(flyway.info().all()))); - return reports; + public ApplicationFlywayBeans flywayBeans() { + ApplicationContext target = this.context; + Map contextFlywayBeans = new HashMap<>(); + while (target != null) { + Map flywayBeans = new HashMap<>(); + target.getBeansOfType(Flyway.class).forEach((name, flyway) -> flywayBeans + .put(name, new FlywayDescriptor(flyway.info().all()))); + ApplicationContext parent = target.getParent(); + contextFlywayBeans.put(target.getId(), new ContextFlywayBeans(flywayBeans, + parent == null ? null : parent.getId())); + target = parent; + } + return new ApplicationFlywayBeans(contextFlywayBeans); } /** - * Report for one {@link Flyway} instance. + * Description of an application's {@link Flyway} beans, primarily intended for + * serialization to JSON. */ - public static class FlywayReport { + public static final class ApplicationFlywayBeans { + + private final Map contexts; + + private ApplicationFlywayBeans(Map contexts) { + this.contexts = contexts; + } + + public Map getContexts() { + return this.contexts; + } + + } + + /** + * Description of an application context's {@link Flyway} beans, primarily intended + * for serialization to JSON. + */ + public static final class ContextFlywayBeans { + + private final Map flywayBeans; + + private final String parentId; + + private ContextFlywayBeans(Map flywayBeans, + String parentId) { + this.flywayBeans = flywayBeans; + this.parentId = parentId; + } + + public Map getFlywayBeans() { + return this.flywayBeans; + } + + public String getParentId() { + return this.parentId; + } + + } + + /** + * Description of a {@link Flyway} bean, primarly intended for serialization to JSON. + */ + public static class FlywayDescriptor { private final List migrations; - public FlywayReport(MigrationInfo[] migrations) { + private FlywayDescriptor(MigrationInfo[] migrations) { this.migrations = Stream.of(migrations).map(FlywayMigration::new) .collect(Collectors.toList()); } - public FlywayReport(List migrations) { + public FlywayDescriptor(List migrations) { this.migrations = migrations; } @@ -83,7 +134,7 @@ public class FlywayEndpoint { /** * Details of a migration performed by Flyway. */ - public static class FlywayMigration { + public static final class FlywayMigration { private final MigrationType type; @@ -105,7 +156,7 @@ public class FlywayEndpoint { private final Integer executionTime; - public FlywayMigration(MigrationInfo info) { + private FlywayMigration(MigrationInfo info) { this.type = info.getType(); this.checksum = info.getChecksum(); this.version = nullSafeToString(info.getVersion()); diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/liquibase/LiquibaseEndpoint.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/liquibase/LiquibaseEndpoint.java index 012305e688..80236bd29e 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/liquibase/LiquibaseEndpoint.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/liquibase/LiquibaseEndpoint.java @@ -36,6 +36,7 @@ import liquibase.integration.spring.SpringLiquibase; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; +import org.springframework.context.ApplicationContext; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -48,24 +49,33 @@ import org.springframework.util.StringUtils; @Endpoint(id = "liquibase") public class LiquibaseEndpoint { - private final Map liquibaseBeans; + private final ApplicationContext context; - public LiquibaseEndpoint(Map liquibaseBeans) { - Assert.notEmpty(liquibaseBeans, "LiquibaseBeans must be specified"); - this.liquibaseBeans = liquibaseBeans; + public LiquibaseEndpoint(ApplicationContext context) { + Assert.notNull(context, "Context must be specified"); + this.context = context; } @ReadOperation - public Map liquibaseReports() { - Map reports = new HashMap<>(); - DatabaseFactory factory = DatabaseFactory.getInstance(); - StandardChangeLogHistoryService service = new StandardChangeLogHistoryService(); - this.liquibaseBeans.forEach((name, liquibase) -> reports.put(name, - createReport(liquibase, service, factory))); - return reports; + public ApplicationLiquibaseBeans liquibaseBeans() { + ApplicationContext target = this.context; + Map contextBeans = new HashMap<>(); + while (target != null) { + Map liquibaseBeans = new HashMap<>(); + DatabaseFactory factory = DatabaseFactory.getInstance(); + StandardChangeLogHistoryService service = new StandardChangeLogHistoryService(); + this.context.getBeansOfType(SpringLiquibase.class) + .forEach((name, liquibase) -> liquibaseBeans.put(name, + createReport(liquibase, service, factory))); + ApplicationContext parent = target.getParent(); + contextBeans.put(target.getId(), new ContextLiquibaseBeans(liquibaseBeans, + parent == null ? null : parent.getId())); + target = parent; + } + return new ApplicationLiquibaseBeans(contextBeans); } - private LiquibaseReport createReport(SpringLiquibase liquibase, + private LiquibaseBean createReport(SpringLiquibase liquibase, ChangeLogHistoryService service, DatabaseFactory factory) { try { DataSource dataSource = liquibase.getDataSource(); @@ -77,7 +87,7 @@ public class LiquibaseEndpoint { database.setDefaultSchemaName(defaultSchema); } service.setDatabase(database); - return new LiquibaseReport(service.getRanChangeSets().stream() + return new LiquibaseBean(service.getRanChangeSets().stream() .map(ChangeSet::new).collect(Collectors.toList())); } finally { @@ -90,13 +100,58 @@ public class LiquibaseEndpoint { } /** - * Report for a single {@link SpringLiquibase} instance. + * Description of an application's {@link SpringLiquibase} beans, primarily intended + * for serialization to JSON. */ - public static class LiquibaseReport { + public static final class ApplicationLiquibaseBeans { + + private final Map contexts; + + private ApplicationLiquibaseBeans(Map contexts) { + this.contexts = contexts; + } + + public Map getContexts() { + return this.contexts; + } + + } + + /** + * Description of an application context's {@link SpringLiquibase} beans, primarily + * intended for serialization to JSON. + */ + public static final class ContextLiquibaseBeans { + + private final Map liquibaseBeans; + + private final String parentId; + + private ContextLiquibaseBeans(Map liquibaseBeans, + String parentId) { + this.liquibaseBeans = liquibaseBeans; + this.parentId = parentId; + } + + public Map getLiquibaseBeans() { + return this.liquibaseBeans; + } + + public String getParentId() { + return this.parentId; + } + + } + + /** + * Description of a {@link SpringLiquibase} bean, primarly intended for serialization + * to JSON. + */ + public static final class LiquibaseBean { private final List changeSets; - public LiquibaseReport(List changeSets) { + public LiquibaseBean(List changeSets) { this.changeSets = changeSets; } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/beans/BeansEndpointTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/beans/BeansEndpointTests.java index 7cd0787313..34e5dbb658 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/beans/BeansEndpointTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/beans/BeansEndpointTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -25,8 +25,9 @@ import org.junit.Test; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.boot.actuate.beans.BeansEndpoint.ApplicationContextDescriptor; +import org.springframework.boot.actuate.beans.BeansEndpoint.ApplicationBeans; import org.springframework.boot.actuate.beans.BeansEndpoint.BeanDescriptor; +import org.springframework.boot.actuate.beans.BeansEndpoint.ContextBeans; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; @@ -48,11 +49,10 @@ public class BeansEndpointTests { ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withUserConfiguration(EndpointConfiguration.class); contextRunner.run((context) -> { - ApplicationContextDescriptor result = context.getBean(BeansEndpoint.class) - .beans(); - assertThat(result.getParent()).isNull(); - assertThat(result.getContextId()).isEqualTo(context.getId()); - Map beans = result.getBeans(); + ApplicationBeans result = context.getBean(BeansEndpoint.class).beans(); + ContextBeans descriptor = result.getContexts().get(context.getId()); + assertThat(descriptor.getParentId()).isNull(); + Map beans = descriptor.getBeans(); assertThat(beans.size()) .isLessThanOrEqualTo(context.getBeanDefinitionCount()); assertThat(beans).containsKey("endpoint"); @@ -70,9 +70,9 @@ public class BeansEndpointTests { .filter((name) -> BeanDefinition.ROLE_INFRASTRUCTURE == factory .getBeanDefinition(name).getRole()) .collect(Collectors.toList()); - ApplicationContextDescriptor result = context.getBean(BeansEndpoint.class) - .beans(); - Map beans = result.getBeans(); + ApplicationBeans result = context.getBean(BeansEndpoint.class).beans(); + ContextBeans contextDescriptor = result.getContexts().get(context.getId()); + Map beans = contextDescriptor.getBeans(); for (String infrastructureBean : infrastructureBeans) { assertThat(beans).doesNotContainKey(infrastructureBean); } @@ -85,10 +85,10 @@ public class BeansEndpointTests { .withUserConfiguration(EndpointConfiguration.class, LazyBeanConfiguration.class); contextRunner.run((context) -> { - ApplicationContextDescriptor result = context.getBean(BeansEndpoint.class) - .beans(); + ApplicationBeans result = context.getBean(BeansEndpoint.class).beans(); + ContextBeans contextDescriptor = result.getContexts().get(context.getId()); assertThat(context).hasBean("lazyBean"); - assertThat(result.getBeans()).doesNotContainKey("lazyBean"); + assertThat(contextDescriptor.getBeans()).doesNotContainKey("lazyBean"); }); } @@ -100,10 +100,11 @@ public class BeansEndpointTests { new ApplicationContextRunner() .withUserConfiguration(EndpointConfiguration.class).withParent(parent) .run(child -> { - BeansEndpoint endpoint = child.getBean(BeansEndpoint.class); - ApplicationContextDescriptor result = endpoint.beans(); - assertThat(result.getParent().getBeans()).containsKey("bean"); - assertThat(result.getBeans()).containsKey("endpoint"); + ApplicationBeans result = child.getBean(BeansEndpoint.class).beans(); + assertThat(result.getContexts().get(parent.getId()).getBeans()) + .containsKey("bean"); + assertThat(result.getContexts().get(child.getId()).getBeans()) + .containsKey("endpoint"); }); }); } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpointMethodAnnotationsTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpointMethodAnnotationsTests.java index a5662a1207..9d961bcd8c 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpointMethodAnnotationsTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpointMethodAnnotationsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -18,8 +18,9 @@ package org.springframework.boot.actuate.context.properties; import org.junit.Test; +import org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint.ApplicationConfigurationProperties; import org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint.ConfigurationPropertiesBeanDescriptor; -import org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint.ConfigurationPropertiesDescriptor; +import org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint.ContextConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.test.context.runner.ApplicationContextRunner; @@ -44,9 +45,13 @@ public class ConfigurationPropertiesReportEndpointMethodAnnotationsTests { contextRunner.run((context) -> { ConfigurationPropertiesReportEndpoint endpoint = context .getBean(ConfigurationPropertiesReportEndpoint.class); - ConfigurationPropertiesDescriptor properties = endpoint + ApplicationConfigurationProperties applicationProperties = endpoint .configurationProperties(); - ConfigurationPropertiesBeanDescriptor other = properties.getBeans() + assertThat(applicationProperties.getContexts()) + .containsOnlyKeys(context.getId()); + ContextConfigurationProperties contextProperties = applicationProperties + .getContexts().get(context.getId()); + ConfigurationPropertiesBeanDescriptor other = contextProperties.getBeans() .get("other"); assertThat(other).isNotNull(); assertThat(other.getPrefix()).isEqualTo("other"); @@ -63,9 +68,14 @@ public class ConfigurationPropertiesReportEndpointMethodAnnotationsTests { contextRunner.run((context) -> { ConfigurationPropertiesReportEndpoint endpoint = context .getBean(ConfigurationPropertiesReportEndpoint.class); - ConfigurationPropertiesDescriptor properties = endpoint + ApplicationConfigurationProperties applicationProperties = endpoint .configurationProperties(); - ConfigurationPropertiesBeanDescriptor bar = properties.getBeans().get("bar"); + assertThat(applicationProperties.getContexts()) + .containsOnlyKeys(context.getId()); + ContextConfigurationProperties contextProperties = applicationProperties + .getContexts().get(context.getId()); + ConfigurationPropertiesBeanDescriptor bar = contextProperties.getBeans() + .get("bar"); assertThat(bar).isNotNull(); assertThat(bar.getPrefix()).isEqualTo("other"); assertThat(bar.getProperties()).isNotNull(); diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpointParentTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpointParentTests.java index 91bb8d9e6d..e2b6e4eca4 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpointParentTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpointParentTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -18,7 +18,7 @@ package org.springframework.boot.actuate.context.properties; import org.junit.Test; -import org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint.ConfigurationPropertiesDescriptor; +import org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint.ApplicationConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.test.context.runner.ApplicationContextRunner; @@ -45,12 +45,15 @@ public class ConfigurationPropertiesReportEndpointParentTests { .withParent(parent).run(child -> { ConfigurationPropertiesReportEndpoint endpoint = child .getBean(ConfigurationPropertiesReportEndpoint.class); - ConfigurationPropertiesDescriptor result = endpoint + ApplicationConfigurationProperties applicationProperties = endpoint .configurationProperties(); - assertThat(result.getBeans().keySet()) - .containsExactlyInAnyOrder("someProperties"); - assertThat((result.getParent().getBeans().keySet())) - .containsExactly("testProperties"); + assertThat(applicationProperties.getContexts()) + .containsOnlyKeys(child.getId(), parent.getId()); + assertThat(applicationProperties.getContexts().get(child.getId()) + .getBeans().keySet()).containsExactly("someProperties"); + assertThat((applicationProperties.getContexts() + .get(parent.getId()).getBeans().keySet())) + .containsExactly("testProperties"); }); }); } @@ -65,12 +68,14 @@ public class ConfigurationPropertiesReportEndpointParentTests { .withParent(parent).run(child -> { ConfigurationPropertiesReportEndpoint endpoint = child .getBean(ConfigurationPropertiesReportEndpoint.class); - ConfigurationPropertiesDescriptor result = endpoint + ApplicationConfigurationProperties applicationProperties = endpoint .configurationProperties(); - assertThat(result.getBeans().keySet()) - .containsExactlyInAnyOrder("otherProperties"); - assertThat((result.getParent().getBeans().keySet())) - .containsExactly("testProperties"); + assertThat(applicationProperties.getContexts().get(child.getId()) + .getBeans().keySet()) + .containsExactlyInAnyOrder("otherProperties"); + assertThat((applicationProperties.getContexts() + .get(parent.getId()).getBeans().keySet())) + .containsExactly("testProperties"); }); }); } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpointProxyTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpointProxyTests.java index 18527e1918..a5adf3997d 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpointProxyTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpointProxyTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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,8 +20,8 @@ import javax.sql.DataSource; import org.junit.Test; +import org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint.ApplicationConfigurationProperties; import org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint.ConfigurationPropertiesBeanDescriptor; -import org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint.ConfigurationPropertiesDescriptor; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.test.context.runner.ApplicationContextRunner; @@ -52,10 +52,11 @@ public class ConfigurationPropertiesReportEndpointProxyTests { ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withUserConfiguration(Config.class, SqlExecutor.class); contextRunner.run((context) -> { - ConfigurationPropertiesDescriptor report = context + ApplicationConfigurationProperties applicationProperties = context .getBean(ConfigurationPropertiesReportEndpoint.class) .configurationProperties(); - assertThat(report.getBeans().values().stream() + assertThat(applicationProperties.getContexts().get(context.getId()).getBeans() + .values().stream() .map(ConfigurationPropertiesBeanDescriptor::getPrefix) .filter("executor.sql"::equals).findFirst()).isNotEmpty(); }); diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpointSerializationTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpointSerializationTests.java index 3b2f39854d..e9752550c9 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpointSerializationTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpointSerializationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -26,8 +26,8 @@ import com.zaxxer.hikari.HikariDataSource; import org.junit.Ignore; import org.junit.Test; +import org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint.ApplicationConfigurationProperties; import org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint.ConfigurationPropertiesBeanDescriptor; -import org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint.ConfigurationPropertiesDescriptor; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.test.context.runner.ApplicationContextRunner; @@ -55,9 +55,10 @@ public class ConfigurationPropertiesReportEndpointSerializationTests { contextRunner.run((context) -> { ConfigurationPropertiesReportEndpoint endpoint = context .getBean(ConfigurationPropertiesReportEndpoint.class); - ConfigurationPropertiesDescriptor properties = endpoint + ApplicationConfigurationProperties applicationProperties = endpoint .configurationProperties(); - ConfigurationPropertiesBeanDescriptor foo = properties.getBeans().get("foo"); + ConfigurationPropertiesBeanDescriptor foo = applicationProperties + .getContexts().get(context.getId()).getBeans().get("foo"); assertThat(foo).isNotNull(); assertThat(foo.getPrefix()).isEqualTo("foo"); Map map = foo.getProperties(); @@ -76,9 +77,10 @@ public class ConfigurationPropertiesReportEndpointSerializationTests { contextRunner.run((context) -> { ConfigurationPropertiesReportEndpoint endpoint = context .getBean(ConfigurationPropertiesReportEndpoint.class); - ConfigurationPropertiesDescriptor properties = endpoint + ApplicationConfigurationProperties applicationProperties = endpoint .configurationProperties(); - ConfigurationPropertiesBeanDescriptor foo = properties.getBeans().get("foo"); + ConfigurationPropertiesBeanDescriptor foo = applicationProperties + .getContexts().get(context.getId()).getBeans().get("foo"); assertThat(foo).isNotNull(); Map map = foo.getProperties(); assertThat(map).isNotNull(); @@ -97,9 +99,10 @@ public class ConfigurationPropertiesReportEndpointSerializationTests { contextRunner.run((context) -> { ConfigurationPropertiesReportEndpoint endpoint = context .getBean(ConfigurationPropertiesReportEndpoint.class); - ConfigurationPropertiesDescriptor properties = endpoint + ApplicationConfigurationProperties applicationProperties = endpoint .configurationProperties(); - ConfigurationPropertiesBeanDescriptor foo = properties.getBeans().get("foo"); + ConfigurationPropertiesBeanDescriptor foo = applicationProperties + .getContexts().get(context.getId()).getBeans().get("foo"); assertThat(foo.getPrefix()).isEqualTo("foo"); Map map = foo.getProperties(); assertThat(map).isNotNull(); @@ -119,10 +122,10 @@ public class ConfigurationPropertiesReportEndpointSerializationTests { contextRunner.run((context) -> { ConfigurationPropertiesReportEndpoint endpoint = context .getBean(ConfigurationPropertiesReportEndpoint.class); - ConfigurationPropertiesDescriptor properties = endpoint + ApplicationConfigurationProperties applicationProperties = endpoint .configurationProperties(); - ConfigurationPropertiesBeanDescriptor cycle = properties.getBeans() - .get("cycle"); + ConfigurationPropertiesBeanDescriptor cycle = applicationProperties + .getContexts().get(context.getId()).getBeans().get("cycle"); assertThat(cycle.getPrefix()).isEqualTo("cycle"); Map map = cycle.getProperties(); assertThat(map).isNotNull(); @@ -140,10 +143,10 @@ public class ConfigurationPropertiesReportEndpointSerializationTests { contextRunner.run((context) -> { ConfigurationPropertiesReportEndpoint endpoint = context .getBean(ConfigurationPropertiesReportEndpoint.class); - ConfigurationPropertiesDescriptor properties = endpoint + ApplicationConfigurationProperties applicationProperties = endpoint .configurationProperties(); - ConfigurationPropertiesBeanDescriptor fooProperties = properties.getBeans() - .get("foo"); + ConfigurationPropertiesBeanDescriptor fooProperties = applicationProperties + .getContexts().get(context.getId()).getBeans().get("foo"); assertThat(fooProperties).isNotNull(); assertThat(fooProperties.getPrefix()).isEqualTo("foo"); Map map = fooProperties.getProperties(); @@ -161,9 +164,10 @@ public class ConfigurationPropertiesReportEndpointSerializationTests { contextRunner.run((context) -> { ConfigurationPropertiesReportEndpoint endpoint = context .getBean(ConfigurationPropertiesReportEndpoint.class); - ConfigurationPropertiesDescriptor properties = endpoint + ApplicationConfigurationProperties applicationProperties = endpoint .configurationProperties(); - ConfigurationPropertiesBeanDescriptor foo = properties.getBeans().get("foo"); + ConfigurationPropertiesBeanDescriptor foo = applicationProperties + .getContexts().get(context.getId()).getBeans().get("foo"); assertThat(foo).isNotNull(); assertThat(foo.getPrefix()).isEqualTo("foo"); Map map = foo.getProperties(); @@ -182,9 +186,10 @@ public class ConfigurationPropertiesReportEndpointSerializationTests { contextRunner.run((context) -> { ConfigurationPropertiesReportEndpoint endpoint = context .getBean(ConfigurationPropertiesReportEndpoint.class); - ConfigurationPropertiesDescriptor properties = endpoint + ApplicationConfigurationProperties applicationProperties = endpoint .configurationProperties(); - ConfigurationPropertiesBeanDescriptor foo = properties.getBeans().get("foo"); + ConfigurationPropertiesBeanDescriptor foo = applicationProperties + .getContexts().get(context.getId()).getBeans().get("foo"); assertThat(foo).isNotNull(); assertThat(foo.getPrefix()).isEqualTo("foo"); Map map = foo.getProperties(); @@ -202,9 +207,10 @@ public class ConfigurationPropertiesReportEndpointSerializationTests { contextRunner.run((context) -> { ConfigurationPropertiesReportEndpoint endpoint = context .getBean(ConfigurationPropertiesReportEndpoint.class); - ConfigurationPropertiesDescriptor properties = endpoint + ApplicationConfigurationProperties applicationProperties = endpoint .configurationProperties(); - ConfigurationPropertiesBeanDescriptor foo = properties.getBeans().get("foo"); + ConfigurationPropertiesBeanDescriptor foo = applicationProperties + .getContexts().get(context.getId()).getBeans().get("foo"); assertThat(foo).isNotNull(); assertThat(foo.getPrefix()).isEqualTo("foo"); Map map = foo.getProperties(); @@ -223,10 +229,10 @@ public class ConfigurationPropertiesReportEndpointSerializationTests { contextRunner.run((context) -> { ConfigurationPropertiesReportEndpoint endpoint = context .getBean(ConfigurationPropertiesReportEndpoint.class); - ConfigurationPropertiesDescriptor properties = endpoint + ApplicationConfigurationProperties applicationProperties = endpoint .configurationProperties(); - assertThat(properties.getBeans()).containsKeys("foo"); - ConfigurationPropertiesBeanDescriptor foo = properties.getBeans().get("foo"); + ConfigurationPropertiesBeanDescriptor foo = applicationProperties + .getContexts().get(context.getId()).getBeans().get("foo"); assertThat(foo.getPrefix()).isEqualTo("foo"); Map propertiesMap = foo.getProperties(); assertThat(propertiesMap).containsOnlyKeys("bar", "name", "map", "list"); @@ -244,9 +250,10 @@ public class ConfigurationPropertiesReportEndpointSerializationTests { contextRunner.run((context) -> { ConfigurationPropertiesReportEndpoint endpoint = context .getBean(ConfigurationPropertiesReportEndpoint.class); - ConfigurationPropertiesDescriptor properties = endpoint + ApplicationConfigurationProperties applicationProperties = endpoint .configurationProperties(); - ConfigurationPropertiesBeanDescriptor hikariDataSource = properties.getBeans() + ConfigurationPropertiesBeanDescriptor hikariDataSource = applicationProperties + .getContexts().get(context.getId()).getBeans() .get("hikariDataSource"); Map nestedProperties = hikariDataSource.getProperties(); assertThat(nestedProperties).doesNotContainKey("error"); diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpointTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpointTests.java index 76dd872933..d5450dcbee 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpointTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpointTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -27,7 +27,7 @@ import java.util.function.BiConsumer; import org.junit.Test; import org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint.ConfigurationPropertiesBeanDescriptor; -import org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint.ConfigurationPropertiesDescriptor; +import org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint.ContextConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.test.context.runner.ApplicationContextRunner; @@ -49,7 +49,6 @@ public class ConfigurationPropertiesReportEndpointTests { @Test public void configurationPropertiesAreReturned() { load((context, properties) -> { - assertThat(properties.getContextId()).isEqualTo(context.getId()); assertThat(properties.getBeans().size()).isGreaterThan(0); ConfigurationPropertiesBeanDescriptor nestedProperties = properties.getBeans() .get("testProperties"); @@ -161,17 +160,17 @@ public class ConfigurationPropertiesReportEndpointTests { } private void load( - BiConsumer properties) { + BiConsumer properties) { load(Collections.emptyList(), properties); } private void load(String keyToSanitize, - BiConsumer properties) { + BiConsumer properties) { load(Collections.singletonList(keyToSanitize), properties); } private void load(List keysToSanitize, - BiConsumer properties) { + BiConsumer properties) { ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withUserConfiguration(Config.class); contextRunner.run((context) -> { @@ -181,7 +180,8 @@ public class ConfigurationPropertiesReportEndpointTests { endpoint.setKeysToSanitize( keysToSanitize.toArray(new String[keysToSanitize.size()])); } - properties.accept(context, endpoint.configurationProperties()); + properties.accept(context, endpoint.configurationProperties().getContexts() + .get(context.getId())); }); } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/flyway/FlywayEndpointTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/flyway/FlywayEndpointTests.java index 2b6fcf65d9..360d117b7c 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/flyway/FlywayEndpointTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/flyway/FlywayEndpointTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -16,14 +16,12 @@ package org.springframework.boot.actuate.flyway; -import java.util.Map; - -import org.flywaydb.core.Flyway; import org.junit.Test; import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration; import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -43,8 +41,8 @@ public class FlywayEndpointTests { public void flywayReportIsProduced() { new ApplicationContextRunner().withUserConfiguration(Config.class) .run((context) -> assertThat( - context.getBean(FlywayEndpoint.class).flywayReports()) - .hasSize(1)); + context.getBean(FlywayEndpoint.class).flywayBeans().getContexts() + .get(context.getId()).getFlywayBeans()).hasSize(1)); } @Configuration @@ -52,8 +50,8 @@ public class FlywayEndpointTests { public static class Config { @Bean - public FlywayEndpoint endpoint(Map flyways) { - return new FlywayEndpoint(flyways); + public FlywayEndpoint endpoint(ApplicationContext context) { + return new FlywayEndpoint(context); } } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/liquibase/LiquibaseEndpointTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/liquibase/LiquibaseEndpointTests.java index e61aa68364..014e95f7f5 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/liquibase/LiquibaseEndpointTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/liquibase/LiquibaseEndpointTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -16,15 +16,13 @@ package org.springframework.boot.actuate.liquibase; -import java.util.Map; - -import liquibase.integration.spring.SpringLiquibase; import org.junit.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration; import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -48,8 +46,9 @@ public class LiquibaseEndpointTests { public void liquibaseReportIsReturned() { this.contextRunner.withUserConfiguration(Config.class) .run((context) -> assertThat( - context.getBean(LiquibaseEndpoint.class).liquibaseReports()) - .hasSize(1)); + context.getBean(LiquibaseEndpoint.class).liquibaseBeans() + .getContexts().get(context.getId()).getLiquibaseBeans()) + .hasSize(1)); } @Test @@ -58,16 +57,17 @@ public class LiquibaseEndpointTests { .withPropertyValues("spring.liquibase.default-schema=CUSTOMSCHEMA", "spring.datasource.schema=classpath:/db/create-custom-schema.sql") .run((context) -> assertThat( - context.getBean(LiquibaseEndpoint.class).liquibaseReports()) - .hasSize(1)); + context.getBean(LiquibaseEndpoint.class).liquibaseBeans() + .getContexts().get(context.getId()).getLiquibaseBeans()) + .hasSize(1)); } @Configuration public static class Config { @Bean - public LiquibaseEndpoint endpoint(Map liquibases) { - return new LiquibaseEndpoint(liquibases); + public LiquibaseEndpoint endpoint(ApplicationContext context) { + return new LiquibaseEndpoint(context); } } diff --git a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/sample/actuator/SampleActuatorApplicationTests.java b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/sample/actuator/SampleActuatorApplicationTests.java index 02d086dee3..b49be6d0ee 100644 --- a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/sample/actuator/SampleActuatorApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/sample/actuator/SampleActuatorApplicationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -28,6 +28,7 @@ import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.context.ApplicationContext; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; @@ -51,6 +52,9 @@ public class SampleActuatorApplicationTests { @Autowired private TestRestTemplate restTemplate; + @Autowired + private ApplicationContext applicationContext; + @Test public void testHomeIsSecure() { @SuppressWarnings("rawtypes") @@ -217,9 +221,7 @@ public class SampleActuatorApplicationTests { .withBasicAuth("user", getPassword()) .getForEntity("/actuator/beans", Map.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(entity.getBody()).containsOnlyKeys("beans", "parent", "contextId"); - assertThat(((String) entity.getBody().get("contextId"))) - .startsWith("application"); + assertThat(entity.getBody()).containsOnlyKeys("contexts"); } @SuppressWarnings("unchecked") @@ -231,7 +233,11 @@ public class SampleActuatorApplicationTests { .getForEntity("/actuator/configprops", Map.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); Map body = entity.getBody(); - assertThat((Map) body.get("beans")) + Map contexts = (Map) body.get("contexts"); + Map context = (Map) contexts + .get(this.applicationContext.getId()); + Map beans = (Map) context.get("beans"); + assertThat(beans) .containsKey("spring.datasource-" + DataSourceProperties.class.getName()); }