Wednesday, October 15, 2014

A workaround to use JMS in LoadUI

As you know, LoadUI is not supporting JMS (yet, but it's been a long time).
I figured a way to integrate JMS in LoadUI without too much hassle. It's a simple groovy script that acts as JMS subscriber; and no, we can't use it directly in LoadUI runner, because it's groovy compiler is legacy (won't let to cast object to JNDI context).

It is also useful is some certain scenarios when JMeter is involved. Yes, JMeter is a very reliable tool to work with JMS but it has some flaws:
  1. JMeter doesn't provide Parallel processing. Therefore, if we have two Topics (for example, for Successful and Failed messages separately), then you have no choice unless to toggle between them every second till you get the response. This might work for Queues, but won't be the best way for Topics, since we need to listen to topics constantly (if they are remote and not durable) and then there is a chance to not receive the message because JMeter was listening to the wrong topic at that moment.
  2. JMeter consumes the queue messages. Hence we can't re-use the same message, neither we would be able to check the message queue later after the test. 
My solution keeps your messages in a database you won't lose them because of message consumption. You won't need JMS sampler and HermesJMS; Instead you'll use a jdbc connection to your own local database!



Thanks to sgilda,  here is a groovy code I've modified to read JMS messages:

import javax.naming.InitialContext;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.Context;

//public class HelloWorldJMSClient {
 
    // Set up all the default values
    String DEFAULT_MESSAGE = "Hello, P :)!";
    String DEFAULT_CONNECTION_FACTORY = "ConnectionFactory";
    String DEFAULT_DESTINATION = "/EXECUTE"; //better to be a queue :)
    int DEFAULT_MESSAGE_COUNT = 1;
    int waitTime = 5000;
    int count = 1; //number of messages we expect to read/write
    String DEFAULT_USERNAME = "";
    String DEFAULT_PASSWORD = "";
    String INITIAL_CONTEXT_FACTORY = "org.jnp.interfaces.NamingContextFactory";
    String PROVIDER_URL = "jnp://ojms01.xxx.com:1099";
    String selector ='' // Given Selector
    
    //public static void main(String[] args) throws Exception {

        ConnectionFactory connectionFactory = null;
        Connection connection = null;
        Session session = null;
        MessageProducer producer = null;
        MessageConsumer consumer = null;
        Destination destination = null;
        TextMessage message = null;
        Context context = null;
        try {
            // Set up the context for the JNDI lookup
            final Properties env = new Properties();
            env.put(Context.INITIAL_CONTEXT_FACTORY, INITIAL_CONTEXT_FACTORY);
            env.put(Context.PROVIDER_URL, System.getProperty(Context.PROVIDER_URL, PROVIDER_URL));
//            env.put(Context.SECURITY_PRINCIPAL, System.getProperty("username", DEFAULT_USERNAME));
//            env.put(Context.SECURITY_CREDENTIALS, System.getProperty("password", DEFAULT_PASSWORD));
            context = new InitialContext(env);

            // Perform the JNDI lookups
            String connectionFactoryString = System.getProperty("connection.factory", DEFAULT_CONNECTION_FACTORY);
            log.info "Attempting to acquire connection factory " + connectionFactoryString  ;
            connectionFactory = (ConnectionFactory) context.lookup(connectionFactoryString);
            log.info "Found connection factory \"" + connectionFactoryString + "\" in JNDI";

            String destinationString = System.getProperty("destination", DEFAULT_DESTINATION);
            log.info("Attempting to acquire destination \"" + destinationString + "\"");
            destination = (Destination) context.lookup(destinationString);
            log.info("Found destination \"" + destinationString + "\" in JNDI");

            // Create the JMS connection, session, producer, and consumer
            connection = connectionFactory.createConnection();
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            producer = session.createProducer(destination);
            consumer = session.createConsumer(destination,selector);
            connection.start();

//            String content = DEFAULT_MESSAGE;
//            log.info("Sending " + count + " messages with content: " + content);
//
//            // Send the specified number of messages
//            for (int i = 0; i < count; i++) {
//                message = session.createTextMessage(content+" No."+(i+1));
//                producer.send(message);
//            }



            
            // Then receive the same number of messages that were sent
            for (int i = 0; i < count; i++) {
                message = (TextMessage) consumer.receive(waitTime);
                if (message != null)
                {
                log.info("Received message with content " + message.getText());
                log.info("Correlation ID: " + message.getJMSCorrelationID());
                log.info("Timestamp: " + message.getJMSTimestamp());
                log.info("MessageID: " + message.getJMSTimestamp());
                }
                else
                {
                  log.warn "Nothing found in "+DEFAULT_DESTINATION;
                  break;

                }
                
            }
        } catch (Exception e) {
            log.error "Exception error: "+(e.getMessage());
            throw e;
        } finally {
            if (context != null) {
                context.close();
            }

            // closing the connection takes care of the session, producer, and consumer
            if (connection != null) {
                connection.close();
            }
        }
//    }


Please note: Probably you'll need to add some jars to your classpath, such as: jms-1.1.jar, netty.jar, jnp-client.jar and other libraries corresponding to your JMS server (HornetQ, ActiveMQ, ...)

  1. Commented part is for sending messages. you won't need it.
  2. Set your parameters in lines 14-24
  3. The "selector" is optional, it is currently set to consume every message regardless of the selector content.

Last step:  What you need to do is to open you Groovy/Java console and change this script to an infinite loop, inserting all received messages (including Header elements, message body and IDs) in your local database with a simple jdbc connection. Run this while you are running your LoadUI/JMeter test, 

Use jdbc connection with a select (selector) query statement for a given "timeout" in a loop. You can do it easily with JMeter Loop controller , or SoapUI groovy steps.




No comments:

Post a Comment