static boolean setProperty()

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;
        }
    }