13

9 Steps to integrate a remote HornetQ with Glassfish 3.1

I recently found myself browsing and searching the web for long hours just for finding an answer for an apparently simple configuration issue: How to configure Glassfish 3.1 to receive messages from and send messages to a standalone / remote instance of the HornetQ Messaging Platform. I didn’t find any tutorial, how-to, or any other useful resource that gave me the answers I needed. Everything was about how to embed an instance in Glassfish (running HornetQ within the Applicationserver) or how to configure JBoss + HornetQ. But nothing up to date about Glassfish as a client for a HornetQ standalone server…

At the end I scanned loads of different sources, modified what I found and tried all options since I found myself – some hours after midnight – sending and receiving simple Messages in Glassfish using a standalone HornetQ. Since there is no effective description about this simple task (at least nothing I found) – and I know that finding out by oneself is quite time consuming – I share the steps I went through to configure my situation.

Maybe you know better ways or have answers to my questions I will put at the end of this article. I really would appreciate any comments or hints….

Step 1: Adding libraries to your Glassfish installation.

After you installed Glassfish and HornetQ on your system(s), you need to copy a group of libraries to the lib folder of your Glassfish installation:

  • hornetq-core-client-2.2.7.Final.jar (Download)
  • hornetq-jms-client-2.2.7.Final.jar (Download)
  • hornetq-logging-2.2.7.Final.jar (Download)
  • netty-3.2.5.Final.jar (Download)

Don’t forget to restart your server afterwards.

Step 2: Deploy the HornetQ Resource Adapter as an application in Glassfish

First locate the file hornetq-ra.rar in the lib directory of your HornetQ installation. This file can be deployed in Glassfish directly using the Admin-Console (usually http://{glassfish-host}:4848). Open the Applications View and choose Deploy as the option. Now you can upload the file to deploy it as a connector application. The application should be listed in the Application View afterwards.

Step 3: Edit the Resource Adapter Configuration (ra.xml)

In order to communicate with a remote or standalone instance of HornetQ you need to modify the ra.xml in the directory of the delpoyed application:
{GLASSFISH_HOME}/domains/{domain_name}/applications/hornetq-ra/META-INF/ra.xml

You need to do several modifications:

First of all change the ConnectorClassName config property. By default this is is set to InVMConnectorFactory which just means that the client connects to an embedded HornetQ (running in the same virtual machine.


<config-property>
<description>
The transport type....
</description>
<config-property-name>ConnectorClassName</config-property-name>
<config-property-type>java.lang.String</config-property-type>
<config-property-value>
org.hornetq.core.remoting.impl.netty.NettyConnectorFactory
</config-property-value>
</config-property>

The second modification regards the ConnectionParameter config property. Make sure to set the right host name or IP and the right port. Note that these parameters are set as a semicolon separated list of properties.


<config-property>
<description>The transport configuration.</description>
<config-property-name>ConnectionParameters</config-property-name>
<config-property-type>java.lang.String</config-property-type>
<!-- Put your values here... -->
<config-property-value>host=127.0.0.1;port=5445</config-property-value>
</config-property>

If – and that should be the case – your Message Server is configured using a username and password for accessing a queue you also should set these credentials within the ra.xml file:


<config-property>
<description>The user name used to login to the JMS server</description>
<config-property-name>UserName</config-property-name>
<config-property-type>java.lang.String</config-property-type>
<config-property-value>test</config-property-value>
</config-property>
<config-property>
<description>The password used to login to the JMS server</description>
<config-property-name>Password</config-property-name>
<config-property-type>java.lang.String</config-property-type>
<config-property-value>test</config-property-value>
</config-property>

Now restart your Glassfish. The deployed resource adapter should use the modified settings now.

Step 4: Create a managed thread pool

The next steps are quite easy. Just open your shell and move to {GLASSFISH_HOME}/bin/. Note that you might be logged in as root or use sodu for the following commands.

To create a managed thread pool type into your shell:

user@computer:$ ./asadmin create-threadpool hrntq-ra-thread-pool

Choose the name (hrntq-ra-thread-pool) carefully because you will need it to connect several resources to each other.

Step 5: Create the managed resource adapter config

Next, create the managed resource adapter config. I am not sure how this differs from the ra.xml described earlier. But for me it doesn’t work to have just one of both configs…

user@computer:$ ./asadmin create-resource-adapter-config
--threadpoolid hrntq-ra-thread-pool --property "ConnectorClassName=org.hornetq.core.remoting.impl.
netty.NettyConnectorFactory:ConnectionParameters=host\\=127.0.0.1;
port\\=5445:UserName=test:Password=test" hornetq-ra

Whatsoever … You have to set the same properties as in ra.xml: The ConnectorClassName, the ConnectionParameters and the UserName and Password. With the threadpoolid option set to the name you used in Step 4 you tell this config that it should use your custom thread pool. Again, this Resource also needs to get a proper name.

Step 6: Create the Connector Pool

Now we create a ConnectorPool configured to use the HornetQ Resource Adapter. This provides the basis for creating multiple connection resources that can be injected into your managed JEE application.

user@computer:$ ./asadmin create-connector-connection-pool --raname hornetq-ra --connectiondefinition org.hornetq.ra.HornetQRAConnectionFactory --property UseTryLock=0:SessionDefaultType=
javax.jms.Queue:UserName=test:Password=test --transactionsupport XATransaction hornetq-ra-connection-pool

Step 7: Create the Connector Resource

user@computer:$ ./asadmin create-connector-resource --poolname hornetq-ra-connection-pool --description "HornetQ Connection Resource" hornetq-ra

This one can be injected into your application using the @Resource annotation – see Step 8 for an example.

Step 8: Create an EJB that sends Messages to the Queue

Finally I will give you an really simple example of how to use the configured Connection Resource within your managed JEE item. As mentioned before you just have to use the @Resource annotation and the name to link to the resource you configured in Step 6. I did not find out how to wrap a HornetQQueue into a configured and managed module. That’s why I instantiate it directly in the code below. But I’m sure that there must be ways to give the responsibility for this to the Appserver. However – the way showed here works!


/**
* Example EJB that connects to a HornetQQue named "testqueue"
* to send a TextMessage with a given String value.
*/
@Stateless
class SimpleMsgSender {
@Resource(mappedName="hornetq-ra")
private QueueConnectionFactory qcf;

public void send(String message) {
QueueConnection queueCon = null;
try {
queueCon = this.qcf.createQueueConnection("test","test");
QueueSession queueSession = queueCon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
Queue queue = new HornetQQueue("Requests");
QueueSender sender = queueSession.createSender(queue);
Message msg = queueSession.createTextMessage(message);
sender.send(msg);
} finally {
if (queueCon != null) {
queueCon.close();
}
}
}
}

Step 9: Create a MDB (MessageDrivenBean) to Receive Messages from the Queue

Sending messages is just one side of the medal. Receiving messages could be also done using managed resource as in Step 8. But then it’s the responsibility of the application to poll the queue. In JEE we have a way to describe a receiving component as a listener to a specific JMS queue. This is called a MessageDrivenBean. The problem here: You cannot use the configured resources described above. I didn’t find any way to connect these resources to a MessageDrivenBean. However, you can use the sun-ejb.xml in the applications META-INF direcotry to configure the MDB to connect to the remote HornetQ server…


<enterprise-beans>
<ejb>
<ejb-name>simpleReceiver</ejb-name>
<jndi-name>simpleReceiver</jndi-name>
<mdb-resource-adapter>
<resource-adapter-mid>hornetq-ra</resource-adapter-mid>
<activation-config>
<activation-config-property>
<activation-config-property-name>
destinationType
</activation-config-property-name>
<activation-config-property-value>
javax.jms.Queue
</activation-config-property-value>
</activation-config-property>
<activation-config-property>
<activation-config-property-name>
destination
</activation-config-property-name>
<activation-config-property-value>
/queue/Requests
</activation-config-property-value>
</activation-config-property>
<activation-config-property>
<activation-config-property-name>
ConnectorClassName
</activation-config-property-name>
<activation-config-property-value>
org.hornetq.core.remoting.impl.netty.NettyConnectorFactory
</activation-config-property-value>
</activation-config-property>
<activation-config-property>
<activation-config-property-name>
ConnectionParameters
</activation-config-property-name>
<activation-config-property-value>
host=127.0.0.1;port=5445
</activation-config-property-value>
</activation-config-property>
<activation-config-property>
<activation-config-property-name>
User
</activation-config-property-name>
<activation-config-property-value>
test
</activation-config-property-value>
</activation-config-property>
<activation-config-property>
<activation-config-property-name>
Password
</activation-config-property-name>
<activation-config-property-value>
test
</activation-config-property-value>
</activation-config-property>
</activation-config>
</mdb-resource-adapter>
</ejb>
...
</enterprise-beans>

With this configuration you can implement a lightweight MessageListener which will connect to the HornetQ just by using the @MessageDriven annotation and the mappedName attribute to link it to the XML configuration.


/**
* Example MessageDrivenBean that receives TextMessages
* and logs them.
*/
@MessageDriven(mappedName="simpleReceiver")
public class SimpleMessageReceiver implements MessageListener {

private static Logger logger = Logger.getLogger(SimpleMessageReceiver.class);

public void onMessage(Message message) {
TextMessage m = (TextMessage) message;
try {
String body = m.getText();
logger.info(body);
} catch (JMSException exc) {
logger.warning("Some Error in receiving JMS Message: " exc.getMessage());
}
}
}

As you see, it’s quite simple to configure a Glassfish to make your application a HornetQ client. But still, there are still open questions and some need for optimization. I am sure that I am not the only one out there who would appreciate any help from you, if you have any experience with this topic :) As said in my introduction, I will start the discussion by giving my questions:

  • How can I configure a managed queue resource to be linked to a HornetQQueue using the Resource Adapter?
  • How can I use the config resource in a MessageDrivenBean?
  • Are there any other pitfalls to consider when using HornetQ within a Glassfish App?

Cheers!

  1. Maurizio says:

    Thanks for posting this!
    Where do you get your sender from in the SimpleMsgSender class? From a jndi lookup?

    Maurizio

  2. Bastian says:

    Thanks for your comment Maurizio. You just found a “bug” in my post ;) I forget two line in copy-pasting from Eclipse to WP:

    Queue queue = new HornetQQueue(“Requests”);
    QueueSender sender = queueSession.createSender(queue);

    As you see, you create it from the session instance by passing a HornetQQueue instance that is created directly using the constructor.

    I already fixed this in the code snippet….

  3. Maurizio says:

    Hi Bastian,

    Thanks for the clarification

    Your MDB should be declared public:
    [EJB3.1 spec, section 5.6.2] Message driven bean implementation class MUST be public, not abstract and not final – SimpleMessageReceiver won’t be considered as a message driven bean, since it doesn’t meet that requirement

    Maurizio

  4. Maurizio says:

    Bastian,

    A few more bugs:

    - The send method should return a boolean
    - There are no connections between sun-ejb.xml and your MDB :

    HornetReactor
    HornetReactor
    @MessageDriven(mappedName=”simpleReceiver”)

    Shouldn’t the jndi-name be ‘simpleReceiver’?

    Maurizio

    Maurizio

  5. Bastian says:

    … there should be a wordpress plugin for eclipse to directly post code :) thanks again for your code review. This, again, was a good example of how well things can get just by giving feedback and practicing code reviews :)

  6. Maurizio says:

    And I think the username in sun-ejb.xml should be ‘guest’. not ‘test’.

    Could you please post a the content (jar tcf) of your war or ear.

    Thanks,

    Maurizio

  7. Bastian says:

    I used “test” because that’s how I configured teh HornetQ credentials :)

  8. Maurizio says:

    Yes, but you’re using test/test in r=the asadmin commands, guest/guest in SimpleMsgSender, and test/guest in sun-ejb.xml.

  9. Maurizio says:

    I’m still not able to register the MDB, Is there a way you could post your war file, or at least its content (jar -tvff)?

    Maurizio

  10. Maurizio says:

    okay, it works now.

  11. Jacques says:

    I was using a similar integration in the past but had problems which made me give up (everything worked well for a day or two and then some kind of deadlock happened–of course only with production loads). Recently I’ve come to the conclusion that the thread pool for the hornetq client may have been too small. I believe the default for new thread pools is 5 threads. I suggest you up that from 5 or determine for sure what is necessary or you might have similar experiences.

  12. Maurizio says:

    Hi Bastian,

    You don’t need to set the host,port and username/password in step 3.

    Maurizio

  13. Maurizio says:

    Actually, you don’t need step #3 at all.