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