Package org.mule.transport.jms.integration

Source Code of org.mule.transport.jms.integration.AbstractJmsFunctionalTestCase

/*
* $Id: AbstractJmsFunctionalTestCase.java 21951 2011-05-19 16:01:57Z aperepel $
* --------------------------------------------------------------------------------------
* 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.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.jms.TopicPublisher;
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 org.junit.After;
import org.junit.Before;

/**
* 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/&lt;provider_name>/connector-&lt;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;
        }
    }

    /**
     * Since we are using JUnit 4, but the Mule Test Framework assumes JUnit 3, we
     * need to explicitly call the setUp and tearDown methods
     *
     * @throws Exception if, well, anything goes wrong
     */
    @Before
    public void before() throws Exception
    {
        super.setUp();
    }

    /**
     * Since we are using JUnit 4, but the Mule Test Framework assumes JUnit 3, we
     * need to explicitly call the setUp and tearDown methods
     *
     * @throws Exception if, well, anything goes wrong
     */
    @After
    public void after() throws Exception
    {
        super.tearDown();
    }

    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();
    }

    @Override
    protected void suitePreSetUp() throws Exception
    {
        super.suitePreSetUp();

        if (purgeQueuesOnPreSetUp)
        {
            purge(getInboundQueueName());
            purge(getOutboundQueueName());
            // TODO DZ: get all of the queue/topic names from the Mule config and just purge those
        }
    }

    /**
     * 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>
     *
     * @return
     */
    @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);
    }

    protected void doSetUp() throws Exception
    {
        super.doSetUp();
        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");
        }
    }

    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
     *
     * @param session
     * @param scenario
     * @return
     * @throws JMSException
     */
    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
     *
     * @param session
     * @param scenario
     * @return
     * @throws JMSException
     */
    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.
     * Typically this method is called from {@link #suitePreSetUp()} and {@link #suitePostTearDown()}, with proper super calls.
     * @param destination destination name without any protocol specifics
     * @see #suitePreSetUp()
     * @see #suitePostTearDown()
     */
    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
     *
     * @param destination
     * @param topic
     * @throws Exception
     */
    protected void purgeTopic(String destination, String topic) throws Exception
    {
        Connection c = null;
        Session s = null;

        try
        {
            logger.debug("purging topic : " + topic);
            c = (TopicConnection) 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");

            TopicPublisher t;

            if (client != null)
            {
                client.dispose();
            }

            MessageConsumer consumer = null;

            try
            {
                consumer = s.createDurableSubscriber((Topic) 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;

        public boolean isPersistent()
        {
            return persistent;
        }

        public void setPersistent(boolean persistent)
        {
            this.persistent = persistent;
        }

        public String getInputDestinationName()
        {
            return inputQueue;
        }

        public String getOutputDestinationName()
        {
            return outputQueue;
        }

        public void setInputDestinationName(String inputQueue)
        {
            this.inputQueue = inputQueue;
        }

        public void setOutputDestinationName(String outputQueue)
        {
            this.outputQueue = outputQueue;
        }

        public int getAcknowledge()
        {
            return Session.AUTO_ACKNOWLEDGE;
        }

        public void send(Session session, MessageProducer producer) throws JMSException
        {
            producer.send(session.createTextMessage(DEFAULT_INPUT_MESSAGE));
            applyTransaction(session);
        }

        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
    {

        public boolean isTransacted()
        {
            return false;
        }

        protected void applyTransaction(Session session) throws JMSException
        {
            // do nothing
        }
    }

    protected class ScenarioCommit extends AbstractScenario
    {

        public boolean isTransacted()
        {
            return true;
        }

        protected void applyTransaction(Session session) throws JMSException
        {
            session.commit();
        }
    }

    protected class ScenarioRollback extends AbstractScenario
    {

        public boolean isTransacted()
        {
            return true;
        }

        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;
        }
    }
}
TOP

Related Classes of org.mule.transport.jms.integration.AbstractJmsFunctionalTestCase

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.