1
2
3
4
5
6
7
8
9
10
11 package org.mule.transport.jms;
12
13 import org.mule.api.MessagingException;
14 import org.mule.api.MuleException;
15 import org.mule.api.context.notification.ConnectionNotificationListener;
16 import org.mule.api.context.notification.ServerNotification;
17 import org.mule.api.endpoint.ImmutableEndpoint;
18 import org.mule.api.endpoint.InboundEndpoint;
19 import org.mule.api.lifecycle.InitialisationException;
20 import org.mule.api.lifecycle.StartException;
21 import org.mule.api.service.Service;
22 import org.mule.api.transaction.Transaction;
23 import org.mule.api.transaction.TransactionException;
24 import org.mule.api.transport.MessageAdapter;
25 import org.mule.api.transport.MessageReceiver;
26 import org.mule.api.transport.ReplyToHandler;
27 import org.mule.config.ExceptionHelper;
28 import org.mule.config.i18n.CoreMessages;
29 import org.mule.context.notification.ConnectionNotification;
30 import org.mule.context.notification.NotificationException;
31 import org.mule.transaction.TransactionCoordination;
32 import org.mule.transport.AbstractConnector;
33 import org.mule.transport.ConnectException;
34 import org.mule.transport.FatalConnectException;
35 import org.mule.transport.jms.i18n.JmsMessages;
36 import org.mule.transport.jms.xa.ConnectionFactoryWrapper;
37
38 import java.text.MessageFormat;
39 import java.util.HashMap;
40 import java.util.Iterator;
41 import java.util.Map;
42
43 import javax.jms.Connection;
44 import javax.jms.ConnectionFactory;
45 import javax.jms.ExceptionListener;
46 import javax.jms.JMSException;
47 import javax.jms.MessageConsumer;
48 import javax.jms.MessageProducer;
49 import javax.jms.Session;
50 import javax.jms.TemporaryQueue;
51 import javax.jms.TemporaryTopic;
52 import javax.jms.XAConnectionFactory;
53 import javax.naming.NamingException;
54
55 import org.apache.commons.lang.UnhandledException;
56
57
58
59
60
61
62
63 public class JmsConnector extends AbstractConnector implements ConnectionNotificationListener
64 {
65
66 public static final String JMS = "jms";
67
68
69
70
71
72 private int acknowledgementMode = Session.AUTO_ACKNOWLEDGE;
73
74 private String clientId;
75
76 private boolean durable;
77
78 private boolean noLocal;
79
80 private boolean persistentDelivery;
81
82 private boolean honorQosHeaders;
83
84 private int maxRedelivery = 0;
85
86 private boolean cacheJmsSessions = false;
87
88 private boolean recoverJmsConnections = true;
89
90
91 private boolean eagerConsumer = true;
92
93
94
95
96
97 private ConnectionFactory connectionFactory;
98
99 public String username = null;
100
101 public String password = null;
102
103
104
105
106 private Connection connection;
107
108
109
110
111
112 private String specification = JmsConstants.JMS_SPECIFICATION_102B;
113
114 private JmsSupport jmsSupport;
115
116 private JmsTopicResolver topicResolver;
117
118 private RedeliveryHandlerFactory redeliveryHandlerFactory;
119
120
121
122
123
124
125 static
126 {
127 ExceptionHelper.registerExceptionReader(new JmsExceptionReader());
128 }
129
130 public String getProtocol()
131 {
132 return JMS;
133 }
134
135 protected void doInitialise() throws InitialisationException
136 {
137 if (connectionFactory == null)
138 {
139 connectionFactory = getDefaultConnectionFactory();
140 }
141 if (connectionFactory == null)
142 {
143 throw new InitialisationException(JmsMessages.noConnectionFactorySet(), this);
144 }
145
146 if (topicResolver == null)
147 {
148 topicResolver = new DefaultJmsTopicResolver(this);
149 }
150 if (redeliveryHandlerFactory == null)
151 {
152 redeliveryHandlerFactory = new DefaultRedeliveryHandlerFactory();
153 }
154
155 try
156 {
157 muleContext.registerListener(this, getName());
158 }
159 catch (NotificationException nex)
160 {
161 throw new InitialisationException(nex, this);
162 }
163 }
164
165
166 protected ConnectionFactory getDefaultConnectionFactory()
167 {
168 return null;
169 }
170
171 protected void doDispose()
172 {
173 if (connection != null)
174 {
175 try
176 {
177 connection.close();
178 }
179 catch (JMSException e)
180 {
181 logger.error("Jms connector failed to dispose properly: ", e);
182 }
183 connection = null;
184 }
185 }
186
187 protected Connection createConnection() throws NamingException, JMSException, InitialisationException
188 {
189 ConnectionFactory cf = this.connectionFactory;
190 Connection connection;
191
192 try
193 {
194 if (cf instanceof XAConnectionFactory && muleContext.getTransactionManager() != null)
195 {
196 cf = new ConnectionFactoryWrapper(cf);
197 }
198 }
199 catch (Exception e)
200 {
201 throw new InitialisationException(e, this);
202 }
203 if (cf == null)
204 {
205 throw new InitialisationException(JmsMessages.noConnectionFactorySet(), this);
206 }
207
208
209 if (username != null)
210 {
211 connection = jmsSupport.createConnection(cf, username, password);
212 }
213 else
214 {
215 connection = jmsSupport.createConnection(cf);
216 }
217
218 if (clientId != null)
219 {
220 connection.setClientID(getClientId());
221 }
222
223
224
225
226 if (recoverJmsConnections && connectionStrategy != null && connection != null)
227 {
228 connection.setExceptionListener(new ExceptionListener()
229 {
230 public void onException(JMSException jmsException)
231 {
232 logger.debug("About to recycle myself due to remote JMS connection shutdown.");
233 final JmsConnector jmsConnector = JmsConnector.this;
234 try
235 {
236 jmsConnector.stop();
237 jmsConnector.initialised.set(false);
238 }
239 catch (MuleException e)
240 {
241 logger.warn(e.getMessage(), e);
242 }
243
244 try
245 {
246 connectionStrategy.connect(jmsConnector);
247
248
249
250
251
252
253 Map receivers = new HashMap(jmsConnector.getReceivers());
254 jmsConnector.initialise();
255
256 for (Iterator itReceivers = receivers.values().iterator(); itReceivers.hasNext();)
257 {
258 MessageReceiver receiver = (MessageReceiver) itReceivers.next();
259 try
260 {
261 jmsConnector.registerListener(receiver.getService(), receiver.getEndpoint());
262 }
263 catch (Exception ex)
264 {
265 throw new FatalConnectException(ex, receiver);
266 }
267 }
268
269 jmsConnector.start();
270 }
271 catch (FatalConnectException fcex)
272 {
273 logger.fatal("Failed to reconnect to JMS server. I'm giving up.");
274 }
275 catch (MuleException umoex)
276 {
277 throw new UnhandledException("Failed to recover a connector.", umoex);
278 }
279 }
280 });
281 }
282
283 return connection;
284 }
285
286 protected void doConnect() throws ConnectException
287 {
288 try
289 {
290 if (jmsSupport == null)
291 {
292 if (JmsConstants.JMS_SPECIFICATION_102B.equals(specification))
293 {
294 jmsSupport = new Jms102bSupport(this);
295 }
296 else
297 {
298 jmsSupport = new Jms11Support(this);
299 }
300 }
301 }
302 catch (Exception e)
303 {
304 throw new ConnectException(CoreMessages.failedToCreate("Jms Connector"), e, this);
305 }
306
307 try
308 {
309 connection = createConnection();
310 if (started.get())
311 {
312 connection.start();
313 }
314 }
315 catch (Exception e)
316 {
317 throw new ConnectException(e, this);
318 }
319 }
320
321 protected void doDisconnect() throws ConnectException
322 {
323 try
324 {
325 if (connection != null)
326 {
327 connection.close();
328 }
329 }
330 catch (Exception e)
331 {
332 throw new ConnectException(e, this);
333 }
334 finally
335 {
336
337 connection = null;
338 }
339 }
340
341 public MessageAdapter getMessageAdapter(Object message) throws MessagingException
342 {
343 JmsMessageAdapter adapter = (JmsMessageAdapter) super.getMessageAdapter(message);
344 adapter.setSpecification(this.getSpecification());
345 return adapter;
346 }
347
348 protected Object getReceiverKey(Service service, InboundEndpoint endpoint)
349 {
350 return service.getName() + "~" + endpoint.getEndpointURI().getAddress();
351 }
352
353 public Session getSessionFromTransaction()
354 {
355 Transaction tx = TransactionCoordination.getInstance().getTransaction();
356 if (tx != null)
357 {
358 if (tx.hasResource(connection))
359 {
360 if (logger.isDebugEnabled())
361 {
362 logger.debug("Retrieving jms session from current transaction " + tx);
363 }
364
365 Session session = (Session) tx.getResource(connection);
366
367 if (logger.isDebugEnabled())
368 {
369 logger.debug("Using " + session + " bound to transaction " + tx);
370 }
371
372 return session;
373 }
374 }
375 return null;
376 }
377
378 public Session getSession(ImmutableEndpoint endpoint) throws JMSException
379 {
380 final boolean topic = getTopicResolver().isTopic(endpoint);
381 return getSession(endpoint.getTransactionConfig().isTransacted(), topic);
382 }
383
384 public Session getSession(boolean transacted, boolean topic) throws JMSException
385 {
386 if (!isConnected())
387 {
388 throw new JMSException("Not connected");
389 }
390 Session session = getSessionFromTransaction();
391 if (session != null)
392 {
393 return session;
394 }
395
396 Transaction tx = TransactionCoordination.getInstance().getTransaction();
397
398 if (logger.isDebugEnabled())
399 {
400 logger.debug(MessageFormat.format(
401 "Retrieving new jms session from connection: " +
402 "topic={0}, transacted={1}, ack mode={2}, nolocal={3}",
403 new Object[]{Boolean.valueOf(topic),
404 Boolean.valueOf(transacted),
405 new Integer(acknowledgementMode),
406 Boolean.valueOf(noLocal)}));
407 }
408
409 session = jmsSupport.createSession(connection, topic, transacted, acknowledgementMode, noLocal);
410 if (tx != null)
411 {
412 logger.debug("Binding session " + session + " to current transaction " + tx);
413 try
414 {
415 tx.bindResource(connection, session);
416 }
417 catch (TransactionException e)
418 {
419 closeQuietly(session);
420 throw new RuntimeException("Could not bind session to current transaction", e);
421 }
422 }
423 return session;
424 }
425
426 protected void doStart() throws MuleException
427 {
428 if (connection != null)
429 {
430 try
431 {
432 connection.start();
433 }
434 catch (JMSException e)
435 {
436 throw new StartException(CoreMessages.failedToStart("Jms Connection"), e, this);
437 }
438 }
439 }
440
441 protected void doStop() throws MuleException
442 {
443
444 }
445
446 public ReplyToHandler getReplyToHandler()
447 {
448 return new JmsReplyToHandler(this, getDefaultResponseTransformers());
449 }
450
451 public void onNotification(ServerNotification notification)
452 {
453 if (notification.getAction() == ConnectionNotification.CONNECTION_DISCONNECTED
454 || notification.getAction() == ConnectionNotification.CONNECTION_FAILED)
455 {
456
457 disposeDispatchers();
458
459
460
461
462
463 }
464 }
465
466
467
468
469
470 public boolean supportsProperty(String property)
471 {
472 return true;
473 }
474
475
476
477
478
479
480
481
482
483 public javax.jms.Message preProcessMessage(javax.jms.Message message, Session session) throws Exception
484 {
485 return message;
486 }
487
488
489
490
491
492
493
494 public void close(MessageProducer producer) throws JMSException
495 {
496 if (producer != null)
497 {
498 producer.close();
499 }
500 }
501
502
503
504
505
506
507
508 public void closeQuietly(MessageProducer producer)
509 {
510 try
511 {
512 close(producer);
513 }
514 catch (JMSException e)
515 {
516 logger.error("Failed to close jms message producer", e);
517 }
518 }
519
520
521
522
523
524
525
526 public void close(MessageConsumer consumer) throws JMSException
527 {
528 if (consumer != null)
529 {
530 consumer.close();
531 }
532 }
533
534
535
536
537
538
539
540 public void closeQuietly(MessageConsumer consumer)
541 {
542 try
543 {
544 close(consumer);
545 }
546 catch (JMSException e)
547 {
548 logger.error("Failed to close jms message consumer", e);
549 }
550 }
551
552
553
554
555
556
557
558 public void close(Session session) throws JMSException
559 {
560 if (session != null)
561 {
562 session.close();
563 }
564 }
565
566
567
568
569
570
571
572 public void closeQuietly(Session session)
573 {
574 try
575 {
576 close(session);
577 }
578 catch (JMSException e)
579 {
580 logger.error("Failed to close jms session consumer", e);
581 }
582 }
583
584
585
586
587
588
589
590 public void close(TemporaryQueue tempQueue) throws JMSException
591 {
592 if (tempQueue != null)
593 {
594 tempQueue.delete();
595 }
596 }
597
598
599
600
601
602
603
604 public void closeQuietly(TemporaryQueue tempQueue)
605 {
606 try
607 {
608 close(tempQueue);
609 }
610 catch (JMSException e)
611 {
612 if (logger.isErrorEnabled())
613 {
614 String queueName = "";
615 try
616 {
617 queueName = tempQueue.getQueueName();
618 }
619 catch (JMSException innerEx)
620 {
621
622 }
623 logger.info(MessageFormat.format(
624 "Faled to delete a temporary queue '{0}' Reason: {1}",
625 new Object[]{queueName, e.getMessage()}));
626 }
627 }
628 }
629
630
631
632
633
634
635
636 public void close(TemporaryTopic tempTopic) throws JMSException
637 {
638 if (tempTopic != null)
639 {
640 tempTopic.delete();
641 }
642 }
643
644
645
646
647
648
649
650 public void closeQuietly(TemporaryTopic tempTopic)
651 {
652 try
653 {
654 close(tempTopic);
655 }
656 catch (JMSException e)
657 {
658 if (logger.isErrorEnabled())
659 {
660 String topicName = "";
661 try
662 {
663 topicName = tempTopic.getTopicName();
664 }
665 catch (JMSException innerEx)
666 {
667
668 }
669 logger.error("Faled to delete a temporary topic " + topicName, e);
670 }
671 }
672 }
673
674
675
676
677
678
679 public Connection getConnection()
680 {
681 return connection;
682 }
683
684 protected void setConnection(Connection connection)
685 {
686 this.connection = connection;
687 }
688
689
690 public int getAcknowledgementMode()
691 {
692 return acknowledgementMode;
693 }
694
695
696 public void setAcknowledgementMode(int acknowledgementMode)
697 {
698 this.acknowledgementMode = acknowledgementMode;
699 }
700
701
702 public boolean isDurable()
703 {
704 return durable;
705 }
706
707
708 public void setDurable(boolean durable)
709 {
710 this.durable = durable;
711 }
712
713
714 public boolean isNoLocal()
715 {
716 return noLocal;
717 }
718
719
720 public void setNoLocal(boolean noLocal)
721 {
722 this.noLocal = noLocal;
723 }
724
725
726 public boolean isPersistentDelivery()
727 {
728 return persistentDelivery;
729 }
730
731
732 public void setPersistentDelivery(boolean persistentDelivery)
733 {
734 this.persistentDelivery = persistentDelivery;
735 }
736
737 public JmsSupport getJmsSupport()
738 {
739 return jmsSupport;
740 }
741
742 public void setJmsSupport(JmsSupport jmsSupport)
743 {
744 this.jmsSupport = jmsSupport;
745 }
746
747 public String getSpecification()
748 {
749 return specification;
750 }
751
752 public void setSpecification(String specification)
753 {
754 this.specification = specification;
755 }
756
757 public void setRecoverJmsConnections(boolean recover)
758 {
759 this.recoverJmsConnections = recover;
760 }
761
762 public boolean isRecoverJmsConnections()
763 {
764 return this.recoverJmsConnections;
765 }
766
767 public String getUsername()
768 {
769 return username;
770 }
771
772 public void setUsername(String username)
773 {
774 this.username = username;
775 }
776
777 public String getPassword()
778 {
779 return password;
780 }
781
782 public void setPassword(String password)
783 {
784 this.password = password;
785 }
786
787 public String getClientId()
788 {
789 return clientId;
790 }
791
792 public void setClientId(String clientId)
793 {
794 this.clientId = clientId;
795 }
796
797 public int getMaxRedelivery()
798 {
799 return maxRedelivery;
800 }
801
802 public void setMaxRedelivery(int maxRedelivery)
803 {
804 this.maxRedelivery = maxRedelivery;
805 }
806
807 public boolean isRemoteSyncEnabled()
808 {
809 return true;
810 }
811
812
813
814
815
816
817
818 public JmsTopicResolver getTopicResolver()
819 {
820 return topicResolver;
821 }
822
823
824
825
826
827
828 public void setTopicResolver(final JmsTopicResolver topicResolver)
829 {
830 this.topicResolver = topicResolver;
831 }
832
833
834
835
836
837
838
839
840 public boolean isEagerConsumer()
841 {
842 return eagerConsumer;
843 }
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862 public void setEagerConsumer(final boolean eagerConsumer)
863 {
864 this.eagerConsumer = eagerConsumer;
865 }
866
867 public boolean isCacheJmsSessions()
868 {
869 return cacheJmsSessions;
870 }
871
872 public void setCacheJmsSessions(boolean cacheJmsSessions)
873 {
874 this.cacheJmsSessions = cacheJmsSessions;
875 }
876
877 public ConnectionFactory getConnectionFactory()
878 {
879 return connectionFactory;
880 }
881
882 public void setConnectionFactory(ConnectionFactory connectionFactory)
883 {
884 this.connectionFactory = connectionFactory;
885 }
886
887 public RedeliveryHandlerFactory getRedeliveryHandlerFactory()
888 {
889 return redeliveryHandlerFactory;
890 }
891
892 public void setRedeliveryHandlerFactory(RedeliveryHandlerFactory redeliveryHandlerFactory)
893 {
894 this.redeliveryHandlerFactory = redeliveryHandlerFactory;
895 }
896
897
898
899
900
901
902
903
904
905
906
907 public void setHonorQosHeaders(boolean honorQosHeaders)
908 {
909 this.honorQosHeaders = honorQosHeaders;
910 }
911
912
913
914
915
916
917
918
919
920 public boolean isHonorQosHeaders()
921 {
922 return honorQosHeaders;
923 }
924 }