This example shows you how to setup a JMS Bridge between two HornetQ servers.
The example will use two HornetQ servers:
/source/topic
/target/queue
Both HornetQ server will run their own JNDI server used by the JMS Bridge and the JMS Client to lookup JMS resources (ConnectionFactory and Destination).
The JMS Bridge will run inside the target server and is configured to bridge messages from the source destination (the topic hosted on server #0) and the target destination (the queue hosted on server #1)
The client will check the bridge works by:
Please note that this example is meant to run on 2 different machines. If you want to run it on a single machine, you will have also to make sure that the HornetQ servers do not share any ports.
To run this example on 2 machines, you will need to adapt the configuration to your machines:
As an example, I will use the following network addresses to show how to configure the example:
192.168.0.10
192.168.0.11
First, make sure that HornetQ is setup and installed on both servers
Then, you will have to adapt each server configuration on their respective servers:
server0/
) must be changed on Server #0server1/
) must be changed on Server #1On server #0, in server0/hornetq-beans.xml
, setup the JNDI server network address by replacing
@SOURCE_SERVER@
by the source server address (e.g. 192.168.0.10
):
<bean name="JNDIServer" class="org.jnp.server.Main"> ... <!-- **************************************** --> <!-- Replace with the *source* server address --> <!-- **************************************** --> <property name="bindAddress">192.168.0.10</property> <!-- **************************************** --> <!-- Replace with the *source* server address --> <!-- **************************************** --> <property name="rmiBindAddress">192.168.0.10</property> ... </bean>
In server0/hornetq-configuration.xml
, configure a Netty connector with the source
server address:
<connectors> <connector name="netty"> <factory-class>org.hornetq.core.remoting.impl.netty.NettyConnectorFactory</factory-class> <!-- **************************************** --> <!-- Replace with the *source* server address --> <!-- **************************************** --> <param key="hornetq.remoting.netty.host" value="192.168.0.10" type="String"/> </connector> </connectors>
Note that in server0/hornetq-configuration.xml
, we also setup the Netty acceptor to accept connections from any of the source server addresses
(by specifying 0.0.0.0
)
The source server defines two JMS resources in server0/hornetq-jms.xml
:
/source/ConnectionFactory
/source/topic
On server #1, in server1/hornetq-beans.xml
, setup the JNDI server network address by replacing
@TARGET_SERVER@
by the target server address (e.g. 192.168.0.11
):
<bean name="JNDIServer" class="org.jnp.server.Main"> ... <!-- **************************************** --> <!-- Replace with the *target* server address --> <!-- **************************************** --> <property name="bindAddress">192.168.0.11</property> <!-- **************************************** --> <!-- Replace with the *target* server address --> <!-- **************************************** --> <property name="rmiBindAddress">192.168.0.11</property> ... </bean>
In server1/hornetq-configuration.xml
, configure a Netty connector with the target
server address:
<connectors> <connector name="netty"> <factory-class>org.hornetq.core.remoting.impl.netty.NettyConnectorFactory</factory-class> <!-- **************************************** --> <!-- Replace with the *target* server address --> <!-- **************************************** --> <param key="hornetq.remoting.netty.host" value="192.168.0.11" type="String"/> </connector> </connectors>
Note that in server1/hornetq-configuration.xml
, we also setup the Netty acceptor to accept connections from any of the target server addresses
(by specifying 0.0.0.0
)
The target server defines two JMS resources in server1/hornetq-jms.xml
:
/target/ConnectionFactory
/target/queue
The JMS Bridge is a POJO in which we inject JNDI configurations so that it looks up its source and target JMS resources. The JMS Bridge is defined a bean and setup by JBoss Microntainer in the same VM than Server #1, the target server.
The JMS Bridge is defined by theJMSBridge
bean in server1/hornetq-beans.xml
:
<!-- The JMS Bridge --> <bean name="JMSBridge" class="org.hornetq.jms.bridge.impl.JMSBridgeImpl"> ... </bean>
the JMSBridgeImpl
constructor is used to inject all the properties required to run the JMS Bridge.
Its first four arguments defines how the bridge will lookup:
Using other POJOs, the JMS Bridge is configured to retrieve:
/source/ConnectionFactory
using
the SourceJNDI
configuration/source/topic
using
the SourceJNDI
configuration/target/ConnectionFactory
using
the TargetJNDI
configuration/target/queue
using
the TargetJNDI
configurationIn turn, SourceJNDI
and TargetJNDI
are POJOs defining how to connect to JNDI server.
SourceJNDI URL must point to your source server, while LocalJNDI must point to your target server:
<bean name="SourceJNDI" class="java.util.Hashtable"> ... <entry> <key>java.naming.provider.url</key> <!-- **************************************** --> <!-- Replace with the *source* server address --> <!-- **************************************** --> <value>jnp://192.168.0.10:1099</value> ... </bean> <bean name="TargetJNDI" class="java.util.Hashtable"> ... <ntry> <key>java.naming.provider.url</key> <!-- **************************************** --> <!-- Replace with the *target* server address --> <!-- **************************************** --> <value>jnp://1192.168.0.11:1099</value> </entry> ... </bean>
To run the example after having setup both HornetQ servers and the JMS bridge:
./build.sh source-server
./build.sh target-server
(this will start the JMS Bridge too)./build.sh client -Dsource=<source server address> -Dtarget=<target server address>
(for example:
./build.sh client -Dsource=192.168.0.11 -Dtarget=192.168.0.11
)Let's look at the Client code (in JMSBridgeExample
class):
./build.sh client
)InitialContext sourceContext = createContext(sourceServer); InitialContext targetContext = createContext(targetServer);
ConnectionFactory sourceConnectionFactory = (ConnectionFactory)sourceContext.lookup("/source/ConnectionFactory"); Topic sourceTopic = (Topic)sourceContext.lookup("/source/topic");
sourceConnection = sourceConnectionFactory.createConnection(); Session sourceSession = sourceConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); MessageProducer sourceProducer = sourceSession.createProducer(sourceTopic);
TextMessage message = sourceSession.createTextMessage("this is a text message sent at " + System.currentTimeMillis()); sourceProducer.send(message);
sourceConnection.close();
At this point, the JMS Bridge will consume the message from the source topic and sends it to the target queue. The client will check the bridge works by consuming a message from the target queue.
ConnectionFactory targetConnectionFactory = (ConnectionFactory)targetContext.lookup("/target/ConnectionFactory"); Queue targetQueue = (Queue)targetContext.lookup("/target/queue");
targetConnection = targetConnectionFactory.createConnection(); Session targetSession = targetConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); MessageConsumer targetConsumer = targetSession.createConsumer(targetQueue);
targetConnection.start();
TextMessage messageReceived = (TextMessage)targetConsumer.receive(5000);
System.out.format("Message ID : %s\n", messageReceived.getJMSMessageID()); System.out.format("Bridged Message ID : %s\n", messageReceived.getStringProperty("HQ_BRIDGE_MSG_ID_LIST"));
Note that the message received from the target queue is not the same message sent to the source topic (their message IDs are different) but they have the same content.
finally
block. Closing a JMS connection will automatically close all of its sessions, consumers, producer and browser objects
finally
{
if (initialContext != null)
{
initialContext.close();
}
if (connection != null)
{
connection.close();
}
}