GH-3192: pub-sub DSL for broker-backed channels (#3193)
* GH-3192: pub-sub DSL for broker-backed channels Fixes https://github.com/spring-projects/spring-integration/issues/3192 * Introduce a `BroadcastCapableChannel` abstract to indicate those `SubscribableChannel` implementations which can provide a pub-sub functionality * Implement a `BroadcastCapableChannel` in broker-baked channels with pub-sub option * Introduce a `BaseIntegrationFlowDefinition.publishSubscribeChannel()` based on the `BroadcastCapableChannel` and `BroadcastPublishSubscribeSpec` to let to configure sub-flow subscribers in fluent manner * * Add some JavaDocs and document new feature * * Show the channel bean definition in the doc * Fix typo
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -23,6 +23,7 @@ import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.integration.MessageDispatchingException;
|
||||
import org.springframework.integration.channel.BroadcastCapableChannel;
|
||||
import org.springframework.integration.context.IntegrationProperties;
|
||||
import org.springframework.integration.dispatcher.AbstractDispatcher;
|
||||
import org.springframework.integration.dispatcher.BroadcastingDispatcher;
|
||||
@@ -37,10 +38,13 @@ import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageDeliveryException;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.messaging.SubscribableChannel;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An {@link AbstractJmsChannel} implementation for message-driven subscriptions.
|
||||
* Also implements a {@link BroadcastCapableChannel} to represent possible pub-sub semantics
|
||||
* when configured against JMS topic.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
@@ -48,7 +52,7 @@ import org.springframework.util.Assert;
|
||||
* @since 2.0
|
||||
*/
|
||||
public class SubscribableJmsChannel extends AbstractJmsChannel
|
||||
implements SubscribableChannel, SmartLifecycle {
|
||||
implements BroadcastCapableChannel, SmartLifecycle {
|
||||
|
||||
private final AbstractMessageListenerContainer container;
|
||||
|
||||
@@ -87,17 +91,22 @@ public class SubscribableJmsChannel extends AbstractJmsChannel
|
||||
return this.dispatcher.removeHandler(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBroadcast() {
|
||||
return this.container.isPubSubDomain();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInit() {
|
||||
if (this.initialized) {
|
||||
return;
|
||||
}
|
||||
super.onInit();
|
||||
boolean isPubSub = this.container.isPubSubDomain();
|
||||
this.configureDispatcher(isPubSub);
|
||||
MessageListener listener = new DispatchingMessageListener(
|
||||
this.getJmsTemplate(), this.dispatcher,
|
||||
this, isPubSub, this.getMessageBuilderFactory());
|
||||
boolean isPubSub = isBroadcast();
|
||||
configureDispatcher(isPubSub);
|
||||
MessageListener listener =
|
||||
new DispatchingMessageListener(getJmsTemplate(), this.dispatcher, this, isPubSub,
|
||||
getMessageBuilderFactory());
|
||||
this.container.setMessageListener(listener);
|
||||
if (!this.container.isActive()) {
|
||||
this.container.afterPropertiesSet();
|
||||
@@ -108,7 +117,7 @@ public class SubscribableJmsChannel extends AbstractJmsChannel
|
||||
private void configureDispatcher(boolean isPubSub) {
|
||||
if (isPubSub) {
|
||||
BroadcastingDispatcher broadcastingDispatcher = new BroadcastingDispatcher(true);
|
||||
broadcastingDispatcher.setBeanFactory(this.getBeanFactory());
|
||||
broadcastingDispatcher.setBeanFactory(getBeanFactory());
|
||||
this.dispatcher = broadcastingDispatcher;
|
||||
}
|
||||
else {
|
||||
@@ -118,8 +127,8 @@ public class SubscribableJmsChannel extends AbstractJmsChannel
|
||||
}
|
||||
if (this.maxSubscribers == null) {
|
||||
this.maxSubscribers = this.getIntegrationProperty(isPubSub ?
|
||||
IntegrationProperties.CHANNELS_MAX_BROADCAST_SUBSCRIBERS :
|
||||
IntegrationProperties.CHANNELS_MAX_UNICAST_SUBSCRIBERS,
|
||||
IntegrationProperties.CHANNELS_MAX_BROADCAST_SUBSCRIBERS :
|
||||
IntegrationProperties.CHANNELS_MAX_UNICAST_SUBSCRIBERS,
|
||||
Integer.class);
|
||||
}
|
||||
this.dispatcher.setMaxSubscribers(this.maxSubscribers);
|
||||
@@ -194,6 +203,7 @@ public class SubscribableJmsChannel extends AbstractJmsChannel
|
||||
DispatchingMessageListener(JmsTemplate jmsTemplate,
|
||||
MessageDispatcher dispatcher, SubscribableJmsChannel channel, boolean isPubSub,
|
||||
MessageBuilderFactory messageBuilderFactory) {
|
||||
|
||||
this.jmsTemplate = jmsTemplate;
|
||||
this.dispatcher = dispatcher;
|
||||
this.channel = channel;
|
||||
@@ -212,8 +222,10 @@ public class SubscribableJmsChannel extends AbstractJmsChannel
|
||||
converted = converter.fromMessage(message);
|
||||
}
|
||||
if (converted != null) {
|
||||
messageToSend = (converted instanceof Message<?>) ? (Message<?>) converted
|
||||
: this.messageBuilderFactory.withPayload(converted).build();
|
||||
messageToSend =
|
||||
converted instanceof Message<?>
|
||||
? (Message<?>) converted
|
||||
: this.messageBuilderFactory.withPayload(converted).build();
|
||||
this.dispatcher.dispatch(messageToSend);
|
||||
}
|
||||
else if (this.logger.isWarnEnabled()) {
|
||||
@@ -231,8 +243,7 @@ public class SubscribableJmsChannel extends AbstractJmsChannel
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new MessageDeliveryException(
|
||||
messageToSend, exceptionMessage, e);
|
||||
throw new MessageDeliveryException(messageToSend, exceptionMessage, e);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016-2019 the original author or authors.
|
||||
* Copyright 2016-2020 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.
|
||||
@@ -41,6 +41,7 @@ import org.springframework.integration.annotation.InboundChannelAdapter;
|
||||
import org.springframework.integration.annotation.IntegrationComponentScan;
|
||||
import org.springframework.integration.annotation.MessagingGateway;
|
||||
import org.springframework.integration.annotation.Poller;
|
||||
import org.springframework.integration.channel.BroadcastCapableChannel;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.channel.FixedSubscriberChannel;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
@@ -125,6 +126,9 @@ public class JmsTests extends ActiveMQMultiContextTests {
|
||||
@Autowired
|
||||
private PollableChannel jmsPubSubBridgeChannel;
|
||||
|
||||
@Autowired
|
||||
private PollableChannel jmsPubSubBridgeChannel2;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("jmsOutboundGateway.handler")
|
||||
private MessageHandler jmsOutboundGatewayHandler;
|
||||
@@ -254,6 +258,11 @@ public class JmsTests extends ActiveMQMultiContextTests {
|
||||
.isNotNull()
|
||||
.extracting(Message::getPayload)
|
||||
.isEqualTo("foo");
|
||||
received = this.jmsPubSubBridgeChannel2.receive(5000);
|
||||
assertThat(received)
|
||||
.isNotNull()
|
||||
.extracting(Message::getPayload)
|
||||
.isEqualTo("foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -351,10 +360,20 @@ public class JmsTests extends ActiveMQMultiContextTests {
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow pubSubFlow() {
|
||||
return IntegrationFlows
|
||||
.from(Jms.publishSubscribeChannel(jmsConnectionFactory())
|
||||
.destination("pubsub"))
|
||||
.channel(c -> c.queue("jmsPubSubBridgeChannel"))
|
||||
return f -> f
|
||||
.publishSubscribeChannel(jmsPublishSubscribeChannel(),
|
||||
pubsub -> pubsub
|
||||
.subscribe(subFlow -> subFlow
|
||||
.channel(c -> c.queue("jmsPubSubBridgeChannel")))
|
||||
.subscribe(subFlow -> subFlow
|
||||
.channel(c -> c.queue("jmsPubSubBridgeChannel2"))));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BroadcastCapableChannel jmsPublishSubscribeChannel() {
|
||||
// TODO reconsider target generic type for channel implementation to return from this kind of specs
|
||||
return (BroadcastCapableChannel) Jms.publishSubscribeChannel(jmsConnectionFactory())
|
||||
.destination("pubsub")
|
||||
.get();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user