7
votes

I have a generic standalone JMS application which works with following JMS providers WebSphere, HornetQ and ActiveMq. I pass Context.INITIAL_CONTEXT_FACTORY and Context.PROVIDER_URL as parameters to my application and create a naming context out of them by doing something like this

Properties environmentParameters = new Properties();
environmentParameters.put(Context.INITIAL_CONTEXT_FACTORY, property.context);
environmentParameters.put(Context.PROVIDER_URL, property.provider);
namingContext = new InitialContext(environmentParameters);

And use this context for object lookup.

I understand RabbitMQ isn't a JMS provider so it doesn't have an InitialContext class or a Provider URL but it provides a JMS Client which is an abstraction of its Java client conforming to JMS specification. RabbitMQ's JMS client documentation has an example of defining objects in JNDI as a resource configuration as part of a web application but I quite couldn't figure out how to do something similar for my standalone application which creates a naming context based on JNDI provider using JMS client's dependencies or to create an InitialContext out of the available dependencies.

So can someone throw some light on how this can be done? Hope my question is clear.

3

3 Answers

8
votes

In order to get JMS working with RabbitMQ, you have to enable the plugin rabbitmq_jms_topic_exchange.
You can download it following the directions in this site (You'll need to login):
https://my.vmware.com/web/vmware/details?downloadGroup=VFRMQ_JMS_105&productId=349

  1. After extraction, put the file rjms-topic-selector-1.0.5.ez inside the Folder $RABBITMQ_SERVER\plugins.
  2. Enable the plugin with the command: rabbitmq-plugins enable rabbitmq_jms_topic_exchange
  3. Check if the plugin it's running ok with the command: rabbitmq-plugins list
    List of RabbitMQ plugins
  4. Restart the RabbitMQ - I'm not sure if it's really necessary, but just in case ;-)
  5. At your RabbitMQ web management (http://localhost:15672/#/exchanges) you can check the new Exchange you have available: Exchanges of the RabbitMQ
  6. Now, in theory :-), you're already able to connect to your RabbiMQ server using the standard Java JMS API.

Bear in mind that you'll have to create a .bindings file in order to JNDI found your registered objects. This is an example of the content of it:


    ConnectionFactory/ClassName=com.rabbitmq.jms.admin.RMQConnectionFactory
    ConnectionFactory/FactoryName=com.rabbitmq.jms.admin.RMQObjectFactory
    ConnectionFactory/RefAddr/0/Content=jms/ConnectionFactory
    ConnectionFactory/RefAddr/0/Type=name
    ConnectionFactory/RefAddr/0/Encoding=String
    ConnectionFactory/RefAddr/1/Content=javax.jms.ConnectionFactory
    ConnectionFactory/RefAddr/1/Type=type
    ConnectionFactory/RefAddr/1/Encoding=String
    ConnectionFactory/RefAddr/2/Content=com.rabbitmq.jms.admin.RMQObjectFactory
    ConnectionFactory/RefAddr/2/Type=factory
    ConnectionFactory/RefAddr/2/Encoding=String
    # Change this line accordingly if the broker is not at localhost
    ConnectionFactory/RefAddr/3/Content=localhost
    ConnectionFactory/RefAddr/3/Type=host
    ConnectionFactory/RefAddr/3/Encoding=String
    # HELLO Queue 
    HELLO/ClassName=com.rabbitmq.jms.admin.RMQDestination
    HELLO/FactoryName=com.rabbitmq.jms.admin.RMQObjectFactory
    HELLO/RefAddr/0/Content=jms/Queue
    HELLO/RefAddr/0/Type=name
    HELLO/RefAddr/0/Encoding=String
    HELLO/RefAddr/1/Content=javax.jms.Queue
    HELLO/RefAddr/1/Type=type
    HELLO/RefAddr/1/Encoding=String
    HELLO/RefAddr/2/Content=com.rabbitmq.jms.admin.RMQObjectFactory
    HELLO/RefAddr/2/Type=factory
    HELLO/RefAddr/2/Encoding=String
    HELLO/RefAddr/3/Content=HELLO
    HELLO/RefAddr/3/Type=destinationName
    HELLO/RefAddr/3/Encoding=String

And then... finally... the code:


    Properties environmentParameters = new Properties();
    environmentParameters.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory");
    environmentParameters.put(Context.PROVIDER_URL, "file:/C:/rabbitmq-bindings");
    namingContext = new InitialContext(environmentParameters);

    ConnectionFactory connFactory = (ConnectionFactory) ctx.lookup("ConnectionFactory");

4
votes

For people who are hitting this exception

Caused by: javax.naming.NamingException: Unknown class [com.rabbitmq.jms.admin.RMQConnectionFactory]

even after following @Ualter Jr.'s answer is because of incorrect entries in the .bindings file.

I corrected the following 2 lines in .bindings file

ConnectionFactory/ClassName=com.rabbitmq.jms.admin.RMQConnectionFactory --->
ConnectionFactory/ClassName=javax.jms.ConnectionFactory

and

YourQueueName/ClassName=com.rabbitmq.jms.admin.RMQDestination --->
StriimQueue/ClassName=javax.jms.Queue

When I hit this exception again I just opened this class and found that it expects the following classnames

      /*
     * Valid class names are:
     * javax.jms.ConnectionFactory
     * javax.jms.QueueConnectionFactory
     * javax.jms.TopicConnectionFactory
     * javax.jms.Topic
     * javax.jms.Queue
     *
     */

Correcting these entries would enable existing/new JMS applications to work with RabbitMQ.

0
votes

We can generate the .bindings file for RabbitMQ using below java code:

import java.util.Properties;
import javax.jms.ConnectionFactory;
import javax.jms.Queue;
import javax.jms.Topic;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.Reference;
import javax.naming.StringRefAddr;

Properties env = new Properties();
    env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory");
    env.put(Context.PROVIDER_URL, "file:bindings");
    Context ctx = new InitialContext(env);

    Reference connectionFactoryRef = new Reference(ConnectionFactory.class.getName(), RMQObjectFactory.class.getName(), null);
    connectionFactoryRef.add(new StringRefAddr("name", "jms/ConnectionFactory"));
    connectionFactoryRef.add(new StringRefAddr("type", ConnectionFactory.class.getName()));
    connectionFactoryRef.add(new StringRefAddr("factory", RMQObjectFactory.class.getName()));
    connectionFactoryRef.add(new StringRefAddr("host", "$JMS_RABBITMQ_HOST$"));
    connectionFactoryRef.add(new StringRefAddr("port", "$JMS_RABBITMQ_PORT$"));
    ctx.rebind("ConnectionFactory", connectionFactoryRef);

    String jndiAppend = "jndi";
    for (int i = 1; i <= 10; i++) {
        String name = String.format("queue%02d", i);
        Reference ref = new Reference(Queue.class.getName(), com.rabbitmq.jms.admin.RMQObjectFactory.class.getName(), null);
        ref.add(new StringRefAddr("name", "jms/Queue"));
        ref.add(new StringRefAddr("type", Queue.class.getName()));
        ref.add(new StringRefAddr("factory", RMQObjectFactory.class.getName()));
        ref.add(new StringRefAddr("destinationName", name));
        ctx.rebind(name+jndiAppend, ref);

        name = String.format("topic%02d", i);
        ref = new Reference(Topic.class.getName(), com.rabbitmq.jms.admin.RMQObjectFactory.class.getName(), null);
        ref.add(new StringRefAddr("name", "jms/Topic"));
        ref.add(new StringRefAddr("type", Topic.class.getName()));
        ref.add(new StringRefAddr("factory", RMQObjectFactory.class.getName()));
        ref.add(new StringRefAddr("destinationName", name));
        ctx.rebind(name+jndiAppend, ref);
    }