I have 2 Spring RabbitMq configurations, one using the RabbitTemplate, one using the JmsTemplate.
The configuration with the RabbitTemplate:
Class AmqpMailIntegrationPerfTestConfig:
#Configuration
#ComponentScan(basePackages = {
"com.test.perf.amqp.receiver",
"com.test.perf.amqp.sender"
})
#EnableRabbit
public class AmqpMailIntegrationPerfTestConfig {
#Bean
public DefaultClassMapper classMapper() {
DefaultClassMapper classMapper = new DefaultClassMapper();
Map<String, Class<?>> idClassMapping = new HashMap<>();
idClassMapping.put("mail", MailMessage.class);
classMapper.setIdClassMapping(idClassMapping);
return classMapper;
}
#Bean
public Jackson2JsonMessageConverter jsonMessageConverter() {
Jackson2JsonMessageConverter jsonConverter = new Jackson2JsonMessageConverter();
jsonConverter.setClassMapper(classMapper());
return jsonConverter;
}
#Bean
public RabbitTemplate myRabbitTemplate(ConnectionFactory connectionFactory) {
final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMessageConverter(jsonMessageConverter());
return rabbitTemplate;
}
#Bean
public ConnectionFactory createConnectionFactory(){
CachingConnectionFactory connectionFactory = new CachingConnectionFactory("localhost");
return connectionFactory;
}
#Bean
Queue queue() {
return new Queue(AmqpMailSenderImpl.QUEUE_NAME, false);
}
#Bean
TopicExchange exchange() {
return new TopicExchange(AmqpMailSenderImpl.TOPIC_EXCHANGE_NAME);
}
#Bean
Binding binding(Queue queue, TopicExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(AmqpMailSenderImpl.ROUTING_KEY);
}
#Bean
public AmqpAdmin amqpAdmin() {
return new RabbitAdmin(createConnectionFactory());
}
#Bean
public SimpleRabbitListenerContainerFactory myRabbitListenerContainerFactory() {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(createConnectionFactory());
factory.setMaxConcurrentConsumers(5);
factory.setMessageConverter(jsonMessageConverter());
return factory;
}
}
The AmqpMailSenderPerfImpl class in com.test.perf.amqp.sender package:
#Component
public class AmqpMailSenderPerfImpl implements MailSender {
public static final String TOPIC_EXCHANGE_NAME = "mails-exchange";
public static final String ROUTING_KEY = "mails";
#Autowired
private RabbitTemplate rabbitTemplate;
#Override
public boolean sendMail(MailMessage message) {
rabbitTemplate.convertAndSend(TOPIC_EXCHANGE_NAME, ROUTING_KEY, message);
return true;
}
}
The AmqpMailReceiverPerfImpl class in com.test.perf.amqp.receiver package:
#Component
public class AmqpMailReceiverPerfImpl implements ReceivedDatesKeeper {
private Logger logger = LoggerFactory.getLogger(getClass());
private Map<String,Date> datesReceived = new HashMap<String, Date>();
#RabbitListener(containerFactory = "myRabbitListenerContainerFactory", queues = AmqpMailSenderImpl.QUEUE_NAME)
public void receiveMessage(MailMessage message) {
logger.info("------ Received mail! ------\nmessage:" + message.getSubject());
datesReceived.put(message.getSubject(), new Date());
}
public Map<String, Date> getDatesReceived() {
return datesReceived;
}
}
The configuration with the JmsTemplate:
Class JmsMailIntegrationPerfTestConfig:
#Configuration
#EnableJms
#ComponentScan(basePackages = {
"com.test.perf.jms.receiver",
"com.test.jms.sender"
})
public class JmsMailIntegrationPerfTestConfig {
#Bean
public MessageConverter jacksonJmsMessageConverter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
Map<String,Class<?>> typeIdMappings = new HashMap<String,Class<?>>();
typeIdMappings.put("mail", MailMessage.class);
converter.setTypeIdMappings(typeIdMappings);
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("_type");
return converter;
}
#Bean
public ConnectionFactory createConnectionFactory(){
RMQConnectionFactory connectionFactory = new RMQConnectionFactory();
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
connectionFactory.setVirtualHost("/");
connectionFactory.setHost("localhost");
connectionFactory.setPort(5672);
return connectionFactory;
}
#Bean(name = "myJmsFactory")
public JmsListenerContainerFactory<?> myFactory(ConnectionFactory connectionFactory) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setConcurrency("10-50");
factory.setMessageConverter(jacksonJmsMessageConverter());
return factory;
}
#Bean
public Destination jmsDestination() {
RMQDestination jmsDestination = new RMQDestination();
jmsDestination.setDestinationName("myQueue");
jmsDestination.setAmqp(false);
jmsDestination.setAmqpQueueName("mails");
return jmsDestination;
}
#Bean
public JmsTemplate myJmsTemplate(ConnectionFactory connectionFactory) {
final JmsTemplate jmsTemplate = new JmsTemplate(connectionFactory);
jmsTemplate.setMessageConverter(jacksonJmsMessageConverter());
return jmsTemplate;
}
}
The JmsMailSenderImpl class in package com.test.jms.sender:
#Component
public class JmsMailSenderImpl implements MailSender {
private Logger logger = LoggerFactory.getLogger(getClass());
#Autowired
private JmsTemplate jmsTemplate;
#Override
public boolean sendMail(MailMessage message) {
logger.info("Sending message!");
jmsTemplate.convertAndSend("mailbox", message);
return false;
}
}
The JmsMailReceiverPerfImpl class in package com.test.perf.jms.receiver:
#Component
public class JmsMailReceiverPerfImpl implements ReceivedDatesKeeper {
private Logger logger = LoggerFactory.getLogger(getClass());
private Map<String,Date> datesReceived = new HashMap<String, Date>();
#JmsListener(destination = "mailbox", containerFactory = "myJmsFactory", concurrency = "10")
public void receiveMail(MailMessage message) {
datesReceived.put(message.getSubject(), new Date());
logger.info("Received <" + message.getSubject() + ">");
}
public Map<String, Date> getDatesReceived() {
return datesReceived;
}
}
I test the above configurations by starting 10 threads and making the respective MailSenders send 1000 mails each.
For the config with the RabbitTemplate I get:
* Total throughput time of all messages: 3687ms
* Time to process one message: 817ms
For the config with the JmsTemplate I get:
* Total throughput time of all messages: 41653ms
* Time to process one message: 67ms
This seems to indicate that the version with the JmsTemplate is not working in parallel, or at least, does not use resources optimally.
Does anybody know what could be causing this? I played around with different transaction and concurrency parameters but to no avail.
What we want is to get the same throughput time with the JmsTemplate as with the RabbitTemplate, so we can use JMS as an abstraction layer.
I can see why the consumer side is slower - the Consumer.receive() uses a synchronous basicGet() for each message whereas the #RabbitListener container uses basicConsume with a prefetch count of 250.
On the JMS sending side, you need to use a CachingConnectionFactory there are well, otherwise a new session/producer/channel is created for each send.
It's still quite a bit slower, though, even with that; I suggest you ask on the rabbitmq-users Google group where the RabbitMQ engineers hang out. They maintain the JMS client.
Related
#EnableIntegration
#Configuration
#ConfigurationProperties(prefix = "mqtt")
#IntegrationComponentScan(basePackages = "org.sample.mqtt")
public class MqttConfig {
private String[] serverUris;
private String username;
private char[] password;
private int keepAliveInterval;
private String[] subTopics;
private Class<? extends BytesMessageMapper> messageMapper;
private String clientIdPrefix;
private String modelPackages;
#Bean
public MqttPahoClientFactory mqttClientFactory() {
DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
MqttConnectOptions options = new MqttConnectOptions();
options.setServerURIs(serverUris);
options.setUserName(username);
options.setPassword(password);
options.setKeepAliveInterval(keepAliveInterval);
factory.setConnectionOptions(options);
return factory;
}
#Bean
public MqttMessageConverter bytesMessageConverter() throws NoSuchMethodException {
BytesMessageMapper bytesMessageMapper = BeanUtils.instantiateClass(messageMapper.getConstructor(String.class), modelPackages);
return new BytesMessageConverter(bytesMessageMapper);
}
#Bean
#ServiceActivator(inputChannel = "mqttOutboundChannel")
public MessageHandler mqttOutbound(MqttMessageConverter mqttMessageConverter) {
MqttPahoMessageHandler messageHandler = new MqttPahoMessageHandler(clientIdPrefix + "_outbound", mqttClientFactory());
messageHandler.setConverter(mqttMessageConverter);
messageHandler.setCompletionTimeout(5000);
messageHandler.setAsync(true);
return messageHandler;
}
#Bean
public MessageProducer mqttInbound(MqttMessageConverter mqttMessageConverter) {
MqttPahoMessageDrivenChannelAdapter adapter = new MqttPahoMessageDrivenChannelAdapter(clientIdPrefix + "_inbound", mqttClientFactory(), subTopics);
adapter.setConverter(mqttMessageConverter);
adapter.setOutputChannel(mqttInboundChannel());
adapter.setCompletionTimeout(5000);
adapter.setQos(1);
return adapter;
}
#Bean
public MessageChannel mqttOutboundChannel() {
return new DirectChannel();
}
#Bean
public MessageChannel mqttInboundChannel() {
return new DirectChannel();
}
MqttClient client = new MqttClient(serverUrl, clientId, new MemoryPersistence());
According to clientId, multiple MQTTClients can be created and maintained in HashMap. How can we achieve the same effect if we use Spring-integration-MQTT
How do I create multiple MQTT clients through HTTP requests and maintain them in a HashMap
IntegrationFlowContext Dynamic registration FlowContext.Registration (flow).register();
But still do not know how to handle and use. How can each MQTT client be maintained and managed so that it can be selected to produce data based on the key of the hashMap
I am trying to connect node-red with rabbitMQ. The program will recive a text with the diet that the AI chose for each day. I made the rabbitMQ Configure java file and the rabbit MQ Sender file. Here are those. The probllem is that I don't know how to do the sender file that contains the conexion:
rabbitMQ Configure:
#Configuration
public class RabbitMQConfig {
public static final String EXCHANGER_NAME = "Prueba";
private static final boolean DURABLE = false;
private static final boolean AUTO_DELETE = false;
public static final String ROUTING_KEY_PUESTA = "SinProcesar";
public static final String ROUTING_KEY_OBTENCION = "Procesada";
public static final String QUEUE_NAME_PUESTA="Dejar";
public static final String QUEUE_NAME_OBTENCION="Obtener";
#Bean
Queue queue1() {
return new Queue(QUEUE_NAME_PUESTA, DURABLE);
}
#Bean
Queue queue2() {
return new Queue(QUEUE_NAME_OBTENCION, DURABLE);
}
#Bean
DirectExchange exchange() {
return new DirectExchange(EXCHANGER_NAME,DURABLE, AUTO_DELETE);
}
#Bean
Binding binding1(Queue queue1, DirectExchange exchange) {
return BindingBuilder.bind(queue1).to(exchange).with(ROUTING_KEY_PUESTA);
}
#Bean
Binding binding2(Queue queue2, DirectExchange exchange) {
return BindingBuilder.bind(queue2).to(exchange).with(ROUTING_KEY_OBTENCION);
}
/*#Bean
public AmqpAdmin amqpAdmin(ConnectionFactory connectionFactory) {
return new RabbitAdmin(connectionFactory);
}*/
#Bean
SimpleMessageListenerContainer container(ConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter) {
final SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setQueueNames(QUEUE_NAME_OBTENCION);
container.setMessageListener(listenerAdapter);
container.setDefaultRequeueRejected(false);
return container;
}
#Bean
Receiver receiver() {
return new Receiver();
}
#Bean
MessageListenerAdapter listenerAdapter(Receiver receiver) {
return new MessageListenerAdapter(receiver, Receiver.RECEIVE_METHOD_NAME);
}
#Bean
public RabbitTemplate rabbitTemplate(final ConnectionFactory connectionFactory) {
final var rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMessageConverter(producerJackson2MessageConverter());
return rabbitTemplate;
}
#Bean
public Jackson2JsonMessageConverter producerJackson2MessageConverter() {
return new Jackson2JsonMessageConverter();
}
}
And the rabbitMQ Sender:
#Controller
public class RabbitMQSender {
#Autowired
private RabbitTemplate template;
#GetMapping(value="/msg")
public void enviarMensaje( ) {
template.convertAndSend(RabbitMQConfig.EXCHANGE, RabbitMQConfig.ROUTING_KEY);
}
}
How should I do the rabbitMQ Sender file?
I need to remove message from activemq soon after I consumed it. For an example, I send a message to the queue, then I consume it and I need to remove it from queue. I have used a messagestorage here and a clear() method. Messages get added to the queue and delete only from message storage but not from queue. I need a way to remove messages from queue. Appreciate your help.!
I have tried below code.
Producer
#Component
public class JmsProducer {
#Autowired
JmsTemplate jmsTemplate;
#Value("${gkz.activemq.queue}")
String queue;
public void send(Customer customer){
jmsTemplate.convertAndSend(queue, customer);
}
}
Consumer
#Component
public class JmsConsumer {
#Autowired
private MessageStorage customerStorage;
#JmsListener(destination = "${gkz.activemq.queue}",containerFactory="jsaFactory")
public void receive(Customer customer){
System.out.println("Recieved Message: " + customer);
customerStorage.add(customer);
}
}
Controller
#PostMapping(value="/api/customer")
public Customer postCustomer(#RequestBody Customer customer){
jmsProducer.send(customer);
return customer;
}
#GetMapping(value="/api/customers")
public List<Customer> getAll(){
List<Customer> customers = customerStorage.getAll();
return customers;
}
#DeleteMapping(value="/api/customers/clear")
public String clearCustomerStorage() {
customerStorage.clear();
return "Clear All CustomerStorage!";
}
MessageStorage
public class MessageStorage {
private List<Customer> customers = new ArrayList<>();
public void add(Customer customer) {
customers.add(customer);
}
public void clear() {
customers.clear();
}
public List<Customer> getAll(){
return customers;
}
}
ConnectionFactoryConfiguration
#Configuration
public class ConnectionFactoryConfiguration {
#Value("${gkz.activemq.broker.url}")
String brokerUrl;
#Value("${gkz.activemq.borker.username}")
String userName;
#Value("${gkz.activemq.borker.password}")
String password;
#Bean
public ConnectionFactory connectionFactory(){
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory();
connectionFactory.setBrokerURL(brokerUrl);
connectionFactory.setUserName(userName);
connectionFactory.setPassword(password);
return connectionFactory;
}
#Bean
public MessageConverter jacksonJmsMessageConverter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("_type");
return converter;
}
//Used for Receiving Message
#Bean
public JmsListenerContainerFactory<?> jsaFactory(ConnectionFactory connectionFactory,
DefaultJmsListenerContainerFactoryConfigurer configurer) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setMessageConverter(jacksonJmsMessageConverter());
configurer.configure(factory, connectionFactory);
return factory;
}
//Used for Sending Messages.
#Bean
public JmsTemplate jmsTemplate(){
JmsTemplate template = new JmsTemplate();
template.setMessageConverter(jacksonJmsMessageConverter());
template.setConnectionFactory(connectionFactory());
return template;
}
}
This whole code is available in https://grokonez.com/java-integration/distributed-system/activemq-producer-consumer-springboot-restapis-example
Click the purge option on MQ later restart the MQ service. That will clear all the messages which are not in pending status.
I have modified my RabbitMQ from a previous post (spring-rabbit JSON deserialization for ArrayList contents) to now use a DirectMessageListener with MessagePostProcessors to GZip and GUnzip the message payloads.
However, it doesn't appear to be working as the breakpoints are not activated, but also because my RabbitListeners are no longer receiving messages, whereas they did with a SimpleMessageFactoryListenerContainer.
Also, it appears the SimpleMessageListenerContainer(?) is still being used. On a side-note, I am autowiring the DirectMessageListenerContainer so I can dynamically set the queues I used.
spring-rabbit: 2.0.3.RELEASE.
spring-boot: 2.0.1.RELEASE.
RabbitMQ configuration:
#Configuration
#EnableRabbit
public class MessagingConfiguration implements ShutdownListener {
#Autowired
private RabbitListenerEndpointRegistry registry;
#Autowired
private DirectMessageListenerContainer container;
#Bean
public DirectMessageListenerContainer messageListenerContainer(final ConnectionFactory connectionFactory) {
final DirectMessageListenerContainer listenerContainer = new DirectMessageListenerContainer();
listenerContainer.setConnectionFactory(connectionFactory);
listenerContainer.setMessageConverter(jsonConverter()); // i.e.#RabbitListener to use Jackson2JsonMessageConverter
listenerContainer.setAutoStartup(false);
// container.setErrorHandler(errorHandler);
final MessageListenerAdapter messageListener = new MessageListenerAdapter(new Object() {
#SuppressWarnings("unused")
public String handleMessage(final String message) {
return message.toUpperCase();
}
});
messageListener.setBeforeSendReplyPostProcessors(new GZipPostProcessor());
listenerContainer.setMessageListener(messageListener);
listenerContainer.setAfterReceivePostProcessors(new GUnzipPostProcessor());
return listenerContainer;
}
#EventListener(ApplicationDatabaseReadyEvent.class)
public void onApplicationDatabaseReadyEvent() {
log.info("Starting all RabbitMQ Listeners..."); //$NON-NLS-1$
for (final MessageListenerContainer listenerContainer : registry.getListenerContainers()) {
listenerContainer.start();
}
log.info("Register is running: {}", registry.isRunning()); //$NON-NLS-1$
log.info("Started all RabbitMQ Listeners."); //$NON-NLS-1$
}
#Bean
public List<Declarable> bindings() {
final List<Declarable> declarations = new ArrayList<>();
final FanoutExchange exchange = new FanoutExchange("fx", true, false);
final Queue queue = QueueBuilder.durable("orders").build();
declarations.add(exchange);
declarations.add(queue);
declarations.add(BindingBuilder.bind(queue).to(exchange));
List<String> q = new ArrayList<>();
q.add(queue.getName());
container.addQueueNames(q.toArray(new String[queues.size()]));
return declarations;
}
#Bean
public Jackson2JsonMessageConverter jsonConverter() {
final Jackson2JsonMessageConverter converter = new Jackson2JsonMessageConverter();
converter.setClassMapper(classMapper());
return converter;
}
private static DefaultJackson2JavaTypeMapper classMapper() {
final DefaultJackson2JavaTypeMapper classMapper = new DefaultJackson2JavaTypeMapper();
classMapper.setTrustedPackages("*"); //$NON-NLS-1$ //TODO add trusted packages
return classMapper;
}
#ConditionalOnProperty(name = "consumer", havingValue = "true")
#Bean
public ConsumerListener listenerConsumer() {
return new ConsumerListener();
}
#ConditionalOnProperty(name = "producer", havingValue = "true")
#Bean
public ProducerListener listenerProducer() {
return new ProducerListener();
}
#Bean
public RabbitAdmin rabbitAdmin(final CachingConnectionFactory connectionFactory) {
return new RabbitAdmin(connectionFactory);
}
#Bean
public RabbitTemplate rabbitTemplate(final ConnectionFactory connectionFactory) {
final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMessageConverter(jsonConverter()); // convert all sent messages to JSON
rabbitTemplate.setReplyTimeout(TimeUnit.SECONDS.toMillis(3));
rabbitTemplate.setReceiveTimeout(TimeUnit.SECONDS.toMillis(3));
return rabbitTemplate;
}
#Override
public void shutdownCompleted(final ShutdownSignalException arg0) {
}
}
It doesn't work that way, you can't autowire containers for #RabbitListeners; they are not beans; they are created by the container factory and registered in the registry. Instead you have to retrieve them from the registry (by id).
However, since you have autoStartup set to false, it shouldn't be "stealing" messages from your #RabbitListener.
Generally, DEBUG logging should help.
I am experimenting with the Spring Reactor 3 components and Spring Integration to create a reactive stream (Flux) from a JMS queue.
I am attempting to create a reactive stream (Spring Reactor 3 Flux) from a JMS queue (ActiveMQ using Spring Integration) for clients to get the JMS messages asynchronously. I believe that I have everything hooked up correctly but the client does not receive any of the JMS messages until the server is stopped. Then all of the messages get "pushed" to the client a once.
Any help would be appreciated.
Here is the configuration file that I am using to configure the JMS, Integration components and the reactive publisher:
#Configuration
#EnableJms
#EnableIntegration
public class JmsConfiguration {
#Value("${spring.activemq.broker-url:tcp://localhost:61616}")
private String defaultBrokerUrl;
#Value("${queues.patient:patient}")
private String patientQueue;
#Autowired
MessageListenerAdapter messageListenerAdapter;
#Bean
public DefaultJmsListenerContainerFactory myFactory(
DefaultJmsListenerContainerFactoryConfigurer configurer) {
DefaultJmsListenerContainerFactory factory =
new DefaultJmsListenerContainerFactory();
configurer.configure(factory, jmsConnectionFactory());
return factory;
}
#Bean
public Queue patientQueue() {
return new ActiveMQQueue(patientQueue);
}
#Bean
public ActiveMQConnectionFactory jmsConnectionFactory() {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory();
connectionFactory.setBrokerURL(defaultBrokerUrl);
connectionFactory.setTrustedPackages(Arrays.asList("com.sapinero"));
return connectionFactory;
}
// Set the jackson message converter
#Bean
public JmsTemplate jmsTemplate() {
JmsTemplate template = new JmsTemplate();
template.setConnectionFactory(jmsConnectionFactory());
template.setDefaultDestinationName(patientQueue);
template.setMessageConverter(jacksonJmsMessageConverter());
return template;
}
#Bean
public MessageListenerAdapter messageListenerAdapter() {
MessageListenerAdapter messageListenerAdapter = new MessageListenerAdapter();
messageListenerAdapter.setMessageConverter(jacksonJmsMessageConverter());
return messageListenerAdapter;
}
#Bean
public AbstractMessageListenerContainer messageListenerContainer() {
DefaultMessageListenerContainer defaultMessageListenerContainer = new DefaultMessageListenerContainer();
defaultMessageListenerContainer.setMessageConverter(jacksonJmsMessageConverter());
defaultMessageListenerContainer.setConnectionFactory(jmsConnectionFactory());
defaultMessageListenerContainer.setDestinationName(patientQueue);
defaultMessageListenerContainer.setMessageListener(messageListenerAdapter());
defaultMessageListenerContainer.setCacheLevel(100);
defaultMessageListenerContainer.setErrorHandler(new ErrorHandler() {
#Override
public void handleError(Throwable t) {
t.printStackTrace();
}
});
return defaultMessageListenerContainer;
}
#Bean // Serialize message content to json using TextMessage
public MessageConverter jacksonJmsMessageConverter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("_type");
return converter;
}
#Bean
public MessageChannel jmsOutboundInboundReplyChannel() {
return MessageChannels.queue().get();
}
#Bean
public Publisher<Message<String>> pollableReactiveFlow() {
return IntegrationFlows
.from(Jms.messageDrivenChannelAdapter(messageListenerContainer()).get())
.channel(MessageChannels.queue())
.log(LoggingHandler.Level.DEBUG)
.log()
.toReactivePublisher();
}
#Bean
public MessageChannel jmsChannel() {
return new DirectChannel();
}
The controller that creates the Flux is:
#RestController
#RequestMapping("patients")
public class PatientChangePushController {
private LocalDateTime lastTimePatientDataRetrieved = LocalDateTime.now();
private int durationInSeconds = 30;
private Patient patient;
AtomicReference<SignalType> checkFinally = new AtomicReference<>();
#Autowired
PatientService patientService;
#Autowired
#Qualifier("pollableReactiveFlow")
private
Publisher<Message<String>> pollableReactiveFlow;
#Autowired
private JmsTemplate jmsTemplate;
#Autowired
private Queue patientQueue;
/**
* Subscribe to a Flux of a patient that has been updated.
*
* #param id
* #return
*/
#GetMapping(value = "/{id}/alerts", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<Message<String>> getPatientAlerts(#PathVariable Long id) {
Flux<Message<String>> messageFlux = Flux.from(pollableReactiveFlow);
return messageFlux;
}
#GetMapping(value = "/generate")
public void generateJmsMessage() {
for (long i = 0L; i < 100; i++) {
Patient patient = new Patient();
patient.setId(i);
send(patient);
System.out.println("Message was sent to the Queue");
}
}
void send(Patient patient) {
this.jmsTemplate.convertAndSend(this.patientQueue, patient);
}
}
If anyone can tell me why the messages do not get sent to the client until after the server is killed, I would appreciate it.
Works well for me:
#SpringBootApplication
#RestController
public class SpringIntegrationSseDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringIntegrationSseDemoApplication.class, args);
}
#Autowired
private ConnectionFactory connectionFactory;
#Autowired
private JmsTemplate jmsTemplate;
#Bean
public Publisher<Message<String>> jmsReactiveSource() {
return IntegrationFlows
.from(Jms.messageDrivenChannelAdapter(this.connectionFactory)
.destination("testQueue"))
.channel(MessageChannels.queue())
.log(LoggingHandler.Level.DEBUG)
.log()
.toReactivePublisher();
}
#GetMapping(value = "/events", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> getPatientAlerts() {
return Flux.from(jmsReactiveSource())
.map(Message::getPayload);
}
#GetMapping(value = "/generate")
public void generateJmsMessage() {
for (int i = 0; i < 100; i++) {
this.jmsTemplate.convertAndSend("testQueue", "testMessage #" + (i + 1));
}
}
}
In one terminal I have curl http://localhost:8080/events which waits for SSEs from that Flux.
In other terminal I perform curl http://localhost:8080/generate and see in the first one:
data:testMessage #1
data:testMessage #2
data:testMessage #3
data:testMessage #4
I use Spring Boot 2.0.0.BUILD-SNAPSHOT.
Also see here: https://spring.io/blog/2017/03/08/spring-tips-server-sent-events-sse