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 java.util.Map;
020    import java.util.concurrent.ScheduledExecutorService;
021    import javax.jms.ConnectionFactory;
022    import javax.jms.ExceptionListener;
023    import javax.jms.Session;
024    
025    import org.apache.camel.CamelContext;
026    import org.apache.camel.Endpoint;
027    import org.apache.camel.component.jms.requestor.Requestor;
028    import org.apache.camel.impl.DefaultComponent;
029    import org.apache.camel.spi.HeaderFilterStrategy;
030    import org.apache.camel.spi.HeaderFilterStrategyAware;
031    import org.apache.camel.util.CastUtils;
032    import org.apache.camel.util.EndpointHelper;
033    import org.apache.camel.util.ObjectHelper;
034    import org.apache.commons.logging.Log;
035    import org.apache.commons.logging.LogFactory;
036    import org.springframework.beans.BeansException;
037    import org.springframework.context.ApplicationContext;
038    import org.springframework.context.ApplicationContextAware;
039    import org.springframework.core.task.TaskExecutor;
040    import org.springframework.jms.connection.JmsTransactionManager;
041    import org.springframework.jms.connection.UserCredentialsConnectionFactoryAdapter;
042    import org.springframework.jms.core.JmsOperations;
043    import org.springframework.jms.support.converter.MessageConverter;
044    import org.springframework.jms.support.destination.DestinationResolver;
045    import org.springframework.transaction.PlatformTransactionManager;
046    
047    import static org.apache.camel.util.ObjectHelper.removeStartingCharacters;
048    
049    /**
050     * A <a href="http://activemq.apache.org/jms.html">JMS Component</a>
051     *
052     * @version $Revision:520964 $
053     */
054    public class JmsComponent extends DefaultComponent implements ApplicationContextAware, HeaderFilterStrategyAware {
055    
056        private static final transient Log LOG = LogFactory.getLog(JmsComponent.class);
057        private static final int DEFAULT_THREADPOOL_SIZE = 100;
058        private static final String DEFAULT_QUEUE_BROWSE_STRATEGY = "org.apache.camel.component.jms.DefaultQueueBrowseStrategy";
059        private static final String KEY_FORMAT_STRATEGY_PARAM = "jmsKeyFormatStrategy";
060        private ScheduledExecutorService scheduledExecutorService;
061        private JmsConfiguration configuration;
062        private ApplicationContext applicationContext;
063        private Requestor requestor;
064        private QueueBrowseStrategy queueBrowseStrategy;
065        private boolean attemptedToCreateQueueBrowserStrategy;
066        private HeaderFilterStrategy headerFilterStrategy = new JmsHeaderFilterStrategy();
067    
068        public JmsComponent() {
069        }
070    
071        public JmsComponent(CamelContext context) {
072            super(context);
073        }
074    
075        public JmsComponent(JmsConfiguration configuration) {
076            this.configuration = configuration;
077        }
078    
079        /**
080         * Static builder method
081         */
082        public static JmsComponent jmsComponent() {
083            return new JmsComponent();
084        }
085    
086        /**
087         * Static builder method
088         */
089        public static JmsComponent jmsComponent(JmsConfiguration configuration) {
090            return new JmsComponent(configuration);
091        }
092    
093        /**
094         * Static builder method
095         */
096        public static JmsComponent jmsComponent(ConnectionFactory connectionFactory) {
097            return jmsComponent(new JmsConfiguration(connectionFactory));
098        }
099    
100        /**
101         * Static builder method
102         */
103        public static JmsComponent jmsComponentClientAcknowledge(ConnectionFactory connectionFactory) {
104            JmsConfiguration template = new JmsConfiguration(connectionFactory);
105            template.setAcknowledgementMode(Session.CLIENT_ACKNOWLEDGE);
106            return jmsComponent(template);
107        }
108    
109        /**
110         * Static builder method
111         */
112        public static JmsComponent jmsComponentAutoAcknowledge(ConnectionFactory connectionFactory) {
113            JmsConfiguration template = new JmsConfiguration(connectionFactory);
114            template.setAcknowledgementMode(Session.AUTO_ACKNOWLEDGE);
115            return jmsComponent(template);
116        }
117    
118        public static JmsComponent jmsComponentTransacted(ConnectionFactory connectionFactory) {
119            JmsTransactionManager transactionManager = new JmsTransactionManager();
120            transactionManager.setConnectionFactory(connectionFactory);
121            return jmsComponentTransacted(connectionFactory, transactionManager);
122        }
123    
124        public static JmsComponent jmsComponentTransacted(ConnectionFactory connectionFactory,
125                                                          PlatformTransactionManager transactionManager) {
126            JmsConfiguration template = new JmsConfiguration(connectionFactory);
127            template.setTransactionManager(transactionManager);
128            template.setTransacted(true);
129            template.setTransactedInOut(true);
130            return jmsComponent(template);
131        }
132    
133        // Properties
134        // -------------------------------------------------------------------------
135    
136        public JmsConfiguration getConfiguration() {
137            if (configuration == null) {
138                configuration = createConfiguration();
139    
140                // If we are being configured with spring...
141                if (applicationContext != null) {
142                    Map<String, Object> beansOfType = 
143                        CastUtils.cast(applicationContext.getBeansOfType(ConnectionFactory.class));
144                    if (!beansOfType.isEmpty()) {
145                        ConnectionFactory cf = (ConnectionFactory)beansOfType.values().iterator().next();
146                        configuration.setConnectionFactory(cf);
147                    }
148                    beansOfType = CastUtils.cast(applicationContext.getBeansOfType(DestinationResolver.class));
149                    if (!beansOfType.isEmpty()) {
150                        DestinationResolver destinationResolver = (DestinationResolver)beansOfType.values()
151                            .iterator().next();
152                        configuration.setDestinationResolver(destinationResolver);
153                    }
154                }
155            }
156            return configuration;
157        }
158    
159        /**
160         * Sets the JMS configuration
161         *
162         * @param configuration the configuration to use by default for endpoints
163         */
164        public void setConfiguration(JmsConfiguration configuration) {
165            this.configuration = configuration;
166        }
167    
168        public void setAcceptMessagesWhileStopping(boolean acceptMessagesWhileStopping) {
169            getConfiguration().setAcceptMessagesWhileStopping(acceptMessagesWhileStopping);
170        }
171    
172        public void setAcknowledgementMode(int consumerAcknowledgementMode) {
173            getConfiguration().setAcknowledgementMode(consumerAcknowledgementMode);
174        }
175    
176        public void setEagerLoadingOfProperties(boolean eagerLoadingOfProperties) {
177            getConfiguration().setEagerLoadingOfProperties(eagerLoadingOfProperties);
178        }
179    
180        public void setAcknowledgementModeName(String consumerAcknowledgementMode) {
181            getConfiguration().setAcknowledgementModeName(consumerAcknowledgementMode);
182        }
183    
184        public void setAutoStartup(boolean autoStartup) {
185            getConfiguration().setAutoStartup(autoStartup);
186        }
187    
188        public void setCacheLevel(int cacheLevel) {
189            getConfiguration().setCacheLevel(cacheLevel);
190        }
191    
192        public void setCacheLevelName(String cacheName) {
193            getConfiguration().setCacheLevelName(cacheName);
194        }
195    
196        public void setClientId(String consumerClientId) {
197            getConfiguration().setClientId(consumerClientId);
198        }
199    
200        public void setConcurrentConsumers(int concurrentConsumers) {
201            getConfiguration().setConcurrentConsumers(concurrentConsumers);
202        }
203    
204        public void setConnectionFactory(ConnectionFactory connectionFactory) {
205            getConfiguration().setConnectionFactory(connectionFactory);
206        }
207    
208        public void setConsumerType(ConsumerType consumerType) {
209            getConfiguration().setConsumerType(consumerType);
210        }
211    
212        public void setDeliveryPersistent(boolean deliveryPersistent) {
213            getConfiguration().setDeliveryPersistent(deliveryPersistent);
214        }
215    
216        public void setDurableSubscriptionName(String durableSubscriptionName) {
217            getConfiguration().setDurableSubscriptionName(durableSubscriptionName);
218        }
219    
220        public void setExceptionListener(ExceptionListener exceptionListener) {
221            getConfiguration().setExceptionListener(exceptionListener);
222        }
223    
224        public void setExplicitQosEnabled(boolean explicitQosEnabled) {
225            getConfiguration().setExplicitQosEnabled(explicitQosEnabled);
226        }
227    
228        public void setExposeListenerSession(boolean exposeListenerSession) {
229            getConfiguration().setExposeListenerSession(exposeListenerSession);
230        }
231    
232        public void setIdleTaskExecutionLimit(int idleTaskExecutionLimit) {
233            getConfiguration().setIdleTaskExecutionLimit(idleTaskExecutionLimit);
234        }
235    
236        public void setMaxConcurrentConsumers(int maxConcurrentConsumers) {
237            getConfiguration().setMaxConcurrentConsumers(maxConcurrentConsumers);
238        }
239    
240        public void setMaxMessagesPerTask(int maxMessagesPerTask) {
241            getConfiguration().setMaxMessagesPerTask(maxMessagesPerTask);
242        }
243    
244        public void setMessageConverter(MessageConverter messageConverter) {
245            getConfiguration().setMessageConverter(messageConverter);
246        }
247    
248        public void setMapJmsMessage(boolean mapJmsMessage) {
249            getConfiguration().setMapJmsMessage(mapJmsMessage);
250        }
251    
252        public void setMessageIdEnabled(boolean messageIdEnabled) {
253            getConfiguration().setMessageIdEnabled(messageIdEnabled);
254        }
255    
256        public void setMessageTimestampEnabled(boolean messageTimestampEnabled) {
257            getConfiguration().setMessageTimestampEnabled(messageTimestampEnabled);
258        }
259    
260        public void setAlwaysCopyMessage(boolean alwaysCopyMessage) {
261            getConfiguration().setAlwaysCopyMessage(alwaysCopyMessage);
262        }
263    
264        public void setUseMessageIDAsCorrelationID(boolean useMessageIDAsCorrelationID) {
265            getConfiguration().setUseMessageIDAsCorrelationID(useMessageIDAsCorrelationID);
266        }
267    
268        public void setPriority(int priority) {
269            getConfiguration().setPriority(priority);
270        }
271    
272        public void setPubSubNoLocal(boolean pubSubNoLocal) {
273            getConfiguration().setPubSubNoLocal(pubSubNoLocal);
274        }
275    
276        public void setReceiveTimeout(long receiveTimeout) {
277            getConfiguration().setReceiveTimeout(receiveTimeout);
278        }
279    
280        public void setRecoveryInterval(long recoveryInterval) {
281            getConfiguration().setRecoveryInterval(recoveryInterval);
282        }
283    
284        public void setSubscriptionDurable(boolean subscriptionDurable) {
285            getConfiguration().setSubscriptionDurable(subscriptionDurable);
286        }
287    
288        public void setTaskExecutor(TaskExecutor taskExecutor) {
289            getConfiguration().setTaskExecutor(taskExecutor);
290        }
291    
292        public void setTimeToLive(long timeToLive) {
293            getConfiguration().setTimeToLive(timeToLive);
294        }
295    
296        public void setTransacted(boolean consumerTransacted) {
297            getConfiguration().setTransacted(consumerTransacted);
298        }
299    
300        public void setTransactionManager(PlatformTransactionManager transactionManager) {
301            getConfiguration().setTransactionManager(transactionManager);
302        }
303    
304        public void setTransactionName(String transactionName) {
305            getConfiguration().setTransactionName(transactionName);
306        }
307    
308        public void setTransactionTimeout(int transactionTimeout) {
309            getConfiguration().setTransactionTimeout(transactionTimeout);
310        }
311    
312        public void setTestConnectionOnStartup(boolean testConnectionOnStartup) {
313            getConfiguration().setTestConnectionOnStartup(testConnectionOnStartup);
314        }
315    
316        public void setRequestTimeout(long requestTimeout) {
317            getConfiguration().setRequestTimeout(requestTimeout);
318        }
319    
320        public void setTransferExchange(boolean transferExchange) {
321            getConfiguration().setTransferExchange(transferExchange);
322        }
323    
324        public void setTransferException(boolean transferException) {
325            getConfiguration().setTransferException(transferException);
326        }
327    
328        /**
329         * @deprecated will be removed in Camel 2.2
330         */
331        @Deprecated
332        public void setUseVersion102(boolean useVersion102) {
333            getConfiguration().setUseVersion102(useVersion102);
334        }
335    
336        public void setJmsOperations(JmsOperations jmsOperations) {
337            getConfiguration().setJmsOperations(jmsOperations);
338        }
339    
340        public void setDestinationResolver(DestinationResolver destinationResolver) {
341            getConfiguration().setDestinationResolver(destinationResolver);
342        }
343    
344        public synchronized Requestor getRequestor() throws Exception {
345            if (requestor == null) {
346                requestor = new Requestor(getConfiguration(), getScheduledExecutorService());
347                requestor.start();
348            }
349            return requestor;
350        }
351    
352        public void setRequestor(Requestor requestor) {
353            this.requestor = requestor;
354        }
355    
356        public synchronized ScheduledExecutorService getScheduledExecutorService() {
357            if (scheduledExecutorService == null) {
358                scheduledExecutorService = getCamelContext().getExecutorServiceStrategy()
359                        .newScheduledThreadPool(this, "JmsComponent", DEFAULT_THREADPOOL_SIZE);
360            }
361            return scheduledExecutorService;
362        }
363    
364        public void setScheduledExecutorService(ScheduledExecutorService scheduledExecutorService) {
365            this.scheduledExecutorService = scheduledExecutorService;
366        }
367    
368        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
369            this.applicationContext = applicationContext;
370        }
371    
372        public QueueBrowseStrategy getQueueBrowseStrategy() {
373            if (queueBrowseStrategy == null) {
374                if (!attemptedToCreateQueueBrowserStrategy) {
375                    attemptedToCreateQueueBrowserStrategy = true;
376                    try {
377                        queueBrowseStrategy = tryCreateDefaultQueueBrowseStrategy(getCamelContext());
378                    } catch (Throwable e) {
379                        LOG.warn("Could not instantiate the QueueBrowseStrategy are you using Spring 2.0.x"
380                            + " by any chance? Error: " + e, e);
381                    }
382                }
383            }
384            return queueBrowseStrategy;
385        }
386    
387        public void setQueueBrowseStrategy(QueueBrowseStrategy queueBrowseStrategy) {
388            this.queueBrowseStrategy = queueBrowseStrategy;
389        }
390    
391        public HeaderFilterStrategy getHeaderFilterStrategy() {
392            return headerFilterStrategy;
393        }
394    
395        public void setHeaderFilterStrategy(HeaderFilterStrategy strategy) {
396            this.headerFilterStrategy = strategy;
397        }
398    
399        // Implementation methods
400        // -------------------------------------------------------------------------
401    
402        @Override
403        protected void doStop() throws Exception {
404            if (requestor != null) {
405                requestor.stop();
406            }
407            super.doStop();
408        }
409    
410        @Override
411        protected Endpoint createEndpoint(String uri, String remaining, Map<String, Object> parameters)
412            throws Exception {
413    
414            boolean pubSubDomain = false;
415            boolean tempDestination = false;
416            if (remaining.startsWith(JmsConfiguration.QUEUE_PREFIX)) {
417                pubSubDomain = false;
418                remaining = removeStartingCharacters(remaining.substring(JmsConfiguration.QUEUE_PREFIX.length()), '/');
419            } else if (remaining.startsWith(JmsConfiguration.TOPIC_PREFIX)) {
420                pubSubDomain = true;
421                remaining = removeStartingCharacters(remaining.substring(JmsConfiguration.TOPIC_PREFIX.length()), '/');
422            } else if (remaining.startsWith(JmsConfiguration.TEMP_QUEUE_PREFIX)) {
423                pubSubDomain = false;
424                tempDestination = true;
425                remaining = removeStartingCharacters(remaining.substring(JmsConfiguration.TEMP_QUEUE_PREFIX.length()), '/');
426            } else if (remaining.startsWith(JmsConfiguration.TEMP_TOPIC_PREFIX)) {
427                pubSubDomain = true;
428                tempDestination = true;
429                remaining = removeStartingCharacters(remaining.substring(JmsConfiguration.TEMP_TOPIC_PREFIX.length()), '/');
430            }
431    
432            final String subject = convertPathToActualDestination(remaining, parameters);
433    
434            // lets make sure we copy the configuration as each endpoint can
435            // customize its own version
436            JmsConfiguration newConfiguration = getConfiguration().copy();        
437            JmsEndpoint endpoint;
438            if (pubSubDomain) {
439                if (tempDestination) {
440                    endpoint = new JmsTemporaryTopicEndpoint(uri, this, subject, newConfiguration);
441                } else {
442                    endpoint = new JmsEndpoint(uri, this, subject, pubSubDomain, newConfiguration);
443                }
444            } else {
445                QueueBrowseStrategy strategy = getQueueBrowseStrategy();
446                if (tempDestination) {
447                    endpoint = new JmsTemporaryQueueEndpoint(uri, this, subject, newConfiguration, strategy);
448                } else {
449                    endpoint = new JmsQueueEndpoint(uri, this, subject, newConfiguration, strategy);
450                }
451            }
452    
453            String selector = getAndRemoveParameter(parameters, "selector", String.class);
454            if (selector != null) {
455                endpoint.setSelector(selector);
456            }
457            String username = getAndRemoveParameter(parameters, "username", String.class);
458            String password = getAndRemoveParameter(parameters, "password", String.class);
459            if (username != null && password != null) {
460                ConnectionFactory cf = endpoint.getConfiguration().getConnectionFactory();
461                UserCredentialsConnectionFactoryAdapter ucfa = new UserCredentialsConnectionFactoryAdapter();
462                ucfa.setTargetConnectionFactory(cf);
463                ucfa.setPassword(password);
464                ucfa.setUsername(username);
465                endpoint.getConfiguration().setConnectionFactory(ucfa);
466            } else {
467                if (username != null || password != null) {
468                    // exclude the the saturation of username and password are all empty
469                    throw new IllegalArgumentException("The JmsComponent's username or password is null");
470                }
471            }
472    
473            // jms header strategy
474            String strategyVal = getAndRemoveParameter(parameters, KEY_FORMAT_STRATEGY_PARAM, String.class);
475            if ("default".equalsIgnoreCase(strategyVal)) {
476                endpoint.setJmsKeyFormatStrategy(new DefaultJmsKeyFormatStrategy());
477            } else if ("passthrough".equalsIgnoreCase(strategyVal)) {
478                endpoint.setJmsKeyFormatStrategy(new PassThroughJmsKeyFormatStrategy());
479            } else { // a reference
480                parameters.put(KEY_FORMAT_STRATEGY_PARAM, strategyVal);
481                endpoint.setJmsKeyFormatStrategy(resolveAndRemoveReferenceParameter(
482                        parameters, KEY_FORMAT_STRATEGY_PARAM, JmsKeyFormatStrategy.class));
483            }
484            
485            setProperties(endpoint.getConfiguration(), parameters);
486            endpoint.setHeaderFilterStrategy(getHeaderFilterStrategy());
487    
488            return endpoint;
489        }   
490    
491        /**
492         * A strategy method allowing the URI destination to be translated into the
493         * actual JMS destination name (say by looking up in JNDI or something)
494         */
495        protected String convertPathToActualDestination(String path, Map<String, Object> parameters) {
496            return path;
497        }
498    
499        /**
500         * Factory method to create the default configuration instance
501         *
502         * @return a newly created configuration object which can then be further
503         *         customized
504         */
505        protected JmsConfiguration createConfiguration() {
506            return new JmsConfiguration();
507        }
508    
509        /**
510         * Attempts to instantiate the default {@link QueueBrowseStrategy} which
511         * should work fine if Spring 2.5.x or later is on the classpath but this
512         * will fail if 2.0.x are on the classpath. We can continue to operate on
513         * this version we just cannot support the browseable queues supported by
514         * {@link JmsQueueEndpoint}
515         *
516         * @return the queue browse strategy or null if it cannot be supported
517         */
518        protected static QueueBrowseStrategy tryCreateDefaultQueueBrowseStrategy(CamelContext context) {
519            // lets try instantiate the default implementation
520            // use the class loading this class from camel-jms to work in OSGi environments as the camel-jms
521            // should import the spring-jms jars.
522            if (JmsHelper.isSpring20x()) {
523                // not possible with spring 2.0.x
524                return null;
525            } else {
526                // lets try instantiate the default implementation
527                Class<?> type = ObjectHelper.loadClass(DEFAULT_QUEUE_BROWSE_STRATEGY, JmsComponent.class.getClassLoader());
528                if (type != null) {
529                    return ObjectHelper.newInstance(type, QueueBrowseStrategy.class);
530                } else {
531                    return null;
532                }
533            }
534        }
535    
536    }