/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.activemq.artemis.tests.integration.ra; import javax.jms.Message; import javax.resource.ResourceException; import javax.transaction.xa.XAException; import javax.transaction.xa.XAResource; import javax.transaction.xa.Xid; import java.lang.reflect.Method; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.activemq.artemis.api.core.client.ClientMessage; import org.apache.activemq.artemis.api.core.client.ClientProducer; import org.apache.activemq.artemis.api.core.client.ClientSession; import org.apache.activemq.artemis.core.postoffice.Binding; import org.apache.activemq.artemis.core.server.Queue; import org.apache.activemq.artemis.core.transaction.impl.XidImpl; import org.apache.activemq.artemis.ra.ActiveMQResourceAdapter; import org.apache.activemq.artemis.ra.inflow.ActiveMQActivationSpec; import org.apache.activemq.artemis.tests.util.Wait; import org.apache.activemq.artemis.utils.UUIDGenerator; import org.junit.Test; public class ActiveMQMessageHandlerXATest extends ActiveMQRATestBase { @Override public boolean useSecurity() { return false; } @Test public void testXACommit() throws Exception { ActiveMQResourceAdapter qResourceAdapter = newResourceAdapter(); MyBootstrapContext ctx = new MyBootstrapContext(); qResourceAdapter.start(ctx); ActiveMQActivationSpec spec = new ActiveMQActivationSpec(); spec.setResourceAdapter(qResourceAdapter); spec.setUseJNDI(false); spec.setDestinationType("javax.jms.Queue"); spec.setDestination(MDBQUEUE); qResourceAdapter.setConnectorClassName(INVM_CONNECTOR_FACTORY); CountDownLatch latch = new CountDownLatch(1); XADummyEndpoint endpoint = new XADummyEndpoint(latch); DummyMessageEndpointFactory endpointFactory = new DummyMessageEndpointFactory(endpoint, true); qResourceAdapter.endpointActivation(endpointFactory, spec); ClientSession session = locator.createSessionFactory().createSession(); ClientProducer clientProducer = session.createProducer(MDBQUEUEPREFIXED); ClientMessage message = session.createMessage(true); message.getBodyBuffer().writeString("teststring"); clientProducer.send(message); session.close(); latch.await(5, TimeUnit.SECONDS); assertNotNull(endpoint.lastMessage); assertEquals(endpoint.lastMessage.getCoreMessage().getBodyBuffer().readString(), "teststring"); endpoint.prepare(); endpoint.commit(); qResourceAdapter.endpointDeactivation(endpointFactory, spec); qResourceAdapter.stop(); } @Test public void testXACommitWhenStopping() throws Exception { ActiveMQResourceAdapter qResourceAdapter = newResourceAdapter(); MyBootstrapContext ctx = new MyBootstrapContext(); qResourceAdapter.start(ctx); ActiveMQActivationSpec spec = new ActiveMQActivationSpec(); spec.setResourceAdapter(qResourceAdapter); spec.setUseJNDI(false); spec.setDestinationType("javax.jms.Queue"); spec.setDestination(MDBQUEUE); spec.setMaxSession(1); qResourceAdapter.setConnectorClassName(INVM_CONNECTOR_FACTORY); CountDownLatch latch = new CountDownLatch(1); CountDownLatch beforeDeliveryLatch = new CountDownLatch(1); PausingXADummyEndpoint endpoint = new PausingXADummyEndpoint(latch, beforeDeliveryLatch); DummyMessageEndpointFactory endpointFactory = new DummyMessageEndpointFactory(endpoint, true); qResourceAdapter.endpointActivation(endpointFactory, spec); ClientSession session = locator.createSessionFactory().createSession(); ClientProducer clientProducer = session.createProducer(MDBQUEUEPREFIXED); ClientMessage message = session.createMessage(true); message.getBodyBuffer().writeString("teststring"); clientProducer.send(message); ClientMessage message2 = session.createMessage(true); message2.getBodyBuffer().writeString("teststring2"); clientProducer.send(message2); session.close(); beforeDeliveryLatch.await(5, TimeUnit.SECONDS); qResourceAdapter.endpointDeactivation(endpointFactory, spec); qResourceAdapter.stop(); assertFalse(endpoint.interrupted); assertNotNull(endpoint.lastMessage); assertEquals(endpoint.lastMessage.getCoreMessage().getBodyBuffer().readString(), "teststring"); Binding binding = server.getPostOffice().getBinding(MDBQUEUEPREFIXEDSIMPLE); long messageCount = getMessageCount((Queue) binding.getBindable()); assertEquals(1, messageCount); } @Test public void testXACommitInterruptsWhenStopping() throws Exception { ActiveMQResourceAdapter qResourceAdapter = newResourceAdapter(); MyBootstrapContext ctx = new MyBootstrapContext(); qResourceAdapter.start(ctx); ActiveMQActivationSpec spec = new ActiveMQActivationSpec(); spec.setCallTimeout(500L); spec.setResourceAdapter(qResourceAdapter); spec.setUseJNDI(false); spec.setDestinationType("javax.jms.Queue"); spec.setDestination(MDBQUEUE); spec.setMaxSession(1); qResourceAdapter.setConnectorClassName(INVM_CONNECTOR_FACTORY); CountDownLatch latch = new CountDownLatch(1); CountDownLatch beforeDeliveryLatch = new CountDownLatch(1); PausingXADummyEndpoint endpoint = new PausingXADummyEndpoint(latch, beforeDeliveryLatch); DummyMessageEndpointFactory endpointFactory = new DummyMessageEndpointFactory(endpoint, true); qResourceAdapter.endpointActivation(endpointFactory, spec); ClientSession session = locator.createSessionFactory().createSession(); ClientProducer clientProducer = session.createProducer(MDBQUEUEPREFIXED); ClientMessage message = session.createMessage(true); message.getBodyBuffer().writeString("teststring"); clientProducer.send(message); ClientMessage message2 = session.createMessage(true); message2.getBodyBuffer().writeString("teststring2"); clientProducer.send(message2); session.close(); beforeDeliveryLatch.await(5, TimeUnit.SECONDS); qResourceAdapter.endpointDeactivation(endpointFactory, spec); qResourceAdapter.stop(); assertTrue(endpoint.interrupted); assertNotNull(endpoint.lastMessage); assertEquals(endpoint.lastMessage.getCoreMessage().getBodyBuffer().readString(), "teststring"); Binding binding = server.getPostOffice().getBinding(MDBQUEUEPREFIXEDSIMPLE); Wait.waitFor(() -> getMessageCount((Queue) binding.getBindable()) == 1); long messageCount = getMessageCount((Queue) binding.getBindable()); assertEquals(1, messageCount); } @Test public void testXARollback() throws Exception { setupDLQ(10); ActiveMQResourceAdapter qResourceAdapter = newResourceAdapter(); qResourceAdapter.setConnectorClassName(INVM_CONNECTOR_FACTORY); MyBootstrapContext ctx = new MyBootstrapContext(); qResourceAdapter.start(ctx); ActiveMQActivationSpec spec = new ActiveMQActivationSpec(); spec.setResourceAdapter(qResourceAdapter); spec.setMaxSession(1); spec.setUseJNDI(false); spec.setDestinationType("javax.jms.Queue"); spec.setDestination(MDBQUEUE); qResourceAdapter.setConnectorClassName(INVM_CONNECTOR_FACTORY); CountDownLatch latch = new CountDownLatch(1); XADummyEndpoint endpoint = new XADummyEndpoint(latch); DummyMessageEndpointFactory endpointFactory = new DummyMessageEndpointFactory(endpoint, true); qResourceAdapter.endpointActivation(endpointFactory, spec); ClientSession session = locator.createSessionFactory().createSession(); ClientProducer clientProducer = session.createProducer(MDBQUEUEPREFIXED); ClientMessage message = session.createMessage(true); message.getBodyBuffer().writeString("teststring"); clientProducer.send(message); session.close(); latch.await(5, TimeUnit.SECONDS); assertNotNull(endpoint.lastMessage); assertEquals(endpoint.lastMessage.getCoreMessage().getBodyBuffer().readString(), "teststring"); latch = new CountDownLatch(1); endpoint.reset(latch); endpoint.rollback(); latch.await(5, TimeUnit.SECONDS); assertNotNull(endpoint.lastMessage); assertEquals(endpoint.lastMessage.getCoreMessage().getBodyBuffer().readString(), "teststring"); qResourceAdapter.endpointDeactivation(endpointFactory, spec); qResourceAdapter.stop(); } class XADummyEndpoint extends DummyMessageEndpoint { private Xid xid; XADummyEndpoint(CountDownLatch latch) { super(latch); xid = new XidImpl("xa1".getBytes(), 1, UUIDGenerator.getInstance().generateStringUUID().getBytes()); } @Override public void beforeDelivery(Method method) throws NoSuchMethodException, ResourceException { super.beforeDelivery(method); try { xaResource.start(xid, XAResource.TMNOFLAGS); } catch (XAException e) { throw new ResourceException(e.getMessage(), e); } } @Override public void afterDelivery() throws ResourceException { try { xaResource.end(xid, XAResource.TMSUCCESS); } catch (XAException e) { throw new ResourceException(e.getMessage(), e); } super.afterDelivery(); } public void rollback() throws XAException { xaResource.rollback(xid); } public void prepare() throws XAException { xaResource.prepare(xid); } public void commit() throws XAException { xaResource.commit(xid, false); } } class PausingXADummyEndpoint extends XADummyEndpoint { private final CountDownLatch beforeDeliveryLatch; boolean interrupted = false; PausingXADummyEndpoint(CountDownLatch latch, CountDownLatch beforeDeliveryLatch) { super(latch); this.beforeDeliveryLatch = beforeDeliveryLatch; } @Override public void beforeDelivery(Method method) throws NoSuchMethodException, ResourceException { super.beforeDelivery(method); beforeDeliveryLatch.countDown(); } @Override public void onMessage(Message message) { super.onMessage(message); try { Thread.sleep(2000); } catch (InterruptedException e) { interrupted = true; } } @Override public void release() { try { prepare(); commit(); } catch (XAException e) { e.printStackTrace(); } super.release(); } } }