or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

configuration.mdinbound-endpoints.mdindex.mdjava-dsl.mdmanagement.mdmessage-conversion.mdmultipart-handling.mdoutbound-handlers.md
tile.json

management.mddocs/

Management and Monitoring

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.

Key Information for Agents

Enabling Management:

  • Management endpoints disabled by default (must be explicitly enabled)
  • Enable via @EnableIntegrationGraphController annotation
  • Enable via @EnableControlBusController annotation
  • Requires Spring Web MVC or WebFlux on classpath

Graph Controller:

  • Default path: /integration (configurable via property or annotation)
  • Property: spring.integration.graph.controller.request.mapping.path
  • Provides GET endpoints for graph visualization
  • Returns JSON representation of integration flow topology

Control Bus Controller:

  • Fixed path: /control-bus
  • Provides GET endpoint to list available commands
  • Provides POST endpoint to invoke commands
  • Commands format: @beanName.methodName

Security:

  • No security by default (must be configured)
  • Should be secured in production environments
  • Consider restricting to admin users or internal networks
  • CORS can be configured for graph controller

Command Execution:

  • Commands executed via SpEL evaluation
  • Parameters converted using FormattingConversionService
  • Returns command execution result as JSON
  • Errors returned as error responses

Graph Data:

  • Graph includes nodes (components) and links (connections)
  • Nodes have component type, pattern type, name, etc.
  • Links show data flow between components
  • Graph can be refreshed to rebuild from current state

Edge Cases:

  • If endpoint not enabled, returns 404
  • If command not found, returns error
  • If command execution fails, returns error with details
  • Graph refresh may take time for large integration flows
  • Control bus requires ControlBusCommandRegistry bean

Performance Considerations:

  • Graph building can be expensive for large flows
  • Consider caching graph data
  • Control bus operations are synchronous
  • Limit access to prevent performance impact

Capabilities

IntegrationGraphController

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 graph

Example 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"
    }
  ]
}

EnableIntegrationGraphController Annotation

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 {
}

ControlBusController

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"}]'

EnableControlBusController Annotation

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
}

Complete Configuration Examples

Full Management Configuration

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
}

Secure Management Endpoints

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();
    }
}

Management with Actuator Integration

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;
        }
    }
}

Custom Graph Controller

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();
    }
}

Monitoring Dashboard Integration

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;
    }
}

Common Management Operations

Start/Stop Endpoints

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"

Query Channel Statistics

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"

Configure Handler Properties

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"}]'

Graph Inspection

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")'

Custom Management Script

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
        ;;
esac

Monitoring Integration

Integrate 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));
    }
}