I was recently trying to set up JMS bridge between two standalone WildFly instances, each running on different machine. The whole experience turned out to be less straightforward than I had anticipated and I decided I should share the solution here to hopefully help others.
The solution
It turns out that to configure JMS bridge for remote servers credentials need to be specified twice. The username and password must be added to the context to allow JNDI registry lookup and then again in destination configuration. The user referred in <user> tag must be registered in ApplicationRealm with permission to consume/send messages (depending on whether source or target is remote endpoint)
|
<jms-bridge name="jms-bridge-name"> |
|
<source> |
|
<connection-factory name="jms/RemoteConnectionFactory"/> |
|
<destination name="jms/queue/sourceQ"/> |
|
<!-- jmsuser with the same password is registered in ApplicationRealm with permission to consume/send messages --> |
|
<user>jmsuser</user> |
|
<password>pass</password> |
|
<context> |
|
<!-- The username and password must be added to the context to allow remote registry lookup --> |
|
<property key="java.naming.factory.initial" value="org.jboss.naming.remote.client.InitialContextFactory"/> |
|
<property key="java.naming.provider.url" value="http-remoting://host.domain:8080"/> |
|
<property key="java.naming.security.principal" value="jmsuser"/> |
|
<property key="java.naming.security.credentials" value="pass"/> |
|
</context> |
|
</source> |
|
<target> |
|
<connection-factory name="ConnectionFactory"/> |
|
<destination name="jms/queue/targetQ"/> |
|
</target> |
|
<quality-of-service>ONCE_AND_ONLY_ONCE</quality-of-service> |
|
<failure-retry-interval>10000</failure-retry-interval> |
|
<max-retries>10</max-retries> |
|
<max-batch-size>500</max-batch-size> |
|
<max-batch-time>50</max-batch-time> |
|
<add-messageID-in-header>true</add-messageID-in-header> |
|
</jms-bridge> |
The problem
Before connection to JMS queue can be established , JMS bridge must first lookup remote objects (queues and connection factories) by their JDNI names. When both bridge and remote broker are running on the same machine no authentication is required, but for truly remote lookup to succeed valid credentials are needed. It’s worth mentioning that the user used for JDNI lookup can be different from the one used for creating the JMS connection and doesn’t need permission to modify queue.
Failure to provide credentials in context definition will result in rather cryptic error
javax.security.sasl.SaslException: Authentication failed: all available authentication mechanisms failed:
JBOSS-LOCAL-USER: javax.security.sasl.SaslException: Failed to read server challenge [Caused by java.io.FileNotFoundException: /Users/Jarek/dev/wildfly-8.2.0.Final/standalone/tmp/auth/local2330515126078615287.challenge (System nie może odnaleźć określonej ścieżki)]
DIGEST-MD5: javax.security.sasl.SaslException: DIGEST-MD5: Cannot perform callback to acquire realm, authentication ID or password [Caused by javax.security.auth.callback.UnsupportedCallbackException]
When user name or password are present but invalid the error message is a little more useful
javax.security.sasl.SaslException: Authentication failed: all available authentication mechanisms failed:
JBOSS-LOCAL-USER: javax.security.sasl.SaslException: Failed to read server challenge [Caused by java.io.FileNotFoundException: /Users/Jarek/dev/wildfly-8.2.0.Final/standalone\tmp\auth\local6004500283609774891.challenge (System nie może odnaleźć określonej ścieżki)]
DIGEST-MD5: Server rejected authentication
Only components that are registered within the java:jboss/exported/ namespace are remote accessible via the naming service.
Recipe
In WildFly, JMS bridge between local and remote destinations jms/queue/sourceQ ⇒ jms/queue/targetQ with ONCE_AND_ONLY_ONCE QoS can be created with following CLI command
/subsystem=messaging/jms-bridge=jms-bridge-name/:add(source-destination="jms/queue/sourceQ", \
source-connection-factory="ConnectionFactory", \
target-destination="jms/queue/targetQ", \
target-connection-factory="jms/RemoteConnectionFactory", \
target-user="jmsuser", \
target-password="pass", \
target-context=[\
"java.naming.factory.initial"="org.jboss.naming.remote.client.InitialContextFactory", \
"java.naming.provider.url"="http-remoting://localhost:8080", \
"java.naming.security.principal"="jmsuser", \
"java.naming.security.credentials"="pass"], \
quality-of-service=ONCE_AND_ONLY_ONCE, \
failure-retry-interval=10000, \
max-retries=10, \
max-batch-size=500, \
max-batch-time=50, \
add-messageID-in-header=true)