com.solace.spring.cloud.stream.binder.inbound.topic.JCSMPInboundTopicMessageMultiplexer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of spring-cloud-stream-binder-solace Show documentation
Show all versions of spring-cloud-stream-binder-solace Show documentation
A Spring Cloud Stream Binder implementation using the Solace Java API (JCSMP)
The newest version!
package com.solace.spring.cloud.stream.binder.inbound.topic;
import com.solace.spring.cloud.stream.binder.meter.SolaceMeterAccessor;
import com.solace.spring.cloud.stream.binder.properties.SolaceConsumerProperties;
import com.solace.spring.cloud.stream.binder.provisioning.SolaceConsumerDestination;
import com.solace.spring.cloud.stream.binder.tracing.TracingProxy;
import com.solace.spring.cloud.stream.binder.util.LargeMessageSupport;
import com.solacesystems.jcsmp.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.stream.binder.ExtendedConsumerProperties;
import org.springframework.cloud.stream.provisioning.ConsumerDestination;
import org.springframework.messaging.MessagingException;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
@Slf4j
@RequiredArgsConstructor
public class JCSMPInboundTopicMessageMultiplexer {
private final JCSMPSession jcsmpSession;
private final Optional solaceMeterAccessorSupplier;
private final Optional tracingProxy;
private final List jcsmpInboundTopicMessageProducers = new ArrayList<>();
private final AtomicReference msgConsumer = new AtomicReference<>(null);
private final LargeMessageSupport largeMessageSupport = new LargeMessageSupport();
private final LivecycleHooks livecycleHooks = new LivecycleHooks() {
@Override
public void start(JCSMPInboundTopicMessageProducer producer) {
log.info("started producer " + producer);
synchronized (jcsmpInboundTopicMessageProducers) {
jcsmpInboundTopicMessageProducers.add(producer);
}
updateTopics();
largeMessageSupport.startHousekeeping();
}
@Override
public void stop(JCSMPInboundTopicMessageProducer producer) {
log.info("stopped producer " + producer);
synchronized (jcsmpInboundTopicMessageProducers) {
jcsmpInboundTopicMessageProducers.remove(producer);
}
updateTopics();
}
};
private final TopicFilterTree topicFilterTree = new TopicFilterTree<>();
private final Set appliedSubscriptions = new HashSet<>();
private void ensureXMLMessageConsumer() {
if (msgConsumer.get() != null) {
return;
}
synchronized (msgConsumer) {
if (msgConsumer.get() != null) {
return;
}
try {
this.msgConsumer.set(jcsmpSession.getMessageConsumer(new XMLMessageListener() {
@Override
public void onReceive(final BytesXMLMessage msg) {
LargeMessageSupport.MessageContext messageContext = largeMessageSupport.assemble(msg, null);
if (messageContext == null) {
return;
}
JCSMPInboundTopicMessageMultiplexer.this.onReceive(messageContext.bytesMessage());
}
@Override
public void onException(final JCSMPException e) {
JCSMPInboundTopicMessageMultiplexer.this.onException(e);
}
}));
this.msgConsumer.get().start();
} catch (JCSMPException e) {
String msg = "Failed to get message consumer for topics";
log.warn(msg, e);
throw new MessagingException(msg, e);
}
}
}
private void onReceive(final BytesXMLMessage msg) {
String topic = msg.getDestination().getName();
for (JCSMPInboundTopicMessageProducer messageProducer : topicFilterTree.getMatching(topic)) {
messageProducer.onReceive(msg);
}
}
private void onException(final JCSMPException e) {
String msg = "Received error while trying to read message from topic";
if ((e instanceof JCSMPTransportException || e instanceof ClosedFacilityException)) {
log.debug(msg, e);
} else {
log.warn(msg, e);
}
}
private void updateTopics() {
Set allTopics = new HashSet<>();
synchronized (jcsmpInboundTopicMessageProducers) {
topicFilterTree.clear();
for (var producer : jcsmpInboundTopicMessageProducers) {
Set producerTopics = producer.getAllTopics();
allTopics.addAll(producerTopics);
for (String topic : producerTopics) {
topicFilterTree.addTopic(topic, producer);
}
}
}
Set toRemove = new HashSet<>();
Set toAdd = new HashSet<>(allTopics);
appliedSubscriptions.forEach(topicName -> {
if (!allTopics.contains(topicName)) {
toRemove.add(topicName);
}
toAdd.remove(topicName);
});
try {
for (String topic : toRemove) {
try {
jcsmpSession.removeSubscription(JCSMPFactory.onlyInstance().createTopic(topic));
appliedSubscriptions.remove(topic);
log.info("remove subscription for topic: " + topic);
} catch (Exception ex) {
log.warn("could not remove subscription, continuing", ex);
}
}
for (String topic : toAdd) {
jcsmpSession.addSubscription(JCSMPFactory.onlyInstance().createTopic(topic));
appliedSubscriptions.add(topic);
log.info("add subscription for topic: " + topic);
}
} catch (JCSMPException e) {
String msg = "Failed to get message consumer for topic consumer";
log.warn(msg, e);
throw new MessagingException(msg, e);
}
}
public JCSMPInboundTopicMessageProducer createTopicMessageProducer(ConsumerDestination destination, String group, ExtendedConsumerProperties properties) {
this.ensureXMLMessageConsumer();
return new JCSMPInboundTopicMessageProducer((SolaceConsumerDestination) destination, group, properties, this.solaceMeterAccessorSupplier, tracingProxy, livecycleHooks);
}
public interface LivecycleHooks {
void start(JCSMPInboundTopicMessageProducer producer);
void stop(JCSMPInboundTopicMessageProducer producer);
}
}