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