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 */
017package org.apache.camel.util;
018
019import java.util.HashMap;
020import java.util.LinkedList;
021import java.util.List;
022import java.util.Map;
023import java.util.concurrent.ExecutionException;
024import java.util.concurrent.Future;
025import java.util.concurrent.TimeUnit;
026import java.util.concurrent.TimeoutException;
027import java.util.function.Predicate;
028
029import org.apache.camel.CamelContext;
030import org.apache.camel.CamelExchangeException;
031import org.apache.camel.CamelExecutionException;
032import org.apache.camel.Endpoint;
033import org.apache.camel.Exchange;
034import org.apache.camel.ExchangePattern;
035import org.apache.camel.InvalidPayloadException;
036import org.apache.camel.Message;
037import org.apache.camel.MessageHistory;
038import org.apache.camel.NoSuchBeanException;
039import org.apache.camel.NoSuchEndpointException;
040import org.apache.camel.NoSuchHeaderException;
041import org.apache.camel.NoSuchPropertyException;
042import org.apache.camel.NoTypeConversionAvailableException;
043import org.apache.camel.TypeConversionException;
044import org.apache.camel.TypeConverter;
045import org.apache.camel.impl.DefaultExchange;
046import org.apache.camel.impl.MessageSupport;
047import org.apache.camel.spi.Synchronization;
048import org.apache.camel.spi.UnitOfWork;
049
050/**
051 * Some helper methods for working with {@link Exchange} objects
052 *
053 * @version
054 */
055public final class ExchangeHelper {
056
057    /**
058     * Utility classes should not have a public constructor.
059     */
060    private ExchangeHelper() {
061    }
062
063    /**
064     * Extracts the Exchange.BINDING of the given type or null if not present
065     *
066     * @param exchange the message exchange
067     * @param type     the expected binding type
068     * @return the binding object of the given type or null if it could not be found or converted
069     */
070    public static <T> T getBinding(Exchange exchange, Class<T> type) {
071        return exchange != null ? exchange.getProperty(Exchange.BINDING, type) : null;
072    }
073
074    /**
075     * Attempts to resolve the endpoint for the given value
076     *
077     * @param exchange the message exchange being processed
078     * @param value    the value which can be an {@link Endpoint} or an object
079     *                 which provides a String representation of an endpoint via
080     *                 {@link #toString()}
081     * @return the endpoint
082     * @throws NoSuchEndpointException if the endpoint cannot be resolved
083     */
084    public static Endpoint resolveEndpoint(Exchange exchange, Object value) throws NoSuchEndpointException {
085        Endpoint endpoint;
086        if (value instanceof Endpoint) {
087            endpoint = (Endpoint) value;
088        } else {
089            String uri = value.toString().trim();
090            endpoint = CamelContextHelper.getMandatoryEndpoint(exchange.getContext(), uri);
091        }
092        return endpoint;
093    }
094
095    /**
096     * Gets the mandatory property of the exchange of the correct type
097     *
098     * @param exchange      the exchange
099     * @param propertyName  the property name
100     * @param type          the type
101     * @return the property value
102     * @throws TypeConversionException is thrown if error during type conversion
103     * @throws NoSuchPropertyException is thrown if no property exists
104     */
105    public static <T> T getMandatoryProperty(Exchange exchange, String propertyName, Class<T> type) throws NoSuchPropertyException {
106        T result = exchange.getProperty(propertyName, type);
107        if (result != null) {
108            return result;
109        }
110        throw new NoSuchPropertyException(exchange, propertyName, type);
111    }
112
113    /**
114     * Gets the mandatory inbound header of the correct type
115     *
116     * @param exchange      the exchange
117     * @param headerName    the header name
118     * @param type          the type
119     * @return the header value
120     * @throws TypeConversionException is thrown if error during type conversion
121     * @throws NoSuchHeaderException is thrown if no headers exists
122     */
123    public static <T> T getMandatoryHeader(Exchange exchange, String headerName, Class<T> type) throws TypeConversionException, NoSuchHeaderException {
124        T answer = exchange.getIn().getHeader(headerName, type);
125        if (answer == null) {
126            throw new NoSuchHeaderException(exchange, headerName, type);
127        }
128        return answer;
129    }
130
131    /**
132     * Gets the mandatory inbound header of the correct type
133     *
134     * @param message       the message
135     * @param headerName    the header name
136     * @param type          the type
137     * @return the header value
138     * @throws TypeConversionException is thrown if error during type conversion
139     * @throws NoSuchHeaderException is thrown if no headers exists
140     */
141    public static <T> T getMandatoryHeader(Message message, String headerName, Class<T> type) throws TypeConversionException, NoSuchHeaderException {
142        T answer = message.getHeader(headerName, type);
143        if (answer == null) {
144            throw new NoSuchHeaderException(message.getExchange(), headerName, type);
145        }
146        return answer;
147    }
148
149    /**
150     * Gets an header or property of the correct type
151     *
152     * @param exchange      the exchange
153     * @param name          the name of the header or the property
154     * @param type          the type
155     * @return the header or property value
156     * @throws TypeConversionException is thrown if error during type conversion
157     * @throws NoSuchHeaderException is thrown if no headers exists
158     */
159    public static <T> T getHeaderOrProperty(Exchange exchange, String name, Class<T> type) throws TypeConversionException {
160        T answer = exchange.getIn().getHeader(name, type);
161        if (answer == null) {
162            answer = exchange.getProperty(name, type);
163        }
164        return answer;
165    }
166
167    /**
168     * Returns the mandatory inbound message body of the correct type or throws
169     * an exception if it is not present
170     *
171     * @param exchange the exchange
172     * @return the body, is never <tt>null</tt>
173     * @throws InvalidPayloadException Is thrown if the body being <tt>null</tt> or wrong class type
174     * @deprecated use {@link org.apache.camel.Message#getMandatoryBody()}
175     */
176    @Deprecated
177    public static Object getMandatoryInBody(Exchange exchange) throws InvalidPayloadException {
178        return exchange.getIn().getMandatoryBody();
179    }
180
181    /**
182     * Returns the mandatory inbound message body of the correct type or throws
183     * an exception if it is not present
184     * @deprecated use {@link org.apache.camel.Message#getMandatoryBody(Class)}
185     */
186    @Deprecated
187    public static <T> T getMandatoryInBody(Exchange exchange, Class<T> type) throws InvalidPayloadException {
188        return exchange.getIn().getMandatoryBody(type);
189    }
190
191    /**
192     * Returns the mandatory outbound message body of the correct type or throws
193     * an exception if it is not present
194     * @deprecated use {@link org.apache.camel.Message#getMandatoryBody()}
195     */
196    @Deprecated
197    public static Object getMandatoryOutBody(Exchange exchange) throws InvalidPayloadException {
198        return exchange.getOut().getMandatoryBody();
199    }
200
201    /**
202     * Returns the mandatory outbound message body of the correct type or throws
203     * an exception if it is not present
204     * @deprecated use {@link org.apache.camel.Message#getMandatoryBody(Class)}
205     */
206    @Deprecated
207    public static <T> T getMandatoryOutBody(Exchange exchange, Class<T> type) throws InvalidPayloadException {
208        return exchange.getOut().getMandatoryBody(type);
209    }
210
211    /**
212     * Converts the value to the given expected type or throws an exception
213     *
214     * @return the converted value
215     * @throws TypeConversionException is thrown if error during type conversion
216     * @throws NoTypeConversionAvailableException} if no type converters exists to convert to the given type
217     */
218    public static <T> T convertToMandatoryType(Exchange exchange, Class<T> type, Object value)
219        throws TypeConversionException, NoTypeConversionAvailableException {
220        CamelContext camelContext = exchange.getContext();
221        ObjectHelper.notNull(camelContext, "CamelContext of Exchange");
222        TypeConverter converter = camelContext.getTypeConverter();
223        if (converter != null) {
224            return converter.mandatoryConvertTo(type, exchange, value);
225        }
226        throw new NoTypeConversionAvailableException(value, type);
227    }
228
229    /**
230     * Converts the value to the given expected type
231     *
232     * @return the converted value
233     * @throws org.apache.camel.TypeConversionException is thrown if error during type conversion
234     */
235    public static <T> T convertToType(Exchange exchange, Class<T> type, Object value) throws TypeConversionException {
236        CamelContext camelContext = exchange.getContext();
237        ObjectHelper.notNull(camelContext, "CamelContext of Exchange");
238        TypeConverter converter = camelContext.getTypeConverter();
239        if (converter != null) {
240            return converter.convertTo(type, exchange, value);
241        }
242        return null;
243    }
244
245    /**
246     * Creates a new instance and copies from the current message exchange so that it can be
247     * forwarded to another destination as a new instance. Unlike regular copy this operation
248     * will not share the same {@link org.apache.camel.spi.UnitOfWork} so its should be used
249     * for async messaging, where the original and copied exchange are independent.
250     *
251     * @param exchange original copy of the exchange
252     * @param handover whether the on completion callbacks should be handed over to the new copy.
253     */
254    public static Exchange createCorrelatedCopy(Exchange exchange, boolean handover) {
255        return createCorrelatedCopy(exchange, handover, false);
256    }
257
258    /**
259     * Creates a new instance and copies from the current message exchange so that it can be
260     * forwarded to another destination as a new instance. Unlike regular copy this operation
261     * will not share the same {@link org.apache.camel.spi.UnitOfWork} so its should be used
262     * for async messaging, where the original and copied exchange are independent.
263     *
264     * @param exchange original copy of the exchange
265     * @param handover whether the on completion callbacks should be handed over to the new copy.
266     * @param useSameMessageId whether to use same message id on the copy message.
267     */
268    public static Exchange createCorrelatedCopy(Exchange exchange, boolean handover, boolean useSameMessageId) {
269        return createCorrelatedCopy(exchange, handover, useSameMessageId, null);
270    }
271
272    /**
273     * Creates a new instance and copies from the current message exchange so that it can be
274     * forwarded to another destination as a new instance. Unlike regular copy this operation
275     * will not share the same {@link org.apache.camel.spi.UnitOfWork} so its should be used
276     * for async messaging, where the original and copied exchange are independent.
277     *
278     * @param exchange original copy of the exchange
279     * @param handover whether the on completion callbacks should be handed over to the new copy.
280     * @param useSameMessageId whether to use same message id on the copy message.
281     * @param filter whether to handover the on completion
282     */
283    public static Exchange createCorrelatedCopy(Exchange exchange, boolean handover, boolean useSameMessageId, Predicate<Synchronization> filter) {
284        String id = exchange.getExchangeId();
285
286        // make sure to do a safe copy as the correlated copy can be routed independently of the source.
287        Exchange copy = exchange.copy(true);
288        // do not reuse message id on copy
289        if (!useSameMessageId) {
290            if (copy.hasOut()) {
291                copy.getOut().setMessageId(null);
292            }
293            copy.getIn().setMessageId(null);
294        }
295        // do not share the unit of work
296        copy.setUnitOfWork(null);
297        // do not reuse the message id
298        // hand over on completion to the copy if we got any
299        UnitOfWork uow = exchange.getUnitOfWork();
300        if (handover && uow != null) {
301            uow.handoverSynchronization(copy, filter);
302        }
303        // set a correlation id so we can track back the original exchange
304        copy.setProperty(Exchange.CORRELATION_ID, id);
305        return copy;
306    }
307
308    /**
309     * Creates a new instance and copies from the current message exchange so that it can be
310     * forwarded to another destination as a new instance.
311     *
312     * @param exchange original copy of the exchange
313     * @param preserveExchangeId whether or not the exchange id should be preserved
314     * @return the copy
315     */
316    public static Exchange createCopy(Exchange exchange, boolean preserveExchangeId) {
317        Exchange copy = exchange.copy();
318        if (preserveExchangeId) {
319            // must preserve exchange id
320            copy.setExchangeId(exchange.getExchangeId());
321        }
322        return copy;
323    }
324
325    /**
326     * Copies the results of a message exchange from the source exchange to the result exchange
327     * which will copy the message contents, exchange properties and the exception.
328     * Notice the {@link ExchangePattern} is <b>not</b> copied/altered.
329     *
330     * @param result the result exchange which will have the output and error state added
331     * @param source the source exchange which is not modified
332     */
333    public static void copyResults(Exchange result, Exchange source) {
334
335        // --------------------------------------------------------------------
336        //  TODO: merge logic with that of copyResultsPreservePattern()
337        // --------------------------------------------------------------------
338
339        if (result == source) {
340            // we just need to ensure MEP is as expected (eg copy result to OUT if out capable)
341            // and the result is not failed
342            if (result.getPattern() == ExchangePattern.InOptionalOut) {
343                // keep as is
344            } else if (result.getPattern().isOutCapable() && !result.hasOut() && !result.isFailed()) {
345                // copy IN to OUT as we expect a OUT response
346                result.getOut().copyFrom(source.getIn());
347            }
348            return;
349        }
350
351        if (result != source) {
352            result.setException(source.getException());
353            if (source.hasOut()) {
354                result.getOut().copyFrom(source.getOut());
355            } else if (result.getPattern() == ExchangePattern.InOptionalOut) {
356                // special case where the result is InOptionalOut and with no OUT response
357                // so we should return null to indicate this fact
358                result.setOut(null);
359            } else {
360                // no results so lets copy the last input
361                // as the final processor on a pipeline might not
362                // have created any OUT; such as a mock:endpoint
363                // so lets assume the last IN is the OUT
364                if (result.getPattern().isOutCapable()) {
365                    // only set OUT if its OUT capable
366                    result.getOut().copyFrom(source.getIn());
367                } else {
368                    // if not replace IN instead to keep the MEP
369                    result.getIn().copyFrom(source.getIn());
370                    // clear any existing OUT as the result is on the IN
371                    if (result.hasOut()) {
372                        result.setOut(null);
373                    }
374                }
375            }
376
377            if (source.hasProperties()) {
378                result.getProperties().putAll(source.getProperties());
379            }
380        }
381    }
382
383    /**
384     * Copies the <code>source</code> exchange to <code>target</code> exchange
385     * preserving the {@link ExchangePattern} of <code>target</code>.
386     *
387     * @param source source exchange.
388     * @param result target exchange.
389     */
390    public static void copyResultsPreservePattern(Exchange result, Exchange source) {
391
392        // --------------------------------------------------------------------
393        //  TODO: merge logic with that of copyResults()
394        // --------------------------------------------------------------------
395
396        if (result == source) {
397            // we just need to ensure MEP is as expected (eg copy result to OUT if out capable)
398            // and the result is not failed
399            if (result.getPattern() == ExchangePattern.InOptionalOut) {
400                // keep as is
401            } else if (result.getPattern().isOutCapable() && !result.hasOut() && !result.isFailed()) {
402                // copy IN to OUT as we expect a OUT response
403                result.getOut().copyFrom(source.getIn());
404            }
405            return;
406        }
407
408        // copy in message
409        result.getIn().copyFrom(source.getIn());
410
411        // copy out message
412        if (source.hasOut()) {
413            // exchange pattern sensitive
414            Message resultMessage = source.getOut().isFault() ? result.getOut() : getResultMessage(result);
415            resultMessage.copyFrom(source.getOut());
416        }
417
418        // copy exception
419        result.setException(source.getException());
420
421        // copy properties
422        if (source.hasProperties()) {
423            result.getProperties().putAll(source.getProperties());
424        }
425    }
426
427    /**
428     * Returns the message where to write results in an
429     * exchange-pattern-sensitive way.
430     *
431     * @param exchange message exchange.
432     * @return result message.
433     */
434    public static Message getResultMessage(Exchange exchange) {
435        if (exchange.getPattern().isOutCapable()) {
436            return exchange.getOut();
437        } else {
438            return exchange.getIn();
439        }
440    }
441
442    /**
443     * Returns true if the given exchange pattern (if defined) can support OUT messages
444     *
445     * @param exchange the exchange to interrogate
446     * @return true if the exchange is defined as an {@link ExchangePattern} which supports
447     *         OUT messages
448     */
449    public static boolean isOutCapable(Exchange exchange) {
450        ExchangePattern pattern = exchange.getPattern();
451        return pattern != null && pattern.isOutCapable();
452    }
453
454    /**
455     * Creates a new instance of the given type from the injector
456     *
457     * @param exchange the exchange
458     * @param type     the given type
459     * @return the created instance of the given type
460     */
461    public static <T> T newInstance(Exchange exchange, Class<T> type) {
462        return exchange.getContext().getInjector().newInstance(type);
463    }
464
465    /**
466     * Creates a Map of the variables which are made available to a script or template
467     *
468     * @param exchange the exchange to make available
469     * @return a Map populated with the require variables
470     */
471    public static Map<String, Object> createVariableMap(Exchange exchange) {
472        Map<String, Object> answer = new HashMap<String, Object>();
473        populateVariableMap(exchange, answer);
474        return answer;
475    }
476
477    /**
478     * Populates the Map with the variables which are made available to a script or template
479     *
480     * @param exchange the exchange to make available
481     * @param map      the map to populate
482     */
483    public static void populateVariableMap(Exchange exchange, Map<String, Object> map) {
484        map.put("exchange", exchange);
485        Message in = exchange.getIn();
486        map.put("in", in);
487        map.put("request", in);
488        map.put("headers", in.getHeaders());
489        map.put("body", in.getBody());
490        if (isOutCapable(exchange)) {
491            // if we are out capable then set out and response as well
492            // however only grab OUT if it exists, otherwise reuse IN
493            // this prevents side effects to alter the Exchange if we force creating an OUT message
494            Message msg = exchange.hasOut() ? exchange.getOut() : exchange.getIn();
495            map.put("out", msg);
496            map.put("response", msg);
497        }
498        map.put("camelContext", exchange.getContext());
499    }
500
501    /**
502     * Returns the MIME content type on the input message or null if one is not defined
503     *
504     * @param exchange the exchange
505     * @return the MIME content type
506     */
507    public static String getContentType(Exchange exchange) {
508        return MessageHelper.getContentType(exchange.getIn());
509    }
510
511    /**
512     * Returns the MIME content encoding on the input message or null if one is not defined
513     *
514     * @param exchange the exchange
515     * @return the MIME content encoding
516     */
517    public static String getContentEncoding(Exchange exchange) {
518        return MessageHelper.getContentEncoding(exchange.getIn());
519    }
520
521    /**
522     * Performs a lookup in the registry of the mandatory bean name and throws an exception if it could not be found
523     *
524     * @param exchange the exchange
525     * @param name     the bean name
526     * @return the bean
527     * @throws NoSuchBeanException if no bean could be found in the registry
528     */
529    public static Object lookupMandatoryBean(Exchange exchange, String name) throws NoSuchBeanException {
530        Object value = lookupBean(exchange, name);
531        if (value == null) {
532            throw new NoSuchBeanException(name);
533        }
534        return value;
535    }
536
537    /**
538     * Performs a lookup in the registry of the mandatory bean name and throws an exception if it could not be found
539     *
540     * @param exchange the exchange
541     * @param name     the bean name
542     * @param type     the expected bean type
543     * @return the bean
544     * @throws NoSuchBeanException if no bean could be found in the registry
545     */
546    public static <T> T lookupMandatoryBean(Exchange exchange, String name, Class<T> type) {
547        T value = lookupBean(exchange, name, type);
548        if (value == null) {
549            throw new NoSuchBeanException(name);
550        }
551        return value;
552    }
553
554    /**
555     * Performs a lookup in the registry of the bean name
556     *
557     * @param exchange the exchange
558     * @param name     the bean name
559     * @return the bean, or <tt>null</tt> if no bean could be found
560     */
561    public static Object lookupBean(Exchange exchange, String name) {
562        return exchange.getContext().getRegistry().lookupByName(name);
563    }
564
565    /**
566     * Performs a lookup in the registry of the bean name and type
567     *
568     * @param exchange the exchange
569     * @param name     the bean name
570     * @param type     the expected bean type
571     * @return the bean, or <tt>null</tt> if no bean could be found
572     */
573    public static <T> T lookupBean(Exchange exchange, String name, Class<T> type) {
574        return exchange.getContext().getRegistry().lookupByNameAndType(name, type);
575    }
576
577    /**
578     * Returns the first exchange in the given collection of exchanges which has the same exchange ID as the one given
579     * or null if none could be found
580     *
581     * @param exchanges  the exchanges
582     * @param exchangeId the exchangeId to find
583     * @return matching exchange, or <tt>null</tt> if none found
584     */
585    public static Exchange getExchangeById(Iterable<Exchange> exchanges, String exchangeId) {
586        for (Exchange exchange : exchanges) {
587            String id = exchange.getExchangeId();
588            if (id != null && id.equals(exchangeId)) {
589                return exchange;
590            }
591        }
592        return null;
593    }
594
595    /**
596     * Prepares the exchanges for aggregation.
597     * <p/>
598     * This implementation will copy the OUT body to the IN body so when you do
599     * aggregation the body is <b>only</b> in the IN body to avoid confusing end users.
600     *
601     * @param oldExchange the old exchange
602     * @param newExchange the new exchange
603     */
604    public static void prepareAggregation(Exchange oldExchange, Exchange newExchange) {
605        // move body/header from OUT to IN
606        if (oldExchange != null) {
607            if (oldExchange.hasOut()) {
608                oldExchange.setIn(oldExchange.getOut());
609                oldExchange.setOut(null);
610            }
611        }
612
613        if (newExchange != null) {
614            if (newExchange.hasOut()) {
615                newExchange.setIn(newExchange.getOut());
616                newExchange.setOut(null);
617            }
618        }
619    }
620
621    /**
622     * Checks whether the exchange has been failure handed
623     *
624     * @param exchange  the exchange
625     * @return <tt>true</tt> if failure handled, <tt>false</tt> otherwise
626     */
627    public static boolean isFailureHandled(Exchange exchange) {
628        return exchange.getProperty(Exchange.FAILURE_HANDLED, false, Boolean.class);
629    }
630
631    /**
632     * Checks whether the exchange {@link UnitOfWork} is exhausted
633     *
634     * @param exchange  the exchange
635     * @return <tt>true</tt> if exhausted, <tt>false</tt> otherwise
636     */
637    public static boolean isUnitOfWorkExhausted(Exchange exchange) {
638        return exchange.getProperty(Exchange.UNIT_OF_WORK_EXHAUSTED, false, Boolean.class);
639    }
640
641    /**
642     * Sets the exchange to be failure handled.
643     *
644     * @param exchange  the exchange
645     */
646    public static void setFailureHandled(Exchange exchange) {
647        exchange.setProperty(Exchange.FAILURE_HANDLED, Boolean.TRUE);
648        // clear exception since its failure handled
649        exchange.setException(null);
650    }
651
652    /**
653     * Checks whether the exchange is redelivery exhausted
654     *
655     * @param exchange  the exchange
656     * @return <tt>true</tt> if exhausted, <tt>false</tt> otherwise
657     */
658    public static boolean isRedeliveryExhausted(Exchange exchange) {
659        return exchange.getProperty(Exchange.REDELIVERY_EXHAUSTED, false, Boolean.class);
660    }
661
662    /**
663     * Checks whether the exchange {@link UnitOfWork} is redelivered
664     *
665     * @param exchange  the exchange
666     * @return <tt>true</tt> if redelivered, <tt>false</tt> otherwise
667     */
668    public static boolean isRedelivered(Exchange exchange) {
669        return exchange.getIn().hasHeaders() && exchange.getIn().getHeader(Exchange.REDELIVERED, false, Boolean.class);
670    }
671
672    /**
673     * Checks whether the exchange {@link UnitOfWork} has been interrupted during processing
674     *
675     * @param exchange  the exchange
676     * @return <tt>true</tt> if interrupted, <tt>false</tt> otherwise
677     */
678    public static boolean isInterrupted(Exchange exchange) {
679        Object value = exchange.getProperty(Exchange.INTERRUPTED);
680        return value != null && Boolean.TRUE == value;
681    }
682
683    /**
684     * Check whether or not stream caching is enabled for the given route or globally.
685     *
686     * @param exchange  the exchange
687     * @return <tt>true</tt> if enabled, <tt>false</tt> otherwise
688     */
689    public static boolean isStreamCachingEnabled(final Exchange exchange) {
690        if (exchange.getFromRouteId() == null) {
691            return exchange.getContext().getStreamCachingStrategy().isEnabled();
692        } else {
693            return exchange.getContext().getRoute(exchange.getFromRouteId()).getRouteContext().isStreamCaching();
694        }
695    }
696
697    /**
698     * Extracts the body from the given exchange.
699     * <p/>
700     * If the exchange pattern is provided it will try to honor it and retrieve the body
701     * from either IN or OUT according to the pattern.
702     *
703     * @param exchange the exchange
704     * @param pattern  exchange pattern if given, can be <tt>null</tt>
705     * @return the result body, can be <tt>null</tt>.
706     * @throws CamelExecutionException is thrown if the processing of the exchange failed
707     */
708    public static Object extractResultBody(Exchange exchange, ExchangePattern pattern) {
709        Object answer = null;
710        if (exchange != null) {
711            // rethrow if there was an exception during execution
712            if (exchange.getException() != null) {
713                throw ObjectHelper.wrapCamelExecutionException(exchange, exchange.getException());
714            }
715
716            // result could have a fault message
717            if (hasFaultMessage(exchange)) {
718                Message msg = exchange.hasOut() ? exchange.getOut() : exchange.getIn();
719                answer = msg.getBody();
720                return answer;
721            }
722
723            // okay no fault then return the response according to the pattern
724            // try to honor pattern if provided
725            boolean notOut = pattern != null && !pattern.isOutCapable();
726            boolean hasOut = exchange.hasOut();
727            if (hasOut && !notOut) {
728                // we have a response in out and the pattern is out capable
729                answer = exchange.getOut().getBody();
730            } else if (!hasOut && exchange.getPattern() == ExchangePattern.InOptionalOut) {
731                // special case where the result is InOptionalOut and with no OUT response
732                // so we should return null to indicate this fact
733                answer = null;
734            } else {
735                // use IN as the response
736                answer = exchange.getIn().getBody();
737            }
738        }
739        return answer;
740    }
741
742    /**
743     * Tests whether the exchange has a fault message set and that its not null.
744     *
745     * @param exchange the exchange
746     * @return <tt>true</tt> if fault message exists
747     */
748    public static boolean hasFaultMessage(Exchange exchange) {
749        Message msg = exchange.hasOut() ? exchange.getOut() : exchange.getIn();
750        return msg.isFault() && msg.getBody() != null;
751    }
752
753    /**
754     * Tests whether the exchange has already been handled by the error handler
755     *
756     * @param exchange the exchange
757     * @return <tt>true</tt> if handled already by error handler, <tt>false</tt> otherwise
758     */
759    public static boolean hasExceptionBeenHandledByErrorHandler(Exchange exchange) {
760        return Boolean.TRUE.equals(exchange.getProperty(Exchange.ERRORHANDLER_HANDLED));
761    }
762
763    /**
764     * Extracts the body from the given future, that represents a handle to an asynchronous exchange.
765     * <p/>
766     * Will wait until the future task is complete.
767     *
768     * @param context the camel context
769     * @param future  the future handle
770     * @param type    the expected body response type
771     * @return the result body, can be <tt>null</tt>.
772     * @throws CamelExecutionException is thrown if the processing of the exchange failed
773     */
774    public static <T> T extractFutureBody(CamelContext context, Future<?> future, Class<T> type) {
775        try {
776            return doExtractFutureBody(context, future.get(), type);
777        } catch (InterruptedException e) {
778            throw ObjectHelper.wrapRuntimeCamelException(e);
779        } catch (ExecutionException e) {
780            // execution failed due to an exception so rethrow the cause
781            throw ObjectHelper.wrapCamelExecutionException(null, e.getCause());
782        } finally {
783            // its harmless to cancel if task is already completed
784            // and in any case we do not want to get hold of the task a 2nd time
785            // and its recommended to cancel according to Brian Goetz in his Java Concurrency in Practice book
786            future.cancel(true);
787        }
788    }
789
790    /**
791     * Extracts the body from the given future, that represents a handle to an asynchronous exchange.
792     * <p/>
793     * Will wait for the future task to complete, but waiting at most the timeout value.
794     *
795     * @param context the camel context
796     * @param future  the future handle
797     * @param timeout timeout value
798     * @param unit    timeout unit
799     * @param type    the expected body response type
800     * @return the result body, can be <tt>null</tt>.
801     * @throws CamelExecutionException is thrown if the processing of the exchange failed
802     * @throws java.util.concurrent.TimeoutException is thrown if a timeout triggered
803     */
804    public static <T> T extractFutureBody(CamelContext context, Future<?> future, long timeout, TimeUnit unit, Class<T> type) throws TimeoutException {
805        try {
806            if (timeout > 0) {
807                return doExtractFutureBody(context, future.get(timeout, unit), type);
808            } else {
809                return doExtractFutureBody(context, future.get(), type);
810            }
811        } catch (InterruptedException e) {
812            // execution failed due interruption so rethrow the cause
813            throw ObjectHelper.wrapCamelExecutionException(null, e);
814        } catch (ExecutionException e) {
815            // execution failed due to an exception so rethrow the cause
816            throw ObjectHelper.wrapCamelExecutionException(null, e.getCause());
817        } finally {
818            // its harmless to cancel if task is already completed
819            // and in any case we do not want to get hold of the task a 2nd time
820            // and its recommended to cancel according to Brian Goetz in his Java Concurrency in Practice book
821            future.cancel(true);
822        }
823    }
824
825    private static <T> T doExtractFutureBody(CamelContext context, Object result, Class<T> type) {
826        if (result == null) {
827            return null;
828        }
829        if (type.isAssignableFrom(result.getClass())) {
830            return type.cast(result);
831        }
832        if (result instanceof Exchange) {
833            Exchange exchange = (Exchange) result;
834            Object answer = ExchangeHelper.extractResultBody(exchange, exchange.getPattern());
835            return context.getTypeConverter().convertTo(type, exchange, answer);
836        }
837        return context.getTypeConverter().convertTo(type, result);
838    }
839
840    /**
841     * @deprecated use org.apache.camel.CamelExchangeException.createExceptionMessage instead
842     */
843    @Deprecated
844    public static String createExceptionMessage(String message, Exchange exchange, Throwable cause) {
845        return CamelExchangeException.createExceptionMessage(message, exchange, cause);
846    }
847
848    /**
849     * Strategy to prepare results before next iterator or when we are complete,
850     * which is done by copying OUT to IN, so there is only an IN as input
851     * for the next iteration.
852     *
853     * @param exchange the exchange to prepare
854     */
855    public static void prepareOutToIn(Exchange exchange) {
856        // we are routing using pipes and filters so we need to manually copy OUT to IN
857        if (exchange.hasOut()) {
858            exchange.setIn(exchange.getOut());
859            exchange.setOut(null);
860        }
861    }
862
863    /**
864     * Gets both the messageId and exchangeId to be used for logging purposes.
865     * <p/>
866     * Logging both ids, can help to correlate exchanges which may be redelivered messages
867     * from for example a JMS broker.
868     *
869     * @param exchange the exchange
870     * @return a log message with both the messageId and exchangeId
871     */
872    public static String logIds(Exchange exchange) {
873        String msgId = exchange.hasOut() ? exchange.getOut().getMessageId() : exchange.getIn().getMessageId();
874        return "(MessageId: " + msgId + " on ExchangeId: " + exchange.getExchangeId()  + ")";
875    }
876
877    /**
878     * Copies the exchange but the copy will be tied to the given context
879     *
880     * @param exchange  the source exchange
881     * @param context   the camel context
882     * @return a copy with the given camel context
883     */
884    public static Exchange copyExchangeAndSetCamelContext(Exchange exchange, CamelContext context) {
885        return copyExchangeAndSetCamelContext(exchange, context, true);
886    }
887
888    /**
889     * Copies the exchange but the copy will be tied to the given context
890     *
891     * @param exchange  the source exchange
892     * @param context   the camel context
893     * @param handover  whether to handover on completions from the source to the copy
894     * @return a copy with the given camel context
895     */
896    public static Exchange copyExchangeAndSetCamelContext(Exchange exchange, CamelContext context, boolean handover) {
897        DefaultExchange answer = new DefaultExchange(context, exchange.getPattern());
898        if (exchange.hasProperties()) {
899            answer.setProperties(safeCopyProperties(exchange.getProperties()));
900        }
901        if (handover) {
902            // Need to hand over the completion for async invocation
903            exchange.handoverCompletions(answer);
904        }
905        answer.setIn(exchange.getIn().copy());
906        if (exchange.hasOut()) {
907            answer.setOut(exchange.getOut().copy());
908        }
909        answer.setException(exchange.getException());
910        return answer;
911    }
912
913    /**
914     * Replaces the existing message with the new message
915     *
916     * @param exchange  the exchange
917     * @param newMessage the new message
918     * @param outOnly    whether to replace the message as OUT message
919     */
920    public static void replaceMessage(Exchange exchange, Message newMessage, boolean outOnly) {
921        Message old = exchange.hasOut() ? exchange.getOut() : exchange.getIn();
922        if (outOnly || exchange.hasOut()) {
923            exchange.setOut(newMessage);
924        } else {
925            exchange.setIn(newMessage);
926        }
927
928        // need to de-reference old from the exchange so it can be GC
929        if (old instanceof MessageSupport) {
930            ((MessageSupport) old).setExchange(null);
931        }
932    }
933
934    /**
935     * Gets the original IN {@link Message} this Unit of Work was started with.
936     * <p/>
937     * The original message is only returned if the option {@link org.apache.camel.RuntimeConfiguration#isAllowUseOriginalMessage()}
938     * is enabled. If its disabled, then <tt>null</tt> is returned.
939     *
940     * @return the original IN {@link Message}, or <tt>null</tt> if using original message is disabled.
941     */
942    public static Message getOriginalInMessage(Exchange exchange) {
943        Message answer = null;
944
945        // try parent first
946        UnitOfWork uow = exchange.getProperty(Exchange.PARENT_UNIT_OF_WORK, UnitOfWork.class);
947        if (uow != null) {
948            answer = uow.getOriginalInMessage();
949        }
950        // fallback to the current exchange
951        if (answer == null) {
952            uow = exchange.getUnitOfWork();
953            if (uow != null) {
954                answer = uow.getOriginalInMessage();
955            }
956        }
957        return answer;
958    }
959
960    @SuppressWarnings("unchecked")
961    private static Map<String, Object> safeCopyProperties(Map<String, Object> properties) {
962        if (properties == null) {
963            return null;
964        }
965
966        Map<String, Object> answer = new HashMap<>(properties);
967
968        // safe copy message history using a defensive copy
969        List<MessageHistory> history = (List<MessageHistory>) answer.remove(Exchange.MESSAGE_HISTORY);
970        if (history != null) {
971            answer.put(Exchange.MESSAGE_HISTORY, new LinkedList<>(history));
972        }
973
974        return answer;
975    }
976}