001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.camel.component.jms;
018    
019    import javax.jms.ConnectionFactory;
020    import javax.jms.Destination;
021    import javax.jms.ExceptionListener;
022    import javax.jms.JMSException;
023    import javax.jms.Message;
024    import javax.jms.MessageProducer;
025    import javax.jms.Session;
026    import javax.jms.TopicPublisher;
027    
028    import org.apache.camel.RuntimeCamelException;
029    import org.apache.camel.util.ObjectHelper;
030    import org.apache.camel.util.PackageHelper;
031    import org.apache.commons.logging.Log;
032    import org.apache.commons.logging.LogFactory;
033    import org.springframework.core.task.TaskExecutor;
034    import org.springframework.jms.JmsException;
035    import org.springframework.jms.connection.JmsTransactionManager;
036    import org.springframework.jms.core.JmsOperations;
037    import org.springframework.jms.core.JmsTemplate;
038    import org.springframework.jms.core.JmsTemplate102;
039    import org.springframework.jms.core.MessageCreator;
040    import org.springframework.jms.core.SessionCallback;
041    import org.springframework.jms.listener.AbstractMessageListenerContainer;
042    import org.springframework.jms.listener.DefaultMessageListenerContainer;
043    import org.springframework.jms.listener.DefaultMessageListenerContainer102;
044    import org.springframework.jms.listener.SimpleMessageListenerContainer;
045    import org.springframework.jms.listener.SimpleMessageListenerContainer102;
046    import org.springframework.jms.support.JmsUtils;
047    import org.springframework.jms.support.converter.MessageConverter;
048    import org.springframework.jms.support.destination.DestinationResolver;
049    import org.springframework.transaction.PlatformTransactionManager;
050    import org.springframework.util.Assert;
051    
052    import static org.apache.camel.util.ObjectHelper.removeStartingCharacters;
053    
054    /**
055     * @version $Revision: 17359 $
056     */
057    public class JmsConfiguration implements Cloneable {
058    
059        public static final String QUEUE_PREFIX = "queue:";
060        public static final String TOPIC_PREFIX = "topic:";
061        public static final String TEMP_QUEUE_PREFIX = "temp:queue:";
062        public static final String TEMP_TOPIC_PREFIX = "temp:topic:";
063    
064        protected static final String TRANSACTED = "TRANSACTED";
065        protected static final String CLIENT_ACKNOWLEDGE = "CLIENT_ACKNOWLEDGE";
066        protected static final String AUTO_ACKNOWLEDGE = "AUTO_ACKNOWLEDGE";
067        protected static final String DUPS_OK_ACKNOWLEDGE = "DUPS_OK_ACKNOWLEDGE";
068        protected static final String REPLYTO_TEMP_DEST_AFFINITY_PER_COMPONENT = "component";
069        protected static final String REPLYTO_TEMP_DEST_AFFINITY_PER_ENDPOINT = "endpoint";
070        protected static final String REPLYTO_TEMP_DEST_AFFINITY_PER_PRODUCER = "producer";
071    
072        private static final transient Log LOG = LogFactory.getLog(JmsConfiguration.class);
073        private JmsOperations jmsOperations;
074        private DestinationResolver destinationResolver;
075        private ConnectionFactory connectionFactory;
076        private ConnectionFactory templateConnectionFactory;
077        private ConnectionFactory listenerConnectionFactory;
078        private int acknowledgementMode = -1;
079        private String acknowledgementModeName;
080        // Used to configure the spring Container
081        private ExceptionListener exceptionListener;
082        private ConsumerType consumerType = ConsumerType.Default;
083        private boolean autoStartup = true;
084        private boolean acceptMessagesWhileStopping;
085        private String clientId;
086        private String durableSubscriptionName;
087        private boolean subscriptionDurable;
088        private boolean exposeListenerSession = true;
089        private TaskExecutor taskExecutor;
090        private boolean pubSubNoLocal;
091        private int concurrentConsumers = 1;
092        private int maxMessagesPerTask = -1;
093        private int cacheLevel = -1;
094        private String cacheLevelName;
095        private long recoveryInterval = -1;
096        private long receiveTimeout = -1;
097        private long requestTimeout = 20000L;
098        private int idleTaskExecutionLimit = 1;
099        private int maxConcurrentConsumers = 1;
100        // JmsTemplate only
101        private boolean useVersion102;
102        private Boolean explicitQosEnabled;
103        private boolean deliveryPersistent = true;
104        private boolean replyToDeliveryPersistent = true;
105        private long timeToLive = -1;
106        private MessageConverter messageConverter;
107        private boolean mapJmsMessage = true;
108        private boolean messageIdEnabled = true;
109        private boolean messageTimestampEnabled = true;
110        private int priority = -1;
111        // Transaction related configuration
112        private boolean transacted;
113        private boolean transactedInOut;
114        private boolean lazyCreateTransactionManager = true;
115        private PlatformTransactionManager transactionManager;
116        private String transactionName;
117        private int transactionTimeout = -1;
118        private boolean preserveMessageQos;
119        private long requestMapPurgePollTimeMillis = 1000L;
120        private boolean disableReplyTo;
121        private boolean eagerLoadingOfProperties;
122        // Always make a JMS message copy when it's passed to Producer
123        private boolean alwaysCopyMessage;
124        private boolean useMessageIDAsCorrelationID;
125        private JmsProviderMetadata providerMetadata = new JmsProviderMetadata();
126        private JmsOperations metadataJmsOperations;
127        // defines the component created temporary replyTo destination sharing strategy:
128        // possible values are: "component", "endpoint", "producer"
129        // component - a single temp queue is shared among all producers for a given component instance
130        // endpoint - a single temp queue is shared among all producers for a given endpoint instance
131        // producer - a single temp queue is created per producer
132        private String replyToTempDestinationAffinity = REPLYTO_TEMP_DEST_AFFINITY_PER_ENDPOINT;
133        private String replyToDestination;
134        private String replyToDestinationSelectorName;
135        private JmsMessageType jmsMessageType;
136        private JmsKeyFormatStrategy jmsKeyFormatStrategy;
137        private boolean transferExchange;
138        private boolean transferException;
139        private boolean testConnectionOnStartup;
140    
141        public JmsConfiguration() {
142        }
143    
144        public JmsConfiguration(ConnectionFactory connectionFactory) {
145            this.connectionFactory = connectionFactory;
146        }
147    
148        /**
149         * Returns a copy of this configuration
150         */
151        public JmsConfiguration copy() {
152            try {
153                return (JmsConfiguration) clone();
154            } catch (CloneNotSupportedException e) {
155                throw new RuntimeCamelException(e);
156            }
157        }
158    
159    
160        public static interface MessageSentCallback {
161            void sent(Message message);
162        }
163    
164        public static class CamelJmsTemplate extends JmsTemplate {
165            private JmsConfiguration config;
166    
167            public CamelJmsTemplate(JmsConfiguration config, ConnectionFactory connectionFactory) {
168                super(connectionFactory);
169                this.config = config;
170            }
171    
172            public void send(final String destinationName,
173                             final MessageCreator messageCreator,
174                             final MessageSentCallback callback) throws JmsException {
175                execute(new SessionCallback() {
176                    public Object doInJms(Session session) throws JMSException {
177                        Destination destination = resolveDestinationName(session, destinationName);
178                        return doSendToDestination(destination, messageCreator, callback, session);
179                    }
180                }, false);
181            }
182    
183            public void send(final Destination destination,
184                             final MessageCreator messageCreator,
185                             final MessageSentCallback callback) throws JmsException {
186                execute(new SessionCallback() {
187                    public Object doInJms(Session session) throws JMSException {
188                        return doSendToDestination(destination, messageCreator, callback, session);
189                    }
190                }, false);
191            }
192    
193            public void send(final String destinationName,
194                             final MessageCreator messageCreator) throws JmsException {
195                execute(new SessionCallback() {
196                    public Object doInJms(Session session) throws JMSException {
197                        Destination destination = resolveDestinationName(session, destinationName);
198                        return doSendToDestination(destination, messageCreator, null, session);
199                    }
200                }, false);
201            }
202    
203            public void send(final Destination destination,
204                             final MessageCreator messageCreator) throws JmsException {
205                execute(new SessionCallback() {
206                    public Object doInJms(Session session) throws JMSException {
207                        return doSendToDestination(destination, messageCreator, null, session);
208                    }
209                }, false);
210            }
211    
212            private Object doSendToDestination(final Destination destination,
213                                               final MessageCreator messageCreator,
214                                               final MessageSentCallback callback,
215                                               final Session session) throws JMSException {
216    
217                Assert.notNull(messageCreator, "MessageCreator must not be null");
218                MessageProducer producer = createProducer(session, destination);
219                Message message = null;
220                try {
221                    message = messageCreator.createMessage(session);
222                    doSend(producer, message);
223                    // Check commit - avoid commit call within a JTA transaction.
224                    if (session.getTransacted() && isSessionLocallyTransacted(session)) {
225                        // Transacted session created by this template -> commit.
226                        JmsUtils.commitIfNecessary(session);
227                    }
228                } finally {
229                    JmsUtils.closeMessageProducer(producer);
230                }
231                if (message != null && callback != null) {
232                    callback.sent(message);
233                }
234                return null;
235            }
236    
237            /**
238             * Override so we can support preserving the Qos settings that have
239             * been set on the message.
240             */
241            @Override
242            protected void doSend(MessageProducer producer, Message message) throws JMSException {
243                if (config.isPreserveMessageQos()) {
244                    long ttl = message.getJMSExpiration();
245                    if (ttl != 0) {
246                        ttl = ttl - System.currentTimeMillis();
247                        // Message had expired.. so set the ttl as small as possible
248                        if (ttl <= 0) {
249                            ttl = 1;
250                        }
251                    }
252    
253                    int priority = message.getJMSPriority();
254                    if (priority < 0 || priority > 9) {
255                        // use priority from endpoint if not provided on message with a valid range
256                        priority = this.getPriority();
257                    }
258    
259                    // if a delivery mode was set as a JMS header then we have used a temporary
260                    // property to store it - CamelJMSDeliveryMode. Otherwise we could not keep
261                    // track whether it was set or not as getJMSDeliveryMode() will default return 1 regardless
262                    // if it was set or not, so we can never tell if end user provided it in a header
263                    int deliveryMode;
264                    if (JmsMessageHelper.hasProperty(message, JmsConstants.JMS_DELIVERY_MODE)) {
265                        deliveryMode = message.getIntProperty(JmsConstants.JMS_DELIVERY_MODE);
266                        // remove the temporary property
267                        JmsMessageHelper.removeJmsProperty(message, JmsConstants.JMS_DELIVERY_MODE);
268                    } else {
269                        deliveryMode = this.getDeliveryMode();
270                    }
271    
272                    // need to log just before so the message is 100% correct when logged
273                    if (logger.isDebugEnabled()) {
274                        logger.debug("Sending JMS message to: " + producer.getDestination() + " with message: " + message);
275                    }
276                    producer.send(message, deliveryMode, priority, ttl);
277                } else {
278                    // need to log just before so the message is 100% correct when logged
279                    if (logger.isDebugEnabled()) {
280                        logger.debug("Sending JMS message to: " + producer.getDestination() + " with message: " + message);
281                    }
282                    super.doSend(producer, message);
283                }
284            }
285        }
286    
287        /**
288         * @deprecated will be removed in the future
289         */
290        @Deprecated
291        public static class CamelJmsTemplate102 extends JmsTemplate102 {
292            private JmsConfiguration config;
293    
294            public CamelJmsTemplate102(JmsConfiguration config, ConnectionFactory connectionFactory, boolean pubSubDomain) {
295                super(connectionFactory, pubSubDomain);
296                this.config = config;
297            }
298    
299            public void send(final String destinationName,
300                             final MessageCreator messageCreator,
301                             final MessageSentCallback callback) throws JmsException {
302                execute(new SessionCallback() {
303                    public Object doInJms(Session session) throws JMSException {
304                        Destination destination = resolveDestinationName(session, destinationName);
305                        return doSendToDestination(destination, messageCreator, callback, session);
306                    }
307                }, false);
308            }
309    
310            public void send(final Destination destination,
311                             final MessageCreator messageCreator,
312                             final MessageSentCallback callback) throws JmsException {
313                execute(new SessionCallback() {
314                    public Object doInJms(Session session) throws JMSException {
315                        return doSendToDestination(destination, messageCreator, callback, session);
316                    }
317                }, false);
318            }
319    
320            public void send(final String destinationName,
321                             final MessageCreator messageCreator) throws JmsException {
322                execute(new SessionCallback() {
323                    public Object doInJms(Session session) throws JMSException {
324                        Destination destination = resolveDestinationName(session, destinationName);
325                        return doSendToDestination(destination, messageCreator, null, session);
326                    }
327                }, false);
328            }
329    
330            public void send(final Destination destination,
331                             final MessageCreator messageCreator) throws JmsException {
332                execute(new SessionCallback() {
333                    public Object doInJms(Session session) throws JMSException {
334                        return doSendToDestination(destination, messageCreator, null, session);
335                    }
336                }, false);
337            }
338    
339            private Object doSendToDestination(final Destination destination,
340                                               final MessageCreator messageCreator,
341                                               final MessageSentCallback callback,
342                                               final Session session) throws JMSException {
343                Assert.notNull(messageCreator, "MessageCreator must not be null");
344                MessageProducer producer = createProducer(session, destination);
345                Message message = null;
346                try {
347                    message = messageCreator.createMessage(session);
348                    if (logger.isDebugEnabled()) {
349                        logger.debug("Sending JMS message to: " + producer.getDestination() + " with message: " + message);
350                    }
351                    doSend(producer, message);
352                    // Check commit - avoid commit call within a JTA transaction.
353                    if (session.getTransacted() && isSessionLocallyTransacted(session)) {
354                        // Transacted session created by this template -> commit.
355                        JmsUtils.commitIfNecessary(session);
356                    }
357                } finally {
358                    JmsUtils.closeMessageProducer(producer);
359                }
360                if (message != null && callback != null) {
361                    callback.sent(message);
362                }
363                return null;
364            }
365    
366            /**
367             * Override so we can support preserving the Qos settings that have
368             * been set on the message.
369             */
370            @Override
371            protected void doSend(MessageProducer producer, Message message) throws JMSException {
372                if (config.isPreserveMessageQos()) {
373                    long ttl = message.getJMSExpiration();
374                    if (ttl != 0) {
375                        ttl = ttl - System.currentTimeMillis();
376                        // Message had expired.. so set the ttl as small as possible
377                        if (ttl <= 0) {
378                            ttl = 1;
379                        }
380                    }
381    
382                    int priority = message.getJMSPriority();
383                    if (priority <= 0) {
384                        // use priority from endpoint if not provided on message
385                        priority = this.getPriority();
386                    }
387    
388                    // if a delivery mode was set as a JMS header then we have used a temporary
389                    // property to store it - CamelJMSDeliveryMode. Otherwise we could not keep
390                    // track whether it was set or not as getJMSDeliveryMode() will default return 1 regardless
391                    // if it was set or not, so we can never tell if end user provided it in a header
392                    int deliveryMode;
393                    if (JmsMessageHelper.hasProperty(message, JmsConstants.JMS_DELIVERY_MODE)) {
394                        deliveryMode = message.getIntProperty(JmsConstants.JMS_DELIVERY_MODE);
395                        // remove the temporary property
396                        JmsMessageHelper.removeJmsProperty(message, JmsConstants.JMS_DELIVERY_MODE);
397                    } else {
398                        deliveryMode = this.getDeliveryMode();
399                    }
400    
401                    // need to log just before so the message is 100% correct when logged
402                    if (logger.isDebugEnabled()) {
403                        logger.debug("Sending JMS message to: " + producer.getDestination() + " with message: " + message);
404                    }
405                    if (isPubSubDomain()) {
406                        ((TopicPublisher) producer).publish(message, deliveryMode, priority, ttl);
407                    } else {
408                        producer.send(message, deliveryMode, priority, ttl);
409                    }
410                } else {
411                    // need to log just before so the message is 100% correct when logged
412                    if (logger.isDebugEnabled()) {
413                        logger.debug("Sending JMS message to: " + producer.getDestination() + " with message: " + message);
414                    }
415                    super.doSend(producer, message);
416                }
417            }
418        }
419    
420    
421        /**
422         * Creates a {@link JmsOperations} object used for request/response using a request timeout value
423         */
424        public JmsOperations createInOutTemplate(JmsEndpoint endpoint, boolean pubSubDomain, String destination, long requestTimeout) {
425            JmsOperations answer = createInOnlyTemplate(endpoint, pubSubDomain, destination);
426            if (answer instanceof JmsTemplate && requestTimeout > 0) {
427                JmsTemplate jmsTemplate = (JmsTemplate) answer;
428                jmsTemplate.setExplicitQosEnabled(true);
429                if (timeToLive < 0) {
430                    // If TTL not specified, then default to
431                    jmsTemplate.setTimeToLive(requestTimeout);
432                }
433                jmsTemplate.setSessionTransacted(isTransactedInOut());
434                if (isTransactedInOut()) {
435                    jmsTemplate.setSessionAcknowledgeMode(Session.SESSION_TRANSACTED);
436                } else {
437                    if (acknowledgementMode >= 0) {
438                        jmsTemplate.setSessionAcknowledgeMode(acknowledgementMode);
439                    } else if (acknowledgementModeName != null) {
440                        jmsTemplate.setSessionAcknowledgeModeName(acknowledgementModeName);
441                    } else {
442                        // default to AUTO
443                        jmsTemplate.setSessionAcknowledgeMode(Session.AUTO_ACKNOWLEDGE);
444                    }
445                }
446            }
447            return answer;
448        }
449    
450        /**
451         * Creates a {@link JmsOperations} object used for one way messaging
452         */
453        public JmsOperations createInOnlyTemplate(JmsEndpoint endpoint, boolean pubSubDomain, String destination) {
454    
455            if (jmsOperations != null) {
456                return jmsOperations;
457            }
458    
459            ConnectionFactory factory = getTemplateConnectionFactory();
460    
461            JmsTemplate template = useVersion102
462                    ? new CamelJmsTemplate102(this, factory, pubSubDomain)
463                    : new CamelJmsTemplate(this, factory);
464    
465            template.setPubSubDomain(pubSubDomain);
466            if (destinationResolver != null) {
467                template.setDestinationResolver(destinationResolver);
468                if (endpoint instanceof DestinationEndpoint) {
469                    LOG.debug("You are overloading the destinationResolver property on a DestinationEndpoint; are you sure you want to do that?");
470                }
471            } else if (endpoint instanceof DestinationEndpoint) {
472                DestinationEndpoint destinationEndpoint = (DestinationEndpoint) endpoint;
473                template.setDestinationResolver(createDestinationResolver(destinationEndpoint));
474            }
475            template.setDefaultDestinationName(destination);
476    
477            template.setExplicitQosEnabled(isExplicitQosEnabled());
478            template.setDeliveryPersistent(deliveryPersistent);
479            if (messageConverter != null) {
480                template.setMessageConverter(messageConverter);
481            }
482            template.setMessageIdEnabled(messageIdEnabled);
483            template.setMessageTimestampEnabled(messageTimestampEnabled);
484            if (priority >= 0) {
485                template.setPriority(priority);
486            }
487            template.setPubSubNoLocal(pubSubNoLocal);
488            if (receiveTimeout >= 0) {
489                template.setReceiveTimeout(receiveTimeout);
490            }
491            if (timeToLive >= 0) {
492                template.setTimeToLive(timeToLive);
493            }
494    
495            template.setSessionTransacted(transacted);
496            if (transacted) {
497                template.setSessionAcknowledgeMode(Session.SESSION_TRANSACTED);
498            } else {
499                // This is here for completeness, but the template should not get
500                // used for receiving messages.
501                if (acknowledgementMode >= 0) {
502                    template.setSessionAcknowledgeMode(acknowledgementMode);
503                } else if (acknowledgementModeName != null) {
504                    template.setSessionAcknowledgeModeName(acknowledgementModeName);
505                }
506            }
507            return template;
508        }
509    
510        public AbstractMessageListenerContainer createMessageListenerContainer(JmsEndpoint endpoint) {
511            AbstractMessageListenerContainer container = chooseMessageListenerContainerImplementation();
512            configureMessageListenerContainer(container, endpoint);
513            return container;
514        }
515    
516    
517        // Properties
518        // -------------------------------------------------------------------------
519        public ConnectionFactory getConnectionFactory() {
520            if (connectionFactory == null) {
521                connectionFactory = createConnectionFactory();
522            }
523            return connectionFactory;
524        }
525    
526        /**
527         * Sets the default connection factory to be used if a connection factory is
528         * not specified for either
529         * {@link #setTemplateConnectionFactory(ConnectionFactory)} or
530         * {@link #setListenerConnectionFactory(ConnectionFactory)}
531         *
532         * @param connectionFactory the default connection factory to use
533         */
534        public void setConnectionFactory(ConnectionFactory connectionFactory) {
535            this.connectionFactory = connectionFactory;
536        }
537    
538        public ConnectionFactory getListenerConnectionFactory() {
539            if (listenerConnectionFactory == null) {
540                listenerConnectionFactory = createListenerConnectionFactory();
541            }
542            return listenerConnectionFactory;
543        }
544    
545        /**
546         * Sets the connection factory to be used for consuming messages via the
547         * {@link #createMessageListenerContainer(JmsEndpoint)}
548         *
549         * @param listenerConnectionFactory the connection factory to use for
550         *                                  consuming messages
551         */
552        public void setListenerConnectionFactory(ConnectionFactory listenerConnectionFactory) {
553            this.listenerConnectionFactory = listenerConnectionFactory;
554        }
555    
556        public ConnectionFactory getTemplateConnectionFactory() {
557            if (templateConnectionFactory == null) {
558                templateConnectionFactory = createTemplateConnectionFactory();
559            }
560            return templateConnectionFactory;
561        }
562    
563        /**
564         * Sets the connection factory to be used for sending messages via the
565         * {@link JmsTemplate} via {@link #createInOnlyTemplate(JmsEndpoint,boolean, String)}
566         *
567         * @param templateConnectionFactory the connection factory for sending messages
568         */
569        public void setTemplateConnectionFactory(ConnectionFactory templateConnectionFactory) {
570            this.templateConnectionFactory = templateConnectionFactory;
571        }
572    
573        /**
574         * @deprecated will be removed in the future
575         */
576        @Deprecated
577        public boolean isUseVersion102() {
578            return useVersion102;
579        }
580    
581        /**
582         * @deprecated will be removed in the future
583         */
584        @Deprecated
585        public void setUseVersion102(boolean useVersion102) {
586            this.useVersion102 = useVersion102;
587        }
588    
589        public boolean isAutoStartup() {
590            return autoStartup;
591        }
592    
593        public void setAutoStartup(boolean autoStartup) {
594            this.autoStartup = autoStartup;
595        }
596    
597        public boolean isAcceptMessagesWhileStopping() {
598            return acceptMessagesWhileStopping;
599        }
600    
601        public void setAcceptMessagesWhileStopping(boolean acceptMessagesWhileStopping) {
602            this.acceptMessagesWhileStopping = acceptMessagesWhileStopping;
603        }
604    
605        public String getClientId() {
606            return clientId;
607        }
608    
609        public void setClientId(String consumerClientId) {
610            this.clientId = consumerClientId;
611        }
612    
613        public String getDurableSubscriptionName() {
614            return durableSubscriptionName;
615        }
616    
617        public void setDurableSubscriptionName(String durableSubscriptionName) {
618            this.durableSubscriptionName = durableSubscriptionName;
619        }
620    
621        public ExceptionListener getExceptionListener() {
622            return exceptionListener;
623        }
624    
625        public void setExceptionListener(ExceptionListener exceptionListener) {
626            this.exceptionListener = exceptionListener;
627        }
628    
629        public boolean isSubscriptionDurable() {
630            return subscriptionDurable;
631        }
632    
633        public void setSubscriptionDurable(boolean subscriptionDurable) {
634            this.subscriptionDurable = subscriptionDurable;
635        }
636    
637        public String getAcknowledgementModeName() {
638            return acknowledgementModeName;
639        }
640    
641        public void setAcknowledgementModeName(String consumerAcknowledgementMode) {
642            this.acknowledgementModeName = consumerAcknowledgementMode;
643            this.acknowledgementMode = -1;
644        }
645    
646        public boolean isExposeListenerSession() {
647            return exposeListenerSession;
648        }
649    
650        public void setExposeListenerSession(boolean exposeListenerSession) {
651            this.exposeListenerSession = exposeListenerSession;
652        }
653    
654        public TaskExecutor getTaskExecutor() {
655            return taskExecutor;
656        }
657    
658        public void setTaskExecutor(TaskExecutor taskExecutor) {
659            this.taskExecutor = taskExecutor;
660        }
661    
662        public boolean isPubSubNoLocal() {
663            return pubSubNoLocal;
664        }
665    
666        public void setPubSubNoLocal(boolean pubSubNoLocal) {
667            this.pubSubNoLocal = pubSubNoLocal;
668        }
669    
670        public int getConcurrentConsumers() {
671            return concurrentConsumers;
672        }
673    
674        public void setConcurrentConsumers(int concurrentConsumers) {
675            this.concurrentConsumers = concurrentConsumers;
676        }
677    
678        public int getMaxMessagesPerTask() {
679            return maxMessagesPerTask;
680        }
681    
682        public void setMaxMessagesPerTask(int maxMessagesPerTask) {
683            this.maxMessagesPerTask = maxMessagesPerTask;
684        }
685    
686        public int getCacheLevel() {
687            return cacheLevel;
688        }
689    
690        public void setCacheLevel(int cacheLevel) {
691            this.cacheLevel = cacheLevel;
692        }
693    
694        public String getCacheLevelName() {
695            return cacheLevelName;
696        }
697    
698        public void setCacheLevelName(String cacheName) {
699            this.cacheLevelName = cacheName;
700        }
701    
702        public long getRecoveryInterval() {
703            return recoveryInterval;
704        }
705    
706        public void setRecoveryInterval(long recoveryInterval) {
707            this.recoveryInterval = recoveryInterval;
708        }
709    
710        public long getReceiveTimeout() {
711            return receiveTimeout;
712        }
713    
714        public void setReceiveTimeout(long receiveTimeout) {
715            this.receiveTimeout = receiveTimeout;
716        }
717    
718        public PlatformTransactionManager getTransactionManager() {
719            if (transactionManager == null && isTransacted() && isLazyCreateTransactionManager()) {
720                transactionManager = createTransactionManager();
721            }
722            return transactionManager;
723        }
724    
725        public void setTransactionManager(PlatformTransactionManager transactionManager) {
726            this.transactionManager = transactionManager;
727        }
728    
729        public String getTransactionName() {
730            return transactionName;
731        }
732    
733        public void setTransactionName(String transactionName) {
734            this.transactionName = transactionName;
735        }
736    
737        public int getTransactionTimeout() {
738            return transactionTimeout;
739        }
740    
741        public void setTransactionTimeout(int transactionTimeout) {
742            this.transactionTimeout = transactionTimeout;
743        }
744    
745        public int getIdleTaskExecutionLimit() {
746            return idleTaskExecutionLimit;
747        }
748    
749        public void setIdleTaskExecutionLimit(int idleTaskExecutionLimit) {
750            this.idleTaskExecutionLimit = idleTaskExecutionLimit;
751        }
752    
753        public int getMaxConcurrentConsumers() {
754            return maxConcurrentConsumers;
755        }
756    
757        public void setMaxConcurrentConsumers(int maxConcurrentConsumers) {
758            this.maxConcurrentConsumers = maxConcurrentConsumers;
759        }
760    
761        public boolean isExplicitQosEnabled() {
762            return explicitQosEnabled != null ? explicitQosEnabled : false;
763        }
764    
765        public void setExplicitQosEnabled(boolean explicitQosEnabled) {
766            this.explicitQosEnabled = explicitQosEnabled;
767        }
768    
769        public boolean isDeliveryPersistent() {
770            return deliveryPersistent;
771        }
772    
773        public void setDeliveryPersistent(boolean deliveryPersistent) {
774            this.deliveryPersistent = deliveryPersistent;
775            configuredQoS();
776        }
777    
778        public boolean isReplyToDeliveryPersistent() {
779            return replyToDeliveryPersistent;
780        }
781    
782        public void setReplyToDeliveryPersistent(boolean replyToDeliveryPersistent) {
783            this.replyToDeliveryPersistent = replyToDeliveryPersistent;
784        }
785    
786        public long getTimeToLive() {
787            return timeToLive;
788        }
789    
790        public void setTimeToLive(long timeToLive) {
791            this.timeToLive = timeToLive;
792            configuredQoS();
793        }
794    
795        public MessageConverter getMessageConverter() {
796            return messageConverter;
797        }
798    
799        public void setMessageConverter(MessageConverter messageConverter) {
800            this.messageConverter = messageConverter;
801        }
802    
803        public boolean isMapJmsMessage() {
804            return mapJmsMessage;
805        }
806    
807        public void setMapJmsMessage(boolean mapJmsMessage) {
808            this.mapJmsMessage = mapJmsMessage;
809        }
810    
811        public boolean isMessageIdEnabled() {
812            return messageIdEnabled;
813        }
814    
815        public void setMessageIdEnabled(boolean messageIdEnabled) {
816            this.messageIdEnabled = messageIdEnabled;
817        }
818    
819        public boolean isMessageTimestampEnabled() {
820            return messageTimestampEnabled;
821        }
822    
823        public void setMessageTimestampEnabled(boolean messageTimestampEnabled) {
824            this.messageTimestampEnabled = messageTimestampEnabled;
825        }
826    
827        public int getPriority() {
828            return priority;
829        }
830    
831        public void setPriority(int priority) {
832            this.priority = priority;
833            configuredQoS();
834        }
835    
836        public ConsumerType getConsumerType() {
837            return consumerType;
838        }
839    
840        public void setConsumerType(ConsumerType consumerType) {
841            this.consumerType = consumerType;
842        }
843    
844        public int getAcknowledgementMode() {
845            return acknowledgementMode;
846        }
847    
848        public void setAcknowledgementMode(int consumerAcknowledgementMode) {
849            this.acknowledgementMode = consumerAcknowledgementMode;
850            this.acknowledgementModeName = null;
851        }
852    
853        public boolean isTransacted() {
854            return transacted;
855        }
856    
857        public void setTransacted(boolean consumerTransacted) {
858            this.transacted = consumerTransacted;
859        }
860    
861        /**
862         * Should InOut operations (request reply) default to using transacted mode?
863         * <p/>
864         * By default this is false as you need to commit the outgoing request before you can consume the input
865         */
866        public boolean isTransactedInOut() {
867            return transactedInOut;
868        }
869    
870        public void setTransactedInOut(boolean transactedInOut) {
871            this.transactedInOut = transactedInOut;
872        }
873    
874        public boolean isLazyCreateTransactionManager() {
875            return lazyCreateTransactionManager;
876        }
877    
878        public void setLazyCreateTransactionManager(boolean lazyCreating) {
879            this.lazyCreateTransactionManager = lazyCreating;
880        }
881    
882        public boolean isEagerLoadingOfProperties() {
883            return eagerLoadingOfProperties;
884        }
885    
886        /**
887         * Enables eager loading of JMS properties as soon as a message is loaded
888         * which generally is inefficient as the JMS properties may not be required
889         * but sometimes can catch early any issues with the underlying JMS provider
890         * and the use of JMS properties
891         *
892         * @param eagerLoadingOfProperties whether or not to enable eager loading of
893         *                                 JMS properties on inbound messages
894         */
895        public void setEagerLoadingOfProperties(boolean eagerLoadingOfProperties) {
896            this.eagerLoadingOfProperties = eagerLoadingOfProperties;
897        }
898    
899        public boolean isDisableReplyTo() {
900            return disableReplyTo;
901        }
902    
903        /**
904         * Disables the use of the JMSReplyTo header for consumers so that inbound
905         * messages are treated as InOnly rather than InOut requests.
906         *
907         * @param disableReplyTo whether or not to disable the use of JMSReplyTo
908         *                       header indicating an InOut
909         */
910        public void setDisableReplyTo(boolean disableReplyTo) {
911            this.disableReplyTo = disableReplyTo;
912        }
913    
914        /**
915         * Set to true if you want to send message using the QoS settings specified
916         * on the message. Normally the QoS settings used are the one configured on
917         * this Object.
918         */
919        public void setPreserveMessageQos(boolean preserveMessageQos) {
920            this.preserveMessageQos = preserveMessageQos;
921        }
922    
923        public JmsOperations getJmsOperations() {
924            return jmsOperations;
925        }
926    
927        public void setJmsOperations(JmsOperations jmsOperations) {
928            this.jmsOperations = jmsOperations;
929        }
930    
931        public DestinationResolver getDestinationResolver() {
932            return destinationResolver;
933        }
934    
935        public void setDestinationResolver(DestinationResolver destinationResolver) {
936            this.destinationResolver = destinationResolver;
937        }
938    
939        public long getRequestMapPurgePollTimeMillis() {
940            return requestMapPurgePollTimeMillis;
941        }
942    
943        /**
944         * Sets the frequency that the requestMap for InOut exchanges is purged for
945         * timed out message exchanges
946         */
947        public void setRequestMapPurgePollTimeMillis(long requestMapPurgePollTimeMillis) {
948            this.requestMapPurgePollTimeMillis = requestMapPurgePollTimeMillis;
949        }
950    
951        public JmsProviderMetadata getProviderMetadata() {
952            return providerMetadata;
953        }
954    
955        /**
956         * Allows the provider metadata to be explicitly configured. Typically this is not required
957         * and Camel will auto-detect the provider metadata from the underlying provider.
958         */
959        public void setProviderMetadata(JmsProviderMetadata providerMetadata) {
960            this.providerMetadata = providerMetadata;
961        }
962    
963        public JmsOperations getMetadataJmsOperations(JmsEndpoint endpoint) {
964            if (metadataJmsOperations == null) {
965                metadataJmsOperations = getJmsOperations();
966                if (metadataJmsOperations == null) {
967                    metadataJmsOperations = createInOnlyTemplate(endpoint, false, null);
968                }
969            }
970            return metadataJmsOperations;
971        }
972    
973        /**
974         * Sets the {@link JmsOperations} used to deduce the {@link JmsProviderMetadata} details which if none
975         * is customized one is lazily created on demand
976         */
977        public void setMetadataJmsOperations(JmsOperations metadataJmsOperations) {
978            this.metadataJmsOperations = metadataJmsOperations;
979        }
980    
981    
982        // Implementation methods
983        // -------------------------------------------------------------------------
984    
985        public static DestinationResolver createDestinationResolver(final DestinationEndpoint destinationEndpoint) {
986            return new DestinationResolver() {
987                public Destination resolveDestinationName(Session session, String destinationName, boolean pubSubDomain) throws JMSException {
988                    return destinationEndpoint.getJmsDestination(session);
989                }
990            };
991        }
992    
993    
994        protected void configureMessageListenerContainer(AbstractMessageListenerContainer container,
995                                                         JmsEndpoint endpoint) {
996            container.setConnectionFactory(getListenerConnectionFactory());
997            if (endpoint instanceof DestinationEndpoint) {
998                container.setDestinationResolver(createDestinationResolver((DestinationEndpoint) endpoint));
999            } else if (destinationResolver != null) {
1000                container.setDestinationResolver(destinationResolver);
1001            }
1002            container.setAutoStartup(autoStartup);
1003    
1004            if (clientId != null) {
1005                container.setClientId(clientId);
1006            }
1007            container.setSubscriptionDurable(subscriptionDurable);
1008            if (durableSubscriptionName != null) {
1009                container.setDurableSubscriptionName(durableSubscriptionName);
1010            }
1011    
1012            // lets default to durable subscription if the subscriber name and
1013            // client ID are specified (as there's
1014            // no reason to specify them if not! :)
1015            if (durableSubscriptionName != null && clientId != null) {
1016                container.setSubscriptionDurable(true);
1017            }
1018    
1019            if (exceptionListener != null) {
1020                container.setExceptionListener(exceptionListener);
1021            }
1022    
1023            container.setAcceptMessagesWhileStopping(acceptMessagesWhileStopping);
1024            container.setExposeListenerSession(exposeListenerSession);
1025            container.setSessionTransacted(transacted);
1026            if (transacted) {
1027                container.setSessionAcknowledgeMode(Session.SESSION_TRANSACTED);
1028            } else {
1029                if (acknowledgementMode >= 0) {
1030                    container.setSessionAcknowledgeMode(acknowledgementMode);
1031                } else if (acknowledgementModeName != null) {
1032                    container.setSessionAcknowledgeModeName(acknowledgementModeName);
1033                }
1034            }
1035    
1036            if (endpoint.getSelector() != null && endpoint.getSelector().length() != 0) {
1037                container.setMessageSelector(endpoint.getSelector());
1038            }
1039    
1040            if (container instanceof DefaultMessageListenerContainer) {
1041                // this includes DefaultMessageListenerContainer102
1042                DefaultMessageListenerContainer listenerContainer = (DefaultMessageListenerContainer) container;
1043                if (concurrentConsumers >= 0) {
1044                    listenerContainer.setConcurrentConsumers(concurrentConsumers);
1045                }
1046    
1047                if (cacheLevel >= 0) {
1048                    listenerContainer.setCacheLevel(cacheLevel);
1049                } else if (cacheLevelName != null) {
1050                    listenerContainer.setCacheLevelName(cacheLevelName);
1051                } else {
1052                    listenerContainer.setCacheLevel(defaultCacheLevel(endpoint));
1053                }
1054    
1055                if (idleTaskExecutionLimit >= 0) {
1056                    listenerContainer.setIdleTaskExecutionLimit(idleTaskExecutionLimit);
1057                }
1058                if (maxConcurrentConsumers >= 0) {
1059                    listenerContainer.setMaxConcurrentConsumers(maxConcurrentConsumers);
1060                }
1061                if (maxMessagesPerTask >= 0) {
1062                    listenerContainer.setMaxMessagesPerTask(maxMessagesPerTask);
1063                }
1064                listenerContainer.setPubSubNoLocal(pubSubNoLocal);
1065                if (receiveTimeout >= 0) {
1066                    listenerContainer.setReceiveTimeout(receiveTimeout);
1067                }
1068                if (recoveryInterval >= 0) {
1069                    listenerContainer.setRecoveryInterval(recoveryInterval);
1070                }
1071                if (taskExecutor != null) {
1072                    listenerContainer.setTaskExecutor(taskExecutor);
1073                }
1074                PlatformTransactionManager tm = getTransactionManager();
1075                if (tm != null && transacted) {
1076                    listenerContainer.setTransactionManager(tm);
1077                } else if (transacted) {
1078                    throw new IllegalArgumentException("Property transacted is enabled but a transactionManager was not injected!");
1079                }
1080                if (transactionName != null) {
1081                    listenerContainer.setTransactionName(transactionName);
1082                }
1083                if (transactionTimeout >= 0) {
1084                    listenerContainer.setTransactionTimeout(transactionTimeout);
1085                }
1086            } else if (container instanceof SimpleMessageListenerContainer) {
1087                // this includes SimpleMessageListenerContainer102
1088                SimpleMessageListenerContainer listenerContainer = (SimpleMessageListenerContainer) container;
1089                if (concurrentConsumers >= 0) {
1090                    listenerContainer.setConcurrentConsumers(concurrentConsumers);
1091                }
1092                listenerContainer.setPubSubNoLocal(pubSubNoLocal);
1093                if (taskExecutor != null) {
1094                    listenerContainer.setTaskExecutor(taskExecutor);
1095                }
1096            }
1097        }
1098    
1099        public void configure(EndpointMessageListener listener) {
1100            if (isDisableReplyTo()) {
1101                listener.setDisableReplyTo(true);
1102            }
1103            if (isEagerLoadingOfProperties()) {
1104                listener.setEagerLoadingOfProperties(true);
1105            }
1106            if (getReplyTo() != null) {
1107                listener.setReplyToDestination(getReplyTo());
1108            }
1109    
1110            // TODO: REVISIT: We really ought to change the model and let JmsProducer
1111            // and JmsConsumer have their own JmsConfiguration instance
1112            // This way producer's and consumer's QoS can differ and be
1113            // independently configured
1114            JmsOperations operations = listener.getTemplate();
1115            if (operations instanceof JmsTemplate) {
1116                JmsTemplate template = (JmsTemplate) operations;
1117                template.setDeliveryPersistent(isReplyToDeliveryPersistent());
1118            }
1119        }
1120    
1121        public AbstractMessageListenerContainer chooseMessageListenerContainerImplementation() {
1122            // TODO we could allow a spring container to auto-inject these objects?
1123            switch (consumerType) {
1124            case Simple:
1125                return isUseVersion102()
1126                    ? new SimpleMessageListenerContainer102() : new SimpleMessageListenerContainer();
1127            case Default:
1128                return isUseVersion102()
1129                    ? new DefaultMessageListenerContainer102() : new DefaultMessageListenerContainer();
1130            default:
1131                throw new IllegalArgumentException("Unknown consumer type: " + consumerType);
1132            }
1133        }
1134    
1135        /**
1136         * Defaults the JMS cache level if none is explicitly specified. Note that
1137         * due to this <a
1138         * href="http://opensource.atlassian.com/projects/spring/browse/SPR-3890">Spring
1139         * Bug</a> we cannot use CACHE_CONSUMER by default (which we should do as
1140         * its most efficient) unless the spring version is 2.5.1 or later. Instead
1141         * we use CACHE_CONNECTION - part from for non-durable topics which must use
1142         * CACHE_CONSUMER to avoid missing messages (due to the consumer being
1143         * created and destroyed per message).
1144         *
1145         * @param endpoint the endpoint
1146         * @return the cacne level
1147         */
1148        protected int defaultCacheLevel(JmsEndpoint endpoint) {
1149            // if we are on a new enough spring version we can assume CACHE_CONSUMER
1150            if (PackageHelper.isValidVersion("org.springframework.jms", 2.51D)) {
1151                return DefaultMessageListenerContainer.CACHE_CONSUMER;
1152            } else {
1153                if (endpoint.isPubSubDomain() && !isSubscriptionDurable()) {
1154                    // we must cache the consumer or we will miss messages
1155                    // see https://issues.apache.org/activemq/browse/CAMEL-253
1156                    return DefaultMessageListenerContainer.CACHE_CONSUMER;
1157                } else {
1158                    // to enable consuming and sending with a single JMS session (to
1159                    // avoid XA) we can only use CACHE_CONNECTION
1160                    // due to this bug :
1161                    // http://opensource.atlassian.com/projects/spring/browse/SPR-3890
1162                    return DefaultMessageListenerContainer.CACHE_CONNECTION;
1163                }
1164            }
1165        }
1166    
1167        /**
1168         * Factory method which allows derived classes to customize the lazy
1169         * creation
1170         */
1171        protected ConnectionFactory createConnectionFactory() {
1172            ObjectHelper.notNull(connectionFactory, "connectionFactory");
1173            return null;
1174        }
1175    
1176        /**
1177         * Factory method which allows derived classes to customize the lazy
1178         * creation
1179         */
1180        protected ConnectionFactory createListenerConnectionFactory() {
1181            return getConnectionFactory();
1182        }
1183    
1184        /**
1185         * Factory method which allows derived classes to customize the lazy
1186         * creation
1187         */
1188        protected ConnectionFactory createTemplateConnectionFactory() {
1189            return getConnectionFactory();
1190        }
1191    
1192        /**
1193         * Factory method which which allows derived classes to customize the lazy
1194         * transcationManager creation
1195         */
1196        protected PlatformTransactionManager createTransactionManager() {
1197            JmsTransactionManager answer = new JmsTransactionManager();
1198            answer.setConnectionFactory(getConnectionFactory());
1199            return answer;
1200        }
1201    
1202        public boolean isPreserveMessageQos() {
1203            return preserveMessageQos;
1204        }
1205    
1206        /**
1207         * When one of the QoS properties are configured such as {@link #setDeliveryPersistent(boolean)},
1208         * {@link #setPriority(int)} or {@link #setTimeToLive(long)} then we should auto default the
1209         * setting of {@link #setExplicitQosEnabled(boolean)} if its not been configured yet
1210         */
1211        protected void configuredQoS() {
1212            if (explicitQosEnabled == null) {
1213                explicitQosEnabled = true;
1214            }
1215        }
1216    
1217    
1218        public boolean isAlwaysCopyMessage() {
1219            return alwaysCopyMessage;
1220        }
1221    
1222        public void setAlwaysCopyMessage(boolean alwaysCopyMessage) {
1223            this.alwaysCopyMessage = alwaysCopyMessage;
1224        }
1225    
1226        public boolean isUseMessageIDAsCorrelationID() {
1227            return useMessageIDAsCorrelationID;
1228        }
1229    
1230        public void setUseMessageIDAsCorrelationID(boolean useMessageIDAsCorrelationID) {
1231            this.useMessageIDAsCorrelationID = useMessageIDAsCorrelationID;
1232        }
1233    
1234        public String getReplyToTempDestinationAffinity() {
1235            return replyToTempDestinationAffinity;
1236        }
1237    
1238        public void setReplyToTempDestinationAffinity(String replyToTempDestinationAffinity) {
1239            this.replyToTempDestinationAffinity = replyToTempDestinationAffinity;
1240        }
1241    
1242        public long getRequestTimeout() {
1243            return requestTimeout;
1244        }
1245    
1246        /**
1247         * Sets the timeout in milliseconds which requests should timeout after
1248         */
1249        public void setRequestTimeout(long requestTimeout) {
1250            this.requestTimeout = requestTimeout;
1251        }
1252    
1253        public String getReplyTo() {
1254            return replyToDestination;
1255        }
1256    
1257        public void setReplyTo(String replyToDestination) {
1258            if (replyToDestination.startsWith(QUEUE_PREFIX)) {
1259                this.replyToDestination = removeStartingCharacters(replyToDestination.substring(QUEUE_PREFIX.length()), '/');
1260            } else if (replyToDestination.startsWith(TOPIC_PREFIX)) {
1261                this.replyToDestination = removeStartingCharacters(replyToDestination.substring(TOPIC_PREFIX.length()), '/');
1262            } else {
1263                this.replyToDestination = replyToDestination;
1264            }
1265        }
1266    
1267        public String getReplyToDestinationSelectorName() {
1268            return replyToDestinationSelectorName;
1269        }
1270    
1271        public void setReplyToDestinationSelectorName(String replyToDestinationSelectorName) {
1272            this.replyToDestinationSelectorName = replyToDestinationSelectorName;
1273            // in case of consumer -> producer and a named replyTo correlation selector
1274            // message pass through is impossible as we need to set the value of selector into
1275            // outgoing message, which would be read-only if pass through were to remain enabled
1276            if (replyToDestinationSelectorName != null) {
1277                setAlwaysCopyMessage(true);
1278            }
1279        }
1280    
1281        public JmsMessageType getJmsMessageType() {
1282            return jmsMessageType;
1283        }
1284    
1285        public void setJmsMessageType(JmsMessageType jmsMessageType) {
1286            this.jmsMessageType = jmsMessageType;
1287        }
1288    
1289        public JmsKeyFormatStrategy getJmsKeyFormatStrategy() {
1290            if (jmsKeyFormatStrategy == null) {
1291                jmsKeyFormatStrategy = new DefaultJmsKeyFormatStrategy();
1292            }
1293            return jmsKeyFormatStrategy;
1294        }
1295    
1296        public void setJmsKeyFormatStrategy(JmsKeyFormatStrategy jmsKeyFormatStrategy) {
1297            this.jmsKeyFormatStrategy = jmsKeyFormatStrategy;
1298        }
1299    
1300        public boolean isTransferExchange() {
1301            return transferExchange;
1302        }
1303    
1304        public void setTransferExchange(boolean transferExchange) {
1305            this.transferExchange = transferExchange;
1306        }
1307    
1308        public boolean isTransferException() {
1309            return transferException;
1310        }
1311    
1312        public void setTransferException(boolean transferException) {
1313            this.transferException = transferException;
1314        }
1315    
1316        public boolean isTestConnectionOnStartup() {
1317            return testConnectionOnStartup;
1318        }
1319    
1320        public void setTestConnectionOnStartup(boolean testConnectionOnStartup) {
1321            this.testConnectionOnStartup = testConnectionOnStartup;
1322        }
1323    }