1
2
3
4
5
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
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
128
129 private boolean multipleProviders = true;
130
131
132
133
134
135
136
137
138
139
140
141
142
143
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
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
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
221
222
223
224
225
226
227
228
229
230
231
232
233
234 @Override
235 protected Properties getStartUpProperties()
236 {
237 Properties props = new Properties();
238
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
258
259
260
261
262
263
264 @Override
265 protected ConfigurationBuilder getBuilder() throws Exception
266 {
267 if (multipleProviders)
268 {
269 final String configResource = getConfigResources();
270
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
290
291
292
293
294 public final JmsVendorConfiguration getJmsConfig()
295 {
296 if (jmsConfig == null)
297 {
298 jmsConfig = createJmsConfig();
299 }
300 return jmsConfig;
301 }
302
303
304
305
306
307
308
309 public final void setJmsConfig(JmsVendorConfiguration jmsConfig)
310 {
311 this.jmsConfig = jmsConfig;
312 }
313
314
315
316
317
318
319
320 protected JmsVendorConfiguration createJmsConfig()
321 {
322
323 return null;
324 }
325
326
327
328
329
330
331
332
333
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
343
344
345
346
347
348
349 protected final String getInboundEndpoint()
350 {
351 checkConfig();
352 return getJmsConfig().getInboundEndpoint();
353 }
354
355
356
357
358
359
360
361
362
363 protected final String getOutboundEndpoint()
364 {
365 checkConfig();
366 return getJmsConfig().getOutboundEndpoint();
367 }
368
369
370
371
372
373
374
375
376
377 protected final String getInboundQueueName()
378 {
379 checkConfig();
380 return getJmsConfig().getInboundDestinationName();
381 }
382
383
384
385
386
387
388
389
390
391 protected final String getDeadLetterQueueName()
392 {
393 checkConfig();
394 return getJmsConfig().getDeadLetterDestinationName();
395 }
396
397
398
399
400
401
402
403
404
405 protected final String getOutboundQueueName()
406 {
407 checkConfig();
408 return getJmsConfig().getOutboundDestinationName();
409 }
410
411
412
413
414
415
416
417
418
419 protected final long getSmallTimeout()
420 {
421 checkConfig();
422 return getJmsConfig().getSmallTimeout();
423
424 }
425
426
427
428
429
430
431
432
433
434 protected final long getTimeout()
435 {
436 checkConfig();
437 return getJmsConfig().getTimeout();
438 }
439
440
441
442
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
581
582
583
584
585
586
587 protected Destination createInputDestination(Session session, Scenario scenario) throws JMSException
588 {
589 return session.createQueue(scenario.getInputDestinationName());
590 }
591
592
593
594
595
596
597
598
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
652
653
654
655
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
697
698
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
711
712
713
714
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
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
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 }