I have a C++ component which passes message to JMS broker via tcp using ActiveMQ. My broker is written in JAVA. I want these two to communicate; to C++ component send messages to the JAVA broker.
On C++ side what I am doing is creating a message (using protocol buffer), converting that to vector of bytes and passing it to the broker. On JAVA side (broker), I am constantly listening and acting upon a received message.
Now, I can tell that the system somehow works, since when I execute my C++ component (and when it passes the messages), I see my JAVA broker printing an error message: unexpected error:null per each message that I am sending from my C++ component. This means that at least my messages do reach to the broker, but somehow they cannot be decrypted, hence the null issue.
I am using the following for composing the message from C++ side:
// convert pointmsg to byte
int size = pointmsg.ByteSize();
char* byteArray = new char[size];
pointmsg.SerializeToArray(byteArray, size);
// convert bytearray to vector
vector<unsigned char> v(byteArray, byteArray + sizeof byteArray / sizeof byteArray[0]);
// pass as a vector
BytesMessage *message = session->createBytesMessage();
message->writeBytes(v);
producer->send(message);
printf("Sent message #%d from thread %s\n", ix + 1, threadIdStr.c_str());
pointmsg is just an object that I create and fill in, and it is not null, I tested it already and it has data in it. I am converting pointmsg to byte array since this is the way to pass an object as far as I read in the documantation. And since the writeBytes() function expects a vector, I am converting the byte array into a vector. I suspect there might be some problem in this part.
On JMS side I am simply listening the upcoming messages with:
public void onMessage(final javax.jms.Message message) {
final Timer traceTimer = new Timer();
final long messageReceived = System.currentTimeMillis();
try {
if (message instanceof ActiveMQBytesMessage) {
final ActiveMQBytesMessage amqBytesMsg = (ActiveMQBytesMessage) message;
final byte[] buffer = amqBytesMsg.getContent().data;
final String msgType = amqBytesMsg.getStringProperty(LLCom.MSG_PROP_CLASS_NAME);
final String topic = amqBytesMsg.getStringProperty(LLCom.MSG_PROP_TOPIC_NAME);
String msgLookUpType;
if (topic == null || topic.isEmpty()) {
// get message class name: foo.bar$MessageMsg
msgLookUpType = msgType.split("\\$")[1];
} else {
// it's a topic we assume, that all subscribers have the
// correct type
msgLookUpType = topic;
}
if (logger.isDebugEnabled())
logger.debug("Router(" + name + ") received message ( " + buffer.length + "bytes)of type or topic " + msgLookUpType);
final Message req = parsers.createMessage(buffer, msgType);
// process explicit topic/queue subscriber
processServiceMessage(msgLookUpType, messageReceived, amqBytesMsg, req, traceTimer);
} else {
logger.error("Not supported JMS MessageType: " + message);
}
} catch (final Exception e) {
logger.error("Unexpected error: " + e.getMessage());
// e.printStackTrace();
}
}
When I debug it I can see that msgType and topic variables (on JMS side) are coming as null, which means that activemq message is somehow not decrypted. What could be the reason for that? I can see the message is being sent, received but not understood.
Any thoughts?
Update: I noticed that I am expecting to get stringProperties on JMS side, which I am not setting on C++ side, but I am not sure whether it causes the problem or not.
Ok, It seems the error was related to set properties, msgType and topic, I gave them the expected strings with using setStringProperty() on C++ side with the required methods, and now that initial error is gone.
message->setStringProperty();
Related
Greetings of the day.
Please help on the below requirement:
Requirement:
We want to delete message from MQ only after it is processed successfully.
Use event based message detection technique and avoid loop
So, to achieve above:
I have created message listener and consumer class below:
{
sessionIn = connectionIn.CreateSession(false, AcknowledgeMode.ClientAcknowledge);
// Create message listener and assign it to consumer
messageListener = new MessageListener(OnMessageCallback);
consumerAsync.MessageListener = messageListener;
Console.WriteLine("Message Listener set. Starting the connection now.");
// Start the connection to receive messages.
connectionWMQ.Start();
}
Reading the message from the call back event and push the message into other system:
OnMessageCallback(Message) {
if (xmsMessage is IBytesMessage)
{
IBytesMessage bytesMessage = (IBytesMessage)xmsMessage;
byte[] arrayMessage = new byte[bytesMessage.BodyLength];
bytesMessage.ReadBytes(arrayMessage);
string message = System.Text.Encoding.Default.GetString(arrayMessage);
}
}
Once the message processed, external system will fire the below over ride method:
Response method override:
protected override Task OnResponse(ReponseMessage message)
{
//Read the message and get the message id and correlation id.
//Delete the message from the queue.
//I am trying to do like this, but Its not working:
messageConsumerDelete = sessionDelete.CreateConsumer(destinationDelete, query);
if (messageConsumerDelete != null)
{
IMessage m = messageConsumerDelete.Receive(1000);
LogWrite("Receive Message=" + m);
m.Acknowledge();
}
}
Please suggest a best solution for this requirement.
I am trying to find a solution for this since weeks, but no breakthrough.
Thanks,
Balaji
I send a simple text message to an MQ Queue (MQ 7.0.1):
"abc"
Using spring JMS the total length of the message is: 291
But putting the same message in the queue using IBM MQ libraries the total length of the message is: 3
How can I get total data length 3 with JMS?
Spring JMS code:
#EnableJms
public class JMSTestController {
...
#Autowired
private JmsTemplate jmsTemplate;
#Autowired
JmsMessagingTemplate jmsMessagingTemplate;
...
public String send() throws JMSException{
jmsTemplate.setReceiveTimeout(10000);
jmsMessagingTemplate.setJmsTemplate(jmsTemplate);
Session session = jmsMessagingTemplate.getConnectionFactory().createConnection()
.createSession(false, Session.AUTO_ACKNOWLEDGE);
Queue entryQueue = session.createQueue("hereQueueName");
Queue replyQueue = session.createQueue("hereReplyQueueName");
TextMessage message = session.createTextMessage("abc");
message.setJMSDeliveryMode(DeliveryMode.NON_PERSISTENT);
message.setJMSDestination(entryQueue);
message.setIntProperty(WMQConstants.JMS_IBM_CHARACTER_SET, 819);
message.setIntProperty(WMQConstants.JMS_IBM_ENCODING, 273);
jmsMessagingTemplate.convertAndSend(entryQueue, message);
String messageId = message.getJMSMessageID();
...
}
Native code:
MQQueueManager qm = createQueueManager(queueManager, host, port,
channel, username, password, connectionType);
MQQueue m_receiver = null;
MQMessage msg = new MQMessage();
msg.format = MQC.MQFMT_STRING;
msg.expiry = timeout / 1000;
msg.replyToQueueName = qReceiver;
msg.replyToQueueManagerName = queueManager;
msg.write("abc".getBytes());
MQPutMessageOptions pmo = new MQPutMessageOptions();
try {
qm.put(qSender, msg, pmo);
} catch (MQException e) {
MQTalkerException ex = new MQTalkerException(
"An error happened sending a message", e);
logger.error(ex);
throw ex;
}
Solution
Following JoshMc's comment I made the following modification and reached the expected result:
Check out these answers, you want to set targetClient to MQ to remove
those properties. There are many ways to accomplish this, changing
your CreateQueue to use a URI is probably the easiest.
JMS transport v/s MQ transport
That is, modify the creation of the queue using the URI instead of just its name.
Queue entryQueue = session.createQueue("queue:///QUEUE_NAME?targetClient=1");
I reached the solution by following JoshMc's comment. That is, modify the creation of the queue using the URI instead of just its name.
Queue entryQueue = session.createQueue("queue:///QUEUE_NAME?targetClient=1");
This removes the MQRFH2 header (the extra bytes I didn't know where they came from)
and with that the message has a total length of 3 bytes.
Spring is counting the bytes of the message body (aka data)
IBM MQ native is counting the bytes of the message headers plus body
In your screenshot, the field directly above shows '3' bytes.
Longitud dataos = length of body = 3
Longitud total = length of headers + body = 291
In the onMessage event, if the message does not get converted as a Spring Integration message it will try and use the MessageBuilder to rebuild it. But it doesn't include the headers. This seems incorrect. We're using Apache Artemis 1.2 and Spring 4.2.5.
Here is the code in question from SubscribableJmsChannel:
public void onMessage(javax.jms.Message message) {
Message<?> messageToSend = null;
try {
Object converted = this.jmsTemplate.getMessageConverter().fromMessage(message);
if (converted != null) {
messageToSend = (converted instanceof Message<?>) ? (Message<?>) converted
: this.messageBuilderFactory.withPayload(converted).build();
this.dispatcher.dispatch(messageToSend);
}
else if (this.logger.isWarnEnabled()) {
logger.warn("MessageConverter returned null, no Message to dispatch");
}
}
Sometimes we see messages get converted as Spring messages and sometimes they are attempted to be converted from the Spring message payload (1 in 20 times or so). In that case, the below code seems to try and handle recreating the Spring message envelope - but it will drop the headers.
What's interesting is that our code is always sending the exact same message in a load test. I haven't figured out why the messages would be received differently yet.
I have some Java code that reads messages from an ActiveMQ queue. The code uses a JmsTemplate from Spring and I use the "browseSelected" method to retrieve any messages from the queue that have a timestamp in their header older than 7 days (by creating the appropriate criteria as part of the messageSelector parameter).
myJmsTemplate.browseSelected(myQueue, myCriteria, new BrowserCallback<Integer>() {
#Override
public Integer doInJms(Session s, QueueBrowser qb) throws JMSException {
#SuppressWarnings("unchecked")
final Enumeration<Message> e = qb.getEnumeration();
int count = 0;
while (e.hasMoreElements()) {
final Message m = e.nextElement();
final TextMessage tm = (TextMessage) MyClass.this.jmsQueueTemplate.receiveSelected(
MyClass.this.myQueue, "JMSMessageID = '" + m.getJMSMessageID() + "'");
myMessages.add(tm);
count++;
}
return count;
}
});
The BrowserCallback's "doInJms" method adds the messages which match the criteria to a list ("myMessages") which subsequently get processed further.
The issue is that I'm finding the code will only process 400 messages each time it runs, even though there are several thousand messages which match the criteria specified.
When I previously used another queueing technology with this code (IBM MQ), it would process all records which met the criteria.
I'm wondering whether I'm experiencing an issue with ActiveMQ's prefetch limit: http://activemq.apache.org/what-is-the-prefetch-limit-for.html
Versions: ActiveMQ 5.10.1 and Spring 3.2.2.
Thanks in advance for any assistance.
The broker will only return up to 400 message by default as configured by the maxBrowsePageSize option in the destination policies. You can increase that value but must use caution as the messages are paged into memory and as such can lead you into an OOM situation.
You must always remember that a message broker is not a database, using it as one will generally end in tears.
I have 2 Websphere application Server(WAS) applications, one sending a message and the other reading and processing it . I need the queue name to be known in the reading application for my downstream processing.
I am trying to get the queue name (in the reading application) by using the following code . But however I get NullPointerException since the getJMSDestination is returning null.
Queue queue = (Queue)message.getJMSDestination();
logger.info("Queue ID: "+queue.getQueueName());
Note that the queue name is set via the destination object in the sending application.
Is there any other parameters that I am missing to set in the sending application ?
The message should have the destination stored in its JMSDestination property, you can try fetch that instead of using getJMSDestination()
I've using Spring with ActiveMQ, and this appears to work for me:
public void processMessage( Message msg )
{
// Get the queue name from the supplied Message.
String sourceQueueName = "UNKNOWN";
try
{
ActiveMQDestination sourceQueue = (ActiveMQDestination) msg.getJMSDestination();
sourceQueueName = sourceQueue.getPhysicalName();
}
catch ( JMSException e )
{
LOGGER.error( "Cannot get JMSDestination from Message: " + msg, e );
}
....
Does WAS have a Queue object you can cast to that exposes similar methods?