diff options
author | Maros Marsalek <mmarsale@cisco.com> | 2016-03-17 14:29:25 +0100 |
---|---|---|
committer | Maros Marsalek <mmarsale@cisco.com> | 2016-03-22 09:47:09 +0000 |
commit | a60162e7c26a1d11976d8d78ee369b23805154aa (patch) | |
tree | 66670eedb77b03c7b88833bb27e0fbc04aa80e09 /v3po/impl | |
parent | 8339ec66e0dfbed1411ff451846db4de8b035a32 (diff) |
Address TODOs for VPP readers
Cleanup the mapping methods
Clenup and document SPIs
Exctract SubtreeManager interface
Change-Id: Idaacebf949926107b0e4f2f467e5a4470126fa96
Signed-off-by: Maros Marsalek <mmarsale@cisco.com>
Diffstat (limited to 'v3po/impl')
22 files changed, 477 insertions, 259 deletions
diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/ChildVppReader.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/ChildVppReader.java index 8f2a1ce9e..040c8fdef 100644 --- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/ChildVppReader.java +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/ChildVppReader.java @@ -24,16 +24,20 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; /** * Child VPP reader allowing its parent to pass the builder object + * + * @param <C> Specific DataObject derived type, that is handled by this reader */ @Beta public interface ChildVppReader<C extends DataObject> extends VppReader<C> { /** - * Read subtree starting from node managed by this reader and place the subtree within parent builder object if - * the data exists. + * Reads subtree starting from node managed by this reader and place the subtree within parent builder object if the + * data exists. * - * @param id Unique identifier pointing to the node managed by this reader. Useful when necessary to determine - * the exact position within more complex subtrees. + * @param id Unique identifier pointing to the node managed by this reader. Useful when necessary to + * determine the exact position within more complex subtrees. + * @param parentBuilder Builder of parent DataObject. Objects read on this level (if any) must be placed into the + * parent builder. */ void read(@Nonnull final InstanceIdentifier<? extends DataObject> id, @Nonnull final Builder<? extends DataObject> parentBuilder); diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/ReaderRegistry.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/ReaderRegistry.java index cea5c8137..54809c98e 100644 --- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/ReaderRegistry.java +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/ReaderRegistry.java @@ -16,6 +16,7 @@ package io.fd.honeycomb.v3po.impl.trans; +import com.google.common.annotations.Beta; import java.util.List; import javax.annotation.Nonnull; import org.opendaylight.yangtools.yang.binding.DataObject; @@ -23,7 +24,12 @@ import org.opendaylight.yangtools.yang.binding.DataObject; /** * Simple delegating reader suitable as a holder for all other root readers, providing readAll feature */ +@Beta public interface ReaderRegistry extends VppReader<DataObject> { - @Nonnull List<? extends DataObject> readAll(); + /** + * Perform read on all underlying readers and merge the results into a single list + */ + @Nonnull + List<? extends DataObject> readAll(); } diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/SubtreeManager.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/SubtreeManager.java new file mode 100644 index 000000000..0aa927cd0 --- /dev/null +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/SubtreeManager.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed 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 io.fd.honeycomb.v3po.impl.trans; + +import com.google.common.annotations.Beta; +import javax.annotation.Nonnull; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Base identifiable subtree manager(reader, writer etc.) + * + * @param <D> Specific DataObject derived type, that is managed by this manager + */ +@Beta +public interface SubtreeManager<D extends DataObject> { + + /** + * Gets the type of node managed by this reader + * + * @return Class object for node managed by this reader + */ + @Nonnull + InstanceIdentifier<D> getManagedDataObjectType(); +} diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/VppReader.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/VppReader.java index 0033cfc07..c8c13f65a 100644 --- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/VppReader.java +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/VppReader.java @@ -23,10 +23,12 @@ import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; /** - * Base VPP reader, responsible for translation between DataObjects and VPP apis + * Base VPP reader, responsible for translation between DataObjects and VPP APIs + * + * @param <D> Specific DataObject derived type, that is handled by this reader */ @Beta -public interface VppReader<D extends DataObject> { +public interface VppReader<D extends DataObject> extends SubtreeManager<D> { // TODO add vpp read context that will be shared by all readers during a single read to keep useful information // preventing possible duplicate reads from VPP @@ -35,21 +37,12 @@ public interface VppReader<D extends DataObject> { /** * Reads from VPP data identified by id * - * @param id unique identifier of subtree to be read. - * The subtree must contain managed data object type enforcing it to point - * to the node type managed by this reader or below. For identifiers pointing below - * node managed by this reader, its reader's responsibility to filter out the right - * node or to delegate the read to a child reader. - * - * @return List of DataObjects identified by id. If the ID points to a single node, it will be wrapped in a list + * @param id unique identifier of subtree to be read. The subtree must contain managed data object type. For + * identifiers pointing below node managed by this reader, it's reader's responsibility to filter out the + * right node or to delegate the read to a child reader. + * @return List of DataObjects identified by id. If the ID points to a single node, it will be wrapped in a list */ - @Nonnull List<? extends DataObject> read(@Nonnull final InstanceIdentifier<? extends DataObject> id); - + @Nonnull + List<? extends DataObject> read(@Nonnull final InstanceIdentifier<? extends DataObject> id); - /** - * Get the type of node managed by this reader - * - * @return Class object for node managed by this reader - */ - @Nonnull InstanceIdentifier<D> getManagedDataObjectType(); } diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/impl/AbstractCompositeVppReader.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/impl/AbstractCompositeVppReader.java index 566c4fcc8..121d50dcb 100644 --- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/impl/AbstractCompositeVppReader.java +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/impl/AbstractCompositeVppReader.java @@ -18,18 +18,16 @@ package io.fd.honeycomb.v3po.impl.trans.impl; import com.google.common.annotations.Beta; import com.google.common.base.Optional; -import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.collect.Collections2; -import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import io.fd.honeycomb.v3po.impl.trans.ChildVppReader; import io.fd.honeycomb.v3po.impl.trans.VppReader; -import io.fd.honeycomb.v3po.impl.trans.util.VppReaderUtils; +import io.fd.honeycomb.v3po.impl.trans.util.ReflectionUtils; +import io.fd.honeycomb.v3po.impl.trans.util.VppRWUtils; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Collections; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.annotation.Nonnull; @@ -50,52 +48,23 @@ abstract class AbstractCompositeVppReader<D extends DataObject, B extends Builde private final Map<Class<? extends DataObject>, ChildVppReader<? extends Augmentation<D>>> augReaders; private final InstanceIdentifier<D> instanceIdentifier; - public AbstractCompositeVppReader( - final Class<D> managedDataObjectType, + AbstractCompositeVppReader(final Class<D> managedDataObjectType, final List<ChildVppReader<? extends ChildOf<D>>> childReaders, final List<ChildVppReader<? extends Augmentation<D>>> augReaders) { - this.childReaders = childReadersToMap(childReaders); - this.augReaders = augReadersToMap(augReaders); + this.childReaders = VppRWUtils.uniqueLinkedIndex(childReaders, VppRWUtils.MANAGER_CLASS_FUNCTION); + this.augReaders = VppRWUtils.uniqueLinkedIndex(augReaders, VppRWUtils.MANAGER_CLASS_AUG_FUNCTION); this.instanceIdentifier = InstanceIdentifier.create(managedDataObjectType); } - protected final Map<Class<? extends DataObject>, ChildVppReader<? extends ChildOf<D>>> childReadersToMap( - final List<ChildVppReader<? extends ChildOf<D>>> childReaders) { - final LinkedHashMap<Class<? extends DataObject>, ChildVppReader<? extends ChildOf<D>>> - classVppReaderLinkedHashMap = new LinkedHashMap<>(); - - for (ChildVppReader<? extends ChildOf<D>> childReader : childReaders) { - Preconditions.checkArgument( - classVppReaderLinkedHashMap.put(childReader.getManagedDataObjectType().getTargetType(), childReader) == null, - "Duplicate (%s) child readers detected under: %s", childReader.getManagedDataObjectType(), - getManagedDataObjectType()); - } - - return classVppReaderLinkedHashMap; - } - - // FIXME add child/augReaders to one list and unify toMap helper methods + move to utils - protected final Map<Class<? extends DataObject>, ChildVppReader<? extends Augmentation<D>>> augReadersToMap( - final List<ChildVppReader<? extends Augmentation<D>>> childReaders) { - final LinkedHashMap<Class<? extends DataObject>, ChildVppReader<? extends Augmentation<D>>> - classVppReaderLinkedHashMap = new LinkedHashMap<>(); - - for (ChildVppReader<? extends DataObject> childReader : childReaders) { - Preconditions.checkArgument( - classVppReaderLinkedHashMap.put(childReader.getManagedDataObjectType().getTargetType(), - (ChildVppReader<? extends Augmentation<D>>) childReader) == null, - "Duplicate (%s) child readers detected under: %s", childReader.getManagedDataObjectType(), - getManagedDataObjectType()); - } - return classVppReaderLinkedHashMap; - } - @Nonnull @Override public final InstanceIdentifier<D> getManagedDataObjectType() { return instanceIdentifier; } + /** + * @param id {@link InstanceIdentifier} pointing to current node. In case of keyed list, key must be present. + */ protected List<D> readCurrent(final InstanceIdentifier<D> id) { final B builder = getBuilder(id); // Cache empty value to determine if anything has changed later TODO cache in a field @@ -103,6 +72,7 @@ abstract class AbstractCompositeVppReader<D extends DataObject, B extends Builde readCurrentAttributes(id, builder); + // TODO expect exceptions from reader for (ChildVppReader<? extends ChildOf<D>> child : childReaders.values()) { child.read(id, builder); } @@ -132,53 +102,57 @@ abstract class AbstractCompositeVppReader<D extends DataObject, B extends Builde private List<? extends DataObject> readSubtree(final InstanceIdentifier<? extends DataObject> id) { // Read only specific subtree - final Class<? extends DataObject> next = VppReaderUtils.getNextId(id, getManagedDataObjectType()).getType(); + final Class<? extends DataObject> next = VppRWUtils.getNextId(id, getManagedDataObjectType()).getType(); final ChildVppReader<? extends ChildOf<D>> vppReader = childReaders.get(next); if (vppReader != null) { return vppReader.read(id); } else { // If there's no dedicated reader, use read current - final InstanceIdentifier<D> currentId = VppReaderUtils.cutId(id, getManagedDataObjectType()); + final InstanceIdentifier<D> currentId = VppRWUtils.cutId(id, getManagedDataObjectType()); final List<D> current = readCurrent(currentId); // then perform post-reading filtering (return only requested sub-node) return current.isEmpty() ? current : filterSubtree(current, id, getManagedDataObjectType().getTargetType()) ; } } - @SuppressWarnings("unchecked") - protected InstanceIdentifier<D> getCurrentId(final InstanceIdentifier<? extends DataObject> parentId) { - Preconditions.checkArgument(!parentId.contains(getManagedDataObjectType()), - "Unexpected InstanceIdentifier %s, already contains %s", parentId, getManagedDataObjectType()); - final InstanceIdentifier.PathArgument t = Iterables.getOnlyElement(getManagedDataObjectType().getPathArguments()); - return (InstanceIdentifier<D>) InstanceIdentifier.create(Iterables.concat( - parentId.getPathArguments(), Collections.singleton(t))); - } - + /** + * Fill in current node's attributes + * + * @param id {@link InstanceIdentifier} pointing to current node. In case of keyed list, key must be present. + * @param builder Builder object for current node where the read attributes must be placed + */ protected abstract void readCurrentAttributes(final InstanceIdentifier<D> id, B builder); - protected abstract B getBuilder(InstanceIdentifier<? extends DataObject> id); + /** + * Return new instance of a builder object for current node + * + * @param id {@link InstanceIdentifier} pointing to current node. In case of keyed list, key must be present. + * @return Builder object for current node type + */ + protected abstract B getBuilder(InstanceIdentifier<D> id); // TODO move filtering out of here into a dedicated Filter ifc @Nonnull private static List<? extends DataObject> filterSubtree(@Nonnull final List<? extends DataObject> built, @Nonnull final InstanceIdentifier<? extends DataObject> absolutPath, @Nonnull final Class<?> managedType) { - // TODO is there a better way than reflection ? + // TODO is there a better way than reflection ? e.g. convert into NN and filter out with a utility + // FIXME this needs to be recursive. right now it expects only 1 additional element in ID + test List<DataObject> filtered = Lists.newArrayList(); for (DataObject parent : built) { final InstanceIdentifier.PathArgument nextId = - VppReaderUtils.getNextId(absolutPath, InstanceIdentifier.create(parent.getClass())); + VppRWUtils.getNextId(absolutPath, InstanceIdentifier.create(parent.getClass())); - Optional<Method> method = VppReaderUtils.findMethodReflex(managedType, "get", + Optional<Method> method = ReflectionUtils.findMethodReflex(managedType, "get", Collections.<Class<?>>emptyList(), nextId.getType()); if (method.isPresent()) { filterSingle(filtered, parent, nextId, method); } else { // List child nodes - method = VppReaderUtils.findMethodReflex(managedType, + method = ReflectionUtils.findMethodReflex(managedType, "get" + nextId.getType().getSimpleName(), Collections.<Class<?>>emptyList(), List.class); if (method.isPresent()) { @@ -204,7 +178,7 @@ abstract class AbstractCompositeVppReader<D extends DataObject, B extends Builde @Override public boolean apply(@Nullable final DataObject input) { final Optional<Method> keyGetter = - VppReaderUtils.findMethodReflex(nextId.getType(), "get", + ReflectionUtils.findMethodReflex(nextId.getType(), "get", Collections.<Class<?>>emptyList(), key.getClass()); final Object actualKey; actualKey = invoke(keyGetter.get(), nextId, input); @@ -229,5 +203,4 @@ abstract class AbstractCompositeVppReader<D extends DataObject, B extends Builde throw new IllegalArgumentException("Unable to get " + nextId + " from " + parent, e); } } - } diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/impl/CompositeChildVppReader.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/impl/CompositeChildVppReader.java index 7c66695c4..e1dac48b3 100644 --- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/impl/CompositeChildVppReader.java +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/impl/CompositeChildVppReader.java @@ -20,7 +20,7 @@ import com.google.common.annotations.Beta; import com.google.common.base.Optional; import io.fd.honeycomb.v3po.impl.trans.ChildVppReader; import io.fd.honeycomb.v3po.impl.trans.impl.spi.ChildVppReaderCustomizer; -import io.fd.honeycomb.v3po.impl.trans.util.VppReaderUtils; +import io.fd.honeycomb.v3po.impl.trans.util.VppRWUtils; import java.util.List; import javax.annotation.Nonnull; import javax.annotation.concurrent.ThreadSafe; @@ -30,13 +30,26 @@ import org.opendaylight.yangtools.yang.binding.ChildOf; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +/** + * Composite implementation of {@link ChildVppReader} able to place the read result into + * parent builder object. + */ @Beta @ThreadSafe -public class CompositeChildVppReader<C extends DataObject, B extends Builder<C>> extends AbstractCompositeVppReader<C, B> +public final class CompositeChildVppReader<C extends DataObject, B extends Builder<C>> extends AbstractCompositeVppReader<C, B> implements ChildVppReader<C> { private final ChildVppReaderCustomizer<C, B> customizer; + /** + * Create new {@link CompositeChildVppReader} + * + * @param managedDataObjectType Class object for managed data type + * @param childReaders Child nodes(container, list) readers + * @param augReaders Child augmentations readers + * @param customizer Customizer instance to customize this generic reader + * + */ public CompositeChildVppReader(@Nonnull final Class<C> managedDataObjectType, @Nonnull final List<ChildVppReader<? extends ChildOf<C>>> childReaders, @Nonnull final List<ChildVppReader<? extends Augmentation<C>>> augReaders, @@ -45,22 +58,29 @@ public class CompositeChildVppReader<C extends DataObject, B extends Builder<C>> this.customizer = customizer; } + /** + * @see {@link CompositeChildVppReader#CompositeChildVppReader(Class, List, List, ChildVppReaderCustomizer)} + */ public CompositeChildVppReader(@Nonnull final Class<C> managedDataObjectType, @Nonnull final List<ChildVppReader<? extends ChildOf<C>>> childReaders, @Nonnull final ChildVppReaderCustomizer<C, B> customizer) { - this(managedDataObjectType, childReaders, VppReaderUtils.<C>emptyAugReaderList(), customizer); + this(managedDataObjectType, childReaders, VppRWUtils.<C>emptyAugReaderList(), customizer); } + /** + * @see {@link CompositeChildVppReader#CompositeChildVppReader(Class, List, List, ChildVppReaderCustomizer)} + */ public CompositeChildVppReader(@Nonnull final Class<C> managedDataObjectType, @Nonnull final ChildVppReaderCustomizer<C, B> customizer) { - this(managedDataObjectType, VppReaderUtils.<C>emptyChildReaderList(), VppReaderUtils.<C>emptyAugReaderList(), + this(managedDataObjectType, VppRWUtils.<C>emptyChildReaderList(), VppRWUtils.<C>emptyAugReaderList(), customizer); } @Override public final void read(@Nonnull final InstanceIdentifier<? extends DataObject> parentId, @Nonnull final Builder<? extends DataObject> parentBuilder) { - final Optional<C> read = Optional.fromNullable(readCurrent(getCurrentId(parentId)).get(0)); + final Optional<C> read = Optional.fromNullable(readCurrent(VppRWUtils.appendTypeToId(parentId, + getManagedDataObjectType())).get(0)); if(read.isPresent()) { customizer.merge(parentBuilder, read.get()); @@ -69,12 +89,12 @@ public class CompositeChildVppReader<C extends DataObject, B extends Builder<C>> @Override protected void readCurrentAttributes(@Nonnull final InstanceIdentifier<C> id, @Nonnull final B builder) { - customizer.readCurrentAttributes(builder); + customizer.readCurrentAttributes(id, builder); } @Override - protected B getBuilder(@Nonnull final InstanceIdentifier<? extends DataObject> id) { - return customizer.getBuilder(); + protected B getBuilder(@Nonnull final InstanceIdentifier<C> id) { + return customizer.getBuilder(id); } } diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/impl/CompositeListVppReader.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/impl/CompositeListVppReader.java index f280fdb76..d64a83ccc 100644 --- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/impl/CompositeListVppReader.java +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/impl/CompositeListVppReader.java @@ -16,15 +16,15 @@ package io.fd.honeycomb.v3po.impl.trans.impl; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; + import com.google.common.annotations.Beta; import com.google.common.base.Function; -import com.google.common.base.Optional; -import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import io.fd.honeycomb.v3po.impl.trans.ChildVppReader; import io.fd.honeycomb.v3po.impl.trans.impl.spi.ListVppReaderCustomizer; -import io.fd.honeycomb.v3po.impl.trans.util.VppReaderUtils; -import java.util.Collections; +import io.fd.honeycomb.v3po.impl.trans.util.VppRWUtils; import java.util.List; import javax.annotation.Nonnull; import javax.annotation.concurrent.ThreadSafe; @@ -36,13 +36,29 @@ import org.opendaylight.yangtools.yang.binding.Identifiable; import org.opendaylight.yangtools.yang.binding.Identifier; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +/** + * Composite implementation of {@link ChildVppReader} able to place the read result into + * parent builder object intended for list node type. + * + * This reader checks if the IDs are wildcarded in which case it performs read of all + * list entries. In case the ID has a key, it reads only the specified value. + */ @Beta @ThreadSafe public final class CompositeListVppReader<C extends DataObject & Identifiable<K>, K extends Identifier<C>, B extends Builder<C>> extends AbstractCompositeVppReader<C, B> implements ChildVppReader<C> { - private ListVppReaderCustomizer<C, K, B> customizer; - + private final ListVppReaderCustomizer<C, K, B> customizer; + + /** + * Create new {@link CompositeListVppReader} + * + * @param managedDataObjectType Class object for managed data type. Must come from a list node type. + * @param childReaders Child nodes(container, list) readers + * @param augReaders Child augmentations readers + * @param customizer Customizer instance to customize this generic reader + * + */ public CompositeListVppReader(@Nonnull final Class<C> managedDataObjectType, @Nonnull final List<ChildVppReader<? extends ChildOf<C>>> childReaders, @Nonnull final List<ChildVppReader<? extends Augmentation<C>>> augReaders, @@ -51,60 +67,51 @@ public final class CompositeListVppReader<C extends DataObject & Identifiable<K> this.customizer = customizer; } + /** + * @see {@link CompositeListVppReader#CompositeListVppReader(Class, List, List, ListVppReaderCustomizer)} + */ public CompositeListVppReader(@Nonnull final Class<C> managedDataObjectType, @Nonnull final List<ChildVppReader<? extends ChildOf<C>>> childReaders, @Nonnull final ListVppReaderCustomizer<C, K, B> customizer) { - this(managedDataObjectType, childReaders, VppReaderUtils.<C>emptyAugReaderList(), customizer); + this(managedDataObjectType, childReaders, VppRWUtils.<C>emptyAugReaderList(), customizer); } + /** + * @see {@link CompositeListVppReader#CompositeListVppReader(Class, List, List, ListVppReaderCustomizer)} + */ public CompositeListVppReader(@Nonnull final Class<C> managedDataObjectType, @Nonnull final ListVppReaderCustomizer<C, K, B> customizer) { - this(managedDataObjectType, VppReaderUtils.<C>emptyChildReaderList(), VppReaderUtils.<C>emptyAugReaderList(), + this(managedDataObjectType, VppRWUtils.<C>emptyChildReaderList(), VppRWUtils.<C>emptyAugReaderList(), customizer); } @Override protected List<C> readCurrent(@Nonnull final InstanceIdentifier<C> id) { - if(shouldReadAll(id)) { - return readList(id); - } - return super.readCurrent(id); + return shouldReadAll(id) ? readList(id) : super.readCurrent(id); } @Override public void read(@Nonnull final InstanceIdentifier<? extends DataObject> id, @Nonnull final Builder<? extends DataObject> parentBuilder) { - final InstanceIdentifier<C> currentId = getCurrentId(id); - - final List<C> ifcs; - if (shouldReadAll(id)) { - ifcs = readList(currentId); - } else { - final Optional<? extends DataObject> readSingle = Optional.fromNullable(read(id).get(0)); - final Optional<C> read = readSingle.transform(new Function<DataObject, C>() { - @Override - public C apply(final DataObject input) { - Preconditions.checkArgument(getManagedDataObjectType().getTargetType().isAssignableFrom(input.getClass())); - return getManagedDataObjectType().getTargetType().cast(input); - } - }); - ifcs = read.isPresent() ? Collections.singletonList(read.get()) : Collections.<C>emptyList(); - } - + // Create ID pointing to current node + final InstanceIdentifier<C> currentId = VppRWUtils.appendTypeToId(id, getManagedDataObjectType()); + // Read all, since current ID is definitely wildcarded + final List<C> ifcs = readList(currentId); customizer.merge(parentBuilder, ifcs); } private List<C> readList(@Nonnull final InstanceIdentifier<C> id) { - Preconditions.checkArgument(id.getTargetType().equals(getManagedDataObjectType().getTargetType()), - "Id %s does not contain expected type %s", id, getManagedDataObjectType()); - - return Lists.transform(customizer.getAllIds(id), new Function<InstanceIdentifier<? extends DataObject>, C>() { + return Lists.transform(customizer.getAllIds(id), new Function<K, C>() { @Override - public C apply(final InstanceIdentifier<? extends DataObject> input) { - final List<? extends DataObject> read = read(input); - Preconditions.checkState(read.size() == 1); - Preconditions.checkArgument(getManagedDataObjectType().getTargetType().isAssignableFrom(read.get(0).getClass())); - return getManagedDataObjectType().getTargetType().cast(read.get(0)); + public C apply(final K key) { + final InstanceIdentifier.IdentifiableItem<C, K> currentBdItem = + VppRWUtils.getCurrentIdItem(id, key); + final InstanceIdentifier<C> keyedId = VppRWUtils.replaceLastInId(id, currentBdItem); + final List<? extends DataObject> read = read(keyedId); + checkState(read.size() == 1); + final DataObject singleItem = read.get(0); + checkArgument(getManagedDataObjectType().getTargetType().isAssignableFrom(singleItem.getClass())); + return getManagedDataObjectType().getTargetType().cast(singleItem); } }); } @@ -120,8 +127,8 @@ public final class CompositeListVppReader<C extends DataObject & Identifiable<K> } @Override - protected B getBuilder(@Nonnull final InstanceIdentifier<? extends DataObject> id) { - return customizer.getBuilder(id.firstKeyOf(getManagedDataObjectType().getTargetType())); + protected B getBuilder(@Nonnull final InstanceIdentifier<C> id) { + return customizer.getBuilder(id); } } diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/impl/CompositeRootVppReader.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/impl/CompositeRootVppReader.java index 605f1b628..1e6b7e93a 100644 --- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/impl/CompositeRootVppReader.java +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/impl/CompositeRootVppReader.java @@ -20,7 +20,7 @@ import com.google.common.annotations.Beta; import io.fd.honeycomb.v3po.impl.trans.ChildVppReader; import io.fd.honeycomb.v3po.impl.trans.VppReader; import io.fd.honeycomb.v3po.impl.trans.impl.spi.RootVppReaderCustomizer; -import io.fd.honeycomb.v3po.impl.trans.util.VppReaderUtils; +import io.fd.honeycomb.v3po.impl.trans.util.VppRWUtils; import java.util.List; import javax.annotation.Nonnull; import javax.annotation.concurrent.ThreadSafe; @@ -30,6 +30,9 @@ import org.opendaylight.yangtools.yang.binding.ChildOf; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +/** + * Composite implementation of {@link VppReader} + */ @Beta @ThreadSafe public final class CompositeRootVppReader<C extends DataObject, B extends Builder<C>> extends AbstractCompositeVppReader<C, B> @@ -37,35 +40,49 @@ public final class CompositeRootVppReader<C extends DataObject, B extends Builde private final RootVppReaderCustomizer<C, B> customizer; + /** + * Create new {@link CompositeRootVppReader} + * + * @param managedDataObjectType Class object for managed data type + * @param childReaders Child nodes(container, list) readers + * @param augReaders Child augmentations readers + * @param customizer Customizer instance to customize this generic reader + * + */ public CompositeRootVppReader(@Nonnull final Class<C> managedDataObjectType, @Nonnull final List<ChildVppReader<? extends ChildOf<C>>> childReaders, - @Nonnull final List<ChildVppReader<? extends Augmentation<C>>> childAugReaders, + @Nonnull final List<ChildVppReader<? extends Augmentation<C>>> augReaders, @Nonnull final RootVppReaderCustomizer<C, B> customizer) { - super(managedDataObjectType, childReaders, childAugReaders); + super(managedDataObjectType, childReaders, augReaders); this.customizer = customizer; } + /** + * @see {@link CompositeRootVppReader#CompositeRootVppReader(Class, List, List, RootVppReaderCustomizer)} + */ public CompositeRootVppReader(@Nonnull final Class<C> managedDataObjectType, @Nonnull final List<ChildVppReader<? extends ChildOf<C>>> childReaders, @Nonnull final RootVppReaderCustomizer<C, B> customizer) { - this(managedDataObjectType, childReaders, VppReaderUtils.<C>emptyAugReaderList(), customizer); + this(managedDataObjectType, childReaders, VppRWUtils.<C>emptyAugReaderList(), customizer); } + /** + * @see {@link CompositeRootVppReader#CompositeRootVppReader(Class, List, List, RootVppReaderCustomizer)} + */ public CompositeRootVppReader(@Nonnull final Class<C> managedDataObjectType, @Nonnull final RootVppReaderCustomizer<C, B> customizer) { - this(managedDataObjectType, VppReaderUtils.<C>emptyChildReaderList(), VppReaderUtils.<C>emptyAugReaderList(), + this(managedDataObjectType, VppRWUtils.<C>emptyChildReaderList(), VppRWUtils.<C>emptyAugReaderList(), customizer); } @Override protected void readCurrentAttributes(@Nonnull final InstanceIdentifier<C> id, @Nonnull final B builder) { - customizer.readCurrentAttributes(builder); + customizer.readCurrentAttributes(id, builder); } @Override - protected B getBuilder(@Nonnull final InstanceIdentifier<? extends DataObject> id) { - // TODO instantiate builder from customizer(as is) or reflection ? - return customizer.getBuilder(); + protected B getBuilder(@Nonnull final InstanceIdentifier<C> id) { + return customizer.getBuilder(id); } } diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/impl/spi/ChildVppReaderCustomizer.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/impl/spi/ChildVppReaderCustomizer.java index 7cf0b60f6..29cfe3eeb 100644 --- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/impl/spi/ChildVppReaderCustomizer.java +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/impl/spi/ChildVppReaderCustomizer.java @@ -17,13 +17,22 @@ package io.fd.honeycomb.v3po.impl.trans.impl.spi; import com.google.common.annotations.Beta; +import javax.annotation.Nonnull; import org.opendaylight.yangtools.concepts.Builder; import org.opendaylight.yangtools.yang.binding.DataObject; +/** + * io.fd.honeycomb.v3po.impl.trans.impl.CompositeChildVppReader SPI to customize its behavior + */ @Beta public interface ChildVppReaderCustomizer<C extends DataObject, B extends Builder<C>> extends RootVppReaderCustomizer<C, B> { - // FIXME need to capture parent builder type, but that's a inconvenient - void merge(Builder<? extends DataObject> parentBuilder, C readValue); + // FIXME need to capture parent builder type, but that's inconvenient at best, is it ok to leave it Builder<?> and + // cast in specific customizers ? ... probably better than adding another type parameter + + /** + * Merge read data into provided parent builder + */ + void merge(@Nonnull final Builder<? extends DataObject> parentBuilder, @Nonnull final C readValue); } diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/impl/spi/ListVppReaderCustomizer.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/impl/spi/ListVppReaderCustomizer.java index 855e8488d..8a6812dd6 100644 --- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/impl/spi/ListVppReaderCustomizer.java +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/impl/spi/ListVppReaderCustomizer.java @@ -18,20 +18,30 @@ package io.fd.honeycomb.v3po.impl.trans.impl.spi; import com.google.common.annotations.Beta; import java.util.List; +import javax.annotation.Nonnull; import org.opendaylight.yangtools.concepts.Builder; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.Identifiable; import org.opendaylight.yangtools.yang.binding.Identifier; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +/** + * io.fd.honeycomb.v3po.impl.trans.impl.CompositeListVppReader SPI to customize its behavior + */ @Beta -public interface ListVppReaderCustomizer<C extends DataObject & Identifiable<K>, K extends Identifier<C>, B extends Builder<C>> { - - void readCurrentAttributes(final InstanceIdentifier<C> id, B builder); - - B getBuilder(K id); +public interface ListVppReaderCustomizer<C extends DataObject & Identifiable<K>, K extends Identifier<C>, B extends Builder<C>> + extends RootVppReaderCustomizer<C, B> { - List<InstanceIdentifier<C>> getAllIds(InstanceIdentifier<C> id); + /** + * Return list with IDs of all list nodes to be read. + * + * @param id wildcarded ID pointing to list node managed by enclosing reader + */ + @Nonnull + List<K> getAllIds(@Nonnull final InstanceIdentifier<C> id); - void merge(Builder<?> builder, List<C> currentBuilder); + /** + * Merge read data into provided parent builder + */ + void merge(@Nonnull final Builder<? extends DataObject> builder, @Nonnull final List<C> readData); } diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/impl/spi/RootVppReaderCustomizer.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/impl/spi/RootVppReaderCustomizer.java index fe09220d2..463e4f9d6 100644 --- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/impl/spi/RootVppReaderCustomizer.java +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/impl/spi/RootVppReaderCustomizer.java @@ -17,13 +17,27 @@ package io.fd.honeycomb.v3po.impl.trans.impl.spi; import com.google.common.annotations.Beta; +import javax.annotation.Nonnull; import org.opendaylight.yangtools.concepts.Builder; import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +/** + * io.fd.honeycomb.v3po.impl.trans.impl.CompositeRootVppReader SPI to customize its behavior + */ @Beta public interface RootVppReaderCustomizer<C extends DataObject, B extends Builder<C>> { - B getBuilder(); + // TODO add (un)checked, well defined exception here to indicate issues in the customizer + + /** + * Create new builder that will be used to build read value + */ + @Nonnull + B getBuilder(@Nonnull final InstanceIdentifier<C> id); - void readCurrentAttributes(B builder); + /** + * Add current data (identified by id) to the provided builder + */ + void readCurrentAttributes(@Nonnull final InstanceIdentifier<C> id, @Nonnull final B builder); } diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/util/DelegatingReaderRegistry.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/util/DelegatingReaderRegistry.java index 0aeda896a..5225bab63 100644 --- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/util/DelegatingReaderRegistry.java +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/util/DelegatingReaderRegistry.java @@ -16,42 +16,41 @@ package io.fd.honeycomb.v3po.impl.trans.util; -import com.google.common.base.Function; -import com.google.common.base.Preconditions; +import static com.google.common.base.Preconditions.checkNotNull; + import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; import io.fd.honeycomb.v3po.impl.trans.ReaderRegistry; import io.fd.honeycomb.v3po.impl.trans.VppReader; +import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.annotation.Nonnull; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +/** + * Simple reader registry able to perform and aggregated read (ROOT read) on top of all + * provided readers. Also able to delegate a specific read to one of the delegate readers. + * + * This could serve as a utility to hold & hide all available readers in upper layers. + */ public final class DelegatingReaderRegistry implements ReaderRegistry { private final Map<Class<? extends DataObject>, VppReader<? extends DataObject>> rootReaders; + /** + * Create new {@link DelegatingReaderRegistry} + * + * @param rootReaders List of delegate readers + */ public DelegatingReaderRegistry(@Nonnull final List<VppReader<? extends DataObject>> rootReaders) { - this.rootReaders = toMap(rootReaders); - } - - private static Map<Class<? extends DataObject>, VppReader<? extends DataObject>> toMap( - final List<VppReader<? extends DataObject>> rootReaders) { - return Maps - .uniqueIndex(rootReaders, new Function<VppReader<? extends DataObject>, Class<? extends DataObject>>() { - @Override - public Class<? extends DataObject> apply(final VppReader<? extends DataObject> input) { - return input.getManagedDataObjectType().getTargetType(); - } - }); + this.rootReaders = VppRWUtils.uniqueLinkedIndex(checkNotNull(rootReaders), VppRWUtils.MANAGER_CLASS_FUNCTION); } @Override @Nonnull public List<? extends DataObject> readAll() { - final List<DataObject> objects = Lists.newArrayListWithExpectedSize(rootReaders.size()); + final List<DataObject> objects = new ArrayList<>(rootReaders.size()); for (VppReader<? extends DataObject> rootReader : rootReaders.values()) { final List<? extends DataObject> read = rootReader.read(rootReader.getManagedDataObjectType()); objects.addAll(read); @@ -62,14 +61,18 @@ public final class DelegatingReaderRegistry implements ReaderRegistry { @Nonnull @Override public List<? extends DataObject> read(@Nonnull final InstanceIdentifier<? extends DataObject> id) { - final InstanceIdentifier.PathArgument first = Preconditions.checkNotNull( + final InstanceIdentifier.PathArgument first = checkNotNull( Iterables.getFirst(id.getPathArguments(), null), "Empty id"); final VppReader<? extends DataObject> vppReader = rootReaders.get(first.getType()); - Preconditions.checkNotNull(vppReader, + checkNotNull(vppReader, "Unable to read %s. Missing reader. Current readers for: %s", id, rootReaders.keySet()); return vppReader.read(id); } + /** + * @throws UnsupportedOperationException This getter is not supported for reader registry since it does not manage a + * specific node type + */ @Nonnull @Override public InstanceIdentifier<DataObject> getManagedDataObjectType() { diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/util/NoopReaderCustomizer.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/util/NoopReaderCustomizer.java index 050f7171f..b485a6db4 100644 --- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/util/NoopReaderCustomizer.java +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/util/NoopReaderCustomizer.java @@ -19,11 +19,12 @@ package io.fd.honeycomb.v3po.impl.trans.util; import io.fd.honeycomb.v3po.impl.trans.impl.spi.RootVppReaderCustomizer; import org.opendaylight.yangtools.concepts.Builder; import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; public abstract class NoopReaderCustomizer<C extends DataObject, B extends Builder<C>> implements RootVppReaderCustomizer<C, B> { @Override - public void readCurrentAttributes(final B builder) { + public void readCurrentAttributes(InstanceIdentifier<C> id, final B builder) { // Noop } } diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/util/ReflectionUtils.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/util/ReflectionUtils.java new file mode 100644 index 000000000..6602d750d --- /dev/null +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/util/ReflectionUtils.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed 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 io.fd.honeycomb.v3po.impl.trans.util; + +import com.google.common.base.Optional; +import java.lang.reflect.Method; +import java.util.List; +import javax.annotation.Nonnull; + +/** + * Reflection based utilities + */ +public final class ReflectionUtils { + + private ReflectionUtils() {} + + /** + * Find a specific method using reflection + * + * @param managedType Class object to find method in + * @param prefix Method name prefix used when finding the method. Case does not matter. + * @param paramTypes List of input argument types + * @param retType Return type + * + * @return Found method or Optional.absent() if there's no such method + */ + @Nonnull + public static Optional<Method> findMethodReflex(@Nonnull final Class<?> managedType, + @Nonnull final String prefix, + @Nonnull final List<Class<?>> paramTypes, + @Nonnull final Class<?> retType) { + for (Method method : managedType.getMethods()) { + if(isMethodMatch(prefix, paramTypes, retType, method)) { + return Optional.of(method); + } + } + + return Optional.absent(); + } + + private static boolean isMethodMatch(final @Nonnull String prefix, + final @Nonnull List<Class<?>> paramTypes, + final @Nonnull Class<?> retType, final Method method) { + if (!method.getName().toLowerCase().startsWith(prefix.toLowerCase())) { + return false; + } + + final Class<?>[] parameterTypes = method.getParameterTypes(); + if (parameterTypes.length != paramTypes.size()) { + return false; + } + + for (int i = 0; i < parameterTypes.length; i++) { + if (!parameterTypes[i].isAssignableFrom(paramTypes.get(i))) { + return false; + } + } + + if (!method.getReturnType().equals(retType)) { + return false; + } + + return true; + } +} diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/util/ReflexiveChildReaderCustomizer.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/util/ReflexiveChildReaderCustomizer.java index 28a44eb07..d67a3afbb 100644 --- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/util/ReflexiveChildReaderCustomizer.java +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/util/ReflexiveChildReaderCustomizer.java @@ -41,7 +41,7 @@ public class ReflexiveChildReaderCustomizer<C extends DataObject, B extends Buil @Override public void merge(final Builder<? extends DataObject> parentBuilder, final C readValue) { final Optional<Method> method = - VppReaderUtils.findMethodReflex(parentBuilder.getClass(), "set", + ReflectionUtils.findMethodReflex(parentBuilder.getClass(), "set", Collections.<Class<?>>singletonList(readValue.getClass()), parentBuilder.getClass()); Preconditions.checkArgument(method.isPresent(), "Unable to set %s to %s", readValue, parentBuilder); diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/util/ReflexiveRootReaderCustomizer.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/util/ReflexiveRootReaderCustomizer.java index 2a98bbe5b..2568f9886 100644 --- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/util/ReflexiveRootReaderCustomizer.java +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/util/ReflexiveRootReaderCustomizer.java @@ -18,6 +18,7 @@ package io.fd.honeycomb.v3po.impl.trans.util; import org.opendaylight.yangtools.concepts.Builder; import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; /** * Might be slow ! @@ -31,7 +32,7 @@ public class ReflexiveRootReaderCustomizer<C extends DataObject, B extends Build } @Override - public B getBuilder() { + public B getBuilder(InstanceIdentifier<C> id) { try { return builderClass.newInstance(); } catch (InstantiationException | IllegalAccessException e) { diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/util/VppApiReaderCustomizer.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/util/VppApiReaderCustomizer.java index 584c7823f..1ca898c8c 100644 --- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/util/VppApiReaderCustomizer.java +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/util/VppApiReaderCustomizer.java @@ -18,15 +18,23 @@ package io.fd.honeycomb.v3po.impl.trans.util; import com.google.common.annotations.Beta; +/** + * Abstract utility to hold the vppApi reference. + */ @Beta public abstract class VppApiReaderCustomizer { private final org.openvpp.vppjapi.vppApi vppApi; - public VppApiReaderCustomizer(final org.openvpp.vppjapi.vppApi vppApi) { + protected VppApiReaderCustomizer(final org.openvpp.vppjapi.vppApi vppApi) { this.vppApi = vppApi; } + /** + * Get vppApi reference + * + * @return vppApi reference + */ public org.openvpp.vppjapi.vppApi getVppApi() { return vppApi; } diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/util/VppReaderUtils.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/util/VppRWUtils.java index 07c54ae90..1cc0dfe92 100644 --- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/util/VppReaderUtils.java +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/trans/util/VppRWUtils.java @@ -16,14 +16,17 @@ package io.fd.honeycomb.v3po.impl.trans.util; -import com.google.common.base.Optional; +import com.google.common.base.Function; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; +import com.google.common.collect.Maps; import io.fd.honeycomb.v3po.impl.trans.ChildVppReader; -import java.lang.reflect.Method; +import io.fd.honeycomb.v3po.impl.trans.SubtreeManager; +import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Map; import javax.annotation.Nonnull; import org.opendaylight.yangtools.yang.binding.Augmentation; import org.opendaylight.yangtools.yang.binding.ChildOf; @@ -32,9 +35,9 @@ import org.opendaylight.yangtools.yang.binding.Identifiable; import org.opendaylight.yangtools.yang.binding.Identifier; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -public final class VppReaderUtils { +public final class VppRWUtils { - private VppReaderUtils() {} + private VppRWUtils() {} /** * Find next item in ID after provided type @@ -77,7 +80,7 @@ public final class VppReaderUtils { */ @SuppressWarnings("unchecked") @Nonnull - public static <D extends DataObject & Identifiable<K>, K extends Identifier<D>> InstanceIdentifier<D> getCurrentId( + public static <D extends DataObject & Identifiable<K>, K extends Identifier<D>> InstanceIdentifier<D> replaceLastInId( @Nonnull final InstanceIdentifier<D> id, final InstanceIdentifier.IdentifiableItem<D, K> currentBdItem) { final Iterable<InstanceIdentifier.PathArgument> pathArguments = id.getPathArguments(); @@ -116,37 +119,46 @@ public final class VppReaderUtils { } /** - * Find a specific method using reflection + * Create a map from a collection, checking for duplicity in the process */ @Nonnull - public static Optional<Method> findMethodReflex(@Nonnull final Class<?> managedType, - @Nonnull final String prefix, - @Nonnull final List<Class<?>> paramTypes, - @Nonnull final Class<?> retType) { - top: - for (Method method : managedType.getMethods()) { - if (!method.getName().toLowerCase().startsWith(prefix.toLowerCase())) { - continue; - } - - final Class<?>[] parameterTypes = method.getParameterTypes(); - if (parameterTypes.length != paramTypes.size()) { - continue; - } + public static <K, V> Map<K, V> uniqueLinkedIndex(@Nonnull final Collection<V> values, @Nonnull final Function<? super V, K> keyFunction) { + final Map<K, V> objectObjectLinkedHashMap = Maps.newLinkedHashMap(); + for (V value : values) { + final K key = keyFunction.apply(value); + Preconditions.checkArgument(objectObjectLinkedHashMap.put(key, value) == null, + "Duplicate key detected : %s", key); + } + return objectObjectLinkedHashMap; + } - for (int i = 0; i < parameterTypes.length; i++) { - if (!parameterTypes[i].isAssignableFrom(paramTypes.get(i))) { - continue top; - } - } + public static final Function<SubtreeManager<? extends DataObject>, Class<? extends DataObject>> + MANAGER_CLASS_FUNCTION = new Function<SubtreeManager<? extends DataObject>, Class<? extends DataObject>>() { + @Override + public Class<? extends DataObject> apply(final SubtreeManager<? extends DataObject> input) { + return input.getManagedDataObjectType().getTargetType(); + } + }; - if (!method.getReturnType().equals(retType)) { - continue; - } + public static final Function<SubtreeManager<? extends Augmentation<?>>, Class<? extends DataObject>> + MANAGER_CLASS_AUG_FUNCTION = new Function<SubtreeManager<? extends Augmentation<?>>, Class<? extends DataObject>>() { - return Optional.of(method); + @Override + @SuppressWarnings("unchecked") + public Class<? extends DataObject> apply(final SubtreeManager<? extends Augmentation<?>> input) { + final Class<? extends Augmentation<?>> targetType = input.getManagedDataObjectType().getTargetType(); + Preconditions.checkArgument(DataObject.class.isAssignableFrom(targetType)); + return (Class<? extends DataObject>) targetType; } + }; - return Optional.absent(); + @SuppressWarnings("unchecked") + public static <D extends DataObject> InstanceIdentifier<D> appendTypeToId( + final InstanceIdentifier<? extends DataObject> parentId, final InstanceIdentifier<D> type) { + Preconditions.checkArgument(!parentId.contains(type), + "Unexpected InstanceIdentifier %s, already contains %s", parentId, type); + final InstanceIdentifier.PathArgument t = Iterables.getOnlyElement(type.getPathArguments()); + return (InstanceIdentifier<D>) InstanceIdentifier.create(Iterables.concat( + parentId.getPathArguments(), Collections.singleton(t))); } } diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/vppstate/BridgeDomainCustomizer.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/vppstate/BridgeDomainCustomizer.java index 447f8509c..97d1867c2 100644 --- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/vppstate/BridgeDomainCustomizer.java +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/vppstate/BridgeDomainCustomizer.java @@ -19,8 +19,9 @@ package io.fd.honeycomb.v3po.impl.vppstate; import com.google.common.collect.Lists; import io.fd.honeycomb.v3po.impl.trans.impl.spi.ListVppReaderCustomizer; import io.fd.honeycomb.v3po.impl.trans.util.VppApiReaderCustomizer; -import io.fd.honeycomb.v3po.impl.trans.util.VppReaderUtils; +import java.util.ArrayList; import java.util.List; +import javax.annotation.Nonnull; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.state.BridgeDomainsBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.state.bridge.domains.BridgeDomain; @@ -32,6 +33,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.state.bridge.domains.bridge.domain.L2Fib; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.state.bridge.domains.bridge.domain.L2FibBuilder; import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.openvpp.vppjapi.vppBridgeDomainDetails; import org.openvpp.vppjapi.vppBridgeDomainInterfaceDetails; @@ -40,27 +42,19 @@ import org.openvpp.vppjapi.vppL2Fib; public final class BridgeDomainCustomizer extends VppApiReaderCustomizer implements ListVppReaderCustomizer<BridgeDomain, BridgeDomainKey, BridgeDomainBuilder> { - public BridgeDomainCustomizer(final org.openvpp.vppjapi.vppApi vppApi) { + public BridgeDomainCustomizer(@Nonnull final org.openvpp.vppjapi.vppApi vppApi) { super(vppApi); } @Override - public void readCurrentAttributes(final InstanceIdentifier<BridgeDomain> id, - final BridgeDomainBuilder builder) { + public void readCurrentAttributes(@Nonnull final InstanceIdentifier<BridgeDomain> id, + @Nonnull final BridgeDomainBuilder builder) { final BridgeDomainKey key = id.firstKeyOf(id.getTargetType()); - final int bdId; - try { - bdId = Integer.parseInt(key.getName()); - } catch (NumberFormatException e) { - // LOG.warn("Invalid key", e); - return; - } + // TODO find out if bd exists based on name and if not return + + final int bdId = getVppApi().bridgeDomainIdFromName(key.getName()); final vppBridgeDomainDetails bridgeDomainDetails = getVppApi().getBridgeDomainDetails(bdId); - // FIXME, the problem here is that while going to VPP, the id for vbd is integer ID - // However in the models vbd's key is the name - // And you can get vbd name from vbd's ID using vppAPI, but not the other way around, making the API hard to use - // TO solve it, we need to store the vbd ID <-> vbd Name mapping in the (not-yet-available) read context and use it here builder.setName(key.getName()); // builder.setName(bridgeDomainDetails.name); builder.setArpTermination(bridgeDomainDetails.arpTerm); @@ -99,7 +93,7 @@ public final class BridgeDomainCustomizer extends VppApiReaderCustomizer } private List<Interface> getIfcs(final vppBridgeDomainDetails bridgeDomainDetails) { - final List<Interface> ifcs = Lists.newArrayListWithExpectedSize(bridgeDomainDetails.interfaces.length); + final List<Interface> ifcs = new ArrayList<>(bridgeDomainDetails.interfaces.length); for (vppBridgeDomainInterfaceDetails anInterface : bridgeDomainDetails.interfaces) { ifcs.add(new InterfaceBuilder() .setBridgedVirtualInterface(bridgeDomainDetails.bviInterfaceName.equals(anInterface.interfaceName)) @@ -110,27 +104,29 @@ public final class BridgeDomainCustomizer extends VppApiReaderCustomizer return ifcs; } + @Nonnull @Override - public BridgeDomainBuilder getBuilder(final BridgeDomainKey id) { + public BridgeDomainBuilder getBuilder(@Nonnull final InstanceIdentifier<BridgeDomain> id) { return new BridgeDomainBuilder(); } + @Nonnull @Override - public List<InstanceIdentifier<BridgeDomain>> getAllIds(final InstanceIdentifier<BridgeDomain> id) { - final int[] ints = getVppApi().bridgeDomainDump(-1); - final List<InstanceIdentifier<BridgeDomain>> allIds = Lists.newArrayListWithExpectedSize(ints.length); - for (int i : ints) { - final InstanceIdentifier.IdentifiableItem<BridgeDomain, BridgeDomainKey> currentBdItem = - VppReaderUtils.getCurrentIdItem(id, new BridgeDomainKey(Integer.toString(i))); - final InstanceIdentifier<BridgeDomain> e = VppReaderUtils.getCurrentId(id, currentBdItem); - allIds.add(e); + public List<BridgeDomainKey> getAllIds(@Nonnull final InstanceIdentifier<BridgeDomain> id) { + final int[] bIds = getVppApi().bridgeDomainDump(-1); + final List<BridgeDomainKey> allIds = new ArrayList<>(bIds.length); + for (int bId : bIds) { + // FIXME this is highly inefficient having to dump all of the bridge domain details + final vppBridgeDomainDetails bridgeDomainDetails = getVppApi().getBridgeDomainDetails(bId); + final String bName = bridgeDomainDetails.name; + allIds.add(new BridgeDomainKey(bName)); } return allIds; } @Override - public void merge(final Builder<?> builder, final List<BridgeDomain> currentBuilder) { - ((BridgeDomainsBuilder) builder).setBridgeDomain(currentBuilder); + public void merge(@Nonnull final Builder<? extends DataObject> builder, @Nonnull final List<BridgeDomain> readData) { + ((BridgeDomainsBuilder) builder).setBridgeDomain(readData); } } diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/vppstate/VersionCustomizer.java b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/vppstate/VersionCustomizer.java index 3ba21bbfb..641d61805 100644 --- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/vppstate/VersionCustomizer.java +++ b/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/vppstate/VersionCustomizer.java @@ -18,33 +18,36 @@ package io.fd.honeycomb.v3po.impl.vppstate; import io.fd.honeycomb.v3po.impl.trans.impl.spi.ChildVppReaderCustomizer; import io.fd.honeycomb.v3po.impl.trans.util.VppApiReaderCustomizer; +import javax.annotation.Nonnull; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppStateBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.state.Version; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.vpp.state.VersionBuilder; import org.opendaylight.yangtools.concepts.Builder; import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.openvpp.vppjapi.vppVersion; public final class VersionCustomizer extends VppApiReaderCustomizer implements ChildVppReaderCustomizer<Version, VersionBuilder> { - public VersionCustomizer(final org.openvpp.vppjapi.vppApi vppApi) { + public VersionCustomizer(@Nonnull final org.openvpp.vppjapi.vppApi vppApi) { super(vppApi); } @Override - public void merge(final Builder<? extends DataObject> parentBuilder, final Version readValue) { + public void merge(@Nonnull final Builder<? extends DataObject> parentBuilder, @Nonnull final Version readValue) { ((VppStateBuilder) parentBuilder).setVersion(readValue); } + @Nonnull @Override - public VersionBuilder getBuilder() { + public VersionBuilder getBuilder(@Nonnull InstanceIdentifier<Version> id) { return new VersionBuilder(); } @Override - public void readCurrentAttributes(final VersionBuilder builder) { + public void readCurrentAttributes(@Nonnull InstanceIdentifier<Version> id, @Nonnull final VersionBuilder builder) { final vppVersion vppVersion = getVppApi().getVppVersion(); builder.setBranch(vppVersion.gitBranch); builder.setName(vppVersion.programName); diff --git a/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/vppstate/BdTest.java b/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/vppstate/BdTest.java index ab3f6e054..7439bcd30 100644 --- a/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/vppstate/BdTest.java +++ b/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/vppstate/BdTest.java @@ -18,6 +18,7 @@ package io.fd.honeycomb.v3po.impl.vppstate; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.anyString; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; @@ -27,9 +28,12 @@ import io.fd.honeycomb.v3po.impl.trans.util.DelegatingReaderRegistry; import java.util.Collections; import java.util.List; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Matchers; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppState; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppStateBuilder; @@ -63,19 +67,46 @@ public class BdTest { private CompositeRootVppReader<VppState, VppStateBuilder> vppStateReader; private DelegatingReaderRegistry readerRegistry; private vppBridgeDomainDetails bdDetails; + private vppBridgeDomainDetails bdDetails2; @Before public void setUp() throws Exception { api = PowerMockito.mock(vppApi.class); bdDetails = new vppBridgeDomainDetails(); - setIfcs(bdDetails); - setBaseAttrs(bdDetails); + setBaseAttrs(bdDetails, "bdn1", 1); + + bdDetails2 = new vppBridgeDomainDetails(); + setIfcs(bdDetails2); + setBaseAttrs(bdDetails2, "bdn2", 2); final vppL2Fib[] l2Fibs = getL2Fibs(); PowerMockito.doReturn(l2Fibs).when(api).l2FibTableDump(Matchers.anyInt()); - PowerMockito.doReturn(bdDetails).when(api).getBridgeDomainDetails(Matchers.anyInt()); + PowerMockito.doAnswer(new Answer<vppBridgeDomainDetails>() { + + @Override + public vppBridgeDomainDetails answer(final InvocationOnMock invocationOnMock) throws Throwable { + final Integer idx = (Integer) invocationOnMock.getArguments()[0]; + switch (idx) { + case 1 : return bdDetails; + case 2 : return bdDetails2; + default: return null; + } + } + }).when(api).getBridgeDomainDetails(Matchers.anyInt()); + + PowerMockito.doAnswer(new Answer<Object>() { + @Override + public Object answer(final InvocationOnMock invocationOnMock) throws Throwable { + final String name = (String) invocationOnMock.getArguments()[0]; + switch (name) { + case "bdn1" : return 1; + case "bdn2" : return 2; + default: return null; + } + } + }).when(api).bridgeDomainIdFromName(anyString()); PowerMockito.doReturn(new int[] {1, 2}).when(api).bridgeDomainDump(Matchers.anyInt()); PowerMockito.doReturn(VERSION).when(api).getVppVersion(); vppStateReader = VppStateUtils.getVppStateReader(api); @@ -96,10 +127,10 @@ public class BdTest { bdDetails.interfaces = new vppBridgeDomainInterfaceDetails[] {ifcDetails}; } - private void setBaseAttrs(final vppBridgeDomainDetails bdDetails) { - bdDetails.name = "bdn"; + private void setBaseAttrs(final vppBridgeDomainDetails bdDetails, final String bdn, final int i) { + bdDetails.name = bdn; bdDetails.arpTerm = true; - bdDetails.bdId = 1; + bdDetails.bdId = i; bdDetails.bviInterfaceName = "ifc"; bdDetails.flood = true; bdDetails.forward = true; @@ -154,7 +185,7 @@ public class BdTest { // Deep child without a dedicated reader with specific l2fib key List<? extends DataObject> read = readerRegistry.read(InstanceIdentifier.create(VppState.class).child(BridgeDomains.class).child( - BridgeDomain.class, new BridgeDomainKey("1")) + BridgeDomain.class, new BridgeDomainKey("bdn1")) .child(L2Fib.class, new L2FibKey(new PhysAddress("01:02:03:04:05:06")))); // System.err.println(read); assertEquals(read.size(), 1); @@ -162,7 +193,7 @@ public class BdTest { // non existing l2fib read = readerRegistry.read(InstanceIdentifier.create(VppState.class).child(BridgeDomains.class).child( - BridgeDomain.class, new BridgeDomainKey("1")) + BridgeDomain.class, new BridgeDomainKey("bdn1")) .child(L2Fib.class, new L2FibKey(new PhysAddress("FF:FF:FF:04:05:06")))); // System.err.println(read); assertEquals(read.size(), 0); @@ -175,7 +206,7 @@ public class BdTest { public void testReadL2FibAll() throws Exception { // Deep child without a dedicated reader final InstanceIdentifier<L2Fib> id = InstanceIdentifier.create(VppState.class).child(BridgeDomains.class).child( - BridgeDomain.class, new BridgeDomainKey("1")).child(L2Fib.class); + BridgeDomain.class, new BridgeDomainKey("bdn1")).child(L2Fib.class); final List<? extends DataObject> read = readerRegistry.read(id); // System.err.println(read); assertEquals(read.toString(), read.size(), 2); @@ -204,19 +235,18 @@ public class BdTest { final List<? extends DataObject> read = readerRegistry.read(InstanceIdentifier.create(VppState.class).child(BridgeDomains.class).child( - BridgeDomain.class, new BridgeDomainKey("1"))); + BridgeDomain.class, new BridgeDomainKey("bdn1"))); -// System.err.println(read); assertEquals(read.size(), 1); assertEquals(Iterables.find(readRoot.getBridgeDomains().getBridgeDomain(), new Predicate<BridgeDomain>() { @Override public boolean apply(final BridgeDomain input) { -// System.err.println(input.getKey()); - return input.getKey().getName().equals("1"); + return input.getKey().getName().equals("bdn1"); } }), read.get(0)); } + @Ignore("Bridge domain customizer does not check whether the bd exists or not and fails with NPE, add it there") @Test public void testReadBridgeDomainNotExisting() throws Exception { final List<? extends DataObject> read = diff --git a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/vppstate/VppStateUtils.java b/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/vppstate/VppStateUtils.java index 7a4a08eaf..e28e5b092 100644 --- a/v3po/impl/src/main/java/io/fd/honeycomb/v3po/impl/vppstate/VppStateUtils.java +++ b/v3po/impl/src/test/java/io/fd/honeycomb/v3po/impl/vppstate/VppStateUtils.java @@ -20,10 +20,9 @@ import io.fd.honeycomb.v3po.impl.trans.ChildVppReader; import io.fd.honeycomb.v3po.impl.trans.impl.CompositeChildVppReader; import io.fd.honeycomb.v3po.impl.trans.impl.CompositeListVppReader; import io.fd.honeycomb.v3po.impl.trans.impl.CompositeRootVppReader; -import io.fd.honeycomb.v3po.impl.trans.impl.spi.RootVppReaderCustomizer; import io.fd.honeycomb.v3po.impl.trans.util.ReflexiveChildReaderCustomizer; import io.fd.honeycomb.v3po.impl.trans.util.ReflexiveRootReaderCustomizer; -import io.fd.honeycomb.v3po.impl.trans.util.VppReaderUtils; +import io.fd.honeycomb.v3po.impl.trans.util.VppRWUtils; import java.util.ArrayList; import java.util.List; import javax.annotation.Nonnull; @@ -38,32 +37,26 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev import org.opendaylight.yangtools.yang.binding.ChildOf; import org.openvpp.vppjapi.vppApi; -public final class VppStateUtils { +final class VppStateUtils { public VppStateUtils() {} /** * Create root VppState reader with all its children wired */ - public static CompositeRootVppReader<VppState, VppStateBuilder> getVppStateReader(@Nonnull final vppApi vppApi) { - final RootVppReaderCustomizer<VppState, VppStateBuilder> rootVppReaderCustomizer = - new ReflexiveRootReaderCustomizer<>(VppStateBuilder.class); + static CompositeRootVppReader<VppState, VppStateBuilder> getVppStateReader(@Nonnull final vppApi vppApi) { final ChildVppReader<Version> versionReader = new CompositeChildVppReader<>( - Version.class, - new VersionCustomizer(vppApi)); + Version.class, new VersionCustomizer(vppApi)); final CompositeListVppReader<BridgeDomain, BridgeDomainKey, BridgeDomainBuilder> - identifierBuilderCompositeListVppReader = new CompositeListVppReader<>( + bridgeDomainReader = new CompositeListVppReader<>( BridgeDomain.class, new BridgeDomainCustomizer(vppApi)); - final List<ChildVppReader<? extends ChildOf<BridgeDomains>>> bdChildReaders = new ArrayList<>(); - bdChildReaders.add(identifierBuilderCompositeListVppReader); - final ChildVppReader<BridgeDomains> bridgeDomainsReader = new CompositeChildVppReader<>( BridgeDomains.class, - bdChildReaders, + VppRWUtils.singletonChildReaderList(bridgeDomainReader), new ReflexiveChildReaderCustomizer<>(BridgeDomainsBuilder.class)); final List<ChildVppReader<? extends ChildOf<VppState>>> childVppReaders = new ArrayList<>(); @@ -73,7 +66,7 @@ public final class VppStateUtils { return new CompositeRootVppReader<>( VppState.class, childVppReaders, - VppReaderUtils.<VppState>emptyAugReaderList(), - rootVppReaderCustomizer); + VppRWUtils.<VppState>emptyAugReaderList(), + new ReflexiveRootReaderCustomizer<>(VppStateBuilder.class)); } } |