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 }