1
2
3
4
5
6
7
8
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.junit4.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.transaction.HeuristicMixedException;
45 import javax.transaction.HeuristicRollbackException;
46 import javax.transaction.RollbackException;
47 import javax.transaction.SystemException;
48
49 import org.apache.commons.logging.Log;
50 import org.apache.commons.logging.LogFactory;
51
52 import static org.junit.Assert.assertEquals;
53 import static org.junit.Assert.assertNotNull;
54 import static org.junit.Assert.assertNull;
55 import static org.junit.Assert.assertTrue;
56 import static org.junit.Assert.fail;
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
99
100
101 public abstract class AbstractJmsFunctionalTestCase extends FunctionalTestCase
102 {
103
104 public static final String DEFAULT_INPUT_MESSAGE = "INPUT MESSAGE";
105 public static final String DEFAULT_OUTPUT_MESSAGE = "OUTPUT MESSAGE";
106
107 public static final String INBOUND_ENDPOINT_KEY = "inbound.destination";
108 public static final String OUTBOUND_ENDPOINT_KEY = "outbound.destination";
109
110 public static final String MIDDLE_ENDPOINT_KEY = "middle.destination";
111 public static final String MIDDLE2_ENDPOINT_KEY = "middle2.destination";
112 public static final String MIDDLE3_ENDPOINT_KEY = "middle3.destination";
113 public static final String BROADCAST_TOPIC_ENDPOINT_KEY = "broadcast.topic.destination";
114
115 protected static final Log logger = LogFactory.getLog("MULE_TESTS");
116
117 protected JmsVendorConfiguration jmsConfig = null;
118 protected Scenario scenarioNoTx;
119
120 protected Scenario scenarioCommit;
121 protected Scenario scenarioRollback;
122 protected Scenario scenarioNotReceive;
123 protected Scenario scenarioReceive;
124 protected boolean purgeQueuesOnPreSetUp = true;
125 protected boolean purgeQueuesOnTearDown = true;
126
127 private MuleClient client = null;
128
129
130
131
132 private boolean multipleProviders = true;
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148 public static Collection<?> jmsProviderConfigs()
149 {
150 JmsVendorConfiguration[][] configs;
151 URL url = ClassUtils.getResource("jms-vendor-configs.txt", AbstractJmsFunctionalTestCase.class);
152
153 if (url == null)
154 {
155 fail("Please specify the org.mule.transport.jms.integration.JmsVendorConfiguration " +
156 "implementation to use in jms-vendor-configs.txt on classpaath.");
157 return CollectionUtils.EMPTY_COLLECTION;
158 }
159
160 if (logger.isInfoEnabled())
161 {
162 logger.info("Parameterized test using: " + url);
163 }
164
165 try
166 {
167 List<?> classes = IOUtils.readLines(url.openStream());
168 configs = new JmsVendorConfiguration[1][classes.size()];
169 int i = 0;
170 for (Iterator<?> iterator = classes.iterator(); iterator.hasNext(); i++)
171 {
172 String cls = (String) iterator.next();
173 configs[0][i] = (JmsVendorConfiguration) ClassUtils.instanciateClass(cls, ClassUtils.NO_ARGS);
174 }
175 return Arrays.asList(configs);
176 }
177 catch (Exception e)
178 {
179 fail("Please specify the org.mule.transport.jms.integration.JmsVendorConfiguration " +
180 "implementation to use in jms-vendor-configs.txt on classpath: " + e.getMessage());
181 return CollectionUtils.EMPTY_COLLECTION;
182 }
183 }
184
185 public AbstractJmsFunctionalTestCase()
186 {
187
188 this(((JmsVendorConfiguration[]) jmsProviderConfigs().iterator().next())[0]);
189 }
190
191 public AbstractJmsFunctionalTestCase(JmsVendorConfiguration config)
192 {
193 setJmsConfig(config);
194 scenarioNoTx = new NonTransactedScenario();
195 scenarioCommit = new ScenarioCommit();
196 scenarioRollback = new ScenarioRollback();
197 scenarioNotReceive = new ScenarioNotReceive();
198 scenarioReceive = new ScenarioReceive();
199 }
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216 @Override
217 protected Properties getStartUpProperties()
218 {
219 Properties props = new Properties();
220
221 props.put(INBOUND_ENDPOINT_KEY, getJmsConfig().getInboundEndpoint());
222 props.put(OUTBOUND_ENDPOINT_KEY, getJmsConfig().getOutboundEndpoint());
223 props.put(MIDDLE_ENDPOINT_KEY, getJmsConfig().getMiddleEndpoint());
224 props.put(MIDDLE2_ENDPOINT_KEY, getJmsConfig().getMiddleEndpoint() + "2");
225 props.put(MIDDLE3_ENDPOINT_KEY, getJmsConfig().getMiddleEndpoint() + "3");
226
227 props.put(BROADCAST_TOPIC_ENDPOINT_KEY, getJmsConfig().getTopicBroadcastEndpoint());
228 props.put("protocol", getJmsConfig().getProtocol());
229
230 Map<?, ?> p = getJmsConfig().getProperties();
231 if (p != null)
232 {
233 props.putAll(p);
234 }
235 return props;
236 }
237
238
239
240
241
242
243
244
245
246 @Override
247 protected ConfigurationBuilder getBuilder() throws Exception
248 {
249 if (multipleProviders)
250 {
251 final String configResource = getConfigResources();
252
253 if (StringUtils.splitAndTrim(configResource, ",; ").length > 1)
254 {
255 throw new IllegalArgumentException("Parameterized tests don't support multiple " +
256 "config files as input: " + configResource);
257 }
258 String resources = configResource.substring(configResource.lastIndexOf("/") + 1);
259 resources = String.format("integration/%s/connector-%s,%s", getJmsConfig().getName(),
260 resources, getConfigResources());
261 SpringXmlConfigurationBuilder builder = new SpringXmlConfigurationBuilder(resources);
262 return builder;
263 }
264 else
265 {
266 return super.getBuilder();
267 }
268 }
269
270
271
272
273
274
275
276 public final JmsVendorConfiguration getJmsConfig()
277 {
278 if (jmsConfig == null)
279 {
280 jmsConfig = createJmsConfig();
281 }
282 return jmsConfig;
283 }
284
285
286
287
288
289
290
291 public final void setJmsConfig(JmsVendorConfiguration jmsConfig)
292 {
293 this.jmsConfig = jmsConfig;
294 }
295
296
297
298
299
300
301
302 protected JmsVendorConfiguration createJmsConfig()
303 {
304
305 return null;
306 }
307
308
309
310
311
312
313
314
315
316
317 protected final Connection getConnection(boolean topic, boolean xa) throws Exception
318 {
319 checkConfig();
320 return getJmsConfig().getConnection(topic, xa);
321 }
322
323
324
325
326
327
328
329
330
331 protected final String getInboundEndpoint()
332 {
333 checkConfig();
334 return getJmsConfig().getInboundEndpoint();
335 }
336
337
338
339
340
341
342
343
344
345 protected final String getOutboundEndpoint()
346 {
347 checkConfig();
348 return getJmsConfig().getOutboundEndpoint();
349 }
350
351
352
353
354
355
356
357
358
359 protected final String getInboundQueueName()
360 {
361 checkConfig();
362 return getJmsConfig().getInboundDestinationName();
363 }
364
365
366
367
368
369
370
371
372
373 protected final String getDeadLetterQueueName()
374 {
375 checkConfig();
376 return getJmsConfig().getDeadLetterDestinationName();
377 }
378
379
380
381
382
383
384
385
386
387 protected final String getOutboundQueueName()
388 {
389 checkConfig();
390 return getJmsConfig().getOutboundDestinationName();
391 }
392
393
394
395
396
397
398
399
400
401 protected final long getSmallTimeout()
402 {
403 checkConfig();
404 return getJmsConfig().getSmallTimeout();
405
406 }
407
408
409
410
411
412
413
414
415
416 protected final long getTimeout()
417 {
418 checkConfig();
419 return getJmsConfig().getTimeout();
420 }
421
422
423
424
425
426 protected void checkConfig()
427 {
428 if (getJmsConfig() == null)
429 {
430 throw new IllegalStateException("There must be a Jms Vendor config set on this test");
431 }
432 }
433
434 protected void dispatchMessage() throws Exception
435 {
436 dispatchMessage(DEFAULT_INPUT_MESSAGE);
437 }
438
439 protected void dispatchMessage(Object payload) throws Exception
440 {
441 dispatchMessage(payload, null);
442 }
443
444 protected void dispatchMessage(Object payload, Properties props) throws Exception
445 {
446 client.dispatch(getInboundEndpoint(), payload, props);
447 }
448
449 protected MuleMessage receiveMessage() throws Exception
450 {
451 return receiveMessage(DEFAULT_OUTPUT_MESSAGE);
452 }
453
454 protected MuleMessage receiveMessage(Object expected) throws Exception
455 {
456 MuleMessage result = client.request(getOutboundEndpoint(), getTimeout());
457 assertNotNull(result);
458 assertNotNull(result.getPayload());
459 assertNull(result.getExceptionPayload());
460 assertEquals(expected, result.getPayload());
461 return result;
462 }
463
464 protected MuleMessage receiveMessage(byte[] expected) throws Exception
465 {
466 MuleMessage result = client.request(getOutboundEndpoint(), getTimeout());
467 assertNotNull(result);
468 assertNotNull(result.getPayload());
469 assertNull(result.getExceptionPayload());
470 byte[] bytes = result.getPayloadAsBytes();
471 assertEquals("Wrong number of bytes", expected.length, bytes.length);
472 for (int i=0; i < expected.length; ++i)
473 {
474 assertEquals("Byte #" + i + " does not match", expected[i], bytes[i]);
475 }
476 return result;
477 }
478
479 public void runAsynchronousDispatching() throws Exception
480 {
481 dispatchMessage();
482 receiveMessage();
483 MuleMessage result = client.request(getOutboundEndpoint(), getSmallTimeout());
484 assertNull(result);
485 }
486
487 @Override
488 protected void doSetUp() throws Exception
489 {
490 super.doSetUp();
491
492 if (purgeQueuesOnPreSetUp)
493 {
494 purge(getInboundQueueName());
495 purge(getOutboundQueueName());
496
497 }
498
499 client = new MuleClient(muleContext);
500 Transaction tx = TransactionCoordination.getInstance().getTransaction();
501 if (tx != null)
502 {
503 TransactionCoordination.getInstance().unbindTransaction(tx);
504 logger.warn("Transaction was active when this test began");
505 }
506 }
507
508 @Override
509 protected void doTearDown() throws Exception
510 {
511 if (purgeQueuesOnTearDown)
512 {
513 purge(getInboundQueueName());
514 purge(getOutboundQueueName());
515 purgeTopics();
516 }
517
518 super.doTearDown();
519 if (client != null)
520 {
521 client.dispose();
522 }
523 Transaction tx = TransactionCoordination.getInstance().getTransaction();
524 if (tx != null)
525 {
526 TransactionCoordination.getInstance().unbindTransaction(tx);
527 logger.warn("Transaction was active when this test ended");
528 }
529 }
530
531 protected MuleClient getClient()
532 {
533 return client;
534 }
535
536 public void send(Scenario scenario) throws Exception
537 {
538 Connection connection = null;
539 try
540 {
541 connection = getConnection(false, false);
542 connection.start();
543 Session session = null;
544 try
545 {
546 session = connection.createSession(scenario.isTransacted(), scenario.getAcknowledge());
547 Destination destination = createInputDestination(session, scenario);
548 MessageProducer producer = null;
549 try
550 {
551 producer = session.createProducer(destination);
552 if (scenario.isPersistent())
553 {
554 producer.setDeliveryMode(DeliveryMode.PERSISTENT);
555 }
556 scenario.send(session, producer);
557 }
558 finally
559 {
560 if (producer != null)
561 {
562 producer.close();
563 }
564 }
565 }
566 finally
567 {
568 if (session != null)
569 {
570 session.close();
571 }
572 }
573 }
574 finally
575 {
576 if (connection != null)
577 {
578 connection.close();
579 }
580 }
581 }
582
583
584
585
586
587
588
589
590
591 protected Destination createInputDestination(Session session, Scenario scenario) throws JMSException
592 {
593 return session.createQueue(scenario.getInputDestinationName());
594 }
595
596
597
598
599
600
601
602
603
604 protected Destination createOutputDestination(Session session, Scenario scenario) throws JMSException
605 {
606 return session.createQueue(scenario.getOutputDestinationName());
607 }
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668 protected void purge(final String destination) throws JMSException
669 {
670 Connection c = null;
671 Session s = null;
672 try
673 {
674 logger.debug("purging queue : " + destination);
675 c = getConnection(false, false);
676 assertNotNull(c);
677 c.start();
678
679 s = c.createSession(false, Session.AUTO_ACKNOWLEDGE);
680 Destination d = s.createQueue(destination);
681 MessageConsumer consumer = s.createConsumer(d);
682
683 while (consumer.receiveNoWait() != null)
684 {
685 logger.debug("Destination " + destination + " isn't empty, draining it");
686 }
687 }
688 catch (Exception e)
689 {
690 logger.error("unable to purge : " + destination);
691 }
692 finally
693 {
694 if (c != null)
695 {
696 c.stop();
697 if (s != null)
698 {
699 s.close();
700 }
701 try
702 {
703 c.close();
704 }
705 catch (JMSException e)
706 {
707 logger.warn("Failed to close jms connection: " + e.getMessage());
708 }
709 }
710 }
711 }
712
713
714
715
716
717
718 protected void purgeTopics() throws Exception
719 {
720 String destination = "broadcast";
721 purgeTopic(destination, "Client1");
722 purgeTopic(destination, "Client2");
723 purgeTopic(destination, "mule.JmsConnectorC1.broadcast");
724 purgeTopic(destination, "mule.JmsConnectorC2.broadcast");
725 }
726
727
728
729
730
731
732
733
734 protected void purgeTopic(String destination, String topic) throws Exception
735 {
736 Connection c = null;
737 Session s = null;
738
739 try
740 {
741 logger.debug("purging topic : " + topic);
742 c = getConnection(true, false);
743 if (c == null)
744 {
745 logger.debug("could not create a connection to topic : " + destination);
746 }
747
748 c.start();
749 s = ((TopicConnection) c).createTopicSession(true, Session.SESSION_TRANSACTED);
750
751 logger.debug("created topic session");
752 Topic dest = s.createTopic(destination);
753 logger.debug("created topic destination");
754
755 if (client != null)
756 {
757 client.dispose();
758 }
759
760 MessageConsumer consumer = null;
761
762 try
763 {
764 consumer = s.createDurableSubscriber(dest, topic);
765 logger.debug("created consumer");
766 while (consumer.receiveNoWait() != null)
767 {
768 logger.debug("Topic " + topic + " isn't empty, draining it");
769 }
770 logger.debug("topic should be empty");
771 consumer.close();
772 s.unsubscribe(topic);
773 }
774 catch (JMSException e)
775 {
776 logger.debug("could not unsubscribe : " + topic);
777 }
778 }
779
780 finally
781 {
782 if (c != null)
783 {
784 c.stop();
785 if (s != null)
786 {
787 s.close();
788 }
789 try
790 {
791 c.close();
792 }
793 catch (JMSException e)
794 {
795 logger.warn("Failed to close jms connection: " + e.getMessage());
796 }
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
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 @Override
852 public boolean isPersistent()
853 {
854 return persistent;
855 }
856
857 @Override
858 public void setPersistent(boolean persistent)
859 {
860 this.persistent = persistent;
861 }
862
863 @Override
864 public String getInputDestinationName()
865 {
866 return inputQueue;
867 }
868
869 @Override
870 public String getOutputDestinationName()
871 {
872 return outputQueue;
873 }
874
875 @Override
876 public void setInputDestinationName(String inputQueue)
877 {
878 this.inputQueue = inputQueue;
879 }
880
881 @Override
882 public void setOutputDestinationName(String outputQueue)
883 {
884 this.outputQueue = outputQueue;
885 }
886
887 @Override
888 public int getAcknowledge()
889 {
890 return Session.AUTO_ACKNOWLEDGE;
891 }
892
893 @Override
894 public void send(Session session, MessageProducer producer) throws JMSException
895 {
896 producer.send(session.createTextMessage(DEFAULT_INPUT_MESSAGE));
897 applyTransaction(session);
898 }
899
900 @Override
901 public Message receive(Session session, MessageConsumer consumer) throws JMSException
902 {
903 Message message = consumer.receive(getTimeout());
904 assertNotNull(message);
905 assertTrue(TextMessage.class.isAssignableFrom(message.getClass()));
906 assertEquals(DEFAULT_OUTPUT_MESSAGE, ((TextMessage) message).getText());
907 applyTransaction(session);
908 return message;
909 }
910
911 abstract protected void applyTransaction(Session session) throws JMSException;
912 }
913
914 protected class NonTransactedScenario extends AbstractScenario
915 {
916 @Override
917 public boolean isTransacted()
918 {
919 return false;
920 }
921
922 @Override
923 protected void applyTransaction(Session session) throws JMSException
924 {
925
926 }
927 }
928
929 protected class ScenarioCommit extends AbstractScenario
930 {
931 @Override
932 public boolean isTransacted()
933 {
934 return true;
935 }
936
937 @Override
938 protected void applyTransaction(Session session) throws JMSException
939 {
940 session.commit();
941 }
942 }
943
944 protected class ScenarioRollback extends AbstractScenario
945 {
946 @Override
947 public boolean isTransacted()
948 {
949 return true;
950 }
951
952 @Override
953 protected void applyTransaction(Session session) throws JMSException
954 {
955 session.rollback();
956 }
957 }
958
959 protected class ScenarioNotReceive extends NonTransactedScenario
960 {
961 @Override
962 public Message receive(Session session, MessageConsumer consumer) throws JMSException
963 {
964 Message message = consumer.receive(getSmallTimeout());
965 assertNull(message);
966 return message;
967 }
968 }
969
970 protected class ScenarioReceive extends NonTransactedScenario
971 {
972 @Override
973 public Message receive(Session session, MessageConsumer consumer) throws JMSException
974 {
975 Message message = consumer.receive(getTimeout());
976 assertNotNull(message);
977 return message;
978 }
979 }
980 }