/**
* 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.cxf.systest.jms.tx;
import java.util.Collections;
import javax.jms.Connection;
import javax.jms.JMSException;
import javax.jms.Queue;
import javax.transaction.TransactionManager;
import javax.transaction.xa.XAException;
import org.apache.activemq.ActiveMQXAConnectionFactory;
import org.apache.activemq.jms.pool.JcaPooledConnectionFactory;
import org.apache.cxf.BusFactory;
import org.apache.cxf.configuration.ConfiguredBeanLocator;
import org.apache.cxf.jaxws.EndpointImpl;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.apache.cxf.systest.jms.AbstractVmJMSTest;
import org.apache.cxf.transport.jms.ConnectionFactoryFeature;
import org.apache.cxf.transport.jms.spec.JMSSpecConstants;
import org.apache.cxf.transport.jms.util.JMSUtil;
import org.apache.geronimo.transaction.manager.GeronimoTransactionManager;
import org.apache.hello_world_doc_lit.Greeter;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
public class JMSTransactionTest extends AbstractVmJMSTest {
private static final String SERVICE_ADDRESS =
"jms:queue:greeter.queue.tx?receivetTimeOut=5000&sessionTransacted=true";
private static EndpointImpl endpoint;
private static TransactionManager transactionManager;
public static void startBusAndJMS(Class<?> testClass) {
String brokerURI = "vm://" + testClass.getName() + "?broker.persistent=false&broker.useJmx=false";
startBusAndJMS(brokerURI);
startBroker(brokerURI);
}
public static void startBusAndJMS(String brokerURI) {
try {
transactionManager = new GeronimoTransactionManager();
} catch (XAException e) {
throw new IllegalStateException(e.getMessage(), e);
}
bus = BusFactory.getDefaultBus();
registerTransactionManager();
ActiveMQXAConnectionFactory cf1 = new ActiveMQXAConnectionFactory(brokerURI);
cf1.setRedeliveryPolicy(redeliveryPolicy());
JcaPooledConnectionFactory pcf = new JcaPooledConnectionFactory();
pcf.setTransactionManager(transactionManager);
pcf.setConnectionFactory(cf1);
cf = pcf;
cff = new ConnectionFactoryFeature(pcf);
}
/**
* For real world scenarios create a bean for the transaction manager in blueprint or spring
*/
private static void registerTransactionManager() {
ConfiguredBeanLocator cbl = bus.getExtension(ConfiguredBeanLocator.class);
MyBeanLocator mybl = new MyBeanLocator(cbl);
mybl.register("tm", transactionManager);
bus.setExtension(mybl, ConfiguredBeanLocator.class);
}
@BeforeClass
public static void startServers() throws Exception {
startBusAndJMS(JMSTransactionTest.class);
//startBusAndJMS("tcp://localhost:61616");
endpoint = new EndpointImpl(bus, new GreeterImplWithTransaction());
endpoint.setAddress(SERVICE_ADDRESS);
endpoint.setFeatures(Collections.singletonList(cff));
endpoint.publish();
}
@AfterClass
public static void clearProperty() {
endpoint.stop();
}
/**
* Request reply should not cause roll backs
*
* @throws Exception
*/
@Test
public void testNoTransactionRequestReply() throws Exception {
Greeter greeter = markForClose(createGreeterProxy());
greeter.greetMe("Good guy");
try {
greeter.greetMe("Bad guy");
Assert.fail("Expecting exception here");
} catch (Exception e) {
// Fine
}
}
@Test
public void testTransactionOneWay() throws Exception {
Connection conn = cf.createConnection();
conn.start();
Queue queue = JMSUtil.createQueue(conn, "ActiveMQ.DLQ");
assertNumMessagesInQueue("DLQ should be empty", conn, queue, 0, 1000);
Greeter greeter = markForClose(createGreeterProxy());
// Should be processed normally
greeter.greetMeOneWay(GreeterImplWithTransaction.GOOD_GUY);
assertNumMessagesInQueue("DLQ should be empty", conn, queue, 0, 1000);
// Should cause rollback, redelivery and in the end the message should go to the dead letter queue
greeter.greetMeOneWay(GreeterImplWithTransaction.BAD_GUY);
assertNumMessagesInQueue("Request should be put into DLQ", conn, queue, 1, 2000);
conn.close();
}
private Greeter createGreeterProxy() throws Exception {
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.setBus(bus);
factory.getFeatures().add(cff);
factory.setTransportId(JMSSpecConstants.SOAP_JMS_SPECIFICATION_TRANSPORTID);
factory.setServiceClass(Greeter.class);
factory.setAddress(SERVICE_ADDRESS);
return (Greeter)markForClose(factory.create());
}
private void assertNumMessagesInQueue(String message, Connection connection, Queue queue,
int expectedNum, int timeout) throws JMSException,
InterruptedException {
long startTime = System.currentTimeMillis();
int actualNum;
do {
actualNum = JMSUtil.getNumMessages(connection, queue);
//System.out.println("Messages in queue " + queue.getQueueName() + ": " + actualNum
// + ", expecting: " + expectedNum);
Thread.sleep(100);
} while ((System.currentTimeMillis() - startTime < timeout) && expectedNum != actualNum);
Assert.assertEquals(message + " -> number of messages", expectedNum, actualNum);
}
}