How to connect to remote wildfly-swarm instance with ActiveMQ Broker running on it, from another wildfly-swarm instance?
Since I couldn't find a single example of this, and it turned out to be a non-trivial thing to do, mainly due to conflicting and out-dated documentation on wildfly-swarm docs, posting it here (although I have a feeling it will get outdated very soon):
For impatient, code is on github.com/dekstroza
Crux of the story, wildfly-swarm hosting activemq broker will not be configured to accept remote connections - so one has to configure that using swarm's yml file - minimally you need to add socket binding, acceptor and connector for it, and disable security or deal with it.
Wildfly-Swarm with ActiveMQ Broker needs:
swarm:
bind:
address: 0.0.0.0
network:
socket-binding-groups:
standard-sockets:
socket-bindings:
activemq:
port: 61616
messaging-activemq:
servers:
default:
jms-queues:
mediation-queue:
entries:
entry: "java:/jms/queue/mediation-queue java:/jboss/exported/jms/queue/mediation-queue"
security-enabled: false
remote-acceptors:
netty-acceptor:
socket-binding: activemq
remote-connectors:
netty-connector:
socket-binding: activemq
http-connectors:
http-connector:
socket-binding: http
endpoint: http-acceptor
http-acceptors:
http-acceptor:
http-listener: default
jmx-management-enabled: true
And Wildfly-Swarm instance with your application which wants to send messages, needs:
swarm:
bind:
address: 0.0.0.0
messaging-activemq:
servers:
default:
jms-queues:
mediation-queue: {}
messaging:
remote:
name: remote-mq
host: 0.0.0.0
port: 61616
jndi-name: java:/jms/remote-mq
remote: true
Then the sender code can be as simple as:
package com.github.dekstroza.jee;
import javax.annotation.Resource;
import javax.inject.Inject;
import javax.jms.JMSConnectionFactory;
import javax.jms.JMSContext;
import javax.jms.Queue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Application;
@Path("/sender")
public class SenderApplication extends Application {
@Inject
@JMSConnectionFactory("java:/jms/remote-mq")
private JMSContext context;
@Resource(mappedName = "jms/queue/mediation-queue")
private Queue queue;
@GET
@Produces("text/plain")
public String get() {
context.createProducer().send(queue, "Hello!");
return "Howdy!";
}
}
And consumer would look like:
package com.github.dekstroza.jee;
import org.jboss.ejb3.annotation.ResourceAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;
import javax.jms.TextMessage;
//remote-mq has to match your remote connection factory name, otherwise it won't work!!!
@ResourceAdapter("remote-mq")
@MessageDriven(name = "MediationQueueMDB", activationConfig = {
@ActivationConfigProperty(propertyName = "destinationLookup", propertyValue = "/jms/queue/mediation-queue"),
@ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"), })
public class ConsumerMDB implements MessageListener {
private static final Logger log = LoggerFactory.getLogger(ConsumerMDB.class);
public void onMessage(Message message) {
try {
log.info("Consumed message: {}", ((TextMessage) message).getText());
} catch (Exception e) {
log.error("Error consuming message:", e);
}
}
}
All of this would not be that hard, if swarm documentation was not so outdated, specially the reference guide. It is referencing swarm.messaging.* everywhere, where in turn you actually need messaging-activemq in some places.
Special thanks to Ken Finnigan for few helpful pointers.