Using STOMP Over WebSocket (stomp.js) connecting to Artemis Server, but setting selector is ineffective - stomp

The duration addressing and queue setting of Artemis server is:
<address-settings>
<address-setting match="#">
<auto-create-addresses>false</auto-create-addresses>
<auto-delete-addresses>false</auto-delete-addresses>
<auto-create-queues>false</auto-create-queues>
<auto-delete-queues>false</auto-delete-queues>
</address-setting>
</address-settings>
<addresses>
<address name="test">
<anycast>
<queue name="abc.filter"/>
</anycast>
</address>
</addresses>
The javascript client used to connect to the queue and subscribe message is:
<script src="jquery-3.5.1.min.js"></script>
<script src="stomp.js"></script>
<script>
$(document).ready(function() {
let client = Stomp.client("ws://127.0.0.1:61613");
// this allows to display debug logs directly on the web page
client.debug = function (str) {
console.log(str);
};
// the client is notified when it is connected to the server.
// let headers = {"ack": "client", "selector": "location = 'Europe'"};
let connected = function(frame) {
client.subscribe("/test", function(message) {
$("#messages").append("<p>" + message.body + "</p>\n");
}, {"durable-subscription-name": "filter", "selector": "tag = 'aaa'"});
};
client.connect({"client-id": "abc"}, connected);
});
</script>
Although the client sets the selector to filter the message, the client can receive message with other tags, such as bbb:
However, the similar selector is effective in Java:
class Consumer {
ActiveMQConnectionFactory factory;
Connection connection;
Session session;
Queue queue;
MessageConsumer consumer;
public Consumer() {
try {
factory = new ActiveMQConnectionFactory("tcp://127.0.0.1:61616");
connection = factory.createConnection();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
queue = session.createQueue("test::abc.filter");
consumer = session.createConsumer(queue, "tag='aaa'");
consumer.setMessageListener(new MessageListener() {
#Override
public void onMessage(Message message) {
try {
System.out.println("C-" + tag + " RECEIVING > " + ((TextMessage) message).getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
});
connection.start();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
I want to ask why the selector does not work in stomp-websocket?
The code to send message is:
public Producer() {
try {
factory = new ActiveMQConnectionFactory("tcp://127.0.0.1:61616");
connection = factory.createConnection();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
queue = session.createQueue("test::abc.filter");
producer = session.createProducer(queue);
producer.setDeliveryMode(DeliveryMode.PERSISTENT);
} catch (Exception e) {
e.printStackTrace();
}
}
public void send(String tag, String msg) {
try {
TextMessage message = session.createTextMessage(msg);
message.setStringProperty("tag", tag);
producer.send(message);
System.out.println("SENDING > " + message.getText());
} catch (Exception e) {
e.printStackTrace();
}
}

Related

Store the message which is received/sent to the queue using JmsListener

Is there any way to put interceptor in jms listener..requirement is to store the request and response while reading and writing to message queue
#JmsListener(destination = "${ibm.mq.queueName}", containerFactory = "containerFactory")
public void readAndProcessMessage(Message<?> message) throws Exception {
UnProcessedEvent unProcessedEvent;
String eventMessage = message.getPayload().toString();
log.info("Received an event: " + eventMessage);
try {
String iban = getIban(eventMessage);
// Identifying Topic
for (Mandate mandate : configProperties.getMandates()) {
for (Topic topic : mandate.getTopic()) {
if (topic.getAccountNumber().equals(iban)) {
publisherService.publishEvent(iban, eventMessage);
return;
}
}
}
unProcessedEvent = UnProcessedEvent.builder().incomingReqPayload((eventMessage)).reason("No Topic Found")
.reasonCode(HttpStatus.BAD_REQUEST.toString()).build();
unprocessedEventRepository.save(unProcessedEvent);
} catch (JAXBException e) {
log.info("Exception while parsing the event message: " + e.getMessage());
unProcessedEvent = UnProcessedEvent.builder().incomingReqPayload((eventMessage)).reason("Bad Request")
.reasonCode(HttpStatus.BAD_REQUEST.toString()).build();
unprocessedEventRepository.save(unProcessedEvent);
}
}

org.zeromq.ZMQException: Errno 48 : Address already in use

I am trying to implement a pub-sub example using ZeroMQ.
I run the publisher's code in a docker container and the subscriber's code in another one.
My subscriber is:
private ZMQ.Context context;
{
context = ZMQ.context(1);
}
public void receive() {
System.out.println("Getting subscriber, listening to tcp://localhost:5565");
getSubscriber();
byte[] raw;
System.out.println("Watching for new Event messages...");
try {
while (!Thread.currentThread().isInterrupted()) {
raw = subscriber.recv();
System.out.println("Event received " + raw);
}
} catch (Exception e) {
System.out.println("Unable to receive messages via ZMQ: " + e.getMessage());
}
if (subscriber != null)
subscriber.close();
subscriber = null;
System.out.println("Attempting restart of Event message watch.");
receive();
}
private ZMQ.Socket getSubscriber() {
if (subscriber == null) {
try {
subscriber = context.socket(ZMQ.SUB);
subscriber.connect("tcp://localhost:5565");
subscriber.subscribe("".getBytes());
} catch (Exception e) {
System.out.println("Unable to get a ZMQ subscriber. Error: " + e);
subscriber = null;
}
}
return subscriber;
}
And my publisher is:
private ZMQ.Context context;
{
context = ZMQ.context(1);
}
public synchronized void sendEventMessage(Event event) {
try {
if (publisher == null) {
getPublisher();
}
if (publisher != null) {
publisher.send(event);
}
} catch (Exception e) {
System.out.println("Unable to send message via ZMQ");
}
}
private void getPublisher() {
try {
if (publisher == null) {
publisher = context.socket(ZMQ.PUB);
publisher.bind("tcp://192.168.32.9:5565"); //where 192.168.32.9 is the IP of the subscriber's docker container
Thread.sleep(PUB_UP_SLEEP); // allow subscribers to connect
}
} catch (Exception e) {
System.out.println("Unable to get a publisher. Error: " + e);
publisher = null;
}
}
When I start the application, I register a subscriber and the logs are:
[2018-12-10 08:01:02.138] boot - 1 INFO [main] --- ZeroMQEventSubscriber: Getting subscriber, listening to tcp://localhost:5565
[2018-12-10 08:01:02.249] boot - 1 INFO [main] --- ZeroMQEventSubscriber: Watching for new Event messages...
My problem is that when I invoke sendEventMessage, the subscriber does not receive anything and on the publisher I get this error:
[2018-12-10 08:54:16.388] boot - 1 ERROR [task-scheduler-5] --- ZeroMQEventPublisherImpl: Unable to get a publisher. Error: org.zeromq.ZMQException: Errno 48 : Address already in use
Any ideas why I cannot bind to the address where the subscriber has connected?

TCPLink Error: invalid magic in the message

Below is the Java code to consume the durable subscription
private void execute()throws Exception {
logger.debug("Creating JNDI context");
Properties jndiProps = new Properties();
jndiProps.setProperty(Context.INITIAL_CONTEXT_FACTORY, config.getJndiFactory());
jndiProps.setProperty(Context.PROVIDER_URL, config.getJndiProviderUrl());
jndiProps.setProperty(Context.SECURITY_CREDENTIALS, config.getJndiSecurityCredential());
jndiProps.setProperty(Context.SECURITY_PRINCIPAL, config.getJndiSecurityPrincipal());
logger.debug("JNDI properties: " + jndiProps.toString());
jndiContext = new InitialContext(jndiProps);
logger.debug("JNDI context created successfully");
logger.debug("Looking up ConnectionFactory : " + config.getJmsConnFactory());
TopicConnectionFactory connFactory = (TopicConnectionFactory) jndiContext.lookup(config.getJmsConnFactory());
logger.debug("Creating connection object");
conn = connFactory.createTopicConnection(config.getJmsUserName(), config.getJmsPasswd());
logger.debug("Connection object successfully created");
TopicSession session = null;
MessageConsumer subscriber = null;
try {
session = conn.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
logger.debug("Starting connection");
conn.start();
logger.debug("Getting destinaton");
Topic topic = (Topic)jndiContext.lookup(config.getDestination());
if (topic == null) {
throw new RuntimeException("Invalid destination");
}
logger.debug("Creating durable subscriber");
if(config.isDurable()){
subscriber = session.createDurableSubscriber(topic, config.getSubscriberId());
}else{
subscriber = session.createSubscriber(topic);
}
boolean runFlag = true;
do {
logger.debug("Receiving messages...");
TextMessage message = (TextMessage) subscriber.receive(DEF_TIMEOUT_MILLIS);
if (message != null) {
logger.debug("Received message : " + message.getText());
continue;
}
logger.debug("No available messages now");
runFlag = false;
} while (runFlag);
logger.debug("There is no more messages available. Exiting...");
} finally {
if(config.isDurable()){
session.unsubscribe(config.getSubscriberId());
}
close(subscriber);
close(session);
}
}
While executing I am getting the "TCPLink Error: invalid magic in the message". And after that, session and connection is getting terminated automatically.
javax.jms.IllegalStateException: Session is closed
javax.jms.JMSException: Connection has been terminated
Please help.
Thanks in advance

How to get number of pending message in a jms queue

Is there any way to get count number of pending messages in jms queue. My aim is to close the connection if there is no message remaining in the queue to process. how can i achieve this.
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url);
Connection connection = connectionFactory.createConnection("admin", "admin");
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination destination = session.createQueue(subject);
MessageConsumer consumer = session.createConsumer(destination);
while (true) {
Message message = consumer.receive();
if (message instanceof TextMessage) {
TextMessage textMessage = (TextMessage) message;
System.out.println("Incoming Message:: '" + textMessage.getText() + "'");
}
}
The only reliable way to get the true Queue count form the broker is to use the JMX MBean for the Queue and call the getQueueSize method.
The other programmatic alternative is to use the Statistics Broker Plugin which requires that you be able to change broker configuration to install it. Once installed you can send a special message to the control queue and get a response with details for the destination you want to monitor.
Using a QueueBrowser doesn't give you a true count because the browser has a max limit on how many messages it will page into memory to send you, so if your queue is deeper than the limit you won't get the actual size, just the value of the max page size limit.
I have done this by using createBrowser method below is my updated code.
public static void main(String[] args) throws JMSException {
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url);
Connection connection = connectionFactory.createConnection("admin", "admin");
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination destination = session.createQueue(subject);
int queueSize = QueueConsumer.getQueueSize(session, (Queue) destination);
System.out.println("QUEUE SIZE: " + queueSize);
MessageConsumer consumer = session.createConsumer(destination);
for (int i = 0; i < queueSize; i++) {
Message message = consumer.receive();
if (message instanceof TextMessage) {
TextMessage textMessage = (TextMessage) message;
System.out.println("Incomming Message: '" + textMessage.getText() + "'");
}
}
connection.close();
}
private int getQueueSize(Session session, Queue queue) {
int count = 0;
try {
QueueBrowser browser = session.createBrowser(queue);
Enumeration elems = browser.getEnumeration();
while (elems.hasMoreElements()) {
elems.nextElement();
count++;
}
} catch (JMSException ex) {
ex.printStackTrace();
}
return count;
}
I have done this with jmx it worked thanx #Tim Bish
here is my updated code
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://0.0.0.0:44444/jndi/rmi://0.0.0.0:1099/karaf-root");
HashMap<String, String[]> environment = new HashMap<String, String[]>();
String[] creds = { "admin", "admin" };
environment.put(JMXConnector.CREDENTIALS, creds);
JMXConnector jmxc = JMXConnectorFactory.connect(url, environment);
MBeanServerConnection connection = jmxc.getMBeanServerConnection();
ObjectName nameConsumers = new ObjectName("org.apache.activemq:type=Broker,brokerName=amq,destinationType=Queue,destinationName=myqueue");
DestinationViewMBean mbView = MBeanServerInvocationHandler.newProxyInstance(connection, nameConsumers, DestinationViewMBean.class, true);
long queueSize = mbView.getQueueSize();
System.out.println(queueSize);
Just break the loop and close the connection if your JMS Message is null..
while (true) {
Message message = consumer.receive(2000);
if (message == null){
break;
}
if (message instanceof TextMessage) {
TextMessage textMessage = (TextMessage) message;
System.out.println("Incoming Message:: '" + textMessage.getText() + "'");
}
}
connection.close();

Trying to get the message from server but receive call gets blocked

// This is send message from where I am sending message to server, Its working fine
public void sendMessage(com.google.protobuf.Message sendmessage) {
try {
createJmsTemplate();
createJmsTemplateReciever();
JmsMessageCreator jmsMessageCreator = new JmsMessageCreator() {
#Override
public Message createMessage(Session session) throws JMSException {
BytesMessage msg = session.createBytesMessage();
msg.writeBytes(sendmessage.toByteArray());
return msg;
}
};
MessageCreator messageCreator = new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
Message msg = jmsMessageCreator.createMessage(session);
msg.setJMSCorrelationID("2708");
return msg;
}
};
jmsTemplate.send(messageCreator);
System.out.println("Message sent... ");
} catch (Exception e) {
e.printStackTrace();
System.exit(0);
}
}
//But when i am calling this method, at receive call it gets blocked...
public void recieveMessage() {
try {
byteMessage = (BytesMessage) jmsTemplateReciever.receive();
try {
if (byteMessage != null) {
byte[] byteArr = new byte[(int) byteMessage.getBodyLength()];
for (int i = 0; i < (int) byteMessage.getBodyLength(); i++) {
byteArr[i] = byteMessage.readByte();
String s = new String(byteArr);
System.out.println(s);
}
String s = new String(byteArr);
System.out.println(s);
byteMessage.acknowledge();
}
} catch (JMSException e) {
}
} catch (Exception e) {
e.printStackTrace();
System.exit(0);
}
}
}
As described in section 9.2.2 of JMS 1.1 Specification, the receive() call blocks indefinitely until a message arrives on the queue. Hence the call is getting blocked in your application.
One option for you is to specify a wait time, for example receive(3000) which waits for 3 seconds and comes out if no message arrives in 3 seconds. JMS implementer might be providing another form of receive method where the method returns immediately if there are no messages in the queue.
The other option is to use a message listener for receiving messages asynchronously as described JMS 1.1 Specifications section 9.3.1. Your application gets notified by the JMS provider whenever a message arrives in a queue.

Resources