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 }