in artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/ConfigurationImpl.java [656:964]
public void populateWithProperties(final Object target, final String propsId, Map<String, Object> beanProperties) throws InvocationTargetException, IllegalAccessException {
CollectionAutoFillPropertiesUtil autoFillCollections = new CollectionAutoFillPropertiesUtil(getBrokerPropertiesRemoveValue(beanProperties));
BeanUtilsBean beanUtils = new BeanUtilsBean(new ConvertUtilsBean(), autoFillCollections) {
// initialize the given property of the given bean with a new instance of the property class
private Object initProperty(Object bean, String name) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
PropertyDescriptor descriptor = getPropertyUtils().getPropertyDescriptor(bean, name);
if (descriptor == null) {
throw new InvocationTargetException(null, "No accessor method descriptor for: " + name + " on: " + bean.getClass());
}
Method writeMethod = descriptor.getWriteMethod();
if (writeMethod == null) {
throw new InvocationTargetException(null, "No Write method for: " + name + " on: " + bean.getClass());
}
Object propertyInstance;
try {
propertyInstance = descriptor.getPropertyType().getDeclaredConstructor().newInstance();
} catch (InstantiationException e) {
throw new InvocationTargetException(e);
}
writeMethod.invoke(bean, propertyInstance);
return propertyInstance;
}
// override to treat missing properties as errors, not skip as the default impl does
@Override
public void setProperty(final Object bean, String name, final Object value) throws InvocationTargetException, IllegalAccessException {
{
if (logger.isDebugEnabled()) {
logger.debug("setProperty on {}, name: {}, value: {}", bean.getClass(), name, value);
}
// Resolve any nested expression to get the actual target bean
Object target = bean;
final Resolver resolver = getPropertyUtils().getResolver();
while (resolver.hasNested(name)) {
try {
String nextName = resolver.next(name);
Object nextTarget = getPropertyUtils().getProperty(target, nextName);
if (nextTarget == null) {
if (resolver.isMapped(nextName)) {
throw new InvocationTargetException(null, "Entry does not exist in: " + resolver.getProperty(name) + " for mapped key: " + resolver.getKey(name));
}
nextTarget = initProperty(target, nextName);
}
target = nextTarget;
name = resolver.remove(name);
} catch (final NoSuchMethodException e) {
throw new InvocationTargetException(e, "No getter for property:" + name + ", on: " + bean);
}
}
logger.trace("resolved target, bean: {}, name: {}", target.getClass(), name);
final String propName = resolver.getProperty(name); // Simple name of target property
if (autoFillCollections.isRemoveValue(value)) {
logger.trace("removing from target, bean: {}, name: {}", target.getClass(), name);
// we may do a further get but no longer want to reference our nested collection stack
if (!autoFillCollections.collections.isEmpty()) {
autoFillCollections.collections.pop();
}
if (target instanceof Map targetMap) {
Iterator<Map.Entry<String, Object>> i = targetMap.entrySet().iterator();
while (i.hasNext()) {
String key = i.next().getKey();
if (propName.equals(key)) {
i.remove();
break;
}
}
} else if (target instanceof Collection targetCollection) {
try {
autoFillCollections.removeByNameProperty(propName, targetCollection);
} catch (NoSuchMethodException e) {
throw new InvocationTargetException(e, "Can only remove named entries from collections or maps" + name + ", on: " + target);
}
} else {
throw new InvocationTargetException(null, "Can only remove entries from collections or maps" + name + ", on: " + target);
}
logger.trace("removed from target, bean: {}, name: {}", target.getClass(), name);
return;
}
Class<?> type = null; // Java type of target property
final int index = resolver.getIndex(name); // Indexed subscript value (if any)
final String key = resolver.getKey(name); // Mapped key value (if any)
// Calculate the property type
if (target instanceof DynaBean) {
throw new InvocationTargetException(null, "Cannot determine DynaBean type to access: " + name + " on: " + target);
} else if (target instanceof Map) {
type = Object.class;
} else if (target != null && target.getClass().isArray() && index >= 0) {
type = Array.get(target, index).getClass();
} else {
PropertyDescriptor descriptor = null;
try {
descriptor = getPropertyUtils().getPropertyDescriptor(target, name);
if (descriptor == null) {
throw new InvocationTargetException(null, "No accessor method descriptor for: " + name + " on: " + target.getClass());
}
} catch (final NoSuchMethodException e) {
throw new InvocationTargetException(e, "Failed to get descriptor for: " + name + " on: " + target.getClass());
}
if (descriptor instanceof MappedPropertyDescriptor mappedPropertyDescriptor) {
if (mappedPropertyDescriptor.getMappedWriteMethod() == null) {
throw new InvocationTargetException(null, "No mapped Write method for: " + name + " on: " + target.getClass());
}
type = mappedPropertyDescriptor.getMappedPropertyType();
} else if (index >= 0 && descriptor instanceof IndexedPropertyDescriptor indexedPropertyDescriptor) {
if (indexedPropertyDescriptor.getIndexedWriteMethod() == null) {
throw new InvocationTargetException(null, "No indexed Write method for: " + name + " on: " + target.getClass());
}
type = indexedPropertyDescriptor.getIndexedPropertyType();
} else if (index >= 0 && List.class.isAssignableFrom(descriptor.getPropertyType())) {
type = Object.class;
} else if (key != null) {
if (descriptor.getReadMethod() == null) {
throw new InvocationTargetException(null, "No Read method for: " + name + " on: " + target.getClass());
}
type = (value == null) ? Object.class : value.getClass();
} else {
if (descriptor.getWriteMethod() == null) {
throw new InvocationTargetException(null, "No Write method for: " + name + " on: " + target.getClass());
}
type = descriptor.getPropertyType();
}
}
// Convert the specified value to the required type
Object newValue = null;
if (type.isArray() && (index < 0)) { // Scalar value into array
if (value == null) {
final String[] values = new String[1];
values[0] = null;
newValue = getConvertUtils().convert(values, type);
} else if (value instanceof String) {
newValue = getConvertUtils().convert(value, type);
} else if (value instanceof String[] strings) {
newValue = getConvertUtils().convert(strings, type);
} else {
newValue = convert(value, type);
}
} else if (type.isArray()) { // Indexed value into array
if (value instanceof String || value == null) {
newValue = getConvertUtils().convert((String) value, type.getComponentType());
} else if (value instanceof String[] strings) {
newValue = getConvertUtils().convert(strings[0], type.getComponentType());
} else {
newValue = convert(value, type.getComponentType());
}
} else { // Value into scalar
if (value instanceof String possibleDotClassValue) {
if (type != String.class && isClassProperty(possibleDotClassValue)) {
final String clazzName = extractPropertyClassName(possibleDotClassValue);
try {
newValue = ClassloadingUtil.getInstanceWithTypeCheck(clazzName, type, this.getClass().getClassLoader());
} catch (Exception e) {
throw new InvocationTargetException(e, " for dot class value: " + possibleDotClassValue + ", on: " + bean);
}
} else {
newValue = getConvertUtils().convert(possibleDotClassValue, type);
}
} else if (value instanceof String[] strings) {
newValue = getConvertUtils().convert(strings[0], type);
} else {
newValue = convert(value, type);
}
}
// Invoke the setter method
try {
getPropertyUtils().setProperty(target, name, newValue);
} catch (final NoSuchMethodException e) {
throw new InvocationTargetException(e, "Cannot set: " + propName + " on: " + target.getClass());
}
}
}
};
autoFillCollections.setBeanUtilsBean(beanUtils);
// nested property keys delimited by . and enclosed by '"' if they key's themselves contain dots
beanUtils.getPropertyUtils().setResolver(new SurroundResolver(getBrokerPropertiesKeySurround(beanProperties)));
beanUtils.getConvertUtils().register(new Converter() {
@Override
public <T> T convert(Class<T> type, Object value) {
return (T) SimpleString.of(value.toString());
}
}, SimpleString.class);
beanUtils.getConvertUtils().register(new Converter() {
@Override
public <T> T convert(Class<T> type, Object value) {
NamedPropertyConfiguration instance = new NamedPropertyConfiguration();
instance.setName(value.toString());
instance.setProperties(new HashMap<>());
return (T) instance;
}
}, NamedPropertyConfiguration.class);
beanUtils.getConvertUtils().register(new Converter() {
@Override
public <T> T convert(Class<T> type, Object value) {
//we only care about DATABASE type as it is the only one used
if (StoreConfiguration.StoreType.DATABASE.toString().equals(value)) {
return (T) new DatabaseStorageConfiguration();
}
throw ActiveMQMessageBundle.BUNDLE.unsupportedStorePropertyType();
}
}, StoreConfiguration.class);
beanUtils.getConvertUtils().register(new Converter() {
@Override
public <T> T convert(Class<T> type, Object value) {
List<String> convertedValue = new ArrayList<>();
for (String entry : value.toString().split(",")) {
convertedValue.add(entry);
}
return (T) convertedValue;
}
}, java.util.List.class);
beanUtils.getConvertUtils().register(new Converter() {
@Override
public <T> T convert(Class<T> type, Object value) {
Set convertedValue = new HashSet();
for (String entry : value.toString().split(",")) {
convertedValue.add(entry);
}
return (T) convertedValue;
}
}, java.util.Set.class);
beanUtils.getConvertUtils().register(new Converter() {
@Override
public <T> T convert(Class<T> type, Object value) {
Map convertedValue = new HashMap();
for (String entry : value.toString().split(",")) {
if (!entry.isBlank()) {
String[] kv = entry.split("=");
if (2 != kv.length) {
throw new IllegalArgumentException("map value " + value + " not in k=v format");
}
convertedValue.put(kv[0], kv[1]);
}
}
return (T) convertedValue;
}
}, java.util.Map.class);
// support 25K or 25m etc like xml config
beanUtils.getConvertUtils().register(new Converter() {
@Override
public <T> T convert(Class<T> type, Object value) {
return (T) (Long) ByteUtil.convertTextBytes(value.toString());
}
}, Long.TYPE);
beanUtils.getConvertUtils().register(new Converter() {
@Override
public <T> T convert(Class<T> type, Object value) {
HAPolicyConfiguration.TYPE haPolicyType =
HAPolicyConfiguration.TYPE.valueOf(value.toString());
return switch (haPolicyType) {
case PRIMARY_ONLY -> (T) new LiveOnlyPolicyConfiguration();
case REPLICATION_PRIMARY_QUORUM_VOTING -> (T) new ReplicatedPolicyConfiguration();
case REPLICATION_BACKUP_QUORUM_VOTING -> (T) new ReplicaPolicyConfiguration();
case SHARED_STORE_PRIMARY -> (T) new SharedStorePrimaryPolicyConfiguration();
case SHARED_STORE_BACKUP -> (T) new SharedStoreBackupPolicyConfiguration();
case COLOCATED -> (T) new ColocatedPolicyConfiguration();
case REPLICATION_PRIMARY_LOCK_MANAGER -> (T) ReplicationPrimaryPolicyConfiguration.withDefault();
case REPLICATION_BACKUP_LOCK_MANAGER -> (T) ReplicationBackupPolicyConfiguration.withDefault();
};
}
}, HAPolicyConfiguration.class);
BeanSupport.customise(beanUtils);
logger.trace("populate: bean: {} with {}", this, beanProperties);
Map<String, String> errors = new LinkedHashMap<>();
// Loop through the property name/value pairs to be set
for (final Map.Entry<String, ? extends Object> entry : beanProperties.entrySet()) {
// Identify the property name and value(s) to be assigned
final String name = entry.getKey();
try {
if (logger.isDebugEnabled()) {
logger.debug("set property target={}, name = {}, value = {}", target.getClass(), name, entry.getValue());
}
// Perform the assignment for this property
beanUtils.setProperty(target, name, entry.getValue());
} catch (InvocationTargetException invocationTargetException) {
logger.trace("failed to populate property with key: {}", name, invocationTargetException);
Throwable toLog = invocationTargetException;
if (invocationTargetException.getCause() != null) {
toLog = invocationTargetException.getCause();
}
trackError(errors, entry, toLog);
} catch (Exception oops) {
trackError(errors, entry, oops);
}
}
updateApplyStatus(propsId, errors);
}