Think Beyond

Life's more than just software

Spring Boot – Apache Qpid Starter — February 18, 2018

Spring Boot – Apache Qpid Starter

I decided to Spring-ify my previous attempt at configuring and running a HelloWorld program using Apache Qpid which was set up locally and you can read all about that here: Apache Qpid setup for dummies.

In order to use Spring Boot, I modified the code (mostly followed autoconfiguration code from https://github.com/tabish121/qpid-jms-spring-boot/tree/master/qpid-jms-spring-boot-autoconfigure):

  1. Use Spring Boot autoconfiguration to load JMS connection factory details
  2. Schedule a simple message to be delivered to the queue every 5 seconds (@EnableScheduling is wonderful)
  3. A JMS Listener to receive the messages and log to the console.

 

The modified code can be found at https://github.com/trishulpani/qpid-helloworld/tree/helloqpidspringboot

 

Advertisements
QPID message broker local set up for dummies like me —

QPID message broker local set up for dummies like me

Apache Qpid (https://qpid.apache.org/index.html) is a messaging tool that supports AMQP amongst other things. I recently set this up locally (running on Mac OS High Sierra 10.13.3) and was overwhelmed with the documentation (it’s pretty good but no single tutorial to show the basics ) – hence this post.

I just wanted to set up a simple messaging (hello world type) infrastructure that would have the following components:

  1. A Messaging Server 

This is the ‘thing’ that runs like a server and is responsible for receiving and routing messages among another mind-boggling number of things it can do. The component that does this (for Java folks) is downloadable at:

https://qpid.apache.org/components/broker-j/index.html

Screen Shot 2018-02-17 at 12.25.50 PM

Click on the ‘Qpid Broker-J 7.0.1’ (latest at the time of writing), and download the tar file to a suitable location. Untar it and simply run:

<qpid server location>/bin/qpid-server

By default, the Broker starts up on HTTP port 8080 and AMQP port: 5672.

Why do these ports matter?

HTTP Port is used by the QPID Broker management console.

AMQP Port: This is required by the JMS client (the piece of code which will send a message) when creating a connection (more on this later).

However, with a little foresight, you might not want this when running locally (8080 is a commonly used port for local Tomcat and other app servers). So, you can restart the Broker by passing a different port number like so:

<qpid server location>/bin/qpid-server -prop “qpid.amqp_port=10000” -prop “qpid.http_port=10001”

You’ll know if the server started up fine if you see something similar in the console/terminal:

Screen Shot 2018-02-17 at 12.43.02 PM

Now, I can access the Broker management console UI like so:

Screen Shot 2018-02-17 at 12.40.52 PM

The default credential is: admin/admin

Once logged in, you should see a similar screen:

Screen Shot 2018-02-17 at 12.44.12 PM.png

So far, so good. Now I need the other piece of the puzzle. A ‘client’ that can publish some message and consume the message.

2. Messaging client

Here’s what I followed for setting up the client:

https://github.com/apache/qpid-jms/tree/0.29.0/qpid-jms-examples

However, I didn’t download the sample as it is. I was using IntelliJ, so I created a blank Maven project. My pom.xml looks like:

Screen Shot 2018-02-17 at 12.49.10 PM.png

Also, I changed the jndi.properties to accommodate the new port that I previously assigned:

Screen Shot 2018-02-17 at 12.53.36 PM

Ok, so I changed the queue name too (I mean, who wants a queue named queue, right?)

Now, I was being dumb and ran the program and was promptly greeted with the following exception:

Caused by: javax.jms.JMSSecurityRuntimeException: No supported mechanism, or none usable with the available credentials. Server offered: [CRAM-MD5, SCRAM-SHA-1, SCRAM-SHA-256]Caused by: javax.jms.JMSSecurityRuntimeException: No supported mechanism, or none usable with the available credentials. Server offered: [CRAM-MD5, SCRAM-SHA-1, SCRAM-SHA-256] at org.apache.qpid.jms.sasl.SaslMechanismFinder.findMatchingMechanism(SaslMechanismFinder.java:103)

Oops!

The first step (like most things in life) was to ask Google if this meant anything. Nothing substantial came up. Some posts indicated that this might have to do with the SASL supported etc. A lot of BS that hardly helped.

So, I went back to the sample and actually READ through the instructions. I missed two critical steps:

a. You need to create the damn queue (remember the testqueue from jndi.properties?). Ok. How to create a queue? Google again. The first link that came up:

https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_MRG/2/html/Messaging_Installation_and_Configuration_Guide/Create_and_Configure_Queues_using_qpid_config.html

Nope. Not what I wanted.

Back to Qpid documentation. Found this:

https://qpid.apache.org/releases/qpid-broker-j-7.0.0/book/Java-Broker-Management-Channel-Web-Console.html

Duh! Create a queue named ‘testqueue’. Just leave the remaining defaults. I need it up and running for the time being.

Another dummy tip: Double click on the ‘defaults’ node under ‘virtualhosts’ which will bring up the following screen:

Screen Shot 2018-02-17 at 1.09.43 PM.png

Scroll down, and you should see a ‘Add queue’ button. Click it. click it NOW!

Screen Shot 2018-02-17 at 1.10.45 PM.png

Enter the name and you are done.

The queue was created, and you should now see another node under ‘default’ node. (Yes, you can double-click on the queue name and it will show you the details now)

Screen Shot 2018-02-17 at 1.12.23 PM.png

b. You need to pass the following as JVM parameters in order to get rid of the error, like so:

-DUSER=guest -DPASSWORD=guest

 

 

Screen Shot 2018-02-17 at 1.15.53 PM.png

 

Why do we need this? Well, by default the server is using ‘passwordFile’ as the authentication mechanism. So, I need to pass some credentials that the server will recognize.

How does one know the list of credentials that can be identified by the server when using this kind of authentication?

The list can be found in the  <qpid server untar’ed location>/7.0.1/etc/passwd file.Screen Shot 2018-02-17 at 1.25.31 PM.png

AHA! Now the exception makes so much sense !!

I got sidetracked a bit. My apologies.

But, finally, now when I ran the HelloWorld client, I got this in the console:

Screen Shot 2018-02-17 at 1.17.32 PM.png

How did I know the client was able to send the message and receive it? Check the Broker console:

Screen Shot 2018-02-17 at 1.18.42 PM.png

Note the ‘Delivered’ statistics.

Yup. Got this simple hello world to work and learnt a lot of terminologies in the process.

Good stuff!

Github source: https://github.com/trishulpani/qpid-helloworld

 

Standalone client to send JMS messages to a JBOSS queue via JNDI — January 13, 2014

Standalone client to send JMS messages to a JBOSS queue via JNDI

Pretty trivial exercise. But needs a few steps and unless you know exactly what you are doing, it might cause a lot of pain.

Firstly, you’d need the JMS connection factory and queues configured in the local JBoss. This is pretty easy to do.

A client jar is required for the InitialContext located at <JBOSS-HOME>\bin\client\jboss-client.jar

Secondly, you’d need something of the following:

import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Hashtable;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.DirectoryFileFilter;
import org.apache.commons.io.filefilter.WildcardFileFilter;

public class LocalJBOssMQSender {

    /**
     * @param args
     */
    public static void main(String[] args) {

        try {

            Hashtable< String, String > envProps = new Hashtable();
            envProps.put("java.naming.factory.initial", "org.jboss.naming.remote.client.InitialContextFactory");
            envProps.put("java.naming.provider.url", "remote://127.0.0.1:4447");
            envProps.put("java.naming.security.principal", "xxxx");
            envProps.put("java.naming.security.credentials", "xxxxxx");

            InitialContext ic = new InitialContext( envProps );

            ConnectionFactory cf = (ConnectionFactory)ic.lookup("jms/<connection factory name>");
            Queue inboundQueue= (Queue)ic.lookup("jms/queue/<QueueName>");

            Connection connection = cf.createConnection();

            Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            MessageProducer producer = session.createProducer(inboundQueue);

            //pick up all .xml files in a given directory and send them as messages to the queue.
             Collection<File> files = FileUtils.listFiles(new File("C:\\temp\\test"), 
                        new WildcardFileFilter("*.xml"), DirectoryFileFilter.DIRECTORY);

                for(File file : files){

                    System.out.println(file.getName());

                    String messageStr = FileUtils.readFileToString(file);
                    TextMessage message = session.createTextMessage(messageStr);

                    //message.setStringProperty("messageType", "Presentation");
                    // Start the connection
                    connection.start();

                    producer.send(message);
                    System.out.println("Sent message:\\n" + message);
            }

                producer.close();
                session.close();
                connection.close();

        } catch (NamingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (JMSException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

}