import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.primitives.Primitives;
-import java.beans.BeanInfo;
-import java.beans.ConstructorProperties;
-import java.beans.IntrospectionException;
-import java.beans.Introspector;
-import java.beans.MethodDescriptor;
-import java.beans.PropertyDescriptor;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Function;
+import javax.management.ConstructorParameters;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.WordUtils;
import org.checkerframework.checker.lock.qual.GuardedBy;
import org.opendaylight.controller.cluster.datastore.DatastoreContext.Builder;
-import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.distributed.datastore.provider.rev140612.DataStoreProperties;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.distributed.datastore.provider.rev140612.DataStorePropertiesContainer;
import org.opendaylight.yangtools.yang.common.Uint16;
import org.opendaylight.yangtools.yang.common.Uint32;
import org.opendaylight.yangtools.yang.common.Uint64;
import org.opendaylight.yangtools.yang.common.Uint8;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
introspectDatastoreContextBuilder();
introspectDataStoreProperties();
introspectPrimitiveTypes();
- } catch (final IntrospectionException e) {
+ } catch (final IllegalArgumentException e) {
LOG.error("Error initializing DatastoreContextIntrospector", e);
}
}
private static void introspectPrimitiveTypes() {
final Set<Class<?>> primitives = ImmutableSet.<Class<?>>builder().addAll(
Primitives.allWrapperTypes()).add(String.class).build();
- for (final Class<?> primitive: primitives) {
+ for (final Class<?> primitive : primitives) {
try {
processPropertyType(primitive);
} catch (final NoSuchMethodException e) {
// Ignore primitives that can't be constructed from a String, eg Character and Void.
- } catch (SecurityException | IntrospectionException e) {
+ } catch (SecurityException | IllegalArgumentException e) {
LOG.error("Error introspect primitive type {}", primitive, e);
}
}
* yang grouping. We use the bean Introspector to find the types of all the properties defined
* in the interface (this is the type returned from the getter method). For each type, we find
* the appropriate constructor that we will use.
+ *
+ * @throws IllegalArgumentException if failed to process yang-defined property
*/
- private static void introspectDataStoreProperties() throws IntrospectionException {
- final BeanInfo beanInfo = Introspector.getBeanInfo(DataStoreProperties.class);
- for (final PropertyDescriptor desc: beanInfo.getPropertyDescriptors()) {
- processDataStoreProperty(desc.getName(), desc.getPropertyType(), desc.getReadMethod());
+ private static void introspectDataStoreProperties() {
+ for (final Method method : DataStoreProperties.class.getDeclaredMethods()) {
+ final String propertyName = getPropertyName(method);
+ if (propertyName != null) {
+ processDataStoreProperty(propertyName, method.getReturnType(), method);
+ }
}
+ }
- // Getter methods that return Boolean and start with "is" instead of "get" aren't recognized as
- // properties and thus aren't returned from getPropertyDescriptors. A getter starting with
- // "is" is only supported if it returns primitive boolean. So we'll check for these via
- // getMethodDescriptors.
- for (final MethodDescriptor desc: beanInfo.getMethodDescriptors()) {
- final String methodName = desc.getName();
- if (Boolean.class.equals(desc.getMethod().getReturnType()) && methodName.startsWith("is")) {
- final String propertyName = WordUtils.uncapitalize(methodName.substring(2));
- processDataStoreProperty(propertyName, Boolean.class, desc.getMethod());
- }
+ private static String getPropertyName(final Method method) {
+ final String methodName = method.getName();
+ if (Boolean.class.equals(method.getReturnType()) && methodName.startsWith("is")) {
+ return WordUtils.uncapitalize(methodName.substring(2));
+ } else if (methodName.startsWith("get")) {
+ return WordUtils.uncapitalize(methodName.substring(3));
}
+ return null;
}
/**
/**
* Finds the appropriate constructor for the specified type that we will use to construct
* instances.
+ *
+ * @throws IllegalArgumentException if yang-defined type has no property, annotated by ConstructorParameters
*/
private static void processPropertyType(final Class<?> propertyType)
- throws NoSuchMethodException, SecurityException, IntrospectionException {
+ throws NoSuchMethodException, SecurityException {
final Class<?> wrappedType = Primitives.wrap(propertyType);
if (CONSTRUCTORS.containsKey(wrappedType)) {
return;
// This must be a yang-defined type. We need to find the constructor that takes a
// primitive as the only argument. This will be used to construct instances to perform
// validation (eg range checking). The yang-generated types have a couple single-argument
- // constructors but the one we want has the bean ConstructorProperties annotation.
+ // constructors but the one we want has the ConstructorParameters annotation.
for (final Constructor<?> ctor: propertyType.getConstructors()) {
- final ConstructorProperties ctorPropsAnnotation = ctor.getAnnotation(ConstructorProperties.class);
- if (ctor.getParameterCount() == 1 && ctorPropsAnnotation != null) {
- findYangTypeGetter(propertyType, ctorPropsAnnotation.value()[0]);
+ final ConstructorParameters ctorParAnnotation = ctor.getAnnotation(ConstructorParameters.class);
+ if (ctor.getParameterCount() == 1 && ctorParAnnotation != null) {
+ findYangTypeGetter(propertyType, ctorParAnnotation.value()[0]);
CONSTRUCTORS.put(propertyType, ctor);
break;
}
/**
* Finds the getter method on a yang-generated type for the specified property name.
+ *
+ * @throws IllegalArgumentException if passed type has no passed property
*/
- private static void findYangTypeGetter(final Class<?> type, final String propertyName)
- throws IntrospectionException {
- for (final PropertyDescriptor desc: Introspector.getBeanInfo(type).getPropertyDescriptors()) {
- if (desc.getName().equals(propertyName)) {
- YANG_TYPE_GETTERS.put(type, desc.getReadMethod());
+ private static void findYangTypeGetter(final Class<?> type, final String propertyName) {
+ for (Method method : type.getDeclaredMethods()) {
+ final String property = getPropertyName(method);
+ if (property != null && property.equals(propertyName)) {
+ YANG_TYPE_GETTERS.put(type, method);
return;
}
}
- throw new IntrospectionException(String.format(
+ throw new IllegalArgumentException(String.format(
"Getter method for constructor property %s not found for YANG type %s",
propertyName, type));
}
private Map<String, Object> currentProperties;
public DatastoreContextIntrospector(final DatastoreContext context,
- final BindingNormalizedNodeSerializer bindingSerializer) {
- final DataStorePropertiesContainer defaultPropsContainer = (DataStorePropertiesContainer)
- bindingSerializer.fromNormalizedNode(YangInstanceIdentifier.of(DataStorePropertiesContainer.QNAME),
- ImmutableNodes.containerNode(DataStorePropertiesContainer.QNAME)).getValue();
+ final DataStorePropertiesContainer defaultPropsContainer) {
final Builder builder = DatastoreContext.newBuilderFrom(context);
for (Entry<String, Entry<Class<?>, Method>> entry: DATA_STORE_PROP_INFO.entrySet()) {
// Call the setter method on the Builder instance.
final Method setter = BUILDER_SETTERS.get(key);
- setter.invoke(builder, constructorValueRecursively(
- Primitives.wrap(setter.getParameterTypes()[0]), value.toString()));
+ if (value.getClass().isEnum()) {
+ setter.invoke(builder, value);
+ } else {
+ setter.invoke(builder, constructorValueRecursively(
+ Primitives.wrap(setter.getParameterTypes()[0]), value.toString()));
+ }
return true;
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException
LOG.debug("Type for property {}: {}, converting value {} ({})",
name, propertyType.getSimpleName(), from, from.getClass().getSimpleName());
+ if (propertyType.isEnum()) {
+ try {
+ final Method enumConstructor = propertyType.getDeclaredMethod("forName", String.class);
+ if (enumConstructor.getReturnType().equals(propertyType)) {
+ return enumConstructor.invoke(null, from.toString().toLowerCase(Locale.ROOT));
+ }
+ } catch (NoSuchMethodException e) {
+ LOG.error("Error constructing value ({}) for enum {}", from, propertyType);
+ }
+ }
+
// Recurse the chain of constructors depth-first to get the resulting value. Eg, if the
// property type is the yang-generated NonZeroUint32Type, it's constructor takes a Long so
// we have to first construct a Long instance from the input value.