HornetQ

How to create custom Expiry Queue in JBoss AS 7.1.2 using CLI


Starting of this year we had shared a Working example of Expiry Queue in JBoss AS 5.1 which was using JBoss Messaging when JBoss AS 7 was not realised. However we all know that JBoss AS 7 is using HorrnetQ and in this article we would be sharing the steps on how to create a custom Expiry Queue for a particular queue and would be using the same working example but with few changes to get it working with JBossAS7

For this article we would be using JBoss AS 7.1.2 and the same concept would be used, which is setting up the expiry period for a messages (i.e. when setTimeToLive is been used in the code) and as per JMS specification which states that clients should not receive messages that have been expired which does not grantee that. Hence once the messages gets expiries they are been moved to the expiry queue which has been assigned to the given queue. Now these “expired” messages can later be consumed from the expiry destination for further inspection.

You can get the complete demo from this link : Demo Link

Features To Discuss

  1. We would be creating a queue using CLI
  2. Then creating a custom Expiry Queue and assign it to the newly created queue using CLI
  3. In this example we will send 1 message with a short time-to-live to a queue.
  4. We will wait for the message to expire and checks that the message is no longer in the queue it was sent to.

Steps to create a custom Expiry Queue in JBoss 7.1.2 using CLI

Using the below CLI file we are doing total 3 things at the same-time and that too with one command, now that’s called saving time 😉

  • Creating a Queue with a name MyQueue and having a remote enabled JNDI name /MyQueue which has a prefix as “java:jboss/exported”
  • Creating a custom Expiry Queue with a name MyExpiryQueue and having a local JDNI name /MyExpiryQueue
  • And configuring the custom Expiry Queue MyExpiryQueue with the queue MyQueue by creating a new address-setting particularly for MyQueue
  1. Create a file called MyExpiryQueue-add.cli in the bin folder of JBoss and copy the below content
  2. connect
    
    #Adding MyQueue
    /subsystem=messaging/hornetq-server=default/jms-queue=MyQueue/:add(entries=["java:jboss/exported/MyQueue"],durable=false)
    
    #Adding MyExpiryQueue
    /subsystem=messaging/hornetq-server=default/jms-queue=MyExpiryQueue/:add(entries=["java:/MyExpiryQueue"],durable=false)
    
    #Adding address-settings for MyQueue
    /subsystem=messaging/hornetq-server=default/address-setting=jms.queue.MyQueue/:add(expiry-address=jms.queue.MyExpiryQueue)
    
    
  3. Now we need to create a new Application User by running “${JBOSS_HOME}/bin/add-user.sh” script as following :
  4. [user@user bin]$ ./add-user.sh
    
    What type of user do you wish to add?
     a) Management User (mgmt-users.properties)
     b) Application User (application-users.properties)
    (a): b
    
    Enter the details of the new user to add.
    Realm (ApplicationRealm) :  ApplicationRealm
    Username : testuser
    Password : testpassword
    Re-enter Password : testpassword
    
    What roles do you want this user to belong to? (Please enter a comma separated list, or leave blank for none) : guest
    About to add user 'testuser' for realm 'ApplicationRealm'
    
    Is this correct yes/no? yes
    
    Added user 'testuser' to file '/home/user/jboss-as-7.1.2.Final/standalone/configuration/application-users.properties'
    Added user 'testuser' to file '/home/user/jboss-as-7.1.2.Final/domain/configuration/application-users.properties'
    Added user 'testuser' with roles testrole to file '/home/user/jboss-as-7.1.2.Final/standalone/configuration/application-roles.properties'
    Added user 'testuser' with roles testrole to file '/home/user/jboss-as-7.1.2.Final/domain/configuration/application-roles.properties'
    
    
  5. Make sure that the JBoss AS 7.1.2 is running, in our case we started JBoss AS 7.1.2 standalone-full.xml profile.
  6. [user@user bin]$ ./standalone.sh -c standalone-full.xml
    
  7. Now you just have to run the below command to execute above three things
  8. [user@user bin]$ ./jboss-cli.sh --file=MyExpiryQueue-add.cli
    {"outcome" => "success"}
    {"outcome" => "success"}
    {"outcome" => "success"}
    [user@user bin]$
    

    Once your above CLI command is executed successfully you will notice the following in your JBossAS 7.1.2 configuration file “standalone-full.xml”:

    <address-settings>
        <address-setting match="#">
    	.
    	.
    	.
        </address-setting>
        <address-setting match="jms.queue.MyQueue">
           <expiry-address>jms.queue.MyExpiryQueue</expiry-address>
        </address-setting>
    </address-settings>
    .
    .
    .
    <jms-destinations>
        <jms-queue name="MyQueue">
            <entry name="java:/exported/MyQueue"/>
            <durable>false</durable>
        </jms-queue>
        <jms-queue name="MyExpiryQueue">
            <entry name="java:/MyExpiryQueue"/>
            <durable>false</durable>
        </jms-queue>
    </jms-destinations>
    
  9. That’s it !!! You are done configuring a simple queue with a custom ExpiryQueue. Let’s test this out using the sender and receiver Java code.

Testing

  1. Create a Directory somewhere in your file system like: “/urs/JBossAS7.1.2/HQ/Queue/MyExpiryQueue-test” to write the QueueSend.java and QueueReceive.java programs.
  2. In QueueSend.java copy the below program, where we are setting the first messages to live for 1s (1000ms) before expiration.
    import java.io.*;
    import java.io.*;
    import java.util.Hashtable;
    import javax.jms.JMSException;
    import javax.jms.Queue;
    import javax.jms.QueueConnection;
    import javax.jms.QueueConnectionFactory;
    import javax.jms.QueueSender;
    import javax.jms.QueueSession;
    import javax.jms.Session;
    import javax.jms.TextMessage;
    import javax.naming.Context;
    import javax.naming.InitialContext;
    import javax.naming.NamingException;
    
    public class QueueSend
    {
    public final static String JNDI_FACTORY="org.jboss.naming.remote.client.InitialContextFactory";
    
    //*************** Connection Factory JNDI name *************************
    public final static String JMS_FACTORY="/jms/RemoteConnectionFactory";
    
    //*************** Remote enabled Queue JNDI name *************************
    public final static String QUEUE="/MyQueue";
    
    private QueueConnectionFactory qconFactory;
    private QueueConnection qcon;
    private QueueSession qsession;
    private QueueSender qsender;
    private Queue queue;
    private TextMessage msg;
    
    public void init(Context ctx, String queueName)throws NamingException, JMSException
    {
    qconFactory = (QueueConnectionFactory) ctx.lookup(JMS_FACTORY);
    
    //*************** Creating Queue Connection using the UserName & Password *************************
    qcon = qconFactory.createQueueConnection("testuser","testpassword");   			//<------------- Change the UserName & Password
    
    qsession = qcon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
    queue = (Queue) ctx.lookup(queueName);
    qsender = qsession.createSender(queue);
    
    //********* Messages sent by this sender will be live for 1s (1000ms) before expiration *********
    qsender.setTimeToLive(1000);
    
    msg = qsession.createTextMessage();
    qcon.start();
    }
    
    public void send(String message) throws JMSException {
    msg.setText(message);
    qsender.send(msg);
    System.out.println("Message="+message+ " Time to live ="+qsender.getTimeToLive());
    }
    
    public void close() throws JMSException {
    qsender.close();
    qsession.close();
    qcon.close();
    }
    
    public static void main(String[] args) throws Exception {
    if (args.length != 1) {
    System.out.println("Usage: java QueueSend URL");
    return;
    }
    InitialContext ic = getInitialContext(args[0]);
    QueueSend qs = new QueueSend();
    qs.init(ic, QUEUE);
    readAndSend(qs);
    qs.close();
    }
    
    private static void readAndSend(QueueSend qs) throws IOException, JMSException
    {
    String line="Test Message Body with counter = ";
    BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
    boolean readFlag=true;
    System.out.println("ntStart Sending Messages (Enter QUIT to Stop):n");
    while(readFlag)
    {
    System.out.print("<Msg_Sender> ");
    String msg=br.readLine();
    if(msg.equals("QUIT") || msg.equals("quit"))
    {
    qs.send(msg);
    System.exit(0);
    }
    qs.send(msg);
    System.out.println();
    }
    br.close();
    }
    
    private static InitialContext getInitialContext(String url) throws NamingException
    {
    Hashtable env = new Hashtable();
    env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
    env.put(Context.PROVIDER_URL, url);
    
    //*************** UserName & Password for the Initial Context for JNDI lookup *************************
    env.put(Context.SECURITY_PRINCIPAL, "testuser");
    env.put(Context.SECURITY_CREDENTIALS, "testpassword");
    
    return new InitialContext(env);
    }
    }
    
  3. In QueueReceive.java copy the below program, if you notice that before we are creating the Receiver we are making sure that the program starts 5 seconds late so that the first message sent gets expired as its time to live is 1 second. So the first messages is never consumed and that messages is been moved to expiry queue. But other messages would get consumed as the consumer is up and listening to the MyQueue.
    import java.util.Hashtable;
    import javax.jms.*;
    import javax.naming.Context;
    import javax.naming.InitialContext;
    import javax.naming.NamingException;
    
    public class QueueReceive implements MessageListener
    {
    public final static String JNDI_FACTORY="org.jboss.naming.remote.client.InitialContextFactory";
    
    //*************** Connection Factory JNDI name *************************
    public final static String JMS_FACTORY="/jms/RemoteConnectionFactory";
    
    //*************** Remote enabled Queue JNDI name *************************
    public final static String QUEUE="/MyQueue";
    
    private QueueConnectionFactory qconFactory;
    private QueueConnection qcon;
    private QueueSession qsession;
    private QueueReceiver qreceiver;
    private Queue queue;
    private boolean quit = false;
    
    public void onMessage(Message msg)
    {
    	try
    	{
    		String msgText;
    		if (msg instanceof TextMessage)
    		{
    			msgText = ((TextMessage)msg).getText();
    		}
    		else
    		{
    			msgText = msg.toString();
    		}
    
    		System.out.println("nt<Msg_Receiver> "+ msgText );
    
    		if (msgText.equalsIgnoreCase("quit"))
    		{
    			synchronized(this)
    			{
    				quit = true;
    				this.notifyAll(); // Notify main thread to quit
    			}
    		}
    	}
    	catch (JMSException jmse)
    	{
    		jmse.printStackTrace();
    	}
    }
    
    public void init(Context ctx, String queueName) throws NamingException, JMSException
    {
    	try
    	{
    		qconFactory = (QueueConnectionFactory) ctx.lookup(JMS_FACTORY);
    
    		//*************** Creating Queue Connection using the UserName & Password *************************
    		qcon = qconFactory.createQueueConnection("testuser","testpassword");   			//<------------- Change the UserName & Password
    
    		qsession = qcon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
    		queue = (Queue) ctx.lookup(queueName);
    
    		//********* Sleeping for 5's so that the message gets expired  ********
    		System.out.println("Sleeping for 5's till the message expire...");
    		Thread.sleep(5000);
    
    		qreceiver = qsession.createReceiver(queue);
    		qreceiver.setMessageListener(this);
    		qcon.start();
    	}
    	catch (InterruptedException jmse)
    	{
    		jmse.printStackTrace();
    	}
    
    }
    
    public void close()throws JMSException
    {
    qreceiver.close();
    qsession.close();
    qcon.close();
    }
    
    public static void main(String[] args) throws Exception
    {
    if (args.length != 1)
    {
    System.out.println("Usage: java QueueReceive URL");
    return;
    }
    InitialContext ic = getInitialContext(args[0]);
    QueueReceive qr = new QueueReceive();
    qr.init(ic, QUEUE);
    System.out.println("JMS Ready To Receive Messages (To quit, send a "quit" message from QueueSender.class).");
    
    synchronized(qr)
    {
    while (! qr.quit)
    {
    try
    {
    qr.wait();
    }
    catch (InterruptedException ie)
    {}
    }
    }
    qr.close();
    }
    
    private static InitialContext getInitialContext(String url) throws NamingException
    {
    Hashtable env = new Hashtable();
    env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
    env.put(Context.PROVIDER_URL, url);
    
    //*************** UserName & Password for the Initial Context for JNDI lookup *************************
    env.put(Context.SECURITY_PRINCIPAL, "testuser");
    env.put(Context.SECURITY_CREDENTIALS, "testpassword");
    
    return new InitialContext(env);
    }
    }
    
  4. Now open 2– command prompt and run the “. ./setJBossEnv.sh” by adding two DOTs separated by a single space, in both the prompts to set the Environment (PATH & CLASSPATH).
  5. setJBossEnv.sh

    #!/bin/sh
    echo "Exporting JBOSS_HOME...."
    export JBOSS_HOME=/Jboss/jboss-as-7.1.2.Final   #<---- Change as per your environment
    echo "JBOSS_HOME Exported....!!!"
    echo ""
    echo "Exporting JAVA_HOME...."
    export JAVA_HOME=/Jdk/jdk1.6.0_21    #<---- Change as per your environment
    echo "JBOSS_HOME Exported....!!!"
    echo ""
    echo "Exporting CLASSPATH...."
    export CLASSPATH=$JBOSS_HOME/bin/client/jboss-client.jar:$CLASSPATH:.:
    echo "CLASSPATH Exported....!!!"
    echo ""
    echo "Exporting PATH...."
    export PATH=$JAVA_HOME/bin:$PATH
    echo "PATH Exported....!!!"
    echo ""
    echo "========================================================================"
    echo "JBOSS_HOME = " $JBOSS_HOME
    echo ""
    echo "JAVA_HOME = " $JAVA_HOME
    echo ""
    echo "CLASSPATH = " $CLASSPATH
    echo ""
    echo "PATH = " $PATH
    echo "========================================================================"
    
  6. Then compile the QueueSend.java and QueueReceive.java programs.
  7. Now run the code with the following command
  8. Prompt-1

    java QueueSend remote://localhost:4447
     
  9. Now send one message when the QueueReceive is not started, this way the message which has sent to the MyQueue would have the Time To Live as 1′s second. But this time would not start till the QueueReceive has been startred
  10. Now start the QueueReceive using the below command
  11. Prompt-2

    java QueueReceive remote://localhost:4447
     
  12. Once the QueueReceive has been started for the first time it would sleep for 5 second, hence the message which was in the MyQueue would get expired as its time to live was 1 second, hence the messages would get moved to expire queue, which can be seen using two ways
  13. CLI

    [user@user bin]$ ./jboss-cli.sh -c /subsystem=messaging/hornetq-server=default/jms-queue=MyExpiryQueue/:count-messages
    {
        "outcome" => "success",
        "result" => 1L
    }
    

    OR

    Admin console path:
    Runtime – Status – JMS Destination – MyExpiryQueue – “Messages in Queue:”

  14. However other messages would get consumed as the consumer is up and listening to the MyQueue

How to create custom Dead Letter Queue in JBoss AS 7.1.2 using CLI


Starting of this year we had shared a Working example of Dead Letter Queue in JBoss AS 5.1 which was using JBoss Messaging when JBoss AS 7 was not realised. However we all know that JBoss AS 7 is using HorrnetQ and in this article we would be sharing the steps on how to create a custom DLQ for a particular queue and would be using the same working example but with few changes to get it working with JBossAS7

For this article we would be using JBoss AS 7.1.2 and the same concept would be used, which is after a specified unsuccessful delivery attempts (i.e. max-delivery-attempts in JBoss AS 7), the message is removed from the destination and moves it inside the defined custom Dead Letter Queue (DLQ) where the messages can be consumed for further investigation.

Features To Discuss

  1. We would be creating a queue using CLI
  2. Then creating a custom DLQ and assign it to the newly created queue using CLI
  3. In testing we would make sure that if a message is having a string as “Hello World”, then that message would be rolled backed after specified unsuccessful delivery attempts and would be moved in our custom created DLQ and other messages can be consumed without any issue.

Steps to create a custom Dead Letter Queue in JBoss 7.1.2 using CLI

Using the below CLI file we are doing total 3 things at the same-time and that too with one command, now that’s called saving time 😉

  • Creating a Queue with a name MyQueue and having a remote enabled JNDI name /MyQueue which has a prefix as “java:jboss/exported”
  • Creating a custom DLQ with a name MyDLQ and having a local JDNI name /MyDLQ
  • And configuring the custom DLQ MyDLQ with the queue MyQueue by creating a new address-setting particularly for MyQueue
  1. Create a file called MyQueue-add.cli in the bin folder of JBoss and copy the below content
  2. connect
    
    #Adding MyQueue
    /subsystem=messaging/hornetq-server=default/jms-queue=MyQueue/:add(entries=["java:jboss/exported/MyQueue"],durable=false)
    
    #Adding MyDLQ
    /subsystem=messaging/hornetq-server=default/jms-queue=MyDLQ/:add(entries=["java:/MyDLQ"],durable=false)
    
    #Adding address-settings for MyQueue
    /subsystem=messaging/hornetq-server=default/address-setting=jms.queue.MyQueue/:add(address-full-policy=BLOCK,dead-letter-address=jms.queue.MyDLQ,max-size-bytes=10485760,message-counter-history-day-limit=10,max-delivery-attempts=3,redelivery-delay=0)
    
  3. Now we need to create a new Application User by running “${JBOSS_HOME}/bin/add-user.sh” script as following :
  4. [user@user bin]$ ./add-user.sh
    
    What type of user do you wish to add?
     a) Management User (mgmt-users.properties)
     b) Application User (application-users.properties)
    (a): b
    
    Enter the details of the new user to add.
    Realm (ApplicationRealm) :  ApplicationRealm
    Username : testuser
    Password : testpassword
    Re-enter Password : testpassword
    
    What roles do you want this user to belong to? (Please enter a comma separated list, or leave blank for none) : guest
    About to add user 'testuser' for realm 'ApplicationRealm'
    
    Is this correct yes/no? yes
    
    Added user 'testuser' to file '/home/user/jboss-as-7.1.2.Final/standalone/configuration/application-users.properties'
    Added user 'testuser' to file '/home/user/jboss-as-7.1.2.Final/domain/configuration/application-users.properties'
    Added user 'testuser' with roles testrole to file '/home/user/jboss-as-7.1.2.Final/standalone/configuration/application-roles.properties'
    Added user 'testuser' with roles testrole to file '/home/user/jboss-as-7.1.2.Final/domain/configuration/application-roles.properties'
    
    
  5. Make sure that the JBoss AS 7.1.2 is running, in our case we started JBoss AS 7.1.2 standalone-full.xml profile.
  6. [user@user bin]$ ./standalone.sh -c standalone-full.xml
    
  7. Now you just have to run the below command to execute above three things
  8. [user@user bin]$ ./jboss-cli.sh --file=MyQueue-add.cli
    {"outcome" => "success"}
    {"outcome" => "success"}
    {"outcome" => "success"}
    [user@user bin]$
    

    Once your above CLI command is executed successfully you will notice the following in your JBossAS 7.1.2 configuration file “standalone-full.xml”:

    <address-settings>
        <address-setting match="#">
    	.
    	.
    	.
        </address-setting>
        <address-setting match="jms.queue.MyQueue">
            <dead-letter-address>jms.queue.MyDLQ</dead-letter-address>
            <redelivery-delay>0</redelivery-delay>
            <max-delivery-attempts>3</max-delivery-attempts>
            <max-size-bytes>10485760</max-size-bytes>
            <address-full-policy>BLOCK</address-full-policy>
            <message-counter-history-day-limit>10</message-counter-history-day-limit>
        </address-setting>
    </address-settings>
    .
    .
    .
    <jms-destinations>
        <jms-queue name="MyQueue">
            <entry name="java:/exported/MyQueue"/>
            <durable>false</durable>
        </jms-queue>
        <jms-queue name="MyDLQ">
            <entry name="java:/MyDLQ"/>
            <durable>false</durable>
        </jms-queue>
    </jms-destinations>
    
  9. That’s it !!! You are done configuring a simple queue with a custom DLQ, having a max-delivery-attempts as 3, now if a message which is in MyQueue does not get delivered in 3 attempts then that message would be moved to MyDLQ which is a custom DLQ. Let’s test this out using the sender and receiver Java code.

Testing

  1. Create a Directory somewhere in your file system like: “/urs/JBossAS7.1.2/HQ/Queue” to write the QueueSend.java and QueueReceive.java programs.
  2. In QueueSend.java copy the below program
    import java.io.*;
    import java.io.*;
    import java.util.Hashtable;
    import javax.jms.JMSException;
    import javax.jms.Queue;
    import javax.jms.QueueConnection;
    import javax.jms.QueueConnectionFactory;
    import javax.jms.QueueSender;
    import javax.jms.QueueSession;
    import javax.jms.Session;
    import javax.jms.TextMessage;
    import javax.naming.Context;
    import javax.naming.InitialContext;
    import javax.naming.NamingException;
    
    public class QueueSend
    {
    public final static String JNDI_FACTORY="org.jboss.naming.remote.client.InitialContextFactory";
    
    //*************** Connection Factory JNDI name *************************
    public final static String JMS_FACTORY="/jms/RemoteConnectionFactory";
    
    //*************** Remote enabled Queue JNDI name *************************
    public final static String QUEUE="/MyQueue";
    
    private QueueConnectionFactory qconFactory;
    private QueueConnection qcon;
    private QueueSession qsession;
    private QueueSender qsender;
    private Queue queue;
    private TextMessage msg;
    
    public void init(Context ctx, String queueName)throws NamingException, JMSException
    {
    qconFactory = (QueueConnectionFactory) ctx.lookup(JMS_FACTORY);
    
    //*************** Creating Queue Connection using the UserName & Password *************************
    qcon = qconFactory.createQueueConnection("testuser","testpassword");   			//<------------- Change the UserName & Password
    
    qsession = qcon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
    queue = (Queue) ctx.lookup(queueName);
    qsender = qsession.createSender(queue);
    msg = qsession.createTextMessage();
    qcon.start();
    }
    
    public void send(String message) throws JMSException {
    msg.setText(message);
    qsender.send(msg);
    }
    
    public void close() throws JMSException {
    qsender.close();
    qsession.close();
    qcon.close();
    }
    
    public static void main(String[] args) throws Exception {
    if (args.length != 1) {
    System.out.println("Usage: java QueueSend URL");
    return;
    }
    InitialContext ic = getInitialContext(args[0]);
    QueueSend qs = new QueueSend();
    qs.init(ic, QUEUE);
    readAndSend(qs);
    qs.close();
    }
    
    private static void readAndSend(QueueSend qs) throws IOException, JMSException
    {
    String line="Test Message Body with counter = ";
    BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
    boolean readFlag=true;
    System.out.println("ntStart Sending Messages (Enter QUIT to Stop):n");
    while(readFlag)
    {
    System.out.print(" ");
    String msg=br.readLine();
    if(msg.equals("QUIT") || msg.equals("quit"))
    {
    qs.send(msg);
    System.exit(0);
    }
    qs.send(msg);
    System.out.println();
    }
    br.close();
    }
    
    private static InitialContext getInitialContext(String url) throws NamingException
    {
    Hashtable env = new Hashtable();
    env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
    env.put(Context.PROVIDER_URL, url);
    
    //*************** UserName & Password for the Initial Context for JNDI lookup *************************
    env.put(Context.SECURITY_PRINCIPAL, "testuser");
    env.put(Context.SECURITY_CREDENTIALS, "testpassword");
    
    return new InitialContext(env);
    }
    }
    
  3. In QueueReceive.java copy the below program, which has a creating a transacted JMS Session and in “onMessage” we are checking it the text message which is been received contents “hello world” ignoring the case would get roll-backed if not would get committed to test our DLQ.
    import java.util.Hashtable;
    import javax.jms.*;
    import javax.naming.Context;
    import javax.naming.InitialContext;
    import javax.naming.NamingException;
    
    public class QueueReceive implements MessageListener
    {
    public final static String JNDI_FACTORY="org.jboss.naming.remote.client.InitialContextFactory";
    
    //*************** Connection Factory JNDI name *************************
    public final static String JMS_FACTORY="/jms/RemoteConnectionFactory";
    
    //*************** Remote enabled Queue JNDI name *************************
    public final static String QUEUE="/MyQueue";
    
    private QueueConnectionFactory qconFactory;
    private QueueConnection qcon;
    private QueueSession qsession;
    private QueueReceiver qreceiver;
    private Queue queue;
    private boolean quit = false;
    
    public void onMessage(Message msg)
    {
    	try
    	{
    		String msgText;
    		if (msg instanceof TextMessage)
    		{
    			msgText = ((TextMessage)msg).getText();
    		}
    		else
    		{
    			msgText = msg.toString();
    		}
    		if (msgText.equalsIgnoreCase("hello world"))
    		{
    			System.out.println("nt "+ msgText +", hence rolling back");
    			// Here we are rolling back the session.
    			// The "Hello World" message which we received is not committed, hence it's undelivered and goes back to the TestQ
    			qsession.rollback();
    		}
    		else
    		{
    			System.out.println("nt "+ msgText );
    			// Here we are committing the session to acknowledge that we have received the message from the TestQ
    			qsession.commit();
    		}
    
    		if (msgText.equalsIgnoreCase("quit"))
    		{
    			synchronized(this)
    			{
    				quit = true;
    				this.notifyAll();
    			}
    		}
    	}
    	catch (JMSException jmse)
    	{
    	jmse.printStackTrace();
    	}
    }
    
    public void init(Context ctx, String queueName) throws NamingException, JMSException
    {
    	qconFactory = (QueueConnectionFactory) ctx.lookup(JMS_FACTORY);
    
            //*************** Creating Queue Connection using the UserName & Password *************************
    	qcon = qconFactory.createQueueConnection("testuser","testpassword");   			//<------------- Change the UserName & Password
    
    	//Creating a *transacted* JMS Session to test our DLQ
    	qsession = qcon.createQueueSession(true, 0);
    
    	queue = (Queue) ctx.lookup(queueName);
    	qreceiver = qsession.createReceiver(queue);
    	qreceiver.setMessageListener(this);
    	qcon.start();
    }
    
    public void close()throws JMSException
    {
    qreceiver.close();
    qsession.close();
    qcon.close();
    }
    
    public static void main(String[] args) throws Exception
    {
    if (args.length != 1)
    {
    System.out.println("Usage: java QueueReceive URL");
    return;
    }
    InitialContext ic = getInitialContext(args[0]);
    QueueReceive qr = new QueueReceive();
    qr.init(ic, QUEUE);
    System.out.println("JMS Ready To Receive Messages (To quit, send a "quit" message from QueueSender.class).");
    
    synchronized(qr)
    {
    while (! qr.quit)
    {
    try
    {
    qr.wait();
    }
    catch (InterruptedException ie)
    {}
    }
    }
    qr.close();
    }
    
    private static InitialContext getInitialContext(String url) throws NamingException
    {
    Hashtable env = new Hashtable();
    env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
    env.put(Context.PROVIDER_URL, url);
    
    //*************** UserName & Password for the Initial Context for JNDI lookup *************************
    env.put(Context.SECURITY_PRINCIPAL, "testuser");
    env.put(Context.SECURITY_CREDENTIALS, "testpassword");
    
    return new InitialContext(env);
    }
    }
    
  4. Now open 2– command prompt and run the “. ./setJBossEnv.sh” by adding two DOTs separated by a single space, in both the prompts to set the Environment (PATH & CLASSPATH).
  5. setJBossEnv.sh

    #!/bin/sh
    echo "Exporting JBOSS_HOME...."
    export JBOSS_HOME=/Jboss/jboss-as-7.1.2.Final   #<---- Change as per your environment
    echo "JBOSS_HOME Exported....!!!"
    echo ""
    echo "Exporting JAVA_HOME...."
    export JAVA_HOME=/Jdk/jdk1.6.0_21    #<---- Change as per your environment
    echo "JBOSS_HOME Exported....!!!"
    echo ""
    echo "Exporting CLASSPATH...."
    export CLASSPATH=$JBOSS_HOME/bin/client/jboss-client.jar:$CLASSPATH:.:
    echo "CLASSPATH Exported....!!!"
    echo ""
    echo "Exporting PATH...."
    export PATH=$JAVA_HOME/bin:$PATH
    echo "PATH Exported....!!!"
    echo ""
    echo "========================================================================"
    echo "JBOSS_HOME = " $JBOSS_HOME
    echo ""
    echo "JAVA_HOME = " $JAVA_HOME
    echo ""
    echo "CLASSPATH = " $CLASSPATH
    echo ""
    echo "PATH = " $PATH
    echo "========================================================================"
    
  6. Then compile the QueueSend.java and QueueReceive.java programs.
  7. Now run the code with the following command
  8. Prompt-1

    java QueueSend remote://localhost:4447
     

    Prompt-2

    java QueueReceive remote://localhost:4447
     

  9. Once this is been done try to send some messages (i.e. with “hello world” one of them ) in the Prompt-1 which can be revived on Prompt-2. , however the message which is “hellow world” would be rollbacked and as per the “max-delivery-attempts” value 3 times it would be re-sent and then it would be moved to the DLQ.

    CLI

    [user@user bin]$ ./jboss-cli.sh -c /subsystem=messaging/hornetq-server=default/jms-queue=MyDLQ/:count-messages
    {
        "outcome" => "success",
        "result" => 1L
    }
    

    OR

    Admin console path:
    Runtime – Status – JMS Destination – MyDLQ – “Messages in Queue:”


Creating JMS Queue/Topic via Application using *-jms.xml in JBossAS7

Hi,

In our some of the previous demos how to effeciently use the JBoss Messaging feature which is implemented over Best Messagng System HornetQ. We created JMS Queue and topics using CLI and using the Admin Console previously. Here in this demo we will see how to create a JMS Queue/Topic using *-jms.xml file and how to deploy it as part of our simple Application like .war file.

Although it is not recommended to use the application specific JMS Queue / Topic deployment because of it’s less managability. We should not use it for production purpose because it will not be possible to monitor it using the console or to manage it. But as a reference and a possibility we are going to discuss application level JMS Queue/Topic deployment as part of this article.

Points We will Discuss

Point-1) How to deploy a JMS Queue/Topic via *-jms.xml file placed inside our “WEB-INF” directory.

Point-2) How to place our MDBs inside the WAR file and deploy it as part of our Web Application (WAR).

Point-3) How to write a simple Standalone program in order to send some JMS Messages to the JMS Queue.

NOTE: the Codes related to this demo are available in the Github repository:
https://github.com/jaysensharma/MiddlewareMagicDemos/tree/master/JMS_Demo

Step-1). Here we are using JBoss AS7.1.1 Final (“jboss-as-7.1.1.Final”) which can be downloaded from the following link: http://www.jboss.org/jbossas/downloads

Step-2). Create a directory with the name “src” somewhere in your file system like “/home/userone/JMS_Demo/”. Inside the “src” directory we are going to place our all source code and xml files for development.

Step-3). Now we will write a simple file “test-jms.xml” where we will define our JMS Queue and Topics. Place this file inside “/home/userone/JMS_Demo/src” as following

<?xml version="1.0" encoding="UTF-8"?>
<messaging-deployment xmlns="urn:jboss:messaging-deployment:1.0">
    <hornetq-server>
        <jms-destinations>
            <jms-queue name="QueueOne">
                <entry name="java:jboss/exported/TestQueueOne"/>
                <durable>true</durable>
            </jms-queue>
            <jms-topic name="TopicOne">
                <entry name="java:jboss/exported/TestTopicOne"/>
            </jms-topic>
        </jms-destinations>
    </hornetq-server>
</messaging-deployment>

Step-4). Now we will write a Simple Servlet program “QueueSendServlet.java” which will send some messages to the JMS queue “TestQueueOne” Place this file inside “/home/userone/JMS_Demo/src” as following:

import java.io.*;
import javax.jms.*;
import javax.naming.*;
import javax.servlet.http.*;
import javax.servlet.ServletException;

public class QueueSendServlet extends HttpServlet
{
	static PrintWriter out;
	public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
 	 {
		try
		{
			out=response.getWriter();
			InitialContext ic = getInitialContext();
			init(ic, QUEUE_NAME);
			sendMsg(request.getParameter("jmsMessage"));
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}

	public final static String CNN_FACTORY="/ConnectionFactory";
	public final static String QUEUE_NAME="java:jboss/exported/TestQueueOne";

	private QueueConnectionFactory qconFactory;
	private QueueConnection qcon;
	private QueueSession qsession;
	private QueueSender qsender;
	private Queue queue;
	private TextMessage msg;

	public void init(Context ctx, String queueName)throws NamingException, JMSException
	{
		qconFactory = (QueueConnectionFactory) ctx.lookup(CNN_FACTORY);
		qcon = qconFactory.createQueueConnection();
		qsession = qcon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
		queue = (Queue) ctx.lookup(queueName);
		qsender = qsession.createSender(queue);
		msg = qsession.createTextMessage();
		qcon.start();
	}

	private  void sendMsg(String userMessage) throws IOException, JMSException
	{
		boolean readFlag=true;
		out.println("");
		out.println("");
		out.println("");
		out.println("<h1>Queue Sender Servlet</h1>");
		out.println("Following Messages has been sent !!!");
		out.println("<BR>====================================<BR>");
                        msg.setText(userMessage);
                        qsender.send(msg);
   			out.println("Message Sent => "+userMessage);
		out.println("<BR>====================================");
		out.println("");
		out.println("");
		out.println("");
                qcon.close();
	}

	private static InitialContext getInitialContext() throws NamingException
	{
		return new InitialContext();
	}
}

Step-5). Now we will Write a simple “Message Driven Bean “MyMDB.java” which will consume messages from TestQueueOne file,Place this file inside “/home/userone/JMS_Demo/src” as following

import javax.jms.Message;
import javax.jms.TextMessage;
import javax.jms.JMSException;
import javax.jms.MessageListener;
import javax.ejb.MessageDriven;
import javax.ejb.ActivationConfigProperty;

@MessageDriven(activationConfig =
        {
        @ActivationConfigProperty(propertyName="destinationType", propertyValue="javax.jms.Queue"),
        @ActivationConfigProperty(propertyName="destination", propertyValue="java:jboss/exported/TestQueueOne")
        })

public class MyMDB implements MessageListener
{
    public void onMessage(Message message)
    {
        TextMessage textMessage = (TextMessage) message;
        try
       {
            System.out.println("===> MyMDB Received: "+ textMessage.getText());
        }
        catch (JMSException e)
        {
            e.printStackTrace();
        }
    }
}

Step-6). Now we will write a simple “index.jsp” page so that users can send their JMS Messages to the QueueSendServlet. Place this index.jsp file inside “/home/userone/JMS_Demo/src” as following:

<html>
   <head>
          <title> Simple App Level JMS Queue/Topic Demo </title>
   </head>
   <body>
      <center>
         <form action="QueueSendServlet">
            Enter Message to Send: <textarea name="jmsMessage"cols="20" rows="10"> Please Enter Your JMS Message Here !!! </textarea>
            <BR>
                <input type="Submit" value="Send JMS Message" /> 
                <input type="Reset" value="Clear" />
         </form>
      </center>
   </body>
</html>

Step-7). Now we will write a simple “web.xml” file inside “/home/userone/JMS_Demo/src” as following:

  <web-app>
     <servlet>
	<servlet-name>QueueSendServlet</servlet-name>
	<servlet-class>QueueSendServlet</servlet-class>
	<load-on-startup>1</load-on-startup>
     </servlet>
     <servlet-mapping>
	<servlet-name>QueueSendServlet</servlet-name>
	<url-pattern>/QueueSendServlet</url-pattern>
     </servlet-mapping>
  </web-app>

Step-8). As we may want to send some messages to the JMS Queue from a remote Client as well using a Standalone program so we will also write a simple program “StandaloneQueueSend.java”, Place this file as well inside “/home/userone/JMS_Demo/src” as following:

import java.io.*;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.jms.*;
public class StandaloneQueueSend
{
public final static String JNDI_FACTORY="org.jboss.naming.remote.client.InitialContextFactory";

public static void main(String[] args) throws Exception
  {
     if (args.length != 1) {
         System.out.println("Usage: java StandaloneQueueSend URL");
         System.out.println("Example:  java StandaloneQueueSend remote://localhost:4447");
         return;
     } 

     InitialContext ic = getInitialContext(args[0]);
     StandaloneQueueSend demo = new StandaloneQueueSend();

     System.out.println("nnt *** Following shows Looking up a Primitive Datatype located in the JNDI ***");
     demo.init(ic,"TestQueueOne");
  }

private static InitialContext getInitialContext(String url) throws NamingException
     {
        Hashtable env = new Hashtable();
        env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
        env.put(Context.PROVIDER_URL, url);

        //*************** UserName & Password for the Initial Context for JNDI lookup *************************
        // ** Make sure that you create a user using "$JBOSS_HOME/bin/add-user.sh" with 
        // **  following userName & Password and the Rolename="guest"

        env.put(Context.SECURITY_PRINCIPAL, "TestJMSUser");
        env.put(Context.SECURITY_CREDENTIALS, "password");
        InitialContext ic=new InitialContext(env);
        System.out.println("nnt Got InitialContext ic: "+ic);
        return ic;
     }

	private QueueConnectionFactory qconFactory;
	private QueueConnection qcon;
	private QueueSession qsession;
	private QueueSender qsender;
	private Queue queue;
	private TextMessage msg;

	public void init(Context ctx, String queueName)throws NamingException, JMSException
	{
		qconFactory = (QueueConnectionFactory) ctx.lookup("/jms/RemoteConnectionFactory");
		qcon = qconFactory.createQueueConnection("TestJMSUser","password");
		qsession = qcon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
		queue = (Queue) ctx.lookup(queueName);
		qsender = qsession.createSender(queue);
		msg = qsession.createTextMessage();
		qcon.start();

                for(int i=1;i<=5;i++)
                  {
                    String message="Hello Message - "+i+" at "+ new java.util.Date();
                    msg.setText(message);
                    System.out.println("nt Message Sent : "+message); 
                    qsender.send(msg);
                  }
               qcon.close();                
	}

  }

Step-9). Now in order to build and deploy the above application we will write a very simple ant build script “build.xml” inside “/home/userone/JMS_Demo” as following:

<project name="ApplicationLevelJMSDemo" default="deploy">
<property name="jboss.home" value="/NotBackedUp/JBoss_All/jboss-as-7.1.1.Final" />
<property name="jboss.module.dir" value="${jboss.home}/modules" />
<property name="basedir" value="." />
<property name="tmp.dir" value="tmp" />
<property name="output.dir" value="build" />
<property name="src.dir" value="src" />
<property name="war.name" value="ApplicationLevelJMSDemo.war" />

        <path id="jboss.classpath">
             <fileset dir="${jboss.module.dir}">
                 <include name="**/*.jar"/>
             </fileset>
             <fileset dir="${jboss.home}/bin/client">
                 <include name="jboss-client.jar"/>
             </fileset>
        </path>

        <target name="init">
           <delete dir="${output.dir}" />
           <mkdir dir="${output.dir}" />
           <delete dir="${tmp.dir}" />
           <mkdir dir="${tmp.dir}" />
           <mkdir dir="${tmp.dir}/WEB-INF/classes"/>
        </target>

        <target name="build" depends="init">
           <javac srcdir="${src.dir}" destdir="${tmp.dir}/WEB-INF/classes" excludes="StandaloneQueueSend.java"
                  includes="*.java" classpathref="jboss.classpath" />
           <copy todir="${tmp.dir}/WEB-INF/classes">
                <fileset dir="${src.dir}" includes="**/*.java"/>
           </copy>
           <copy file="${src.dir}/index.jsp" tofile="${tmp.dir}/index.jsp"/>
           <copy file="${src.dir}/web.xml" tofile="${tmp.dir}/WEB-INF/web.xml"/>      
           <copy file="${src.dir}/test-jms.xml" tofile="${tmp.dir}/WEB-INF/test-jms.xml"/>      
           <jar jarfile="${tmp.dir}/${war.name}" basedir="${tmp.dir}" compress="true"/> 

           <copy file="${tmp.dir}/${war.name}" tofile="${output.dir}/${war.name}"/>
           <delete includeEmptyDirs="true">
              <fileset dir="${tmp.dir}"/>
           </delete>
        </target>

        <target name="deploy" depends="build">
            <echo message="*******************  Deploying the WAR file ${war.name} *********************" />
            <echo message="********** ${output.dir}/${war.name} to ${jboss.home}/standalone/deployments **********" />
            <copy todir="${jboss.home}/standalone/deployments/">
                <fileset dir="${output.dir}/">
                  <include name="${war.name}"/>
                </fileset>
            </copy>
            <echo message="*******************  Deployed Successfully   *********************" />
        </target>

        <target name="run">
           <delete dir="${tmp.dir}" />
           <mkdir dir="${tmp.dir}" />
           <javac srcdir="${src.dir}" destdir="${tmp.dir}"  includes="StandaloneQueueSend.java" classpathref="jboss.classpath"/>
           <jar jarfile="${output.dir}/${client.jar.name}" basedir="${tmp.dir}" compress="true" />
           <java classname="StandaloneQueueSend" fork="true">
               <classpath>
                  <pathelement location="${tmp.dir}"/>
                  <path refid="jboss.classpath"/>
               </classpath>
               <arg value="remote://localhost:4447"/>
           </java>
           <delete dir="${tmp.dir}" />
        </target>  
</project>

NOTE: The only change in the above file you need to do is to change the “jboss.home” directory path in the second line of the above script to point to your own JBoss AS7 directory.

Step-9). Now before running your ANT script to build and deploy the above webapplication you should have the ANT as well as JAVA set in the $PATH variable of the Shell / command prompt as following:

For Unix Based OS:
export PATH=/home/userone/jdk1.6.0_21/bin:/home/userone/org.apache.ant_1.6.5/bin:$PATH

For Windows Based OS:
set PATH=C:/jdk1.6.0_21/bin;C:/org.apache.ant_1.6.5/bin;%PATH%

Step-10). Now once the PATH is set In the command/Shell prompt you can move inside the directory “/home/userone/JMS_Demo” and then run the ant to build & deploy the webapplication. by running the command “ant”

[userone@localhost JMS_Demo]$ ant 
ant
Buildfile: /home/userone/JMS_Demo/build.xml

init:
   [delete] Deleting directory /home/userone/JMS_Demo/build
    [mkdir] Created dir: /home/userone/JMS_Demo/build
    [mkdir] Created dir: /home/userone/JMS_Demo/tmp
    [mkdir] Created dir: /home/userone/JMS_Demo/tmp/WEB-INF/classes

build:
    [javac] /home/userone/JMS_Demo/build.xml:29: warning: 'includeantruntime' was not set, defaulting to build.sysclasspath=last; set to false for repeatable builds
    [javac] Compiling 2 source files to /home/userone/JMS_Demo/tmp/WEB-INF/classes
     [copy] Copying 3 files to /home/userone/JMS_Demo/tmp/WEB-INF/classes
     [copy] Copying 1 file to /home/userone/JMS_Demo/tmp
     [copy] Copying 1 file to /home/userone/JMS_Demo/tmp/WEB-INF
     [copy] Copying 1 file to /home/userone/JMS_Demo/tmp/WEB-INF
      [jar] Building jar: /home/userone/JMS_Demo/tmp/ApplicationLevelJMSDemo.war
     [copy] Copying 1 file to /home/userone/JMS_Demo/build

deploy:
     [echo] *******************  Deploying the WAR file ApplicationLevelJMSDemo.war *********************
     [echo] ********** build/ApplicationLevelJMSDemo.war to /home/userone/jboss-as-7.1.1.Final/standalone/deployments **********
     [copy] Copying 1 file to /home/userone/jboss-as-7.1.1.Final/standalone/deployments
     [echo] *******************  Deployed Successfully   *********************

BUILD SUCCESSFUL
Total time: 2 seconds

Step-11). Now you will able to access your application. “http://localhost:8080/ApplicationLevelJMSDemo/index.jsp” then you will be able to send some mesages from this page.

Sending JMS Messages using Standalone Client.

Step-12). Make sure that you create a new User using “$JBOSS_HOME/bin/add-user.sh” where:

UserName: TestJMSUser
Password: password
RoleName: guest

./add-user.sh 

What type of user do you wish to add? 
 a) Management User (mgmt-users.properties) 
 b) Application User (application-users.properties)
(a): b

Enter the details of the new user to add.
Realm (ApplicationRealm) : 
Username : TestJMSUser
Password : password
Re-enter Password : password
What roles do you want this user to belong to? (Please enter a comma separated list, or leave blank for none) : guest
About to add user 'TestJMSUser' for realm 'ApplicationRealm'
Is this correct yes/no? yes
Added user 'TestJMSUser' to file '/home/userone/jboss-as-7.1.1.Final/standalone/configuration/application-users.properties'
Added user 'TestJMSUser' to file '/home/userone/jboss-as-7.1.1.Final/domain/configuration/application-users.properties'
Added user 'TestJMSUser' with roles guest to file '/home/userone/jboss-as-7.1.1.Final/standalone/configuration/application-roles.properties'
Added user 'TestJMSUser' with roles guest to file '/home/userone/jboss-as-7.1.1.Final/domain/configuration/application-roles.properties'

Step-12). Now we will need to run the ANT script in order to compile and run the “StandaloneQueueSend.java” program to send 5 Messages to the JMS Queue as following : (just type ant run)

ant run
Buildfile: /home/userone/JMS_Demo/build.xml

run:
    [mkdir] Created dir: /home/userone/JMS_Demo/tmp
    [javac] Compiling 1 source file to /home/userone/JMS_Demo/tmp
    [javac] Note: /home/userone/JMS_Demo/src/StandaloneQueueSend.java uses unchecked or unsafe operations.
    [javac] Note: Recompile with -Xlint:unchecked for details.
      [jar] Building jar: /home/userone/JMS_Demo/build/${client.jar.name}
      log4j:WARN No appenders could be found for logger (org.jboss.logging).
      log4j:WARN Please initialize the log4j system properly.
      log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
      
      
      	 Got InitialContext ic: javax.naming.InitialContext@5f49d886
      
      
      	 *** Following shows Looking up a Primitive Datatype located in the JNDI ***
      
      	 Message Sent : Hello Message - 1 at Fri Jun 29 01:11:49 IST 2012
      
      	 Message Sent : Hello Message - 2 at Fri Jun 29 01:11:49 IST 2012
      
      	 Message Sent : Hello Message - 3 at Fri Jun 29 01:11:49 IST 2012
      
      	 Message Sent : Hello Message - 4 at Fri Jun 29 01:11:49 IST 2012
      
      	 Message Sent : Hello Message - 5 at Fri Jun 29 01:11:49 IST 2012
   [delete] Deleting directory /home/userone/JMS_Demo/tmp

BUILD SUCCESSFUL
Total time: 5 seconds

Some Issues You Might Face

Issue-1). Whiel running / writing your Remote JMS Client code you may see the following exception:

    Exception in thread "main" javax.jms.JMSSecurityException: Unable to validate user: null
    	at org.hornetq.core.protocol.core.impl.ChannelImpl.sendBlocking(ChannelImpl.java:312)
    	at org.hornetq.core.client.impl.ClientSessionFactoryImpl.createSessionInternal(ClientSessionFactoryImpl.java:780)
    	at org.hornetq.core.client.impl.ClientSessionFactoryImpl.createSession(ClientSessionFactoryImpl.java:279)
    	at org.hornetq.jms.client.HornetQConnection.authorize(HornetQConnection.java:601)
    	at org.hornetq.jms.client.HornetQConnectionFactory.createConnectionInternal(HornetQConnectionFactory.java:684)
    	at org.hornetq.jms.client.HornetQConnectionFactory.createQueueConnection(HornetQConnectionFactory.java:131)
    	at org.hornetq.jms.client.HornetQConnectionFactory.createQueueConnection(HornetQConnectionFactory.java:126)
    	at StandaloneQueueSend.init(Unknown Source)
    	at StandaloneQueueSend.main(Unknown Source)
    Caused by: HornetQException[errorCode=105 message=Unable to validate user: null]

In order to resolve above Error you will need to make sure that you Create an ApplicationRealm User using “$JBOSS_HOME/bin/add-user.sh” and the User should have the “guest” role. Also You should pass the same username and password while creating the “QueueConnection” as following:

queueConnection = queueConnectionFactory.createQueueConnection(“TestJMSUser”,”password”);

.
.
Thanks
Middleware Magic Team 🙂


Copyright © 2010-2012 Middleware Magic. All rights reserved. |