1
2
3
4
5
6
7
8
9
10
11 package org.mule.providers.jms;
12
13 import org.mule.MuleManager;
14 import org.mule.config.ExceptionHelper;
15 import org.mule.config.i18n.CoreMessages;
16 import org.mule.impl.internal.notifications.ConnectionNotification;
17 import org.mule.impl.internal.notifications.ConnectionNotificationListener;
18 import org.mule.impl.internal.notifications.NotificationException;
19 import org.mule.providers.AbstractConnector;
20 import org.mule.providers.ConnectException;
21 import org.mule.providers.FatalConnectException;
22 import org.mule.providers.ReplyToHandler;
23 import org.mule.providers.jms.i18n.JmsMessages;
24 import org.mule.providers.jms.xa.ConnectionFactoryWrapper;
25 import org.mule.providers.service.TransportFactoryException;
26 import org.mule.transaction.TransactionCoordination;
27 import org.mule.umo.MessagingException;
28 import org.mule.umo.TransactionException;
29 import org.mule.umo.UMOComponent;
30 import org.mule.umo.UMOException;
31 import org.mule.umo.UMOTransaction;
32 import org.mule.umo.endpoint.UMOEndpoint;
33 import org.mule.umo.endpoint.UMOImmutableEndpoint;
34 import org.mule.umo.lifecycle.InitialisationException;
35 import org.mule.umo.lifecycle.LifecycleException;
36 import org.mule.umo.manager.UMOServerNotification;
37 import org.mule.umo.provider.UMOMessageAdapter;
38 import org.mule.util.BeanUtils;
39 import org.mule.util.ClassUtils;
40
41 import java.lang.reflect.InvocationTargetException;
42 import java.text.MessageFormat;
43 import java.util.Hashtable;
44 import java.util.Map;
45
46 import javax.jms.Connection;
47 import javax.jms.ConnectionFactory;
48 import javax.jms.ExceptionListener;
49 import javax.jms.JMSException;
50 import javax.jms.MessageConsumer;
51 import javax.jms.MessageProducer;
52 import javax.jms.Session;
53 import javax.jms.TemporaryQueue;
54 import javax.jms.TemporaryTopic;
55 import javax.jms.XAConnectionFactory;
56 import javax.naming.Context;
57 import javax.naming.InitialContext;
58 import javax.naming.NamingException;
59
60 import org.apache.commons.lang.UnhandledException;
61
62
63
64
65
66
67
68 public class JmsConnector extends AbstractConnector implements ConnectionNotificationListener
69 {
70
71 static
72 {
73 ExceptionHelper.registerExceptionReader(new JmsExceptionReader());
74 }
75
76 private String connectionFactoryJndiName;
77
78 private ConnectionFactory connectionFactory;
79
80 private String connectionFactoryClass;
81
82 private String jndiInitialFactory;
83
84 private String jndiProviderUrl;
85
86 private int acknowledgementMode = Session.AUTO_ACKNOWLEDGE;
87
88 private String clientId;
89
90 private boolean durable;
91
92 private boolean noLocal;
93
94 private boolean persistentDelivery;
95
96 private boolean honorQosHeaders;
97
98 private Map jndiProviderProperties;
99
100 private Map connectionFactoryProperties;
101
102 private Connection connection;
103
104 private String specification = JmsConstants.JMS_SPECIFICATION_102B;
105
106 private JmsSupport jmsSupport;
107
108 private Context jndiContext;
109
110 private boolean jndiDestinations = false;
111
112 private boolean forceJndiDestinations = false;
113
114 public String username = null;
115
116 public String password = null;
117
118 private int maxRedelivery = 0;
119
120 private String redeliveryHandler = DefaultRedeliveryHandler.class.getName();
121
122 private boolean cacheJmsSessions = false;
123
124 private boolean recoverJmsConnections = true;
125
126 private JmsTopicResolver topicResolver;
127
128
129
130
131 private boolean eagerConsumer = true;
132
133 public JmsConnector()
134 {
135 super();
136 topicResolver = new DefaultJmsTopicResolver(this);
137 }
138
139 protected void doInitialise() throws InitialisationException
140 {
141
142 try
143 {
144 MuleManager.getInstance().registerListener(this, getName());
145
146 defaultInboundTransformer = serviceDescriptor.createNewInboundTransformer();
147 defaultOutboundTransformer = serviceDescriptor.createNewOutboundTransformer();
148 defaultResponseTransformer = serviceDescriptor.createNewResponseTransformer();
149 }
150 catch (NotificationException nex)
151 {
152 throw new InitialisationException(nex, this);
153 }
154 catch (TransportFactoryException tex)
155 {
156 throw new InitialisationException(tex, this);
157 }
158 }
159
160 protected void doDispose()
161 {
162 if (connection != null)
163 {
164 try
165 {
166 connection.close();
167 }
168 catch (JMSException e)
169 {
170 logger.error("Jms connector failed to dispose properly: ", e);
171 }
172 connection = null;
173 }
174
175 if (jndiContext != null)
176 {
177 try
178 {
179 jndiContext.close();
180 }
181 catch (NamingException e)
182 {
183 logger.error("Jms connector failed to dispose properly: ", e);
184 }
185
186 jndiContext = null;
187 }
188 }
189
190 protected void initJndiContext() throws NamingException, InitialisationException
191 {
192 if (jndiContext == null)
193 {
194 Hashtable props = new Hashtable();
195
196 if (jndiInitialFactory != null)
197 {
198 props.put(Context.INITIAL_CONTEXT_FACTORY, jndiInitialFactory);
199 }
200 else if (jndiProviderProperties == null
201 || !jndiProviderProperties.containsKey(Context.INITIAL_CONTEXT_FACTORY))
202 {
203 throw new InitialisationException(CoreMessages.objectIsNull("jndiInitialFactory"), this);
204 }
205
206 if (jndiProviderUrl != null)
207 {
208 props.put(Context.PROVIDER_URL, jndiProviderUrl);
209 }
210
211 if (jndiProviderProperties != null)
212 {
213 props.putAll(jndiProviderProperties);
214 }
215 jndiContext = new InitialContext(props);
216 }
217 }
218
219 protected void setConnection(Connection connection)
220 {
221 this.connection = connection;
222 }
223
224 protected ConnectionFactory createConnectionFactory() throws InitialisationException, NamingException
225 {
226
227 Object temp = jndiContext.lookup(connectionFactoryJndiName);
228
229 if (temp instanceof ConnectionFactory)
230 {
231 return (ConnectionFactory)temp;
232 }
233 else
234 {
235 throw new InitialisationException(
236 JmsMessages.invalidResourceType(ConnectionFactory.class,
237 (temp == null ? null : temp.getClass())), this);
238 }
239 }
240
241 protected Connection createConnection() throws NamingException, JMSException, InitialisationException
242 {
243 Connection connection;
244
245 if (connectionFactory == null)
246 {
247 connectionFactory = createConnectionFactory();
248 }
249
250 if (connectionFactory != null && connectionFactory instanceof XAConnectionFactory)
251 {
252 if (MuleManager.getInstance().getTransactionManager() != null)
253 {
254 connectionFactory = new ConnectionFactoryWrapper(connectionFactory, MuleManager.getInstance()
255 .getTransactionManager());
256 }
257 }
258
259 if (username != null)
260 {
261 connection = jmsSupport.createConnection(connectionFactory, username, password);
262 }
263 else
264 {
265 connection = jmsSupport.createConnection(connectionFactory);
266 }
267
268 if (clientId != null)
269 {
270 connection.setClientID(getClientId());
271 }
272
273
274
275
276 if (recoverJmsConnections && connectionStrategy != null && connection != null)
277 {
278 connection.setExceptionListener(new ExceptionListener()
279 {
280 public void onException(JMSException jmsException)
281 {
282 logger.debug("About to recycle myself due to remote JMS connection shutdown.");
283 final JmsConnector jmsConnector = JmsConnector.this;
284 try
285 {
286 jmsConnector.stopConnector();
287 jmsConnector.initialised.set(false);
288 }
289 catch (UMOException e)
290 {
291 logger.warn(e.getMessage(), e);
292 }
293
294 try
295 {
296 connectionStrategy.connect(jmsConnector);
297 jmsConnector.initialise();
298 jmsConnector.startConnector();
299 }
300 catch (FatalConnectException fcex)
301 {
302 logger.fatal("Failed to reconnect to JMS server. I'm giving up.");
303 }
304 catch (UMOException umoex)
305 {
306 throw new UnhandledException("Failed to recover a connector.", umoex);
307 }
308 }
309 });
310 }
311
312 return connection;
313 }
314
315 protected void doConnect() throws ConnectException
316 {
317 try
318 {
319
320
321
322
323
324 if (connectionFactoryClass != null)
325 {
326 connectionFactory = (ConnectionFactory)ClassUtils.instanciateClass(connectionFactoryClass,
327 ClassUtils.NO_ARGS);
328 }
329
330
331
332 if (connectionFactory == null || jndiInitialFactory != null)
333 {
334 initJndiContext();
335 }
336 else
337 {
338
339
340 jndiDestinations = false;
341 forceJndiDestinations = false;
342 }
343
344 if (jmsSupport == null)
345 {
346 if (JmsConstants.JMS_SPECIFICATION_102B.equals(specification))
347 {
348 jmsSupport = new Jms102bSupport(this, jndiContext, jndiDestinations,
349 forceJndiDestinations);
350 }
351 else
352 {
353 jmsSupport = new Jms11Support(this, jndiContext, jndiDestinations, forceJndiDestinations);
354 }
355 }
356 if (connectionFactory == null)
357 {
358 connectionFactory = createConnectionFactory();
359 }
360 if (connectionFactoryProperties != null && !connectionFactoryProperties.isEmpty())
361 {
362
363 BeanUtils.populateWithoutFail(connectionFactory, connectionFactoryProperties, true);
364 }
365 }
366 catch (Exception e)
367 {
368 throw new ConnectException(CoreMessages.failedToCreate("Jms Connector"), e, this);
369 }
370
371 try
372 {
373 connection = createConnection();
374 if (started.get())
375 {
376 connection.start();
377 }
378 }
379 catch (Exception e)
380 {
381 throw new ConnectException(e, this);
382 }
383 }
384
385 protected void doDisconnect() throws ConnectException
386 {
387 try
388 {
389 if (connection != null)
390 {
391 connection.close();
392 }
393 }
394 catch (Exception e)
395 {
396 throw new ConnectException(e, this);
397 }
398 finally
399 {
400
401 connection = null;
402 }
403 }
404
405 public UMOMessageAdapter getMessageAdapter(Object message) throws MessagingException
406 {
407 JmsMessageAdapter adapter = (JmsMessageAdapter)super.getMessageAdapter(message);
408 adapter.setSpecification(this.getSpecification());
409 return adapter;
410 }
411
412 protected Object getReceiverKey(UMOComponent component, UMOEndpoint endpoint)
413 {
414 return component.getDescriptor().getName() + "~" + endpoint.getEndpointURI().getAddress();
415 }
416
417 public Session getSessionFromTransaction()
418 {
419 UMOTransaction tx = TransactionCoordination.getInstance().getTransaction();
420 if (tx != null)
421 {
422 if (tx.hasResource(connection))
423 {
424 if (logger.isDebugEnabled())
425 {
426 logger.debug("Retrieving jms session from current transaction " + tx);
427 }
428
429 return (Session)tx.getResource(connection);
430 }
431 }
432 return null;
433 }
434
435 public Session getSession(UMOImmutableEndpoint endpoint) throws JMSException
436 {
437 final boolean topic = getTopicResolver().isTopic(endpoint);
438 return getSession(endpoint.getTransactionConfig().isTransacted(), topic);
439 }
440
441 public Session getSession(boolean transacted, boolean topic) throws JMSException
442 {
443 if (!isConnected())
444 {
445 throw new JMSException("Not connected");
446 }
447 Session session = getSessionFromTransaction();
448 if (session != null)
449 {
450 return session;
451 }
452
453 UMOTransaction tx = TransactionCoordination.getInstance().getTransaction();
454
455 if (logger.isDebugEnabled())
456 {
457 logger.debug(MessageFormat.format(
458 "Retrieving new jms session from connection: " +
459 "topic={0}, transacted={1}, ack mode={2}, nolocal={3}",
460 new Object[]{Boolean.valueOf(topic),
461 Boolean.valueOf(transacted),
462 new Integer(acknowledgementMode),
463 Boolean.valueOf(noLocal)}));
464 }
465
466 session = jmsSupport.createSession(connection, topic, transacted, acknowledgementMode, noLocal);
467 if (tx != null)
468 {
469 logger.debug("Binding session to current transaction");
470 try
471 {
472 tx.bindResource(connection, session);
473 }
474 catch (TransactionException e)
475 {
476 throw new RuntimeException("Could not bind session to current transaction", e);
477 }
478 }
479 return session;
480 }
481
482 protected void doStart() throws UMOException
483 {
484 if (connection != null)
485 {
486 try
487 {
488 connection.start();
489 }
490 catch (JMSException e)
491 {
492 throw new LifecycleException(CoreMessages.failedToStart("Jms Connection"), e);
493 }
494 }
495 }
496
497 protected void doStop() throws UMOException
498 {
499
500 }
501
502 public String getProtocol()
503 {
504 return "jms";
505 }
506
507
508
509
510 public int getAcknowledgementMode()
511 {
512 return acknowledgementMode;
513 }
514
515
516
517
518 public void setAcknowledgementMode(int acknowledgementMode)
519 {
520 this.acknowledgementMode = acknowledgementMode;
521 }
522
523
524
525
526 public String getConnectionFactoryJndiName()
527 {
528 return connectionFactoryJndiName;
529 }
530
531
532
533
534 public void setConnectionFactoryJndiName(String connectionFactoryJndiName)
535 {
536 this.connectionFactoryJndiName = connectionFactoryJndiName;
537 }
538
539
540
541
542 public boolean isDurable()
543 {
544 return durable;
545 }
546
547
548
549
550 public void setDurable(boolean durable)
551 {
552 this.durable = durable;
553 }
554
555
556
557
558 public boolean isNoLocal()
559 {
560 return noLocal;
561 }
562
563
564
565
566 public void setNoLocal(boolean noLocal)
567 {
568 this.noLocal = noLocal;
569 }
570
571
572
573
574 public boolean isPersistentDelivery()
575 {
576 return persistentDelivery;
577 }
578
579
580
581
582 public void setPersistentDelivery(boolean persistentDelivery)
583 {
584 this.persistentDelivery = persistentDelivery;
585 }
586
587
588
589
590
591
592
593
594 public void setHonorQosHeaders(boolean honorQosHeaders)
595 {
596 this.honorQosHeaders = honorQosHeaders;
597 }
598
599
600
601
602
603
604
605 public boolean isHonorQosHeaders()
606 {
607 return honorQosHeaders;
608 }
609
610
611
612
613
614 public Map getJndiProviderProperties()
615 {
616 return jndiProviderProperties;
617 }
618
619
620
621
622
623 public void setJndiProviderProperties(final Map jndiProviderProperties)
624 {
625 this.jndiProviderProperties = jndiProviderProperties;
626 }
627
628
629
630
631 public Map getConnectionFactoryProperties()
632 {
633 return connectionFactoryProperties;
634 }
635
636
637
638
639
640 public void setConnectionFactoryProperties(final Map connectionFactoryProperties)
641 {
642 this.connectionFactoryProperties = connectionFactoryProperties;
643 }
644
645 public String getJndiInitialFactory()
646 {
647 return jndiInitialFactory;
648 }
649
650 public void setJndiInitialFactory(String jndiInitialFactory)
651 {
652 this.jndiInitialFactory = jndiInitialFactory;
653 }
654
655 public String getJndiProviderUrl()
656 {
657 return jndiProviderUrl;
658 }
659
660 public void setJndiProviderUrl(String jndiProviderUrl)
661 {
662 this.jndiProviderUrl = jndiProviderUrl;
663 }
664
665 public ConnectionFactory getConnectionFactory()
666 {
667 return connectionFactory;
668 }
669
670 public void setConnectionFactory(ConnectionFactory connectionFactory)
671 {
672 this.connectionFactory = connectionFactory;
673 }
674
675 public String getConnectionFactoryClass()
676 {
677 return connectionFactoryClass;
678 }
679
680 public void setConnectionFactoryClass(String connectionFactoryClass)
681 {
682 this.connectionFactoryClass = connectionFactoryClass;
683 }
684
685 public JmsSupport getJmsSupport()
686 {
687 return jmsSupport;
688 }
689
690 public void setJmsSupport(JmsSupport jmsSupport)
691 {
692 this.jmsSupport = jmsSupport;
693 }
694
695 public String getSpecification()
696 {
697 return specification;
698 }
699
700 public void setSpecification(String specification)
701 {
702 this.specification = specification;
703 }
704
705 public boolean isJndiDestinations()
706 {
707 return jndiDestinations;
708 }
709
710 public void setJndiDestinations(boolean jndiDestinations)
711 {
712 this.jndiDestinations = jndiDestinations;
713 }
714
715 public boolean isForceJndiDestinations()
716 {
717 return forceJndiDestinations;
718 }
719
720 public void setForceJndiDestinations(boolean forceJndiDestinations)
721 {
722 this.forceJndiDestinations = forceJndiDestinations;
723 }
724
725 public Context getJndiContext()
726 {
727 return jndiContext;
728 }
729
730 public void setJndiContext(Context jndiContext)
731 {
732 this.jndiContext = jndiContext;
733 }
734
735 public void setRecoverJmsConnections(boolean recover)
736 {
737 this.recoverJmsConnections = recover;
738 }
739
740 public boolean isRecoverJmsConnections()
741 {
742 return this.recoverJmsConnections;
743 }
744
745 protected RedeliveryHandler createRedeliveryHandler()
746 throws IllegalAccessException, NoSuchMethodException, InvocationTargetException,
747 InstantiationException, ClassNotFoundException
748 {
749 if (redeliveryHandler != null)
750 {
751 return (RedeliveryHandler)ClassUtils.instanciateClass(redeliveryHandler, ClassUtils.NO_ARGS);
752 }
753 else
754 {
755 return new DefaultRedeliveryHandler();
756 }
757 }
758
759 public ReplyToHandler getReplyToHandler()
760 {
761 return new JmsReplyToHandler(this, defaultResponseTransformer);
762 }
763
764 public String getUsername()
765 {
766 return username;
767 }
768
769 public void setUsername(String username)
770 {
771 this.username = username;
772 }
773
774 public String getPassword()
775 {
776 return password;
777 }
778
779 public void setPassword(String password)
780 {
781 this.password = password;
782 }
783
784
785
786
787 public Connection getConnection()
788 {
789 return connection;
790 }
791
792 public String getClientId()
793 {
794 return clientId;
795 }
796
797 public void setClientId(String clientId)
798 {
799 this.clientId = clientId;
800 }
801
802 public int getMaxRedelivery()
803 {
804 return maxRedelivery;
805 }
806
807 public void setMaxRedelivery(int maxRedelivery)
808 {
809 this.maxRedelivery = maxRedelivery;
810 }
811
812 public String getRedeliveryHandler()
813 {
814 return redeliveryHandler;
815 }
816
817 public void setRedeliveryHandler(String redeliveryHandler)
818 {
819 this.redeliveryHandler = redeliveryHandler;
820 }
821
822 public boolean isRemoteSyncEnabled()
823 {
824 return true;
825 }
826
827
828
829
830
831
832
833 public JmsTopicResolver getTopicResolver ()
834 {
835 return topicResolver;
836 }
837
838
839
840
841
842
843 public void setTopicResolver (final JmsTopicResolver topicResolver)
844 {
845 this.topicResolver = topicResolver;
846 }
847
848
849
850
851
852
853
854
855 public boolean isEagerConsumer ()
856 {
857 return eagerConsumer;
858 }
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877 public void setEagerConsumer (final boolean eagerConsumer)
878 {
879 this.eagerConsumer = eagerConsumer;
880 }
881
882 public void onNotification(UMOServerNotification notification)
883 {
884 if (notification.getAction() == ConnectionNotification.CONNECTION_DISCONNECTED
885 || notification.getAction() == ConnectionNotification.CONNECTION_FAILED)
886 {
887
888 disposeDispatchers();
889
890
891
892
893
894 }
895 }
896
897 public boolean isCacheJmsSessions()
898 {
899 return cacheJmsSessions;
900 }
901
902 public void setCacheJmsSessions(boolean cacheJmsSessions)
903 {
904 this.cacheJmsSessions = cacheJmsSessions;
905 }
906
907
908
909
910
911 public boolean supportsProperty(String property)
912 {
913 return true;
914 }
915
916
917
918
919
920
921
922
923
924 public javax.jms.Message preProcessMessage(javax.jms.Message message, Session session) throws Exception
925 {
926 return message;
927 }
928
929
930
931
932
933
934
935 public void close(MessageProducer producer) throws JMSException
936 {
937 if (producer != null)
938 {
939 producer.close();
940 }
941 }
942
943
944
945
946
947
948
949 public void closeQuietly(MessageProducer producer)
950 {
951 try
952 {
953 close(producer);
954 }
955 catch (JMSException e)
956 {
957 logger.error("Failed to close jms message producer", e);
958 }
959 }
960
961
962
963
964
965
966
967 public void close(MessageConsumer consumer) throws JMSException
968 {
969 if (consumer != null)
970 {
971 consumer.close();
972 }
973 }
974
975
976
977
978
979
980
981 public void closeQuietly(MessageConsumer consumer)
982 {
983 try
984 {
985 close(consumer);
986 }
987 catch (JMSException e)
988 {
989 logger.error("Failed to close jms message consumer", e);
990 }
991 }
992
993
994
995
996
997
998
999 public void close(Session session) throws JMSException
1000 {
1001 if (session != null)
1002 {
1003 session.close();
1004 }
1005 }
1006
1007
1008
1009
1010
1011
1012
1013 public void closeQuietly(Session session)
1014 {
1015 try
1016 {
1017 close(session);
1018 }
1019 catch (JMSException e)
1020 {
1021 logger.error("Failed to close jms session consumer", e);
1022 }
1023 }
1024
1025
1026
1027
1028
1029
1030
1031 public void close(TemporaryQueue tempQueue) throws JMSException
1032 {
1033 if (tempQueue != null)
1034 {
1035 tempQueue.delete();
1036 }
1037 }
1038
1039
1040
1041
1042
1043
1044
1045 public void closeQuietly(TemporaryQueue tempQueue)
1046 {
1047 try
1048 {
1049 close(tempQueue);
1050 }
1051 catch (JMSException e)
1052 {
1053 if (logger.isErrorEnabled())
1054 {
1055 String queueName = "";
1056 try
1057 {
1058 queueName = tempQueue.getQueueName();
1059 }
1060 catch (JMSException innerEx)
1061 {
1062
1063 }
1064 logger.info(MessageFormat.format(
1065 "Faled to delete a temporary queue '{0}' Reason: {1}",
1066 new Object[] {queueName, e.getMessage()}));
1067 }
1068 }
1069 }
1070
1071
1072
1073
1074
1075
1076
1077 public void close(TemporaryTopic tempTopic) throws JMSException
1078 {
1079 if (tempTopic != null)
1080 {
1081 tempTopic.delete();
1082 }
1083 }
1084
1085
1086
1087
1088
1089
1090
1091 public void closeQuietly(TemporaryTopic tempTopic)
1092 {
1093 try
1094 {
1095 close(tempTopic);
1096 }
1097 catch (JMSException e)
1098 {
1099 if (logger.isErrorEnabled())
1100 {
1101 String topicName = "";
1102 try
1103 {
1104 topicName = tempTopic.getTopicName();
1105 }
1106 catch (JMSException innerEx)
1107 {
1108
1109 }
1110 logger.error("Faled to delete a temporary topic " + topicName, e);
1111 }
1112 }
1113 }
1114 }