View Javadoc

1   /*
2    * $Id: AbstractJmsFunctionalTestCase.java 19500 2010-09-09 17:29:17Z dzapata $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.transport.jms.integration;
12  
13  import org.mule.api.MuleMessage;
14  import org.mule.api.config.ConfigurationBuilder;
15  import org.mule.api.transaction.Transaction;
16  import org.mule.config.spring.SpringXmlConfigurationBuilder;
17  import org.mule.module.client.MuleClient;
18  import org.mule.tck.FunctionalTestCase;
19  import org.mule.transaction.TransactionCoordination;
20  import org.mule.util.ClassUtils;
21  import org.mule.util.CollectionUtils;
22  import org.mule.util.IOUtils;
23  import org.mule.util.StringUtils;
24  
25  import java.net.URL;
26  import java.util.Arrays;
27  import java.util.Collection;
28  import java.util.Iterator;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Properties;
32  
33  import javax.jms.Connection;
34  import javax.jms.DeliveryMode;
35  import javax.jms.Destination;
36  import javax.jms.JMSException;
37  import javax.jms.Message;
38  import javax.jms.MessageConsumer;
39  import javax.jms.MessageProducer;
40  import javax.jms.Session;
41  import javax.jms.TextMessage;
42  import javax.jms.Topic;
43  import javax.jms.TopicConnection;
44  import javax.jms.TopicPublisher;
45  import javax.transaction.HeuristicMixedException;
46  import javax.transaction.HeuristicRollbackException;
47  import javax.transaction.RollbackException;
48  import javax.transaction.SystemException;
49  
50  import org.apache.commons.logging.Log;
51  import org.apache.commons.logging.LogFactory;
52  import org.junit.After;
53  import org.junit.Before;
54  
55  /**
56   * This is the base class for all integration tests that are part of the JMS integration test suite.  This is
57   * a suite that can be run on multiple JMS providers since all configuration for the provider is abstracted into
58   * a single class which implements {@link org.mule.transport.jms.integration.JmsVendorConfiguration}.  The implementation
59   * of this class is loaded by looking for the classname in a properties file called 'jms-vendor-configs.txt'in the root
60   * classpath.
61   * <p/>
62   * This test case provides a number of support methods for testing Jms providers with Mule.  This implementation is based
63   * around the concept of scenarios.  Scenarios define an action or set of actions and are represented as implementations
64   * of {@link org.mule.transport.jms.integration.AbstractJmsFunctionalTestCase.Scenario}.  Scenarios can be combined to create
65   * a test.  The default scenarios are usually sufficient to create a test.  These are:
66   * {@link org.mule.transport.jms.integration.AbstractJmsFunctionalTestCase.ScenarioReceive}
67   * {@link org.mule.transport.jms.integration.AbstractJmsFunctionalTestCase.ScenarioNotReceive}
68   * {@link org.mule.transport.jms.integration.AbstractJmsFunctionalTestCase.ScenarioCommit}
69   * {@link org.mule.transport.jms.integration.AbstractJmsFunctionalTestCase.ScenarioRollback}
70   * {@link org.mule.transport.jms.integration.AbstractJmsFunctionalTestCase.NonTransactedScenario}
71   * <p/>
72   * This object will also add properties to the registry that can be accessed within XML config files using placeholders.
73   * The following properties are made available -
74   * <ul>
75   * <li>${inbound.destination} - the URI of the inbound destination (retrieved from an {@link org.mule.transport.jms.integration.JmsVendorConfiguration} implementation)</li>
76   * <li>${outbound.destination} - the URI of the outbound destination (retrieved from an {@link org.mule.transport.jms.integration.JmsVendorConfiguration implementation)</li>
77   * <li>${middle.destination} - the URI of the middle destination (retrieved from an {@link org.mule.transport.jms.integration.JmsVendorConfiguration} implementation)</li>
78   * <li>${middle2.destination} - the URI of a second middle destination 'middle2'.</li>
79   * <li>${middle3.destination} - the URI of a third middle destination 'middle3'.</li>
80   * <li>${broadcast.destination} - the URI of the broadcast topic (retrieved from an {@link org.mule.transport.jms.integration.JmsVendorConfiguration} implementation)</li>
81   * <li>${protocol} - the protocol of the current messaging connector (retrieved from an {@link org.mule.transport.jms.integration.JmsVendorConfiguration} implementation)</li>
82   * </ul>
83   * <p/>
84   * For each integration test there are 2 configuration files. One is provided by the JMS integration suite and defines the
85   * event flow for the test. The other is a vendor-specific config file that defines the connectors and possibly endpoints and
86   * transformers for the Jms connector being tested. These configurations are known as 'connector' files, they share the same
87   * file name as the generic configuration file prepended with 'connector-'.  The location of these files must be
88   * <p/>
89   * <code>
90   * integration/&lt;provider_name>/connector-&lt;event_flow_config_name></code>
91   * <p/>
92   * The 'provider_name' is obtained from the {@link org.mule.transport.jms.integration.JmsVendorConfiguration} implementation.
93   * <p/>
94   * In order to know what objects to define in the 'connector-' files you must copy the connector files from the ActiveMQ (default)
95   * test suite and configure the objects to match the configuration in the ActiveMQ tests.  Note that the object names must
96   * be consistently the same for things to work.
97   */
98  public abstract class AbstractJmsFunctionalTestCase extends FunctionalTestCase
99  {
100 
101     public static final String DEFAULT_INPUT_MESSAGE = "INPUT MESSAGE";
102     public static final String DEFAULT_OUTPUT_MESSAGE = "OUTPUT MESSAGE";
103 
104     public static final String INBOUND_ENDPOINT_KEY = "inbound.destination";
105     public static final String OUTBOUND_ENDPOINT_KEY = "outbound.destination";
106 
107     public static final String MIDDLE_ENDPOINT_KEY = "middle.destination";
108     public static final String MIDDLE2_ENDPOINT_KEY = "middle2.destination";
109     public static final String MIDDLE3_ENDPOINT_KEY = "middle3.destination";
110     public static final String BROADCAST_TOPIC_ENDPOINT_KEY = "broadcast.topic.destination";
111 
112     protected static final Log logger = LogFactory.getLog("MULE_TESTS");
113 
114     protected JmsVendorConfiguration jmsConfig = null;
115     protected Scenario scenarioNoTx;
116 
117     protected Scenario scenarioCommit;
118     protected Scenario scenarioRollback;
119     protected Scenario scenarioNotReceive;
120     protected Scenario scenarioReceive;
121 
122     private MuleClient client = null;
123 
124     /**
125      * This test case is refactored to support multiple JMS providers.
126      */
127     private boolean multipleProviders = true;
128 
129     /**
130      * Finds the {@link org.mule.transport.jms.integration.JmsVendorConfiguration} instances to test with by looking
131      * in a file called "jms-vendor-configs.txt" which contains one or more fuly qualified classnames of
132      * {@link org.mule.transport.jms.integration.JmsVendorConfiguration} instances to load.
133      *
134      * @return a collection of {@link org.mule.transport.jms.integration.JmsVendorConfiguration} instance to test
135      * against.
136      *
137      * @throws Exception if the 'jms-vendor-configs.txt' cannot be loaded or the classes defined within that file
138      * are not on the classpath
139      *
140      * TODO this method can return more than one provider, but our test class can only handle one at a time
141      * IMPORTANT: Only set one class in 'jms-vendor-configs.txt'
142      */
143     public static Collection jmsProviderConfigs()
144     {
145         JmsVendorConfiguration[][] configs;
146         URL url = ClassUtils.getResource("jms-vendor-configs.txt", AbstractJmsFunctionalTestCase.class);
147 
148         if (url == null)
149         {
150             fail("Please specify the org.mule.transport.jms.integration.JmsVendorConfiguration " +
151                  "implementation to use in jms-vendor-configs.txt on classpaath.");
152             return CollectionUtils.EMPTY_COLLECTION;
153         }
154 
155         if (logger.isInfoEnabled())
156         {
157             logger.info("Parameterized test using: " + url);
158         }
159 
160         try
161         {
162             List classes = IOUtils.readLines(url.openStream());
163             configs = new JmsVendorConfiguration[1][classes.size()];
164             int i = 0;
165             for (Iterator iterator = classes.iterator(); iterator.hasNext(); i++)
166             {
167                 String cls = (String) iterator.next();
168                 configs[0][i] = (JmsVendorConfiguration) ClassUtils.instanciateClass(cls, ClassUtils.NO_ARGS);
169             }
170             return Arrays.asList(configs);
171         }
172         catch (Exception e)
173         {
174             fail("Please specify the org.mule.transport.jms.integration.JmsVendorConfiguration " +
175                  "implementation to use in jms-vendor-configs.txt on classpath: " + e.getMessage());
176             return CollectionUtils.EMPTY_COLLECTION;
177         }
178     }
179 
180     /**
181      * Since we are using JUnit 4, but the Mule Test Framework assumes JUnit 3, we
182      * need to explicitly call the setUp and tearDown methods
183      *
184      * @throws Exception if, well, anything goes wrong
185      */
186     @Before
187     public void before() throws Exception
188     {
189         super.setUp();
190     }
191 
192     /**
193      * Since we are using JUnit 4, but the Mule Test Framework assumes JUnit 3, we
194      * need to explicitly call the setUp and tearDown methods
195      *
196      * @throws Exception if, well, anything goes wrong
197      */
198     @After
199     public void after() throws Exception
200     {
201         super.tearDown();
202     }
203 
204     public AbstractJmsFunctionalTestCase()
205     {
206         // TODO jmsProviderConfigs() can return more than one provider, but our test class can only handle one at a time
207         this(((JmsVendorConfiguration[]) jmsProviderConfigs().iterator().next())[0]);
208     }
209 
210     public AbstractJmsFunctionalTestCase(JmsVendorConfiguration config)
211     {
212         setJmsConfig(config);
213         scenarioNoTx = new NonTransactedScenario();
214         scenarioCommit = new ScenarioCommit();
215         scenarioRollback = new ScenarioRollback();
216         scenarioNotReceive = new ScenarioNotReceive();
217         scenarioReceive = new ScenarioReceive();
218     }
219 
220     @Override
221     protected void suitePreSetUp() throws Exception
222     {
223         super.suitePreSetUp();
224 
225         purge(getInboundQueueName());
226         purge(getOutboundQueueName());
227         // TODO DZ: get all of the queue/topic names from the Mule config and just purge those 
228     }
229 
230     /**
231      * Adds the following properties to the registry so that the Xml configuration files can reference them.
232      * <p/>
233      * <ul>
234      * <li>${inbound.destination} - the URI of the inbound destination (retrieved from an {@link org.mule.transport.jms.integration.JmsVendorConfiguration} implementation)</li>
235      * <li>${outbound.destination} - the URI of the outbound destination (retrieved from an {@link org.mule.transport.jms.integration.JmsVendorConfiguration implementation)</li>
236      * <li>${middle.destination} - the URI of the middle destination (retrieved from an {@link org.mule.transport.jms.integration.JmsVendorConfiguration} implementation)</li>
237      * <li>${middle2.destination} - the URI of a second middle destination 'middle2'.</li>
238      * <li>${middle3.destination} - the URI of a third middle destination 'middle3'.</li>
239      * <li>${broadcast.destination} - the URI of the broadcast topic (retrieved from an {@link org.mule.transport.jms.integration.JmsVendorConfiguration} implementation)</li>
240      * <li>${protocol} - the protocol of the current messaging connector (retrieved from an {@link org.mule.transport.jms.integration.JmsVendorConfiguration} implementation)</li>
241      * </ul>
242      *
243      * @return
244      */
245     @Override
246     protected Properties getStartUpProperties()
247     {
248         Properties props = new Properties();
249         // Inject endpoint names into the config
250         props.put(INBOUND_ENDPOINT_KEY, getJmsConfig().getInboundEndpoint());
251         props.put(OUTBOUND_ENDPOINT_KEY, getJmsConfig().getOutboundEndpoint());
252         props.put(MIDDLE_ENDPOINT_KEY, getJmsConfig().getMiddleEndpoint());
253         props.put(MIDDLE2_ENDPOINT_KEY, getJmsConfig().getMiddleEndpoint() + "2");
254         props.put(MIDDLE3_ENDPOINT_KEY, getJmsConfig().getMiddleEndpoint() + "3");
255 
256         props.put(BROADCAST_TOPIC_ENDPOINT_KEY, getJmsConfig().getTopicBroadcastEndpoint());
257         props.put("protocol", getJmsConfig().getProtocol());
258 
259         Map p = getJmsConfig().getProperties();
260         if (p != null)
261         {
262             props.putAll(p);
263         }
264         return props;
265     }
266 
267     /**
268      * This creates a {@link org.mule.config.spring.SpringXmlConfigurationBuilder} as expected but also figures out
269      * which 'connector' configuration file to load with the event flow configuration (obtained from the overriding \
270      * class which implements {@link #getConfigResources()}).
271      *
272      * @return The config builder used to create the Mule instance for this test
273      * @throws Exception
274      */
275     @Override
276     protected ConfigurationBuilder getBuilder() throws Exception
277     {
278         if (multipleProviders)
279         {
280             final String configResource = getConfigResources();
281             // multiple configs arent' supported by this mechanism, validate and fail if needed
282             if (StringUtils.splitAndTrim(configResource, ",; ").length > 1)
283             {
284                 throw new IllegalArgumentException("Parameterized tests don't support multiple " +
285                                                    "config files as input: " + configResource);
286             }
287             String resources = configResource.substring(configResource.lastIndexOf("/") + 1);
288             resources = String.format("integration/%s/connector-%s,%s", getJmsConfig().getName(),
289                                       resources, getConfigResources());
290             SpringXmlConfigurationBuilder builder = new SpringXmlConfigurationBuilder(resources);
291             return builder;
292         }
293         else
294         {
295             return super.getBuilder();
296         }
297     }
298 
299     /**
300      * Returns the {@link org.mule.transport.jms.integration.JmsVendorConfiguration} implemetation to be used
301      * with this test
302      *
303      * @return settings for this test
304      */
305     public final JmsVendorConfiguration getJmsConfig()
306     {
307         if (jmsConfig == null)
308         {
309             jmsConfig = createJmsConfig();
310         }
311         return jmsConfig;
312     }
313 
314     /**
315      * Sets the {@link org.mule.transport.jms.integration.JmsVendorConfiguration} implemetation to be used
316      * with this test
317      *
318      * @param jmsConfig the settings for this test
319      */
320     public final void setJmsConfig(JmsVendorConfiguration jmsConfig)
321     {
322         this.jmsConfig = jmsConfig;
323     }
324 
325     /**
326      * Overriding classes must override this or inject this object. It is responsible for creating the
327      * {@link org.mule.transport.jms.integration.JmsVendorConfiguration} instance to be used by this test.
328      *
329      * @return the settings for this test
330      */
331     protected JmsVendorConfiguration createJmsConfig()
332     {
333         // Overriding classes must override this or inject this object
334         return null;
335     }
336 
337     /**
338      * Create a connection factory for the Jms profider being tested.  This calls
339      * through to {@link org.mule.transport.jms.integration.JmsVendorConfiguration#getConnection(boolean, boolean)}
340      *
341      * @param topic whether to use a topic or queue connection factory, for 1.1
342      *              implementations this proerty can be ignored
343      * @param xa    whether to create an XA connection factory
344      * @return a new JMS connection
345      */
346     protected final Connection getConnection(boolean topic, boolean xa) throws Exception
347     {
348         checkConfig();
349         return getJmsConfig().getConnection(topic, xa);
350     }
351 
352     /**
353      * Returns the {@link #getInboundQueueName()} in the form of an endpoint URI i.e.
354      * jms://in.
355      * <p/>
356      * This calls through to {@link JmsVendorConfiguration#getInboundEndpoint()}
357      *
358      * @return the Inbound JMS endpoint
359      */
360     protected final String getInboundEndpoint()
361     {
362         checkConfig();
363         return getJmsConfig().getInboundEndpoint();
364     }
365 
366     /**
367      * Returns the {@link #getOutboundQueueName()} in the form of an endpoint URI i.e.
368      * jms://out.
369      * <p/>
370      * This calls through to {@link org.mule.transport.jms.integration.JmsVendorConfiguration#getOutboundEndpoint()}
371      *
372      * @return the Outbound JMS endpoint
373      */
374     protected final String getOutboundEndpoint()
375     {
376         checkConfig();
377         return getJmsConfig().getOutboundEndpoint();
378     }
379 
380     /**
381      * The test inbound queue name.  For consistency this should always be 'in'. Note that you need to make
382      * sure that this queue is available in the the JMS provider being tested.
383      * <p/>
384      * This calls through to {@link org.mule.transport.jms.integration.JmsVendorConfiguration#getInboundDestinationName()}
385      *
386      * @return The test inbound destination name
387      */
388     protected final String getInboundQueueName()
389     {
390         checkConfig();
391         return getJmsConfig().getInboundDestinationName();
392     }
393 
394     /**
395      * The test dead letter queue name.  For consistency this should always be 'dlq'. Note that you need to make
396      * sure that this queue is available in the the JMS provider being tested.
397      * <p/>
398      * This calls through to {@link org.mule.transport.jms.integration.JmsVendorConfiguration#getDeadLetterDestinationName()}
399      *
400      * @return The test inbound destination name
401      */    
402     protected final String getDeadLetterQueueName()
403     {
404         checkConfig();
405         return getJmsConfig().getDeadLetterDestinationName();
406     }    
407     
408     /**
409      * The test outbound queue name.  For consistency this should always be 'out'. Note that you need to make
410      * sure that this queue is available in the the JMS provider being tested.
411      * <p/>
412      * This calls through to {@link org.mule.transport.jms.integration.JmsVendorConfiguration#getOutboundDestinationName()}
413      *
414      * @return The test outbound destination name
415      */
416     protected final String getOutboundQueueName()
417     {
418         checkConfig();
419         return getJmsConfig().getOutboundDestinationName();
420     }
421 
422     /**
423      * Timeout in milliseconds used when checking that a message is NOT present. This is usually 1000-2000ms.
424      * It is customizable so that slow connections i.e. over a wAN can be accounted for.
425      * <p/>
426      * This calls through to {@link JmsVendorConfiguration#getSmallTimeout()}
427      *
428      * @return timeout in milliseconds used when checking that a message is NOT present
429      */
430     protected final long getSmallTimeout()
431     {
432         checkConfig();
433         return getJmsConfig().getSmallTimeout();
434 
435     }
436 
437     /**
438      * The timeout in milliseconds used when waiting for a message to arrive. This is usually 3000-5000ms.
439      * However, it is customizable so that slow connections i.e. over a wAN can be accounted for.
440      * <p/>
441      * This calls through to {@link JmsVendorConfiguration#getTimeout()}
442      *
443      * @return The timeout used when waiting for a message to arrive
444      */
445     protected final long getTimeout()
446     {
447         checkConfig();
448         return getJmsConfig().getTimeout();
449     }
450 
451     /**
452      * Ensures that the {@link org.mule.transport.jms.integration.JmsVendorConfiguration} instance is not null
453      * if it is an {@link IllegalStateException} will be thrown
454      */
455     protected void checkConfig()
456     {
457         if (getJmsConfig() == null)
458         {
459             throw new IllegalStateException("There must be a Jms Vendor config set on this test");
460         }
461     }
462 
463     protected void dispatchMessage() throws Exception
464     {
465         dispatchMessage(DEFAULT_INPUT_MESSAGE);
466     }
467 
468     protected void dispatchMessage(Object payload) throws Exception
469     {
470         dispatchMessage(payload, null);
471     }
472 
473     protected void dispatchMessage(Object payload, Properties props) throws Exception
474     {
475         client.dispatch(getInboundEndpoint(), payload, props);
476     }
477 
478     protected MuleMessage receiveMessage() throws Exception
479     {
480         return receiveMessage(DEFAULT_OUTPUT_MESSAGE);
481     }
482 
483     protected MuleMessage receiveMessage(Object expected) throws Exception
484     {
485         MuleMessage result = client.request(getOutboundEndpoint(), getTimeout());
486         assertNotNull(result);
487         assertNotNull(result.getPayload());
488         assertNull(result.getExceptionPayload());
489         assertEquals(expected, result.getPayload());
490         return result;
491     }
492 
493     protected MuleMessage receiveMessage(byte[] expected) throws Exception
494     {
495         MuleMessage result = client.request(getOutboundEndpoint(), getTimeout());
496         assertNotNull(result);
497         assertNotNull(result.getPayload());
498         assertNull(result.getExceptionPayload());
499         byte[] bytes = result.getPayloadAsBytes();
500         assertEquals("Wrong number of bytes", expected.length, bytes.length);
501         for (int i=0; i < expected.length; ++i)
502         {
503             assertEquals("Byte #" + i + " does not match", expected[i], bytes[i]);
504         }
505         return result;
506     }
507 
508     public void runAsynchronousDispatching() throws Exception
509     {
510         dispatchMessage();
511         receiveMessage();
512         MuleMessage result = client.request(getOutboundEndpoint(), getSmallTimeout());
513         assertNull(result);
514     }
515 
516     protected void doSetUp() throws Exception
517     {
518         super.doSetUp();
519         client = new MuleClient(muleContext);
520         Transaction tx = TransactionCoordination.getInstance().getTransaction();
521         if (tx != null)
522         {
523             TransactionCoordination.getInstance().unbindTransaction(tx);
524             logger.warn("Transaction was active when this test began");
525         }
526     }
527 
528     protected void doTearDown() throws Exception
529     {
530         purge(getInboundQueueName());
531         purge(getOutboundQueueName());
532         purgeTopics();
533 
534         super.doTearDown();
535         if (client != null)
536         {
537             client.dispose();
538         }
539         Transaction tx = TransactionCoordination.getInstance().getTransaction();
540         if (tx != null)
541         {
542             TransactionCoordination.getInstance().unbindTransaction(tx);
543             logger.warn("Transaction was active when this test ended");
544         }
545     }
546 
547     protected MuleClient getClient()
548     {
549         return client;
550     }
551 
552     public void send(Scenario scenario) throws Exception
553     {
554         Connection connection = null;
555         try
556         {
557             connection = getConnection(false, false);
558             connection.start();
559             Session session = null;
560             try
561             {
562                 session = connection.createSession(scenario.isTransacted(), scenario.getAcknowledge());
563                 Destination destination = createInputDestination(session, scenario);
564                 MessageProducer producer = null;
565                 try
566                 {
567                     producer = session.createProducer(destination);
568                     if (scenario.isPersistent())
569                     {
570                         producer.setDeliveryMode(DeliveryMode.PERSISTENT);
571                     }
572                     scenario.send(session, producer);
573                 }
574                 finally
575                 {
576                     if (producer != null)
577                     {
578                         producer.close();
579                     }
580                 }
581             }
582             finally
583             {
584                 if (session != null)
585                 {
586                     session.close();
587                 }
588             }
589         }
590         finally
591         {
592             if (connection != null)
593             {
594                 connection.close();
595             }
596         }
597     }
598 
599     /**
600      * By default this will create a Queue, override to create a topic
601      *
602      * @param session
603      * @param scenario
604      * @return
605      * @throws JMSException
606      */
607     protected Destination createInputDestination(Session session, Scenario scenario) throws JMSException
608     {
609         return session.createQueue(scenario.getInputDestinationName());
610     }
611 
612     /**
613      * By default this will create a Queue, override to create a topic
614      *
615      * @param session
616      * @param scenario
617      * @return
618      * @throws JMSException
619      */
620     protected Destination createOutputDestination(Session session, Scenario scenario) throws JMSException
621     {
622         return session.createQueue(scenario.getOutputDestinationName());
623     }
624 
625      /**/
626     public Message receive(Scenario scenario) throws Exception
627     {
628         assertNotNull("scenario is null!", scenario);
629         Connection connection = null;
630         try
631         {
632             connection = getConnection(false, false);
633             connection.start();
634             Session session = null;
635             try
636             {
637                 session = connection.createSession(scenario.isTransacted(), scenario.getAcknowledge());
638                 Destination destination = createOutputDestination(session, scenario);
639                 MessageConsumer consumer = null;
640                 try
641                 {
642                     consumer = session.createConsumer(destination);
643                     return scenario.receive(session, consumer);
644                 }
645                 finally
646                 {
647                     if (consumer != null)
648                     {
649                         consumer.close();
650                     }
651                 }
652             }
653             finally
654             {
655                 if (session != null)
656                 {
657                     session.close();
658                 }
659             }
660         }
661         finally
662         {
663             if (connection != null)
664             {
665                 connection.close();
666             }
667         }
668     }
669 
670     /**
671      * Purge destinations for clean test setup. Especially applicable to WMQ tests, as messages from
672      * other tests may still exist from other tests' runs.
673      * <p/>
674      * Well-behaving tests should drain both inbound and outbound destinations, as well as any intermediary ones.
675      * Typically this method is called from {@link #suitePreSetUp()} and {@link #suitePostTearDown()}, with proper super calls.
676      * @param destination destination name without any protocol specifics
677      * @see #suitePreSetUp()
678      * @see #suitePostTearDown()
679      */
680     protected void purge(final String destination) throws JMSException
681     {
682         Connection c = null;
683         Session s = null;
684         try
685         {
686             logger.debug("purging queue : " + destination);
687             c = getConnection(false, false);
688             assertNotNull(c);
689             c.start();
690 
691             s = c.createSession(false, Session.AUTO_ACKNOWLEDGE);
692             Destination d = s.createQueue(destination);
693             MessageConsumer consumer = s.createConsumer(d);
694 
695             while (consumer.receiveNoWait() != null)
696             {
697                 logger.debug("Destination " + destination + " isn't empty, draining it");
698             }
699         }
700         catch (Exception e)        
701         {
702             logger.error("unable to purge : " + destination);
703         }
704         finally
705         {
706             if (c != null)
707             {
708                 c.stop();
709                 if (s != null)
710                 {
711                     s.close();
712                 }
713                 c.close();
714             }
715         }
716     }
717 
718     /**
719      * Purge all of the topics which are created during testing
720      * TODO DZ: we should be getting this list dynamically, and only calling them for the topic tests
721      * @throws Exception
722      */
723     protected void purgeTopics() throws Exception
724     {
725         String destination = "broadcast";
726         purgeTopic(destination, "Client1");
727         purgeTopic(destination, "Client2");
728         purgeTopic(destination, "mule.JmsConnectorC1.broadcast");
729         purgeTopic(destination, "mule.JmsConnectorC2.broadcast");
730     }
731 
732     /**
733      * Clear the specified topic
734      *
735      * @param destination
736      * @param topic
737      * @throws Exception
738      */
739     protected void purgeTopic(String destination, String topic) throws Exception
740     {
741         Connection c = null;
742         Session s = null;
743 
744         try
745         {
746             logger.debug("purging topic : " + topic);
747             c = (TopicConnection) getConnection(true, false);
748             if (c == null)
749             {
750                 logger.debug("could not create a connection to topic : " + destination);
751             }
752 
753             c.start();
754             s = ((TopicConnection) c).createTopicSession(true, Session.SESSION_TRANSACTED);
755 
756             logger.debug("created topic session");
757             Topic dest = s.createTopic(destination);
758             logger.debug("created topic destination");
759 
760             TopicPublisher t;
761 
762             if (client != null)
763             {
764                 client.dispose();
765             }
766 
767             MessageConsumer consumer = null;
768 
769             try
770             {
771                 consumer = s.createDurableSubscriber((Topic) dest, topic);
772                 logger.debug("created consumer");
773                 while (consumer.receiveNoWait() != null)
774                 {
775                     logger.debug("Topic " + topic + " isn't empty, draining it");
776                 }
777                 logger.debug("topic should be empty");
778                 consumer.close();
779                 s.unsubscribe(topic);
780             }
781             catch (JMSException e)
782             {
783                 logger.debug("could not unsubscribe : " + topic);
784             }
785         }
786 
787         finally
788         {
789             if (c != null)
790             {
791                 c.stop();
792                 if (s != null)
793                 {
794                     s.close();
795                 }
796                 c.close();
797             }
798         }
799         logger.debug("completed draining topic :" + topic);
800     }
801 
802     public boolean isMultipleProviders()
803     {
804         return multipleProviders;
805     }
806 
807     public void setMultipleProviders(boolean multipleProviders)
808     {
809         this.multipleProviders = multipleProviders;
810     }
811 
812     // /////////////////////////////////////////////////////////////////////////////////////////////////
813     // Test Scenarios
814     // /////////////////////////////////////////////////////////////////////////////////////////////////
815 
816     protected interface Scenario
817     {
818 
819         boolean isPersistent();
820 
821         void setPersistent(boolean persistent);
822 
823         String getInputDestinationName();
824 
825         void setInputDestinationName(String inputQueue);
826 
827         String getOutputDestinationName();
828 
829         void setOutputDestinationName(String outputQueue);
830 
831         int getAcknowledge();
832 
833         void send(Session session, MessageProducer producer)
834                 throws JMSException, SystemException, HeuristicMixedException, HeuristicRollbackException,
835                        RollbackException;
836 
837         Message receive(Session session, MessageConsumer consumer)
838                 throws JMSException, SystemException, HeuristicMixedException, HeuristicRollbackException,
839                        RollbackException;
840 
841         boolean isTransacted();
842     }
843 
844     protected abstract class AbstractScenario implements Scenario
845     {
846 
847         private String inputQueue = getInboundQueueName();
848         private String outputQueue = getOutboundQueueName();
849         private boolean persistent = false;
850 
851         public boolean isPersistent()
852         {
853             return persistent;
854         }
855 
856         public void setPersistent(boolean persistent)
857         {
858             this.persistent = persistent;
859         }
860 
861         public String getInputDestinationName()
862         {
863             return inputQueue;
864         }
865 
866         public String getOutputDestinationName()
867         {
868             return outputQueue;
869         }
870 
871         public void setInputDestinationName(String inputQueue)
872         {
873             this.inputQueue = inputQueue;
874         }
875 
876         public void setOutputDestinationName(String outputQueue)
877         {
878             this.outputQueue = outputQueue;
879         }
880 
881         public int getAcknowledge()
882         {
883             return Session.AUTO_ACKNOWLEDGE;
884         }
885 
886         public void send(Session session, MessageProducer producer) throws JMSException
887         {
888             producer.send(session.createTextMessage(DEFAULT_INPUT_MESSAGE));
889             applyTransaction(session);
890         }
891 
892         public Message receive(Session session, MessageConsumer consumer) throws JMSException
893         {
894             Message message = consumer.receive(getTimeout());
895             assertNotNull(message);
896             assertTrue(TextMessage.class.isAssignableFrom(message.getClass()));
897             assertEquals(DEFAULT_OUTPUT_MESSAGE, ((TextMessage) message).getText());
898             applyTransaction(session);
899             return message;
900         }
901 
902         abstract protected void applyTransaction(Session session) throws JMSException;
903     }
904 
905     protected class NonTransactedScenario extends AbstractScenario
906     {
907 
908         public boolean isTransacted()
909         {
910             return false;
911         }
912 
913         protected void applyTransaction(Session session) throws JMSException
914         {
915             // do nothing
916         }
917     }
918 
919     protected class ScenarioCommit extends AbstractScenario
920     {
921 
922         public boolean isTransacted()
923         {
924             return true;
925         }
926 
927         protected void applyTransaction(Session session) throws JMSException
928         {
929             session.commit();
930         }
931     }
932 
933     protected class ScenarioRollback extends AbstractScenario
934     {
935 
936         public boolean isTransacted()
937         {
938             return true;
939         }
940 
941         protected void applyTransaction(Session session) throws JMSException
942         {
943             session.rollback();
944         }
945     }
946 
947     protected class ScenarioNotReceive extends NonTransactedScenario
948     {
949 
950         @Override
951         public Message receive(Session session, MessageConsumer consumer) throws JMSException
952         {
953             Message message = consumer.receive(getSmallTimeout());
954             assertNull(message);
955             return message;
956         }
957     }
958 
959     protected class ScenarioReceive extends NonTransactedScenario
960     {
961 
962         @Override
963         public Message receive(Session session, MessageConsumer consumer) throws JMSException
964         {
965             Message message = consumer.receive(getTimeout());
966             assertNotNull(message);
967             return message;
968         }
969     }
970 }