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.requestor;
018    
019    import java.math.BigInteger;
020    import java.util.Random;
021    import java.util.concurrent.ScheduledExecutorService;
022    
023    import javax.jms.Destination;
024    import javax.jms.ExceptionListener;
025    import javax.jms.JMSException;
026    import javax.jms.Message;
027    import javax.jms.Session;
028    
029    import org.apache.camel.component.jms.JmsConfiguration;
030    import org.apache.camel.component.jms.requestor.DeferredRequestReplyMap.DeferredMessageSentCallback;
031    import org.springframework.core.task.TaskExecutor;
032    import org.springframework.jms.listener.AbstractMessageListenerContainer;
033    import org.springframework.jms.listener.DefaultMessageListenerContainer;
034    import org.springframework.jms.listener.DefaultMessageListenerContainer102;
035    import org.springframework.jms.support.destination.DestinationResolver;
036    import org.springframework.transaction.PlatformTransactionManager;
037    
038    public class PersistentReplyToRequestor extends Requestor {
039        private String replyToSelectorValue;
040    
041        public class DestinationResolverDelegate implements DestinationResolver {
042            private DestinationResolver delegate;
043            private Destination destination;
044    
045            public DestinationResolverDelegate(DestinationResolver delegate) {
046                this.delegate = delegate;
047            }
048    
049            public Destination resolveDestinationName(Session session, String destinationName,
050                                                      boolean pubSubDomain) throws JMSException {
051                synchronized (getOutterInstance()) {
052                    try {
053                        if (destination == null) {
054                            destination = delegate.resolveDestinationName(session, destinationName, pubSubDomain);
055                            setReplyTo(destination);
056                        }
057                    } finally {
058                        getOutterInstance().notifyAll();
059                    }
060                }
061                return destination;
062            }
063        };
064    
065        public static interface MessageSelectorComposer {
066            void addCorrelationID(String id);
067            void removeCorrelationID(String id);
068        }
069    
070        public static class CamelDefaultMessageListenerContainer102 extends DefaultMessageListenerContainer102
071                                                                    implements MessageSelectorComposer {
072            MessageSelectorProvider provider = new MessageSelectorProvider();
073    
074            public void addCorrelationID(String id) {
075                provider.addCorrelationID(id);
076            }
077    
078            public void removeCorrelationID(String id) {
079                provider.removeCorrelationID(id);
080            }
081    
082            @Override
083            public void setMessageSelector(String messageSelector) {
084                throw new UnsupportedOperationException();
085            }
086    
087            @Override
088            public String getMessageSelector() {
089                return provider.get();
090            }
091        }
092    
093        public static class CamelDefaultMessageListenerContainer extends DefaultMessageListenerContainer
094                                                                 implements MessageSelectorComposer {
095    
096            MessageSelectorProvider provider = new MessageSelectorProvider();
097    
098            public void addCorrelationID(String id) {
099                provider.addCorrelationID(id);
100            }
101    
102            public void removeCorrelationID(String id) {
103                provider.removeCorrelationID(id);
104            }
105    
106            @Override
107            public void setMessageSelector(String messageSelector) {
108                throw new UnsupportedOperationException();
109            }
110    
111            @Override
112            public String getMessageSelector() {
113                return provider.get();
114            }
115        }
116    
117        public PersistentReplyToRequestor(JmsConfiguration configuration,
118                                          ScheduledExecutorService executorService) {
119            super(configuration, executorService);
120        }
121    
122    
123        @Override
124        protected FutureHandler createFutureHandler(String correlationID) {
125            boolean dynamicSelector = getConfiguration().getReplyToDestinationSelectorName() == null;
126            if (dynamicSelector) {
127                return new PersistentReplyToFutureHandler(this, correlationID);
128            }
129            return new FutureHandler();
130        }
131    
132        @Override
133        protected FutureHandler createFutureHandler(DeferredMessageSentCallback callback) {
134            boolean dynamicSelector = getConfiguration().getReplyToDestinationSelectorName() == null;
135            if (dynamicSelector) {
136                return new PersistentReplyToFutureHandler(this, callback);
137            }
138            return new FutureHandler();
139        }
140    
141        @Override
142        public AbstractMessageListenerContainer createListenerContainer() {
143            JmsConfiguration config = getConfiguration();
144            String replyToSelectorName = getConfiguration().getReplyToDestinationSelectorName();
145    
146            DefaultMessageListenerContainer container =
147                config.isUseVersion102()
148                        ? (replyToSelectorName != null) ? new DefaultMessageListenerContainer102()
149                               : new CamelDefaultMessageListenerContainer102()
150                        : (replyToSelectorName != null) ? new DefaultMessageListenerContainer()
151                               : new CamelDefaultMessageListenerContainer();
152    
153            container.setConnectionFactory(config.getListenerConnectionFactory());
154    
155            DestinationResolver resolver = config.getDestinationResolver();
156            if (resolver == null) {
157                resolver = container.getDestinationResolver();
158            }
159    
160            container.setDestinationResolver(new DestinationResolverDelegate(resolver));
161            container.setDestinationName(getConfiguration().getReplyTo());
162    
163            if (replyToSelectorName != null) {
164                replyToSelectorValue = "ID:" + new BigInteger(24 * 8, new Random()).toString(16);
165                container.setMessageSelector(replyToSelectorName + "='" + replyToSelectorValue + "'");
166            } else {
167                ((MessageSelectorComposer)container).addCorrelationID("ID:" + new BigInteger(24 * 8, new Random()).toString(16));
168            }
169    
170            container.setAutoStartup(true);
171            container.setMessageListener(this);
172            container.setPubSubDomain(false);
173            container.setSubscriptionDurable(false);
174    
175            ExceptionListener exceptionListener = config.getExceptionListener();
176            if (exceptionListener != null) {
177                container.setExceptionListener(exceptionListener);
178            }
179    
180            container.setSessionTransacted(config.isTransacted());
181            if (config.isTransacted()) {
182                container.setSessionAcknowledgeMode(Session.SESSION_TRANSACTED);
183            } else {
184                if (config.getAcknowledgementMode() >= 0) {
185                    container.setSessionAcknowledgeMode(config.getAcknowledgementMode());
186                } else if (config.getAcknowledgementModeName() != null) {
187                    container.setSessionAcknowledgeModeName(config.getAcknowledgementModeName());
188                }
189            }
190    
191            container.setConcurrentConsumers(1);
192            container.setCacheLevel(DefaultMessageListenerContainer.CACHE_SESSION);
193    
194            if (config.getReceiveTimeout() >= 0) {
195                container.setReceiveTimeout(config.getReceiveTimeout());
196            }
197            if (config.getRecoveryInterval() >= 0) {
198                container.setRecoveryInterval(config.getRecoveryInterval());
199            }
200            TaskExecutor taskExecutor = config.getTaskExecutor();
201            if (taskExecutor != null) {
202                container.setTaskExecutor(taskExecutor);
203            }
204            PlatformTransactionManager tm = config.getTransactionManager();
205            if (tm != null) {
206                container.setTransactionManager(tm);
207            } else if (config.isTransacted()) {
208                throw new IllegalArgumentException("Property transacted is enabled but a transactionManager was not injected!");
209            }
210            if (config.getTransactionName() != null) {
211                container.setTransactionName(config.getTransactionName());
212            }
213            if (config.getTransactionTimeout() >= 0) {
214                container.setTransactionTimeout(config.getTransactionTimeout());
215            }
216    
217            return container;
218        }
219    
220        @Override
221        public void setReplyToSelectorHeader(org.apache.camel.Message in, Message jmsIn) throws JMSException {
222            String replyToSelectorName = getConfiguration().getReplyToDestinationSelectorName();
223            if (replyToSelectorValue != null) {
224                in.setHeader(replyToSelectorName, replyToSelectorValue);
225                jmsIn.setStringProperty(replyToSelectorName, replyToSelectorValue);
226            }
227        }
228    }