eluinstra / ebms-core

Java implementation of the EbMS 2.0 specification.
Apache License 2.0
8 stars 5 forks source link

JTA exception with Atomikos and handling Pong #18

Closed krpors closed 1 month ago

krpors commented 7 months ago

Hi,

We've come to an issue with receiving an EbMS Pong. The message is received properly, validated and all that, but in the end the client receives an HTTP 500. With some trickery, the following logs could be observed:

org.springframework.jms.UncategorizedJmsException: Uncategorized exception occurred during JMS processing; nested exception is com.atomikos.jms.internal.AtomikosTransactionRequiredJMSException: The JMS session you are using requires a JTA transaction context for the calling thread and none was found.
Please correct your code to do one of the following: 
1. start a JTA transaction if you want your JMS operations to be subject to JTA commit/rollback, or
2. create a non-transacted session and do session acknowledgment yourself, or
3. set localTransactionMode to true so connection-level commit/rollback are enabled.
        at org.springframework.jms.support.JmsUtils.convertJmsAccessException(JmsUtils.java:311) ~[app.jar:?]
        at org.springframework.jms.support.JmsAccessor.convertJmsAccessException(JmsAccessor.java:185) ~[app.jar:?]
        at org.springframework.jms.core.JmsTemplate.execute(JmsTemplate.java:507) ~[app.jar:?]
        at org.springframework.jms.core.JmsTemplate.send(JmsTemplate.java:584) ~[app.jar:?]
        at org.springframework.jms.core.JmsTemplate.convertAndSend(JmsTemplate.java:691) ~[app.jar:?]
        at nl.clockwork.ebms.delivery.JMSDeliveryManager.handleResponseMessage(JMSDeliveryManager.java:100) ~[app.jar:?]
        at nl.clockwork.ebms.delivery.JMSDeliveryManager$$FastClassBySpringCGLIB$$d9519577.invoke(<generated>) ~[app.jar:?]
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[app.jar:?]
        at org.springframework.aop.framework.CglibAopProxy.invokeMethod(CglibAopProxy.java:386) ~[app.jar:?]
        at org.springframework.aop.framework.CglibAopProxy.access$000(CglibAopProxy.java:85) ~[app.jar:?]
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:704) ~[app.jar:?]
        at nl.clockwork.ebms.delivery.JMSDeliveryManager$$EnhancerBySpringCGLIB$$2a7a12f.handleResponseMessage(<generated>) ~[app.jar:?]
        at nl.clockwork.ebms.processor.PongProcessor.processPong(PongProcessor.java:72) ~[app.jar:?]
        at nl.clockwork.ebms.processor.EbMSMessageProcessor.processRequest(EbMSMessageProcessor.java:210) ~[app.jar:?]
        at nl.clockwork.ebms.processor.EbMSMessageProcessor.processRequest(EbMSMessageProcessor.java:155) ~[app.jar:?]
        at nl.clockwork.ebms.server.EbMSInputStreamHandler.handleRequest(EbMSInputStreamHandler.java:101) ~[app.jar:?]
        at nl.clockwork.ebms.server.EbMSInputStreamHandler.handle(EbMSInputStreamHandler.java:62) ~[app.jar:?]
        at nl.clockwork.ebms.server.EbMSHttpHandler.handle(EbMSHttpHandler.java:103) ~[app.jar:?]
        at nl.clockwork.ebms.server.servlet.EbMSServlet.service(EbMSServlet.java:53) ~[app.jar:?]

I was able to reproduce this, using this fork.

Steps to reproduce:

  1. Clone https://github.com/krpors/ebms-core and checkout dev-2.19.x
  2. Run PostgreSQL: docker run --name some-postgres -e POSTGRES_PASSWORD=ebms -e POSTGRES_USER=ebms -p 5432:5432 -d postgres
  3. Load any CPA into the database
  4. Run the EbMS Server from my branch. I used nl/clockwork/ebms/EbMSServer.java.
  5. Change the From and To PartyIds in ./debugging/pong.xml, according to the CPA
  6. Execute the ./debugging/send_pong.sh

The JMSDeliveryManager should output the stacktrace above.

Possible fix

I was able to fix the error by making two changes:

  1. In JMSConfig.java, change result.setLocalTransactionMode(false); to result.setLocalTransactionMode(true);
  2. In /src/test/resources/nl/clockwork/ebms/default.properties, I changed the JMS broker URL to include an extra property as follows: jms.brokerURL=vm://localhost?jms.xaAckMode=1. This is according to this JIRA issue.

I am unaware though if the above fix is a proper one, since the ConnectionFactory is used in other places.

Any thoughts? Thanks!

krpors commented 7 months ago

I noticed that one other possible fix is to execute the Pong processing in a transaction like so in EbMSMessageProcessor.java.

ebMSDAO.executeTransaction(() -> pongProcessor.processPong((EbMSPong)message));

This is of course not the correct place (because there may not be a transaction manager in use).

eluinstra commented 6 months ago

hi,

sorry for the late response. I will take a look at it. It should not be necessary that the pong is part of a transaction. So maybe removing the transaction could solve it, but I have to take a better look at it.

kind regards, Edwin

krpors commented 6 months ago

No problem, thank you for your time.

I also noticed that sending an (asynchronous) Ping has the same behaviour (using Atomikos as a tx-manager). It can be traced back to the following piece of code in JMSDeliveryManager.java:

return Optional.ofNullable(
        (EbMSResponseMessage)jmsTemplate
                .receiveSelectedAndConvert(JMS_DESTINATION_NAME, "JMSCorrelationID='" + messageHeader.getMessageData().getMessageId() + "'"));

The end result is that a Ping is sent successfully using the HTTP client, but waiting for the Pong reply (using the jmsTemplate) results in the same JTA exception described above. Once again, wrapping it in a transaction using ebmsDAO.executeTransaction(...) fixes the problem (but obv only when a transaction manager is active).

eluinstra commented 6 months ago

what call should be wrapped than? I was wondering, because the processPing returns a result.

Btw, I committed a fix on the branch fix2.

eluinstra commented 1 month ago

This issue is fixed in release 2.19.4

PieterDup98 commented 1 month ago

Hi @eluinstra thanks for the fix. I don't see a tag for 2.19.4 yet, can I use this version, or is there still more changes coming?

eluinstra commented 1 month ago

I just tagged and released it.