View Javadoc

1   /*
2    * $Id: JmsConnector.java 20501 2010-12-08 00:52:19Z tcarlson $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.transport.jms;
12  
13  import org.mule.api.MuleContext;
14  import org.mule.api.MuleException;
15  import org.mule.api.MuleRuntimeException;
16  import org.mule.api.construct.FlowConstruct;
17  import org.mule.api.context.notification.ConnectionNotificationListener;
18  import org.mule.api.endpoint.ImmutableEndpoint;
19  import org.mule.api.endpoint.InboundEndpoint;
20  import org.mule.api.lifecycle.InitialisationException;
21  import org.mule.api.lifecycle.StartException;
22  import org.mule.api.lifecycle.StopException;
23  import org.mule.api.transaction.Transaction;
24  import org.mule.api.transaction.TransactionException;
25  import org.mule.api.transport.ReplyToHandler;
26  import org.mule.config.ExceptionHelper;
27  import org.mule.config.i18n.CoreMessages;
28  import org.mule.config.i18n.MessageFactory;
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.jms.i18n.JmsMessages;
35  import org.mule.transport.jms.redelivery.AutoDiscoveryRedeliveryHandlerFactory;
36  import org.mule.transport.jms.redelivery.RedeliveryHandlerFactory;
37  import org.mule.transport.jms.xa.ConnectionFactoryWrapper;
38  import org.mule.util.BeanUtils;
39  
40  import java.text.MessageFormat;
41  import java.util.Hashtable;
42  import java.util.Map;
43  
44  import javax.jms.Connection;
45  import javax.jms.ConnectionFactory;
46  import javax.jms.ExceptionListener;
47  import javax.jms.JMSException;
48  import javax.jms.MessageConsumer;
49  import javax.jms.MessageProducer;
50  import javax.jms.Session;
51  import javax.jms.TemporaryQueue;
52  import javax.jms.TemporaryTopic;
53  import javax.jms.XAConnectionFactory;
54  import javax.naming.CommunicationException;
55  import javax.naming.Context;
56  import javax.naming.InitialContext;
57  import javax.naming.NamingException;
58  
59  import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicInteger;
60  
61  /**
62   * <code>JmsConnector</code> is a JMS 1.0.2b compliant connector that can be used
63   * by a Mule endpoint. The connector supports all JMS functionality including topics
64   * and queues, durable subscribers, acknowledgement modes and local transactions.
65   */
66  
67  public class JmsConnector extends AbstractConnector implements ExceptionListener
68  {
69  
70      public static final String JMS = "jms";
71  
72      /**
73       * Indicates that Mule should throw an exception on any redelivery attempt.
74       */
75      public static final int REDELIVERY_FAIL_ON_FIRST = 0;
76  
77      public static final int REDELIVERY_IGNORE = -1;
78  
79      private AtomicInteger receiverReportedExceptionCount = new AtomicInteger();
80  
81      ////////////////////////////////////////////////////////////////////////
82      // Properties
83      ////////////////////////////////////////////////////////////////////////
84  
85      private int acknowledgementMode = Session.AUTO_ACKNOWLEDGE;
86  
87      private String clientId;
88  
89      private boolean durable;
90  
91      private boolean noLocal;
92  
93      private boolean persistentDelivery;
94  
95      private boolean honorQosHeaders;
96  
97      private int maxRedelivery = REDELIVERY_FAIL_ON_FIRST;
98  
99      private boolean cacheJmsSessions = false;
100 
101     /**
102      * Whether to create a consumer on connect.
103      */
104     private boolean eagerConsumer = true;
105 
106     ////////////////////////////////////////////////////////////////////////
107     // JMS Connection
108     ////////////////////////////////////////////////////////////////////////
109 
110     /**
111      * JMS Connection, not settable by the user.
112      */
113     private Connection connection;
114 
115     private ConnectionFactory connectionFactory;
116 
117     private Map connectionFactoryProperties;
118 
119     public String username = null;
120 
121     public String password = null;
122 
123     ////////////////////////////////////////////////////////////////////////
124     // JNDI Connection
125     ////////////////////////////////////////////////////////////////////////
126 
127     private Context jndiContext = null;
128 
129     /**
130      * This object guards all access to the jndiContext
131      */
132     private final Object jndiLock = new Object();
133 
134     private String jndiProviderUrl;
135 
136     private String jndiInitialFactory;
137 
138     private Map jndiProviderProperties;
139 
140     private String connectionFactoryJndiName;
141 
142     private boolean jndiDestinations = false;
143 
144     private boolean forceJndiDestinations = false;
145 
146     ////////////////////////////////////////////////////////////////////////
147     // Strategy classes
148     ////////////////////////////////////////////////////////////////////////
149 
150     private String specification = JmsConstants.JMS_SPECIFICATION_102B;
151 
152     private JmsSupport jmsSupport;
153 
154     private JmsTopicResolver topicResolver;
155 
156     private RedeliveryHandlerFactory redeliveryHandlerFactory;
157 
158     /**
159      * determines whether a temporary JMSReplyTo destination will be used when using synchronous outbound JMS endpoints
160      */
161     private boolean disableTemporaryReplyToDestinations = false;
162 
163     /**
164      * If disableTemporaryReplyToDestinations = "true", this flag causes the original JMS Message to be returned as a
165      * synchronous response with any properties set on it by the JMS Provider (e.g., JMSMessageID).
166      *
167      * @see EE-1688/MULE-3059
168      */
169     private boolean returnOriginalMessageAsReply = false;
170 
171     /**
172      * In-container embedded mode disables some features for strict Java EE compliance.
173      */
174     private boolean embeddedMode;
175 
176     /**
177      * Overrides XaResource.isSameRM() result. Needed for IBM WMQ XA
178      * implementation (set to 'false'). Default value is null (don't override).
179      */
180     private Boolean sameRMOverrideValue;
181 
182     ////////////////////////////////////////////////////////////////////////
183     // Methods
184     ////////////////////////////////////////////////////////////////////////
185 
186     /* Register the Jms Exception reader if this class gets loaded */
187 
188     static
189     {
190         ExceptionHelper.registerExceptionReader(new JmsExceptionReader());
191     }
192 
193     public JmsConnector(MuleContext context)
194     {
195         super(context);
196     }
197     
198     public String getProtocol()
199     {
200         return JMS;
201     }
202 
203     @Override
204     protected void doInitialise() throws InitialisationException
205     {        
206         try
207         {
208             connectionFactory = this.createConnectionFactory();
209         }
210         catch (NamingException ne)
211         {
212             throw new InitialisationException(JmsMessages.errorCreatingConnectionFactory(), ne, this);
213         }
214 
215         if ((connectionFactoryProperties != null) && !connectionFactoryProperties.isEmpty())
216         {
217             // apply connection factory properties
218             BeanUtils.populateWithoutFail(connectionFactory, connectionFactoryProperties, true);
219         }
220 
221         if (topicResolver == null)
222         {
223             topicResolver = new DefaultJmsTopicResolver(this);
224         }
225         if (redeliveryHandlerFactory == null)
226         {
227             redeliveryHandlerFactory = new AutoDiscoveryRedeliveryHandlerFactory(this);
228         }
229 
230         try
231         {
232             muleContext.registerListener(new ConnectionNotificationListener<ConnectionNotification>()
233             {
234                 public void onNotification(ConnectionNotification notification)
235                 {
236                     if (notification.getAction() == ConnectionNotification.CONNECTION_DISCONNECTED
237                             || notification.getAction() == ConnectionNotification.CONNECTION_FAILED)
238                     {
239                         // Remove all dispatchers as any cached session will be invalidated
240                         clearDispatchers();
241                         // TODO should we dispose receivers here as well (in case they are
242                         // transactional)
243                         // gives a harmless NPE at
244                         // AbstractConnector.connect(AbstractConnector.java:927)
245                         // disposeReceivers();
246                     }
247                 }
248 
249             }, getName());
250 
251 
252         }
253         catch (NotificationException nex)
254         {
255             throw new InitialisationException(nex, this);
256         }
257 
258         if (jmsSupport == null)
259         {
260             jmsSupport = createJmsSupport();
261         }
262     }
263 
264     /**
265      * A factory method to create various JmsSupport class versions.
266      *
267      * @return JmsSupport instance
268      * @see JmsSupport
269      */
270     protected JmsSupport createJmsSupport()
271     {
272         final JmsSupport result;
273         if (JmsConstants.JMS_SPECIFICATION_102B.equals(specification))
274         {
275             result = new Jms102bSupport(this);
276         }
277         else
278         {
279             result = new Jms11Support(this);
280         }
281 
282         return result;
283     }
284 
285     protected ConnectionFactory createConnectionFactory() throws InitialisationException, NamingException
286     {
287         // if an initial factory class was configured that takes precedence over the 
288         // spring-configured connection factory or the one that our subclasses may provide
289         if (jndiInitialFactory != null)
290         {
291             this.initJndiContext();
292 
293             Object temp = jndiContext.lookup(connectionFactoryJndiName);
294             if (temp instanceof ConnectionFactory)
295             {
296                 return (ConnectionFactory) temp;
297             }
298             else
299             {
300                 throw new InitialisationException(
301                         JmsMessages.invalidResourceType(ConnectionFactory.class, temp), this);
302             }
303         }
304         else
305         {
306             // don't look up objects from JNDI in any case
307             jndiDestinations = false;
308             forceJndiDestinations = false;
309 
310             // don't use JNDI. Use the spring-configured connection factory if that's provided
311             if (connectionFactory != null)
312             {
313                 return connectionFactory;
314             }
315 
316             // no spring-configured connection factory. See if there is a default one (e.g. from
317             // subclass)
318             ConnectionFactory factory;
319             try
320             {
321                 factory = getDefaultConnectionFactory();
322             }
323             catch (Exception e)
324             {
325                 throw new InitialisationException(e, this);
326             }
327             if (factory == null)
328             {
329                 // no connection factory ... give up
330                 throw new InitialisationException(JmsMessages.noConnectionFactoryConfigured(), this);
331             }
332             return factory;
333         }
334     }
335 
336     /**
337      * Override this method to provide a default ConnectionFactory for a vendor-specific JMS Connector.
338      * @throws Exception 
339      */
340     protected ConnectionFactory getDefaultConnectionFactory() throws Exception
341     {
342         return null;
343     }
344 
345     @Override
346     protected void doDispose()
347     {
348         if (connection != null)
349         {
350             try
351             {
352                 connection.close();
353             }
354             catch (JMSException e)
355             {
356                 logger.error("Jms connector failed to dispose properly: ", e);
357             }
358             connection = null;
359         }
360 
361         if (jndiContext != null)
362         {
363             try
364             {
365                 jndiContext.close();
366             }
367             catch (NamingException ne)
368             {
369                 logger.error("Jms connector failed to dispose properly: ", ne);
370             }
371             finally
372             {
373                 jndiContext = null;
374             }
375         }
376     }
377 
378     protected void initJndiContext() throws NamingException, InitialisationException
379     {
380         synchronized (jndiLock)
381         {
382             Hashtable<String, Object> props = new Hashtable<String, Object>();
383 
384             if (jndiInitialFactory != null)
385             {
386                 props.put(Context.INITIAL_CONTEXT_FACTORY, jndiInitialFactory);
387             }
388             else if (jndiProviderProperties == null
389                     || !jndiProviderProperties.containsKey(Context.INITIAL_CONTEXT_FACTORY))
390             {
391                 throw new InitialisationException(CoreMessages.objectIsNull("jndiInitialFactory"), this);
392             }
393 
394             if (jndiProviderUrl != null)
395             {
396                 props.put(Context.PROVIDER_URL, jndiProviderUrl);
397             }
398 
399             if (jndiProviderProperties != null)
400             {
401                 props.putAll(jndiProviderProperties);
402             }
403 
404             jndiContext = new InitialContext(props);
405         }
406     }
407 
408     protected Object lookupFromJndi(String jndiName) throws NamingException
409     {
410         synchronized (jndiLock)
411         {
412             try
413             {
414                 return jndiContext.lookup(jndiName);
415             }
416             catch (CommunicationException ce)
417             {
418                 try
419                 {
420                     final Transaction tx = TransactionCoordination.getInstance().getTransaction();
421                     if (tx != null)
422                     {
423                         tx.setRollbackOnly();
424                     }
425                 }
426                 catch (TransactionException e)
427                 {
428                     throw new MuleRuntimeException(
429                             MessageFactory.createStaticMessage("Failed to mark transaction for rollback: "), e);
430                 }
431 
432                 // re-throw
433                 throw ce;
434             }
435         }
436     }
437 
438     protected Connection createConnection() throws NamingException, JMSException, InitialisationException
439     {
440         ConnectionFactory cf = this.connectionFactory;
441         Connection connection;
442 
443         try
444         {
445             if (cf instanceof XAConnectionFactory && muleContext.getTransactionManager() != null)
446             {
447                 cf = new ConnectionFactoryWrapper(cf, sameRMOverrideValue);
448             }
449         }
450         catch (Exception e)
451         {
452             throw new InitialisationException(e, this);
453         }
454 
455         if (username != null)
456         {
457             connection = jmsSupport.createConnection(cf, username, password);
458         }
459         else
460         {
461             connection = jmsSupport.createConnection(cf);
462         }
463 
464         if (connection != null)
465         {
466             // EE-1901: only sets the clientID if it was not already set
467             if (clientId != null && !clientId.equals(connection.getClientID()))
468             {
469                 connection.setClientID(getClientId());
470             }
471             if (!embeddedMode)
472             {
473                 connection.setExceptionListener(this);
474             }
475         }
476 
477 
478         return connection;
479     }
480 
481     public void onException(JMSException jmsException)
482     {
483         final JmsConnector jmsConnector = JmsConnector.this;
484         Map receivers = jmsConnector.getReceivers();
485         boolean isMultiConsumerReceiver = false;
486 
487         if (!receivers.isEmpty())
488         {
489             Map.Entry entry = (Map.Entry) receivers.entrySet().iterator().next();
490             if (entry.getValue() instanceof MultiConsumerJmsMessageReceiver)
491             {
492                 isMultiConsumerReceiver = true;
493             }
494         }
495 
496         int expectedReceiverCount = isMultiConsumerReceiver ? 1 :
497                 (jmsConnector.getReceivers().size() * jmsConnector.getNumberOfConcurrentTransactedReceivers());
498 
499         if (logger.isDebugEnabled())
500         {
501             logger.debug("About to recycle myself due to remote JMS connection shutdown but need "
502                     + "to wait for all active receivers to report connection loss. Receiver count: "
503                     + (receiverReportedExceptionCount.get() + 1) + '/' + expectedReceiverCount);
504         }
505 
506         if (receiverReportedExceptionCount.incrementAndGet() >= expectedReceiverCount)
507         {
508             receiverReportedExceptionCount.set(0);
509 
510             handleException(new ConnectException(jmsException, this));
511         }
512     }
513 
514     // TODO This might make retry work a bit better w/ JMS
515 //    @Override
516 //    public boolean validateConnection() throws Exception
517 //    {
518 //        logger.debug("Creating a temporary session to verify that we have a healthy connection...");
519 //
520 //        Connection connection;
521 //        Session session;
522 //        try
523 //        {
524 //            connection = createConnection();
525 //            if (connection == null)
526 //            {
527 //                return false;
528 //            }
529 //            session = connection.createSession(false, 1);
530 //            if (session == null)
531 //            {
532 //                return false;
533 //            }
534 //            session.close();
535 //            connection.close();
536 //            return true;
537 //        }
538 //        finally
539 //        {
540 //            session = null;
541 //            connection = null;
542 //        }
543 //    }
544 
545     @Override
546     protected void doConnect() throws Exception
547     {
548         connection = createConnection();
549         if (isStarted())
550         {
551             connection.start();
552         }
553     }
554 
555     @Override
556     protected void doDisconnect() throws Exception
557     {
558         try
559         {
560             if (connection != null)
561             {
562                 // Ignore exceptions while closing the connection
563                 if (!embeddedMode)
564                 {
565                     connection.setExceptionListener(null);
566                 }
567                 connection.close();
568             }
569         }
570         finally
571         {
572             // connectionFactory = null;
573             connection = null;
574         }
575     }
576 
577     @Override
578     protected Object getReceiverKey(FlowConstruct flowConstruct, InboundEndpoint endpoint)
579     {
580         return flowConstruct.getName() + "~" + endpoint.getEndpointURI().getAddress();
581     }
582 
583     public Session getSessionFromTransaction()
584     {
585         Transaction tx = TransactionCoordination.getInstance().getTransaction();
586         if (tx != null)
587         {
588             if (tx.hasResource(connection))
589             {
590                 if (logger.isDebugEnabled())
591                 {
592                     logger.debug("Retrieving jms session from current transaction " + tx);
593                 }
594 
595                 Session session = (Session) tx.getResource(connection);
596 
597                 if (logger.isDebugEnabled())
598                 {
599                     logger.debug("Using " + session + " bound to transaction " + tx);
600                 }
601 
602                 return session;
603             }
604         }
605         return null;
606     }
607 
608     public Session getSession(ImmutableEndpoint endpoint) throws JMSException
609     {
610         final boolean topic = getTopicResolver().isTopic(endpoint);
611         return getSession(endpoint.getTransactionConfig().isTransacted(), topic);
612     }
613 
614     public Session getSession(boolean transacted, boolean topic) throws JMSException
615     {
616         Session session = getSessionFromTransaction();
617         if (session != null)
618         {
619             return session;
620         }
621 
622         Transaction tx = TransactionCoordination.getInstance().getTransaction();
623 
624         session = jmsSupport.createSession(connection, topic, transacted, acknowledgementMode, noLocal);
625 
626         if (logger.isDebugEnabled())
627         {
628             logger.debug(MessageFormat.format(
629                     "Retrieved new jms session from connection: " +
630                             "topic={0}, transacted={1}, ack mode={2}, nolocal={3}: {4}",
631                     topic, transacted, acknowledgementMode, noLocal, session));
632         }
633 
634         if (tx != null)
635         {
636             logger.debug("Binding session " + session + " to current transaction " + tx);
637             try
638             {
639                 tx.bindResource(connection, session);
640             }
641             catch (TransactionException e)
642             {
643                 closeQuietly(session);
644                 throw new RuntimeException("Could not bind session to current transaction", e);
645             }
646         }
647         return session;
648     }
649 
650     @Override
651     protected void doStart() throws MuleException
652     {
653         //TODO: This should never be null or an exception should be thrown
654         if (connection != null)
655         {
656             try
657             {
658                 connection.start();
659             }
660             catch (JMSException e)
661             {
662                 throw new StartException(CoreMessages.failedToStart("Jms Connection"), e, this);
663             }
664         }
665     }
666 
667 
668     /**
669      * Closes a session if there is no active transaction in the current thread, otherwise the
670      * session will continue active until there is a direct call to close it.
671      *
672      * @param session the session that ill be closed if there is an active transaction.
673      */
674     public void closeSessionIfNoTransactionActive(Session session)
675     {
676         final Transaction transaction = TransactionCoordination.getInstance().getTransaction();
677         if (transaction == null)
678         {
679             if (logger.isDebugEnabled())
680             {
681                 logger.error("Closing non-TX session: " + session);
682             }
683             closeQuietly(session);
684         }
685         else if (logger.isDebugEnabled())
686         {
687             logger.error("Not closing TX session: " + session);
688         }
689     }
690 
691     @Override
692     protected void doStop() throws MuleException
693     {
694         if (connection != null)
695         {
696             try
697             {
698                 connection.stop();
699             }
700             catch (JMSException e)
701             {
702                 throw new StopException(CoreMessages.failedToStop("Jms Connection"), e, this);
703             }
704         }
705     }
706 
707     @Override
708     public ReplyToHandler getReplyToHandler(ImmutableEndpoint endpoint)
709     {
710         return new JmsReplyToHandler(this, getDefaultResponseTransformers(endpoint));
711     }
712 
713     /**
714      * This method may be overridden in case a certain JMS implementation does not
715      * support all the standard JMS properties.
716      */
717     public boolean supportsProperty(String property)
718     {
719         return true;
720     }
721 
722     /**
723      * This method may be overridden in order to apply pre-processing to the message
724      * as soon as it arrives.
725      *
726      * @param message - the incoming message
727      * @param session - the JMS session
728      * @return the preprocessed message
729      */
730     public javax.jms.Message preProcessMessage(javax.jms.Message message, Session session) throws Exception
731     {
732         return message;
733     }
734 
735     /**
736      * Closes the MessageProducer
737      *
738      * @param producer
739      * @throws JMSException
740      */
741     public void close(MessageProducer producer) throws JMSException
742     {
743         if (producer != null)
744         {
745             if (logger.isDebugEnabled())
746             {
747                 logger.debug("Closing producer: " + producer);
748             }
749             producer.close();
750         }
751         else if (logger.isDebugEnabled())
752         {
753             logger.debug("Producer is null, nothing to close");
754         }
755     }
756 
757     /**
758      * Closes the MessageProducer without throwing an exception (an error message is
759      * logged instead).
760      *
761      * @param producer
762      */
763     public void closeQuietly(MessageProducer producer)
764     {
765         try
766         {
767             close(producer);
768         }
769         catch (JMSException e)
770         {
771             logger.error("Failed to close jms message producer", e);
772         }
773     }
774 
775     /**
776      * Closes the MessageConsumer
777      *
778      * @param consumer
779      * @throws JMSException
780      */
781     public void close(MessageConsumer consumer) throws JMSException
782     {
783         if (consumer != null)
784         {
785             if (logger.isDebugEnabled())
786             {
787                 logger.debug("Closing consumer: " + consumer);
788             }
789             consumer.close();
790         }
791         else if (logger.isDebugEnabled())
792         {
793             logger.debug("Consumer is null, nothing to close");
794         }
795     }
796 
797     /**
798      * Closes the MessageConsumer without throwing an exception (an error message is
799      * logged instead).
800      *
801      * @param consumer
802      */
803     public void closeQuietly(MessageConsumer consumer)
804     {
805         try
806         {
807             close(consumer);
808         }
809         catch (JMSException e)
810         {
811             logger.error("Failed to close jms message consumer", e);
812         }
813     }
814 
815     /**
816      * Closes the MuleSession
817      *
818      * @param session
819      * @throws JMSException
820      */
821     public void close(Session session) throws JMSException
822     {
823         if (session != null)
824         {
825             if (logger.isDebugEnabled())
826             {
827                 logger.debug("Closing session " + session);
828             }
829             session.close();
830         }
831     }
832 
833     /**
834      * Closes the MuleSession without throwing an exception (an error message is logged
835      * instead).
836      *
837      * @param session
838      */
839     public void closeQuietly(Session session)
840     {
841         try
842         {
843             close(session);
844         }
845         catch (JMSException e)
846         {
847             logger.warn("Failed to close jms session consumer", e);
848         }
849     }
850 
851     /**
852      * Closes the TemporaryQueue
853      *
854      * @param tempQueue
855      * @throws JMSException
856      */
857     public void close(TemporaryQueue tempQueue) throws JMSException
858     {
859         if (tempQueue != null)
860         {
861             tempQueue.delete();
862         }
863     }
864 
865     /**
866      * Closes the TemporaryQueue without throwing an exception (an error message is
867      * logged instead).
868      *
869      * @param tempQueue
870      */
871     public void closeQuietly(TemporaryQueue tempQueue)
872     {
873         try
874         {
875             close(tempQueue);
876         }
877         catch (JMSException e)
878         {
879             if (logger.isWarnEnabled())
880             {
881                 String queueName = "";
882                 try
883                 {
884                     queueName = tempQueue.getQueueName();
885                 }
886                 catch (JMSException innerEx)
887                 {
888                     // ignore, we are just trying to get the queue name
889                 }
890                 logger.warn(MessageFormat.format(
891                         "Failed to delete a temporary queue ''{0}'' Reason: {1}",
892                         queueName, e.getMessage()));
893             }
894         }
895     }
896 
897     /**
898      * Closes the TemporaryTopic
899      *
900      * @param tempTopic
901      * @throws JMSException
902      */
903     public void close(TemporaryTopic tempTopic) throws JMSException
904     {
905         if (tempTopic != null)
906         {
907             tempTopic.delete();
908         }
909     }
910 
911     /**
912      * Closes the TemporaryTopic without throwing an exception (an error message is
913      * logged instead).
914      *
915      * @param tempTopic
916      */
917     public void closeQuietly(TemporaryTopic tempTopic)
918     {
919         try
920         {
921             close(tempTopic);
922         }
923         catch (JMSException e)
924         {
925             if (logger.isWarnEnabled())
926             {
927                 String topicName = "";
928                 try
929                 {
930                     topicName = tempTopic.getTopicName();
931                 }
932                 catch (JMSException innerEx)
933                 {
934                     // ignore, we are just trying to get the topic name
935                 }
936                 logger.warn("Failed to delete a temporary topic " + topicName, e);
937             }
938         }
939     }
940 
941     ////////////////////////////////////////////////////////////////////////
942     // Getters and Setters
943     ////////////////////////////////////////////////////////////////////////
944 
945     /**
946      * @return Returns the connection.
947      */
948     public Connection getConnection()
949     {
950         return connection;
951     }
952 
953     protected void setConnection(Connection connection)
954     {
955         this.connection = connection;
956     }
957 
958     /**
959      * @return Returns the acknowledgeMode.
960      */
961     public int getAcknowledgementMode()
962     {
963         return acknowledgementMode;
964     }
965 
966     /**
967      * @param acknowledgementMode The acknowledgementMode to set.
968      */
969     public void setAcknowledgementMode(int acknowledgementMode)
970     {
971         this.acknowledgementMode = acknowledgementMode;
972     }
973 
974     /**
975      * @return Returns the durable.
976      */
977     public boolean isDurable()
978     {
979         return durable;
980     }
981 
982     /**
983      * @param durable The durable to set.
984      */
985     public void setDurable(boolean durable)
986     {
987         this.durable = durable;
988     }
989 
990     /**
991      * @return Returns the noLocal.
992      */
993     public boolean isNoLocal()
994     {
995         return noLocal;
996     }
997 
998     /**
999      * @param noLocal The noLocal to set.
1000      */
1001     public void setNoLocal(boolean noLocal)
1002     {
1003         this.noLocal = noLocal;
1004     }
1005 
1006     /**
1007      * @return Returns the persistentDelivery.
1008      */
1009     public boolean isPersistentDelivery()
1010     {
1011         return persistentDelivery;
1012     }
1013 
1014     /**
1015      * @param persistentDelivery The persistentDelivery to set.
1016      */
1017     public void setPersistentDelivery(boolean persistentDelivery)
1018     {
1019         this.persistentDelivery = persistentDelivery;
1020     }
1021 
1022     public JmsSupport getJmsSupport()
1023     {
1024         return jmsSupport;
1025     }
1026 
1027     public void setJmsSupport(JmsSupport jmsSupport)
1028     {
1029         this.jmsSupport = jmsSupport;
1030     }
1031 
1032     public String getSpecification()
1033     {
1034         return specification;
1035     }
1036 
1037     public void setSpecification(String specification)
1038     {
1039         if (JmsConstants.JMS_SPECIFICATION_11.equals(specification)
1040             || (JmsConstants.JMS_SPECIFICATION_102B.equals(specification)))
1041         {
1042             this.specification = specification;
1043         }
1044         else
1045         {
1046             throw new IllegalArgumentException(
1047                 "JMS specification needs to be one of the defined values in JmsConstants but was: "
1048                                 + specification);
1049         }
1050     }
1051 
1052     public String getUsername()
1053     {
1054         return username;
1055     }
1056 
1057     public void setUsername(String username)
1058     {
1059         this.username = username;
1060     }
1061 
1062     public String getPassword()
1063     {
1064         return password;
1065     }
1066 
1067     public void setPassword(String password)
1068     {
1069         this.password = password;
1070     }
1071 
1072     public String getClientId()
1073     {
1074         return clientId;
1075     }
1076 
1077     public void setClientId(String clientId)
1078     {
1079         this.clientId = clientId;
1080     }
1081 
1082     public int getMaxRedelivery()
1083     {
1084         return maxRedelivery;
1085     }
1086 
1087     public void setMaxRedelivery(int maxRedelivery)
1088     {
1089         this.maxRedelivery = maxRedelivery;
1090     }
1091 
1092     @Override
1093     public boolean isResponseEnabled()
1094     {
1095         return true;
1096     }
1097 
1098 
1099     /**
1100      * Getter for property 'topicResolver'.
1101      *
1102      * @return Value for property 'topicResolver'.
1103      */
1104     public JmsTopicResolver getTopicResolver()
1105     {
1106         return topicResolver;
1107     }
1108 
1109     /**
1110      * Setter for property 'topicResolver'.
1111      *
1112      * @param topicResolver Value to set for property 'topicResolver'.
1113      */
1114     public void setTopicResolver(final JmsTopicResolver topicResolver)
1115     {
1116         this.topicResolver = topicResolver;
1117     }
1118 
1119     /**
1120      * Getter for property 'eagerConsumer'. Default
1121      * is {@code true}.
1122      *
1123      * @return Value for property 'eagerConsumer'.
1124      * @see #eagerConsumer
1125      */
1126     public boolean isEagerConsumer()
1127     {
1128         return eagerConsumer;
1129     }
1130 
1131     /**
1132      * A value of {@code true} will create a consumer on
1133      * connect, in contrast to lazy instantiation in the poll loop.
1134      * This setting very much depends on the JMS vendor.
1135      * Affects transactional receivers, typical symptoms are:
1136      * <ul>
1137      * <li> consumer thread hanging forever, though a message is
1138      * available
1139      * <li>failure to consume the first message (the rest
1140      * are fine)
1141      * </ul>
1142      * <p/>
1143      *
1144      * @param eagerConsumer Value to set for property 'eagerConsumer'.
1145      * @see #eagerConsumer
1146      * @see org.mule.transport.jms.XaTransactedJmsMessageReceiver
1147      */
1148     public void setEagerConsumer(final boolean eagerConsumer)
1149     {
1150         this.eagerConsumer = eagerConsumer;
1151     }
1152 
1153     public boolean isCacheJmsSessions()
1154     {
1155         return cacheJmsSessions;
1156     }
1157 
1158     public void setCacheJmsSessions(boolean cacheJmsSessions)
1159     {
1160         this.cacheJmsSessions = cacheJmsSessions;
1161     }
1162 
1163     public ConnectionFactory getConnectionFactory()
1164     {
1165         return connectionFactory;
1166     }
1167 
1168     public void setConnectionFactory(ConnectionFactory connectionFactory)
1169     {
1170         this.connectionFactory = connectionFactory;
1171     }
1172 
1173     public RedeliveryHandlerFactory getRedeliveryHandlerFactory()
1174     {
1175         return redeliveryHandlerFactory;
1176     }
1177 
1178     public void setRedeliveryHandlerFactory(RedeliveryHandlerFactory redeliveryHandlerFactory)
1179     {
1180         this.redeliveryHandlerFactory = redeliveryHandlerFactory;
1181     }
1182 
1183     /**
1184      * Sets the <code>honorQosHeaders</code> property, which determines whether
1185      * {@link JmsMessageDispatcher} should honor incoming message's QoS headers
1186      * (JMSPriority, JMSDeliveryMode).
1187      *
1188      * @param honorQosHeaders <code>true</code> if {@link JmsMessageDispatcher}
1189      *                        should honor incoming message's QoS headers; otherwise
1190      *                        <code>false</code> Default is <code>false</code>, meaning that
1191      *                        connector settings will override message headers.
1192      */
1193     public void setHonorQosHeaders(boolean honorQosHeaders)
1194     {
1195         this.honorQosHeaders = honorQosHeaders;
1196     }
1197 
1198     /**
1199      * Gets the value of <code>honorQosHeaders</code> property.
1200      *
1201      * @return <code>true</code> if <code>JmsMessageDispatcher</code> should
1202      *         honor incoming message's QoS headers; otherwise <code>false</code>
1203      *         Default is <code>false</code>, meaning that connector settings will
1204      *         override message headers.
1205      */
1206     public boolean isHonorQosHeaders()
1207     {
1208         return honorQosHeaders;
1209     }
1210 
1211     public Context getJndiContext()
1212     {
1213         return jndiContext;
1214     }
1215 
1216     public void setJndiContext(Context jndiContext)
1217     {
1218         this.jndiContext = jndiContext;
1219     }
1220 
1221     public String getJndiInitialFactory()
1222     {
1223         return jndiInitialFactory;
1224     }
1225 
1226     public void setJndiInitialFactory(String jndiInitialFactory)
1227     {
1228         this.jndiInitialFactory = jndiInitialFactory;
1229     }
1230 
1231     public String getJndiProviderUrl()
1232     {
1233         return jndiProviderUrl;
1234     }
1235 
1236     public void setJndiProviderUrl(String jndiProviderUrl)
1237     {
1238         this.jndiProviderUrl = jndiProviderUrl;
1239     }
1240 
1241     public Map getJndiProviderProperties()
1242     {
1243         return jndiProviderProperties;
1244     }
1245 
1246     public void setJndiProviderProperties(Map jndiProviderProperties)
1247     {
1248         this.jndiProviderProperties = jndiProviderProperties;
1249     }
1250 
1251     public String getConnectionFactoryJndiName()
1252     {
1253         return connectionFactoryJndiName;
1254     }
1255 
1256     public void setConnectionFactoryJndiName(String connectionFactoryJndiName)
1257     {
1258         this.connectionFactoryJndiName = connectionFactoryJndiName;
1259     }
1260 
1261     public boolean isJndiDestinations()
1262     {
1263         return jndiDestinations;
1264     }
1265 
1266     public void setJndiDestinations(boolean jndiDestinations)
1267     {
1268         this.jndiDestinations = jndiDestinations;
1269     }
1270 
1271     public boolean isForceJndiDestinations()
1272     {
1273         return forceJndiDestinations;
1274     }
1275 
1276     public void setForceJndiDestinations(boolean forceJndiDestinations)
1277     {
1278         this.forceJndiDestinations = forceJndiDestinations;
1279     }
1280 
1281     public boolean isDisableTemporaryReplyToDestinations()
1282     {
1283         return disableTemporaryReplyToDestinations;
1284     }
1285 
1286     public void setDisableTemporaryReplyToDestinations(boolean disableTemporaryReplyToDestinations)
1287     {
1288         this.disableTemporaryReplyToDestinations = disableTemporaryReplyToDestinations;
1289     }
1290 
1291     public boolean isReturnOriginalMessageAsReply()
1292     {
1293         return returnOriginalMessageAsReply;
1294     }
1295 
1296     public void setReturnOriginalMessageAsReply(boolean returnOriginalMessageAsReply)
1297     {
1298         this.returnOriginalMessageAsReply = returnOriginalMessageAsReply;
1299     }
1300 
1301     /**
1302      * @return Returns underlying connection factory properties.
1303      */
1304     public Map getConnectionFactoryProperties()
1305     {
1306         return connectionFactoryProperties;
1307     }
1308 
1309     /**
1310      * @param connectionFactoryProperties properties to be set on the underlying
1311      *                                    ConnectionFactory.
1312      */
1313     public void setConnectionFactoryProperties(Map connectionFactoryProperties)
1314     {
1315         this.connectionFactoryProperties = connectionFactoryProperties;
1316     }
1317 
1318     /**
1319      * A synonym for {@link #numberOfConcurrentTransactedReceivers}. Note that
1320      * it affects both transactional and non-transactional scenarios.
1321      *
1322      * @param count number of consumers
1323      */
1324     public void setNumberOfConsumers(int count)
1325     {
1326         this.numberOfConcurrentTransactedReceivers = count;
1327     }
1328 
1329     /**
1330      * A synonym for {@link #numberOfConcurrentTransactedReceivers}.
1331      *
1332      * @return number of consumers
1333      */
1334     public int getNumberOfConsumers()
1335     {
1336         return this.numberOfConcurrentTransactedReceivers;
1337     }
1338 
1339     public boolean isEmbeddedMode()
1340     {
1341         return embeddedMode;
1342     }
1343 
1344     public void setEmbeddedMode(boolean embeddedMode)
1345     {
1346         this.embeddedMode = embeddedMode;
1347     }
1348 
1349     public Boolean getSameRMOverrideValue()
1350     {
1351         return sameRMOverrideValue;
1352     }
1353 
1354     public void setSameRMOverrideValue(Boolean sameRMOverrideValue)
1355     {
1356         this.sameRMOverrideValue = sameRMOverrideValue;
1357     }
1358 }