I have embedded ActiveMQ broker configured in Spring with websocket support (using STOMP).
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketMqConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableStompBrokerRelay("/topic");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/messaging")
.setAllowedOrigins("*")
.withSockJS();
}
#Bean(initMethod = "start", destroyMethod = "stop")
public BrokerService brokerService() throws Exception {
PersistenceAdapter persistenceAdapter = getPersistenceAdapter();
BrokerService brokerService = new BrokerService();
brokerService.setPersistent(true);
brokerService.setDeleteAllMessagesOnStartup(true);
brokerService.setUseJmx(false);
brokerService.setBrokerName("broker");
brokerService.addConnector("stomp://localhost:61613");
return borkerService;
}
In my JavaScript client I subscribe to topic:
var successHandler = function() {
stompClient.subscribe('/topic/test', function(not) {
pushNotification(not);
}, {'id': clientId, 'activemq.subscriptionName': clientId});
};
var socket = new SockJS('/messaging');
var stompClient = Stomp.over(socket);
stompClient.connect({'client-id': clientId}, successHandler, failureHandler);
And I am using backend service to feed this topic:
#Autowired
private SimpMessagingTemplate messagingTemplate;
messagingTemplate.convertAndSend("/topic/test", event);
And here are my questions:
When I send message to topic, but client haven't been subscribed yet, why messages are not persisted (I suppose that after client subscribe, he should be notified about missed messages)?
If client disconnect from topic, every message is persisted, is there any mean to restrict number of persisted messages, time or size of KahaDB's log files?
Messages sent to a Topic are not persisted unless the client has created a durable Topic subscription previously and the message is sent with the persistent flag set. To create a durable subscription add the headers as specified in the ActiveMQ STOMP documentation.
Once you start using durable Topic subscriptions then yes message can accumulate in the KahaDB store at which point you can configure the store usage limits to control size.
Related
The following code is from spring mvc documentation:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/portfolio");
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/app");
registry.enableSimpleBroker("/topic");
}
}
#Controller
public class GreetingController {
#MessageMapping("/greeting") {
public String handle(String greeting) {
return "[" + getTimestamp() + ": " + greeting;
}
}
The client connects to http://localhost:8080/portfolio to establish WebSocket connection, I wonder what's the exact url of client sending request?
http://localhost:8080/portfolio/app
or
http://localhost:8080/app?
and in actual WebSocket frame, does the destination header contain relative url like /app, /topic or absolute url?
[Android] https://github.com/NaikSoftware/StompProtocolAndroid
[Spring] https://docs.spring.io/spring/docs/5.1.9.RELEASE/spring-framework-reference/web.html#websocket-stomp
Just set the end point by using
addEndpoint("/portfolio");
Use the following Url to connect to websocket
ws://localhost:8080/portfolio
But remember you have to connect to socket only once and after that just invoke the endpoints without URL. Beacause socket is streamline connection and you have to establish connection only once.
setApplicationDestinationPrefixes("/app");
Above line will set the end point /app using this you can only publish over the socket. However all who has subscribed to this topic will get notified.
enableSimpleBroker("/topic");
Broker are responsible for handling subscribe and publish for both as they listen and send data in dual way means publish and subscribe both unlike /app.
private var mStompClient: StompClient? = null
mStompClient = Stomp.over(Stomp.ConnectionProvider.OKHTTP, "ws://localhost:8080/portfolio")
Connect to websocket using the above line. since we have to connect to socket end point only once write this in singleton.
val response = stomp.topic("/topic")
.subscribe { topicMessage -> }
Now above line will subscribe to your socket client means anytime you pushed the data from /topic this will this response variable will notified.
stompClient.send(StompMessage(StompCommand.SEND,
listOf(StompHeader(StompHeader.DESTINATION, "/topic")),
gson.toJson(myDataModel)))?
.subscribe()
Using above line you will you will you will send data to the socket which is specified as /topic.
#MessageMapping("/action")
fun performDeviceAction(#Payload myDataModel: MyDataModel) {}
Use the above line to receive the data from client on socket /action
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new SocketTextHandler(), "/user");
}
In order to tell Spring to forward client requests to the endpoint , we need to register the handler. Above snipplet will register a client.
Use below link and download source code for more information
https://www.javainuse.com/spring/boot-websocket
I am using JMS via IBM MQ. My sender code:
#Autowired
private JmsTemplate jmsTemplate;
public void sendPHRq(String msg) {
jmsTemplate.send(AntiFraudRq, session -> {
Message message = session.createTextMessage(msg);
LOGGER.info("1" + message.getJMSCorrelationID());
LOGGER.info("2" + message.getJMSMessageID());
return message;
});
}
But in my log I can see only such record:
1null
2null
How can I get my messageid? Because I'm listening reply queue with another listeners, and shouldn't take their messages.
You can get JMSMessageID after message is sent.
It will be generated by MQ JMS right before sending the message.
I have implemented the example as shown here Spring Dynamic Destination
In the rabbitmq, it is creating an exchange dynamically, but there is no option to provide binding or routing key. My requirement is to send a message to this dynamically created exchange with a routing key. How would i need to implement this to setup the routing key?
#Component
public class DDProducerBean {
#Autowired
private BinderAwareChannelResolver poChannelResolver = null;
public void publish(DDSocketVO ddSocketVO) throws Exception {
this.poChannelResolver.resolveDestination(ddSocketVO.getDestination()).send(MessageBuilder.withPayload(new ObjectMapper().
setVisibility(PropertyAccessor.FIELD, Visibility.ANY).
writeValueAsString(ddSocketVO)).build());
}
}
Here is the workaround as suggested Here
Basically create a MessageChannel with the dynamic destination using BinderAwareChannelResolver, then connect to RabbitMQ with RabbitAdmin API and bind the newly created exchange to another queue or exchange with routing key before sending messages.
#Autowired
private BinderAwareChannelResolver poChannelResolver;
public void publish(WebSocketVO webSocketVO) throws Exception {
MessageChannel channel = this.poChannelResolver.resolveDestination(webSocketVO.getDestination());
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setUsername(System.getProperty("spring.cloud.stream.binders.corerabbit.environment.spring.rabbitmq.username"));
connectionFactory.setPassword(System.getProperty("spring.cloud.stream.binders.corerabbit.environment.spring.rabbitmq.password"));
connectionFactory.setAddresses(System.getProperty("spring.cloud.stream.binders.corerabbit.environment.spring.rabbitmq.addresses"));
connectionFactory.setVirtualHost(System.getProperty("spring.cloud.stream.binders.corerabbit.environment.spring.rabbitmq.virtual-host"));
AmqpAdmin amqpAdmin = new RabbitAdmin(connectionFactory);
TopicExchange sourceExchange = new TopicExchange(webSocketVO.getDestination(), false, true);
TopicExchange destExchange = new TopicExchange("amq.topic");
amqpAdmin.declareBinding(BindingBuilder.bind(destExchange).to(sourceExchange).with(webSocketVO.getRoutingKeyExpression()));
channel.send(MessageBuilder.withPayload(new ObjectMapper().
setVisibility(PropertyAccessor.FIELD, Visibility.ANY).
writeValueAsString(webSocketVO)).build());
amqpAdmin.deleteExchange(webSocketVO.getDestination());
connectionFactory.destroy();
}
I want to use ActiveMQ within Spring Boot app as embedded server. To setup ActiveMQ I used following tutorial: Spring Boot. Messaging with JMS. My app will be the broker and the consumer. There are multiple threads creating messages like this:
#Autowired
private JmsTemplate jmsTemplate;
.......
MessageCreator messageCreator = session -> session.createObjectMessage(transactionNotificationData);
jmsTemplate.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE);
jmsTemplate.send(QUEUE, messageCreator);
I have another class with following method:
#JmsListener(destination = QUEUE)
public void receive(Message message) throws IOException {
brokerService.getPersistenceAdapter();
try {
if (message instanceof ObjectMessage) {
ObjectMessage objMessage = (ObjectMessage) message;
NotificationData notification = (NotificationData) objMessage.getObject();
LOG.info("Received <" + notification.notification + ">");
...... do some stuff ........
// message.acknowledge();
}
} catch (JMSException e) {
e.printStackTrace();
}
During the tests I can see the messages are produced and consumed.
As you can see message.acknowledge() is commented. So I expect the message will be redelivered after rerun of my app. However it doesn't happen.
Message Acknowledgement is automatically handled by the container and it executes after the onMessage() is successfully executed,(receive() in your case),
so even when you comment message.acknowledge(); , container on its own sends the acknowledgement
you can have a look at following link for more reference
Acknowledgement from Consumer in ActiveMQ
Hope this helps!
Good luck!
Have Apache Camel simple message route from folder to ActiveMQ topic:
//Create context to create endpoint, routes, processor within context scope
CamelContext context = new DefaultCamelContext();
//Create endpoint route
context.addRoutes(new RouteBuilder() {
#Override
public void configure() throws Exception
{
from("file:data/outbox").to("activemq:topic:Vadim_Topic");
//from("activemq:topic:TEST").to.to("file:data/outbox");
}
});
context.start();
Thread.sleep(5000);
context.stop();
}
And JMS implementation if Topic Consumer:
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory();
try {
Connection connection = connectionFactory.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//connection.setClientID("12345");
connection.start();
Topic topic = session.createTopic("Vadim_Topic");
MessageConsumer messageConsumer = session.createConsumer(topic);
MessageListener messageListener = new MessageListener() {
public void onMessage(Message message) {
TextMessage textMessage = (TextMessage) message;
try {
System.out.println("Received message: " + textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
};
messageConsumer.setMessageListener(messageListener);
} catch (Exception e) {
e.printStackTrace();
}
Can't understand why is my consumer can't recieve messages sent by Camel route??
I guess thet problem is then I need to subscribe my JMS Consumer on messages sent by Camel?
How can I do this if this is the case?
Camel not only allows you to send messages to a topic, it can also very easily read messages from a topic and send it to one of your POJOs.
A route that reads from your topic and sends the messages to a POJO would look like this:
from("activemq:topic:Vadim_Topic").bean(ExampleBean.class);
Camel will figure out which method to call on the POJO depending on the type of message it received, and the available method signatures. See this page for details on using POJO's in camel routes: https://camel.apache.org/bean.html