diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/config/IntegrationGraphControllerRegistrarImportSelector.java b/spring-integration-http/src/main/java/org/springframework/integration/http/config/IntegrationGraphControllerRegistrarImportSelector.java
index 2e087c45d8..5d5cc0ca99 100644
--- a/spring-integration-http/src/main/java/org/springframework/integration/http/config/IntegrationGraphControllerRegistrarImportSelector.java
+++ b/spring-integration-http/src/main/java/org/springframework/integration/http/config/IntegrationGraphControllerRegistrarImportSelector.java
@@ -21,7 +21,6 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
-import org.springframework.integration.http.support.DefaultHttpHeaderMapper;
import org.springframework.integration.http.support.HttpContextUtils;
/**
@@ -30,7 +29,7 @@ import org.springframework.integration.http.support.HttpContextUtils;
*/
class IntegrationGraphControllerRegistrarImportSelector implements ImportSelector {
- private static final Log logger = LogFactory.getLog(DefaultHttpHeaderMapper.class);
+ private static final Log logger = LogFactory.getLog(IntegrationGraphControllerRegistrarImportSelector.class);
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/management/IntegrationGraphControllerParserTests-context.xml b/spring-integration-http/src/test/java/org/springframework/integration/http/management/IntegrationGraphControllerParserTests-context.xml
index 685ee9e609..a969d64fd3 100644
--- a/spring-integration-http/src/test/java/org/springframework/integration/http/management/IntegrationGraphControllerParserTests-context.xml
+++ b/spring-integration-http/src/test/java/org/springframework/integration/http/management/IntegrationGraphControllerParserTests-context.xml
@@ -10,8 +10,14 @@
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
-
+
-
+
+
+
+
+
diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/management/IntegrationGraphControllerTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/management/IntegrationGraphControllerTests.java
index 55ef914316..6f5b366bab 100644
--- a/spring-integration-http/src/test/java/org/springframework/integration/http/management/IntegrationGraphControllerTests.java
+++ b/spring-integration-http/src/test/java/org/springframework/integration/http/management/IntegrationGraphControllerTests.java
@@ -36,10 +36,13 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ClassPathXmlApplicationContext;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.config.EnableIntegration;
+import org.springframework.integration.config.EnableIntegrationManagement;
import org.springframework.integration.http.config.EnableIntegrationGraphController;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
@@ -53,7 +56,9 @@ import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.HandlerMapping;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
@@ -80,6 +85,7 @@ public class IntegrationGraphControllerTests {
@Test
public void testIntegrationGraphGet() throws Exception {
this.mockMvc.perform(get("/testIntegration")
+ .header(HttpHeaders.ORIGIN, "http://foo.bar.com")
.accept(MediaType.parseMediaType("application/json;charset=UTF-8")))
.andExpect(status().isOk())
.andExpect(content().contentType("application/json;charset=UTF-8"))
@@ -99,7 +105,6 @@ public class IntegrationGraphControllerTests {
"IntegrationGraphControllerParserTests-context.xml", getClass());
-
HandlerMapping handlerMapping =
context.getBean(RequestMappingHandlerMapping.class.getName(), HandlerMapping.class);
@@ -108,6 +113,7 @@ public class IntegrationGraphControllerTests {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setMethod("GET");
request.setRequestURI("/foo");
+ request.addHeader(HttpHeaders.ORIGIN, "http://foo.bar.com");
MockHttpServletResponse response = new MockHttpServletResponse();
HandlerExecutionChain executionChain = handlerMapping.getHandler(request);
@@ -142,8 +148,19 @@ public class IntegrationGraphControllerTests {
@Configuration
@EnableWebMvc
@EnableIntegration
+ @EnableIntegrationManagement(statsEnabled = "_org.springframework.integration.errorLogger.handler",
+ countsEnabled = "!*",
+ defaultLoggingEnabled = "false")
@EnableIntegrationGraphController(path = "/testIntegration")
- public static class ContextConfiguration {
+ public static class ContextConfiguration extends WebMvcConfigurerAdapter {
+
+ @Override
+ public void addCorsMappings(CorsRegistry registry) {
+ registry.addMapping("/testIntegration/**")
+ .allowedOrigins("http://foo.bar.com")
+ .allowedMethods(HttpMethod.GET.name());
+
+ }
}
diff --git a/src/reference/asciidoc/graph.adoc b/src/reference/asciidoc/graph.adoc
new file mode 100644
index 0000000000..7d3a11d7d1
--- /dev/null
+++ b/src/reference/asciidoc/graph.adoc
@@ -0,0 +1,191 @@
+[[integration-graph]]
+=== Integration Graph
+
+Starting with _version 4.3_, Spring Integration provides access to an application's runtime object model which can, optionally, include component metrics.
+It is exposed as a graph, which may be used to visualize the current state of the integration application.
+The `o.s.i.support.management.graph` package contains all the required classes to collect, build and render the runtime state of Spring Integration components as a single tree-like `Graph` object.
+The `IntegrationGraphServer` should be declared as a bean to build, retrieve and refresh the `Graph` object.
+The resulting `Graph` object can be serialized to any format, although JSON is flexible and convenient to parse and represent on the client side.
+A simple Spring Integration application with only the default components would expose a graph as follows:
+
+[source,json]
+----
+{
+ "contentDescriptor": {
+ "providerVersion": "4.3.0.RELEASE",
+ "providerFormatVersion": 1.0,
+ "provider": "spring-integration",
+ "name": "myApplication"
+ },
+ "nodes": [
+ {
+ "nodeId": 1,
+ "name": "nullChannel",
+ "stats": null,
+ "componentType": "channel"
+ },
+ {
+ "nodeId": 2,
+ "name": "errorChannel",
+ "stats": null,
+ "componentType": "publish-subscribe-channel"
+ },
+ {
+ "nodeId": 3,
+ "name": "_org.springframework.integration.errorLogger",
+ "stats": {
+ "duration": {
+ "count": 0,
+ "min": 0.0,
+ "max": 0.0,
+ "mean": 0.0,
+ "standardDeviation": 0.0,
+ "countLong": 0
+ },
+ "errorCount": 0,
+ "standardDeviationDuration": 0.0,
+ "countsEnabled": true,
+ "statsEnabled": true,
+ "loggingEnabled": false,
+ "handleCount": 0,
+ "meanDuration": 0.0,
+ "maxDuration": 0.0,
+ "minDuration": 0.0,
+ "activeCount": 0
+ },
+ "componentType": "logging-channel-adapter",
+ "output": null,
+ "input": "errorChannel"
+ }
+ ],
+ "links": [
+ {
+ "from": 2,
+ "to": 3
+ }
+ ]
+}
+----
+
+As you can see, the graph consists of three top-level elements.
+
+The `contentDescriptor` graph element is pretty straightforward and contains general information about the application providing the data.
+The `name` can be customized on the `IntegrationGraphServer` bean or via `spring.application.name` application context environment property.
+Other properties are provided by the framework and allows you to distinguish a similar model from other sources.
+
+The `links` graph element represents connections between nodes from the `nodes` graph element and, therefore, between integration components in the source Spring Integration application.
+For example _from_ a `MessageChannel` _to_ an `EventDrivenConsumer` with some `MessageHandler`;
+or _from_ an `AbstractReplyProducingMessageHandler` _to_ a `MessageChannel`.
+The information from this element can be used by a visualizing tool to render connections between nodes from the `nodes` graph element, where the `from` and `to` numbers represent the value from the `nodeId` property of the linked nodes.
+
+The `nodes` graph element is perhaps the most interesting because its elements contain not only the runtime components with their `componentType` s and `name` s, but can also optionally contain metrics exposed by the component.
+To enable the metrics, add an `@EnableIntegrationManagement` to some `@Configuration` class or add an `` element to your XML configuration.
+You can control exactly which components in the framework collect statistics.
+See <> for complete information.
+See the `stats` attribute from the `_org.springframework.integration.errorLogger` component in the JSON example above.
+The `nullChannel` and `errorChannel` don't provide statistics information in this case, because the configuration for this example was:
+
+[source,java]
+----
+@Configuration
+@EnableIntegration
+@EnableIntegrationManagement(statsEnabled = "_org.springframework.integration.errorLogger.handler",
+ countsEnabled = "!*",
+ defaultLoggingEnabled = "false")
+public class ManagementConfiguration {
+
+ @Bean
+ public IntegrationGraphServer integrationGraphServer() {
+ return new IntegrationGraphServer();
+ }
+
+}
+----
+
+The `nodeId` represents a unique incremental identifier to distinguish one component from another.
+It is also used in the `links` element to represent a relationship (connection) of this component to others, if any.
+The `input` and `output` attributes are for the `inputChannel` and `outputChannel` properties of the `AbstractEndpoint`, `MessageHandler`, `SourcePollingChannelAdapter` or `MessageProducerSupport`.
+See the next paragraph for more information.
+
+==== Graph Runtime Model
+
+Spring Integration components have various levels of complexity.
+For example, any polled `MessageSource` also has a `SourcePollingChannelAdapter` and a `MessageChannel` to which to send messages from the source data periodically.
+Other components might be middleware request-reply components, e.g. `JmsOutboundGateway`, with a consuming `AbstractEndpoint` to subscribe to (or poll) the `requestChannel` (`input`) for messages, and a `replyChannel` (`output`) to produce a reply message to send downstream.
+Meanwhile, any `MessageProducerSupport` implementation (e.g. `ApplicationEventListeningMessageProducer`) simply wraps some source protocol listening logic and sends messages to the `outputChannel`.
+
+Within the graph, Spring Integration components are represented using the `IntegrationNode` class hierarchy, which you can find in the `o.s.i.support.management.graph` package.
+For example the `ErrorCapableDiscardingMessageHandlerNode` could be used for the `AggregatingMessageHandler` (because it has a `discardChannel` option) and can produce errors when consuming from a `PollableChannel` using a `PollingConsumer`.
+Another sample is `CompositeMessageHandlerNode` - for a `MessageHandlerChain` when subscribed to a `SubscribableChannel`, using an `EventDrivenConsumer`.
+
+This `IntegrationNode` hierarchy can be used for parsing the graph model on the client side, as well as for the understanding the general Spring Integration runtime behavior.
+See also <> for more information.
+
+=== Integration Graph Controller
+
+If your application is WEB-based (or built on top of Spring Boot using an embedded web container) and the Spring Integration HTTP module (see <>) is present on the classpath, you can use a `IntegrationGraphController` to expose the `IntegrationGraphServer` functionality as a REST service.
+For this purpose, the `@EnableIntegrationGraphController` `@Configuration` class annotation and the `` XML element, are available in the HTTP module.
+Together with the `@EnableWebMvc` annotation (or `` for xml definitions), this configuration registers an `IntegrationGraphController` `@RestController` where its `@RequestMapping.path` can be configured on the `@EnableIntegrationGraphController` annotation or `` element.
+The default path is `/integration`.
+
+The `IntegrationGraphController` `@RestController` provides these services:
+
+- `@GetMapping(name = "getGraph")` - to retrieve the state of the Spring Integration components since the last `IntegrationGraphServer` refresh.
+The `o.s.i.support.management.graph.Graph` is returned as a `@ResponseBody` of the REST service;
+- `@GetMapping(path = "/refresh", name = "refreshGraph")` - to refresh the current `Graph` for the actual runtime state and return it as a REST response.
+It is not necessaery to refresh the graph for metrics, they are provided in real-time when the graph is retrieved.
+Refresh can be called if the application context has been modified since the graph was last retrieved and the graph is completely rebuilt.
+
+Any Security and Cross Origin restrictions for the `IntegrationGraphController` can be achieved with the standard configuration options and components provided by Spring Security and Spring MVC projects.
+The simple example of that may be:
+
+[source,xml]
+----
+
+
+
+
+
+
+
+
+
+
+
+
+----
+
+and the Java & Annotation Configuration variant is:
+
+[source,java]
+----
+@Configuration
+@EnableWebMvc
+@EnableWebSecurity
+@EnableIntegration
+@EnableIntegrationGraphController(path = "/testIntegration")
+public class IntegrationConfiguration extends WebSecurityConfigurerAdapter
+ implements WebMvcConfigurer {
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ http
+ .authorizeRequests()
+ .antMatchers("/testIntegration/**").hasRole("ADMIN")
+ // ...
+ .formLogin();
+ }
+
+ @Override
+ public void addCorsMappings(CorsRegistry registry) {
+ registry.addMapping("/testIntegration/**")
+ .allowedOrigins("http://localhost:9090")
+ .allowedMethods(HttpMethod.GET.name());
+ }
+
+ //...
+
+}
+----
diff --git a/src/reference/asciidoc/http.adoc b/src/reference/asciidoc/http.adoc
index 74b13fcf13..b08499024d 100644
--- a/src/reference/asciidoc/http.adoc
+++ b/src/reference/asciidoc/http.adoc
@@ -791,6 +791,12 @@ If further customization is required you can also configure a `DefaultHttpHeader
Of course, you can even implement the HeaderMapper strategy interface directly and provide a reference to that if you need to do something other than what the `DefaultHttpHeaderMapper` supports.
+[[int-graph-controller]]
+=== Integration Graph Controller
+
+Starting with _version 4.3_, the HTTP module provides an `@EnableIntegrationGraphController` `@Configuration` class annotation and `` XML element to expose the `IntegrationGraphServer` as a REST service.
+See <> for more information.
+
[[http-samples]]
=== HTTP Samples
diff --git a/src/reference/asciidoc/system-management.adoc b/src/reference/asciidoc/system-management.adoc
index 23b63efb7d..e8a93d03a8 100644
--- a/src/reference/asciidoc/system-management.adoc
+++ b/src/reference/asciidoc/system-management.adoc
@@ -15,4 +15,6 @@ include::./meta-data-store.adoc[]
include::./control-bus.adoc[]
include::./shutdown.adoc[]
+
+include::./graph.adoc[]
// BE SURE TO PRECEDE ALL include:: with a blank line - see https://github.com/asciidoctor/asciidoctor/issues/1297
diff --git a/src/reference/asciidoc/whats-new.adoc b/src/reference/asciidoc/whats-new.adoc
index efd765e11f..0c5346d8aa 100644
--- a/src/reference/asciidoc/whats-new.adoc
+++ b/src/reference/asciidoc/whats-new.adoc
@@ -38,6 +38,11 @@ See <> and <> for more information.
A new `StreamTransformer` is provided to transform an `InputStream` payload to either a `byte[]` or `String`.
See <> for more information.
+==== Integration Graph
+
+A new `IntegrationGraphServer` together with the `IntegrationGraphController` REST service are provided to expose the runtime model of a Spring Integration application as a graph.
+See <> for more information.
+
[[x4.3-general]]
=== General Changes