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