I have been trying to connect to IBM MQ from JMeter JMS publisher. Unable to find corrert "Initial context factory" and "connection factory" values to use without JNDI properties. I have all MQ jars present in LIB folder.
I have the following information-host name - Venus, Port - 21717, Destination Queue name - request.queue,Queue manager - venus.QMGR,channel - venus.server.chl
(no authorization required).
My requirement - To connect to IBM MQ using JMS publisher with above details. But I am not able to sort out on what to give for Provider URL, Initial context factory and connection factory. Can you please help as this has been bugging me for past two weeks and couldn't find a solution yet?
It would be great if you can tell me on where to populate the above values in JMS publisher as well for connecting to IBM MQ.
I have tried with user.classpath=/folder/with/mq/jars as well but it is not working and all jars are in place with JMeter restart still no luck.
Note: I have gone through all sites in these two weeks but couldn't get any luck.
Example configuration steps would be something like:
Add javax.jms-api.-x.x.x jar to JMeter Classpath
Add mq-allclient-x.x.x.x.jar to JMeter Classpath
Add JSR223 Sampler to your Test Plan
Put the following code into "Script" area:
import com.ibm.jms.JMSTextMessage;
import com.ibm.mq.jms.*;
import com.ibm.msg.client.wmq.WMQConstants;
import javax.jms.JMSException;
import javax.jms.Session;
MQQueueConnectionFactory cf = new MQQueueConnectionFactory();
cf.setHostName("your_IBMMQ_host");
cf.setPort(1414); // or other port
cf.setIntProperty(WMQConstants.WMQ_CONNECTION_MODE, WMQConstants.WMQ_CM_CLIENT);
cf.setQueueManager("your_IBMMQ_queue_manager");
cf.setChannel("your_IBMMQ_channel");
cf.setStringProperty(WMQConstants.USERID, "your_IBMMQ_username");
cf.setStringProperty(WMQConstants.PASSWORD, "your_IBMMQ_password");
connection = (MQQueueConnection) cf.createQueueConnection();
MQQueueSession session = (MQQueueSession) connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
MQQueue queue = (MQQueue) session.createQueue("queue:///your_IBMMQ_queue");
MQQueueSender sender = (MQQueueSender) session.createSender(queue);
JMSTextMessage message = (JMSTextMessage) session.createTextMessage("your_message_body");
connection.start();
sender.send(message);
More information:
What is installed for IBM MQ classes for Java
Apache Groovy - Why and How You Should Use It
Simplest sample applications using WebSphere MQ JMS
Depending on your exact requirements, you may be interested by JMSToolBox and its possibility to define scriptsthat will read the payload from csv files stored in a directiory, then create and post them in a MQ Q
as a JMS Message from a message template
Related
Need to create an automate script with jmeter for sending xml to queue.
We are using IBM websphere mq to put xml to queues.
We have received hostname,port,queue manager and queue name info from application team along with the xml contents.
Found blogs on Google that we can get it done using jms point to point sampler in jmeter.
Since it's a new concept for me so not sure what are the mandatory details that we have to enter in that sampler.
Inputs for jms resources- who will provide that.
2.JNDI properties-initial context factory value.
Any pre-requisite jar or something that we have to install.
Any help on this will be appreciated.
Thanks
I have added jms p2p sampler and provided connection details under jndi properties .
Not sure what value to be put for initial context factory ,jndi name request queue ,receive queue and under queue connection factory parameters.
Is this the right way of handling IBM websphere mqs using jmeter.Using jmeter 5.1 version for testing.
Any pre-requisite jar or something that we have to install - yes, as per documentation
JMeter includes the JMS API jar, but does not include a JMS client implementation. If you want to run JMS tests, you will need to download the appropriate jars from the JMS provider.
for IBMMQ it would be com.ibm.mq.allclient.jar which needs to be put into JMeter Classpath
With regards to sending the message I believe you should consider using JSR223 Sampler and writing the code to connect to the queue manager and send the message in Groovy, something like:
import com.ibm.msg.client.jms.JmsFactoryFactory
import com.ibm.msg.client.wmq.WMQConstants
import javax.jms.Session
def hostName = "your IBMMQ Host"
def hostPort = 1414
def channelName = "DEV.APP.SVRCONN"
def queueManagerName = "QM1"
def queueName = "DEV.QUEUE.1"
def ff = JmsFactoryFactory.getInstance(WMQConstants.WMQ_PROVIDER)
def cf = ff.createConnectionFactory()
cf.setStringProperty(WMQConstants.WMQ_HOST_NAME, hostName)
cf.setIntProperty(WMQConstants.WMQ_PORT, hostPort)
cf.setStringProperty(WMQConstants.WMQ_CHANNEL, channelName)
cf.setIntProperty(WMQConstants.WMQ_CONNECTION_MODE, WMQConstants.WMQ_CM_CLIENT)
cf.setStringProperty(WMQConstants.WMQ_QUEUE_MANAGER, queueManagerName)
def conn = cf.createConnection("app", "test")
def sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE)
def destination = sess.createQueue(queueName)
conn.start()
def producer = sess.createProducer(destination)
def rnd = new Random(System.currentTimeMillis())
def payload = String.format("JMeter...IBM MQ...test message no. %09d!", rnd.nextInt(Integer.MAX_VALUE))
def msg = sess.createTextMessage(payload)
producer.send(msg)
producer.close()
More information: IBM MQ testing with JMeter - Learn How
I have a local IBM MQ instance set up with some default queues.
Using JMeter 5.4.1, I first want to send messages to one of these queues to test that the connection works.
1. Is it possible with IBM MQ or does it only work with Active MQ?
The test scenario I wish to achieve is this:
JMeter sends some message with ID1 to a queue named "DEV.QUEUE.1"
The System under test takes the message from "DEV.QUEUE.1", does some processing and places the response on "DEV.QUEUE.2"
JMeter checks and asserts that a response has been received for the message with ID1 on the queue named "DEV.QUEUE.2" within X seconds
View results in Aggregate Report
2. Is this achievable with JMS Point-to-Point Sampler?
I've checked the official JMeter documentation, but I don't understand what connection details I need to place in each of the sampler's configuration fields.
JMS Resources
QueueConnection Factory:
JNDI name Request queue:
JNDI name Receive queue:
Number of samples to aggregate
JMS Selector
....
etc...
I was able to connect to one queue and send messages using custom code in a JSR223 Sampler.
These are the connection details I have used to create the connection:
def hostName = "127.0.0.1"
def hostPort = 1414
def channelName = "DEV.APP.SVRCONN"
def queueManagerName = "QM1"
def queueName = "DEV.QUEUE.1"
def ff = JmsFactoryFactory.getInstance(WMQConstants.WMQ_PROVIDER)
def cf = ff.createConnectionFactory()
cf.setStringProperty(WMQConstants.WMQ_HOST_NAME, hostName)
cf.setIntProperty(WMQConstants.WMQ_PORT, hostPort)
cf.setStringProperty(WMQConstants.WMQ_CHANNEL, channelName)
cf.setIntProperty(WMQConstants.WMQ_CONNECTION_MODE, WMQConstants.WMQ_CM_CLIENT)
cf.setStringProperty(WMQConstants.WMQ_QUEUE_MANAGER, queueManagerName)
def connInboundQueue = cf.createConnection("mquser", "secretpassword")
def sessInboundQueue = connInboundQueue.createSession(false, Session.AUTO_ACKNOWLEDGE)
def destinationInboundQueue = sessInboundQueue.createQueue(queueName)
connInboundQueue.start()
I'm guessing that I can map these connection credentials to this JMS Point-to-Point Sampler so that I can achieve the same connection, I just don't know how.
So to sum up:
1a. How can I achieve connection to my two queues using the JMS Point-to-Point Sampler?
2a. How can the JMS Point-to-Point Sampler be configured for the scenario I have described above?
I would appreciate any help.
I don't think it's currently possible, the options are in:
Use mqmeter - MQ JMeter Extension, it will provide a custom Java Request sampler
Continue with JSR223 Test Elements, example producer and consumer code snippets can be found in IBM MQ testing with JMeter - Learn How article
We have programmatically created subscriber to IBM MQ AMQP TOPIC with createDurableSubscriber by providing clientId and subscriber name.
We start the program so it subscribes to TOPIC and stop the program. Then send the msgs to topic and again start the receiver program again but we cannot receive the msgs sent and loose the messages which should not happen in case of durable subscription..
We can see amqp topic and its durable subscription when subscriber is connected using mqsc commands DISPLAY TOPIC, DISPLAY TPSTATUS, DISPLAY TPSTATUS SUB, DISPLAY SUB SUBID but not when subscriber program is stopped. We have defined attribute DEFPSIST(YES) and client(producer to topic) is sending persistent messages.
Where are the messages gone as we cannot see messages in durable queues of subscriber? Does it depends on expiry attribute?
Output of DISPLAY SUB SUBID for our subscriber when it is connected.
AMQ8096: WebSphere MQ subscription inquired.
SUBID("hex sub id")
SUB(:private:CLINET01:TOPIC01) TOPICSTR(TOPIC01)
TOPICOBJ(SYSTEM.BASE.TOPIC) DISTYPE(RESOLVED)
DEST(SYSTEM.MANAGED.DURABLE.5F6B5C2524FB9AED)
DESTQMGR(qm.name) PUBAPPID( )
SELECTOR( ) SELTYPE(NONE)
USERDATA(010)
PUBACCT(***************************************************)
DESTCORL(***************************************************)
DESTCLAS(MANAGED) DURABLE(YES)
EXPIRY(0) PSPROP(MSGPROP)
PUBPRTY(ASPUB) REQONLY(NO)
SUBSCOPE(ALL) SUBLEVEL(1)
SUBTYPE(API) VARUSER(FIXED)
WSCHEMA(TOPIC) SUBUSER(mqm)
CRDATE(2020-09-28) CRTIME(04:14:09)
ALTDATE(2020-09-28) ALTTIME(04:14:09)
Subscriber id has private(not sure why) and client id but not subscriber name which is sub4
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Topic;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
import java.lang.String;
import javax.jms.Destination;
import javax.naming.Context;
import org.apache.qpid.jms.JmsConnectionFactory;
import javax.jms.DeliveryMode;
import javax.naming.InitialContext;
import javax.jms.Message;
public class AMQPQueueExample1 implements Runnable {
private static final int DELIVERY_MODE = DeliveryMode.PERSISTENT;
public void run(){
try{
Connection connection = null;
Context context = new InitialContext();
ConnectionFactory connectionFactory = (ConnectionFactory) context.lookup("myFactoryLookup");
connection = connectionFactory.createConnection();
connection.setClientID("123");//("WHATS_MY_PURPOSE3"); // Why do we need clientID while publishing the TOPIC from consumer / publisher
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Topic priceTopic = (Topic) context.lookup("myTopicLookup1");
MessageConsumer subscriber1 = session.createDurableSubscriber(priceTopic,"sub420"); //"sub3");
System.out.println("TOPIC "+priceTopic);
connection.start();
while(true){
TextMessage message1 = (TextMessage) subscriber1.receive(1000);
if(message1!=null)
System.out.println("Subscriber 1 received : " + message1.getText());
}
}catch(Exception e){
e.printStackTrace();
}
}
public static void main(String[] args) {
AMQPQueueExample1 amp=new AMQPQueueExample1();
Thread thread = new Thread(amp);
thread.start();
}
}
Values are taken from jndi.properties file for context factory and provider url.
It looks from the comments like you're using MQ 8.0.0.5? If that's the case then Apache Qpid JMS clients aren't supported with that version of MQ. I believe with that version a very basic non-durable subscribe might work, but any other JMS methods are unlikely to.
I suspect what is happening is the AMQP 1.0 flows from Qpid JMS aren't fully understood by that version of MQ, so the expiry of the subscription is being set to 0 rather than unlimited.
MQ 9.2 added support for more of the JMS 2.0 spec - although not every JMS feature. There is more information about the methods which are supported here:
https://www.ibm.com/support/knowledgecenter/SSFKSJ_9.2.0/com.ibm.mq.dev.doc/q125050_.htm
Creating durable subscribers and/or consumers should work as you expect.
An article from Matthew Whitehead "MQ Light messaging from Microsoft®.NET™ (Part 4)" states the following:
AMQP channels don’t support setting an unlimited expiry time for MQ Light subscriptions. While it is possible to create subscriptions that have a very long time-to-live, it isn’t possible to create subscriptions to exist forever.
If you wish to create subscriptions that never expire you can do so by creating an MQ administered subscription and having MQ Light clients join and leave the subscription. This can also help to ensure that any messages published to a topic before the first subscribers have connected, aren’t lost completely. Read my previous article on joining MQ Light clients to administered subscriptions.
Related AMQP Fields
To provide the expiry capabilities described above MQ Light uses 2 features of AMQP 1.0:
Source Timeout
Source Expiry Policy
The source timeout is used to specify the time in seconds that the subscription will expire.
The source expiry policy is used to determine what causes the expiry timer to begin. MQ AMQP channels only support an expiry policy of link-detach, which means the timer starts as soon as the last link detaches from the subscription.
I searched and couldn't find a reference on how to set Source Timeout or Source Expiry Policy in Apache QPID, but the linked blog references setting expiry via a administratively defined subscription. Based on the info in your question I think you can just define something like this ahead of time. I have not specified EXPIRY because this will pick up EXPIRY(UNLIMITED) from SYSTEM.DEFAULT.SUB:
DEFINE SUB(':private:CLINET01:TOPIC01') TOPICOBJ(SYSTEM.BASE.TOPIC) TOPICSTR('TOPIC01') DESTCLAS(MANAGED)
When you then connect your AMQP subscriber it will resume this existing subscription with the expiry set to UNLIMITED.
I'm unable to process large messages from IBM MQ and get the below error:
JMSCMQ0001: WebSphere MQ call failed with compcode '1' ('MQCC_WARNING') reason '2080' ('MQRC_TRUNCATED_MSG_FAILED')
I'm using the DefaultListenerContainer and not consuming via a MessageConsumer using IBM MQ Java API classes directly. I believe by using IBM MQ JMS API you can specific options before retrieving the message from the queue. But how do I do that with DefaultListenerContainer, is there a system property I can set for these?
If using IBM MQ JMS API(I'm not consuming message like this, pasted just for reference):
MQGetMessageOptions mqGetMessageOptions = new MQGetMessageOptions();
mqGetMessageOptions.waitInterval = ipreoProperties.getMqReceiveWaitTime();
mqGetMessageOptions.options = MQC.MQGMO_WAIT | MQC.MQPMO_SYNCPOINT | MQC.MQGMO_ACCEPT_TRUNCATED_MSG;
Below is my Java Config for the IBM MQ Connection:
#Bean
public CachingConnectionFactory ipreoMQCachingConnectionFactory() {
CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory();
//Not defining MQQueueConnectionFactory as separate bean as Spring boot's auto-configuration finds two instances
//of ConnectionFactory and throws ambiguous implementation exception
//One implementation is CachingConnectionFactory and other one would be MQQueueConnectionFactory if defined separately
MQQueueConnectionFactory mqConnectionFactory = new MQQueueConnectionFactory();
try {
mqConnectionFactory.setHostName(env.getRequiredProperty(AppEnvPropertyConstants.JmsConstants.IPREO_MQ_HOSTNAME));
mqConnectionFactory.setQueueManager(env.getRequiredProperty(AppEnvPropertyConstants.JmsConstants.IPREO_MQ_QUEUE_MGR));
mqConnectionFactory.setPort(env.getRequiredProperty(AppEnvPropertyConstants.JmsConstants.IPREO_MQ_PORT, Integer.class));
mqConnectionFactory.setChannel(env.getRequiredProperty(AppEnvPropertyConstants.JmsConstants.IPREO_MQ_CHANNEL));
//mqConnectionFactory.setTransportType(WMQConstants.WMQ_CM_CLIENT);
//Setting connection mode as Client so it doesn't complain for native IBM MQ libraries
mqConnectionFactory.setIntProperty(CommonConstants.WMQ_CONNECTION_MODE, CommonConstants.WMQ_CM_CLIENT);
} catch (JMSException exception) {
exception.printStackTrace();
}
cachingConnectionFactory.setTargetConnectionFactory(mqConnectionFactory);
//Setting session caching size as 10, don't think we need more
cachingConnectionFactory.setSessionCacheSize(10);
cachingConnectionFactory.setReconnectOnException(true);
return cachingConnectionFactory;
}
public DefaultMessageListenerContainer ipreoDealActivityListenerContainer() {
DefaultMessageListenerContainer factory = new DefaultMessageListenerContainer();
factory.setConnectionFactory(ipreoMQCachingConnectionFactory());
factory.setDestinationName(env.getRequiredProperty(AppEnvPropertyConstants.JmsConstants.IPREO_DEAL_QUEUE_NAME));
factory.setMessageListener(ipreoDealActivityListener());
factory.setSessionAcknowledgeMode(Session.AUTO_ACKNOWLEDGE);
return factory;
}
#Bean
public MessageListener ipreoDealActivityListener() {
return new IpreoDealActivityListener();
}
Appreciate your help, thanks.
Adding a late response as it might be useful to someone.
In my case, when the java client had this exception, we noticed the actual message size was larger than the default 4 MB buffer size.
The Java API does not provide a hook to change buffer size. Hence, the buffer size has to be updated at the MQ server level.
First, we increased the message size in queue properties - It did not work.
Then, we increased the message size property at the MQ channel level as well, which finally resolved the issue.
To summarise, increase the buffer size at the MQ server for queue & the channel both.
On a client connection to a queue manager you can limit the size of messages on both the server and client side. I've seen this error before when the client side limit was smaller then the size of the message.
I don't know how you can set the message size limit directly in the JMS client, but you could use a Client Channel Definition Table. It's a file containing the details for connecting to queue managers, created on a queue manager and then copied to the client host. You need to reference the file by issuing setCCDTURL on the connection factory (setting the host, port and channel is not required when using a CCDT, the CCDT will specify those).
When the CCDT is created on the queue manager the appropriate message size limit needs to be set on the client channel.
The server side limit is set on the server connection channel.
Within the JMS client code handling of the receive buffer us handled automatically; the theory is that specific error should never be received by a JMS Application.
The first snippet of code is the Java Classes API and this could get that error.
How big actually are these messages? What level of the JMS client code are you using - make sure that it is the latest version. And certainly one of the 7.5 or 8 releases.
This answer also has some more information on this.
I want to send message to a remote IBM MQ using Jmeter for performance testing. I went through this link. But it requires the JNDI specific details like, QueueConnection Factory, JNDI Name Request queue, Initial Context Factory & Provider URL. Whereas the queu details i have are Qmanager, Qname, hostname, channel, port as given in the code shared in this link. Do these properties have any relation? Can i configure the Jmeter JMS test using the queue details i have?
Thanks in advance.
The first link you gave has a description using Java JMS/MQ and the second shows Java MQ (non-JMS).
JMS is just an abstraction layer. In simple terms, JMS is like giving everything a nick-name. A QCF (QueueConnectionFactory) is simply an object that has all of the information to connect to a queue manager.
i.e.
DEFINE QCF(myQCF) QMANAGER(MQWT1) CHANNEL(TEST.CHL) HOSTNAME(127.0.0.1) PORT(1415) TRANSPORT(CLIENT) FAILIFQUIESCE(YES)
A JMS queue is just a nick-name to an MQ queue.
DEFINE Q(test.q) QUEUE(TEST.Q1) QMANAGER(MQWT1) TARGCLIENT(JMS) FAILIFQUIESCE(YES)
Therefore, in your JMS code you simply reference your QCF (i.e. myQCF) and the JMS queue (i.e. test.q) and you are good to go.
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory");
env.put(Context.PROVIDER_URL, ""file:/C:/JNDI-Directory");
try
{
Context ctx = new InitialContext(env);
QueueConnectionFactory cf = (QueueConnectionFactory) ctx.lookup("myQCF");
Queue q = (Queue) ctx.lookup("test.q");
}
catch (NamingException e)
{
System.err.println(e.getLocalizedMessage());
e.printStackTrace();
}
It can be done via the beanshell as well. You can directly access the queue manager via the api, or via exposing the queue via a jms binding. The first is more simple and does not require the MQ client installation.