Commit a6b30a3a authored by Andy Wilkinson's avatar Andy Wilkinson

Reflect context hierarchy in beans endpoint’s response structure

Closes gh-10156
parent ab548011
...@@ -16,8 +16,6 @@ ...@@ -16,8 +16,6 @@
package org.springframework.boot.actuate.endpoint; package org.springframework.boot.actuate.endpoint;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
...@@ -32,7 +30,7 @@ import org.springframework.context.ConfigurableApplicationContext; ...@@ -32,7 +30,7 @@ import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
/** /**
* {@link Endpoint} to expose details of an application's bean, grouped by application * {@link Endpoint} to expose details of an application's beans, grouped by application
* context. * context.
* *
* @author Dave Syer * @author Dave Syer
...@@ -55,50 +53,53 @@ public class BeansEndpoint { ...@@ -55,50 +53,53 @@ public class BeansEndpoint {
} }
@ReadOperation @ReadOperation
public Map<String, Object> beans() { public ApplicationContextDescriptor beans() {
List<ApplicationContextDescriptor> contexts = new ArrayList<>(); return ApplicationContextDescriptor.describing(this.context);
ConfigurableApplicationContext current = this.context;
while (current != null) {
contexts.add(ApplicationContextDescriptor.describing(current));
current = getConfigurableParent(current);
}
return Collections.singletonMap("contexts", contexts);
} }
private ConfigurableApplicationContext getConfigurableParent( /**
ConfigurableApplicationContext context) { * Response produced by the {@link BeansEndpoint}, primarily intended for
ApplicationContext parent = context.getParent(); * serialization to JSON.
if (parent instanceof ConfigurableApplicationContext) { */
return (ConfigurableApplicationContext) parent; public static class BeansEndpointResponse {
private List<ApplicationContextDescriptor> contexts;
public BeansEndpointResponse(List<ApplicationContextDescriptor> contexts) {
this.contexts = contexts;
} }
return null;
public List<ApplicationContextDescriptor> getContexts() {
return this.contexts;
}
} }
/** /**
* A description of an application context, primarily intended for serialization to * A description of an application context, primarily intended for serialization to
* JSON. * JSON.
*/ */
static final class ApplicationContextDescriptor { public static final class ApplicationContextDescriptor {
private final String id; private final String id;
private final String parentId;
private final Map<String, BeanDescriptor> beans; private final Map<String, BeanDescriptor> beans;
private ApplicationContextDescriptor(String id, String parentId, private final ApplicationContextDescriptor parent;
Map<String, BeanDescriptor> beans) {
private ApplicationContextDescriptor(String id, Map<String, BeanDescriptor> beans,
ApplicationContextDescriptor parent) {
this.id = id; this.id = id;
this.parentId = parentId;
this.beans = beans; this.beans = beans;
this.parent = parent;
} }
public String getId() { public String getId() {
return this.id; return this.id;
} }
public String getParentId() { public ApplicationContextDescriptor getParent() {
return this.parentId; return this.parent;
} }
public Map<String, BeanDescriptor> getBeans() { public Map<String, BeanDescriptor> getBeans() {
...@@ -107,10 +108,12 @@ public class BeansEndpoint { ...@@ -107,10 +108,12 @@ public class BeansEndpoint {
private static ApplicationContextDescriptor describing( private static ApplicationContextDescriptor describing(
ConfigurableApplicationContext context) { ConfigurableApplicationContext context) {
ApplicationContext parent = context.getParent(); if (context == null) {
return null;
}
return new ApplicationContextDescriptor(context.getId(), return new ApplicationContextDescriptor(context.getId(),
parent == null ? null : parent.getId(), describeBeans(context.getBeanFactory()),
describeBeans(context.getBeanFactory())); describing(getConfigurableParent(context)));
} }
private static Map<String, BeanDescriptor> describeBeans( private static Map<String, BeanDescriptor> describeBeans(
...@@ -138,13 +141,22 @@ public class BeansEndpoint { ...@@ -138,13 +141,22 @@ public class BeansEndpoint {
&& (!bd.isLazyInit() || bf.containsSingleton(beanName))); && (!bd.isLazyInit() || bf.containsSingleton(beanName)));
} }
private static ConfigurableApplicationContext getConfigurableParent(
ConfigurableApplicationContext context) {
ApplicationContext parent = context.getParent();
if (parent instanceof ConfigurableApplicationContext) {
return (ConfigurableApplicationContext) parent;
}
return null;
}
} }
/** /**
* A description of a bean in an application context, primarily intended for * A description of a bean in an application context, primarily intended for
* serialization to JSON. * serialization to JSON.
*/ */
static final class BeanDescriptor { public static final class BeanDescriptor {
private final String[] aliases; private final String[] aliases;
......
...@@ -43,27 +43,22 @@ import static org.assertj.core.api.Assertions.assertThat; ...@@ -43,27 +43,22 @@ import static org.assertj.core.api.Assertions.assertThat;
*/ */
public class BeansEndpointTests { public class BeansEndpointTests {
@SuppressWarnings("unchecked")
@Test @Test
public void beansAreFound() { public void beansAreFound() {
ApplicationContextRunner contextRunner = new ApplicationContextRunner() ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withUserConfiguration(EndpointConfiguration.class); .withUserConfiguration(EndpointConfiguration.class);
contextRunner.run((context) -> { contextRunner.run((context) -> {
Map<String, Object> result = context.getBean(BeansEndpoint.class).beans(); ApplicationContextDescriptor result = context.getBean(BeansEndpoint.class)
List<ApplicationContextDescriptor> contexts = (List<ApplicationContextDescriptor>) result .beans();
.get("contexts"); assertThat(result.getParent()).isNull();
assertThat(contexts).hasSize(1); assertThat(result.getId()).isEqualTo(context.getId());
ApplicationContextDescriptor contextDescriptor = contexts.get(0); Map<String, BeanDescriptor> beans = result.getBeans();
assertThat(contextDescriptor.getParentId()).isNull();
assertThat(contextDescriptor.getId()).isEqualTo(context.getId());
Map<String, BeanDescriptor> beans = contextDescriptor.getBeans();
assertThat(beans.size()) assertThat(beans.size())
.isLessThanOrEqualTo(context.getBeanDefinitionCount()); .isLessThanOrEqualTo(context.getBeanDefinitionCount());
assertThat(contexts.get(0).getBeans()).containsKey("endpoint"); assertThat(beans).containsKey("endpoint");
}); });
} }
@SuppressWarnings("unchecked")
@Test @Test
public void infrastructureBeansAreOmitted() { public void infrastructureBeansAreOmitted() {
ApplicationContextRunner contextRunner = new ApplicationContextRunner() ApplicationContextRunner contextRunner = new ApplicationContextRunner()
...@@ -75,32 +70,28 @@ public class BeansEndpointTests { ...@@ -75,32 +70,28 @@ public class BeansEndpointTests {
.filter((name) -> BeanDefinition.ROLE_INFRASTRUCTURE == factory .filter((name) -> BeanDefinition.ROLE_INFRASTRUCTURE == factory
.getBeanDefinition(name).getRole()) .getBeanDefinition(name).getRole())
.collect(Collectors.toList()); .collect(Collectors.toList());
Map<String, Object> result = context.getBean(BeansEndpoint.class).beans(); ApplicationContextDescriptor result = context.getBean(BeansEndpoint.class)
List<ApplicationContextDescriptor> contexts = (List<ApplicationContextDescriptor>) result .beans();
.get("contexts"); Map<String, BeanDescriptor> beans = result.getBeans();
Map<String, BeanDescriptor> beans = contexts.get(0).getBeans();
for (String infrastructureBean : infrastructureBeans) { for (String infrastructureBean : infrastructureBeans) {
assertThat(beans).doesNotContainKey(infrastructureBean); assertThat(beans).doesNotContainKey(infrastructureBean);
} }
}); });
} }
@SuppressWarnings("unchecked")
@Test @Test
public void lazyBeansAreOmitted() { public void lazyBeansAreOmitted() {
ApplicationContextRunner contextRunner = new ApplicationContextRunner() ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withUserConfiguration(EndpointConfiguration.class, .withUserConfiguration(EndpointConfiguration.class,
LazyBeanConfiguration.class); LazyBeanConfiguration.class);
contextRunner.run((context) -> { contextRunner.run((context) -> {
Map<String, Object> result = context.getBean(BeansEndpoint.class).beans(); ApplicationContextDescriptor result = context.getBean(BeansEndpoint.class)
List<ApplicationContextDescriptor> contexts = (List<ApplicationContextDescriptor>) result .beans();
.get("contexts");
assertThat(context).hasBean("lazyBean"); assertThat(context).hasBean("lazyBean");
assertThat(contexts.get(0).getBeans()).doesNotContainKey("lazyBean"); assertThat(result.getBeans()).doesNotContainKey("lazyBean");
}); });
} }
@SuppressWarnings("unchecked")
@Test @Test
public void beansInParentContextAreFound() { public void beansInParentContextAreFound() {
ApplicationContextRunner parentRunner = new ApplicationContextRunner() ApplicationContextRunner parentRunner = new ApplicationContextRunner()
...@@ -110,31 +101,9 @@ public class BeansEndpointTests { ...@@ -110,31 +101,9 @@ public class BeansEndpointTests {
.withUserConfiguration(EndpointConfiguration.class).withParent(parent) .withUserConfiguration(EndpointConfiguration.class).withParent(parent)
.run(child -> { .run(child -> {
BeansEndpoint endpoint = child.getBean(BeansEndpoint.class); BeansEndpoint endpoint = child.getBean(BeansEndpoint.class);
Map<String, Object> result = endpoint.beans(); ApplicationContextDescriptor result = endpoint.beans();
List<ApplicationContextDescriptor> contexts = (List<ApplicationContextDescriptor>) result assertThat(result.getParent().getBeans()).containsKey("bean");
.get("contexts"); assertThat(result.getBeans()).containsKey("endpoint");
assertThat(contexts).hasSize(2);
assertThat(contexts.get(1).getBeans()).containsKey("bean");
assertThat(contexts.get(0).getBeans()).containsKey("endpoint");
});
});
}
@SuppressWarnings("unchecked")
@Test
public void beansInChildContextAreNotFound() {
ApplicationContextRunner parentRunner = new ApplicationContextRunner()
.withUserConfiguration(EndpointConfiguration.class);
parentRunner.run((parent) -> {
new ApplicationContextRunner().withUserConfiguration(BeanConfiguration.class)
.withParent(parent).run(child -> {
BeansEndpoint endpoint = child.getBean(BeansEndpoint.class);
Map<String, Object> result = endpoint.beans();
List<ApplicationContextDescriptor> contexts = (List<ApplicationContextDescriptor>) result
.get("contexts");
assertThat(contexts).hasSize(1);
assertThat(contexts.get(0).getBeans()).containsKey("endpoint");
assertThat(contexts.get(0).getBeans()).doesNotContainKey("bean");
}); });
}); });
} }
......
...@@ -217,10 +217,8 @@ public class SampleActuatorApplicationTests { ...@@ -217,10 +217,8 @@ public class SampleActuatorApplicationTests {
.withBasicAuth("user", getPassword()) .withBasicAuth("user", getPassword())
.getForEntity("/application/beans", Map.class); .getForEntity("/application/beans", Map.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).hasSize(1); assertThat(entity.getBody()).containsOnlyKeys("beans", "parent", "id");
Map<String, Object> body = (Map<String, Object>) ((List<?>) entity.getBody() assertThat(((String) entity.getBody().get("id"))).startsWith("application");
.get("contexts")).get(0);
assertThat(((String) body.get("id"))).startsWith("application");
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment