X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=blobdiff_plain;f=opendaylight%2Fmd-sal%2Fsal-distributed-datastore%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fcluster%2Fdatastore%2FDatastoreContextIntrospector.java;h=1f0911851089d37aaf33e8d4350c39307394ffbd;hp=0bbeefd6fa64f3c5db3caf2e79f655dc4450a7c0;hb=925cb4a228d0fda99c7bfeb432eb25285a223887;hpb=7f44a5812395762479de68d339c3d017db6c0e9d diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DatastoreContextIntrospector.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DatastoreContextIntrospector.java index 0bbeefd6fa..1f09118510 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DatastoreContextIntrospector.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DatastoreContextIntrospector.java @@ -8,6 +8,7 @@ package org.opendaylight.controller.cluster.datastore; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.primitives.Primitives; import java.beans.BeanInfo; @@ -19,10 +20,11 @@ import java.beans.PropertyDescriptor; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; -import java.util.Comparator; import java.util.Dictionary; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.lang3.StringUtils; @@ -40,13 +42,13 @@ import org.slf4j.LoggerFactory; public class DatastoreContextIntrospector { private static final Logger LOG = LoggerFactory.getLogger(DatastoreContextIntrospector.class); - private static final Map> dataStorePropTypes = new HashMap<>(); + private static final Map> DATA_STORE_PROP_TYPES = new HashMap<>(); - private static final Map, Constructor> constructors = new HashMap<>(); + private static final Map, Constructor> CONSTRUCTORS = new HashMap<>(); - private static final Map, Method> yangTypeGetters = new HashMap<>(); + private static final Map, Method> YANG_TYPE_GETTERS = new HashMap<>(); - private static final Map builderSetters = new HashMap<>(); + private static final Map BUILDER_SETTERS = new HashMap<>(); static { try { @@ -63,11 +65,12 @@ public class DatastoreContextIntrospector { * constructor that takes a single String argument. For primitive wrappers, this constructor * converts from a String representation. */ + @SuppressWarnings("checkstyle:IllegalCatch") private static void introspectPrimitiveTypes() { Set> primitives = ImmutableSet.>builder().addAll( Primitives.allWrapperTypes()).add(String.class).build(); - for(Class primitive: primitives) { + for (Class primitive: primitives) { try { processPropertyType(primitive); } catch (Exception e) { @@ -83,9 +86,9 @@ public class DatastoreContextIntrospector { * the methods that return Builder. */ private static void introspectDatastoreContextBuilder() { - for(Method method: Builder.class.getMethods()) { - if(Builder.class.equals(method.getReturnType())) { - builderSetters.put(method.getName(), method); + for (Method method: Builder.class.getMethods()) { + if (Builder.class.equals(method.getReturnType())) { + BUILDER_SETTERS.put(method.getName(), method); } } } @@ -98,7 +101,7 @@ public class DatastoreContextIntrospector { */ private static void introspectDataStoreProperties() throws IntrospectionException { BeanInfo beanInfo = Introspector.getBeanInfo(DataStoreProperties.class); - for(PropertyDescriptor desc: beanInfo.getPropertyDescriptors()) { + for (PropertyDescriptor desc: beanInfo.getPropertyDescriptors()) { processDataStoreProperty(desc.getName(), desc.getPropertyType()); } @@ -106,9 +109,9 @@ public class DatastoreContextIntrospector { // 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(MethodDescriptor desc: beanInfo.getMethodDescriptors()) { + for (MethodDescriptor desc: beanInfo.getMethodDescriptors()) { String methodName = desc.getName(); - if(Boolean.class.equals(desc.getMethod().getReturnType()) && methodName.startsWith("is")) { + if (Boolean.class.equals(desc.getMethod().getReturnType()) && methodName.startsWith("is")) { String propertyName = WordUtils.uncapitalize(methodName.substring(2)); processDataStoreProperty(propertyName, Boolean.class); } @@ -118,12 +121,14 @@ public class DatastoreContextIntrospector { /** * Processes a property defined on the DataStoreProperties interface. */ + @SuppressWarnings("checkstyle:IllegalCatch") private static void processDataStoreProperty(String name, Class propertyType) { - Preconditions.checkArgument(builderSetters.containsKey(name), String.format( - "DataStoreProperties property \"%s\" does not have corresponding setter in DatastoreContext.Builder", name)); + Preconditions.checkArgument(BUILDER_SETTERS.containsKey(name), String.format( + "DataStoreProperties property \"%s\" does not have corresponding setter in DatastoreContext.Builder", + name)); try { processPropertyType(propertyType); - dataStorePropTypes.put(name, propertyType); + DATA_STORE_PROP_TYPES.put(name, propertyType); } catch (Exception e) { LOG.error("Error finding constructor for type {}", propertyType, e); } @@ -135,27 +140,25 @@ public class DatastoreContextIntrospector { */ private static void processPropertyType(Class propertyType) throws Exception { Class wrappedType = Primitives.wrap(propertyType); - if(constructors.containsKey(wrappedType)) { + if (CONSTRUCTORS.containsKey(wrappedType)) { return; } // If the type is a primitive (or String type), we look for the constructor that takes a // single String argument, which, for primitives, validates and converts from a String // representation which is the form we get on ingress. - if(propertyType.isPrimitive() || Primitives.isWrapperType(propertyType) || - propertyType.equals(String.class)) - { - constructors.put(wrappedType, propertyType.getConstructor(String.class)); + if (propertyType.isPrimitive() || Primitives.isWrapperType(propertyType) || propertyType.equals(String.class)) { + CONSTRUCTORS.put(wrappedType, propertyType.getConstructor(String.class)); } else { // 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. - for(Constructor ctor: propertyType.getConstructors()) { + for (Constructor ctor: propertyType.getConstructors()) { ConstructorProperties ctorPropsAnnotation = ctor.getAnnotation(ConstructorProperties.class); - if(ctor.getParameterTypes().length == 1 && ctorPropsAnnotation != null) { + if (ctor.getParameterTypes().length == 1 && ctorPropsAnnotation != null) { findYangTypeGetter(propertyType, ctorPropsAnnotation.value()[0]); - constructors.put(propertyType, ctor); + CONSTRUCTORS.put(propertyType, ctor); break; } } @@ -167,9 +170,9 @@ public class DatastoreContextIntrospector { */ private static void findYangTypeGetter(Class type, String propertyName) throws Exception { - for(PropertyDescriptor desc: Introspector.getBeanInfo(type).getPropertyDescriptors()) { - if(desc.getName().equals(propertyName)) { - yangTypeGetters.put(type, desc.getReadMethod()); + for (PropertyDescriptor desc: Introspector.getBeanInfo(type).getPropertyDescriptors()) { + if (desc.getName().equals(propertyName)) { + YANG_TYPE_GETTERS.put(type, desc.getReadMethod()); return; } } @@ -180,6 +183,7 @@ public class DatastoreContextIntrospector { } private DatastoreContext context; + private Map currentProperties; public DatastoreContextIntrospector(DatastoreContext context) { this.context = context; @@ -189,77 +193,123 @@ public class DatastoreContextIntrospector { return context; } + public DatastoreContextFactory newContextFactory() { + return new DatastoreContextFactory(this); + } + + public synchronized DatastoreContext getShardDatastoreContext(String forShardName) { + if (currentProperties == null) { + return context; + } + + Builder builder = DatastoreContext.newBuilderFrom(context); + String dataStoreTypePrefix = context.getDataStoreName() + '.'; + final String shardNamePrefix = forShardName + '.'; + + List keys = getSortedKeysByDatastoreType(currentProperties.keySet(), dataStoreTypePrefix); + + for (String key: keys) { + Object value = currentProperties.get(key); + if (key.startsWith(dataStoreTypePrefix)) { + key = key.replaceFirst(dataStoreTypePrefix, ""); + } + + if (key.startsWith(shardNamePrefix)) { + key = key.replaceFirst(shardNamePrefix, ""); + convertValueAndInvokeSetter(key, value, builder); + } + } + + return builder.build(); + } + /** * Applies the given properties to the cached DatastoreContext and yields a new DatastoreContext - * instance which can be obtained via {@link getContext}. + * instance which can be obtained via {@link #getContext()}. * * @param properties the properties to apply * @return true if the cached DatastoreContext was updated, false otherwise. */ public synchronized boolean update(Dictionary properties) { - if(properties == null || properties.isEmpty()) { + currentProperties = null; + if (properties == null || properties.isEmpty()) { return false; } LOG.debug("In update: properties: {}", properties); + ImmutableMap.Builder mapBuilder = ImmutableMap.builder(); + Builder builder = DatastoreContext.newBuilderFrom(context); - final String dataStoreTypePrefix = context.getDataStoreType() + '.'; + final String dataStoreTypePrefix = context.getDataStoreName() + '.'; - // Sort the property keys by putting the names prefixed with the data store type last. This - // is done so data store specific settings are applied after global settings. - ArrayList keys = Collections.list(properties.keys()); - Collections.sort(keys, new Comparator() { - @Override - public int compare(String key1, String key2) { - return key1.startsWith(dataStoreTypePrefix) ? 1 : - key2.startsWith(dataStoreTypePrefix) ? -1 : key1.compareTo(key2); - } - }); + List keys = getSortedKeysByDatastoreType(Collections.list(properties.keys()), dataStoreTypePrefix); boolean updated = false; - for(String key: keys) { + for (String key: keys) { Object value = properties.get(key); - try { - // If the key is prefixed with the data store type, strip it off. - if(key.startsWith(dataStoreTypePrefix)) { - key = key.replaceFirst(dataStoreTypePrefix, ""); - } + mapBuilder.put(key, value); - key = convertToCamelCase(key); - - // Convert the value to the right type. - value = convertValue(key, value); - if(value == null) { - continue; - } - - LOG.debug("Converted value for property {}: {} ({})", - key, value, value.getClass().getSimpleName()); - - // Call the setter method on the Builder instance. - Method setter = builderSetters.get(key); - setter.invoke(builder, constructorValueRecursively( - Primitives.wrap(setter.getParameterTypes()[0]), value.toString())); + // If the key is prefixed with the data store type, strip it off. + if (key.startsWith(dataStoreTypePrefix)) { + key = key.replaceFirst(dataStoreTypePrefix, ""); + } + if (convertValueAndInvokeSetter(key, value, builder)) { updated = true; - - } catch (Exception e) { - LOG.error("Error converting value ({}) for property {}", value, key, e); } } - if(updated) { + currentProperties = mapBuilder.build(); + + if (updated) { context = builder.build(); } return updated; } - private String convertToCamelCase(String inString) { + private static ArrayList getSortedKeysByDatastoreType(Collection inKeys, + final String dataStoreTypePrefix) { + // Sort the property keys by putting the names prefixed with the data store type last. This + // is done so data store specific settings are applied after global settings. + ArrayList keys = new ArrayList<>(inKeys); + Collections.sort(keys, (key1, key2) -> key1.startsWith(dataStoreTypePrefix) ? 1 : + key2.startsWith(dataStoreTypePrefix) ? -1 : key1.compareTo(key2)); + return keys; + } + + @SuppressWarnings("checkstyle:IllegalCatch") + private boolean convertValueAndInvokeSetter(String inKey, Object inValue, Builder builder) { + String key = convertToCamelCase(inKey); + + try { + // Convert the value to the right type. + Object value = convertValue(key, inValue); + if (value == null) { + return false; + } + + LOG.debug("Converted value for property {}: {} ({})", + key, value, value.getClass().getSimpleName()); + + // Call the setter method on the Builder instance. + Method setter = BUILDER_SETTERS.get(key); + setter.invoke(builder, constructorValueRecursively( + Primitives.wrap(setter.getParameterTypes()[0]), value.toString())); + + return true; + } catch (Exception e) { + LOG.error("Error converting value ({}) for property {}", inValue, key, e); + } + + return false; + } + + private static String convertToCamelCase(String inString) { String str = inString.trim(); - if(StringUtils.contains(str, '-') || StringUtils.contains(str, ' ')) { + if (StringUtils.contains(str, '-') || StringUtils.contains(str, ' ')) { str = inString.replace('-', ' '); str = WordUtils.capitalizeFully(str); str = StringUtils.deleteWhitespace(str); @@ -269,8 +319,8 @@ public class DatastoreContextIntrospector { } private Object convertValue(String name, Object from) throws Exception { - Class propertyType = dataStorePropTypes.get(name); - if(propertyType == null) { + Class propertyType = DATA_STORE_PROP_TYPES.get(name); + if (propertyType == null) { LOG.debug("Property not found for {}", name); return null; } @@ -284,8 +334,8 @@ public class DatastoreContextIntrospector { Object converted = constructorValueRecursively(propertyType, from.toString()); // If the converted type is a yang-generated type, call the getter to obtain the actual value. - Method getter = yangTypeGetters.get(converted.getClass()); - if(getter != null) { + Method getter = YANG_TYPE_GETTERS.get(converted.getClass()); + if (getter != null) { converted = getter.invoke(converted); } @@ -296,11 +346,11 @@ public class DatastoreContextIntrospector { LOG.trace("convertValueRecursively - toType: {}, fromValue {} ({})", toType.getSimpleName(), fromValue, fromValue.getClass().getSimpleName()); - Constructor ctor = constructors.get(toType); + Constructor ctor = CONSTRUCTORS.get(toType); LOG.trace("Found {}", ctor); - if(ctor == null) { + if (ctor == null) { throw new IllegalArgumentException(String.format("Constructor not found for type %s", toType)); } @@ -308,7 +358,7 @@ public class DatastoreContextIntrospector { // Since the original input type is a String, once we find a constructor that takes a String // argument, we're done recursing. - if(!ctor.getParameterTypes()[0].equals(String.class)) { + if (!ctor.getParameterTypes()[0].equals(String.class)) { value = constructorValueRecursively(ctor.getParameterTypes()[0], fromValue); }