ConnectorPropertiesUtils.java
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.apache.doris.datasource.property;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Utility class for handling fields annotated with {@link ConnectorProperty}.
* Provides methods to extract supported connector properties from a class and bind them to an object instance.
*/
public class ConnectorPropertiesUtils {
/**
* Retrieves all fields annotated with {@link ConnectorProperty} from the given class and its superclasses,
* where {@code supported = true}.
*
* @param clazz the target class to inspect
* @return list of supported fields annotated with {@code @ConnectorProperty}
*/
public static List<Field> getConnectorProperties(Class<?> clazz) {
List<Field> fields = new ArrayList<>();
Class<?> currentClass = clazz;
while (currentClass != null && currentClass != Object.class) {
for (Field field : currentClass.getDeclaredFields()) {
if (field.isAnnotationPresent(ConnectorProperty.class)) {
ConnectorProperty connectorProperty = field.getAnnotation(ConnectorProperty.class);
if (connectorProperty.supported()) {
field.setAccessible(true);
fields.add(field);
}
}
}
currentClass = currentClass.getSuperclass();
}
return fields;
}
/**
* Binds matching property values from the given map to the corresponding fields in the target object.
* Only fields annotated with {@code @ConnectorProperty} and marked as supported will be set.
*
* @param target the target object to populate
* @param props the key-value map of string properties
* @throws RuntimeException if a conversion or reflection error occurs
*/
public static void bindConnectorProperties(Object target, Map<String, String> props) {
List<Field> supportedProps = getConnectorProperties(target.getClass());
for (Field field : supportedProps) {
String matchedName = getMatchedPropertyName(field, props);
if (matchedName != null) {
try {
Object rawValue = props.get(matchedName);
Object convertedValue = convertValue(rawValue, field.getType());
field.set(target, convertedValue);
} catch (Exception e) {
throw new IllegalArgumentException(
"Failed to set property '" + matchedName + "' on " + target.getClass().getSimpleName()
+ ": " + e.getMessage(), e
);
}
}
}
}
/**
* Finds the first matching property name from the field's {@code @ConnectorProperty#names()} list
* that exists in the provided property map.
*
* @param field the field to match
* @param props the available property map
* @return the matching property name if found; {@code null} otherwise
*/
public static String getMatchedPropertyName(Field field, Map<String, String> props) {
ConnectorProperty annotation = field.getAnnotation(ConnectorProperty.class);
if (annotation == null) {
return null;
}
for (String name : annotation.names()) {
if (props.containsKey(name)) {
return name;
}
}
return null;
}
/**
* Converts a string-based value into a strongly-typed object based on the target field type.
*
* @param value the raw value (usually a string from configuration)
* @param targetType the field's target type
* @return the converted value
* @throws IllegalArgumentException if the type is unsupported
*/
public static Object convertValue(Object value, Class<?> targetType) {
if (value == null) {
return null;
}
String str = value.toString().trim();
if (targetType == String.class) {
return str;
}
if (targetType == Integer.class || targetType == int.class) {
return Integer.parseInt(str);
}
if (targetType == Boolean.class || targetType == boolean.class) {
return Boolean.parseBoolean(str);
}
if (targetType == Long.class || targetType == long.class) {
return Long.parseLong(str);
}
if (targetType == Double.class || targetType == double.class) {
return Double.parseDouble(str);
}
throw new IllegalArgumentException("Unsupported property type: " + targetType.getName());
}
}