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.util;
018    
019    import java.util.HashMap;
020    import java.util.Map;
021    import java.util.concurrent.ExecutionException;
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.InvalidPayloadException;
032    import org.apache.camel.Message;
033    import org.apache.camel.NoSuchBeanException;
034    import org.apache.camel.NoSuchEndpointException;
035    import org.apache.camel.NoSuchHeaderException;
036    import org.apache.camel.NoSuchPropertyException;
037    import org.apache.camel.NoTypeConversionAvailableException;
038    import org.apache.camel.TypeConverter;
039    
040    /**
041     * Some helper methods for working with {@link Exchange} objects
042     *
043     * @version $Revision: 790811 $
044     */
045    public final class ExchangeHelper {
046    
047        /**
048         * Utility classes should not have a public constructor.
049         */
050        private ExchangeHelper() {
051        }
052    
053        /**
054         * Extracts the exchange property of the given name and type; if it is not present then the
055         * default value will be used
056         *
057         * @param exchange the message exchange
058         * @param propertyName the name of the property on the exchange
059         * @param type the expected type of the property
060         * @param defaultValue the default value to be used if the property name does not exist or could not be
061         * converted to the given type
062         * @return the property value as the given type or the defaultValue if it could not be found or converted
063         */
064        public static <T> T getExchangeProperty(Exchange exchange, String propertyName, Class<T> type, T defaultValue) {
065            T answer = exchange.getProperty(propertyName, type);
066            if (answer == null) {
067                return defaultValue;
068            }
069            return answer;
070        }
071    
072        /**
073         * Extracts the Exchange.BINDING of the given type or null if not present
074         *
075         * @param exchange the message exchange
076         * @param type the expected binding type
077         * @return the binding object of the given type or null if it could not be found or converted
078         */
079        public static <T> T getBinding(Exchange exchange, Class<T> type) {
080            return exchange != null ? (T) exchange.getProperty(Exchange.BINDING, type) : null;
081        }
082    
083        /**
084         * Attempts to resolve the endpoint for the given value
085         *
086         * @param exchange the message exchange being processed
087         * @param value the value which can be an {@link Endpoint} or an object
088         *                which provides a String representation of an endpoint via
089         *                {@link #toString()}
090         *
091         * @return the endpoint
092         * @throws NoSuchEndpointException if the endpoint cannot be resolved
093         */
094        public static Endpoint resolveEndpoint(Exchange exchange, Object value)
095            throws NoSuchEndpointException {
096            Endpoint endpoint;
097            if (value instanceof Endpoint) {
098                endpoint = (Endpoint)value;
099            } else {
100                String uri = value.toString();
101                endpoint = CamelContextHelper.getMandatoryEndpoint(exchange.getContext(), uri);
102            }
103            return endpoint;
104        }
105    
106        public static <T> T getMandatoryProperty(Exchange exchange, String propertyName, Class<T> type)
107            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        public static <T> T getMandatoryHeader(Exchange exchange, String propertyName, Class<T> type)
116            throws NoSuchHeaderException {
117            T answer = exchange.getIn().getHeader(propertyName, type);
118            if (answer == null) {
119                throw new NoSuchHeaderException(exchange, propertyName, type);
120            }
121            return answer;
122        }
123    
124        /**
125         * Returns the mandatory inbound message body of the correct type or throws
126         * an exception if it is not present
127         */
128        public static Object getMandatoryInBody(Exchange exchange) throws InvalidPayloadException {
129            return exchange.getIn().getMandatoryBody();
130        }
131    
132        /**
133         * Returns the mandatory inbound message body of the correct type or throws
134         * an exception if it is not present
135         */
136        public static <T> T getMandatoryInBody(Exchange exchange, Class<T> type) throws InvalidPayloadException {
137            return exchange.getIn().getMandatoryBody(type);
138        }
139    
140        /**
141         * Returns the mandatory outbound message body of the correct type or throws
142         * an exception if it is not present
143         */
144        public static Object getMandatoryOutBody(Exchange exchange) throws InvalidPayloadException {
145            return exchange.getOut().getMandatoryBody();
146        }
147    
148        /**
149         * Returns the mandatory outbound message body of the correct type or throws
150         * an exception if it is not present
151         */
152        public static <T> T getMandatoryOutBody(Exchange exchange, Class<T> type) throws InvalidPayloadException {
153            return exchange.getOut().getMandatoryBody(type);
154        }
155    
156        /**
157         * Converts the value to the given expected type or throws an exception
158         */
159        public static <T> T convertToMandatoryType(Exchange exchange, Class<T> type, Object value) throws NoTypeConversionAvailableException {
160            CamelContext camelContext = exchange.getContext();
161            TypeConverter converter = camelContext.getTypeConverter();
162            if (converter != null) {
163                return converter.mandatoryConvertTo(type, exchange, value);
164            }
165            throw new NoTypeConversionAvailableException(value, type);
166        }
167    
168        /**
169         * Converts the value to the given expected type returning null if it could
170         * not be converted
171         */
172        public static <T> T convertToType(Exchange exchange, Class<T> type, Object value) {
173            CamelContext camelContext = exchange.getContext();
174            TypeConverter converter = camelContext.getTypeConverter();
175            if (converter != null) {
176                return converter.convertTo(type, exchange, value);
177            }
178            return null;
179        }
180    
181        /**
182         * Copies the results of a message exchange from the source exchange to the result exchange
183         * which will copy the out and fault message contents and the exception
184         *
185         * @param result the result exchange which will have the output and error state added
186         * @param source the source exchange which is not modified
187         */
188        public static void copyResults(Exchange result, Exchange source) {
189    
190            // --------------------------------------------------------------------
191            //  TODO: merge logic with that of copyResultsPreservePattern()
192            // --------------------------------------------------------------------
193            
194            if (result != source) {
195                result.setException(source.getException());
196                if (source.hasFault()) {
197                    result.getFault().copyFrom(source.getFault());
198                }
199    
200                if (source.hasOut()) {
201                    result.getOut().copyFrom(source.getOut());
202                } else if (result.getPattern() == ExchangePattern.InOptionalOut) {
203                    // special case where the result is InOptionalOut and with no OUT response
204                    // so we should return null to indicate this fact
205                    result.setOut(null);
206                } else {
207                    // no results so lets copy the last input
208                    // as the final processor on a pipeline might not
209                    // have created any OUT; such as a mock:endpoint
210                    // so lets assume the last IN is the OUT
211                    if (result.getPattern().isOutCapable()) {
212                        // only set OUT if its OUT capable
213                        result.getOut().copyFrom(source.getIn());
214                    } else {
215                        // if not replace IN instead to keep the MEP
216                        result.getIn().copyFrom(source.getIn());
217                    }
218                }
219                result.getProperties().clear();
220                result.getProperties().putAll(source.getProperties());
221            }
222        }
223    
224        /**
225         * Copies the <code>source</code> exchange to <code>target</code> exchange
226         * preserving the {@link ExchangePattern} of <code>target</code>.  
227         * 
228         * @param source source exchange.
229         * @param result target exchange.
230         */
231        public static void copyResultsPreservePattern(Exchange result, Exchange source) {
232    
233            // --------------------------------------------------------------------
234            //  TODO: merge logic with that of copyResults()
235            // --------------------------------------------------------------------
236            
237            if (source == result) {
238                // no need to copy
239                return;
240            }
241            
242            // copy in message
243            result.getIn().copyFrom(source.getIn());
244        
245            // copy out message
246            if (source.hasOut()) {
247                // exchange pattern sensitive
248                getResultMessage(result).copyFrom(source.getOut());
249            }
250            
251            // copy fault message
252            if (source.hasFault()) {
253                result.getFault().copyFrom(source.getFault());
254            }
255            
256            // copy exception
257            result.setException(source.getException());
258            
259            // copy properties
260            result.getProperties().clear();
261            result.getProperties().putAll(source.getProperties());
262        }
263    
264        /**
265         * Returns the message where to write results in an
266         * exchange-pattern-sensitive way.
267         * 
268         * @param exchange
269         *            message exchange.
270         * @return result message.
271         */
272        public static Message getResultMessage(Exchange exchange) {
273            if (exchange.getPattern().isOutCapable()) {
274                return exchange.getOut();
275            } else {
276                return exchange.getIn();
277            }
278        }
279    
280        /**
281         * Returns true if the given exchange pattern (if defined) can support IN messagea
282         *
283         * @param exchange the exchange to interrogate
284         * @return true if the exchange is defined as an {@link ExchangePattern} which supports
285         * IN messages
286         */
287        public static boolean isInCapable(Exchange exchange) {
288            ExchangePattern pattern = exchange.getPattern();
289            return pattern != null && pattern.isInCapable();
290        }
291    
292        /**
293         * Returns true if the given exchange pattern (if defined) can support OUT messagea
294         *
295         * @param exchange the exchange to interrogate
296         * @return true if the exchange is defined as an {@link ExchangePattern} which supports
297         * OUT messages
298         */
299        public static boolean isOutCapable(Exchange exchange) {
300            ExchangePattern pattern = exchange.getPattern();
301            return pattern != null && pattern.isOutCapable();
302        }
303    
304        /**
305         * Creates a new instance of the given type from the injector
306         */
307        public static <T> T newInstance(Exchange exchange, Class<T> type) {
308            return exchange.getContext().getInjector().newInstance(type);
309        }
310    
311        /**
312         * Creates a Map of the variables which are made available to a script or template
313         *
314         * @param exchange the exchange to make available
315         * @return a Map populated with the require dvariables
316         */
317        public static Map createVariableMap(Exchange exchange) {
318            Map answer = new HashMap();
319            populateVariableMap(exchange, answer);
320            return answer;
321        }
322    
323        /**
324         * Populates the Map with the variables which are made available to a script or template
325         *
326         * @param exchange the exchange to make available
327         * @param map      the map to populate
328         */
329        @SuppressWarnings("unchecked")
330        public static void populateVariableMap(Exchange exchange, Map map) {
331            map.put("exchange", exchange);
332            Message in = exchange.getIn();
333            map.put("in", in);
334            map.put("request", in);
335            map.put("headers", in.getHeaders());
336            map.put("body", in.getBody());
337            if (isOutCapable(exchange)) {
338                Message out = exchange.getOut();
339                map.put("out", out);
340                map.put("response", out);
341            }
342            map.put("camelContext", exchange.getContext());
343        }
344    
345        /**
346         * Returns the MIME content type on the input message or null if one is not defined
347         */
348        public static String getContentType(Exchange exchange) {
349            return MessageHelper.getContentType(exchange.getIn());        
350        }
351    
352        /**
353         * Returns the MIME content encoding on the input message or null if one is not defined
354         */
355        public static String getContentEncoding(Exchange exchange) {
356            return MessageHelper.getContentEncoding(exchange.getIn());
357        }
358    
359        /**
360         * Performs a lookup in the registry of the mandatory bean name and throws an exception if it could not be found
361         */
362        public static Object lookupMandatoryBean(Exchange exchange, String name) {
363            Object value = lookupBean(exchange, name);
364            if (value == null) {
365                throw new NoSuchBeanException(name);
366            }
367            return value;
368        }
369    
370        /**
371         * Performs a lookup in the registry of the mandatory bean name and throws an exception if it could not be found
372         */
373        public static <T> T lookupMandatoryBean(Exchange exchange, String name, Class<T> type) {
374            T value = lookupBean(exchange, name, type);
375            if (value == null) {
376                throw new NoSuchBeanException(name);
377            }
378            return value;
379        }
380    
381        /**
382         * Performs a lookup in the registry of the bean name
383         */
384        public static Object lookupBean(Exchange exchange, String name) {
385            return exchange.getContext().getRegistry().lookup(name);
386        }
387    
388        /**
389         * Performs a lookup in the registry of the bean name and type
390         */
391        public static <T> T lookupBean(Exchange exchange, String name, Class<T> type) {
392            return exchange.getContext().getRegistry().lookup(name, type);
393        }
394    
395        /**
396         * Returns the first exchange in the given collection of exchanges which has the same exchange ID as the one given
397         * or null if none could be found
398         */
399        public static Exchange getExchangeById(Iterable<Exchange> exchanges, String exchangeId) {
400            for (Exchange exchange : exchanges) {
401                String id = exchange.getExchangeId();
402                if (id != null && id.equals(exchangeId)) {
403                    return exchange;
404                }
405            }
406            return null;
407        }
408    
409        /**
410         * Prepares the exchanges for aggregation.
411         * <p/>
412         * This implementation will copy the OUT body to the IN body so when you do
413         * aggregation the body is <b>only</b> in the IN body to avoid confusing end users.
414         *
415         * @param oldExchange  the old exchange
416         * @param newExchange  the new exchange
417         */
418        public static void prepareAggregation(Exchange oldExchange, Exchange newExchange) {
419            // copy body/header from OUT to IN
420            if (oldExchange != null) {
421                if (oldExchange.hasOut()) {
422                    oldExchange.getIn().copyFrom(oldExchange.getOut());
423                    oldExchange.setOut(null);
424                }
425            }
426    
427            if (newExchange != null) {
428                if (newExchange.hasOut()) {
429                    newExchange.getIn().copyFrom(newExchange.getOut());
430                    newExchange.setOut(null);
431                }
432            }
433        }
434    
435        public static boolean isFailureHandled(Exchange exchange) {
436            Boolean handled = exchange.getProperty(Exchange.FAILURE_HANDLED, Boolean.class);
437            return handled != null && handled;
438        }
439    
440        public static void setFailureHandled(Exchange exchange) {
441            exchange.setProperty(Exchange.FAILURE_HANDLED, Boolean.TRUE);
442            // clear exception since its failure handled
443            exchange.setException(null);
444        }
445    
446        /**
447         * Extracts the body from the given exchange.
448         * <p/>
449         * If the exchange pattern is provided it will try to honor it and retrive the body
450         * from either IN or OUT according to the pattern.
451         *
452         * @param exchange   the exchange
453         * @param pattern    exchange pattern if given, can be <tt>null</tt>
454         * @return the result body, can be <tt>null</tt>.
455         * @throws CamelExecutionException if the processing of the exchange failed
456         */
457        public static Object extractResultBody(Exchange exchange, ExchangePattern pattern) {
458            Object answer = null;
459            if (exchange != null) {
460                // rethrow if there was an exception during execution
461                if (exchange.getException() != null) {
462                    throw ObjectHelper.wrapCamelExecutionException(exchange, exchange.getException());
463                }
464    
465                // result could have a fault message
466                if (hasFaultMessage(exchange)) {
467                    return exchange.getFault().getBody();
468                }
469    
470                // okay no fault then return the response according to the pattern
471                // try to honor pattern if provided
472                boolean notOut = pattern != null && !pattern.isOutCapable();
473                boolean hasOut = exchange.hasOut();
474                if (hasOut && !notOut) {
475                    // we have a response in out and the pattern is out capable
476                    answer = exchange.getOut().getBody();
477                } else if (!hasOut && exchange.getPattern() == ExchangePattern.InOptionalOut) {
478                    // special case where the result is InOptionalOut and with no OUT response
479                    // so we should return null to indicate this fact
480                    answer = null;
481                } else {
482                    // use IN as the response
483                    answer = exchange.getIn().getBody();
484                }
485            }
486            return answer;
487        }
488    
489        /**
490         * Tests whether the exchange has a fault message set and that its not null.
491         *
492         * @param exchange  the exchange
493         * @return <tt>true</tt> if fault message exists
494         */
495        public static boolean hasFaultMessage(Exchange exchange) {
496            if (exchange.hasFault()) {
497                Object faultBody = exchange.getFault().getBody();
498                if (faultBody != null) {
499                    return true;
500                }
501            }
502            return false;
503        }
504    
505        /**
506         * Extracts the body from the given future, that represents a handle to an asynchronous exchange.
507         * <p/>
508         * Will wait until the future task is complete.
509         *
510         * @param context the camel context
511         * @param future the future handle
512         * @param type the expected body response type
513         * @return the result body, can be <tt>null</tt>.
514         * @throws CamelExecutionException if the processing of the exchange failed
515         */
516        public static <T> T extractFutureBody(CamelContext context, Future future, Class<T> type) {
517            try {
518                return doExtractFutureBody(context, future.get(), type);
519            } catch (InterruptedException e) {
520                throw ObjectHelper.wrapRuntimeCamelException(e);
521            } catch (ExecutionException e) {
522                // execution failed due to an exception so rethrow the cause
523                throw ObjectHelper.wrapCamelExecutionException(null, e.getCause());
524            } finally {
525                // its harmless to cancel if task is already completed
526                // and in any case we do not want to get hold of the task a 2nd time
527                // and its recommended to cancel according to Brian Goetz in his Java Concurrency in Practice book
528                future.cancel(true);
529            }
530        }
531    
532        /**
533         * Extracts the body from the given future, that represents a handle to an asynchronous exchange.
534         * <p/>
535         * Will wait for the future task to complete, but waiting at most the timeout value.
536         *
537         * @param context the camel context
538         * @param future the future handle
539         * @param timeout timeout value
540         * @param unit    timeout unit
541         * @param type the expected body response type
542         * @return the result body, can be <tt>null</tt>.
543         * @throws CamelExecutionException if the processing of the exchange failed
544         * @throws java.util.concurrent.TimeoutException is thrown if a timeout triggered
545         */
546        public static <T> T extractFutureBody(CamelContext context, Future future, long timeout, TimeUnit unit, Class<T> type) throws TimeoutException {
547            try {
548                if (timeout > 0) {
549                    return doExtractFutureBody(context, future.get(timeout, unit), type);
550                } else {
551                    return doExtractFutureBody(context, future.get(), type);
552                }
553            } catch (InterruptedException e) {
554                throw ObjectHelper.wrapRuntimeCamelException(e);
555            } catch (ExecutionException e) {
556                // execution failed due to an exception so rethrow the cause
557                throw ObjectHelper.wrapCamelExecutionException(null, e.getCause());
558            } finally {
559                // its harmless to cancel if task is already completed
560                // and in any case we do not want to get hold of the task a 2nd time
561                // and its recommended to cancel according to Brian Goetz in his Java Concurrency in Practice book
562                future.cancel(true);
563            }
564        }
565    
566        private static <T> T doExtractFutureBody(CamelContext context, Object result, Class<T> type) {
567            if (result == null) {
568                return null;
569            }
570            if (type.isAssignableFrom(result.getClass())) {
571                return type.cast(result);
572            }
573            if (result instanceof Exchange) {
574                Exchange exchange = (Exchange) result;
575                Object answer = ExchangeHelper.extractResultBody(exchange, exchange.getPattern());
576                return context.getTypeConverter().convertTo(type, answer);
577            }
578            return context.getTypeConverter().convertTo(type, result);
579        }
580    
581    }