in core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/IntrospectionSupport.java [446:679]
static boolean setProperty(
CamelContext context, TypeConverter typeConverter, Object target, String name, Object value, String refName,
boolean allowBuilderPattern, boolean allowPrivateSetter, boolean ignoreCase)
throws Exception {
// does the property name include a lookup key, then we need to set the property as a map or list
if (name.contains("[") && name.endsWith("]")) {
int pos = name.indexOf('[');
String lookupKey = name.substring(pos + 1, name.length() - 1);
String key = name.substring(0, pos);
Object obj = IntrospectionSupport.getOrElseProperty(target, key, null, ignoreCase);
if (obj == null) {
// it was supposed to be a list or map, but its null, so lets create a new list or map and set it automatically
Method getter = IntrospectionSupport.getPropertyGetter(target.getClass(), key, ignoreCase);
if (getter != null) {
// what type does it have
Class<?> returnType = getter.getReturnType();
if (Map.class.isAssignableFrom(returnType)) {
obj = new LinkedHashMap<>();
} else if (Collection.class.isAssignableFrom(returnType)) {
obj = new ArrayList<>();
} else if (returnType.isArray()) {
obj = Array.newInstance(returnType.getComponentType(), 0);
}
} else {
// fallback as map type
obj = new LinkedHashMap<>();
}
boolean hit = IntrospectionSupport.setProperty(context, target, key, obj);
if (!hit) {
throw new IllegalArgumentException(
"Cannot set property: " + name + " as a Map because target bean has no setter method for the Map");
}
}
if (obj instanceof Map) {
Map<Object, Object> map = (Map) obj;
if (context != null && refName != null && value == null) {
String s = refName.replace("#", "");
value = CamelContextHelper.lookup(context, s);
}
map.put(lookupKey, value);
return true;
} else if (obj instanceof List) {
List<Object> list = (List) obj;
if (context != null && refName != null && value == null) {
String s = refName.replace("#", "");
value = CamelContextHelper.lookup(context, s);
}
if (isNotEmpty(lookupKey)) {
int idx = Integer.parseInt(lookupKey);
if (idx < list.size()) {
list.set(idx, value);
} else if (idx == list.size()) {
list.add(value);
} else {
// If the list implementation is based on an array, we
// can increase tha capacity to the required value to
// avoid potential re-allocation weh invoking List::add.
//
// Note that ArrayList is the default List impl that
// is automatically created if the property is null.
if (list instanceof ArrayList<?> al) {
al.ensureCapacity(idx + 1);
}
while (list.size() < idx) {
list.add(null);
}
list.add(idx, value);
}
} else {
list.add(value);
}
return true;
} else if (obj != null && obj.getClass().isArray()) {
if (context != null && refName != null && value == null) {
String s = refName.replace("#", "");
value = CamelContextHelper.lookup(context, s);
}
int idx = Integer.parseInt(lookupKey);
int size = Array.getLength(obj);
if (idx >= size) {
obj = Arrays.copyOf((Object[]) obj, idx + 1);
// replace array
boolean hit = IntrospectionSupport.setProperty(context, target, key, obj);
if (!hit) {
throw new IllegalArgumentException(
"Cannot set property: " + name
+ " as an array because target bean has no setter method for the array");
}
}
Array.set(obj, idx, value);
return true;
} else {
// not a map or list
throw new IllegalArgumentException(
"Cannot set property: " + name
+ " as either a Map/List/array because target bean is not a Map, List or array type: "
+ target);
}
}
Class<?> clazz = target.getClass();
Collection<Method> setters;
// we need to lookup the value from the registry
if (context != null && refName != null && value == null) {
setters = findSetterMethodsOrderedByParameterType(clazz, name, allowBuilderPattern, allowPrivateSetter, ignoreCase);
} else {
// find candidates of setter methods as there can be overloaded setters
setters = findSetterMethods(clazz, name, value, allowBuilderPattern, allowPrivateSetter, ignoreCase);
}
if (setters.isEmpty()) {
return false;
}
// loop and execute the best setter method
Exception typeConversionFailed = null;
Method stringSetterMethod = null;
Iterator<Method> it = setters.iterator();
while (it.hasNext()) {
Method setter = it.next();
Class<?> parameterType = setter.getParameterTypes()[0];
if (parameterType.getName().equals("java.lang.String")) {
stringSetterMethod = setter;
}
Object ref = value;
// try and lookup the reference based on the method
if (context != null && refName != null && ref == null) {
String s = refName.replace("#", "");
ref = CamelContextHelper.lookup(context, s);
if (ref == null) {
// try the next method if nothing was found
// if we did not found a good candidate then fallback to use the string setter (if possible) with the actual ref name value as-is
if (!it.hasNext() && stringSetterMethod != null) {
setter = stringSetterMethod;
ref = refName;
} else {
continue;
}
} else {
// setter method has not the correct type
// (must use ObjectHelper.isAssignableFrom which takes primitive types into account)
boolean assignable = ObjectHelper.isAssignableFrom(parameterType, ref.getClass());
if (!assignable) {
continue;
}
}
}
boolean myself = false;
try {
try {
// If the type is null or it matches the needed type, just use the value directly
if (value == null || ObjectHelper.isAssignableFrom(parameterType, ref.getClass())) {
// we may want to set options on classes that has package view visibility, so override the accessible
setter.setAccessible(true);
setter.invoke(target, ref);
if (LOG.isTraceEnabled()) {
// hide sensitive data
String val = ref != null ? ref.toString() : "";
if (SECRETS.matcher(name).find()) {
val = "xxxxxx";
}
LOG.trace("Configured property: {} on bean: {} with value: {}", name, target, val);
}
return true;
} else {
// We need to convert it
// special for boolean values with string values as we only want to accept "true" or "false"
if ((parameterType == Boolean.class || parameterType == boolean.class) && ref instanceof String val) {
if (!val.equalsIgnoreCase("true") && !val.equalsIgnoreCase("false")) {
// this is our self
myself = true;
throw new IllegalArgumentException(
"Cannot convert the String value: " + ref + " to type: " + parameterType
+ " as the value is not true or false");
}
}
Object convertedValue
= typeConverter != null ? typeConverter.mandatoryConvertTo(parameterType, ref) : ref;
// we may want to set options on classes that has package view visibility, so override the accessible
setter.setAccessible(true);
setter.invoke(target, convertedValue);
if (LOG.isTraceEnabled()) {
// hide sensitive data
String val = ref.toString();
if (SECRETS.matcher(name).find()) {
val = "xxxxxx";
}
LOG.trace("Configured property: {} on bean: {} with value: {}", name, target, val);
}
return true;
}
} catch (InvocationTargetException e) {
// lets unwrap the exception
Throwable throwable = e.getCause();
if (throwable instanceof Exception exception) {
throw exception;
} else {
throw (Error) throwable;
}
}
// ignore exceptions as there could be another setter method where we could type convert successfully
} catch (IllegalArgumentException e) {
// this can be either our own or while trying to set the property on the bean that fails in the 3rd party component
if (myself) {
typeConversionFailed = e;
} else {
throw e;
}
} catch (SecurityException | NoTypeConversionAvailableException e) {
typeConversionFailed = e;
}
LOG.trace("Setter \"{}\" with parameter type \"{}\" could not be used for type conversions of {}",
setter, parameterType, ref);
}
if (typeConversionFailed != null && !isPropertyPlaceholder(context, value)) {
// we did not find a setter method to use, and if we did try to use a type converter then throw
// this kind of exception as the caused by will hint this error
throw new IllegalArgumentException(
"Could not find a suitable setter for property: " + name
+ " as there isn't a setter method with same type: "
+ (value != null ? value.getClass().getCanonicalName() : "[null]")
+ " nor type conversion possible: " + typeConversionFailed.getMessage());
} else {
return false;
}
}