REST controllers for integration graph visualization and control bus management operations, providing HTTP endpoints for runtime monitoring and control of Spring Integration flows. These management features enable operational visibility and dynamic control of integration components.
Enabling Management:
@EnableIntegrationGraphController annotation@EnableControlBusController annotationGraph Controller:
/integration (configurable via property or annotation)spring.integration.graph.controller.request.mapping.pathControl Bus Controller:
/control-bus@beanName.methodNameSecurity:
Command Execution:
FormattingConversionServiceGraph Data:
Edge Cases:
ControlBusCommandRegistry beanPerformance Considerations:
REST Controller providing management API over IntegrationGraphServer to expose integration flow graph for monitoring and visualization. Enables inspection of integration topology and component relationships at runtime.
@RestController
@RequestMapping("${spring.integration.graph.controller.request.mapping.path:/integration}")
public class IntegrationGraphController {
/**
* Creates controller with IntegrationGraphServer.
* IntegrationGraphServer provides graph data from application context.
*
* @param integrationGraphServer the integration graph server
*/
public IntegrationGraphController(
IntegrationGraphServer integrationGraphServer);
/**
* GET endpoint returning integration Graph.
* Returns current state of integration flow graph.
*
* @return the integration graph
*/
@GetMapping
public Graph getGraph();
/**
* GET endpoint at "/refresh" to rebuild and return integration Graph.
* Forces graph rebuild from current application state.
*
* @return the refreshed integration graph
*/
@GetMapping("/refresh")
public Graph refreshGraph();
}Default Request Mapping: ${spring.integration.graph.controller.request.mapping.path:/integration}
Default Path: /integration
Usage Example - Enable via Annotation:
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.http.config.EnableIntegrationGraphController;
@Configuration
@EnableIntegrationGraphController
public class ManagementConfig {
// IntegrationGraphController is automatically registered
}Usage Example - Custom Path:
@Configuration
@EnableIntegrationGraphController(path = "/api/integration-graph")
public class CustomPathConfig {
// Controller available at /api/integration-graph
}Usage Example - With CORS:
@Configuration
@EnableIntegrationGraphController(
path = "/integration",
allowedOrigins = {"https://admin.example.com", "https://dashboard.example.com"}
)
public class CorsEnabledConfig {
// Controller with CORS enabled for specified origins
}API Endpoints:
# Get current integration graph
GET /integration
Response: JSON representation of integration flow graph
# Refresh and get integration graph
GET /integration/refresh
Response: Rebuilt JSON representation of integration flow graphExample Response:
{
"contentDescriptor": {
"providerVersion": "7.0.0",
"providerFormatVersion": 1.2
},
"nodes": [
{
"nodeId": 1,
"componentType": "gateway",
"integrationPatternType": "inbound_gateway",
"integrationPatternCategory": "messaging_endpoint",
"name": "httpInboundGateway",
"input": "httpRequestChannel",
"output": "httpReplyChannel"
},
{
"nodeId": 2,
"componentType": "channel",
"name": "httpRequestChannel"
}
],
"links": [
{
"from": 1,
"to": 2,
"type": "input"
}
]
}Annotation to enable IntegrationGraphController if Spring Web MVC or WebFlux is present. Simplifies management endpoint configuration with declarative approach.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(IntegrationGraphControllerConfiguration.class)
public @interface EnableIntegrationGraphController {
/**
* Alias for path attribute.
*
* @return request mapping path
*/
String[] value() default {};
/**
* Request mapping path for the controller.
* Default: "/integration"
*
* @return the path array
*/
String[] path() default {};
/**
* Allowed origin URLs for CORS.
* Default: empty (no CORS).
*
* @return array of allowed origins
*/
String[] allowedOrigins() default {};
}Usage Example - Simple:
@Configuration
@EnableIntegrationGraphController
public class GraphConfig {
// Uses default path: /integration
}Usage Example - Custom Path and CORS:
@Configuration
@EnableIntegrationGraphController(
path = "/management/flows",
allowedOrigins = {"*"} // Allow all origins (use cautiously)
)
public class PublicGraphConfig {
}REST Controller providing management API for Control Bus pattern to invoke management operations on integration components. Enables runtime control and inspection of Spring Integration beans.
@RestController
@RequestMapping("/control-bus")
public class ControlBusController
implements BeanFactoryAware, InitializingBean {
/**
* Creates controller with command registry and conversion service.
*
* @param controlBusCommandRegistry the command registry
* @param conversionService the conversion service for parameter conversion
*/
public ControlBusController(
ControlBusCommandRegistry controlBusCommandRegistry,
FormattingConversionService conversionService);
/**
* Sets BeanFactory for bean resolution.
*
* @param beanFactory the bean factory
* @throws BeansException if error occurs
*/
public void setBeanFactory(BeanFactory beanFactory) throws BeansException;
/**
* Initializes evaluation context after properties set.
*
* @throws Exception if initialization fails
*/
public void afterPropertiesSet() throws Exception;
/**
* GET endpoint returning list of available commands.
* Lists all registered components and their available operations.
*
* @return list of ControlBusBean records with commands
*/
@GetMapping
public List<ControlBusBean> getCommands();
/**
* POST endpoint to invoke command with arguments.
* Executes management operation on specified component.
*
* @param command the command to execute (e.g., "beanName.method") as path variable
* @param arguments optional list of command arguments
* @return command execution result
*/
@PostMapping(path = "/{command}")
public Object invokeCommand(
@PathVariable String command,
@RequestBody(required = false) List<CommandArgument> arguments);
}Request Mapping: /control-bus
Inner Records:
/**
* Represents a bean with its available commands.
*
* @param beanName the bean name
* @param commands list of available commands
*/
public record ControlBusBean(
String beanName,
List<ControlBusCommand> commands) {
}
/**
* Represents a single command with metadata.
*
* @param command the command signature
* @param description the command description
* @param parameterTypes list of parameter types
*/
public record ControlBusCommand(
String command,
String description,
List<Class<?>> parameterTypes) {
}
/**
* Represents a command argument.
*
* @param value the argument value
* @param parameterType the parameter type
*/
public record CommandArgument(
String value,
Class<?> parameterType) {
}Usage Example - Enable via Annotation:
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.http.config.EnableControlBusController;
@Configuration
@EnableControlBusController
public class ControlBusConfig {
// ControlBusController is automatically registered
}API Endpoints:
# Get available commands
GET /control-bus
Response: List of beans and their available commands
# Invoke command without arguments
POST /control-bus/@myChannel.start
# Invoke command with arguments
POST /control-bus/@myHandler.setLoggingEnabled
Body: [{"value": "true", "parameterType": "boolean"}]Example GET Response:
[
{
"beanName": "httpInboundGateway",
"commands": [
{
"command": "start",
"description": "Starts the endpoint",
"parameterTypes": []
},
{
"command": "stop",
"description": "Stops the endpoint",
"parameterTypes": []
},
{
"command": "isRunning",
"description": "Returns running state",
"parameterTypes": []
}
]
},
{
"beanName": "httpRequestChannel",
"commands": [
{
"command": "getQueueSize",
"description": "Returns queue size",
"parameterTypes": []
}
]
}
]Example POST Request - Start Endpoint:
curl -X POST "http://localhost:8080/control-bus/@httpInboundGateway.start"Example POST Request - With Arguments:
curl -X POST "http://localhost:8080/control-bus/@myHandler.setProperty" \
-H "Content-Type: application/json" \
-d '[{"value": "newValue", "parameterType": "java.lang.String"}]'Annotation to enable ControlBusController if Spring Web MVC or WebFlux is present. Provides declarative configuration for control bus management endpoints.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(ControlBusControllerConfiguration.class)
public @interface EnableControlBusController {
// No attributes - uses fixed path /control-bus
}Usage Example:
@Configuration
@EnableControlBusController
public class ControlBusManagementConfig {
// Controller registered at /control-bus
}Enable both graph and control bus controllers:
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.http.config.EnableIntegrationGraphController;
import org.springframework.integration.http.config.EnableControlBusController;
@Configuration
@EnableIntegrationGraphController(
path = "/management/integration",
allowedOrigins = {"https://admin.example.com"}
)
@EnableControlBusController
public class CompleteManagementConfig {
// Both management endpoints enabled:
// - Integration graph at /management/integration
// - Control bus at /control-bus
}Configure security for management endpoints:
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
@EnableWebSecurity
@EnableIntegrationGraphController
@EnableControlBusController
public class SecureManagementConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
// Require ADMIN role for management endpoints
.antMatchers("/integration/**").hasRole("ADMIN")
.antMatchers("/control-bus/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.csrf().disable();
}
}Integrate with Spring Boot Actuator:
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.integration.graph.IntegrationGraphServer;
import org.springframework.stereotype.Component;
@Configuration
@EnableIntegrationGraphController
@EnableControlBusController
public class ActuatorIntegrationConfig {
@Component
@Endpoint(id = "integrationFlow")
public static class IntegrationFlowEndpoint {
private final IntegrationGraphServer graphServer;
public IntegrationFlowEndpoint(IntegrationGraphServer graphServer) {
this.graphServer = graphServer;
}
@ReadOperation
public Map<String, Object> integrationFlows() {
Graph graph = graphServer.getGraph();
Map<String, Object> info = new HashMap<>();
info.put("nodeCount", graph.getNodes().size());
info.put("linkCount", graph.getLinks().size());
info.put("graph", graph);
return info;
}
}
}Extend graph controller with custom functionality:
import org.springframework.integration.graph.Graph;
import org.springframework.integration.graph.IntegrationGraphServer;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/integration")
public class CustomGraphController {
private final IntegrationGraphServer graphServer;
public CustomGraphController(IntegrationGraphServer graphServer) {
this.graphServer = graphServer;
}
@GetMapping("/graph")
public Graph getGraph() {
return graphServer.getGraph();
}
@GetMapping("/graph/summary")
public Map<String, Object> getGraphSummary() {
Graph graph = graphServer.getGraph();
Map<String, Object> summary = new HashMap<>();
summary.put("totalComponents", graph.getNodes().size());
summary.put("totalConnections", graph.getLinks().size());
// Count by component type
Map<String, Long> componentCounts = graph.getNodes().stream()
.collect(Collectors.groupingBy(
node -> node.getComponentType(),
Collectors.counting()));
summary.put("componentTypes", componentCounts);
return summary;
}
@GetMapping("/graph/nodes/{nodeId}")
public Object getNode(@PathVariable int nodeId) {
return graphServer.getGraph().getNodes().stream()
.filter(node -> node.getNodeId() == nodeId)
.findFirst()
.orElse(null);
}
@PostMapping("/graph/rebuild")
public Graph rebuildGraph() {
graphServer.rebuild();
return graphServer.getGraph();
}
}Integrate with monitoring dashboard:
@RestController
@RequestMapping("/api/monitoring")
public class MonitoringController {
private final IntegrationGraphServer graphServer;
private final ControlBusCommandRegistry commandRegistry;
public MonitoringController(
IntegrationGraphServer graphServer,
ControlBusCommandRegistry commandRegistry) {
this.graphServer = graphServer;
this.commandRegistry = commandRegistry;
}
@GetMapping("/health")
public Map<String, Object> getHealthStatus() {
Map<String, Object> health = new HashMap<>();
Graph graph = graphServer.getGraph();
health.put("componentCount", graph.getNodes().size());
health.put("status", "UP");
// Check endpoint status
List<Map<String, Object>> endpoints = new ArrayList<>();
commandRegistry.findAll().forEach(bean -> {
Map<String, Object> endpoint = new HashMap<>();
endpoint.put("name", bean.beanName());
endpoint.put("commandCount", bean.commands().size());
endpoints.add(endpoint);
});
health.put("managedEndpoints", endpoints);
return health;
}
@GetMapping("/metrics")
public Map<String, Object> getMetrics() {
Graph graph = graphServer.getGraph();
Map<String, Object> metrics = new HashMap<>();
// Count components by type
Map<String, Long> typeCounts = graph.getNodes().stream()
.collect(Collectors.groupingBy(
node -> node.getComponentType(),
Collectors.counting()));
metrics.put("componentsByType", typeCounts);
// Count by pattern type
Map<String, Long> patternCounts = graph.getNodes().stream()
.filter(node -> node.getIntegrationPatternType() != null)
.collect(Collectors.groupingBy(
node -> node.getIntegrationPatternType(),
Collectors.counting()));
metrics.put("componentsByPattern", patternCounts);
return metrics;
}
}Control endpoint lifecycle:
# Stop an endpoint
curl -X POST "http://localhost:8080/control-bus/@httpInboundGateway.stop"
# Start an endpoint
curl -X POST "http://localhost:8080/control-bus/@httpInboundGateway.start"
# Check if running
curl -X POST "http://localhost:8080/control-bus/@httpInboundGateway.isRunning"Get channel information:
# Get queue size
curl -X POST "http://localhost:8080/control-bus/@orderChannel.getQueueSize"
# Get remaining capacity
curl -X POST "http://localhost:8080/control-bus/@orderChannel.getRemainingCapacity"Modify handler configuration at runtime:
# Enable logging
curl -X POST "http://localhost:8080/control-bus/@loggingHandler.setLogExpressionString" \
-H "Content-Type: application/json" \
-d '[{"value": "payload", "parameterType": "java.lang.String"}]'
# Change log level
curl -X POST "http://localhost:8080/control-bus/@loggingHandler.setLevel" \
-H "Content-Type: application/json" \
-d '[{"value": "DEBUG", "parameterType": "java.lang.String"}]'Inspect integration topology:
# Get full graph
curl http://localhost:8080/integration
# Refresh graph
curl http://localhost:8080/integration/refresh
# Process graph data
curl http://localhost:8080/integration | jq '.nodes[] | select(.componentType == "gateway")'Create management script using the APIs:
#!/bin/bash
BASE_URL="http://localhost:8080"
# Function to stop all gateways
stop_all_gateways() {
echo "Stopping all gateways..."
# Get graph and extract gateway bean names
gateways=$(curl -s "$BASE_URL/integration" | \
jq -r '.nodes[] | select(.componentType == "gateway") | .name')
for gateway in $gateways; do
echo "Stopping $gateway..."
curl -X POST "$BASE_URL/control-bus/@${gateway}.stop"
done
}
# Function to start all gateways
start_all_gateways() {
echo "Starting all gateways..."
gateways=$(curl -s "$BASE_URL/integration" | \
jq -r '.nodes[] | select(.componentType == "gateway") | .name')
for gateway in $gateways; do
echo "Starting $gateway..."
curl -X POST "$BASE_URL/control-bus/@${gateway}.start"
done
}
# Function to check system health
check_health() {
echo "Checking system health..."
# Get graph statistics
node_count=$(curl -s "$BASE_URL/integration" | jq '.nodes | length')
link_count=$(curl -s "$BASE_URL/integration" | jq '.links | length')
echo "Total components: $node_count"
echo "Total connections: $link_count"
# Check gateway status
gateways=$(curl -s "$BASE_URL/integration" | \
jq -r '.nodes[] | select(.componentType == "gateway") | .name')
for gateway in $gateways; do
running=$(curl -s -X POST "$BASE_URL/control-bus/@${gateway}.isRunning")
echo "$gateway: $running"
done
}
# Main script
case "$1" in
stop)
stop_all_gateways
;;
start)
start_all_gateways
;;
status)
check_health
;;
*)
echo "Usage: $0 {start|stop|status}"
exit 1
;;
esacIntegrate with monitoring tools:
@Component
public class IntegrationMonitor {
private final IntegrationGraphServer graphServer;
private final MeterRegistry meterRegistry;
public IntegrationMonitor(
IntegrationGraphServer graphServer,
MeterRegistry meterRegistry) {
this.graphServer = graphServer;
this.meterRegistry = meterRegistry;
}
@Scheduled(fixedRate = 60000) // Every minute
public void updateMetrics() {
Graph graph = graphServer.getGraph();
// Register component count metrics
meterRegistry.gauge("integration.components.total",
graph.getNodes().size());
// Register metrics by type
Map<String, Long> typeCounts = graph.getNodes().stream()
.collect(Collectors.groupingBy(
node -> node.getComponentType(),
Collectors.counting()));
typeCounts.forEach((type, count) ->
meterRegistry.gauge("integration.components.by_type",
Tags.of("type", type), count));
}
}