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.util.HashMap;
020    import java.util.Map;
021    import java.util.concurrent.FutureTask;
022    import java.util.concurrent.ScheduledExecutorService;
023    
024    import javax.jms.Destination;
025    import javax.jms.ExceptionListener;
026    import javax.jms.JMSException;
027    import javax.jms.Message;
028    import javax.jms.MessageListener;
029    import javax.jms.Session;
030    import javax.jms.TemporaryQueue;
031    
032    import org.apache.camel.component.jms.JmsConfiguration;
033    import org.apache.camel.component.jms.JmsProducer;
034    import org.apache.camel.component.jms.requestor.DeferredRequestReplyMap.DeferredMessageSentCallback;
035    import org.apache.camel.impl.ServiceSupport;
036    import org.apache.camel.util.DefaultTimeoutMap;
037    import org.apache.camel.util.TimeoutMap;
038    import org.apache.camel.util.UuidGenerator;
039    import org.apache.commons.logging.Log;
040    import org.apache.commons.logging.LogFactory;
041    import org.springframework.core.task.TaskExecutor;
042    import org.springframework.jms.listener.AbstractMessageListenerContainer;
043    import org.springframework.jms.listener.SimpleMessageListenerContainer;
044    import org.springframework.jms.listener.SimpleMessageListenerContainer102;
045    import org.springframework.jms.support.destination.DestinationResolver;
046    
047    /**
048     * @version $Revision: 19241 $
049     */
050    public class Requestor extends ServiceSupport implements MessageListener {
051        private static final transient Log LOG = LogFactory.getLog(Requestor.class);
052        private static UuidGenerator uuidGenerator;
053        private final JmsConfiguration configuration;
054        private ScheduledExecutorService executorService;
055        private AbstractMessageListenerContainer listenerContainer;
056        private TimeoutMap<String, Object> requestMap;
057        private Map<JmsProducer, DeferredRequestReplyMap> producerDeferredRequestReplyMap;
058        private TimeoutMap<String, Object> deferredRequestMap;
059        private TimeoutMap<String, Object> deferredReplyMap;
060        private Destination replyTo;
061        private long maxRequestTimeout = -1;
062        private long replyToResolverTimeout = 5000;
063    
064    
065        public Requestor(JmsConfiguration configuration, ScheduledExecutorService executorService) {
066            this.configuration = configuration;
067            this.executorService = executorService;
068            requestMap = new DefaultTimeoutMap<String, Object>(executorService, configuration.getRequestMapPurgePollTimeMillis());
069            producerDeferredRequestReplyMap = new HashMap<JmsProducer, DeferredRequestReplyMap>();
070            deferredRequestMap = new DefaultTimeoutMap<String, Object>(executorService, configuration.getRequestMapPurgePollTimeMillis());
071            deferredReplyMap = new DefaultTimeoutMap<String, Object>(executorService, configuration.getRequestMapPurgePollTimeMillis());
072        }
073    
074        public synchronized DeferredRequestReplyMap getDeferredRequestReplyMap(JmsProducer producer) {
075            DeferredRequestReplyMap map = producerDeferredRequestReplyMap.get(producer);
076            if (map == null) {
077                map = new DeferredRequestReplyMap(this, producer, deferredRequestMap, deferredReplyMap);
078                producerDeferredRequestReplyMap.put(producer, map);
079                if (maxRequestTimeout == -1) {
080                    maxRequestTimeout = producer.getRequestTimeout();
081                } else if (maxRequestTimeout < producer.getRequestTimeout()) {
082                    maxRequestTimeout = producer.getRequestTimeout();
083                }
084            }
085            return map;
086        }
087    
088        public synchronized void removeDeferredRequestReplyMap(JmsProducer producer) {
089            DeferredRequestReplyMap map = producerDeferredRequestReplyMap.remove(producer);
090            if (map == null) {
091                // already removed;
092                return;
093            }
094            if (maxRequestTimeout == producer.getRequestTimeout()) {
095                long max = -1;
096                for (Map.Entry<JmsProducer, DeferredRequestReplyMap> entry : producerDeferredRequestReplyMap.entrySet()) {
097                    if (max < entry.getKey().getRequestTimeout()) {
098                        max = entry.getKey().getRequestTimeout();
099                    }
100                }
101                maxRequestTimeout = max;
102            }
103        }
104    
105        public synchronized long getMaxRequestTimeout() {
106            return maxRequestTimeout;
107        }
108    
109        public TimeoutMap getRequestMap() {
110            return requestMap;
111        }
112    
113        public TimeoutMap getDeferredRequestMap() {
114            return deferredRequestMap;
115        }
116    
117        public TimeoutMap getDeferredReplyMap() {
118            return deferredReplyMap;
119        }
120    
121        public FutureTask getReceiveFuture(String correlationID, long requestTimeout) {
122            FutureHandler future = createFutureHandler(correlationID);
123            requestMap.put(correlationID, future, requestTimeout);
124            return future;
125        }
126    
127        public FutureTask getReceiveFuture(DeferredMessageSentCallback callback) {
128            FutureHandler future = createFutureHandler(callback);
129            DeferredRequestReplyMap map = callback.getDeferredRequestReplyMap();
130            map.put(callback, future);
131            return future;
132        }
133    
134        protected FutureHandler createFutureHandler(String correlationID) {
135            return new FutureHandler();
136        }
137    
138        protected FutureHandler createFutureHandler(DeferredMessageSentCallback callback) {
139            return new FutureHandler();
140        }
141    
142        public void onMessage(Message message) {
143            try {
144                String correlationID = message.getJMSCorrelationID();
145                if (LOG.isDebugEnabled()) {
146                    LOG.debug("Message correlationID: " + correlationID);
147                }
148                if (correlationID == null) {
149                    LOG.warn("Ignoring message with no correlationID: " + message);
150                    return;
151                }
152                // lets notify the monitor for this response
153                Object handler = requestMap.get(correlationID);
154                if (handler != null && handler instanceof ReplyHandler) {
155                    ReplyHandler replyHandler = (ReplyHandler) handler;
156                    boolean complete = replyHandler.handle(message);
157                    if (complete) {
158                        requestMap.remove(correlationID);
159                    }
160                } else {
161                    DeferredRequestReplyMap.processDeferredRequests(
162                            this, deferredRequestMap, deferredReplyMap,
163                            correlationID, getMaxRequestTimeout(), message);
164                }
165            } catch (JMSException e) {
166                throw new FailedToProcessResponse(message, e);
167            }
168        }
169    
170    
171        public AbstractMessageListenerContainer getListenerContainer() {
172            if (listenerContainer == null) {
173                listenerContainer = createListenerContainer();
174            }
175            return listenerContainer;
176        }
177    
178        public void setListenerContainer(AbstractMessageListenerContainer listenerContainer) {
179            this.listenerContainer = listenerContainer;
180        }
181    
182        public Destination getReplyTo() {
183            synchronized (this) {
184                try {
185                    if (replyTo == null) {
186                        wait(replyToResolverTimeout);
187                    }
188                } catch (Throwable e) {
189                    // eat it
190                }
191            }
192            return replyTo;
193        }
194    
195        public void setReplyTo(Destination replyTo) {
196            this.replyTo = replyTo;
197        }
198    
199        // Implementation methods
200        //-------------------------------------------------------------------------
201    
202        @Override
203        protected void doStart() throws Exception {
204            AbstractMessageListenerContainer container = getListenerContainer();
205            container.afterPropertiesSet();
206            // Need to call the container start in Spring 3.x
207            container.start();
208        }
209    
210        @Override
211        protected void doStop() throws Exception {
212            if (listenerContainer != null) {
213                listenerContainer.stop();
214                listenerContainer.destroy();
215            }
216        }
217    
218        protected Requestor getOutterInstance() {
219            return this;
220        }
221    
222        protected AbstractMessageListenerContainer createListenerContainer() {
223            SimpleMessageListenerContainer answer = configuration.isUseVersion102()
224                ? new SimpleMessageListenerContainer102() : new SimpleMessageListenerContainer();
225            answer.setDestinationName("temporary");
226            answer.setDestinationResolver(new DestinationResolver() {
227    
228                public Destination resolveDestinationName(Session session, String destinationName,
229                                                          boolean pubSubDomain) throws JMSException {
230                    TemporaryQueue queue = null;
231                    synchronized (getOutterInstance()) {
232                        try {
233                            queue = session.createTemporaryQueue();
234                            setReplyTo(queue);
235                        } finally {
236                            getOutterInstance().notifyAll();
237                        }
238                    }
239                    return queue;
240                }
241            });
242            answer.setAutoStartup(true);
243            answer.setMessageListener(this);
244            answer.setPubSubDomain(false);
245            answer.setSubscriptionDurable(false);
246            answer.setConcurrentConsumers(1);
247            answer.setConnectionFactory(configuration.getConnectionFactory());
248            String clientId = configuration.getClientId();
249            if (clientId != null) {
250                clientId += ".Requestor";
251                answer.setClientId(clientId);
252            }
253            TaskExecutor taskExecutor = configuration.getTaskExecutor();
254            if (taskExecutor != null) {
255                answer.setTaskExecutor(taskExecutor);
256            }
257            ExceptionListener exceptionListener = configuration.getExceptionListener();
258            if (exceptionListener != null) {
259                answer.setExceptionListener(exceptionListener);
260            }
261            return answer;
262        }
263    
264        public static synchronized UuidGenerator getUuidGenerator() {
265            if (uuidGenerator == null) {
266                uuidGenerator = UuidGenerator.get();
267            }
268            return uuidGenerator;
269        }
270    
271        protected JmsConfiguration getConfiguration() {
272            return configuration;
273        }
274    
275        public void setReplyToSelectorHeader(org.apache.camel.Message in, Message jmsIn) throws JMSException {
276            // complete
277        }
278    }