Configuring a Remote HornetQ JMS Server as a Resource Adapter: Difference between revisions

From NovaOrdis Knowledge Base
Jump to navigation Jump to search
Line 179: Line 179:
=TODO=
=TODO=


<font color=red>Only messages sent to one node end being processed by the MDB. Figure this out. It is probably something related to load balancing.</font>
<font color=red>Only messages sent to one node end being processed by the MDB. Figure this out. It is probably something related to load balancing. Or, do I need to cluster the remote HQ nodes?</font>


=If the MDB Needs to Send Messages to Remote Destinations=
=If the MDB Needs to Send Messages to Remote Destinations=

Revision as of 15:44, 20 March 2017

External

Internal

Overview

This article describes the configuration required to make a cluster of two HornetQ-based JMS nodes, running as messaging subsystems within EAP 6.4 instances, accessible to a third JBoss EAP 6.4. The cluster will be deployed as resource adapter within the third JBoss instance, and thus available to the MDBs deployed on that JBoss instance.

The procedure consists in declaring netty connections from the resource adapter node to the HornetQ nodes and using those connections from a pooled connection which exposes the remote cluster as a resource adapter.

Procedure

Declare the Outbound Socket Bindings

Declare the outbound socket bindings corresponding to the HornetQ nodes. Note that the configuration shown below implies that the HornetQ node 1 runs with a port offset of 100, and HornetQ node 2 runs with a port offset of 200.

<outbound-socket-binding name="remote-hornetq-node-1-socket-binding">
    <remote-destination host="${remote.hornetq.node.one.address:127.0.0.1}" 
                        port="${remote.hornetq.node.one.hornetq.port:5545}"/>
</outbound-socket-binding>
<outbound-socket-binding name="remote-hornetq-node-2-socket-binding">
    <remote-destination host="${remote.hornetq.node.two.address:127.0.0.1}"
                        port="${remote.hornetq.node.two.hornetq.port:5645}"/>
</outbound-socket-binding>

Declare the Netty Connectors

Declare the netty connectors corresponding to the HornetQ nodes in the messaging subsystem configuration.

<subsystem xmlns="urn:jboss:domain:messaging:...">
    <hornetq-server>
        ...
        <connectors>
            ...
            <netty-connector name="remote-hornetq-node-1-connection" 
                             socket-binding="remote-hornetq-node-1-socket-binding"/>
            <netty-connector name="remote-hornetq-node-2-connection" 
                             socket-binding="remote-hornetq-node-2-socket-binding"/>
        </connectors>

       ...
</subsystem>     

Declare the Resource Adapter

Declare the resource adapter that will balance among the HornetQ nodes. It is declared as a pooled connection factory. The semantics of the system properties used in declaration will be explained in the System Properties section.

<subsystem xmlns="urn:jboss:domain:messaging:...">
    <hornetq-server>
        ...
        <jms-connection-factories>
            ...
            <pooled-connection-factory name="hornetq-remote-ra">
                <inbound-config>
                    <use-jndi>true</use-jndi>
                    <jndi-params>
java.naming.factory.initial=org.jboss.naming.remote.client.InitialContextFactory;
java.naming.provider.url=
remote://${remote.hornetq.node.one.address}:${remote.hornetq.node.one.remoting.port},
remote://${remote.hornetq.node.two.address}:${remote.hornetq.node.two.remoting.port};
java.naming.security.principal=${remoting.user.name};
java.naming.security.credentials=${remoting.password}
                    </jndi-params>
                </inbound-config>  
                <transaction mode="xa"/>  
                <user>${jms.user.name}</user>  
                <password>${jms.password}</password>  
                <connectors>  
                    <connector-ref connector-name="remote-hornetq-node-1-connection"/>  
                    <connector-ref connector-name="remote-hornetq-node-2-connection"/>  
                </connectors>  
                <entries>  
                    <entry name="java:/RemoteJmsXA"/>  
                </entries>  
            </pooled-connection-factory>
       </jms-connection-factories>
       ...
</subsystem>     

The above declaration refers to a series of system properties, which must be set appropriately and are explained in the next section.

Note that "java.naming.security.principal" and "java.naming.security.credentials" used in <jndi-params> are not necessary if remoting security is disabled.

Likewise, <user>${jms.user.name}</user> and <password>${jms.password}</password> are not used if HornetQ security is disabled on the remote hosts.

TODO: Properly explain in the proper place what <use-jndi>true</use-jndi> is. Use the HornetQ documentation. <use-jndi>true</use-jndi> establishes that JNDI should be used to locate the destination for incoming connections. Link back to MDB#useJNDI

TODO: Properly explain in the proper place what <ha>true</ha> is. Use the HornetQ documentation. Link back to MDB#hA

System Properties

<system-properties>
        
    <property name="remote.hornetq.node.one.address" value="127.0.0.1"/>
    <property name="remote.hornetq.node.one.hornetq.port" value="5545"/>
    <property name="remote.hornetq.node.one.remoting.port" value="4547"/>
    <property name="remote.hornetq.node.two.address" value="127.0.0.1"/>
    <property name="remote.hornetq.node.two.hornetq.port" value="5645"/>
    <property name="remote.hornetq.node.two.remoting.port" value="4647"/>
    <property name="remoting.user.name" value="some-remoting-user"/>
    <property name="remoting.password" value="some-remoting-password"/>
    <property name="jms.user.name" value="some-jms-user"/>
    <property name="jms.password" value="some-jms-password"/>
</system-properties>
  • "remote.hornetq.node.one.address" the address of the first HornetQ node.
  • "remote.hornetq.node.one.hornetq.port" the netty connector port of the first HornetQ node.
  • "remote.hornetq.node.one.remoting.port" the remoting port of the first HornetQ node. The default value is 4447 and if the node runs with an offset of 100, the value should be 4547.
  • "remote.hornetq.node.two.address" the address of the second HornetQ node.
  • "remote.hornetq.node.two.hornetq.port" the netty connector port of the second HornetQ node.
  • "remote.hornetq.node.two.remoting.port" the remoting port of the second HornetQ node. The default value is 4447 and if the node runs with an offset of 200, the value should be 4647.
  • "remoting.user.name"/"remoting.password" the remoting credentials. Remoting can be configured to allow unauthenticated access as described here: "Disabling Remoting Authentication".
  • "jms.user.name"/"jms.password" credentials required to create JMS connections. HornetQ can be configured to allow anonymous connections, as described here: "HornetQ Configuration - Disabling Security"

Resource Adapter Deployment Smoke Test

22:44:44,499 INFO  [org.hornetq.ra] (MSC service thread 1-7) HornetQ resource adaptor started
22:44:44,499 INFO  [org.jboss.as.connector.services.resourceadapters.ResourceAdapterActivatorService$ResourceAdapterActivator] (MSC service thread 1-7) IJ020002: Deployed: file://RaActivatorhornetq-remote-ra
22:44:44,500 INFO  [org.jboss.as.connector.deployment] (MSC service thread 1-1) JBAS010401: Bound JCA ConnectionFactory [java:/RemoteJmsXA]

Configure the MDB to Use the Remote JMS Server Resource Adapter

import org.jboss.ejb3.annotation.ResourceAdapter;
...
@MessageDriven(...)
@ResourceAdapter("hornetq-remote-ra")

For more details about the @ResourceAdapter annotation see:

MDB - @ResourceAdapter

Activation Configuration

@MessageDriven(activationConfig = {
    @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),  
    @ActivationConfigProperty(propertyName = "destination", propertyValue = "${hornetq.in.queue.short}"),  
    @ActivationConfigProperty(propertyName = "useJNDI",propertyValue = "true")  
}
,mappedName = "${hornetq.inf.queue.long}")  


What is "${hornetq.in.queue.short}"?

What is "useJNDI"?

What is ",mappedName = "${hornetq.inf.queue.long}") "?

MDB Deployment Smoke Test

23:10:00,636 INFO  [org.jboss.as.server.deployment] (MSC service thread 1-6) JBAS015876: Starting deployment of "mdb-remote-ra.jar" (runtime-name: "mdb-remote-ra.jar")
23:10:00,816 INFO  [org.jboss.as.ejb3] (MSC service thread 1-6) JBAS014142: Started message driven bean 'MDB' with 'hornetq-remote-ra' resource adapter

GitHub Example

https://github.com/NovaOrdis/playground/tree/master/jee/ejb/mdb-with-remote-resource-adapter

TODO

Only messages sent to one node end being processed by the MDB. Figure this out. It is probably something related to load balancing. Or, do I need to cluster the remote HQ nodes?

If the MDB Needs to Send Messages to Remote Destinations

Enable Property Substitution

<subsystem xmlns="urn:jboss:domain:ee:1.2">
  ...
  <spec-descriptor-property-replacement>true</spec-descriptor-property-replacement>
  <jboss-descriptor-property-replacement>true</jboss-descriptor-property-replacement>
  <annotation-property-replacement>true</annotation-property-replacement>
</subsystem>

For more details on ee subsystem configuration see

ee Subsystem Configuration

Declare an External JNDI Context

NOT TESTED

<bindings>
	<external-context name="java:global/remote" module="org.jboss.remote-naming" class="javax.naming.InitialContext">
		<environment>  
			<property name="java.naming.factory.initial" value="org.jboss.naming.remote.client.InitialContextFactory"/>  
			<property name="java.naming.provider.url" value=
"remote://${remote.jms.server.one.bind.address:ragga}:4447,remote://${remote.jms.server.two.bind.address:ragga}:4547"/>  
			<property name="java.naming.security.principal" value="${user.name}"/>  
			<property name="java.naming.security.credentials" value="${user.password}"/>  
		</environment>  
	</external-context>  
</bindings>

MDB that Sends Messages to the Origin Queue

@MessageDriven(name = "InQueueMDB", activationConfig = {
	@ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
	@ActivationConfigProperty(propertyName = "destination", propertyValue = "${hornetq.in.queue.short}"),  
	@ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge"),
	@ActivationConfigProperty(propertyName = "useJNDI",propertyValue = "true"),  
	@ActivationConfigProperty(propertyName = "hA", propertyValue = "true")  
},mappedName = "${hornetq.inf.queue.full}")  
@ResourceAdapter("hornetq-remote-ra")  
public class InQueueMDB implements MessageListener {  

    @Resource(lookup = "java:global/remote")  
    
    private InitialContext context;  

    @Resource( name = "${hornetq.jms.connection}")  
    private ConnectionFactory  qcf;

    public void onMessage(Message message) {  

        try {  
	     if (message instanceof TextMessage) {
                 Object obj =  (Queue) context.lookup("/jms/queue/outQueue");  
		qConnection = (QueueConnection) qcf.createConnection("quickuser","quick123+");  
		qSession = qConnection.createQueueSession(true, Session.SESSION_TRANSACTED);  
		qSender = qSession.createSender(outQueue);
		qSender.send(message);

For this code to work, "org.hornetq.ra" EAP module must contain the "" dependency:

<module xmlns="urn:jboss:module:1.1" name="org.hornetq.ra">
    ...
    <dependencies>  
         ...
	 <module name="org.jboss.remote-naming"/>  

Also, org.hornetq must be added to global modules so the JMS API is visible to the application:

<global-modules>
	<module name="org.jboss.common-core" slot="main"/>
	<module name="org.hornetq" slot="main"/>
</global-modules>