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.io.Closeable;
020    import java.io.File;
021    import java.io.FileNotFoundException;
022    import java.io.IOException;
023    import java.io.InputStream;
024    import java.lang.annotation.Annotation;
025    import java.lang.reflect.AnnotatedElement;
026    import java.lang.reflect.InvocationTargetException;
027    import java.lang.reflect.Method;
028    import java.net.URL;
029    import java.nio.channels.ReadableByteChannel;
030    import java.nio.charset.Charset;
031    import java.util.ArrayList;
032    import java.util.Arrays;
033    import java.util.Collection;
034    import java.util.Collections;
035    import java.util.Iterator;
036    import java.util.List;
037    import java.util.Scanner;
038    
039    import org.w3c.dom.Node;
040    import org.w3c.dom.NodeList;
041    
042    import org.apache.camel.CamelExecutionException;
043    import org.apache.camel.Exchange;
044    import org.apache.camel.Message;
045    import org.apache.camel.RuntimeCamelException;
046    import org.apache.camel.TypeConverter;
047    import org.apache.camel.component.file.GenericFile;
048    import org.apache.commons.logging.Log;
049    import org.apache.commons.logging.LogFactory;
050    
051    /**
052     * A number of useful helper methods for working with Objects
053     *
054     * @version $Revision: 905171 $
055     */
056    public final class ObjectHelper {
057        private static final transient Log LOG = LogFactory.getLog(ObjectHelper.class);
058    
059        /**
060         * Utility classes should not have a public constructor.
061         */
062        private ObjectHelper() {
063        }
064    
065        /**
066         * A helper method for comparing objects for equality in which it uses type coerce to coerce
067         * types between the left and right values. This allows you to equal test eg String and Integer as
068         * Camel will be able to coerce the types
069         */
070        public static boolean typeCoerceEquals(TypeConverter converter, Object leftValue, Object rightValue) {
071            // try without type coerce
072            boolean answer = equal(leftValue, rightValue);
073            if (answer) {
074                return true;
075            }
076    
077            if (leftValue == null || rightValue == null) {
078                // no reason to continue as the first equal did not match and now one of the values is null
079                // so it wont help to type coerce to a null type
080                return false;
081            }
082    
083            // convert left to right
084            Object value = converter.convertTo(rightValue.getClass(), leftValue);
085            answer = equal(value, rightValue);
086            if (answer) {
087                return true;
088            }
089    
090            // convert right to left
091            value = converter.convertTo(leftValue.getClass(), rightValue);
092            answer = equal(leftValue, value);
093            return answer;
094        }
095    
096        /**
097         * A helper method for comparing objects for equality in which it uses type coerce to coerce
098         * types between the left and right values. This allows you to equal test eg String and Integer as
099         * Camel will be able to coerce the types
100         */
101        public static boolean typeCoerceNotEquals(TypeConverter converter, Object leftValue, Object rightValue) {
102            return !typeCoerceEquals(converter, leftValue, rightValue);
103        }
104    
105        /**
106         * A helper method for comparing objects ordering in which it uses type coerce to coerce
107         * types between the left and right values. This allows you to equal test eg String and Integer as
108         * Camel will be able to coerce the types
109         */
110        @SuppressWarnings("unchecked")
111        public static int typeCoerceCompare(TypeConverter converter, Object leftValue, Object rightValue) {
112            if (rightValue instanceof Comparable) {
113                Object value = converter.convertTo(rightValue.getClass(), leftValue);
114                if (value != null) {
115                    return ((Comparable) rightValue).compareTo(value) * -1;
116                }
117            }
118    
119            if (leftValue instanceof Comparable) {
120                Object value = converter.convertTo(leftValue.getClass(), rightValue);
121                if (value != null) {
122                    return ((Comparable) leftValue).compareTo(value);
123                }
124            }
125    
126            // use regular compare
127            return compare(leftValue, rightValue);
128        }
129    
130        /**
131         * A helper method for comparing objects for equality while handling nulls
132         */
133        public static boolean equal(Object a, Object b) {
134            if (a == b) {
135                return true;
136            }
137    
138            if (a instanceof byte[] && b instanceof byte[]) {
139                return equalByteArray((byte[])a, (byte[])b);
140            }
141    
142            return a != null && b != null && a.equals(b);
143        }
144    
145        /**
146         * A helper method for comparing byte arrays for equality while handling
147         * nulls
148         */
149        public static boolean equalByteArray(byte[] a, byte[] b) {
150            if (a == b) {
151                return true;
152            }
153    
154            // loop and compare each byte
155            if (a != null && b != null && a.length == b.length) {
156                for (int i = 0; i < a.length; i++) {
157                    if (a[i] != b[i]) {
158                        return false;
159                    }
160                }
161                // all bytes are equal
162                return true;
163            }
164    
165            return false;
166        }
167    
168        /**
169         * Returns true if the given object is equal to any of the expected value
170         */
171        public static boolean isEqualToAny(Object object, Object... values) {
172            for (Object value : values) {
173                if (equal(object, value)) {
174                    return true;
175                }
176            }
177            return false;
178        }
179    
180        /**
181         * A helper method for performing an ordered comparison on the objects
182         * handling nulls and objects which do not handle sorting gracefully
183         */
184        public static int compare(Object a, Object b) {
185            return compare(a, b, false);
186        }
187    
188        /**
189         * A helper method for performing an ordered comparison on the objects
190         * handling nulls and objects which do not handle sorting gracefully
191         *
192         * @param a  the first object
193         * @param b  the second object
194         * @param ignoreCase  ignore case for string comparison
195         */
196        @SuppressWarnings("unchecked")
197        public static int compare(Object a, Object b, boolean ignoreCase) {
198            if (a == b) {
199                return 0;
200            }
201            if (a == null) {
202                return -1;
203            }
204            if (b == null) {
205                return 1;
206            }
207            if (ignoreCase && a instanceof String && b instanceof String) {
208                return ((String) a).compareToIgnoreCase((String) b);
209            }
210            if (a instanceof Comparable) {
211                Comparable comparable = (Comparable)a;
212                return comparable.compareTo(b);
213            } else {
214                int answer = a.getClass().getName().compareTo(b.getClass().getName());
215                if (answer == 0) {
216                    answer = a.hashCode() - b.hashCode();
217                }
218                return answer;
219            }
220        }
221    
222        public static Boolean toBoolean(Object value) {
223            if (value instanceof Boolean) {
224                return (Boolean)value;
225            }
226            if (value instanceof String) {
227                return "true".equalsIgnoreCase(value.toString()) ? Boolean.TRUE : Boolean.FALSE;
228            }
229            if (value instanceof Integer) {
230                return (Integer)value > 0 ? Boolean.TRUE : Boolean.FALSE;
231            }
232            return null;
233        }
234    
235        /**
236         * Asserts whether the value is <b>not</b> <tt>null</tt>
237         *
238         * @param value  the value to test
239         * @param name   the key that resolved the value
240         * @throws IllegalArgumentException is thrown if assertion fails
241         */
242        public static void notNull(Object value, String name) {
243            if (value == null) {
244                throw new IllegalArgumentException(name + " must be specified");
245            }
246        }
247    
248        /**
249         * Asserts whether the value is <b>not</b> <tt>null</tt>
250         *
251         * @param value  the value to test
252         * @param on     additional description to indicate where this problem occured (appended as toString())
253         * @param name   the key that resolved the value
254         * @throws IllegalArgumentException is thrown if assertion fails
255         */
256        public static void notNull(Object value, String name, Object on) {
257            if (on == null) {
258                notNull(value, name);
259            } else if (value == null) {
260                throw new IllegalArgumentException(name + " must be specified on: " + on);
261            }
262        }
263    
264        /**
265         * Asserts whether the string is <b>not</b> empty.
266         *
267         * @param value  the string to test
268         * @param name   the key that resolved the value
269         * @throws IllegalArgumentException is thrown if assertion fails
270         */
271        public static void notEmpty(String value, String name) {
272            if (isEmpty(value)) {
273                throw new IllegalArgumentException(name + " must be specified and not empty");
274            }
275        }
276    
277        /**
278         * Asserts whether the string is <b>not</b> empty.
279         *
280         * @param value  the string to test
281         * @param on     additional description to indicate where this problem occured (appended as toString())
282         * @param name   the key that resolved the value
283         * @throws IllegalArgumentException is thrown if assertion fails
284         */
285        public static void notEmpty(String value, String name, Object on) {
286            if (on == null) {
287                notNull(value, name);
288            } else if (isEmpty(value)) {
289                throw new IllegalArgumentException(name + " must be specified and not empty on: " + on);
290            }
291        }
292    
293        /**
294         * Tests whether the value is <tt>null</tt> or an empty string.
295         *
296         * @param value  the value, if its a String it will be tested for text length as well
297         * @return true if empty
298         */
299        public static boolean isEmpty(Object value) {
300            return !isNotEmpty(value);
301        }
302    
303        /**
304         * Tests whether the value is <b>not</b> <tt>null</tt> or an empty string.
305         *
306         * @param value  the value, if its a String it will be tested for text length as well
307         * @return true if <b>not</b> empty
308         */
309        public static boolean isNotEmpty(Object value) {
310            if (value == null) {
311                return false;
312            } else if (value instanceof String) {
313                String text = (String) value;
314                return text.trim().length() > 0;
315            } else {
316                return true;
317            }
318        }
319    
320        public static String[] splitOnCharacter(String value, String needle, int count) {
321            String rc[] = new String[count];
322            rc[0] = value;
323            for (int i = 1; i < count; i++) {
324                String v = rc[i - 1];
325                int p = v.indexOf(needle);
326                if (p < 0) {
327                    return rc;
328                }
329                rc[i - 1] = v.substring(0, p);
330                rc[i] = v.substring(p + 1);
331            }
332            return rc;
333        }
334    
335        /**
336         * Removes any starting characters on the given text which match the given
337         * character
338         *
339         * @param text the string
340         * @param ch the initial characters to remove
341         * @return either the original string or the new substring
342         */
343        public static String removeStartingCharacters(String text, char ch) {
344            int idx = 0;
345            while (text.charAt(idx) == ch) {
346                idx++;
347            }
348            if (idx > 0) {
349                return text.substring(idx);
350            }
351            return text;
352        }
353    
354        public static String capitalize(String text) {
355            if (text == null) {
356                return null;
357            }
358            int length = text.length();
359            if (length == 0) {
360                return text;
361            }
362            String answer = text.substring(0, 1).toUpperCase();
363            if (length > 1) {
364                answer += text.substring(1, length);
365            }
366            return answer;
367        }
368    
369        public static String after(String text, String after) {
370            if (!text.contains(after)) {
371                return null;
372            }
373            return text.substring(text.indexOf(after) + after.length());
374        }
375    
376        public static String before(String text, String before) {
377            if (!text.contains(before)) {
378                return null;
379            }
380            return text.substring(0, text.indexOf(before));
381        }
382    
383        public static String between(String text, String after, String before) {
384            text = after(text, after);
385            if (text == null) {
386                return null;
387            }
388            return before(text, before);
389        }
390    
391        /**
392         * Returns true if the collection contains the specified value
393         */
394        @SuppressWarnings("unchecked")
395        public static boolean contains(Object collectionOrArray, Object value) {
396            if (collectionOrArray instanceof Collection) {
397                Collection collection = (Collection)collectionOrArray;
398                return collection.contains(value);
399            } else if (collectionOrArray instanceof String && value instanceof String) {
400                String str = (String)collectionOrArray;
401                String subStr = (String)value;
402                return str.contains(subStr);
403            } else {
404                Iterator iter = createIterator(collectionOrArray);
405                while (iter.hasNext()) {
406                    if (equal(value, iter.next())) {
407                        return true;
408                    }
409                }
410            }
411            return false;
412        }
413    
414        /**
415         * Creates an iterator over the value if the value is a collection, an
416         * Object[] or a primitive type array; otherwise to simplify the caller's
417         * code, we just create a singleton collection iterator over a single value
418         * <p/>
419         * Will default use comma for String separating String values.
420         *
421         * @param value  the value
422         * @return the iterator
423         */
424        public static Iterator<Object> createIterator(Object value) {
425            return createIterator(value, ",");
426        }
427    
428        /**
429         * Creates an iterator over the value if the value is a collection, an
430         * Object[] or a primitive type array; otherwise to simplify the caller's
431         * code, we just create a singleton collection iterator over a single value
432         *
433         * @param value  the value
434         * @param  delimiter  delimiter for separating String values
435         * @return the iterator
436         */
437        @SuppressWarnings("unchecked")
438        public static Iterator<Object> createIterator(Object value, String delimiter) {
439            // if its a message than we want to iterate its body
440            if (value instanceof Message) {
441                value = ((Message) value).getBody();
442            }
443    
444            if (value == null) {
445                return Collections.emptyList().iterator();
446            } else if (value instanceof Iterator) {
447                return (Iterator)value;
448            } else if (value instanceof Collection) {
449                Collection collection = (Collection)value;
450                return collection.iterator();
451            } else if (value.getClass().isArray()) {
452                // TODO we should handle primitive array types?
453                List<Object> list = Arrays.asList((Object[])value);
454                return list.iterator();
455            } else if (value instanceof NodeList) {
456                // lets iterate through DOM results after performing XPaths
457                final NodeList nodeList = (NodeList)value;
458                return CastUtils.cast(new Iterator<Node>() {
459                    int idx = -1;
460    
461                    public boolean hasNext() {
462                        return ++idx < nodeList.getLength();
463                    }
464    
465                    public Node next() {
466                        return nodeList.item(idx);
467                    }
468    
469                    public void remove() {
470                        throw new UnsupportedOperationException();
471                    }
472                });
473            } else if (value instanceof String) {
474                final String s = (String) value;
475    
476                // this code is optimized to only use a Scanner if needed, eg there is a delimiter
477    
478                if (s.contains(delimiter)) {
479                    // use a scanner if it contains the delimiter
480                    Scanner scanner = new Scanner((String)value);
481                    scanner.useDelimiter(delimiter);
482                    return CastUtils.cast(scanner);
483                } else {
484                    // use a plain iterator that returns the value as is as there are only a single value
485                    return CastUtils.cast(new Iterator<String>() {
486                        int idx = -1;
487    
488                        public boolean hasNext() {
489                            // empty string should not be regarded as having next
490                            return ++idx == 0 && ObjectHelper.isNotEmpty(s);
491                        }
492    
493                        public String next() {
494                            return s;
495                        }
496    
497                        public void remove() {
498                            throw new UnsupportedOperationException();
499                        }
500                    });
501                }
502            } else {
503                return Collections.singletonList(value).iterator();
504            }
505        }
506    
507        /**
508         * Returns the predicate matching boolean on a {@link List} result set where
509         * if the first element is a boolean its value is used otherwise this method
510         * returns true if the collection is not empty
511         *
512         * @return <tt>true</tt> if the first element is a boolean and its value
513         *         is true or if the list is non empty
514         */
515        public static boolean matches(List<Object> list) {
516            if (!list.isEmpty()) {
517                Object value = list.get(0);
518                if (value instanceof Boolean) {
519                    return (Boolean)value;
520                } else {
521                    // lets assume non-empty results are true
522                    return true;
523                }
524            }
525            return false;
526        }
527    
528        /**
529         * A helper method to access a system property, catching any security
530         * exceptions
531         *
532         * @param name the name of the system property required
533         * @param defaultValue the default value to use if the property is not
534         *                available or a security exception prevents access
535         * @return the system property value or the default value if the property is
536         *         not available or security does not allow its access
537         */
538        public static String getSystemProperty(String name, String defaultValue) {
539            try {
540                return System.getProperty(name, defaultValue);
541            } catch (Exception e) {
542                if (LOG.isDebugEnabled()) {
543                    LOG.debug("Caught security exception accessing system property: " + name + ". Reason: " + e, e);
544                }
545                return defaultValue;
546            }
547        }
548    
549        /**
550         * A helper method to access a boolean system property, catching any
551         * security exceptions
552         *
553         * @param name the name of the system property required
554         * @param defaultValue the default value to use if the property is not
555         *                available or a security exception prevents access
556         * @return the boolean representation of the system property value or the
557         *         default value if the property is not available or security does
558         *         not allow its access
559         */
560        public static boolean getSystemProperty(String name, Boolean defaultValue) {
561            String result = getSystemProperty(name, defaultValue.toString());
562            return Boolean.parseBoolean(result);
563        }
564    
565        /**
566         * Returns the type name of the given type or null if the type variable is
567         * null
568         */
569        public static String name(Class<?> type) {
570            return type != null ? type.getName() : null;
571        }
572    
573        /**
574         * Returns the type name of the given value
575         */
576        public static String className(Object value) {
577            return name(value != null ? value.getClass() : null);
578        }
579    
580        /**
581         * Returns the canonical type name of the given value
582         */
583        public static String classCanonicalName(Object value) {
584            if (value != null) {
585                return value.getClass().getCanonicalName();
586            } else {
587                return null;
588            }
589        }
590    
591        /**
592         * Attempts to load the given class name using the thread context class
593         * loader or the class loader used to load this class
594         *
595         * @param name the name of the class to load
596         * @return the class or null if it could not be loaded
597         */
598        public static Class<?> loadClass(String name) {
599            return loadClass(name, ObjectHelper.class.getClassLoader());
600        }
601        
602        /**
603         * Attempts to load the given class name using the thread context class
604         * loader or the given class loader
605         *
606         * @param name the name of the class to load
607         * @param loader the class loader to use after the thread context class
608         *                loader
609         * @return the class or null if it could not be loaded
610         */
611        public static Class<?> loadClass(String name, ClassLoader loader) {
612            return loadClass(name, loader, true);
613        }
614    
615        /**
616         * Attempts to load the given class name using the thread context class
617         * loader or the given class loader
618         *
619         * @param name the name of the class to load
620         * @param loader the class loader to use after the thread context class
621         *                loader
622         * @param needToWarn if it is true will use log a warning message for not loading the class               
623         * @return the class or null if it could not be loaded
624         */
625        public static Class<?> loadClass(String name, ClassLoader loader, boolean needToWarn) {
626            // must clean the name so its pure java name, eg remoing \n or whatever people can do in the Spring XML
627            name = normalizeClassName(name);
628    
629            // special for byte[] as its common to use
630            if ("java.lang.byte[]".equals(name) || "byte[]".equals(name)) {
631                return byte[].class;
632            } else if ("java.lang.String".equals(name) || "String".equals(name)) {
633                return String.class;
634            }
635    
636            // try context class loader first
637            Class<?> clazz = doLoadClass(name, Thread.currentThread().getContextClassLoader());
638            if (clazz == null) {
639                // then the provided loader
640                clazz = doLoadClass(name, loader);
641            }
642            if (clazz == null) {
643                // and fallback to the loader the loaded the ObjectHelper class
644                clazz = doLoadClass(name, ObjectHelper.class.getClassLoader());
645            }
646    
647            if (clazz == null) {
648                if (needToWarn) {
649                    LOG.warn("Cannot find class: " + name);
650                }
651            }
652    
653            return clazz;
654        }
655    
656        /**
657         * Loads the given class with the provided classloader (may be null).
658         * Will ignore any class not found and return null.
659         *
660         * @param name    the name of the class to load
661         * @param loader  a provided loader (may be null)
662         * @return the class, or null if it could not be loaded
663         */
664        private static Class<?> doLoadClass(String name, ClassLoader loader) {
665            ObjectHelper.notEmpty(name, "name");
666            if (loader == null) {
667                return null;
668            }
669            try {
670                if (LOG.isTraceEnabled()) {
671                    LOG.trace("Loading class: " + name + " using classloader: " + loader);
672                }
673                return loader.loadClass(name);
674            } catch (ClassNotFoundException e) {
675                if (LOG.isTraceEnabled()) {
676                    LOG.trace("Cannot load class: " + name + " using classloader: " + loader, e);
677                }
678    
679            }
680            return null;
681        }
682    
683        /**
684         * Attempts to load the given resource as a stream using the thread context
685         * class loader or the class loader used to load this class
686         *
687         * @param name the name of the resource to load
688         * @return the stream or null if it could not be loaded
689         */
690        public static InputStream loadResourceAsStream(String name) {
691            InputStream in = null;
692    
693            ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
694            if (contextClassLoader != null) {
695                in = contextClassLoader.getResourceAsStream(name);
696            }
697            if (in == null) {
698                in = ObjectHelper.class.getClassLoader().getResourceAsStream(name);
699            }
700    
701            return in;
702        }
703    
704        /**
705         * Attempts to load the given resource as a stream using the thread context
706         * class loader or the class loader used to load this class
707         *
708         * @param name the name of the resource to load
709         * @return the stream or null if it could not be loaded
710         */
711        public static URL loadResourceAsURL(String name) {
712            URL url = null;
713    
714            ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
715            if (contextClassLoader != null) {
716                url = contextClassLoader.getResource(name);
717            }
718            if (url == null) {
719                url = ObjectHelper.class.getClassLoader().getResource(name);
720            }
721    
722            return url;
723        }
724    
725        /**
726         * A helper method to invoke a method via reflection and wrap any exceptions
727         * as {@link RuntimeCamelException} instances
728         *
729         * @param method the method to invoke
730         * @param instance the object instance (or null for static methods)
731         * @param parameters the parameters to the method
732         * @return the result of the method invocation
733         */
734        public static Object invokeMethod(Method method, Object instance, Object... parameters) {
735            try {
736                return method.invoke(instance, parameters);
737            } catch (IllegalAccessException e) {
738                throw new RuntimeCamelException(e);
739            } catch (InvocationTargetException e) {
740                throw new RuntimeCamelException(e.getCause());
741            }
742        }
743    
744        /**
745         * Tests whether the target method overrides the source method.
746         * <p/>
747         * Tests whether they have the same name, return type, and parameter list.
748         *
749         * @param source  the source method
750         * @param target  the target method
751         * @return <tt>true</tt> if it override, <tt>false</tt> otherwise
752         */
753        public static boolean isOverridingMethod(Method source, Method target) {
754            if (source.getName().equals(target.getName())
755                    && source.getReturnType().equals(target.getReturnType()) 
756                    && source.getParameterTypes().length == target.getParameterTypes().length) {
757    
758                // test if parameter types is the same as well
759                for (int i = 0; i < source.getParameterTypes().length; i++) {
760                    if (!(source.getParameterTypes()[i].equals(target.getParameterTypes()[i]))) {
761                        return false;
762                    }
763                }
764    
765                // the have same name, return type and parameter list, so its overriding
766                return true;
767            }
768    
769            return false;
770        }
771    
772        /**
773         * Returns a list of methods which are annotated with the given annotation
774         *
775         * @param type the type to reflect on
776         * @param annotationType the annotation type
777         * @return a list of the methods found
778         */
779        public static List<Method> findMethodsWithAnnotation(Class<?> type,
780                                                             Class<? extends Annotation> annotationType) {
781            return findMethodsWithAnnotation(type, annotationType, false);
782        }
783    
784        /**
785         * Returns a list of methods which are annotated with the given annotation
786         *
787         * @param type the type to reflect on
788         * @param annotationType the annotation type
789         * @param checkMetaAnnotations check for meta annotations
790         * @return a list of the methods found
791         */
792        public static List<Method> findMethodsWithAnnotation(Class<?> type,
793                                                             Class<? extends Annotation> annotationType,
794                                                             boolean checkMetaAnnotations) {
795            List<Method> answer = new ArrayList<Method>();
796            do {
797                Method[] methods = type.getDeclaredMethods();
798                for (Method method : methods) {
799                    if (hasAnnotation(method, annotationType, checkMetaAnnotations)) {
800                        answer.add(method);
801                    }
802                }
803                type = type.getSuperclass();
804            } while (type != null);
805            return answer;
806        }
807    
808        /**
809         * Checks if a Class or Method are annotated with the given annotation
810         *
811         * @param elem the Class or Method to reflect on
812         * @param annotationType the annotation type
813         * @param checkMetaAnnotations check for meta annotations
814         * @return true if annotations is present
815         */
816        public static boolean hasAnnotation(AnnotatedElement elem, Class<? extends Annotation> annotationType,
817                                            boolean checkMetaAnnotations) {
818            if (elem.isAnnotationPresent(annotationType)) {
819                return true;
820            }
821            if (checkMetaAnnotations) {
822                for (Annotation a : elem.getAnnotations()) {
823                    for (Annotation meta : a.annotationType().getAnnotations()) {
824                        if (meta.annotationType().getName().equals(annotationType.getName())) {
825                            return true;
826                        }
827                    }
828                }
829            }
830            return false;
831        }
832    
833        /**
834         * Turns the given object arrays into a meaningful string
835         *
836         * @param objects an array of objects or null
837         * @return a meaningful string
838         */
839        public static String asString(Object[] objects) {
840            if (objects == null) {
841                return "null";
842            } else {
843                StringBuffer buffer = new StringBuffer("{");
844                int counter = 0;
845                for (Object object : objects) {
846                    if (counter++ > 0) {
847                        buffer.append(", ");
848                    }
849                    String text = (object == null) ? "null" : object.toString();
850                    buffer.append(text);
851                }
852                buffer.append("}");
853                return buffer.toString();
854            }
855        }
856    
857        /**
858         * Returns true if a class is assignable from another class like the
859         * {@link Class#isAssignableFrom(Class)} method but which also includes
860         * coercion between primitive types to deal with Java 5 primitive type
861         * wrapping
862         */
863        public static boolean isAssignableFrom(Class<?> a, Class<?> b) {
864            a = convertPrimitiveTypeToWrapperType(a);
865            b = convertPrimitiveTypeToWrapperType(b);
866            return a.isAssignableFrom(b);
867        }
868    
869        /**
870         * Converts primitive types such as int to its wrapper type like
871         * {@link Integer}
872         */
873        public static Class<?> convertPrimitiveTypeToWrapperType(Class<?> type) {
874            Class<?> rc = type;
875            if (type.isPrimitive()) {
876                if (type == int.class) {
877                    rc = Integer.class;
878                } else if (type == long.class) {
879                    rc = Long.class;
880                } else if (type == double.class) {
881                    rc = Double.class;
882                } else if (type == float.class) {
883                    rc = Float.class;
884                } else if (type == short.class) {
885                    rc = Short.class;
886                } else if (type == byte.class) {
887                    rc = Byte.class;
888                } else if (type == boolean.class) {
889                    rc = Boolean.class;
890                }
891            }
892            return rc;
893        }
894    
895        /**
896         * Helper method to return the default character set name
897         */
898        public static String getDefaultCharacterSet() {
899            return Charset.defaultCharset().name();
900        }
901    
902        /**
903         * Returns the Java Bean property name of the given method, if it is a
904         * setter
905         */
906        public static String getPropertyName(Method method) {
907            String propertyName = method.getName();
908            if (propertyName.startsWith("set") && method.getParameterTypes().length == 1) {
909                propertyName = propertyName.substring(3, 4).toLowerCase() + propertyName.substring(4);
910            }
911            return propertyName;
912        }
913    
914        /**
915         * Returns true if the given collection of annotations matches the given
916         * type
917         */
918        public static boolean hasAnnotation(Annotation[] annotations, Class<?> type) {
919            for (Annotation annotation : annotations) {
920                if (type.isInstance(annotation)) {
921                    return true;
922                }
923            }
924            return false;
925        }
926    
927        /**
928         * Closes the given resource if it is available, logging any closing
929         * exceptions to the given log
930         *
931         * @param closeable the object to close
932         * @param name the name of the resource
933         * @param log the log to use when reporting closure warnings
934         */
935        public static void close(Closeable closeable, String name, Log log) {
936            if (closeable != null) {
937                try {
938                    closeable.close();
939                } catch (IOException e) {
940                    if (log != null) {
941                        log.warn("Cannot close: " + name + ". Reason: " + e, e);
942                    }
943                }
944            }
945        }
946    
947        /**
948         * Converts the given value to the required type or throw a meaningful exception
949         */
950        @SuppressWarnings("unchecked")
951        public static <T> T cast(Class<T> toType, Object value) {
952            if (toType == boolean.class) {
953                return (T)cast(Boolean.class, value);
954            } else if (toType.isPrimitive()) {
955                Class newType = convertPrimitiveTypeToWrapperType(toType);
956                if (newType != toType) {
957                    return (T)cast(newType, value);
958                }
959            }
960            try {
961                return toType.cast(value);
962            } catch (ClassCastException e) {
963                throw new IllegalArgumentException("Failed to convert: " 
964                    + value + " to type: " + toType.getName() + " due to: " + e, e);
965            }
966        }
967    
968        /**
969         * A helper method to create a new instance of a type using the default
970         * constructor arguments.
971         */
972        public static <T> T newInstance(Class<T> type) {
973            try {
974                return type.newInstance();
975            } catch (InstantiationException e) {
976                throw new RuntimeCamelException(e);
977            } catch (IllegalAccessException e) {
978                throw new RuntimeCamelException(e);
979            }
980        }
981    
982        /**
983         * A helper method to create a new instance of a type using the default
984         * constructor arguments.
985         */
986        public static <T> T newInstance(Class<?> actualType, Class<T> expectedType) {
987            try {
988                Object value = actualType.newInstance();
989                return cast(expectedType, value);
990            } catch (InstantiationException e) {
991                throw new RuntimeCamelException(e);
992            } catch (IllegalAccessException e) {
993                throw new RuntimeCamelException(e);
994            }
995        }
996    
997        /**
998         * Returns true if the given name is a valid java identifier
999         */
1000        public static boolean isJavaIdentifier(String name) {
1001            if (name == null) {
1002                return false;
1003            }
1004            int size = name.length();
1005            if (size < 1) {
1006                return false;
1007            }
1008            if (Character.isJavaIdentifierStart(name.charAt(0))) {
1009                for (int i = 1; i < size; i++) {
1010                    if (!Character.isJavaIdentifierPart(name.charAt(i))) {
1011                        return false;
1012                    }
1013                }
1014                return true;
1015            }
1016            return false;
1017        }
1018    
1019        /**
1020         * Returns the type of the given object or null if the value is null
1021         */
1022        public static Object type(Object bean) {
1023            return bean != null ? bean.getClass() : null;
1024        }
1025    
1026        /**
1027         * Evaluate the value as a predicate which attempts to convert the value to
1028         * a boolean otherwise true is returned if the value is not null
1029         */
1030        public static boolean evaluateValuePredicate(Object value) {
1031            if (value instanceof Boolean) {
1032                return (Boolean)value;
1033            } else if (value instanceof String) {
1034                if ("true".equals(value)) {
1035                    return true;
1036                } else if ("false".equals(value)) {
1037                    return false;
1038                }
1039            }
1040            return value != null;
1041        }
1042    
1043        /**
1044         * Wraps the caused exception in a {@link RuntimeCamelException} if its not
1045         * already such an exception.
1046         *
1047         * @param e the caused exception
1048         * @return the wrapper exception
1049         */
1050        public static RuntimeCamelException wrapRuntimeCamelException(Throwable e) {
1051            if (e instanceof RuntimeCamelException) {
1052                // don't double wrap
1053                return (RuntimeCamelException)e;
1054            } else {
1055                return new RuntimeCamelException(e);
1056            }
1057        }
1058    
1059        /**
1060         * Wraps the caused exception in a {@link CamelExecutionException} if its not
1061         * already such an exception.
1062         *
1063         * @param e the caused exception
1064         * @return the wrapper exception
1065         */
1066        public static CamelExecutionException wrapCamelExecutionException(Exchange exchange, Throwable e) {
1067            if (e instanceof CamelExecutionException) {
1068                // don't double wrap
1069                return (CamelExecutionException)e;
1070            } else {
1071                return new CamelExecutionException("Exception occurred during execution", exchange, e);
1072            }
1073        }
1074    
1075        /**
1076         * Cleans the string to pure java identifier so we can use it for loading class names.
1077         * <p/>
1078         * Especially from Spring DSL people can have \n \t or other characters that otherwise
1079         * would result in ClassNotFoundException
1080         *
1081         * @param name the class name
1082         * @return normalized classname that can be load by a class loader.
1083         */
1084        public static String normalizeClassName(String name) {
1085            StringBuffer sb = new StringBuffer(name.length());
1086            for (char ch : name.toCharArray()) {
1087                if (ch == '.' || ch == '[' || ch == ']' || ch == '-' || Character.isJavaIdentifierPart(ch)) {
1088                    sb.append(ch);
1089                }
1090            }
1091            return sb.toString();
1092        }
1093    
1094        /**
1095         * Creates an iterator to walk the exception from the bottom up
1096         * (the last caused by going upwards to the root exception).
1097         *
1098         * @param exception  the exception
1099         * @return the iterator
1100         */
1101        public static Iterator<Throwable> createExceptionIterator(Throwable exception) {
1102            return new ExceptionIterator(exception);
1103        }
1104    
1105        /**
1106         * Retrieves the given exception type from the exception.
1107         * <p/>
1108         * Is used to get the caused exception that typically have been wrapped in some sort
1109         * of Camel wrapper exception
1110         * <p/>
1111         * The strategy is to look in the exception hierarchy to find the first given cause that matches the type.
1112         * Will start from the bottom (the real cause) and walk upwards.
1113         *
1114         * @param type the exception type wanted to retrieve
1115         * @param exception the caused exception
1116         * @return the exception found (or <tt>null</tt> if not found in the exception hierarchy)
1117         */
1118        public static <T> T getException(Class<T> type, Throwable exception) {
1119            if (exception == null) {
1120                return null;
1121            }
1122    
1123            // walk the hierarchy and look for it
1124            Iterator<Throwable> it = createExceptionIterator(exception);
1125            while (it.hasNext()) {
1126                Throwable e = it.next();
1127                if (type.isInstance(e)) {
1128                    return type.cast(e);
1129                }
1130            }
1131    
1132            // not found
1133            return null;
1134        }
1135    
1136        /**
1137         * Creates a {@link Scanner} for scanning the given value.
1138         *
1139         * @param exchange  the current exchange
1140         * @param value     the value, typically the message IN body
1141         * @return the scanner, is newer <tt>null</tt>
1142         */
1143        public static Scanner getScanner(Exchange exchange, Object value) {
1144            if (value instanceof GenericFile) {
1145                // generic file is just a wrapper for the real file so call again with the real file
1146                GenericFile<?> gf = (GenericFile<?>) value;
1147                return getScanner(exchange, gf.getFile());
1148            }
1149    
1150            String charset = exchange.getProperty(Exchange.CHARSET_NAME, String.class);
1151    
1152            Scanner scanner = null;
1153            if (value instanceof Readable) {
1154                scanner = new Scanner((Readable)value);
1155            } else if (value instanceof InputStream) {
1156                scanner = charset == null ? new Scanner((InputStream)value) : new Scanner((InputStream)value, charset);
1157            } else if (value instanceof File) {
1158                try {
1159                    scanner = charset == null ? new Scanner((File)value) : new Scanner((File)value, charset);
1160                } catch (FileNotFoundException e) {
1161                    throw new RuntimeCamelException(e);
1162                }
1163            } else if (value instanceof String) {
1164                scanner = new Scanner((String)value);
1165            } else if (value instanceof ReadableByteChannel) {
1166                scanner = charset == null ? new Scanner((ReadableByteChannel)value) : new Scanner((ReadableByteChannel)value, charset);
1167            }
1168    
1169            if (scanner == null) {
1170                // value is not a suitable type, try to convert value to a string
1171                String text = exchange.getContext().getTypeConverter().convertTo(String.class, exchange, value);
1172                if (text != null) {
1173                    scanner = new Scanner(text);
1174                }
1175            }
1176    
1177            if (scanner == null) {
1178                scanner = new Scanner("");
1179            }
1180    
1181            return scanner;
1182        }
1183    
1184        public static String getIdentityHashCode(Object object) {
1185            return "0x" + Integer.toHexString(System.identityHashCode(object));
1186        }
1187    
1188        private static class ExceptionIterator implements Iterator<Throwable> {
1189            private List<Throwable> tree = new ArrayList<Throwable>();
1190            private Iterator<Throwable> it;
1191    
1192            public ExceptionIterator(Throwable exception) {
1193                Throwable current = exception;
1194                // spool to the bottom of the caused by tree
1195                while (current != null) {
1196                    tree.add(current);
1197                    current = current.getCause();
1198                }
1199    
1200                // reverse tree so we go from bottom to top
1201                Collections.reverse(tree);
1202                it = tree.iterator();
1203            }
1204    
1205            public boolean hasNext() {
1206                return it.hasNext();
1207            }
1208    
1209            public Throwable next() {
1210                return it.next();
1211            }
1212    
1213            public void remove() {
1214                it.remove();
1215            }
1216        }
1217    }