A JMS Browser for JBoss-WildFly

JBoss AS 7 and WildFly are missing a native interface to browse the JMS queue messages like for example ActiveMQ does. So I started to code by myself a simple application which does it.

The application is available on github at 

https://github.com/fmarchioni/mastertheboss/tree/master/JMSBrowser

It is a regular Web application which uses the javax.jms API to browse messages and the JBoss Client API to perform some CLI commands. That was needed for example to discover the list of Queue which are available on the application server.

In the target folder I have included the binary file JMSBrowser.war which can be simply deployed on WildFly application server.

Otherwise you can simply compile and deploy it using:

mvn clean install wildfly:deploy

 The application contains a property file in the resources folder:

mode=standalone
#mode=domain
#profile=full
host=127.0.0.1
port=9990

As you can easily guess, you can switch from standalone mode to domain by setting the “mode” option. In the latter option you have to select the profile which you will be using.

I’m including here just the core EJB part which contains the methods for browsing messages, consuming them, send test messages and discover the Queues available:

package com.mastertheboss.jmsbrowser;

import com.google.gson.*;
import com.mastertheboss.jmsbrowser.bean.MessageDTO;
import com.mastertheboss.jmsbrowser.bean.QueueDTO;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import javax.ejb.Stateless;
import java.util.Enumeration;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.inject.Inject;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;

import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.QueueBrowser;

import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.jboss.as.controller.client.ModelControllerClient;
import org.jboss.as.controller.client.OperationBuilder;
import org.jboss.as.controller.client.helpers.ClientConstants;
import org.jboss.dmr.ModelNode;

@Stateless
public class EJBBrowser {


    @Inject ConnectionFactory cf;
    Properties properties;
    
    public List<MessageDTO> browseMessage(String q) {
        List<MessageDTO> list = new ArrayList();
       
        Queue queue = null;
        try {
      
            queue = (Queue) new InitialContext().lookup(q);
        } catch (NamingException ex) {
            Logger.getLogger(EJBBrowser.class.getName()).log(Level.SEVERE, null, ex);
        }

        Connection connection = null;
        Session session = null;

        try {
            connection = cf.createConnection();
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            QueueBrowser browser = session.createBrowser(queue);

            Enumeration messageEnum = browser.getEnumeration();
            while (messageEnum.hasMoreElements()) {
                TextMessage message = (TextMessage) messageEnum.nextElement();
                list.add(new MessageDTO(message.getJMSMessageID(), message.getText(), message.getJMSPriority()));

            }

            browser.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {

            if (connection != null) {
                try {
                    connection.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return list;

    }
  
    public void sendTestMessages(String strQueue, int testmessages) {
        Queue queue = null;
        try {
            queue = (Queue) new InitialContext().lookup(strQueue);
        } catch (NamingException ex) {
            Logger.getLogger(EJBBrowser.class.getName()).log(Level.SEVERE, null, ex);
        }
        Connection connection = null;
        Session session = null;
        MessageProducer producer = null;
        TextMessage message = null;

        try {

            connection = cf.createConnection();
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

            producer = session.createProducer(queue);

            connection.start();

            // Send the specified number of messages
            for (int i = 0; i < testmessages; i++) {
                message = session.createTextMessage("Message sent by Browser at " + new java.util.Date());
                producer.send(message);
                
                Logger.getLogger(EJBBrowser.class.getName()).log(Level.INFO, null, "sent message " + (i + 1));
            }
        } catch (Exception exc) {
            Logger.getLogger(EJBBrowser.class.getName()).log(Level.SEVERE, null, exc);
        } finally {
            try {
                connection.close();
            } catch (JMSException ex) {
                Logger.getLogger(EJBBrowser.class.getName()).log(Level.SEVERE, null, ex);
            }

        }

    }

    public List<QueueDTO> getListQueues() {

        String host = properties.getProperty("host");
        int port = Integer.parseInt(properties.getProperty("port"));
        System.out.println("PORT---->"+port);
        final ModelNode req = new ModelNode();
        req.get(ClientConstants.OP).set("read-children-resources");
        req.get("child-type").set("jms-queue");
 
        String mode = properties.getProperty("mode");
        if (mode.equals("domain")) {
            req.get(ClientConstants.OP_ADDR).add("profile", properties.getProperty("profile"));
        }
        req.get(ClientConstants.OP_ADDR).add("subsystem", "messaging");
        req.get(ClientConstants.OP_ADDR).add("hornetq-server", "default");

        ModelControllerClient client = null;
        List<QueueDTO> listQueues = new ArrayList();
         
        try {
            client = ModelControllerClient.Factory.create(InetAddress.getByName(host), port);
            final ModelNode resp = client.execute(new OperationBuilder(req).build());

            List<ModelNode> list = resp.get(ClientConstants.RESULT).asList();

            for (ModelNode node : list) {

                String json = node.toJSONString(true);
                JsonObject res = new JsonParser().parse(json).getAsJsonObject();
                Set<Map.Entry<String, JsonElement>> es = res.entrySet();
                for (Map.Entry<String, JsonElement> entry : es) {
                    QueueDTO queue = new QueueDTO();
                    queue.setName(entry.getKey());

                    JsonElement element = entry.getValue();
                    JsonObject obj = element.getAsJsonObject();
                    JsonElement binding = obj.get("entries");
                    queue.setEntry(binding.getAsString());

                    listQueues.add(queue);
                }
            }

        } catch (Exception exc) {
            exc.printStackTrace();
        }
       return listQueues;
    }
	
    public void consumeMessages(String strQueue) {
         try {
             
             
             Connection connection = null;
             Session session = null;
             MessageConsumer consumer = null;
             
             connection = cf.createConnection();
             session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
             Queue queueConsume=null;
             try {
                 queueConsume = (Queue)new InitialContext().lookup(strQueue);
             } catch (NamingException ex) {
                 Logger.getLogger(EJBBrowser.class.getName()).log(Level.SEVERE, null, ex);
             }
             consumer = session.createConsumer(queueConsume);
             
             connection.start();
            
             javax.jms.Message msg = consumer.receiveNoWait();
           
             Logger.getLogger(EJBBrowser.class.getName()).log(Level.INFO, "Received "+msg, "");
             connection.close();
             
         } catch (JMSException ex) {
             Logger.getLogger(EJBBrowser.class.getName()).log(Level.SEVERE, null, ex);
         }
    }
        @PostConstruct
	private void readPropertyFile()   {
        try {
            InputStream inputStream = this.getClass().getClassLoader()
                    .getResourceAsStream("jms.properties");
            
            properties = new Properties();
            
            
            
            // load the inputStream using the Properties
            properties.load(inputStream);
            // get the value of the property

            Logger.getLogger(EJBBrowser.class.getName()).log(Level.INFO, "Read Properties ", "");
        } catch (IOException ex) {
            Logger.getLogger(EJBBrowser.class.getName()).log(Level.SEVERE, null, ex);
        }
	        
		
	}
}

 As you can see from the above code, the Queue discovery is done using the CLI by means of the following command:

/subsystem=messaging/hornetq-server=default:read-children-resources(child-type=jms-queue)

 Here is a snapshot of the Web application:

jboss jms browser

Basically you can select the JMS Queue in the left combo and click on “Search” to visualize messages. I’ve added a SendTestMessage button to create some test messages and a Consume Message to consume one of the messages in the Queue.

As you can guess, Primefaces is the library used for the cool look and feel which buys me also some built-in features like pagination and filtering on all fields.

The Web application exposes also a REST API to browse messages on a Queue Usage: http://localhost:8080/Browser/rest/list?queue=[JNDI_BINDING]

Example:

http://localhost:8080/Browser/rest/list?queue=java:/ExampleQueue

Further works

The application could be improved in lots of ways, for example by enabling remote lookup of Connection factories so that the Web application does not need to be deployed on the JMS server to be monitored. Also, having the CLI client API available, it could be possible to use some native operations to move or flush commands for example.

Do you want to contribute on this project: write to [email protected] 

 

Found the article helpful? if so please follow us on Socials