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.Constructor;
027 import java.lang.reflect.Field;
028 import java.lang.reflect.InvocationTargetException;
029 import java.lang.reflect.Method;
030 import java.net.URL;
031 import java.nio.channels.ReadableByteChannel;
032 import java.nio.charset.Charset;
033 import java.util.ArrayList;
034 import java.util.Arrays;
035 import java.util.Collection;
036 import java.util.Collections;
037 import java.util.Enumeration;
038 import java.util.Iterator;
039 import java.util.List;
040 import java.util.Locale;
041 import java.util.Map;
042 import java.util.Properties;
043 import java.util.Scanner;
044
045 import org.w3c.dom.Node;
046 import org.w3c.dom.NodeList;
047
048 import org.apache.camel.CamelContext;
049 import org.apache.camel.CamelExecutionException;
050 import org.apache.camel.Exchange;
051 import org.apache.camel.Message;
052 import org.apache.camel.Ordered;
053 import org.apache.camel.RuntimeCamelException;
054 import org.apache.camel.TypeConverter;
055 import org.apache.camel.WrappedFile;
056 import org.slf4j.Logger;
057 import org.slf4j.LoggerFactory;
058
059 /**
060 * A number of useful helper methods for working with Objects
061 *
062 * @version
063 */
064 public final class ObjectHelper {
065 private static final transient Logger LOG = LoggerFactory.getLogger(ObjectHelper.class);
066 private static final String DEFAULT_DELIMITER = ",";
067
068 /**
069 * Utility classes should not have a public constructor.
070 */
071 private ObjectHelper() {
072 }
073
074 /**
075 * A helper method for comparing objects for equality in which it uses type coercion to coerce
076 * types between the left and right values. This allows you test for equality for example with
077 * a String and Integer type as Camel will be able to coerce the types.
078 */
079 public static boolean typeCoerceEquals(TypeConverter converter, Object leftValue, Object rightValue) {
080 // sanity check
081 if (leftValue == null && rightValue == null) {
082 // they are equal
083 return true;
084 } else if (leftValue == null || rightValue == null) {
085 // only one of them is null so they are not equal
086 return false;
087 }
088
089 // try without type coerce
090 boolean answer = equal(leftValue, rightValue);
091 if (answer) {
092 return true;
093 }
094
095 // are they same type, if so return false as the equals returned false
096 if (leftValue.getClass().isInstance(rightValue)) {
097 return false;
098 }
099
100 // convert left to right
101 Object value = converter.tryConvertTo(rightValue.getClass(), leftValue);
102 answer = equal(value, rightValue);
103 if (answer) {
104 return true;
105 }
106
107 // convert right to left
108 value = converter.tryConvertTo(leftValue.getClass(), rightValue);
109 answer = equal(leftValue, value);
110 return answer;
111 }
112
113 /**
114 * A helper method for comparing objects for inequality in which it uses type coercion to coerce
115 * types between the left and right values. This allows you test for inequality for example with
116 * a String and Integer type as Camel will be able to coerce the types.
117 */
118 public static boolean typeCoerceNotEquals(TypeConverter converter, Object leftValue, Object rightValue) {
119 return !typeCoerceEquals(converter, leftValue, rightValue);
120 }
121
122 /**
123 * A helper method for comparing objects ordering in which it uses type coercion to coerce
124 * types between the left and right values. This allows you test for ordering for example with
125 * a String and Integer type as Camel will be able to coerce the types.
126 */
127 @SuppressWarnings({"unchecked", "rawtypes"})
128 public static int typeCoerceCompare(TypeConverter converter, Object leftValue, Object rightValue) {
129
130 // if both values is numeric then compare using numeric
131 Long leftNum = converter.tryConvertTo(Long.class, leftValue);
132 Long rightNum = converter.tryConvertTo(Long.class, rightValue);
133 if (leftNum != null && rightNum != null) {
134 return leftNum.compareTo(rightNum);
135 }
136
137 // also try with floating point numbers
138 Double leftDouble = converter.tryConvertTo(Double.class, leftValue);
139 Double rightDouble = converter.tryConvertTo(Double.class, rightValue);
140 if (leftDouble != null && rightDouble != null) {
141 return leftDouble.compareTo(rightDouble);
142 }
143
144 // prefer to NOT coerce to String so use the type which is not String
145 // for example if we are comparing String vs Integer then prefer to coerce to Integer
146 // as all types can be converted to String which does not work well for comparison
147 // as eg "10" < 6 would return true, where as 10 < 6 will return false.
148 // if they are both String then it doesn't matter
149 if (rightValue instanceof String && (!(leftValue instanceof String))) {
150 // if right is String and left is not then flip order (remember to * -1 the result then)
151 return typeCoerceCompare(converter, rightValue, leftValue) * -1;
152 }
153
154 // prefer to coerce to the right hand side at first
155 if (rightValue instanceof Comparable) {
156 Object value = converter.tryConvertTo(rightValue.getClass(), leftValue);
157 if (value != null) {
158 return ((Comparable) rightValue).compareTo(value) * -1;
159 }
160 }
161
162 // then fallback to the left hand side
163 if (leftValue instanceof Comparable) {
164 Object value = converter.tryConvertTo(leftValue.getClass(), rightValue);
165 if (value != null) {
166 return ((Comparable) leftValue).compareTo(value);
167 }
168 }
169
170 // use regular compare
171 return compare(leftValue, rightValue);
172 }
173
174 /**
175 * A helper method for comparing objects for equality while handling nulls
176 */
177 public static boolean equal(Object a, Object b) {
178 if (a == b) {
179 return true;
180 }
181
182 if (a instanceof byte[] && b instanceof byte[]) {
183 return equalByteArray((byte[])a, (byte[])b);
184 }
185
186 return a != null && b != null && a.equals(b);
187 }
188
189 /**
190 * A helper method for comparing byte arrays for equality while handling
191 * nulls
192 */
193 public static boolean equalByteArray(byte[] a, byte[] b) {
194 if (a == b) {
195 return true;
196 }
197
198 // loop and compare each byte
199 if (a != null && b != null && a.length == b.length) {
200 for (int i = 0; i < a.length; i++) {
201 if (a[i] != b[i]) {
202 return false;
203 }
204 }
205 // all bytes are equal
206 return true;
207 }
208
209 return false;
210 }
211
212 /**
213 * Returns true if the given object is equal to any of the expected value
214 */
215 public static boolean isEqualToAny(Object object, Object... values) {
216 for (Object value : values) {
217 if (equal(object, value)) {
218 return true;
219 }
220 }
221 return false;
222 }
223
224 /**
225 * A helper method for performing an ordered comparison on the objects
226 * handling nulls and objects which do not handle sorting gracefully
227 */
228 public static int compare(Object a, Object b) {
229 return compare(a, b, false);
230 }
231
232 /**
233 * A helper method for performing an ordered comparison on the objects
234 * handling nulls and objects which do not handle sorting gracefully
235 *
236 * @param a the first object
237 * @param b the second object
238 * @param ignoreCase ignore case for string comparison
239 */
240 @SuppressWarnings({"unchecked", "rawtypes"})
241 public static int compare(Object a, Object b, boolean ignoreCase) {
242 if (a == b) {
243 return 0;
244 }
245 if (a == null) {
246 return -1;
247 }
248 if (b == null) {
249 return 1;
250 }
251 if (a instanceof Ordered && b instanceof Ordered) {
252 return ((Ordered) a).getOrder() - ((Ordered) b).getOrder();
253 }
254 if (ignoreCase && a instanceof String && b instanceof String) {
255 return ((String) a).compareToIgnoreCase((String) b);
256 }
257 if (a instanceof Comparable) {
258 Comparable comparable = (Comparable)a;
259 return comparable.compareTo(b);
260 }
261 int answer = a.getClass().getName().compareTo(b.getClass().getName());
262 if (answer == 0) {
263 answer = a.hashCode() - b.hashCode();
264 }
265 return answer;
266 }
267
268 public static Boolean toBoolean(Object value) {
269 if (value instanceof Boolean) {
270 return (Boolean)value;
271 }
272 if (value instanceof String) {
273 return "true".equalsIgnoreCase(value.toString()) ? Boolean.TRUE : Boolean.FALSE;
274 }
275 if (value instanceof Integer) {
276 return (Integer)value > 0 ? Boolean.TRUE : Boolean.FALSE;
277 }
278 return null;
279 }
280
281 /**
282 * Asserts whether the value is <b>not</b> <tt>null</tt>
283 *
284 * @param value the value to test
285 * @param name the key that resolved the value
286 * @throws IllegalArgumentException is thrown if assertion fails
287 */
288 public static void notNull(Object value, String name) {
289 if (value == null) {
290 throw new IllegalArgumentException(name + " must be specified");
291 }
292 }
293
294 /**
295 * Asserts whether the value is <b>not</b> <tt>null</tt>
296 *
297 * @param value the value to test
298 * @param on additional description to indicate where this problem occurred (appended as toString())
299 * @param name the key that resolved the value
300 * @throws IllegalArgumentException is thrown if assertion fails
301 */
302 public static void notNull(Object value, String name, Object on) {
303 if (on == null) {
304 notNull(value, name);
305 } else if (value == null) {
306 throw new IllegalArgumentException(name + " must be specified on: " + on);
307 }
308 }
309
310 /**
311 * Asserts whether the string is <b>not</b> empty.
312 *
313 * @param value the string to test
314 * @param name the key that resolved the value
315 * @throws IllegalArgumentException is thrown if assertion fails
316 */
317 public static void notEmpty(String value, String name) {
318 if (isEmpty(value)) {
319 throw new IllegalArgumentException(name + " must be specified and not empty");
320 }
321 }
322
323 /**
324 * Asserts whether the string is <b>not</b> empty.
325 *
326 * @param value the string to test
327 * @param on additional description to indicate where this problem occurred (appended as toString())
328 * @param name the key that resolved the value
329 * @throws IllegalArgumentException is thrown if assertion fails
330 */
331 public static void notEmpty(String value, String name, Object on) {
332 if (on == null) {
333 notNull(value, name);
334 } else if (isEmpty(value)) {
335 throw new IllegalArgumentException(name + " must be specified and not empty on: " + on);
336 }
337 }
338
339 /**
340 * Tests whether the value is <tt>null</tt> or an empty string.
341 *
342 * @param value the value, if its a String it will be tested for text length as well
343 * @return true if empty
344 */
345 public static boolean isEmpty(Object value) {
346 return !isNotEmpty(value);
347 }
348
349 /**
350 * Tests whether the value is <b>not</b> <tt>null</tt> or an empty string.
351 *
352 * @param value the value, if its a String it will be tested for text length as well
353 * @return true if <b>not</b> empty
354 */
355 public static boolean isNotEmpty(Object value) {
356 if (value == null) {
357 return false;
358 } else if (value instanceof String) {
359 String text = (String) value;
360 return text.trim().length() > 0;
361 } else {
362 return true;
363 }
364 }
365
366 public static String[] splitOnCharacter(String value, String needle, int count) {
367 String rc[] = new String[count];
368 rc[0] = value;
369 for (int i = 1; i < count; i++) {
370 String v = rc[i - 1];
371 int p = v.indexOf(needle);
372 if (p < 0) {
373 return rc;
374 }
375 rc[i - 1] = v.substring(0, p);
376 rc[i] = v.substring(p + 1);
377 }
378 return rc;
379 }
380
381 /**
382 * Removes any starting characters on the given text which match the given
383 * character
384 *
385 * @param text the string
386 * @param ch the initial characters to remove
387 * @return either the original string or the new substring
388 */
389 public static String removeStartingCharacters(String text, char ch) {
390 int idx = 0;
391 while (text.charAt(idx) == ch) {
392 idx++;
393 }
394 if (idx > 0) {
395 return text.substring(idx);
396 }
397 return text;
398 }
399
400 public static String capitalize(String text) {
401 if (text == null) {
402 return null;
403 }
404 int length = text.length();
405 if (length == 0) {
406 return text;
407 }
408 String answer = text.substring(0, 1).toUpperCase(Locale.ENGLISH);
409 if (length > 1) {
410 answer += text.substring(1, length);
411 }
412 return answer;
413 }
414
415 public static String after(String text, String after) {
416 if (!text.contains(after)) {
417 return null;
418 }
419 return text.substring(text.indexOf(after) + after.length());
420 }
421
422 public static String before(String text, String before) {
423 if (!text.contains(before)) {
424 return null;
425 }
426 return text.substring(0, text.indexOf(before));
427 }
428
429 public static String between(String text, String after, String before) {
430 text = after(text, after);
431 if (text == null) {
432 return null;
433 }
434 return before(text, before);
435 }
436
437 /**
438 * Returns true if the collection contains the specified value
439 */
440 public static boolean contains(Object collectionOrArray, Object value) {
441 if (collectionOrArray instanceof Collection) {
442 Collection<?> collection = (Collection<?>)collectionOrArray;
443 return collection.contains(value);
444 } else if (collectionOrArray instanceof String && value instanceof String) {
445 String str = (String)collectionOrArray;
446 String subStr = (String)value;
447 return str.contains(subStr);
448 } else {
449 Iterator<Object> iter = createIterator(collectionOrArray);
450 while (iter.hasNext()) {
451 if (equal(value, iter.next())) {
452 return true;
453 }
454 }
455 }
456 return false;
457 }
458
459 /**
460 * Creates an iterator over the value if the value is a collection, an
461 * Object[], a String with values separated by comma,
462 * or a primitive type array; otherwise to simplify the caller's code,
463 * we just create a singleton collection iterator over a single value
464 * <p/>
465 * Will default use comma for String separating String values.
466 * This method does <b>not</b> allow empty values
467 *
468 * @param value the value
469 * @return the iterator
470 */
471 public static Iterator<Object> createIterator(Object value) {
472 return createIterator(value, DEFAULT_DELIMITER);
473 }
474
475 /**
476 * Creates an iterator over the value if the value is a collection, an
477 * Object[], a String with values separated by the given delimiter,
478 * or a primitive type array; otherwise to simplify the caller's
479 * code, we just create a singleton collection iterator over a single value
480 * <p/>
481 * This method does <b>not</b> allow empty values
482 *
483 * @param value the value
484 * @param delimiter delimiter for separating String values
485 * @return the iterator
486 */
487 public static Iterator<Object> createIterator(Object value, String delimiter) {
488 return createIterator(value, delimiter, false);
489 }
490
491 /**
492 * Creates an iterator over the value if the value is a collection, an
493 * Object[], a String with values separated by the given delimiter,
494 * or a primitive type array; otherwise to simplify the caller's
495 * code, we just create a singleton collection iterator over a single value
496 *
497 * @param value the value
498 * @param delimiter delimiter for separating String values
499 * @param allowEmptyValues whether to allow empty values
500 * @return the iterator
501 */
502 @SuppressWarnings("unchecked")
503 public static Iterator<Object> createIterator(Object value, String delimiter, final boolean allowEmptyValues) {
504
505 // if its a message than we want to iterate its body
506 if (value instanceof Message) {
507 value = ((Message) value).getBody();
508 }
509
510 if (value == null) {
511 return Collections.emptyList().iterator();
512 } else if (value instanceof Iterator) {
513 return (Iterator<Object>)value;
514 } else if (value instanceof Iterable) {
515 return ((Iterable<Object>)value).iterator();
516 } else if (value.getClass().isArray()) {
517 // TODO we should handle primitive array types?
518 List<Object> list = Arrays.asList((Object[])value);
519 return list.iterator();
520 } else if (value instanceof NodeList) {
521 // lets iterate through DOM results after performing XPaths
522 final NodeList nodeList = (NodeList) value;
523 return CastUtils.cast(new Iterator<Node>() {
524 int idx = -1;
525
526 public boolean hasNext() {
527 return (idx + 1) < nodeList.getLength();
528 }
529
530 public Node next() {
531 idx++;
532 return nodeList.item(idx);
533 }
534
535 public void remove() {
536 throw new UnsupportedOperationException();
537 }
538 });
539 } else if (value instanceof String) {
540 final String s = (String) value;
541
542 // this code is optimized to only use a Scanner if needed, eg there is a delimiter
543
544 if (delimiter != null && s.contains(delimiter)) {
545 // use a scanner if it contains the delimiter
546 Scanner scanner = new Scanner((String)value);
547
548 if (DEFAULT_DELIMITER.equals(delimiter)) {
549 // we use the default delimiter which is a comma, then cater for bean expressions with OGNL
550 // which may have balanced parentheses pairs as well.
551 // if the value contains parentheses we need to balance those, to avoid iterating
552 // in the middle of parentheses pair, so use this regular expression (a bit hard to read)
553 // the regexp will split by comma, but honor parentheses pair that may include commas
554 // as well, eg if value = "bean=foo?method=killer(a,b),bean=bar?method=great(a,b)"
555 // then the regexp will split that into two:
556 // -> bean=foo?method=killer(a,b)
557 // -> bean=bar?method=great(a,b)
558 // http://stackoverflow.com/questions/1516090/splitting-a-title-into-separate-parts
559 delimiter = ",(?!(?:[^\\(,]|[^\\)],[^\\)])+\\))";
560 }
561
562 scanner.useDelimiter(delimiter);
563 return CastUtils.cast(scanner);
564 } else {
565 // use a plain iterator that returns the value as is as there are only a single value
566 return CastUtils.cast(new Iterator<String>() {
567 int idx = -1;
568
569 public boolean hasNext() {
570 return idx + 1 == 0 && (allowEmptyValues || ObjectHelper.isNotEmpty(s));
571 }
572
573 public String next() {
574 idx++;
575 return s;
576 }
577
578 public void remove() {
579 throw new UnsupportedOperationException();
580 }
581 });
582 }
583 } else {
584 return Collections.singletonList(value).iterator();
585 }
586 }
587
588 /**
589 * Returns the predicate matching boolean on a {@link List} result set where
590 * if the first element is a boolean its value is used otherwise this method
591 * returns true if the collection is not empty
592 *
593 * @return <tt>true</tt> if the first element is a boolean and its value
594 * is true or if the list is non empty
595 */
596 public static boolean matches(List<?> list) {
597 if (!list.isEmpty()) {
598 Object value = list.get(0);
599 if (value instanceof Boolean) {
600 return (Boolean)value;
601 } else {
602 // lets assume non-empty results are true
603 return true;
604 }
605 }
606 return false;
607 }
608
609 /**
610 * A helper method to access a system property, catching any security exceptions
611 *
612 * @param name the name of the system property required
613 * @param defaultValue the default value to use if the property is not
614 * available or a security exception prevents access
615 * @return the system property value or the default value if the property is
616 * not available or security does not allow its access
617 */
618 public static String getSystemProperty(String name, String defaultValue) {
619 try {
620 return System.getProperty(name, defaultValue);
621 } catch (Exception e) {
622 if (LOG.isDebugEnabled()) {
623 LOG.debug("Caught security exception accessing system property: " + name + ". Will use default value: " + defaultValue, e);
624 }
625 return defaultValue;
626 }
627 }
628
629 /**
630 * A helper method to access a boolean system property, catching any
631 * security exceptions
632 *
633 * @param name the name of the system property required
634 * @param defaultValue the default value to use if the property is not
635 * available or a security exception prevents access
636 * @return the boolean representation of the system property value or the
637 * default value if the property is not available or security does
638 * not allow its access
639 */
640 public static boolean getSystemProperty(String name, Boolean defaultValue) {
641 String result = getSystemProperty(name, defaultValue.toString());
642 return Boolean.parseBoolean(result);
643 }
644
645 /**
646 * A helper method to access a camel context properties with a prefix
647 *
648 * @param prefix the prefix
649 * @param camelContext the camel context
650 * @return the properties which holds the camel context properties with the prefix,
651 * and the key omit the prefix part
652 */
653 public static Properties getCamelPropertiesWithPrefix(String prefix, CamelContext camelContext) {
654 Properties answer = new Properties();
655 Map<String, String> camelProperties = camelContext.getProperties();
656 if (camelProperties != null) {
657 for (Map.Entry<String, String> entry : camelProperties.entrySet()) {
658 String key = entry.getKey();
659 if (key.startsWith(prefix)) {
660 answer.put(key.substring(prefix.length()), entry.getValue());
661 }
662 }
663 }
664 return answer;
665 }
666
667 /**
668 * Returns the type name of the given type or null if the type variable is
669 * null
670 */
671 public static String name(Class<?> type) {
672 return type != null ? type.getName() : null;
673 }
674
675 /**
676 * Returns the type name of the given value
677 */
678 public static String className(Object value) {
679 return name(value != null ? value.getClass() : null);
680 }
681
682 /**
683 * Returns the canonical type name of the given value
684 */
685 public static String classCanonicalName(Object value) {
686 if (value != null) {
687 return value.getClass().getCanonicalName();
688 } else {
689 return null;
690 }
691 }
692
693 /**
694 * Attempts to load the given class name using the thread context class
695 * loader or the class loader used to load this class
696 *
697 * @param name the name of the class to load
698 * @return the class or <tt>null</tt> if it could not be loaded
699 */
700 public static Class<?> loadClass(String name) {
701 return loadClass(name, ObjectHelper.class.getClassLoader());
702 }
703
704 /**
705 * Attempts to load the given class name using the thread context class
706 * loader or the given class loader
707 *
708 * @param name the name of the class to load
709 * @param loader the class loader to use after the thread context class loader
710 * @return the class or <tt>null</tt> if it could not be loaded
711 */
712 public static Class<?> loadClass(String name, ClassLoader loader) {
713 return loadClass(name, loader, true);
714 }
715
716 /**
717 * Attempts to load the given class name using the thread context class
718 * loader or the given class loader
719 *
720 * @param name the name of the class to load
721 * @param loader the class loader to use after the thread context class loader
722 * @param needToWarn when <tt>true</tt> logs a warning when a class with the given name could not be loaded
723 * @return the class or <tt>null</tt> if it could not be loaded
724 */
725 public static Class<?> loadClass(String name, ClassLoader loader, boolean needToWarn) {
726 // must clean the name so its pure java name, eg removing \n or whatever people can do in the Spring XML
727 name = normalizeClassName(name);
728
729 // Try simple type first
730 Class<?> clazz = loadSimpleType(name);
731 if (clazz == null) {
732 // try context class loader
733 clazz = doLoadClass(name, Thread.currentThread().getContextClassLoader());
734 }
735 if (clazz == null) {
736 // then the provided loader
737 clazz = doLoadClass(name, loader);
738 }
739 if (clazz == null) {
740 // and fallback to the loader the loaded the ObjectHelper class
741 clazz = doLoadClass(name, ObjectHelper.class.getClassLoader());
742 }
743
744 if (clazz == null) {
745 if (needToWarn) {
746 LOG.warn("Cannot find class: " + name);
747 }
748 }
749
750 return clazz;
751 }
752
753
754 /**
755 * Load a simple type
756 *
757 * @param name the name of the class to load
758 * @return the class or <tt>null</tt> if it could not be loaded
759 */
760 public static Class<?> loadSimpleType(String name) {
761 // special for byte[] as its common to use
762 if ("java.lang.byte[]".equals(name) || "byte[]".equals(name)) {
763 return byte[].class;
764 // and these is common as well
765 } else if ("java.lang.String".equals(name) || "String".equals(name)) {
766 return String.class;
767 } else if ("java.lang.Boolean".equals(name) || "Boolean".equals(name)) {
768 return Boolean.class;
769 } else if ("boolean".equals(name)) {
770 return boolean.class;
771 } else if ("java.lang.Integer".equals(name) || "Integer".equals(name)) {
772 return Integer.class;
773 } else if ("int".equals(name)) {
774 return int.class;
775 } else if ("java.lang.Long".equals(name) || "Long".equals(name)) {
776 return Long.class;
777 } else if ("long".equals(name)) {
778 return long.class;
779 } else if ("java.lang.Short".equals(name) || "Short".equals(name)) {
780 return Short.class;
781 } else if ("short".equals(name)) {
782 return short.class;
783 } else if ("java.lang.Byte".equals(name) || "Byte".equals(name)) {
784 return Byte.class;
785 } else if ("byte".equals(name)) {
786 return byte.class;
787 } else if ("java.lang.Float".equals(name) || "Float".equals(name)) {
788 return Float.class;
789 } else if ("float".equals(name)) {
790 return float.class;
791 } else if ("java.lang.Double".equals(name) || "Double".equals(name)) {
792 return Double.class;
793 } else if ("double".equals(name)) {
794 return double.class;
795 }
796
797 return null;
798 }
799
800 /**
801 * Loads the given class with the provided classloader (may be null).
802 * Will ignore any class not found and return null.
803 *
804 * @param name the name of the class to load
805 * @param loader a provided loader (may be null)
806 * @return the class, or null if it could not be loaded
807 */
808 private static Class<?> doLoadClass(String name, ClassLoader loader) {
809 ObjectHelper.notEmpty(name, "name");
810 if (loader == null) {
811 return null;
812 }
813
814 try {
815 LOG.trace("Loading class: {} using classloader: {}", name, loader);
816 return loader.loadClass(name);
817 } catch (ClassNotFoundException e) {
818 if (LOG.isTraceEnabled()) {
819 LOG.trace("Cannot load class: " + name + " using classloader: " + loader, e);
820 }
821 }
822
823 return null;
824 }
825
826 /**
827 * Attempts to load the given resource as a stream using the thread context
828 * class loader or the class loader used to load this class
829 *
830 * @param name the name of the resource to load
831 * @return the stream or null if it could not be loaded
832 */
833 public static InputStream loadResourceAsStream(String name) {
834 InputStream in = null;
835
836 String resolvedName = resolveUriPath(name);
837 ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
838 if (contextClassLoader != null) {
839 in = contextClassLoader.getResourceAsStream(resolvedName);
840 }
841 if (in == null) {
842 in = ObjectHelper.class.getClassLoader().getResourceAsStream(resolvedName);
843 }
844
845 return in;
846 }
847
848 /**
849 * Attempts to load the given resource as a stream using the thread context
850 * class loader or the class loader used to load this class
851 *
852 * @param name the name of the resource to load
853 * @return the stream or null if it could not be loaded
854 */
855 public static URL loadResourceAsURL(String name) {
856 URL url = null;
857
858 String resolvedName = resolveUriPath(name);
859 ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
860 if (contextClassLoader != null) {
861 url = contextClassLoader.getResource(resolvedName);
862 }
863 if (url == null) {
864 url = ObjectHelper.class.getClassLoader().getResource(resolvedName);
865 }
866
867 return url;
868 }
869
870 /**
871 * Attempts to load the given resources from the given package name using the thread context
872 * class loader or the class loader used to load this class
873 *
874 * @param packageName the name of the package to load its resources
875 * @return the URLs for the resources or null if it could not be loaded
876 */
877 public static Enumeration<URL> loadResourcesAsURL(String packageName) {
878 Enumeration<URL> url = null;
879
880 ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
881 if (contextClassLoader != null) {
882 try {
883 url = contextClassLoader.getResources(packageName);
884 } catch (IOException e) {
885 // ignore
886 }
887 }
888 if (url == null) {
889 try {
890 url = ObjectHelper.class.getClassLoader().getResources(packageName);
891 } catch (IOException e) {
892 // ignore
893 }
894 }
895
896 return url;
897 }
898
899 /**
900 * Helper operation used to remove relative path notation from
901 * resources. Most critical for resources on the Classpath
902 * as resource loaders will not resolve the relative paths correctly.
903 *
904 * @param name the name of the resource to load
905 * @return the modified or unmodified string if there were no changes
906 */
907 private static String resolveUriPath(String name) {
908 String answer = name;
909 if (answer.indexOf("//") > -1) {
910 answer = answer.replaceAll("//", "/");
911 }
912 if (answer.indexOf("../") > -1) {
913 answer = answer.replaceAll("[A-Za-z0-9]*/\\.\\./", "");
914 }
915 if (answer.indexOf("./") > -1) {
916 answer = answer.replaceAll("\\./", "");
917 }
918 return answer;
919 }
920
921 /**
922 * A helper method to invoke a method via reflection and wrap any exceptions
923 * as {@link RuntimeCamelException} instances
924 *
925 * @param method the method to invoke
926 * @param instance the object instance (or null for static methods)
927 * @param parameters the parameters to the method
928 * @return the result of the method invocation
929 */
930 public static Object invokeMethod(Method method, Object instance, Object... parameters) {
931 try {
932 return method.invoke(instance, parameters);
933 } catch (IllegalAccessException e) {
934 throw new RuntimeCamelException(e);
935 } catch (InvocationTargetException e) {
936 throw ObjectHelper.wrapRuntimeCamelException(e.getCause());
937 }
938 }
939
940 /**
941 * Tests whether the target method overrides the source method.
942 * <p/>
943 * Tests whether they have the same name, return type, and parameter list.
944 *
945 * @param source the source method
946 * @param target the target method
947 * @return <tt>true</tt> if it override, <tt>false</tt> otherwise
948 */
949 public static boolean isOverridingMethod(Method source, Method target) {
950 if (source.getName().equals(target.getName())
951 && source.getReturnType().equals(target.getReturnType())
952 && source.getParameterTypes().length == target.getParameterTypes().length) {
953
954 // test if parameter types is the same as well
955 for (int i = 0; i < source.getParameterTypes().length; i++) {
956 if (!(source.getParameterTypes()[i].equals(target.getParameterTypes()[i]))) {
957 return false;
958 }
959 }
960
961 // the have same name, return type and parameter list, so its overriding
962 return true;
963 }
964
965 return false;
966 }
967
968 /**
969 * Returns a list of methods which are annotated with the given annotation
970 *
971 * @param type the type to reflect on
972 * @param annotationType the annotation type
973 * @return a list of the methods found
974 */
975 public static List<Method> findMethodsWithAnnotation(Class<?> type,
976 Class<? extends Annotation> annotationType) {
977 return findMethodsWithAnnotation(type, annotationType, false);
978 }
979
980 /**
981 * Returns a list of methods which are annotated with the given annotation
982 *
983 * @param type the type to reflect on
984 * @param annotationType the annotation type
985 * @param checkMetaAnnotations check for meta annotations
986 * @return a list of the methods found
987 */
988 public static List<Method> findMethodsWithAnnotation(Class<?> type,
989 Class<? extends Annotation> annotationType,
990 boolean checkMetaAnnotations) {
991 List<Method> answer = new ArrayList<Method>();
992 do {
993 Method[] methods = type.getDeclaredMethods();
994 for (Method method : methods) {
995 if (hasAnnotation(method, annotationType, checkMetaAnnotations)) {
996 answer.add(method);
997 }
998 }
999 type = type.getSuperclass();
1000 } while (type != null);
1001 return answer;
1002 }
1003
1004 /**
1005 * Checks if a Class or Method are annotated with the given annotation
1006 *
1007 * @param elem the Class or Method to reflect on
1008 * @param annotationType the annotation type
1009 * @param checkMetaAnnotations check for meta annotations
1010 * @return true if annotations is present
1011 */
1012 public static boolean hasAnnotation(AnnotatedElement elem, Class<? extends Annotation> annotationType,
1013 boolean checkMetaAnnotations) {
1014 if (elem.isAnnotationPresent(annotationType)) {
1015 return true;
1016 }
1017 if (checkMetaAnnotations) {
1018 for (Annotation a : elem.getAnnotations()) {
1019 for (Annotation meta : a.annotationType().getAnnotations()) {
1020 if (meta.annotationType().getName().equals(annotationType.getName())) {
1021 return true;
1022 }
1023 }
1024 }
1025 }
1026 return false;
1027 }
1028
1029 /**
1030 * Turns the given object arrays into a meaningful string
1031 *
1032 * @param objects an array of objects or null
1033 * @return a meaningful string
1034 */
1035 public static String asString(Object[] objects) {
1036 if (objects == null) {
1037 return "null";
1038 } else {
1039 StringBuilder buffer = new StringBuilder("{");
1040 int counter = 0;
1041 for (Object object : objects) {
1042 if (counter++ > 0) {
1043 buffer.append(", ");
1044 }
1045 String text = (object == null) ? "null" : object.toString();
1046 buffer.append(text);
1047 }
1048 buffer.append("}");
1049 return buffer.toString();
1050 }
1051 }
1052
1053 /**
1054 * Returns true if a class is assignable from another class like the
1055 * {@link Class#isAssignableFrom(Class)} method but which also includes
1056 * coercion between primitive types to deal with Java 5 primitive type
1057 * wrapping
1058 */
1059 public static boolean isAssignableFrom(Class<?> a, Class<?> b) {
1060 a = convertPrimitiveTypeToWrapperType(a);
1061 b = convertPrimitiveTypeToWrapperType(b);
1062 return a.isAssignableFrom(b);
1063 }
1064
1065 /**
1066 * Converts primitive types such as int to its wrapper type like
1067 * {@link Integer}
1068 */
1069 public static Class<?> convertPrimitiveTypeToWrapperType(Class<?> type) {
1070 Class<?> rc = type;
1071 if (type.isPrimitive()) {
1072 if (type == int.class) {
1073 rc = Integer.class;
1074 } else if (type == long.class) {
1075 rc = Long.class;
1076 } else if (type == double.class) {
1077 rc = Double.class;
1078 } else if (type == float.class) {
1079 rc = Float.class;
1080 } else if (type == short.class) {
1081 rc = Short.class;
1082 } else if (type == byte.class) {
1083 rc = Byte.class;
1084 } else if (type == boolean.class) {
1085 rc = Boolean.class;
1086 }
1087 }
1088 return rc;
1089 }
1090
1091 /**
1092 * Helper method to return the default character set name
1093 */
1094 public static String getDefaultCharacterSet() {
1095 return Charset.defaultCharset().name();
1096 }
1097
1098 /**
1099 * Returns the Java Bean property name of the given method, if it is a
1100 * setter
1101 */
1102 public static String getPropertyName(Method method) {
1103 String propertyName = method.getName();
1104 if (propertyName.startsWith("set") && method.getParameterTypes().length == 1) {
1105 propertyName = propertyName.substring(3, 4).toLowerCase(Locale.ENGLISH) + propertyName.substring(4);
1106 }
1107 return propertyName;
1108 }
1109
1110 /**
1111 * Returns true if the given collection of annotations matches the given type
1112 */
1113 public static boolean hasAnnotation(Annotation[] annotations, Class<?> type) {
1114 for (Annotation annotation : annotations) {
1115 if (type.isInstance(annotation)) {
1116 return true;
1117 }
1118 }
1119 return false;
1120 }
1121
1122 /**
1123 * Gets the annotation from the given instance.
1124 *
1125 * @param instance the instance
1126 * @param type the annotation
1127 * @return the annotation, or <tt>null</tt> if the instance does not have the given annotation
1128 */
1129 public static <A extends java.lang.annotation.Annotation> A getAnnotation(Object instance, Class<A> type) {
1130 return instance.getClass().getAnnotation(type);
1131 }
1132
1133 /**
1134 * Closes the given resource if it is available, logging any closing
1135 * exceptions to the given log
1136 *
1137 * @param closeable the object to close
1138 * @param name the name of the resource
1139 * @param log the log to use when reporting closure warnings
1140 * @deprecated will be removed in Camel 3.0. Instead use {@link org.apache.camel.util.IOHelper#close(java.io.Closeable, String, org.slf4j.Logger)} instead
1141 */
1142 @Deprecated
1143 public static void close(Closeable closeable, String name, Logger log) {
1144 IOHelper.close(closeable, name, log);
1145 }
1146
1147
1148 /**
1149 * Converts the given value to the required type or throw a meaningful exception
1150 */
1151 @SuppressWarnings("unchecked")
1152 public static <T> T cast(Class<T> toType, Object value) {
1153 if (toType == boolean.class) {
1154 return (T)cast(Boolean.class, value);
1155 } else if (toType.isPrimitive()) {
1156 Class<?> newType = convertPrimitiveTypeToWrapperType(toType);
1157 if (newType != toType) {
1158 return (T)cast(newType, value);
1159 }
1160 }
1161 try {
1162 return toType.cast(value);
1163 } catch (ClassCastException e) {
1164 throw new IllegalArgumentException("Failed to convert: "
1165 + value + " to type: " + toType.getName() + " due to: " + e, e);
1166 }
1167 }
1168
1169 /**
1170 * A helper method to create a new instance of a type using the default
1171 * constructor arguments.
1172 */
1173 public static <T> T newInstance(Class<T> type) {
1174 try {
1175 return type.newInstance();
1176 } catch (InstantiationException e) {
1177 throw new RuntimeCamelException(e);
1178 } catch (IllegalAccessException e) {
1179 throw new RuntimeCamelException(e);
1180 }
1181 }
1182
1183 /**
1184 * A helper method to create a new instance of a type using the default
1185 * constructor arguments.
1186 */
1187 public static <T> T newInstance(Class<?> actualType, Class<T> expectedType) {
1188 try {
1189 Object value = actualType.newInstance();
1190 return cast(expectedType, value);
1191 } catch (InstantiationException e) {
1192 throw new RuntimeCamelException(e);
1193 } catch (IllegalAccessException e) {
1194 throw new RuntimeCamelException(e);
1195 }
1196 }
1197
1198 /**
1199 * Does the given class have a default public no-arg constructor.
1200 */
1201 public static boolean hasDefaultPublicNoArgConstructor(Class<?> type) {
1202 // getConstructors() returns only public constructors
1203 for (Constructor<?> ctr : type.getConstructors()) {
1204 if (ctr.getParameterTypes().length == 0) {
1205 return true;
1206 }
1207 }
1208 return false;
1209 }
1210
1211 /**
1212 * Returns true if the given name is a valid java identifier
1213 */
1214 public static boolean isJavaIdentifier(String name) {
1215 if (name == null) {
1216 return false;
1217 }
1218 int size = name.length();
1219 if (size < 1) {
1220 return false;
1221 }
1222 if (Character.isJavaIdentifierStart(name.charAt(0))) {
1223 for (int i = 1; i < size; i++) {
1224 if (!Character.isJavaIdentifierPart(name.charAt(i))) {
1225 return false;
1226 }
1227 }
1228 return true;
1229 }
1230 return false;
1231 }
1232
1233 /**
1234 * Returns the type of the given object or null if the value is null
1235 */
1236 public static Object type(Object bean) {
1237 return bean != null ? bean.getClass() : null;
1238 }
1239
1240 /**
1241 * Evaluate the value as a predicate which attempts to convert the value to
1242 * a boolean otherwise true is returned if the value is not null
1243 */
1244 public static boolean evaluateValuePredicate(Object value) {
1245 if (value instanceof Boolean) {
1246 return (Boolean)value;
1247 } else if (value instanceof String) {
1248 if ("true".equalsIgnoreCase((String)value)) {
1249 return true;
1250 } else if ("false".equalsIgnoreCase((String)value)) {
1251 return false;
1252 }
1253 } else if (value instanceof NodeList) {
1254 // is it an empty dom with empty attributes
1255 if (value instanceof Node && ((Node)value).hasAttributes()) {
1256 return true;
1257 }
1258 NodeList list = (NodeList) value;
1259 return list.getLength() > 0;
1260 } else if (value instanceof Collection) {
1261 // is it an empty collection
1262 Collection<?> col = (Collection<?>) value;
1263 return col.size() > 0;
1264 }
1265 return value != null;
1266 }
1267
1268 /**
1269 * Wraps the caused exception in a {@link RuntimeCamelException} if its not
1270 * already such an exception.
1271 *
1272 * @param e the caused exception
1273 * @return the wrapper exception
1274 */
1275 public static RuntimeCamelException wrapRuntimeCamelException(Throwable e) {
1276 if (e instanceof RuntimeCamelException) {
1277 // don't double wrap
1278 return (RuntimeCamelException)e;
1279 } else {
1280 return new RuntimeCamelException(e);
1281 }
1282 }
1283
1284 /**
1285 * Wraps the caused exception in a {@link CamelExecutionException} if its not
1286 * already such an exception.
1287 *
1288 * @param e the caused exception
1289 * @return the wrapper exception
1290 */
1291 public static CamelExecutionException wrapCamelExecutionException(Exchange exchange, Throwable e) {
1292 if (e instanceof CamelExecutionException) {
1293 // don't double wrap
1294 return (CamelExecutionException)e;
1295 } else {
1296 return new CamelExecutionException("Exception occurred during execution", exchange, e);
1297 }
1298 }
1299
1300 /**
1301 * Cleans the string to a pure Java identifier so we can use it for loading class names.
1302 * <p/>
1303 * Especially from Spring DSL people can have \n \t or other characters that otherwise
1304 * would result in ClassNotFoundException
1305 *
1306 * @param name the class name
1307 * @return normalized classname that can be load by a class loader.
1308 */
1309 public static String normalizeClassName(String name) {
1310 StringBuilder sb = new StringBuilder(name.length());
1311 for (char ch : name.toCharArray()) {
1312 if (ch == '.' || ch == '[' || ch == ']' || ch == '-' || Character.isJavaIdentifierPart(ch)) {
1313 sb.append(ch);
1314 }
1315 }
1316 return sb.toString();
1317 }
1318
1319 /**
1320 * Creates an iterator to walk the exception from the bottom up
1321 * (the last caused by going upwards to the root exception).
1322 *
1323 * @param exception the exception
1324 * @return the iterator
1325 */
1326 public static Iterator<Throwable> createExceptionIterator(Throwable exception) {
1327 return new ExceptionIterator(exception);
1328 }
1329
1330 /**
1331 * Retrieves the given exception type from the exception.
1332 * <p/>
1333 * Is used to get the caused exception that typically have been wrapped in some sort
1334 * of Camel wrapper exception
1335 * <p/>
1336 * The strategy is to look in the exception hierarchy to find the first given cause that matches the type.
1337 * Will start from the bottom (the real cause) and walk upwards.
1338 *
1339 * @param type the exception type wanted to retrieve
1340 * @param exception the caused exception
1341 * @return the exception found (or <tt>null</tt> if not found in the exception hierarchy)
1342 */
1343 public static <T> T getException(Class<T> type, Throwable exception) {
1344 if (exception == null) {
1345 return null;
1346 }
1347
1348 // walk the hierarchy and look for it
1349 Iterator<Throwable> it = createExceptionIterator(exception);
1350 while (it.hasNext()) {
1351 Throwable e = it.next();
1352 if (type.isInstance(e)) {
1353 return type.cast(e);
1354 }
1355 }
1356
1357 // not found
1358 return null;
1359 }
1360
1361 /**
1362 * Creates a {@link Scanner} for scanning the given value.
1363 *
1364 * @param exchange the current exchange
1365 * @param value the value, typically the message IN body
1366 * @return the scanner, is newer <tt>null</tt>
1367 */
1368 public static Scanner getScanner(Exchange exchange, Object value) {
1369 if (value instanceof WrappedFile) {
1370 // generic file is just a wrapper for the real file so call again with the real file
1371 WrappedFile<?> gf = (WrappedFile<?>) value;
1372 return getScanner(exchange, gf.getFile());
1373 }
1374
1375 String charset = exchange.getProperty(Exchange.CHARSET_NAME, String.class);
1376
1377 Scanner scanner = null;
1378 if (value instanceof Readable) {
1379 scanner = new Scanner((Readable)value);
1380 } else if (value instanceof InputStream) {
1381 scanner = charset == null ? new Scanner((InputStream)value) : new Scanner((InputStream)value, charset);
1382 } else if (value instanceof File) {
1383 try {
1384 scanner = charset == null ? new Scanner((File)value) : new Scanner((File)value, charset);
1385 } catch (FileNotFoundException e) {
1386 throw new RuntimeCamelException(e);
1387 }
1388 } else if (value instanceof String) {
1389 scanner = new Scanner((String)value);
1390 } else if (value instanceof ReadableByteChannel) {
1391 scanner = charset == null ? new Scanner((ReadableByteChannel)value) : new Scanner((ReadableByteChannel)value, charset);
1392 }
1393
1394 if (scanner == null) {
1395 // value is not a suitable type, try to convert value to a string
1396 String text = exchange.getContext().getTypeConverter().convertTo(String.class, exchange, value);
1397 if (text != null) {
1398 scanner = new Scanner(text);
1399 }
1400 }
1401
1402 if (scanner == null) {
1403 scanner = new Scanner("");
1404 }
1405
1406 return scanner;
1407 }
1408
1409 public static String getIdentityHashCode(Object object) {
1410 return "0x" + Integer.toHexString(System.identityHashCode(object));
1411 }
1412
1413 /**
1414 * Lookup the constant field on the given class with the given name
1415 *
1416 * @param clazz the class
1417 * @param name the name of the field to lookup
1418 * @return the value of the constant field, or <tt>null</tt> if not found
1419 */
1420 public static String lookupConstantFieldValue(Class<?> clazz, String name) {
1421 if (clazz == null) {
1422 return null;
1423 }
1424
1425 for (Field field : clazz.getFields()) {
1426 if (field.getName().equals(name)) {
1427 try {
1428 return (String) field.get(null);
1429 } catch (IllegalAccessException e) {
1430 // ignore
1431 return null;
1432 }
1433 }
1434 }
1435
1436 return null;
1437 }
1438
1439 /**
1440 * Is the given value a numeric NaN type
1441 *
1442 * @param value the value
1443 * @return <tt>true</tt> if its a {@link Float#NaN} or {@link Double#NaN}.
1444 */
1445 public static boolean isNaN(Object value) {
1446 if (value == null || !(value instanceof Number)) {
1447 return false;
1448 }
1449 // value must be a number
1450 return value.equals(Float.NaN) || value.equals(Double.NaN);
1451 }
1452
1453 private static final class ExceptionIterator implements Iterator<Throwable> {
1454 private List<Throwable> tree = new ArrayList<Throwable>();
1455 private Iterator<Throwable> it;
1456
1457 public ExceptionIterator(Throwable exception) {
1458 Throwable current = exception;
1459 // spool to the bottom of the caused by tree
1460 while (current != null) {
1461 tree.add(current);
1462 current = current.getCause();
1463 }
1464
1465 // reverse tree so we go from bottom to top
1466 Collections.reverse(tree);
1467 it = tree.iterator();
1468 }
1469
1470 public boolean hasNext() {
1471 return it.hasNext();
1472 }
1473
1474 public Throwable next() {
1475 return it.next();
1476 }
1477
1478 public void remove() {
1479 it.remove();
1480 }
1481 }
1482 }