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.impl;
018    
019    import java.util.Map;
020    import java.util.concurrent.Callable;
021    import java.util.concurrent.ExecutorService;
022    import java.util.concurrent.Future;
023    import java.util.concurrent.TimeUnit;
024    import java.util.concurrent.TimeoutException;
025    
026    import org.apache.camel.CamelContext;
027    import org.apache.camel.CamelExecutionException;
028    import org.apache.camel.Endpoint;
029    import org.apache.camel.Exchange;
030    import org.apache.camel.ExchangePattern;
031    import org.apache.camel.Message;
032    import org.apache.camel.NoSuchEndpointException;
033    import org.apache.camel.Processor;
034    import org.apache.camel.ProducerTemplate;
035    import org.apache.camel.spi.Synchronization;
036    import org.apache.camel.support.ServiceSupport;
037    import org.apache.camel.util.CamelContextHelper;
038    import org.apache.camel.util.ExchangeHelper;
039    import org.apache.camel.util.ObjectHelper;
040    import org.apache.camel.util.ServiceHelper;
041    
042    /**
043     * Template (named like Spring's TransactionTemplate & JmsTemplate
044     * et al) for working with Camel and sending {@link Message} instances in an
045     * {@link Exchange} to an {@link Endpoint}.
046     *
047     * @version 
048     */
049    public class DefaultProducerTemplate extends ServiceSupport implements ProducerTemplate {
050        private final CamelContext context;
051        private volatile ProducerCache producerCache;
052        private volatile ExecutorService executor;
053        private Endpoint defaultEndpoint;
054        private int maximumCacheSize;
055    
056        public DefaultProducerTemplate(CamelContext context) {
057            this.context = context;
058        }
059    
060        public DefaultProducerTemplate(CamelContext context, ExecutorService executor) {
061            this.context = context;
062            this.executor = executor;
063        }
064    
065        public DefaultProducerTemplate(CamelContext context, Endpoint defaultEndpoint) {
066            this(context);
067            this.defaultEndpoint = defaultEndpoint;
068        }
069    
070        public static DefaultProducerTemplate newInstance(CamelContext camelContext, String defaultEndpointUri) {
071            Endpoint endpoint = CamelContextHelper.getMandatoryEndpoint(camelContext, defaultEndpointUri);
072            return new DefaultProducerTemplate(camelContext, endpoint);
073        }
074    
075        public int getMaximumCacheSize() {
076            return maximumCacheSize;
077        }
078    
079        public void setMaximumCacheSize(int maximumCacheSize) {
080            this.maximumCacheSize = maximumCacheSize;
081        }
082    
083        public int getCurrentCacheSize() {
084            if (producerCache == null) {
085                return 0;
086            }
087            return producerCache.size();
088        }
089    
090        public Exchange send(String endpointUri, Exchange exchange) {
091            Endpoint endpoint = resolveMandatoryEndpoint(endpointUri);
092            return send(endpoint, exchange);
093        }
094    
095        public Exchange send(String endpointUri, Processor processor) {
096            Endpoint endpoint = resolveMandatoryEndpoint(endpointUri);
097            return send(endpoint, processor);
098        }
099    
100        public Exchange send(String endpointUri, ExchangePattern pattern, Processor processor) {
101            Endpoint endpoint = resolveMandatoryEndpoint(endpointUri);
102            return send(endpoint, pattern, processor);
103        }
104    
105        public Exchange send(Endpoint endpoint, Exchange exchange) {
106            getProducerCache().send(endpoint, exchange);
107            return exchange;
108        }
109    
110        public Exchange send(Endpoint endpoint, Processor processor) {
111            return getProducerCache().send(endpoint, processor);
112        }
113    
114        public Exchange send(Endpoint endpoint, ExchangePattern pattern, Processor processor) {
115            return getProducerCache().send(endpoint, pattern, processor);
116        }
117    
118        public Object sendBody(Endpoint endpoint, ExchangePattern pattern, Object body) {
119            Exchange result = send(endpoint, pattern, createSetBodyProcessor(body));
120            return extractResultBody(result, pattern);
121        }
122    
123        public void sendBody(Endpoint endpoint, Object body) throws CamelExecutionException {
124            Exchange result = send(endpoint, createSetBodyProcessor(body));
125            // must invoke extract result body in case of exception to be rethrown
126            extractResultBody(result);
127        }
128    
129        public void sendBody(String endpointUri, Object body) throws CamelExecutionException {
130            Endpoint endpoint = resolveMandatoryEndpoint(endpointUri);
131            sendBody(endpoint, body);
132        }
133    
134        public Object sendBody(String endpointUri, ExchangePattern pattern, Object body) throws CamelExecutionException {
135            Endpoint endpoint = resolveMandatoryEndpoint(endpointUri);
136            Object result = sendBody(endpoint, pattern, body);
137            if (pattern.isOutCapable()) {
138                return result;
139            } else {
140                // return null if not OUT capable
141                return null;
142            }
143        }
144    
145        public void sendBodyAndHeader(String endpointUri, final Object body, final String header, final Object headerValue) throws CamelExecutionException {
146            sendBodyAndHeader(resolveMandatoryEndpoint(endpointUri), body, header, headerValue);
147        }
148    
149        public void sendBodyAndHeader(Endpoint endpoint, final Object body, final String header, final Object headerValue) throws CamelExecutionException {
150            Exchange result = send(endpoint, createBodyAndHeaderProcessor(body, header, headerValue));
151            // must invoke extract result body in case of exception to be rethrown
152            extractResultBody(result);
153        }
154    
155        public Object sendBodyAndHeader(Endpoint endpoint, ExchangePattern pattern, final Object body,
156                                        final String header, final Object headerValue) throws CamelExecutionException {
157            Exchange exchange = send(endpoint, pattern, createBodyAndHeaderProcessor(body, header, headerValue));
158            Object result = extractResultBody(exchange, pattern);
159            if (pattern.isOutCapable()) {
160                return result;
161            } else {
162                // return null if not OUT capable
163                return null;
164            }
165        }
166    
167        public Object sendBodyAndHeader(String endpoint, ExchangePattern pattern, final Object body,
168                                        final String header, final Object headerValue) throws CamelExecutionException {
169            Exchange exchange = send(endpoint, pattern, createBodyAndHeaderProcessor(body, header, headerValue));
170            Object result = extractResultBody(exchange, pattern);
171            if (pattern.isOutCapable()) {
172                return result;
173            } else {
174                // return null if not OUT capable
175                return null;
176            }
177        }
178    
179        public void sendBodyAndProperty(String endpointUri, final Object body,
180                                        final String property, final Object propertyValue) throws CamelExecutionException {
181            sendBodyAndProperty(resolveMandatoryEndpoint(endpointUri), body, property, propertyValue);
182        }
183    
184        public void sendBodyAndProperty(Endpoint endpoint, final Object body,
185                                        final String property, final Object propertyValue) throws CamelExecutionException {
186            Exchange result = send(endpoint, createBodyAndPropertyProcessor(body, property, propertyValue));
187            // must invoke extract result body in case of exception to be rethrown
188            extractResultBody(result);
189        }
190    
191        public Object sendBodyAndProperty(Endpoint endpoint, ExchangePattern pattern, final Object body,
192                                          final String property, final Object propertyValue) throws CamelExecutionException {
193            Exchange exchange = send(endpoint, pattern, createBodyAndPropertyProcessor(body, property, propertyValue));
194            Object result = extractResultBody(exchange, pattern);
195            if (pattern.isOutCapable()) {
196                return result;
197            } else {
198                // return null if not OUT capable
199                return null;
200            }
201        }
202    
203        public Object sendBodyAndProperty(String endpoint, ExchangePattern pattern, final Object body,
204                                          final String property, final Object propertyValue) throws CamelExecutionException {
205            Exchange exchange = send(endpoint, pattern, createBodyAndPropertyProcessor(body, property, propertyValue));
206            Object result = extractResultBody(exchange, pattern);
207            if (pattern.isOutCapable()) {
208                return result;
209            } else {
210                // return null if not OUT capable
211                return null;
212            }
213        }
214    
215        public void sendBodyAndHeaders(String endpointUri, final Object body, final Map<String, Object> headers) throws CamelExecutionException {
216            sendBodyAndHeaders(resolveMandatoryEndpoint(endpointUri), body, headers);
217        }
218    
219        public void sendBodyAndHeaders(Endpoint endpoint, final Object body, final Map<String, Object> headers) throws CamelExecutionException {
220            Exchange result = send(endpoint, new Processor() {
221                public void process(Exchange exchange) {
222                    Message in = exchange.getIn();
223                    for (Map.Entry<String, Object> header : headers.entrySet()) {
224                        in.setHeader(header.getKey(), header.getValue());
225                    }
226                    in.setBody(body);
227                }
228            });
229            // must invoke extract result body in case of exception to be rethrown
230            extractResultBody(result);
231        }
232    
233        public Object sendBodyAndHeaders(String endpointUri, ExchangePattern pattern, Object body, Map<String, Object> headers) throws CamelExecutionException {
234            return sendBodyAndHeaders(resolveMandatoryEndpoint(endpointUri), pattern, body, headers);
235        }
236    
237        public Object sendBodyAndHeaders(Endpoint endpoint, ExchangePattern pattern, final Object body, final Map<String, Object> headers) throws CamelExecutionException {
238            Exchange exchange = send(endpoint, pattern, new Processor() {
239                public void process(Exchange exchange) throws Exception {
240                    Message in = exchange.getIn();
241                    for (Map.Entry<String, Object> header : headers.entrySet()) {
242                        in.setHeader(header.getKey(), header.getValue());
243                    }
244                    in.setBody(body);
245                }
246            });
247            Object result = extractResultBody(exchange, pattern);
248            if (pattern.isOutCapable()) {
249                return result;
250            } else {
251                // return null if not OUT capable
252                return null;
253            }
254        }
255    
256        // Methods using an InOut ExchangePattern
257        // -----------------------------------------------------------------------
258    
259        public Exchange request(Endpoint endpoint, Processor processor) {
260            return send(endpoint, ExchangePattern.InOut, processor);
261        }
262    
263        public Object requestBody(Object body) throws CamelExecutionException {
264            return sendBody(getMandatoryDefaultEndpoint(), ExchangePattern.InOut, body);
265        }
266    
267        public Object requestBody(Endpoint endpoint, Object body) throws CamelExecutionException {
268            return sendBody(endpoint, ExchangePattern.InOut, body);
269        }
270    
271        public Object requestBodyAndHeader(Object body, String header, Object headerValue) throws CamelExecutionException {
272            return sendBodyAndHeader(getMandatoryDefaultEndpoint(), ExchangePattern.InOut, body, header, headerValue);
273        }
274    
275        public Object requestBodyAndHeader(Endpoint endpoint, Object body, String header, Object headerValue) throws CamelExecutionException {
276            return sendBodyAndHeader(endpoint, ExchangePattern.InOut, body, header, headerValue);
277        }
278    
279        public Exchange request(String endpoint, Processor processor) throws CamelExecutionException {
280            return send(endpoint, ExchangePattern.InOut, processor);
281        }
282    
283        public Object requestBody(String endpoint, Object body) throws CamelExecutionException {
284            return sendBody(endpoint, ExchangePattern.InOut, body);
285        }
286    
287        public Object requestBodyAndHeader(String endpoint, Object body, String header, Object headerValue) throws CamelExecutionException {
288            return sendBodyAndHeader(endpoint, ExchangePattern.InOut, body, header, headerValue);
289        }
290    
291        public Object requestBodyAndHeaders(String endpointUri, Object body, Map<String, Object> headers) {
292            return requestBodyAndHeaders(resolveMandatoryEndpoint(endpointUri), body, headers);
293        }
294    
295        public Object requestBodyAndHeaders(Endpoint endpoint, final Object body, final Map<String, Object> headers) {
296            return sendBodyAndHeaders(endpoint, ExchangePattern.InOut, body, headers);
297        }
298    
299        public Object requestBodyAndHeaders(final Object body, final Map<String, Object> headers) {
300            return sendBodyAndHeaders(getDefaultEndpoint(), ExchangePattern.InOut, body, headers);
301        }
302    
303        public <T> T requestBody(Object body, Class<T> type) {
304            Object answer = requestBody(body);
305            return context.getTypeConverter().convertTo(type, answer);
306        }
307    
308        public <T> T requestBody(Endpoint endpoint, Object body, Class<T> type) {
309            Object answer = requestBody(endpoint, body);
310            return context.getTypeConverter().convertTo(type, answer);
311        }
312    
313        public <T> T requestBody(String endpointUri, Object body, Class<T> type) {
314            Object answer = requestBody(endpointUri, body);
315            return context.getTypeConverter().convertTo(type, answer);
316        }
317    
318        public <T> T requestBodyAndHeader(Endpoint endpoint, Object body, String header, Object headerValue, Class<T> type) {
319            Object answer = requestBodyAndHeader(endpoint, body, header, headerValue);
320            return context.getTypeConverter().convertTo(type, answer);
321        }
322    
323        public <T> T requestBodyAndHeader(String endpointUri, Object body, String header, Object headerValue, Class<T> type) {
324            Object answer = requestBodyAndHeader(endpointUri, body, header, headerValue);
325            return context.getTypeConverter().convertTo(type, answer);
326        }
327    
328        public <T> T requestBodyAndHeaders(String endpointUri, Object body, Map<String, Object> headers, Class<T> type) {
329            Object answer = requestBodyAndHeaders(endpointUri, body, headers);
330            return context.getTypeConverter().convertTo(type, answer);
331        }
332    
333        public <T> T requestBodyAndHeaders(Endpoint endpoint, Object body, Map<String, Object> headers, Class<T> type) {
334            Object answer = requestBodyAndHeaders(endpoint, body, headers);
335            return context.getTypeConverter().convertTo(type, answer);
336        }
337    
338        // Methods using the default endpoint
339        // -----------------------------------------------------------------------
340    
341        public void sendBody(Object body) {
342            sendBody(getMandatoryDefaultEndpoint(), body);
343        }
344    
345        public Exchange send(Exchange exchange) {
346            return send(getMandatoryDefaultEndpoint(), exchange);
347        }
348    
349        public Exchange send(Processor processor) {
350            return send(getMandatoryDefaultEndpoint(), processor);
351        }
352    
353        public void sendBodyAndHeader(Object body, String header, Object headerValue) {
354            sendBodyAndHeader(getMandatoryDefaultEndpoint(), body, header, headerValue);
355        }
356    
357        public void sendBodyAndProperty(Object body, String property, Object propertyValue) {
358            sendBodyAndProperty(getMandatoryDefaultEndpoint(), body, property, propertyValue);
359        }
360    
361        public void sendBodyAndHeaders(Object body, Map<String, Object> headers) {
362            sendBodyAndHeaders(getMandatoryDefaultEndpoint(), body, headers);
363        }
364    
365        // Properties
366        // -----------------------------------------------------------------------
367    
368        public CamelContext getContext() {
369            return context;
370        }
371    
372        public Endpoint getDefaultEndpoint() {
373            return defaultEndpoint;
374        }
375    
376        public void setDefaultEndpoint(Endpoint defaultEndpoint) {
377            this.defaultEndpoint = defaultEndpoint;
378        }
379    
380        /**
381         * Sets the default endpoint to use if none is specified
382         */
383        public void setDefaultEndpointUri(String endpointUri) {
384            setDefaultEndpoint(getContext().getEndpoint(endpointUri));
385        }
386    
387        public <T extends Endpoint> T getResolvedEndpoint(String endpointUri, Class<T> expectedClass) {
388            return context.getEndpoint(endpointUri, expectedClass);
389        }
390    
391        // Implementation methods
392        // -----------------------------------------------------------------------
393    
394        protected Processor createBodyAndHeaderProcessor(final Object body, final String header, final Object headerValue) {
395            return new Processor() {
396                public void process(Exchange exchange) {
397                    Message in = exchange.getIn();
398                    in.setHeader(header, headerValue);
399                    in.setBody(body);
400                }
401            };
402        }
403    
404        protected Processor createBodyAndPropertyProcessor(final Object body, final String property, final Object propertyValue) {
405            return new Processor() {
406                public void process(Exchange exchange) {
407                    exchange.setProperty(property, propertyValue);
408                    Message in = exchange.getIn();
409                    in.setBody(body);
410                }
411            };
412        }
413    
414        protected Processor createSetBodyProcessor(final Object body) {
415            return new Processor() {
416                public void process(Exchange exchange) {
417                    Message in = exchange.getIn();
418                    in.setBody(body);
419                }
420            };
421        }
422    
423        protected Endpoint resolveMandatoryEndpoint(String endpointUri) {
424            Endpoint endpoint = context.getEndpoint(endpointUri);
425            if (endpoint == null) {
426                throw new NoSuchEndpointException(endpointUri);
427            }
428            return endpoint;
429        }
430    
431        protected Endpoint getMandatoryDefaultEndpoint() {
432            Endpoint answer = getDefaultEndpoint();
433            ObjectHelper.notNull(answer, "defaultEndpoint");
434            return answer;
435        }
436    
437        protected Object extractResultBody(Exchange result) {
438            return extractResultBody(result, null);
439        }
440    
441        protected Object extractResultBody(Exchange result, ExchangePattern pattern) {
442            return ExchangeHelper.extractResultBody(result, pattern);
443        }
444    
445        public void setExecutorService(ExecutorService executorService) {
446            this.executor = executorService;
447        }
448    
449        public Future<Exchange> asyncSend(final String uri, final Exchange exchange) {
450            return asyncSend(resolveMandatoryEndpoint(uri), exchange);
451        }
452    
453        public Future<Exchange> asyncSend(final String uri, final Processor processor) {
454            return asyncSend(resolveMandatoryEndpoint(uri), processor);
455        }
456    
457        public Future<Object> asyncSendBody(final String uri, final Object body) {
458            return asyncSendBody(resolveMandatoryEndpoint(uri), body);
459        }
460    
461        public Future<Object> asyncRequestBody(final String uri, final Object body) {
462            return asyncRequestBody(resolveMandatoryEndpoint(uri), body);
463        }
464    
465        public <T> Future<T> asyncRequestBody(final String uri, final Object body, final Class<T> type) {
466            return asyncRequestBody(resolveMandatoryEndpoint(uri), body, type);
467        }
468    
469        public Future<Object> asyncRequestBodyAndHeader(final String endpointUri, final Object body, final String header, final Object headerValue) {
470            return asyncRequestBodyAndHeader(resolveMandatoryEndpoint(endpointUri), body, header, headerValue);
471        }
472    
473        public <T> Future<T> asyncRequestBodyAndHeader(final String endpointUri, final Object body, final String header, final Object headerValue, final Class<T> type) {
474            return asyncRequestBodyAndHeader(resolveMandatoryEndpoint(endpointUri), body, header, headerValue, type);
475        }
476    
477        public Future<Object> asyncRequestBodyAndHeaders(final String endpointUri, final Object body, final Map<String, Object> headers) {
478            return asyncRequestBodyAndHeaders(resolveMandatoryEndpoint(endpointUri), body, headers);
479        }
480    
481        public <T> Future<T> asyncRequestBodyAndHeaders(final String endpointUri, final Object body, final Map<String, Object> headers, final Class<T> type) {
482            return asyncRequestBodyAndHeaders(resolveMandatoryEndpoint(endpointUri), body, headers, type);
483        }
484    
485        public <T> T extractFutureBody(Future<Object> future, Class<T> type) {
486            return ExchangeHelper.extractFutureBody(context, future, type);
487        }
488    
489        public <T> T extractFutureBody(Future<Object> future, long timeout, TimeUnit unit, Class<T> type) throws TimeoutException {
490            return ExchangeHelper.extractFutureBody(context, future, timeout, unit, type);
491        }
492    
493        public Future<Object> asyncCallbackSendBody(String uri, Object body, Synchronization onCompletion) {
494            return asyncCallbackSendBody(resolveMandatoryEndpoint(uri), body, onCompletion);
495        }
496    
497        public Future<Object> asyncCallbackSendBody(Endpoint endpoint, Object body, Synchronization onCompletion) {
498            return asyncCallback(endpoint, ExchangePattern.InOnly, body, onCompletion);
499        }
500    
501        public Future<Object> asyncCallbackRequestBody(String uri, Object body, Synchronization onCompletion) {
502            return asyncCallbackRequestBody(resolveMandatoryEndpoint(uri), body, onCompletion);
503        }
504    
505        public Future<Object> asyncCallbackRequestBody(Endpoint endpoint, Object body, Synchronization onCompletion) {
506            return asyncCallback(endpoint, ExchangePattern.InOut, body, onCompletion);
507        }
508    
509        public Future<Exchange> asyncCallback(String uri, Exchange exchange, Synchronization onCompletion) {
510            return asyncCallback(resolveMandatoryEndpoint(uri), exchange, onCompletion);
511        }
512    
513        public Future<Exchange> asyncCallback(String uri, Processor processor, Synchronization onCompletion) {
514            return asyncCallback(resolveMandatoryEndpoint(uri), processor, onCompletion);
515        }
516    
517        public Future<Object> asyncRequestBody(final Endpoint endpoint, final Object body) {
518            Callable<Object> task = new Callable<Object>() {
519                public Object call() throws Exception {
520                    return requestBody(endpoint, body);
521                }
522            };
523            return getExecutorService().submit(task);
524        }
525    
526        public <T> Future<T> asyncRequestBody(final Endpoint endpoint, final Object body, final Class<T> type) {
527            Callable<T> task = new Callable<T>() {
528                public T call() throws Exception {
529                    return requestBody(endpoint, body, type);
530                }
531            };
532            return getExecutorService().submit(task);
533        }
534    
535        public Future<Object> asyncRequestBodyAndHeader(final Endpoint endpoint, final Object body, final String header,
536                                                        final Object headerValue) {
537            Callable<Object> task = new Callable<Object>() {
538                public Object call() throws Exception {
539                    return requestBodyAndHeader(endpoint, body, header, headerValue);
540                }
541            };
542            return getExecutorService().submit(task);
543        }
544    
545        public <T> Future<T> asyncRequestBodyAndHeader(final Endpoint endpoint, final Object body, final String header,
546                                                       final Object headerValue, final Class<T> type) {
547            Callable<T> task = new Callable<T>() {
548                public T call() throws Exception {
549                    return requestBodyAndHeader(endpoint, body, header, headerValue, type);
550                }
551            };
552            return getExecutorService().submit(task);
553        }
554    
555        public Future<Object> asyncRequestBodyAndHeaders(final Endpoint endpoint, final Object body,
556                                                         final Map<String, Object> headers) {
557            Callable<Object> task = new Callable<Object>() {
558                public Object call() throws Exception {
559                    return requestBodyAndHeaders(endpoint, body, headers);
560                }
561            };
562            return getExecutorService().submit(task);
563        }
564    
565        public <T> Future<T> asyncRequestBodyAndHeaders(final Endpoint endpoint, final Object body,
566                                                        final Map<String, Object> headers, final Class<T> type) {
567            Callable<T> task = new Callable<T>() {
568                public T call() throws Exception {
569                    return requestBodyAndHeaders(endpoint, body, headers, type);
570                }
571            };
572            return getExecutorService().submit(task);
573        }
574    
575        public Future<Exchange> asyncSend(final Endpoint endpoint, final Exchange exchange) {
576            Callable<Exchange> task = new Callable<Exchange>() {
577                public Exchange call() throws Exception {
578                    return send(endpoint, exchange);
579                }
580            };
581            return getExecutorService().submit(task);
582        }
583    
584        public Future<Exchange> asyncSend(final Endpoint endpoint, final Processor processor) {
585            Callable<Exchange> task = new Callable<Exchange>() {
586                public Exchange call() throws Exception {
587                    return send(endpoint, processor);
588                }
589            };
590            return getExecutorService().submit(task);
591        }
592    
593        public Future<Object> asyncSendBody(final Endpoint endpoint, final Object body) {
594            Callable<Object> task = new Callable<Object>() {
595                public Object call() throws Exception {
596                    sendBody(endpoint, body);
597                    // its InOnly, so no body to return
598                    return null;
599                }
600            };
601            return getExecutorService().submit(task);
602        }
603    
604        private Future<Object> asyncCallback(final Endpoint endpoint, final ExchangePattern pattern, final Object body, final Synchronization onCompletion) {
605            Callable<Object> task = new Callable<Object>() {
606                public Object call() throws Exception {
607                    Exchange answer = send(endpoint, pattern, createSetBodyProcessor(body));
608    
609                    // invoke callback before returning answer
610                    // as it allows callback to be used without UnitOfWorkProcessor invoking it
611                    // and thus it works directly from a producer template as well, as opposed
612                    // to the UnitOfWorkProcessor that is injected in routes
613                    if (answer.isFailed()) {
614                        onCompletion.onFailure(answer);
615                    } else {
616                        onCompletion.onComplete(answer);
617                    }
618    
619                    Object result = extractResultBody(answer, pattern);
620                    if (pattern.isOutCapable()) {
621                        return result;
622                    } else {
623                        // return null if not OUT capable
624                        return null;
625                    }
626                }
627            };
628            return getExecutorService().submit(task);
629        }
630    
631        public Future<Exchange> asyncCallback(final Endpoint endpoint, final Exchange exchange, final Synchronization onCompletion) {
632            Callable<Exchange> task = new Callable<Exchange>() {
633                public Exchange call() throws Exception {
634                    // process the exchange, any exception occurring will be caught and set on the exchange
635                    send(endpoint, exchange);
636    
637                    // invoke callback before returning answer
638                    // as it allows callback to be used without UnitOfWorkProcessor invoking it
639                    // and thus it works directly from a producer template as well, as opposed
640                    // to the UnitOfWorkProcessor that is injected in routes
641                    if (exchange.isFailed()) {
642                        onCompletion.onFailure(exchange);
643                    } else {
644                        onCompletion.onComplete(exchange);
645                    }
646                    return exchange;
647                }
648            };
649            return getExecutorService().submit(task);
650        }
651    
652        public Future<Exchange> asyncCallback(final Endpoint endpoint, final Processor processor, final Synchronization onCompletion) {
653            Callable<Exchange> task = new Callable<Exchange>() {
654                public Exchange call() throws Exception {
655                    // process the exchange, any exception occurring will be caught and set on the exchange
656                    Exchange answer = send(endpoint, processor);
657    
658                    // invoke callback before returning answer
659                    // as it allows callback to be used without UnitOfWorkProcessor invoking it
660                    // and thus it works directly from a producer template as well, as opposed
661                    // to the UnitOfWorkProcessor that is injected in routes
662                    if (answer.isFailed()) {
663                        onCompletion.onFailure(answer);
664                    } else {
665                        onCompletion.onComplete(answer);
666                    }
667                    return answer;
668                }
669            };
670            return getExecutorService().submit(task);
671        }
672    
673        private ProducerCache getProducerCache() {
674            if (!isStarted()) {
675                throw new IllegalStateException("ProducerTemplate has not been started");
676            }
677            return producerCache;
678        }
679    
680        private ExecutorService getExecutorService() {
681            if (!isStarted()) {
682                throw new IllegalStateException("ProducerTemplate has not been started");
683            }
684    
685            if (executor != null) {
686                return executor;
687            }
688    
689            // create a default executor which must be synchronized
690            synchronized (this) {
691                if (executor != null) {
692                    return executor;
693                }
694                executor = context.getExecutorServiceManager().newDefaultThreadPool(this, "ProducerTemplate");
695            }
696    
697            ObjectHelper.notNull(executor, "ExecutorService");
698            return executor;
699        }
700    
701        protected void doStart() throws Exception {
702            if (producerCache == null) {
703                if (maximumCacheSize > 0) {
704                    producerCache = new ProducerCache(this, context, maximumCacheSize);
705                } else {
706                    producerCache = new ProducerCache(this, context);
707                }
708            }
709            ServiceHelper.startService(producerCache);
710        }
711    
712        protected void doStop() throws Exception {
713            ServiceHelper.stopService(producerCache);
714            producerCache = null;
715    
716            if (executor != null) {
717                context.getExecutorServiceManager().shutdownNow(executor);
718                executor = null;
719            }
720        }
721    
722    }