Sunday, July 7, 2013

Remote JMS Messaging with HornetQ on JBoss AS - Wildfly

When working with JEE as developers and/or administrators we have used JMS at some point. Most of us are very familiar with using Queues and ConnectionFactories provided by the JEE container in applications residing within the same container.

The posts detailing the process of connecting to and messaging a remote queue often do not cover all the methods of remote messaging.

With this post I would like to summarize at least three different ways of remote messaging that I am aware of:

  1. Messaging a remote queue by a simple client that is not deployed on any JBoss AS
  2. Messaging a remote queue by an application (WAR) that is deployed on a JBoss AS
  3. Messaging a remote queue thru a JMS bridge. The message is sent to a local queue on Server 1 which then gets forwarded to the remote queue on Server 2, with local queue and remote queue participating in a JMS Bridge

Prerequisites

For the purpose of executing the instructions and watching the messaging as a live demo, we need to have at least the following:

  • Two running instances of JBoss AS (Wildfly), lets call them Server 1 and Server 2
  • On Server 1:
    • Local queues: LocalServer1Q, JMSBridgeSourceQ
    • JMS Bridge: simple-jms-brdige
    • A pooled-connection-factory using outbound JCA adapter
  • On Server 2:
    • Local queues: LocalServer2Q, JMSBridgeTargetQ

Visit my GitHub project here to download all the raw materials for the demo and to get started right away. Click on Download Zip to download the entire project as a zip file. After unzipping the project to the desired location follow the Pre-Installation and Command-Line Installation instructions. The installation process should take care of all the prerequisites listed above using JBoss CLI.




Case 1 - Remote messaging with a simple client

In this scenario we will be using a command-line client attempting to send message to the queue: LocalServer1Q on Server 1.

For any Java based client to be able to do that, it requires:

  1. The jar file jboss-client.jar in the classpath. Found usually under $JBOSS_HOME/bin/client
  2. Create a InitialContext object with properties as shown below. Key thing to note below is the URL format and port. The URL format should be of the form remote://<Remote IP>:PORT. The default remoting port is 4447. If you have started the server with a port-offset of 100 for example, then this port would be 4547 (4447+100).
        Properties props = new Properties();
        props.put(Context.INITIAL_CONTEXT_FACTORY,"org.jboss.naming.remote.client.InitialContextFactory");
    
        // The URL below should point to the your instance of Server 1, if no
        // port offset is used for Server 1 the port can remain at 4447
        props.put(Context.PROVIDER_URL, "remote://192.168.1.20:4447");
    
        // Credentials are required when the security is enabled (default behavior) on the HornetQ server 
        props.put(Context.SECURITY_PRINCIPAL, "USERNAME");
        props.put(Context.SECURITY_CREDENTIALS, "PASSWORD");
    
        InitialContext ic = new InitialContext(props);
    
  3. Use the InitialContext object to lookup the Queue and the ConnectionFactory as shown below:
        remoteQueueCF = (ConnectionFactory) ic.lookup("jms/RemoteConnectionFactory");
        remoteQueue = (Queue) ic.lookup("jms/queues/LocalServer1Q");
    
        // You would have to provide the same username/password combination to create a connection
        // when the security is enabled on the HornetQ server
        remoteQueueConnection = remoteQueueCF.createConnection("USERNAME","PASSWORD");
    

Important Note :

For the remote client to be able to see the Queue and the ConnectionFactory, both should have JNDI entries that start with java:jboss/exported. The following snippet shows the usage. When looking up in the code however, you just need to pass what comes after java:jboss/exported in the JNDI entry (as shown above).

   ...
   <connection-factory name="RemoteConnectionFactory">
       <connectors>
           <connector-ref connector-name="netty"/>
       </connectors>
       <entries>
           <entry name="java:jboss/exported/jms/RemoteConnectionFactory"/>
       </entries>
       <ha>true</ha>
       <block-on-acknowledge>true</block-on-acknowledge>
       <retry-interval>1000</retry-interval>
       <retry-interval-multiplier>1.0</retry-interval-multiplier>
       <reconnect-attempts>-1</reconnect-attempts>
   </connection-factory>
   ...
   <jms-destinations>
       <jms-queue name="LocalServer1Q">
           <entry name="queue/LocalServer1Q"/>
           <entry name="java:jboss/exported/jms/queues/LocalServer1Q"/>
       </jms-queue>   
   ...

Additional Reference:



Case 2 - Remote messaging with a local pooled-connection-factory

In this case we will be attempting to send a message to the remote queue (LocalServer2Q) but using a local pooled-connection-factory on Server 1. This way, the client doesn't need to know where the remote queue is located, this would have been taken care of by the administrator when he/she setup the pooled-connection-factory.

Why is this kind of connection so special ? Following is the entry straight from the official documentation:

There is also a pooled-connection-factory which is special in that it leverages the outbound adapter of the HornetQ JCA Resource Adapter. It is also special because:
  1. It is only available to local clients, although it can be configured to point to a remote server.
  2. As the name suggests, it is pooled and therefore provides superior performance to the clients which are able to use it. The pool size can be configured via the max-pool-size and min-pool-size attributes.
  3. It should only be used to send (i.e. produce) messages.
  4. It can be configured to use specific security credentials via the user and password attributes. This is useful if the remote server to which it is pointing is secured.

Local Server (Server 1) Side Setup

Following are the steps to prepare the local server:

  1. Add a new outbound socket-binding entry as shown and highlighted below. Make sure it is pointing to the remote server's IP and messaging port. Since I had my Server 2 run with a port offset of 100, the messaging port is at 5545 (5445 + 100)
            ...
            <outbound-socket-binding name="mail-smtp">
                <remote-destination host="localhost" port="25"/>
            </outbound-socket-binding>
            
            <outbound-socket-binding name="messaging-remote">
                <remote-destination host="192.168.1.20" port="5545"/>
            </outbound-socket-binding>
        </socket-binding-group>   
    
  2. Add a new netty connector that uses the above socket binding
        ... 
        <connectors>
            <netty-connector name="netty" socket-binding="messaging"/>
            <netty-connector name="netty-throughput" socket-binding="messaging-throughput">
                <param key="batch-delay" value="50"/>
            </netty-connector>
            <netty-connector name="netty-remote" socket-binding="messaging-remote"/>
            <servlet-connector name="servlet" host="default-host" socket-binding="http"/>
            <in-vm-connector name="in-vm" server-id="0"/>
        </connectors>  
        ...
    
  3. Add a new pooled-connection-factory that uses the newly added netty-remote connector
            ...
            <pooled-connection-factory name="hornetq-ra-remote">
                <transaction mode="xa"/>
                <connectors>
                    <connector-ref connector-name="netty-remote"/>
                </connectors>
                <entries>
                    <entry name="java:/RemoteJmsXA"/>
                </entries>
            </pooled-connection-factory>
        </jms-connection-factories> 
        ...
    

Use of pooled-connection-factory in Code

  1. Lookup the connection factory as shown below using the @Resource annotation
        @Resource(mappedName = "java:/RemoteJmsXA")
        private ConnectionFactory cf;
    
  2. Instead of looking up a queue, which you cannot since we are talking about a remote queue , create a queue identity as shown below
        Connection connection = cf.createConnection();
        Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
    
        /*
         * Important note here that you shouldn't try to lookup the remote
         * server queue from here as you do not need to. You are just
         * creating a queue identity here, not a physical queue.
         * http://docs.oracle.com/javaee/6/api/javax/jms/QueueSession.html#createQueue(java.lang.String)
         */
         Queue queue = session.createQueue("LocalServer2Q");
    

Additional References



Case 3 - Remote messaging with a JMS Bridge

In this scenario:

  • We define a JMS bridge between queues JMSBridgeSourceQ on Server 1 and JMSBridgeTargetQ on Server 2
  • The bridge definition goes onto Server 1
  • The clients, local to Server 1, don't necessarily know the existence of a bridge when they send messages to the local queue JMSBridgeSourceQ. The bridge transparently *copies* the message over to the target queue JMSBridgeTargetQ
  • The copy is only One-Way from Source to Target

Defining the JMS Bridge

The following snippet covers the XML entry for the bridge

     </hornetq-server>
     <jms-bridge name="simple-jms-bridge">
         <source>
             <connection-factory name="ConnectionFactory"/>
             <destination name="queue/JMSBridgeSourceQ"/>
         </source>
         <target>
             <connection-factory name="jms/RemoteConnectionFactory"/>
             <destination name="jms/queues/JMSBridgeTargetQ"/>
             <context>
                 <property key="java.naming.factory.initial" value="org.jboss.naming.remote.client.InitialContextFactory"/>
                 <property key="java.naming.provider.url" value="remote://192.168.1.20:4547"/>
             </context>
         </target>
         <quality-of-service>AT_MOST_ONCE</quality-of-service>
         <failure-retry-interval>1000</failure-retry-interval>
         <max-retries>-1</max-retries>
         <max-batch-size>10</max-batch-size>
         <max-batch-time>100</max-batch-time>
     </jms-bridge>
 </subsystem>

Few observations about the JMS Bridge entry in standalone-full-ha.xml

  1. The entry is below the <hornetq-server>
  2. To connect to the JMSBridgeSourceQ queue, local connection factory ConnectionFactory can be used
  3. To be able to connect to the JMSBridgeTargetQ, we would need to use the jms/RemoteConnectionFactory
  4. And the only way we will be able to lookup the jms/RemoteConnectionFactory is by defining the context of the lookup. The entries here are similar to the remoting lookup we saw in Case 1

Using the JMS Bridge in Code

Using a JMS bridge in code is no different from sending a message to a local queue hence you would find the instructions familiar

  1. Locate the connection factory and the queue by looking them up with @Resource annotation
        @Resource(mappedName = "java:/ConnectionFactory")
        private ConnectionFactory cf;
    
        @Resource(mappedName = "java:/queue/JMSBridgeSourceQ")
        private Queue queue;
    
  2. Use the connection factory and queue as you would normally when setting up a connection
        Connection connection = cf.createConnection();
        Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
    
        MessageProducer producer = session.createProducer(queue);
    

Additional References




Consuming from a Remote Queue

So far we have seen how to *send* messages to be remote queue. Lets now see how to consume from remote queues.

Standalone Java Client

This scenario is similar to Case #1, except here we are attempting to consume from the remote queue. You can use the source code in RemoteQueueInteractor.java as an example. It has methods both for sending and receiving the messages connecting to a remote queue.

Remote MDB Client


In a JEE setting the recommended method to consume a message is thru the use of an Message Driven Bean (MDB). A MDB can be configured to take in values for the attribute activationConfig. Below is the sample use of a MDB connecting to a remote queue.
@MessageDriven(name = "RemoteMDB", activationConfig = { 
     @ActivationConfigProperty(propertyName="destinationType", propertyValue = "javax.jms.Queue"),
     @ActivationConfigProperty(propertyName="destination", propertyValue = "testQueue"),
     @ActivationConfigProperty(propertyName="connectorClassName", propertyValue = "org.hornetq.core.remoting.impl.netty.NettyConnectorFactory"),        
     @ActivationConfigProperty(propertyName="connectionParameters", propertyValue = "host=remoteHost;port=5445")})

Note that if you choose to use the embedded MDB in place in the downloaded project, any messages sent to LocalServer2Q will be consumed right away.

Additional References




All References:

  1. Messaging Configuration for JBoss AS 7.1
  2. Middleware Magic : How to connect to a remote queue in JBoss AS 7 ?
  3. JBoss Community post related to MDB

Sunday, June 30, 2013

Provisioning JBoss Application Server as a RHQ bundle

** Click here to jump to references/introductions to terms and technologies **

Need for Application Provisioning

Rather than coming up with my interpretation of Application Provisioning and potentially not getting it quite right, I have decided to present it to you right from the JBoss Operations Network official documentation

Provisioning is a way that administrators can define and control applications, from development to production. The ultimate effect of the provisioning system is simplifying how applications are deployed. Administrators can control which versions of the same application are deployed to different resources, from different content sources, within the same application definition (the bundle definition). Resources can be reverted to different versions or jump ahead in deployment.

Obviously there is more detail provided in the documentation if you choose to read further. For now, we will next look at when you might use a RHQ bundle for provisioning.


When to use a RHQ Bundle for Provisioning ?

Without JON


You may choose to use a RHQ bundle for provisioning when :
  • You need portability of the bundle. Bundles come in as .zip files, a format that is well understood by various OS platforms
  • You have an application that can (or needs to) be deployed on mixed OS platforms
  • You have to externalize the deployment properties to a file that can be customized per destination at deploy time
  • You have (optional) pre-install and post-install tasks that you would like to execute on the destination before and after bundle deployment

With JON


In addition to all of the above reasons, one can use RHQ bundle with JON for following additional reasons:
  • You would like to deploy to a group of platforms at once
  • You would like to track the versions of bundles deployed thus far and have the ability to revert to any specific version at any time
  • You would like to be asked to provide values for the deployment properties prior to the deployment

Lets now take a look at the bundle I put together for our demo! The goal is to be able to provision the JBoss community Wildfly application server and start it post-deployment, all automatically.


Provisioning JBoss Application Server as a RHQ bundle

Setup

Follow the instructions step-by-step below to setup the bundle contents
  1. Go to Vijay's GitHub site here
  2. Click on Download ZIP and download the zip file. Unzip the file to an empty/clean folder of your choice
  3. While the browser window is pointing at the same site, read and execute the Pre-Installation Setup instructions in the Readme document
Following are images that show contents of the folder at various stages:

Extract from GitHub and initial unzip

Folder snapshot post-completion of steps of "Pre-Installation Setup"

Command-Line Installation

Follow the instructions step-by-step below to deploy the from command-line:
  1. Go to Vijay's GitHub site here
  2. Read and execute the Command-Line Installation instructions in the Readme document
  3. If everything goes well, you should see the server start-up as shown in the image below
Following are snapshots of the folder and command execution:

Creating deployment destination and defining deploy-time properties

Checkout of app server post command-line installation

Bundle Installation via JBoss Operations Network


Follow the instructions step-by-step below to upload/deploy the bundle from JON:
  1. Package the working demo folder into in deployable RHQ bundle

    Creating the RHQ bundle (.zip) for deployment via JON

  2. Assuming you have JON installed, make sure you made groups of compatible platforms that are potential destinations (Inventory tab)

    Creating a group of compatible destinations

  3. Create a new bundle (Bundle tab) by clicking on the the New button at the bottom of the window in the center. Once the zip file is uploaded, click thru next screens that ends with clicking on a Finish button

    Creation of a bundle in JON

  4. Select the bundle and click on Deploy button to start the deployment process. As a first step you would have to define a Destination, refer to the image below:

    Bundle Deployment - Assigning a Destination

  5. After choosing the default version of 1.0 in the next screen, move on to the screen where you need to fill in the deploy-time property values

    Bundle Deployment - Providing values for the deploy-time properties

  6. After successfully deploying the bundle on a platform (Windows) that is different from the platform (RHEL), verify both the destination deployment folder and the server start

    Folder snapshot post bundle deployment via JON - Windows Destination 

    Checkout of app server post bundle installation via JON

  7. Peek into JON for the view post-bundle deployment. To revert to a different version or to purge/delete the bundle from a destination you would click on one of the buttons shown in the image below

    Post-Bundle deployment view in JON

  8. To appreciate the value of the readymade scripts as part of the bundle deployment run the shutdown script as shown below and see the server shutdown

    Shutdown the server thru the ready-made shutdown script

References

Sunday, June 23, 2013

Ways of connecting to JBoss EAP/AS 7 using JMX



Prerequisites:

In this article we would assume that you have the latest installation/setup of :

  • JDK 1.7 - JAVA_HOME environment variable points to the installation
  • JBoss EAP 6 or AS7 installed - $EAP_HOME environment variable maps to the folder jboss-eap-6.X

Since I am using Windows, below is how the environment variables are configured on my system:

C:\>echo %JAVA_HOME%
C:\Java\jdk1.7.0_25

C:\>echo %EAP_HOME%
C:\EAP-6.1.0\jboss-eap-6.1

For the sake of simplicity we would also assume that you have started the out-of-the-box EAP/AS server in standalone mode

C:\Users\VijayBhaskar>cd %EAP_HOME%
C:\EAP-6.1.0\jboss-eap-6.1>cd bin
C:\EAP-6.1.0\jboss-eap-6.1\bin>standalone.bat

If the link http://localhost:9990 brings up a login window we are ready to go ...



Using JBoss EAP/AS provided JConsole

The EAP/AS packaged JConsole lets us use all the features of the JDK provided JConsole and in addition you will be provided the JBoss CLI (Command Line Interface) as the last tab of the console to manipulate the runtime settings of the server. So, follow the instructions below to be access the JBoss CLI as well thru JConsole.

===Note ===

For this to work correctly ensure the following:
  • There aren't any spaces in the path pointed to by the JAVA_HOME variable, as this variable is later used in the script jconsole.bat in the lines containing %EAP_HOME%\bin
  • When using jconsole locally, I had previously run into a situation when the server was started as SYSTEM user and opening the JConsole as any other user wouldn't list the JBoss Server process and its PID. "Both JConsole and the application must by executed by the same user. The management and monitoring system uses the operating system's file permissions", according to official documentation of JConsole.

Open up the JConsole provided with the EAP/AS installation as shown below

C:\>cd %EAP_HOME%\bin 
C:\EAP-6.1.0\jboss-eap-6.1\bin>jconsole.bat

Local Process Connection - Simplest Way

The simplest way to get into the guts of the JBoss server's MBeanServer is to select the Local Process in the New Connection window and double-click on the line item that starts with jboss-modules. You would go for this option if you are logged on the machine running the standalone server

EAP/AS provided jconsole window

Remote Process Connection - The Other Way

The other way to to provide a URL for the remote process of the form: service:jmx:remoting-jmx://{host_name}:{port}.

You would choose this option if the server was started on a remote machine such that the management port binds to the remote IP (that is pingable from the machine where you are running JConsole from).

You may use the command : telnet IP PORT to verify reachability from the host running JConsole .

In the example below the management port of the remote server is bound to the IP address: 192.168.1.20

C:\EAP-6.1.0\jboss-eap-6.1\bin>standalone.bat --server-config=standalone-full-ha.xml -b 192.168.1.20 -Djboss.bind.address.management=192.168.1.20

The following image displays such a usage:




Using the Oracle JDK provided JConsole/JVisualVM


Oracle JDK provided JConsole

The behavior of directly using jconsole.exe provided by Oracle JDK, is very similar to its working described above when used with wrapper jconsole.bat.

Since we are not adding additional JBoss specific JARs to the classpath before launching it:

  • You can connect to the server's MBeans only when the jconsole.exe is run on the same physical host as that of the standalone server
  • We miss the JBoss CLI functionality. You can rely on the jboss-as mbeans exposed in the last tab for many of the attributes/operations you might be interested in executing against the server.

C:\>cd %JAVA_HOME%\bin
C:\Java\jdk1.7.0_25\bin>jconsole.exe

Oracle JDK provided jvisualvm

The behavior of Oracle's jvisualvm is similar to jconsole's when used directly and without adding the jars required by the remoting-jmx to the classpath. That is:

  • To be able to connect to server's MBeans it has to run local to the server
  • No JBoss CLI functionality

If however, there is an existing JBoss EAP/AS installation we could leverage it and make JMX connections :

  • Locally and Remotely
  • With JBoss CLI functionality
Follow the steps listed below to be able to execute jvisualvm.exe and use the remoting-jmx functionality supported by JBoss to connect remotely:

  1. Copy the jconsole.bat in the %EAP_HOME%\bin into the same folder and call it jvisualvm.bat
  2. Edit the line that launches jconsole.exe and rename it jvisualvm.exe, as shown below
    // Before 
    "%JAVA_HOME%\bin\jconsole.exe" -J"-Djava.class.path=%CLASSPATH%"
    
    // After
    "%JAVA_HOME%\bin\jvisualvm.exe" "-cp:a" "%CLASSPATH%"
    
  3. Run the batch file jvisualvm.bat at command prompt
  4. Right click on the Remote and add the remote IP and optionally give it a name
  5. Now right-click on the new entry under Remote and choose Add JMX Connection
  6. In the new dialog box provide the connection in the form: service:jmx:remoting-jmx://{host_name}:{port}
  7. Before choosing Ok, provide the username and password for the connection as the remote JMX connection is secure by default

Following images should help with running jvisualvm:

Adding a remote host to jvisualvm
Adding a VM to monitor on the host
Properties of remote VM when connected

References:

  1. Using JConsole to connect to JMX on AS7
  2. Connecting VisualVM with a remote JBoss AS 7 / EAP6 JVM process