This is a quick walkthrough of a LoadRunner script I created to load test a “web” service that communicates with SOAP messages sent over JMS.
Note that this script supports the queuing/point-to-point model of JMS (rather than the publish and subscribe model), works with JMS version 1.1 rather than 1.02b (as it uses the BytesMessage.getBodyLength() method among others), and has been tested with Java 1.5 using the Tibco implementation of JMS.
The LoadRunner script can be downloaded here, the source code is available here, and a mirror of the JMS 1.1 specification can be downloaded here. People using Tibco’s JMS implementation can download their Javadocs here (10MB). Feedback is appreciated.
From looking at the script, you may wonder why I chose to write my own script using the Java vuser type instead of using the Web Services vuser (version 8.1, Feature Pack 4 supports web services using JMS as well as HTTP), but that is a story for some other time.
Anyway…for the tiny number of people that I haven’t lost already, here’s how it all works…
At a high level, we are trying to put a SOAP message on a queue (MyLoadTest.Warehouse.Request), receive the corresponding response from a different queue (MyLoadTest.Warehouse.Response), and check that the response message matches our expected message. Download my source code and follow along.
On with the detail…
The javax.jms.* classes provide interfaces only, other vendors have provided implementations of these interfaces (I am using Tibco’s implementation – found in com.tibco.tibjms.*). This should mean that any JMS code you write will be portable between implementations; whether this is true or not is something I haven’t tested.
Initially we perform a JNDI lookup to find a ConnectionFactory and get the details for the queues we will be using.
We then create a Connection and a Session, and then create the MessageProducer (using the request queue details) that we will be using to send our message to the request queue.
We create either BytesMessage or a TextMessage (MapMessage, ObjectMessage and StreamMessage have not been implemented in the script), and set the JMS header properties. The important JMS headers are the JMSDestination (the request queue), and the JMSReplyTo (the response queue). We can also set Message properties, which are simple name/value pairs. We then set the Message body, using a string constant containing the SOAP XML message.
We only want to measure system response times, so we only put our LoadRunner transaction timing points around the code that sends and receives the message.
Sending the message is as simple as calling myMessageProducer.send(myMessage).
In the following extract from the Tibco EMS logs, you can see the JMS headers, the Message properties and the Message body (shown here with a size only). The JMS headers show the request and response queues along with a unique msgID.
2007-02-26 13:20:07 [MSG:73422]
received from user='anonymous':
connID=1669
prodID=16906
msgID='ID:SVP-EMS-SERVER.391E45DA4ADA6C8:3083'
Time=1172456256000
mode=PERSISTENT
queue='MyLoadTest.Warehouse.Request'
msg=BytesMessage=
{
Header=
{
JMSDestination={QUEUE:'MyLoadTest.Warehouse.Request'}
JMSReplyTo={QUEUE:'MyLoadTest.Warehouse.Response'}
JMSDeliveryMode={PERSISTENT}
JMSPriority={4}
JMSMessageID={ID:SVP-EMS-SERVER.391E45DA4ADA6C8:3083}
JMSType={BytesMessage} JMSTimestamp={1172456256000}
}
Properties=
{
"SOAPJMS_requestIRI"={string:''}
"SOAPJMS_targetService"={string:''}
"SOAPJMS_soapAction"={string:''}
"SOAPJMS_isFault"={string:'false'}
"SOAPJMS_contentType"={string:''}
"SOAPJMS_soapMEP"={string:'http://www.w3.org/2003/05/soap/mep/request-response/'}
"SOAPJMS_bindingVersion"={string:''}
}
Body=
{
byte[]:1126 bytes
}
}
To receive a Message, we must create a MessageConsumer (using the response queue). Rather than simply receiving the messages from the queue in the order they arrived (FIFO – which would mean that we could be receiving the wrong response message when we are running load against the system), we can use a message selector to make sure that we receive the message that we want from the queue.
In the log above, we saw that each JMS message has a unique JMSMessageID. When a response is sent by the server, the response message has a JMSCorrelationID that matches the JMSMessageID of the message it is replying to. A message selector is defined in the same way as the WHERE clause of an SQL statement, so our selector would be
"JMSCorrelationID = '" + myBytesMessage.getMessageID() + "'".
Note that the unique message ID is only set when the message is sent so, if you are using a selector, you must create your MessageConsumer *after* you have called myMessageProducer.send(myMessage). A selector can use any of the JMS headers and Message properties, but cannot use the Message body.
Using the MessageConsumer, receiving the correct message from the queue is done by simply calling myMessageConsumer.receive(timeout). The script will wait to receive the message until it reaches the timeout value. If no message is received before the timeout, the response message will be null. Remember to close the MessageConsumer, or you will leak and end your test with thousands of open receivers.
Here is the Tibco EMS log entry for the response message. You can see that the message has a different JMSMessageID, and has a JMSCorrelationID with the message ID of the request it is replying to.
2007-02-26 13:20:09 [MSG:73425]
received from user='anonymous':
connID=1714
prodID=19956
msgID='ID:SVP-EMS-SERVER.391E45DA4ADA6F5:7835'
Time=1172456409014
mode=PERSISTENT
queue='MyLoadTest.Warehouse.Response'
msg=BytesMessage=
{
Header=
{
JMSDestination={QUEUE:'MyLoadTest.Warehouse.Response'}
JMSDeliveryMode={PERSISTENT}
JMSPriority={4}
JMSMessageID={ID:SVP-EMS-SERVER.391E45DA4ADA6F5:7835}
JMSCorrelationID={ID:SVP-EMS-SERVER.391E45DA4ADA6C8:3083}
JMSTimestamp={1172456409014}
}
Properties=
{
"SOAPJMS_requestIRI"={string:''}
"SOAPJMS_contentType"={string:'text/xml'}
"SOAPJMS_soapMEP"={string:'http://www.w3.org/2003/05/soap/mep/request-response/'}
"SOAPJMS_bindingVersion"={string:''}
}
Body=
{
byte[]:681 bytes
}
}
Extracting contents of the Message body from the JMS Message is done by calling myMessage.getText() if it is a TextMessage (if it is a BytesMessage, it is a bit trickier – see the attached script).
And finally comparing the received SOAP message to the expected message is just done using String.equals().
Remember to close the Session and Connection at the end of the script.
The exercise of modifying the script to send many different messages to different queues (which would mean you only have to maintain one script instead of one script per SOAP operation) is left as an exercise for the reader…
22 Comments
Comments are closed.
>>wonder why I chose to write my own script using the Java vuser type >>instead of using the Web Services vuser
So will we get a follow up post :)?
Not sure when/if I will write a follow-up. Basically, I think that the LoadRunner 8.1.4 implementation of Web Services with JMS is very immature.
It lacks really obvious features (like being able to change the message type to something other than TextMessage), and does not allow you to change any of the JMS Messages properties. It seems to force you to use the WSDL wizard approach of generating scripts instead of just letting you dump blobs of XML into a send_this_over_jms() function.
When it doesn’t work, error reporting is poor (nothing much is logged in LoadRunner), so it is very hard to get it working.
A popular open source load testing tool seems to have a JMS testing option that is far more configurable than LoadRunner’s but, even so, LoadRunner’s monitoring, analysis and Diagnostics make it the better tool as long as you are prepared to write a little Java code (or modify my template script).
Cheers,
Stuart.
Hi Stuart,
What’s the OS tool please ?
jc
JMeter supports JMS messaging, but LoadRunner is a much better tool to use.
Where i found jms.jar a tibjms.jar?
You can find the TIBCO tibjms.jar file in the lib folder of the EMS installation (e.g. C:\tibco\ems\6.0\lib)
The jms.jar file can be downloaded from Sun/Oracle. You should search for a link that says something like “Download the Java Message Service version 1.0.2b API Documentation, Jar, and Source”
What is the name of the other open source tool which seems to support JMS ? (OpenSta?)
b2w, nice blog..!
I tried using your script over a sample application using IBM MQ. We have a .bindings file and I gave the location of the file as provider URL. Getting error as below –
javax.naming.NoInitialContextException: Cannot instantiate class: EntPerfTestQCF [Root exception is java.lang.ClassNotFoundException: EntPerfTestQCF]
No compilation errors. I have added all necessary jar files in the classpath.
done step by step debugging also. Not able to resolve it. Please provide me any suggestions in fixing this error. This is failing at the following line of the code
jndiContext = new InitialContext(env);
Hi!
Could you pls give a link to tibco.weblogic ? I can’t find where to download from..
Hi Stuart,
I know you wrote this blog a long time ago, but I was hoping you are still involved in Tibco JMS today.
If so, perhaps you could help me with a problem. I am using the tibco classes as part of a JMS adapter implementation using SAP XI 3.0.
I am struggling to find a way to increase the polling interval on the queues. Do you have any idea if there is a proporty I can modify?
Many thanks,
Brian
Hi Stuart,
I hope you don’t mind me referring your blog on my website(http://harinderseera.blogspot.com).
Cheers,
Harinder
we are having issues while posting jms requests
javax.naming.ServiceUnavailableException: Failed to query JNDI: Failed to connect to the server at tcp://ncdap-lod2605.core.afcc.com:16343
we have configured ssl for the JNDI provider url but it shows as tcp in the loadrunner error log.
Any suggessions will be appreciated.
we are having issues while posting jms requests
javax.naming.ServiceUnavailableException: Failed to query JNDI: Failed to connect to the server at tcp://:16343
we have configured ssl for the JNDI provider url but it shows as tcp in the loadrunner error log.
Any suggessions will be appreciated.
Hi,
Nice one.
I have few queries :
a) wht should be the name of the queues. Does it include the path of the location where the queues are placed or just the queue name?
b) Wht are the necessary jar files which needs to be added?
Request you kindly help get this answer.
Thanks
deepa.
Why did you choose to use the Java vuser type instead of using the Web Services vuser ?
Nevermind, I got it.
Hi Stuart,
This information is excellent ! Thanks a million. By referring your script I could successfully able to send text message on TIBCO queues and able to get response too. But my challenge is to send XML input message on TIBCO queues. I did try to do the same way you did in your script sending soap message but unfortunately it didn’t work for me. Vugen shows an wearied message “Notify: Found jdk version: 1.6.0., Notify: classpath=C:\…Notify: Path=C:\..,.Notify: VM Param”
I dont understand why this message appears and Vugen is not executing the script? Please help.
Regards,
San
Sam can you pass me the script please? so that i can give a try on that?Appreciate it
performance test a TIBOC based Message queue sitting on an EMS server using load runner performance test tool set : Am trying to performance test a TIBOC based Message queue sitting on an EMS server using load runner performance test tool set
i was able to create client test this successfully from the same subnet machine (the subnet The server which host the EMS / Queues are sitting in an another environment ) , how ever since our LG/Controllers are in different net work we had to get it working on a machine in that network , we had set up the script in that net work and fire wall is opened and telnet and ping does work well
If i use tcp://hostname:4222 it throws below error
Failed to set property name JMS_MESSAGE_TYPE value TextMessage due to the following exception : javax.naming.ServiceUnavailableException: Failed to query JNDI: Failed to connect to the server at tcp://hostname:4222 [Root exception is javax.jms.JMSException:
Failed to connect to the server at tcp://hostname:4222]
If i use a FQDN tcp://hostname.FQDN :4222 it passes through the above step
but i get
following exception : javax.jms.JMSException: Failed to connect to the server at tcp://hostname.FQDN :4222
javax.jms.JMSException: Failed to connect to the server at tcp://hostname.FQDN :4222
I also observed that regardless of what ever whether it is FQDN or hostname i set up in run time setings the JMS is taking the tcp://hostname:4222 and i assume it is due to the fact that it would have hard coded in connection factory settings
My question is is there a way to by pass that above said connection factory settings to force the JMS to use the FQDN ?
Thank You
Jyothis
Hi Stuart,
How about SOAPUI along HermesJMS to store JMS connection.
Hi Stuart
I am new to web spehere,I jst install mq explorer,How to put message using loadrunner scripts.Please help
Hi Anees,
Once you configure you can build the SOAP request by creating JMS end point.
Websphere MQ users:
You need the following JAR-files in SoapUI’s hermes/lib folder:
•com.ibm.mq.pcf-6.1.jar
•com.ibm.mq.jar
•com.ibm.mqjms.jar
•dhbcore.jar
•connector.jar
Once you creates an SOAP request you can simulate either from LoadRunner or any testing tool.
Thanks,
-Yacoob