Add actuator endpoint for exposing the Spring Integration graph

See gh-12331
This commit is contained in:
Tim Ysewyn 2018-02-25 15:52:10 +01:00 committed by Stephane Nicoll
parent 3b80b3dd93
commit 8c67ef1079
14 changed files with 486 additions and 0 deletions

View File

@ -0,0 +1,40 @@
[[integrationgraph]]
= Spring Integration graph (`integrationgraph`)
The `integrationgraph` endpoint exposes a graph containing all Spring Integration components.
[[integrationgraph-retrieving]]
== Retrieving the Spring Integration graph
To retrieve the information about the application, make a `GET` request to
`/actuator/integrationgraph`, as shown in the following curl-based example:
include::{snippets}integrationgraph/graph/curl-request.adoc[]
The resulting response is similar to the following:
include::{snippets}integrationgraph/graph/http-response.adoc[]
[[integrationgraph-retrieving-response-structure]]
=== Response Structure
The response contains all Spring Integration components used within the application, as well as the links between them.
More information about the structure can be found in the https://docs.spring.io/spring-integration/reference/html/system-management-chapter.html#integration-graph[subchapter] for the integration graph in the reference documentation of Spring Integration.
[[integrationgraph-rebuilding]]
== Rebuilding the Spring Integration graph
To rebuild the exposed graph, make a `POST` request to
`/actuator/integrationgraph`, as shown in the following curl-based example:
include::{snippets}integrationgraph/rebuild/curl-request.adoc[]
This will result in a `204 - No Content` response:
include::{snippets}integrationgraph/rebuild/http-response.adoc[]

View File

@ -59,6 +59,7 @@ include::endpoints/health.adoc[leveloffset=+1]
include::endpoints/heapdump.adoc[leveloffset=+1]
include::endpoints/httptrace.adoc[leveloffset=+1]
include::endpoints/info.adoc[leveloffset=+1]
include::endpoints/integrationgraph.adoc[leveloffset=+1]
include::endpoints/liquibase.adoc[leveloffset=+1]
include::endpoints/logfile.adoc[leveloffset=+1]
include::endpoints/loggers.adoc[leveloffset=+1]

View File

@ -0,0 +1,49 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.integration;
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint;
import org.springframework.boot.actuate.integration.IntegrationGraphEndpoint;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.support.management.graph.IntegrationGraphServer;
/**
* {@link EnableAutoConfiguration Auto-configuration} for the {@link IntegrationGraphEndpoint}.
*
* @author Tim Ysewyn
* @since 2.1.0
*/
@Configuration
@ConditionalOnClass(IntegrationGraphServer.class)
public class IntegrationGraphEndpointAutoConfiguration {
@Bean
@ConditionalOnBean(IntegrationGraphServer.class)
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
public IntegrationGraphEndpoint integrationGraphEndpoint(
IntegrationGraphServer integrationGraphServer) {
return new IntegrationGraphEndpoint(integrationGraphServer);
}
}

View File

@ -0,0 +1,20 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Auto-configuration for actuator Spring Integration concerns.
*/
package org.springframework.boot.actuate.autoconfigure.integration;

View File

@ -21,6 +21,7 @@ org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfigu
org.springframework.boot.actuate.autoconfigure.influx.InfluxDbHealthIndicatorAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.info.InfoContributorAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.info.InfoEndpointAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.integration.IntegrationGraphEndpointAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.jdbc.DataSourceHealthIndicatorAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.jms.JmsHealthIndicatorAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.jolokia.JolokiaEndpointAutoConfiguration,\

View File

@ -0,0 +1,69 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.endpoint.web.documentation;
import org.junit.Test;
import org.springframework.boot.actuate.integration.IntegrationGraphEndpoint;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.support.management.graph.IntegrationGraphServer;
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* Tests for generating documentation describing the {@link IntegrationGraphEndpoint}.
*
* @author Tim Ysewyn
*/
public class IntegrationGraphEndpointDocumentationTests extends MockMvcEndpointDocumentationTests {
@Test
public void graph() throws Exception {
this.mockMvc.perform(get("/actuator/integrationgraph")).andExpect(status().isOk())
.andDo(MockMvcRestDocumentation.document("integrationgraph/graph"));
}
@Test
public void rebuild() throws Exception {
this.mockMvc.perform(post("/actuator/integrationgraph")).andExpect(status().isNoContent())
.andDo(MockMvcRestDocumentation.document("integrationgraph/rebuild"));
}
@Configuration
@EnableIntegration
@Import(BaseDocumentationConfiguration.class)
static class TestConfiguration {
@Bean
public IntegrationGraphServer integrationGraphServer() {
return new IntegrationGraphServer();
}
@Bean
public IntegrationGraphEndpoint endpoint() {
return new IntegrationGraphEndpoint(integrationGraphServer());
}
}
}

View File

@ -0,0 +1,68 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.integration;
import org.junit.Test;
import org.springframework.boot.actuate.integration.IntegrationGraphEndpoint;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.support.management.graph.IntegrationGraphServer;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link IntegrationGraphEndpointAutoConfiguration}.
*
* @author Tim Ysewyn
*/
public class IntegrationGraphEndpointAutoConfigurationTests {
private final ApplicationContextRunner contextRunnerWithoutIntegrationGraph = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(IntegrationGraphEndpointAutoConfiguration.class));
private final ApplicationContextRunner contextRunnerWithIntegrationGraph = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(IntegrationGraphEndpointAutoConfiguration.class))
.withUserConfiguration(TestConfiguration.class);
@Test
public void runShouldNotHaveEndpointBean() {
this.contextRunnerWithoutIntegrationGraph.run((context) -> assertThat(context).doesNotHaveBean(IntegrationGraphEndpoint.class));
this.contextRunnerWithoutIntegrationGraph.withPropertyValues("management.endpoint.integrationgraph.enabled:true")
.run((context) -> assertThat(context).doesNotHaveBean(IntegrationGraphEndpoint.class));
this.contextRunnerWithIntegrationGraph.run((context) -> assertThat(context).doesNotHaveBean(IntegrationGraphEndpoint.class));
}
@Test
public void runWhenEnabledPropertyIsTrueShouldHaveEndpointBean() {
this.contextRunnerWithIntegrationGraph.withPropertyValues("management.endpoint.integrationgraph.enabled:true")
.run((context) -> assertThat(context).hasSingleBean(IntegrationGraphEndpoint.class));
}
@Configuration
public static class TestConfiguration {
@Bean
public IntegrationGraphServer integrationGraphServer() {
return new IntegrationGraphServer();
}
}
}

View File

@ -0,0 +1,58 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.integration;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
import org.springframework.integration.support.management.graph.Graph;
import org.springframework.integration.support.management.graph.IntegrationGraphServer;
/**
* {@link Endpoint} to expose the Spring Integration graph.
*
* @author Tim Ysewyn
* @since 2.1.0
*/
@Endpoint(id = "integrationgraph", enableByDefault = false)
public class IntegrationGraphEndpoint {
private final IntegrationGraphServer integrationGraphServer;
/**
* Creates a new {@code IntegrationGraphEndpoint} that exposes a graph containing all the
* Spring Integration components in the given {@code integrationGraphServer}.
*
* @param integrationGraphServer the integration graph server
* @see IntegrationGraphServer
*/
public IntegrationGraphEndpoint(IntegrationGraphServer integrationGraphServer) {
this.integrationGraphServer = integrationGraphServer;
}
@ReadOperation
public Graph graph() {
return this.integrationGraphServer.getGraph();
}
@WriteOperation
public void rebuild() {
this.integrationGraphServer.rebuild();
}
}

View File

@ -0,0 +1,20 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Actuator support relating to Spring Integration.
*/
package org.springframework.boot.actuate.integration;

View File

@ -0,0 +1,70 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.integration;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.integration.support.management.graph.Graph;
import org.springframework.integration.support.management.graph.IntegrationGraphServer;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.mock;
import static org.mockito.BDDMockito.verify;
import static org.mockito.BDDMockito.when;
/**
* Tests for {@link IntegrationGraphEndpoint}.
*
* @author Tim Ysewyn
*/
public class IntegrationGraphEndpointTests {
@Mock
private IntegrationGraphServer integrationGraphServer;
@InjectMocks
private IntegrationGraphEndpoint integrationGraphEndpoint;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
@Test
public void shouldReturnGraph() {
Graph mockedGraph = mock(Graph.class);
when(this.integrationGraphServer.getGraph()).thenReturn(mockedGraph);
Graph graph = this.integrationGraphEndpoint.graph();
verify(this.integrationGraphServer).getGraph();
assertThat(graph).isEqualTo(mockedGraph);
}
@Test
public void shouldRebuildGraph() {
this.integrationGraphEndpoint.rebuild();
verify(this.integrationGraphServer).rebuild();
}
}

View File

@ -0,0 +1,71 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.integration;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.actuate.endpoint.web.test.WebEndpointRunners;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.support.management.graph.IntegrationGraphServer;
import org.springframework.test.web.reactive.server.WebTestClient;
/**
* Integration tests for {@link IntegrationGraphEndpoint} exposed by Jersey, Spring MVC, and WebFlux.
*
* @author Tim Ysewyn
*/
@RunWith(WebEndpointRunners.class)
public class IntegrationGraphEndpointWebIntegrationTests {
private static WebTestClient client;
@Test
public void graph() {
client.get().uri("/actuator/integrationgraph").accept(MediaType.APPLICATION_JSON).exchange()
.expectStatus().isOk().expectBody()
.jsonPath("contentDescriptor.providerVersion").isEqualTo("5.0.3.RELEASE")
.jsonPath("contentDescriptor.providerFormatVersion").isEqualTo(1.0f)
.jsonPath("contentDescriptor.provider").isEqualTo("spring-integration");
}
@Test
public void rebuild() {
client.post().uri("/actuator/integrationgraph").accept(MediaType.APPLICATION_JSON).exchange()
.expectStatus().isNoContent();
}
@Configuration
@EnableIntegration
public static class TestConfiguration {
@Bean
public IntegrationGraphEndpoint endpoint(IntegrationGraphServer integrationGraphServer) {
return new IntegrationGraphEndpoint(integrationGraphServer);
}
@Bean
public IntegrationGraphServer integrationGraphServer() {
return new IntegrationGraphServer();
}
}
}

View File

@ -43,6 +43,7 @@ import org.springframework.integration.jdbc.store.JdbcMessageStore;
import org.springframework.integration.jmx.config.EnableIntegrationMBeanExport;
import org.springframework.integration.monitor.IntegrationMBeanExporter;
import org.springframework.integration.support.management.IntegrationManagementConfigurer;
import org.springframework.integration.support.management.graph.IntegrationGraphServer;
import org.springframework.util.StringUtils;
/**
@ -54,6 +55,7 @@ import org.springframework.util.StringUtils;
* @author Stephane Nicoll
* @author Vedran Pavic
* @author Madhura Bhave
* @author Tim Ysewyn
* @since 1.1.0
*/
@Configuration
@ -69,6 +71,12 @@ public class IntegrationAutoConfiguration {
@EnableIntegration
protected static class IntegrationConfiguration {
@Bean
@ConditionalOnMissingBean
public IntegrationGraphServer integrationGraphServer() {
return new IntegrationGraphServer();
}
}
/**

View File

@ -42,6 +42,7 @@ import org.springframework.integration.gateway.RequestReplyExchanger;
import org.springframework.integration.handler.MessageProcessor;
import org.springframework.integration.support.channel.HeaderChannelRegistry;
import org.springframework.integration.support.management.IntegrationManagementConfigurer;
import org.springframework.integration.support.management.graph.IntegrationGraphServer;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jmx.export.MBeanExporter;
@ -55,6 +56,7 @@ import static org.mockito.Mockito.mock;
* @author Artem Bilan
* @author Stephane Nicoll
* @author Vedran Pavic
* @author Tim Ysewyn
*/
public class IntegrationAutoConfigurationTests {
@ -68,6 +70,7 @@ public class IntegrationAutoConfigurationTests {
@Test
public void integrationIsAvailable() {
this.contextRunner.run((context) -> {
assertThat(context).hasSingleBean(IntegrationGraphServer.class);
assertThat(context).hasSingleBean(TestGateway.class);
assertThat(context)
.hasSingleBean(IntegrationComponentScanAutoConfiguration.class);

View File

@ -105,6 +105,10 @@ exchanges).
|Displays arbitrary application info.
|Yes
|`integrationgraph`
|Exposes the Spring Integration graph.
|No
|`loggers`
|Shows and modifies the configuration of loggers in the application.
|Yes
@ -252,6 +256,10 @@ endpoints:
|Yes
|Yes
|`integrationgraph`
|Yes
|Yes
|`jolokia`
|N/A
|No