quarkusio / quarkus

Quarkus: Supersonic Subatomic Java.
https://quarkus.io
Apache License 2.0
13.78k stars 2.68k forks source link

Is it possible to use XA with JMS in Quarkus? #5762

Open jglink opened 4 years ago

jglink commented 4 years ago

Is there a possibility to use the Quarkus JMS (Artemis) integration in combination with XA? I don´t find any configuration examples for it and Quarkus just uses the ActiveMQConnectionFactory and not the ActiveMQXAConnectionFactory one.

It´s just the typical use case to do some database operation (insert/update) and sending a JMS message which should be done in XA. (Of course, there are other patterns like 'transactional outbox' specifically defined for Microservice that I´m aware of. But having XA available could also be helpful in some situations.)

gsmet commented 4 years ago

@middagj any thoughts on this one?

We have Narayana available for XA so it should be doable.

middagj commented 4 years ago

I think so, I have looked at it once, but lack experience in Narayana to pull it off quickly.

bender316 commented 4 years ago

Yes, you can integrate it as Narayana supports XA out of the box. But you currently have to do it on application level.

I created something like this:

@RequestScoped
public class XAJMSContextProducer {

    @Inject
    TransactionManager transactionManager;

    XAConnectionFactory connectionFactory;

    Map<Transaction, XAJMSContext> sessions = new HashMap<>();

    @PostConstruct
    public void init() {
        connectionFactory = new ActiveMQXAConnectionFactory("tcp://localhost:61616", "admin", "admin");
    }

    public XAJMSContext getContext() {
        try {
            XAJMSContext xaJmsContext = null;

            final Transaction transaction = transactionManager.getTransaction();

            if (!sessions.containsKey(transaction)) {
                xaJmsContext = connectionFactory.createXAContext();
                transaction.enlistResource(xaJmsContext.getXAResource());
                sessions.put(transaction, xaJmsContext);
            } else {
                xaJmsContext = sessions.get(transaction);
            }

            return xaJmsContext;
        } catch (final Exception e) {
            throw new ModuleException(e);
        }
    }

}

And in your client code just do a

@Inject
XAJMSSessionProducer xaJmaSessionProducer;

// other stuff
public void publish() {
  (XA)JMSContext context = xaJmaSessionProducer.getContext()
}

publishing works like in the Artemis JMS guide of quarkus.

Of course a XAConnectionFactory with proper transaction mapping should be sometime provided by Quarkus itself but for the moment you can make do with this solution

gpietrek commented 4 years ago

basic feature, we need it urgently

zhfeng commented 3 years ago

I raise https://github.com/quarkusio/quarkus/issues/14871 to add this feature.

middagj commented 2 years ago

We have added XA support in https://github.com/quarkiverse/quarkus-artemis/pull/45, so this can be closed.

zhfeng commented 2 years ago

Also there is https://github.com/quarkiverse/quarkus-pooled-jms to provide XA integration with quarkus-narayana-jta. So you just need to include these extensions in pom.xml if you want o use XA with JMS in Quarkus.

<dependency>
    <groupId>io.quarkiverse.artemis</groupId>
    <artifactId>quarkus-artemis-jms</artifactId>
</dependency>
<dependency>
    <groupId>io.quarkiverse.artemis</groupId>
    <artifactId>quarkus-pooled-jms</artifactId>
</dependency>
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-narayana-jta</artifactId>
</dependency>

And in application.properties

# Default Datasource
quarkus.datasource.db-kind=h2
quarkus.datasource.jdbc.max-size=8
quarkus.datasource.jdbc.transactions=xa

# Quarkus Artemis and Messaginghub Pooled JMS
quarkus.artemis.url=tcp://localhost:61616
quarkus.artemis.username=admin
quarkus.artemis.password=admin
quarkus.pooled-jms.xa.enabled=true

Then you can use them in your application just like

@Inject
DataSource db;

@Inject
ConnectionFactory cf;

@Transactional
public void upateDadaSourceandSendJMS(String data) {
    db.update(....);
    context = cf.createContext();
    context.createProducer().send();
}
turing85 commented 2 years ago

@zhfeng that is a very, very useful extension! Does it support native compilation? Also, is it possible to create the pooled connection factories manually (in case we need more than one, thinking of camel),

zhfeng commented 2 years ago

@turing85 yeah, it does support native compilation. I think it could be used to create pooled connection factories manually and what is your case in camel?

turing85 commented 2 years ago

@zhfeng Imagine multiple camel routes using different JMS queues/topics (e.g. reading from one queue, writing to another, but more complex scenarios are also possible). In this scenario, we'd need one connection factory per queue/topic.

turing85 commented 1 year ago

So I think this ticket can be closed. WDYT @zhfeng, @middagj, @gsmet ?