Java tutorial
/* * $Id$ * -------------------------------------------------------------------------------------- * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com * * The software in this package is published under the terms of the CPAL v1.0 * license, a copy of which has been included with this distribution in the * LICENSE.txt file. */ package org.mule.transport.jms.integration; import org.mule.api.MuleMessage; import org.mule.api.config.ConfigurationBuilder; import org.mule.api.transaction.Transaction; import org.mule.config.spring.SpringXmlConfigurationBuilder; import org.mule.module.client.MuleClient; import org.mule.tck.junit4.FunctionalTestCase; import org.mule.transaction.TransactionCoordination; import org.mule.util.ClassUtils; import org.mule.util.CollectionUtils; import org.mule.util.IOUtils; import org.mule.util.StringUtils; import java.net.URL; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import javax.jms.Connection; import javax.jms.DeliveryMode; import javax.jms.Destination; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageConsumer; import javax.jms.MessageProducer; import javax.jms.Session; import javax.jms.TextMessage; import javax.jms.Topic; import javax.jms.TopicConnection; import javax.transaction.HeuristicMixedException; import javax.transaction.HeuristicRollbackException; import javax.transaction.RollbackException; import javax.transaction.SystemException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; /** * This is the base class for all integration tests that are part of the JMS integration test suite. This is * a suite that can be run on multiple JMS providers since all configuration for the provider is abstracted into * a single class which implements {@link org.mule.transport.jms.integration.JmsVendorConfiguration}. The implementation * of this class is loaded by looking for the classname in a properties file called 'jms-vendor-configs.txt' in the root * classpath. * <p/> * This test case provides a number of support methods for testing Jms providers with Mule. This implementation is based * around the concept of scenarios. Scenarios define an action or set of actions and are represented as implementations * of {@link org.mule.transport.jms.integration.AbstractJmsFunctionalTestCase.Scenario}. Scenarios can be combined to create * a test. The default scenarios are usually sufficient to create a test. These are: * {@link org.mule.transport.jms.integration.AbstractJmsFunctionalTestCase.ScenarioReceive} * {@link org.mule.transport.jms.integration.AbstractJmsFunctionalTestCase.ScenarioNotReceive} * {@link org.mule.transport.jms.integration.AbstractJmsFunctionalTestCase.ScenarioCommit} * {@link org.mule.transport.jms.integration.AbstractJmsFunctionalTestCase.ScenarioRollback} * {@link org.mule.transport.jms.integration.AbstractJmsFunctionalTestCase.NonTransactedScenario} * <p/> * This object will also add properties to the registry that can be accessed within XML config files using placeholders. * The following properties are made available - * <ul> * <li>${inbound.destination} - the URI of the inbound destination (retrieved from an {@link org.mule.transport.jms.integration.JmsVendorConfiguration} implementation)</li> * <li>${outbound.destination} - the URI of the outbound destination (retrieved from an {@link org.mule.transport.jms.integration.JmsVendorConfiguration} implementation)</li> * <li>${middle.destination} - the URI of the middle destination (retrieved from an {@link org.mule.transport.jms.integration.JmsVendorConfiguration} implementation)</li> * <li>${middle2.destination} - the URI of a second middle destination 'middle2'.</li> * <li>${middle3.destination} - the URI of a third middle destination 'middle3'.</li> * <li>${broadcast.destination} - the URI of the broadcast topic (retrieved from an {@link org.mule.transport.jms.integration.JmsVendorConfiguration} implementation)</li> * <li>${protocol} - the protocol of the current messaging connector (retrieved from an {@link org.mule.transport.jms.integration.JmsVendorConfiguration} implementation)</li> * </ul> * <p/> * For each integration test there are 2 configuration files. One is provided by the JMS integration suite and defines the * event flow for the test. The other is a vendor-specific config file that defines the connectors and possibly endpoints and * transformers for the Jms connector being tested. These configurations are known as 'connector' files, they share the same * file name as the generic configuration file prepended with 'connector-'. The location of these files must be * <p/> * <code>integration/<provider_name>/connector-<event_flow_config_name></code> * <p/> * The 'provider_name' is obtained from the {@link org.mule.transport.jms.integration.JmsVendorConfiguration} implementation. * <p/> * In order to know what objects to define in the 'connector-' files you must copy the connector files from the ActiveMQ (default) * test suite and configure the objects to match the configuration in the ActiveMQ tests. Note that the object names must * be consistently the same for things to work. */ public abstract class AbstractJmsFunctionalTestCase extends FunctionalTestCase { public static final String DEFAULT_INPUT_MESSAGE = "INPUT MESSAGE"; public static final String DEFAULT_OUTPUT_MESSAGE = "OUTPUT MESSAGE"; public static final String INBOUND_ENDPOINT_KEY = "inbound.destination"; public static final String OUTBOUND_ENDPOINT_KEY = "outbound.destination"; public static final String MIDDLE_ENDPOINT_KEY = "middle.destination"; public static final String MIDDLE2_ENDPOINT_KEY = "middle2.destination"; public static final String MIDDLE3_ENDPOINT_KEY = "middle3.destination"; public static final String BROADCAST_TOPIC_ENDPOINT_KEY = "broadcast.topic.destination"; protected static final Log logger = LogFactory.getLog("MULE_TESTS"); protected JmsVendorConfiguration jmsConfig = null; protected Scenario scenarioNoTx; protected Scenario scenarioCommit; protected Scenario scenarioRollback; protected Scenario scenarioNotReceive; protected Scenario scenarioReceive; protected boolean purgeQueuesOnPreSetUp = true; protected boolean purgeQueuesOnTearDown = true; private MuleClient client = null; /** * This test case is refactored to support multiple JMS providers. */ private boolean multipleProviders = true; /** * Finds the {@link org.mule.transport.jms.integration.JmsVendorConfiguration} instances to test with by looking * in a file called "jms-vendor-configs.txt" which contains one or more fuly qualified classnames of * {@link org.mule.transport.jms.integration.JmsVendorConfiguration} instances to load. * * @return a collection of {@link org.mule.transport.jms.integration.JmsVendorConfiguration} instance to test * against. * * @throws Exception if the 'jms-vendor-configs.txt' cannot be loaded or the classes defined within that file * are not on the classpath * * TODO this method can return more than one provider, but our test class can only handle one at a time * IMPORTANT: Only set one class in 'jms-vendor-configs.txt' */ public static Collection<?> jmsProviderConfigs() { JmsVendorConfiguration[][] configs; URL url = ClassUtils.getResource("jms-vendor-configs.txt", AbstractJmsFunctionalTestCase.class); if (url == null) { fail("Please specify the org.mule.transport.jms.integration.JmsVendorConfiguration " + "implementation to use in jms-vendor-configs.txt on classpaath."); return CollectionUtils.EMPTY_COLLECTION; } if (logger.isInfoEnabled()) { logger.info("Parameterized test using: " + url); } try { List<?> classes = IOUtils.readLines(url.openStream()); configs = new JmsVendorConfiguration[1][classes.size()]; int i = 0; for (Iterator<?> iterator = classes.iterator(); iterator.hasNext(); i++) { String cls = (String) iterator.next(); configs[0][i] = (JmsVendorConfiguration) ClassUtils.instanciateClass(cls, ClassUtils.NO_ARGS); } return Arrays.asList(configs); } catch (Exception e) { fail("Please specify the org.mule.transport.jms.integration.JmsVendorConfiguration " + "implementation to use in jms-vendor-configs.txt on classpath: " + e.getMessage()); return CollectionUtils.EMPTY_COLLECTION; } } public AbstractJmsFunctionalTestCase() { // TODO jmsProviderConfigs() can return more than one provider, but our test class can only handle one at a time this(((JmsVendorConfiguration[]) jmsProviderConfigs().iterator().next())[0]); } public AbstractJmsFunctionalTestCase(JmsVendorConfiguration config) { setJmsConfig(config); scenarioNoTx = new NonTransactedScenario(); scenarioCommit = new ScenarioCommit(); scenarioRollback = new ScenarioRollback(); scenarioNotReceive = new ScenarioNotReceive(); scenarioReceive = new ScenarioReceive(); } /** * Adds the following properties to the registry so that the Xml configuration files can reference them. * <p/> * <ul> * <li>${inbound.destination} - the URI of the inbound destination (retrieved from an {@link org.mule.transport.jms.integration.JmsVendorConfiguration} implementation)</li> * <li>${outbound.destination} - the URI of the outbound destination (retrieved from an {@link org.mule.transport.jms.integration.JmsVendorConfiguration} implementation)</li> * <li>${middle.destination} - the URI of the middle destination (retrieved from an {@link org.mule.transport.jms.integration.JmsVendorConfiguration} implementation)</li> * <li>${middle2.destination} - the URI of a second middle destination 'middle2'.</li> * <li>${middle3.destination} - the URI of a third middle destination 'middle3'.</li> * <li>${broadcast.destination} - the URI of the broadcast topic (retrieved from an {@link org.mule.transport.jms.integration.JmsVendorConfiguration} implementation)</li> * <li>${protocol} - the protocol of the current messaging connector (retrieved from an {@link org.mule.transport.jms.integration.JmsVendorConfiguration} implementation)</li> * </ul> */ @Override protected Properties getStartUpProperties() { Properties props = new Properties(); // Inject endpoint names into the config props.put(INBOUND_ENDPOINT_KEY, getJmsConfig().getInboundEndpoint()); props.put(OUTBOUND_ENDPOINT_KEY, getJmsConfig().getOutboundEndpoint()); props.put(MIDDLE_ENDPOINT_KEY, getJmsConfig().getMiddleEndpoint()); props.put(MIDDLE2_ENDPOINT_KEY, getJmsConfig().getMiddleEndpoint() + "2"); props.put(MIDDLE3_ENDPOINT_KEY, getJmsConfig().getMiddleEndpoint() + "3"); props.put(BROADCAST_TOPIC_ENDPOINT_KEY, getJmsConfig().getTopicBroadcastEndpoint()); props.put("protocol", getJmsConfig().getProtocol()); Map<?, ?> p = getJmsConfig().getProperties(); if (p != null) { props.putAll(p); } return props; } /** * This creates a {@link org.mule.config.spring.SpringXmlConfigurationBuilder} as expected but also figures out * which 'connector' configuration file to load with the event flow configuration (obtained from the overriding \ * class which implements {@link #getConfigResources()}). * * @return The config builder used to create the Mule instance for this test * @throws Exception */ @Override protected ConfigurationBuilder getBuilder() throws Exception { if (multipleProviders) { final String configResource = getConfigResources(); // multiple configs arent' supported by this mechanism, validate and fail if needed if (StringUtils.splitAndTrim(configResource, ",; ").length > 1) { throw new IllegalArgumentException( "Parameterized tests don't support multiple " + "config files as input: " + configResource); } String resources = configResource.substring(configResource.lastIndexOf("/") + 1); resources = String.format("integration/%s/connector-%s,%s", getJmsConfig().getName(), resources, getConfigResources()); SpringXmlConfigurationBuilder builder = new SpringXmlConfigurationBuilder(resources); return builder; } else { return super.getBuilder(); } } /** * Returns the {@link org.mule.transport.jms.integration.JmsVendorConfiguration} implemetation to be used * with this test * * @return settings for this test */ public final JmsVendorConfiguration getJmsConfig() { if (jmsConfig == null) { jmsConfig = createJmsConfig(); } return jmsConfig; } /** * Sets the {@link org.mule.transport.jms.integration.JmsVendorConfiguration} implemetation to be used * with this test * * @param jmsConfig the settings for this test */ public final void setJmsConfig(JmsVendorConfiguration jmsConfig) { this.jmsConfig = jmsConfig; } /** * Overriding classes must override this or inject this object. It is responsible for creating the * {@link org.mule.transport.jms.integration.JmsVendorConfiguration} instance to be used by this test. * * @return the settings for this test */ protected JmsVendorConfiguration createJmsConfig() { // Overriding classes must override this or inject this object return null; } /** * Create a connection factory for the Jms profider being tested. This calls * through to {@link org.mule.transport.jms.integration.JmsVendorConfiguration#getConnection(boolean, boolean)} * * @param topic whether to use a topic or queue connection factory, for 1.1 * implementations this proerty can be ignored * @param xa whether to create an XA connection factory * @return a new JMS connection */ protected final Connection getConnection(boolean topic, boolean xa) throws Exception { checkConfig(); return getJmsConfig().getConnection(topic, xa); } /** * Returns the {@link #getInboundQueueName()} in the form of an endpoint URI i.e. * jms://in. * <p/> * This calls through to {@link JmsVendorConfiguration#getInboundEndpoint()} * * @return the Inbound JMS endpoint */ protected final String getInboundEndpoint() { checkConfig(); return getJmsConfig().getInboundEndpoint(); } /** * Returns the {@link #getOutboundQueueName()} in the form of an endpoint URI i.e. * jms://out. * <p/> * This calls through to {@link org.mule.transport.jms.integration.JmsVendorConfiguration#getOutboundEndpoint()} * * @return the Outbound JMS endpoint */ protected final String getOutboundEndpoint() { checkConfig(); return getJmsConfig().getOutboundEndpoint(); } /** * The test inbound queue name. For consistency this should always be 'in'. Note that you need to make * sure that this queue is available in the the JMS provider being tested. * <p/> * This calls through to {@link org.mule.transport.jms.integration.JmsVendorConfiguration#getInboundDestinationName()} * * @return The test inbound destination name */ protected final String getInboundQueueName() { checkConfig(); return getJmsConfig().getInboundDestinationName(); } /** * The test dead letter queue name. For consistency this should always be 'dlq'. Note that you need to make * sure that this queue is available in the the JMS provider being tested. * <p/> * This calls through to {@link org.mule.transport.jms.integration.JmsVendorConfiguration#getDeadLetterDestinationName()} * * @return The test inbound destination name */ protected final String getDeadLetterQueueName() { checkConfig(); return getJmsConfig().getDeadLetterDestinationName(); } /** * The test outbound queue name. For consistency this should always be 'out'. Note that you need to make * sure that this queue is available in the the JMS provider being tested. * <p/> * This calls through to {@link org.mule.transport.jms.integration.JmsVendorConfiguration#getOutboundDestinationName()} * * @return The test outbound destination name */ protected final String getOutboundQueueName() { checkConfig(); return getJmsConfig().getOutboundDestinationName(); } /** * Timeout in milliseconds used when checking that a message is NOT present. This is usually 1000-2000ms. * It is customizable so that slow connections i.e. over a wAN can be accounted for. * <p/> * This calls through to {@link JmsVendorConfiguration#getSmallTimeout()} * * @return timeout in milliseconds used when checking that a message is NOT present */ protected final long getSmallTimeout() { checkConfig(); return getJmsConfig().getSmallTimeout(); } /** * The timeout in milliseconds used when waiting for a message to arrive. This is usually 3000-5000ms. * However, it is customizable so that slow connections i.e. over a wAN can be accounted for. * <p/> * This calls through to {@link JmsVendorConfiguration#getTimeout()} * * @return The timeout used when waiting for a message to arrive */ protected final long getTimeout() { checkConfig(); return getJmsConfig().getTimeout(); } /** * Ensures that the {@link org.mule.transport.jms.integration.JmsVendorConfiguration} instance is not null * if it is an {@link IllegalStateException} will be thrown */ protected void checkConfig() { if (getJmsConfig() == null) { throw new IllegalStateException("There must be a Jms Vendor config set on this test"); } } protected void dispatchMessage() throws Exception { dispatchMessage(DEFAULT_INPUT_MESSAGE); } protected void dispatchMessage(Object payload) throws Exception { dispatchMessage(payload, null); } protected void dispatchMessage(Object payload, Properties props) throws Exception { client.dispatch(getInboundEndpoint(), payload, props); } protected MuleMessage receiveMessage() throws Exception { return receiveMessage(DEFAULT_OUTPUT_MESSAGE); } protected MuleMessage receiveMessage(Object expected) throws Exception { MuleMessage result = client.request(getOutboundEndpoint(), getTimeout()); assertNotNull(result); assertNotNull(result.getPayload()); assertNull(result.getExceptionPayload()); assertEquals(expected, result.getPayload()); return result; } protected MuleMessage receiveMessage(byte[] expected) throws Exception { MuleMessage result = client.request(getOutboundEndpoint(), getTimeout()); assertNotNull(result); assertNotNull(result.getPayload()); assertNull(result.getExceptionPayload()); byte[] bytes = result.getPayloadAsBytes(); assertEquals("Wrong number of bytes", expected.length, bytes.length); for (int i = 0; i < expected.length; ++i) { assertEquals("Byte #" + i + " does not match", expected[i], bytes[i]); } return result; } public void runAsynchronousDispatching() throws Exception { dispatchMessage(); receiveMessage(); MuleMessage result = client.request(getOutboundEndpoint(), getSmallTimeout()); assertNull(result); } @Override protected void doSetUp() throws Exception { super.doSetUp(); if (purgeQueuesOnPreSetUp) { purge(getInboundQueueName()); purge(getOutboundQueueName()); // TODO DZ: get all of the queue/topic names from the Mule config and just purge those } client = new MuleClient(muleContext); Transaction tx = TransactionCoordination.getInstance().getTransaction(); if (tx != null) { TransactionCoordination.getInstance().unbindTransaction(tx); logger.warn("Transaction was active when this test began"); } } @Override protected void doTearDown() throws Exception { if (purgeQueuesOnTearDown) { purge(getInboundQueueName()); purge(getOutboundQueueName()); purgeTopics(); } super.doTearDown(); if (client != null) { client.dispose(); } Transaction tx = TransactionCoordination.getInstance().getTransaction(); if (tx != null) { TransactionCoordination.getInstance().unbindTransaction(tx); logger.warn("Transaction was active when this test ended"); } } protected MuleClient getClient() { return client; } public void send(Scenario scenario) throws Exception { Connection connection = null; try { connection = getConnection(false, false); connection.start(); Session session = null; try { session = connection.createSession(scenario.isTransacted(), scenario.getAcknowledge()); Destination destination = createInputDestination(session, scenario); MessageProducer producer = null; try { producer = session.createProducer(destination); if (scenario.isPersistent()) { producer.setDeliveryMode(DeliveryMode.PERSISTENT); } scenario.send(session, producer); } finally { if (producer != null) { producer.close(); } } } finally { if (session != null) { session.close(); } } } finally { if (connection != null) { connection.close(); } } } /** * By default this will create a Queue, override to create a topic */ protected Destination createInputDestination(Session session, Scenario scenario) throws JMSException { return session.createQueue(scenario.getInputDestinationName()); } /** * By default this will create a Queue, override to create a topic */ protected Destination createOutputDestination(Session session, Scenario scenario) throws JMSException { return session.createQueue(scenario.getOutputDestinationName()); } public Message receive(Scenario scenario) throws Exception { assertNotNull("scenario is null!", scenario); Connection connection = null; try { connection = getConnection(false, false); connection.start(); Session session = null; try { session = connection.createSession(scenario.isTransacted(), scenario.getAcknowledge()); Destination destination = createOutputDestination(session, scenario); MessageConsumer consumer = null; try { consumer = session.createConsumer(destination); return scenario.receive(session, consumer); } finally { if (consumer != null) { consumer.close(); } } } finally { if (session != null) { session.close(); } } } finally { if (connection != null) { try { connection.close(); } catch (JMSException e) { logger.warn("Failed to close jms connection: " + e.getMessage()); } } } } /** * Purge destinations for clean test setup. Especially applicable to WMQ tests, as messages from * other tests may still exist from other tests' runs. * <p/> * Well-behaving tests should drain both inbound and outbound destinations, as well as any intermediary ones. * @param destination destination name without any protocol specifics */ protected void purge(final String destination) throws JMSException { Connection c = null; Session s = null; try { logger.debug("purging queue : " + destination); c = getConnection(false, false); assertNotNull(c); c.start(); s = c.createSession(false, Session.AUTO_ACKNOWLEDGE); Destination d = s.createQueue(destination); MessageConsumer consumer = s.createConsumer(d); while (consumer.receiveNoWait() != null) { logger.debug("Destination " + destination + " isn't empty, draining it"); } } catch (Exception e) { logger.error("unable to purge : " + destination); } finally { if (c != null) { c.stop(); if (s != null) { s.close(); } try { c.close(); } catch (JMSException e) { logger.warn("Failed to close jms connection: " + e.getMessage()); } } } } /** * Purge all of the topics which are created during testing * TODO DZ: we should be getting this list dynamically, and only calling them for the topic tests * @throws Exception */ protected void purgeTopics() throws Exception { String destination = "broadcast"; purgeTopic(destination, "Client1"); purgeTopic(destination, "Client2"); purgeTopic(destination, "mule.JmsConnectorC1.broadcast"); purgeTopic(destination, "mule.JmsConnectorC2.broadcast"); } /** * Clear the specified topic */ protected void purgeTopic(String destination, String topic) throws Exception { Connection c = null; Session s = null; try { logger.debug("purging topic : " + topic); c = getConnection(true, false); if (c == null) { logger.debug("could not create a connection to topic : " + destination); } c.start(); s = ((TopicConnection) c).createTopicSession(true, Session.SESSION_TRANSACTED); logger.debug("created topic session"); Topic dest = s.createTopic(destination); logger.debug("created topic destination"); if (client != null) { client.dispose(); } MessageConsumer consumer = null; try { consumer = s.createDurableSubscriber(dest, topic); logger.debug("created consumer"); while (consumer.receiveNoWait() != null) { logger.debug("Topic " + topic + " isn't empty, draining it"); } logger.debug("topic should be empty"); consumer.close(); s.unsubscribe(topic); } catch (JMSException e) { logger.debug("could not unsubscribe : " + topic); } } finally { if (c != null) { c.stop(); if (s != null) { s.close(); } try { c.close(); } catch (JMSException e) { logger.warn("Failed to close jms connection: " + e.getMessage()); } } } logger.debug("completed draining topic :" + topic); } public boolean isMultipleProviders() { return multipleProviders; } public void setMultipleProviders(boolean multipleProviders) { this.multipleProviders = multipleProviders; } // ///////////////////////////////////////////////////////////////////////////////////////////////// // Test Scenarios // ///////////////////////////////////////////////////////////////////////////////////////////////// protected interface Scenario { boolean isPersistent(); void setPersistent(boolean persistent); String getInputDestinationName(); void setInputDestinationName(String inputQueue); String getOutputDestinationName(); void setOutputDestinationName(String outputQueue); int getAcknowledge(); void send(Session session, MessageProducer producer) throws JMSException, SystemException, HeuristicMixedException, HeuristicRollbackException, RollbackException; Message receive(Session session, MessageConsumer consumer) throws JMSException, SystemException, HeuristicMixedException, HeuristicRollbackException, RollbackException; boolean isTransacted(); } protected abstract class AbstractScenario implements Scenario { private String inputQueue = getInboundQueueName(); private String outputQueue = getOutboundQueueName(); private boolean persistent = false; @Override public boolean isPersistent() { return persistent; } @Override public void setPersistent(boolean persistent) { this.persistent = persistent; } @Override public String getInputDestinationName() { return inputQueue; } @Override public String getOutputDestinationName() { return outputQueue; } @Override public void setInputDestinationName(String inputQueue) { this.inputQueue = inputQueue; } @Override public void setOutputDestinationName(String outputQueue) { this.outputQueue = outputQueue; } @Override public int getAcknowledge() { return Session.AUTO_ACKNOWLEDGE; } @Override public void send(Session session, MessageProducer producer) throws JMSException { producer.send(session.createTextMessage(DEFAULT_INPUT_MESSAGE)); applyTransaction(session); } @Override public Message receive(Session session, MessageConsumer consumer) throws JMSException { Message message = consumer.receive(getTimeout()); assertNotNull(message); assertTrue(TextMessage.class.isAssignableFrom(message.getClass())); assertEquals(DEFAULT_OUTPUT_MESSAGE, ((TextMessage) message).getText()); applyTransaction(session); return message; } abstract protected void applyTransaction(Session session) throws JMSException; } protected class NonTransactedScenario extends AbstractScenario { @Override public boolean isTransacted() { return false; } @Override protected void applyTransaction(Session session) throws JMSException { // do nothing } } protected class ScenarioCommit extends AbstractScenario { @Override public boolean isTransacted() { return true; } @Override protected void applyTransaction(Session session) throws JMSException { session.commit(); } } protected class ScenarioRollback extends AbstractScenario { @Override public boolean isTransacted() { return true; } @Override protected void applyTransaction(Session session) throws JMSException { session.rollback(); } } protected class ScenarioNotReceive extends NonTransactedScenario { @Override public Message receive(Session session, MessageConsumer consumer) throws JMSException { Message message = consumer.receive(getSmallTimeout()); assertNull(message); return message; } } protected class ScenarioReceive extends NonTransactedScenario { @Override public Message receive(Session session, MessageConsumer consumer) throws JMSException { Message message = consumer.receive(getTimeout()); assertNotNull(message); return message; } } }