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);
255 }
256 }
257
258 if (username != null)
259 {
260 connection = jmsSupport.createConnection(connectionFactory, username, password);
261 }
262 else
263 {
264 connection = jmsSupport.createConnection(connectionFactory);
265 }
266
267 if (clientId != null)
268 {
269 connection.setClientID(getClientId());
270 }
271
272
273
274
275 if (recoverJmsConnections && connectionStrategy != null && connection != null)
276 {
277 connection.setExceptionListener(new ExceptionListener()
278 {
279 public void onException(JMSException jmsException)
280 {
281 logger.debug("About to recycle myself due to remote JMS connection shutdown.");
282 final JmsConnector jmsConnector = JmsConnector.this;
283 try
284 {
285 jmsConnector.stopConnector();
286 jmsConnector.initialised.set(false);
287 }
288 catch (UMOException e)
289 {
290 logger.warn(e.getMessage(), e);
291 }
292
293 try
294 {
295 connectionStrategy.connect(jmsConnector);
296 jmsConnector.initialise();
297 jmsConnector.startConnector();
298 }
299 catch (FatalConnectException fcex)
300 {
301 logger.fatal("Failed to reconnect to JMS server. I'm giving up.");
302 }
303 catch (UMOException umoex)
304 {
305 throw new UnhandledException("Failed to recover a connector.", umoex);
306 }
307 }
308 });
309 }
310
311 return connection;
312 }
313
314 protected void doConnect() throws ConnectException
315 {
316 try
317 {
318
319
320
321
322
323 if (connectionFactoryClass != null)
324 {
325 connectionFactory = (ConnectionFactory)ClassUtils.instanciateClass(connectionFactoryClass,
326 ClassUtils.NO_ARGS);
327 }
328
329
330
331 if (connectionFactory == null || jndiInitialFactory != null)
332 {
333 initJndiContext();
334 }
335 else
336 {
337
338
339 jndiDestinations = false;
340 forceJndiDestinations = false;
341 }
342
343 if (jmsSupport == null)
344 {
345 if (JmsConstants.JMS_SPECIFICATION_102B.equals(specification))
346 {
347 jmsSupport = new Jms102bSupport(this, jndiContext, jndiDestinations,
348 forceJndiDestinations);
349 }
350 else
351 {
352 jmsSupport = new Jms11Support(this, jndiContext, jndiDestinations, forceJndiDestinations);
353 }
354 }
355 if (connectionFactory == null)
356 {
357 connectionFactory = createConnectionFactory();
358 }
359 if (connectionFactoryProperties != null && !connectionFactoryProperties.isEmpty())
360 {
361
362 BeanUtils.populateWithoutFail(connectionFactory, connectionFactoryProperties, true);
363 }
364 applyVendorSpecificConnectionFactoryProperties();
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 applyVendorSpecificConnectionFactoryProperties(){
386
387 }
388
389 protected void doDisconnect() throws ConnectException
390 {
391 try
392 {
393 if (connection != null)
394 {
395 connection.close();
396 }
397 }
398 catch (Exception e)
399 {
400 throw new ConnectException(e, this);
401 }
402 finally
403 {
404
405 connection = null;
406 }
407 }
408
409 public UMOMessageAdapter getMessageAdapter(Object message) throws MessagingException
410 {
411 JmsMessageAdapter adapter = (JmsMessageAdapter)super.getMessageAdapter(message);
412 adapter.setSpecification(this.getSpecification());
413 return adapter;
414 }
415
416 protected Object getReceiverKey(UMOComponent component, UMOEndpoint endpoint)
417 {
418 return component.getDescriptor().getName() + "~" + endpoint.getEndpointURI().getAddress();
419 }
420
421 public Session getSessionFromTransaction()
422 {
423 UMOTransaction tx = TransactionCoordination.getInstance().getTransaction();
424 if (tx != null)
425 {
426 if (tx.hasResource(connection))
427 {
428 if (logger.isDebugEnabled())
429 {
430 logger.debug("Retrieving jms session from current transaction " + tx);
431 }
432
433 Session session = (Session) tx.getResource(connection);
434
435 if (logger.isDebugEnabled())
436 {
437 logger.debug("Using " + session + " bound to transaction " + tx);
438 }
439
440 return session;
441 }
442 }
443 return null;
444 }
445
446 public Session getSession(UMOImmutableEndpoint endpoint) throws JMSException
447 {
448 final boolean topic = getTopicResolver().isTopic(endpoint);
449 return getSession(endpoint.getTransactionConfig().isTransacted(), topic);
450 }
451
452 public Session getSession(boolean transacted, boolean topic) throws JMSException
453 {
454 if (!isConnected())
455 {
456 throw new JMSException("Not connected");
457 }
458 Session session = getSessionFromTransaction();
459 if (session != null)
460 {
461 return session;
462 }
463
464 UMOTransaction tx = TransactionCoordination.getInstance().getTransaction();
465
466 if (logger.isDebugEnabled())
467 {
468 logger.debug(MessageFormat.format(
469 "Retrieving new jms session from connection: " +
470 "topic={0}, transacted={1}, ack mode={2}, nolocal={3}",
471 new Object[]{Boolean.valueOf(topic),
472 Boolean.valueOf(transacted),
473 new Integer(acknowledgementMode),
474 Boolean.valueOf(noLocal)}));
475 }
476
477 session = jmsSupport.createSession(connection, topic, transacted, acknowledgementMode, noLocal);
478 if (tx != null)
479 {
480 logger.debug("Binding session " + session + " to current transaction " + tx);
481 try
482 {
483 tx.bindResource(connection, session);
484 }
485 catch (TransactionException e)
486 {
487 closeQuietly(session);
488 throw new RuntimeException("Could not bind session to current transaction", e);
489 }
490 }
491 return session;
492 }
493
494 protected void doStart() throws UMOException
495 {
496 if (connection != null)
497 {
498 try
499 {
500 connection.start();
501 }
502 catch (JMSException e)
503 {
504 throw new LifecycleException(CoreMessages.failedToStart("Jms Connection"), e);
505 }
506 }
507 }
508
509 protected void doStop() throws UMOException
510 {
511
512 }
513
514 public String getProtocol()
515 {
516 return "jms";
517 }
518
519
520
521
522 public int getAcknowledgementMode()
523 {
524 return acknowledgementMode;
525 }
526
527
528
529
530 public void setAcknowledgementMode(int acknowledgementMode)
531 {
532 this.acknowledgementMode = acknowledgementMode;
533 }
534
535
536
537
538 public String getConnectionFactoryJndiName()
539 {
540 return connectionFactoryJndiName;
541 }
542
543
544
545
546 public void setConnectionFactoryJndiName(String connectionFactoryJndiName)
547 {
548 this.connectionFactoryJndiName = connectionFactoryJndiName;
549 }
550
551
552
553
554 public boolean isDurable()
555 {
556 return durable;
557 }
558
559
560
561
562 public void setDurable(boolean durable)
563 {
564 this.durable = durable;
565 }
566
567
568
569
570 public boolean isNoLocal()
571 {
572 return noLocal;
573 }
574
575
576
577
578 public void setNoLocal(boolean noLocal)
579 {
580 this.noLocal = noLocal;
581 }
582
583
584
585
586 public boolean isPersistentDelivery()
587 {
588 return persistentDelivery;
589 }
590
591
592
593
594 public void setPersistentDelivery(boolean persistentDelivery)
595 {
596 this.persistentDelivery = persistentDelivery;
597 }
598
599
600
601
602
603
604
605
606 public void setHonorQosHeaders(boolean honorQosHeaders)
607 {
608 this.honorQosHeaders = honorQosHeaders;
609 }
610
611
612
613
614
615
616
617 public boolean isHonorQosHeaders()
618 {
619 return honorQosHeaders;
620 }
621
622
623
624
625
626 public Map getJndiProviderProperties()
627 {
628 return jndiProviderProperties;
629 }
630
631
632
633
634
635 public void setJndiProviderProperties(final Map jndiProviderProperties)
636 {
637 this.jndiProviderProperties = jndiProviderProperties;
638 }
639
640
641
642
643 public Map getConnectionFactoryProperties()
644 {
645 return connectionFactoryProperties;
646 }
647
648
649
650
651
652 public void setConnectionFactoryProperties(final Map connectionFactoryProperties)
653 {
654 this.connectionFactoryProperties = connectionFactoryProperties;
655 }
656
657 public String getJndiInitialFactory()
658 {
659 return jndiInitialFactory;
660 }
661
662 public void setJndiInitialFactory(String jndiInitialFactory)
663 {
664 this.jndiInitialFactory = jndiInitialFactory;
665 }
666
667 public String getJndiProviderUrl()
668 {
669 return jndiProviderUrl;
670 }
671
672 public void setJndiProviderUrl(String jndiProviderUrl)
673 {
674 this.jndiProviderUrl = jndiProviderUrl;
675 }
676
677 public ConnectionFactory getConnectionFactory()
678 {
679 return connectionFactory;
680 }
681
682 public void setConnectionFactory(ConnectionFactory connectionFactory)
683 {
684 this.connectionFactory = connectionFactory;
685 }
686
687 public String getConnectionFactoryClass()
688 {
689 return connectionFactoryClass;
690 }
691
692 public void setConnectionFactoryClass(String connectionFactoryClass)
693 {
694 this.connectionFactoryClass = connectionFactoryClass;
695 }
696
697 public JmsSupport getJmsSupport()
698 {
699 return jmsSupport;
700 }
701
702 public void setJmsSupport(JmsSupport jmsSupport)
703 {
704 this.jmsSupport = jmsSupport;
705 }
706
707 public String getSpecification()
708 {
709 return specification;
710 }
711
712 public void setSpecification(String specification)
713 {
714 this.specification = specification;
715 }
716
717 public boolean isJndiDestinations()
718 {
719 return jndiDestinations;
720 }
721
722 public void setJndiDestinations(boolean jndiDestinations)
723 {
724 this.jndiDestinations = jndiDestinations;
725 }
726
727 public boolean isForceJndiDestinations()
728 {
729 return forceJndiDestinations;
730 }
731
732 public void setForceJndiDestinations(boolean forceJndiDestinations)
733 {
734 this.forceJndiDestinations = forceJndiDestinations;
735 }
736
737 public Context getJndiContext()
738 {
739 return jndiContext;
740 }
741
742 public void setJndiContext(Context jndiContext)
743 {
744 this.jndiContext = jndiContext;
745 }
746
747 public void setRecoverJmsConnections(boolean recover)
748 {
749 this.recoverJmsConnections = recover;
750 }
751
752 public boolean isRecoverJmsConnections()
753 {
754 return this.recoverJmsConnections;
755 }
756
757 protected RedeliveryHandler createRedeliveryHandler()
758 throws IllegalAccessException, NoSuchMethodException, InvocationTargetException,
759 InstantiationException, ClassNotFoundException
760 {
761 if (redeliveryHandler != null)
762 {
763 return (RedeliveryHandler)ClassUtils.instanciateClass(redeliveryHandler, ClassUtils.NO_ARGS);
764 }
765 else
766 {
767 return new DefaultRedeliveryHandler();
768 }
769 }
770
771 public ReplyToHandler getReplyToHandler()
772 {
773 return new JmsReplyToHandler(this, defaultResponseTransformer);
774 }
775
776 public String getUsername()
777 {
778 return username;
779 }
780
781 public void setUsername(String username)
782 {
783 this.username = username;
784 }
785
786 public String getPassword()
787 {
788 return password;
789 }
790
791 public void setPassword(String password)
792 {
793 this.password = password;
794 }
795
796
797
798
799 public Connection getConnection()
800 {
801 return connection;
802 }
803
804 public String getClientId()
805 {
806 return clientId;
807 }
808
809 public void setClientId(String clientId)
810 {
811 this.clientId = clientId;
812 }
813
814 public int getMaxRedelivery()
815 {
816 return maxRedelivery;
817 }
818
819 public void setMaxRedelivery(int maxRedelivery)
820 {
821 this.maxRedelivery = maxRedelivery;
822 }
823
824 public String getRedeliveryHandler()
825 {
826 return redeliveryHandler;
827 }
828
829 public void setRedeliveryHandler(String redeliveryHandler)
830 {
831 this.redeliveryHandler = redeliveryHandler;
832 }
833
834 public boolean isRemoteSyncEnabled()
835 {
836 return true;
837 }
838
839
840
841
842
843
844
845 public JmsTopicResolver getTopicResolver ()
846 {
847 return topicResolver;
848 }
849
850
851
852
853
854
855 public void setTopicResolver (final JmsTopicResolver topicResolver)
856 {
857 this.topicResolver = topicResolver;
858 }
859
860
861
862
863
864
865
866
867 public boolean isEagerConsumer ()
868 {
869 return eagerConsumer;
870 }
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889 public void setEagerConsumer (final boolean eagerConsumer)
890 {
891 this.eagerConsumer = eagerConsumer;
892 }
893
894 public void onNotification(UMOServerNotification notification)
895 {
896 if (notification.getAction() == ConnectionNotification.CONNECTION_DISCONNECTED
897 || notification.getAction() == ConnectionNotification.CONNECTION_FAILED)
898 {
899
900 disposeDispatchers();
901
902
903
904
905
906 }
907 }
908
909 public boolean isCacheJmsSessions()
910 {
911 return cacheJmsSessions;
912 }
913
914 public void setCacheJmsSessions(boolean cacheJmsSessions)
915 {
916 this.cacheJmsSessions = cacheJmsSessions;
917 }
918
919
920
921
922
923 public boolean supportsProperty(String property)
924 {
925 return true;
926 }
927
928
929
930
931
932
933
934
935
936 public javax.jms.Message preProcessMessage(javax.jms.Message message, Session session) throws Exception
937 {
938 return message;
939 }
940
941
942
943
944
945
946
947 public void close(MessageProducer producer) throws JMSException
948 {
949 if (producer != null)
950 {
951 producer.close();
952 }
953 }
954
955
956
957
958
959
960
961 public void closeQuietly(MessageProducer producer)
962 {
963 try
964 {
965 close(producer);
966 }
967 catch (JMSException e)
968 {
969 logger.error("Failed to close jms message producer", e);
970 }
971 }
972
973
974
975
976
977
978
979 public void close(MessageConsumer consumer) throws JMSException
980 {
981 if (consumer != null)
982 {
983 consumer.close();
984 }
985 }
986
987
988
989
990
991
992
993 public void closeQuietly(MessageConsumer consumer)
994 {
995 try
996 {
997 close(consumer);
998 }
999 catch (JMSException e)
1000 {
1001 logger.error("Failed to close jms message consumer", e);
1002 }
1003 }
1004
1005
1006
1007
1008
1009
1010
1011 public void close(Session session) throws JMSException
1012 {
1013 if (session != null)
1014 {
1015 session.close();
1016 }
1017 }
1018
1019
1020
1021
1022
1023
1024
1025 public void closeQuietly(Session session)
1026 {
1027 try
1028 {
1029 close(session);
1030 }
1031 catch (JMSException e)
1032 {
1033 logger.error("Failed to close jms session", e);
1034 }
1035 }
1036
1037
1038
1039
1040
1041
1042
1043 public void close(TemporaryQueue tempQueue) throws JMSException
1044 {
1045 if (tempQueue != null)
1046 {
1047 tempQueue.delete();
1048 }
1049 }
1050
1051
1052
1053
1054
1055
1056
1057 public void closeQuietly(TemporaryQueue tempQueue)
1058 {
1059 try
1060 {
1061 close(tempQueue);
1062 }
1063 catch (JMSException e)
1064 {
1065 if (logger.isErrorEnabled())
1066 {
1067 String queueName = "";
1068 try
1069 {
1070 queueName = tempQueue.getQueueName();
1071 }
1072 catch (JMSException innerEx)
1073 {
1074
1075 }
1076 logger.info(MessageFormat.format(
1077 "Faled to delete a temporary queue '{0}' Reason: {1}",
1078 new Object[] {queueName, e.getMessage()}));
1079 }
1080 }
1081 }
1082
1083
1084
1085
1086
1087
1088
1089 public void close(TemporaryTopic tempTopic) throws JMSException
1090 {
1091 if (tempTopic != null)
1092 {
1093 tempTopic.delete();
1094 }
1095 }
1096
1097
1098
1099
1100
1101
1102
1103 public void closeQuietly(TemporaryTopic tempTopic)
1104 {
1105 try
1106 {
1107 close(tempTopic);
1108 }
1109 catch (JMSException e)
1110 {
1111 if (logger.isErrorEnabled())
1112 {
1113 String topicName = "";
1114 try
1115 {
1116 topicName = tempTopic.getTopicName();
1117 }
1118 catch (JMSException innerEx)
1119 {
1120
1121 }
1122 logger.error("Faled to delete a temporary topic " + topicName, e);
1123 }
1124 }
1125 }
1126 }