Get ahead
VMware offers training and certification to turbo-charge your progress.
Learn moreSpring Integration 6.4 is your one-stop shop for all matters of enterprise application integration. So it supports numerous messaging and integration patterns and even more numerous adapters for all manner of technologies - SFTP, FTP, Redis, Apache Pulsar, Apache Kafka, JDBC, TCP/IP, etc. So, as you might have surmised, there’s just no way to keep up with them. The release notes do a pretty good job, so I’ll list some of my favorites.
clearFetchedCache()
method to remove references from the cache for unprocessed remote files.LockRepository#delete
- that now returns the result of removing ownership of a distributed lock.RedisLockRegistry
- throws a ConcurrentModificationException
if the ownership of the lock is expired.Consumer<SshClient>
to allow for further customization of the internal SshClient
MqttPahoMessageDrivenChannelAdapter
and Mqttv5PahoMessageDrivenChannelAdapter
can now be added at runtime using corresponding ClientManager
through IntegrationFlowContext
.AbstractMailReceiver
exposes an option to disable setting Flags.Flag.FLAGGED
.BaseMessageBuilder
has been extracted from the MessageBuilder
to simplify building your custom builder implementation, where most of the logic should be identical to that of `MessageBuilder.ControlBusFactoryBean
that simplifies standing up a new ControlBus.
In addition, there’s a new Control Bus HTTP controller.The improved control bus support is, I think, well worth looking at! The idea behind the control bus pattern is that it should be possible to control the system with the same in-band messaging infrastructure you use to send messages to the system. Control Spring Integration (and your Spring applications) with Spring Integration. Spring has a mechanism that will automatically detect and expose Spring beans annotated with @ManagedResource
as JMX beans for operations. The ControlBus mechanism in Spring Integration takes those same beans and exports them for manipulation with messages coming to and from Spring Integration.
package com.example.bootiful_34.integration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.dsl.DirectChannelSpec;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.MessageChannels;
import org.springframework.integration.http.config.EnableControlBusController;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.messaging.MessageChannel;
import java.util.concurrent.atomic.AtomicInteger;
@Configuration
@EnableControlBusController
class ControlBusConfiguration {
@Bean
IntegrationFlow controlBusFlow(MessageChannel controlBusFlowMessageChannel) {
return IntegrationFlow.from(controlBusFlowMessageChannel).controlBusOnRegistry().get();
}
@Bean
DirectChannelSpec controlBusFlowMessageChannel() {
return MessageChannels.direct();
}
@Bean
MyOperationsManagedResource myOperationsManagedResource() {
return new MyOperationsManagedResource();
}
@ManagedResource
public static class MyOperationsManagedResource {
static final AtomicInteger COUNTER = new AtomicInteger(0);
@ManagedOperation(description = "Update the magic number")
public void updateMagicNumber(int magicNumber) {
System.out.println("doSomething with magic number " + magicNumber);
COUNTER.incrementAndGet();
}
}
}
In this example, a bean manipulates some important state—configuring some magic number. OK, OK, I can see why this isn’t the most tantalizing sample. But bear with me. Anyway, it provides a method—updateMagicNumber(int)
—that we expect to be accessible via JMX and Spring Integration’s ControlBus
. I register the bean normally, making sure to also annotate the class with @ManagedResource
.
I define a Spring MessageChannel
of type DirectChannel
and then a Spring Integration IntegrationFlow
, which routes all messages via the DirectChannel
to the control bus in the Spring application. The control bus will then adapt and route the message to the appropriate managed resource. Let’s look at what the message needs to look like to invoke that managed resource and its method expecting a parameter.
package com.example.bootiful_34.integration;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.integration.IntegrationMessageHeaderAccessor;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.support.MessageBuilder;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
@SpringBootTest
class ControlBusConfigurationTest {
@Test
void controlBusConfigurationRunner(@Autowired MessageChannel controlBusFlowMessageChannel) {
var msg = MessageBuilder.withPayload("myOperationsManagedResource.updateMagicNumber")
.setHeaderIfAbsent(IntegrationMessageHeaderAccessor.CONTROL_BUS_ARGUMENTS, List.of(42))
.build();
assertEquals(ControlBusConfiguration.MyOperationsManagedResource.COUNTER.get(), 0,
"there should be zero invocations of the control bus thus far");
controlBusFlowMessageChannel.send(msg);
assertEquals(ControlBusConfiguration.MyOperationsManagedResource.COUNTER.get(), 1,
"there should be one invocation of the control bus thus far");
}
}
Straightforward enough, indeed? In this test, we inject the DirectChannel
and craft a new Message
whose payload is the name of the managed resource bean and the method on that managed resource, along with some data to pass into the parameters of that managed resource bean’s method. The rest of the test is just about ensuring the message was successfully delivered, and the counter has incremented to reflect the invocation.
The ControlBus
pattern is super powerful! Remember: Spring MessageChannel
instances are your gateway to any other system, be it via Spring Cloud Stream, Spring Framework 4’s WebSocket support, or Spring Integration’s inbound adapters. This method invocation could’ve just as quickly resulted from a file being deposited on an FTP server, a new message arriving on an Apache Kafka queue, or a TCP payload arriving on a particular part. We could’ve used Spring Security to secure the inbound message channel, rejecting messages that can’t be authenticated. This is a lot easier to do than, say, JMX directly.
And if that weren’t enough, the new @EnableControlBusController
annotation publicly exports the ControlBus
functionality!