diff options
author | Maros Marsalek <mmarsale@cisco.com> | 2016-06-29 09:14:51 +0200 |
---|---|---|
committer | Maros Marsalek <mmarsale@cisco.com> | 2016-07-13 11:24:26 +0200 |
commit | 9f6b16d6e8ade6dfa40e9bbf4196d55adf8f2fec (patch) | |
tree | 9cd3d971e42b9351fbba36c788631e7a68a1027d /v3po/translate-utils/src/main/java | |
parent | 348d54eb9a762f1bde68ef8becb5d9e5a1ca7006 (diff) |
HONEYCOMB-94 Reimplement writer registry with better ordering options
Now the registry is flat and allows for full control of writer execution order
Change-Id: I864e1d676588ffe59b596145e0829e81b1a1ed2f
Signed-off-by: Maros Marsalek <mmarsale@cisco.com>
Diffstat (limited to 'v3po/translate-utils/src/main/java')
14 files changed, 703 insertions, 454 deletions
diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/RWUtils.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/RWUtils.java index b712e159c..55ae9ecf0 100644 --- a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/RWUtils.java +++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/RWUtils.java @@ -23,13 +23,13 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import io.fd.honeycomb.v3po.translate.SubtreeManager; import io.fd.honeycomb.v3po.translate.read.ChildReader; -import io.fd.honeycomb.v3po.translate.write.ChildWriter; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.stream.Collector; import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import javax.annotation.Nonnull; import org.opendaylight.yangtools.yang.binding.Augmentation; import org.opendaylight.yangtools.yang.binding.ChildOf; @@ -79,18 +79,10 @@ public final class RWUtils { return Collections.emptyList(); } - public static <T> List<ChildWriter<? extends ChildOf<T>>> emptyChildWriterList() { - return Collections.emptyList(); - } - public static <T> List<ChildReader<? extends Augmentation<T>>> emptyAugReaderList() { return Collections.emptyList(); } - public static <T> List<ChildWriter<? extends Augmentation<T>>> emptyAugWriterList() { - return Collections.emptyList(); - } - public static <T> List<ChildReader<? extends Augmentation<T>>> singletonAugReaderList( ChildReader<? extends Augmentation<T>> item) { return Collections.<ChildReader<? extends Augmentation<T>>>singletonList(item); @@ -101,16 +93,6 @@ public final class RWUtils { return Collections.<ChildReader<? extends ChildOf<T>>>singletonList(item); } - public static <T> List<ChildWriter<? extends ChildOf<T>>> singletonChildWriterList( - ChildWriter<? extends ChildOf<T>> item) { - return Collections.<ChildWriter<? extends ChildOf<T>>>singletonList(item); - } - - public static <T> List<ChildWriter<? extends Augmentation<T>>> singletonAugWriterList( - ChildWriter<? extends Augmentation<T>> item) { - return Collections.<ChildWriter<? extends Augmentation<T>>>singletonList(item); - } - /** * Replace last item in ID with a provided IdentifiableItem of the same type */ @@ -197,4 +179,21 @@ public final class RWUtils { return (InstanceIdentifier<D>) InstanceIdentifier.create(Iterables.concat( parentId.getPathArguments(), Collections.singleton(t))); } + + /** + * Transform a keyed instance identifier into a wildcarded one. + */ + public static InstanceIdentifier<?> makeIidWildcarded(final InstanceIdentifier<?> id) { + final List<InstanceIdentifier.PathArgument> transformedPathArguments = + StreamSupport.stream(id.getPathArguments().spliterator(), false) + .map(RWUtils::cleanPathArgumentFromKeys) + .collect(Collectors.toList()); + return InstanceIdentifier.create(transformedPathArguments); + } + + private static InstanceIdentifier.PathArgument cleanPathArgumentFromKeys(final InstanceIdentifier.PathArgument pathArgument) { + return pathArgument instanceof InstanceIdentifier.IdentifiableItem<?, ?> + ? new InstanceIdentifier.Item<>(pathArgument.getType()) + : pathArgument; + } } diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/CloseableWriterRegistry.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/CloseableWriterRegistry.java deleted file mode 100644 index cd53a4f8b..000000000 --- a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/CloseableWriterRegistry.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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.translate.util.write; - -import io.fd.honeycomb.v3po.translate.TranslationException; -import io.fd.honeycomb.v3po.translate.write.WriteContext; -import io.fd.honeycomb.v3po.translate.write.WriteFailedException; -import io.fd.honeycomb.v3po.translate.write.WriterRegistry; -import java.util.Map; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -/** - * WriterRegistry wrapper providing AutoCloseable interface. - */ -public final class CloseableWriterRegistry implements WriterRegistry, AutoCloseable { - private final WriterRegistry writerRegistry; - - public CloseableWriterRegistry( final WriterRegistry writerRegistry) { - this.writerRegistry = writerRegistry; - } - - @Override - public void update( - @Nonnull final Map<InstanceIdentifier<?>, DataObject> nodesBefore, - @Nonnull final Map<InstanceIdentifier<?>, DataObject> nodesAfter, - @Nonnull final WriteContext ctx) throws TranslationException { - writerRegistry.update(nodesBefore, nodesAfter, ctx); - } - - @Override - public void update( - @Nonnull final InstanceIdentifier<? extends DataObject> id, - @Nullable final DataObject dataBefore, @Nullable final DataObject dataAfter, - @Nonnull final WriteContext ctx) throws WriteFailedException { - writerRegistry.update(id, dataBefore, dataAfter, ctx); - } - - @Nonnull - @Override - public InstanceIdentifier<DataObject> getManagedDataObjectType() { - return writerRegistry.getManagedDataObjectType(); - } - - @Override - public void close() throws Exception { - // NOOP - } -}
\ No newline at end of file diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/DelegatingWriterRegistry.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/DelegatingWriterRegistry.java deleted file mode 100644 index 061d3fa4a..000000000 --- a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/DelegatingWriterRegistry.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * 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.translate.util.write; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; -import com.google.common.collect.Multimap; -import io.fd.honeycomb.v3po.translate.util.RWUtils; -import io.fd.honeycomb.v3po.translate.write.WriteContext; -import io.fd.honeycomb.v3po.translate.write.WriteFailedException; -import io.fd.honeycomb.v3po.translate.write.Writer; -import io.fd.honeycomb.v3po.translate.write.WriterRegistry; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Simple writer registry able to perform and aggregated write (ROOT write) on top of all provided writers. Also able to - * delegate a specific write to one of the delegate writers. - * - * This could serve as a utility to hold & hide all available writers in upper layers. - */ -public final class DelegatingWriterRegistry implements WriterRegistry { - - private static final Logger LOG = LoggerFactory.getLogger(DelegatingWriterRegistry.class); - - private final Map<Class<? extends DataObject>, Writer<? extends DataObject>> rootWriters; - - /** - * Create new {@link DelegatingWriterRegistry} - * - * @param rootWriters List of delegate writers - */ - public DelegatingWriterRegistry(@Nonnull final List<Writer<? extends DataObject>> rootWriters) { - this.rootWriters = RWUtils.uniqueLinkedIndex(checkNotNull(rootWriters), RWUtils.MANAGER_CLASS_FUNCTION); - } - - /** - * @throws UnsupportedOperationException This getter is not supported for writer registry since it does not manage a - * specific node type - */ - @Nonnull - @Override - public InstanceIdentifier<DataObject> getManagedDataObjectType() { - throw new UnsupportedOperationException("Root registry has no type"); - } - - @Override - public void update(@Nonnull final InstanceIdentifier<? extends DataObject> id, - @Nullable final DataObject dataBefore, - @Nullable final DataObject dataAfter, - @Nonnull final WriteContext ctx) throws WriteFailedException { - final InstanceIdentifier.PathArgument first = checkNotNull( - Iterables.getFirst(id.getPathArguments(), null), "Empty id"); - final Writer<? extends DataObject> writer = rootWriters.get(first.getType()); - checkNotNull(writer, - "Unable to write %s. Missing writer. Current writers for: %s", id, rootWriters.keySet()); - writer.update(id, dataBefore, dataAfter, ctx); - } - - @Override - public void update(@Nonnull final Map<InstanceIdentifier<?>, DataObject> nodesBefore, - @Nonnull final Map<InstanceIdentifier<?>, DataObject> nodesAfter, - @Nonnull final WriteContext ctx) throws WriteFailedException { - - Multimap<InstanceIdentifier<?>, InstanceIdentifier<?>> rootIdToNestedIds = HashMultimap.create(); - try { - checkAllWritersPresent(nodesBefore, rootIdToNestedIds); - checkAllWritersPresent(nodesAfter, rootIdToNestedIds); - } catch (IllegalArgumentException e) { - LOG.warn("Unable to process update", e); - throw e; - } - - final List<InstanceIdentifier<?>> processedNodes = Lists.newArrayList(); - - for (Map.Entry<Class<? extends DataObject>, Writer<? extends DataObject>> rootWriterEntry : rootWriters - .entrySet()) { - - final InstanceIdentifier<? extends DataObject> id = rootWriterEntry.getValue().getManagedDataObjectType(); - // FIXME !! this is not ideal, we are not handling nested updates in expected order - // Root writers are invoked in order they were registered, but nested updates are not, since they are - // iterated here. - // - for (InstanceIdentifier<?> specificInstanceIdentifier : rootIdToNestedIds.get(id)) { - final DataObject dataBefore = nodesBefore.get(specificInstanceIdentifier); - final DataObject dataAfter = nodesAfter.get(specificInstanceIdentifier); - - // No change to current writer - if (dataBefore == null && dataAfter == null) { - continue; - } - - try { - LOG.debug("ChangesProcessor.applyChanges() processing dataBefore={}, dataAfter={}", dataBefore, dataAfter); - update(specificInstanceIdentifier, dataBefore, dataAfter, ctx); - processedNodes.add(id); - } catch (Exception e) { - LOG.error("Error while processing data change of: {} (before={}, after={})", id, dataBefore, dataAfter, e); - throw new BulkUpdateException(id, new ReverterImpl(this, processedNodes, nodesBefore, nodesAfter, ctx), e); - } - } - } - } - - private void checkAllWritersPresent(final @Nonnull Map<InstanceIdentifier<?>, DataObject> nodesBefore, - final @Nonnull Multimap<InstanceIdentifier<?>, InstanceIdentifier<?>> rootIdToNestedIds) { - for (final InstanceIdentifier<?> changeId : nodesBefore.keySet()) { - final InstanceIdentifier.PathArgument first = Iterables.getFirst(changeId.getPathArguments(), null); - checkNotNull(first, "Empty identifier detected"); - final InstanceIdentifier<? extends DataObject> rootId = InstanceIdentifier.create(first.getType()); - checkArgument(rootWriters.keySet().contains(first.getType()), - "Unable to handle change. Missing dedicated writer for: %s", first.getType()); - rootIdToNestedIds.put(rootId, changeId); - } - } - - private static final class ReverterImpl implements Reverter { - private final WriterRegistry delegatingWriterRegistry; - private final List<InstanceIdentifier<?>> processedNodes; - private final Map<InstanceIdentifier<?>, DataObject> nodesBefore; - private final Map<InstanceIdentifier<?>, DataObject> nodesAfter; - private final WriteContext ctx; - - ReverterImpl(final WriterRegistry delegatingWriterRegistry, - final List<InstanceIdentifier<?>> processedNodes, - final Map<InstanceIdentifier<?>, DataObject> nodesBefore, - final Map<InstanceIdentifier<?>, DataObject> nodesAfter, final WriteContext ctx) { - this.delegatingWriterRegistry = delegatingWriterRegistry; - this.processedNodes = processedNodes; - this.nodesBefore = nodesBefore; - this.nodesAfter = nodesAfter; - this.ctx = ctx; - } - - @Override - public void revert() throws RevertFailedException { - final LinkedList<InstanceIdentifier<?>> notReverted = new LinkedList<>(processedNodes); - - while (notReverted.size() > 0) { - final InstanceIdentifier<?> node = notReverted.peekLast(); - LOG.debug("ChangesProcessor.revertChanges() processing node={}", node); - - final DataObject dataBefore = nodesBefore.get(node); - final DataObject dataAfter = nodesAfter.get(node); - - // revert a change by invoking writer with reordered arguments - try { - delegatingWriterRegistry.update(node, dataAfter, dataBefore, ctx); - notReverted.removeLast(); // change was successfully reverted - } catch (Exception e) { - throw new RevertFailedException(notReverted, e); - } - - } - } - } -} diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/NoopWriterCustomizer.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/NoopWriterCustomizer.java deleted file mode 100644 index 9f9c9f53e..000000000 --- a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/NoopWriterCustomizer.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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.translate.util.write; - -import io.fd.honeycomb.v3po.translate.spi.write.RootWriterCustomizer; -import io.fd.honeycomb.v3po.translate.write.WriteContext; -import javax.annotation.Nonnull; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -/** - * Customizer not performing any changes on current level. Suitable for nodes that don't have any leaves and all of - * its child nodes are managed by dedicated writers - */ -public class NoopWriterCustomizer<D extends DataObject> implements RootWriterCustomizer<D> { - - @Override - public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final D dataAfter, - @Nonnull final WriteContext ctx) { - - } - - @Override - public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final D dataBefore, - @Nonnull final D dataAfter, - @Nonnull final WriteContext ctx) { - - } - - @Override - public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final D dataBefore, - @Nonnull final WriteContext ctx) { - - } -} diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/NoopWriterRegistry.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/NoopWriterRegistry.java index 866854212..236ad8917 100644 --- a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/NoopWriterRegistry.java +++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/NoopWriterRegistry.java @@ -20,7 +20,6 @@ import io.fd.honeycomb.v3po.translate.TranslationException; import io.fd.honeycomb.v3po.translate.write.WriteContext; import io.fd.honeycomb.v3po.translate.write.WriteFailedException; import io.fd.honeycomb.v3po.translate.write.WriterRegistry; -import java.util.Map; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opendaylight.yangtools.yang.binding.DataObject; @@ -32,19 +31,11 @@ import org.slf4j.LoggerFactory; * Empty registry that does not perform any changes. Can be used in data layer, if we want to disable passing data to * translation layer. */ -public class NoopWriterRegistry implements WriterRegistry { +public class NoopWriterRegistry implements WriterRegistry, AutoCloseable { private static final Logger LOG = LoggerFactory.getLogger(NoopWriterRegistry.class); @Override - public void update(@Nonnull final Map<InstanceIdentifier<?>, DataObject> dataBefore, - @Nonnull final Map<InstanceIdentifier<?>, DataObject> dataAfter, @Nonnull final WriteContext ctx) - throws TranslationException { - LOG.trace("NoopWriterRegistry.update dataBefore{}, dataAfter={], ctx={}", dataBefore, dataAfter, ctx); - // NOOP - } - - @Override public void update(@Nonnull final InstanceIdentifier<? extends DataObject> id, @Nullable final DataObject dataBefore, @Nullable final DataObject dataAfter, @Nonnull final WriteContext ctx) throws WriteFailedException { @@ -53,9 +44,20 @@ public class NoopWriterRegistry implements WriterRegistry { // NOOP } + @Override + public void update(@Nonnull final DataObjectUpdates updates, + @Nonnull final WriteContext ctx) throws TranslationException { + // NOOP + } + @Nonnull @Override public InstanceIdentifier<DataObject> getManagedDataObjectType() { - throw new UnsupportedOperationException("Root registry has no type"); + throw new UnsupportedOperationException("Noop registry has no type"); + } + + @Override + public void close() throws Exception { + // NOOP } } diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/ReflexiveAugmentWriterCustomizer.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/ReflexiveAugmentWriterCustomizer.java deleted file mode 100644 index 6d29214b3..000000000 --- a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/ReflexiveAugmentWriterCustomizer.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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.translate.util.write; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; - -import com.google.common.base.Optional; -import io.fd.honeycomb.v3po.translate.spi.write.ChildWriterCustomizer; -import javax.annotation.Nonnull; -import org.opendaylight.yangtools.yang.binding.Augmentable; -import org.opendaylight.yangtools.yang.binding.Augmentation; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -/** - * Might be slow ! - */ -public class ReflexiveAugmentWriterCustomizer<C extends DataObject> extends NoopWriterCustomizer<C> implements - ChildWriterCustomizer<C> { - - @Nonnull - @Override - @SuppressWarnings("unchecked") - public Optional<C> extract(@Nonnull final InstanceIdentifier<C> currentId, @Nonnull final DataObject parentData) { - checkArgument(parentData instanceof Augmentable<?>, "Not augmnatable parent object: %s", parentData); - final Class<C> currentType = currentId.getTargetType(); - final Augmentation<?> augmentation = ((Augmentable) parentData).getAugmentation(currentType); - if(augmentation == null) { - return Optional.absent(); - } else { - checkState(currentType.isAssignableFrom(augmentation.getClass())); - return Optional.of(currentType.cast(augmentation)); - } - } -} diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/ReflexiveChildWriterCustomizer.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/ReflexiveChildWriterCustomizer.java deleted file mode 100644 index 79cdf62c3..000000000 --- a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/ReflexiveChildWriterCustomizer.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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.translate.util.write; - -import com.google.common.base.Optional; -import com.google.common.base.Preconditions; -import com.google.common.collect.Iterables; -import io.fd.honeycomb.v3po.translate.util.ReflectionUtils; -import io.fd.honeycomb.v3po.translate.spi.write.ChildWriterCustomizer; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.Collections; -import javax.annotation.Nonnull; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -/** - * Might be slow ! - */ -public class ReflexiveChildWriterCustomizer<C extends DataObject> extends NoopWriterCustomizer<C> implements - ChildWriterCustomizer<C> { - - @Nonnull - @Override - @SuppressWarnings("unchecked") - public Optional<C> extract(@Nonnull final InstanceIdentifier<C> currentId, @Nonnull final DataObject parentData) { - final Class<C> currentType = currentId.getTargetType(); - final Optional<Method> method = ReflectionUtils.findMethodReflex(getParentType(currentId), - "get" + currentType.getSimpleName(), Collections.<Class<?>>emptyList(), currentType); - - Preconditions.checkArgument(method.isPresent(), "Unable to get %s from %s", currentType, parentData); - - try { - return method.isPresent() - ? Optional.fromNullable((C) method.get().invoke(parentData)) - : Optional.absent(); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new IllegalArgumentException("Unable to get " + currentType + " from " + parentData, e); - } - } - - private Class<? extends DataObject> getParentType(final @Nonnull InstanceIdentifier<C> currentId) { - return Iterables.get(currentId.getPathArguments(), Iterables.size(currentId.getPathArguments()) - 2).getType(); - } -} diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistry.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistry.java new file mode 100644 index 000000000..79d8eb88d --- /dev/null +++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistry.java @@ -0,0 +1,334 @@ +/* + * 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.translate.util.write.registry; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.Optional; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; +import io.fd.honeycomb.v3po.translate.TranslationException; +import io.fd.honeycomb.v3po.translate.util.RWUtils; +import io.fd.honeycomb.v3po.translate.write.DataObjectUpdate; +import io.fd.honeycomb.v3po.translate.write.WriteContext; +import io.fd.honeycomb.v3po.translate.write.WriteFailedException; +import io.fd.honeycomb.v3po.translate.write.Writer; +import io.fd.honeycomb.v3po.translate.write.WriterRegistry; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.concurrent.ThreadSafe; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Flat writer registry, delegating updates to writers in the order writers were submitted. + */ +@ThreadSafe +final class FlatWriterRegistry implements WriterRegistry { + + private static final Logger LOG = LoggerFactory.getLogger(FlatWriterRegistry.class); + + // All types handled by writers directly or as children + private final ImmutableSet<InstanceIdentifier<?>> handledTypes; + + private final Set<InstanceIdentifier<?>> writersOrderReversed; + private final Set<InstanceIdentifier<?>> writersOrder; + private final Map<InstanceIdentifier<?>, Writer<?>> writers; + + /** + * Create flat registry instance. + * + * @param writers immutable, ordered map of writers to use to process updates. Order of the writers has to be + * one in which create and update operations should be handled. Deletes will be handled in reversed + * order. All deletes are handled before handling all the updates. + */ + FlatWriterRegistry(@Nonnull final ImmutableMap<InstanceIdentifier<?>, Writer<?>> writers) { + this.writers = writers; + this.writersOrderReversed = Sets.newLinkedHashSet(Lists.reverse(Lists.newArrayList(writers.keySet()))); + this.writersOrder = writers.keySet(); + this.handledTypes = getAllHandledTypes(writers); + } + + private static ImmutableSet<InstanceIdentifier<?>> getAllHandledTypes( + @Nonnull final ImmutableMap<InstanceIdentifier<?>, Writer<?>> writers) { + final ImmutableSet.Builder<InstanceIdentifier<?>> handledTypesBuilder = ImmutableSet.builder(); + for (Map.Entry<InstanceIdentifier<?>, Writer<?>> writerEntry : writers.entrySet()) { + final InstanceIdentifier<?> writerType = writerEntry.getKey(); + final Writer<?> writer = writerEntry.getValue(); + handledTypesBuilder.add(writerType); + if (writer instanceof SubtreeWriter) { + handledTypesBuilder.addAll(((SubtreeWriter<?>) writer).getHandledChildTypes()); + } + } + return handledTypesBuilder.build(); + } + + @Override + public void update(@Nonnull final InstanceIdentifier<? extends DataObject> id, + @Nullable final DataObject dataBefore, + @Nullable final DataObject dataAfter, + @Nonnull final WriteContext ctx) throws WriteFailedException { + singleUpdate(ImmutableMultimap.of( + RWUtils.makeIidWildcarded(id), DataObjectUpdate.create(id, dataBefore, dataAfter)), ctx); + } + + @Override + public void update(@Nonnull final DataObjectUpdates updates, + @Nonnull final WriteContext ctx) throws TranslationException { + if (updates.isEmpty()) { + return; + } + + // Optimization + if (updates.containsOnlySingleType()) { + // First process delete + singleUpdate(updates.getDeletes(), ctx); + // Next is update + singleUpdate(updates.getUpdates(), ctx); + } else { + // First process deletes + bulkUpdate(updates.getDeletes(), ctx, true, writersOrderReversed); + // Next are updates + bulkUpdate(updates.getUpdates(), ctx, true, writersOrder); + } + + LOG.debug("Update successful for types: {}", updates.getTypeIntersection()); + LOG.trace("Update successful for: {}", updates); + } + + private void singleUpdate(@Nonnull final Multimap<InstanceIdentifier<?>, ? extends DataObjectUpdate> updates, + @Nonnull final WriteContext ctx) throws WriteFailedException { + if (updates.isEmpty()) { + return; + } + + final InstanceIdentifier<?> singleType = updates.keySet().iterator().next(); + LOG.debug("Performing single type update for: {}", singleType); + Collection<? extends DataObjectUpdate> singleTypeUpdates = updates.get(singleType); + Writer<?> writer = getWriter(singleType); + + if (writer == null) { + // This node must be handled by a subtree writer, find it and call it or else fail + checkArgument(handledTypes.contains(singleType), "Unable to process update. Missing writers for: %s", + singleType); + writer = getSubtreeWriterResponsible(singleType); + singleTypeUpdates = getParentDataObjectUpdate(ctx, updates, writer); + } + + LOG.trace("Performing single type update with writer: {}", writer); + for (DataObjectUpdate singleUpdate : singleTypeUpdates) { + writer.update(singleUpdate.getId(), singleUpdate.getDataBefore(), singleUpdate.getDataAfter(), ctx); + } + } + + private Writer<?> getSubtreeWriterResponsible(final InstanceIdentifier<?> singleType) { + final Writer<?> writer;// This is slow ( minor TODO-perf ) + writer = writers.values().stream() + .filter(w -> w instanceof SubtreeWriter) + .filter(w -> ((SubtreeWriter<?>) w).getHandledChildTypes().contains(singleType)) + .findFirst() + .get(); + return writer; + } + + private Collection<DataObjectUpdate> getParentDataObjectUpdate(final WriteContext ctx, + final Multimap<InstanceIdentifier<?>, ? extends DataObjectUpdate> updates, + final Writer<?> writer) { + // Now read data for subtree reader root, but first keyed ID is needed and that ID can be cut from updates + InstanceIdentifier<?> firstAffectedChildId = ((SubtreeWriter<?>) writer).getHandledChildTypes().stream() + .filter(updates::containsKey) + .map(unkeyedId -> updates.get(unkeyedId)) + .flatMap(doUpdates -> doUpdates.stream()) + .map(DataObjectUpdate::getId) + .findFirst() + .get(); + + final InstanceIdentifier<?> parentKeyedId = + RWUtils.cutId(firstAffectedChildId, writer.getManagedDataObjectType()); + + final Optional<? extends DataObject> parentBefore = ctx.readBefore(parentKeyedId); + final Optional<? extends DataObject> parentAfter = ctx.readAfter(parentKeyedId); + return Collections.singleton( + DataObjectUpdate.create(parentKeyedId, parentBefore.orNull(), parentAfter.orNull())); + } + + private void bulkUpdate(@Nonnull final Multimap<InstanceIdentifier<?>, ? extends DataObjectUpdate> updates, + @Nonnull final WriteContext ctx, + final boolean attemptRevert, + @Nonnull final Set<InstanceIdentifier<?>> writersOrder) throws BulkUpdateException { + if (updates.isEmpty()) { + return; + } + + LOG.debug("Performing bulk update with revert attempt: {} for: {}", attemptRevert, updates.keySet()); + + // Check that all updates can be handled + checkAllTypesCanBeHandled(updates); + + // Capture all changes successfully processed in case revert is needed + final Set<InstanceIdentifier<?>> processedNodes = new HashSet<>(); + + // Iterate over all writers and call update if there are any related updates + for (InstanceIdentifier<?> writerType : writersOrder) { + Collection<? extends DataObjectUpdate> writersData = updates.get(writerType); + final Writer<?> writer = getWriter(writerType); + + if (writersData.isEmpty()) { + // If there are no data for current writer, but it is a SubtreeWriter and there are updates to + // its children, still invoke it with its root data + if (writer instanceof SubtreeWriter<?> && isAffected(((SubtreeWriter<?>) writer), updates)) { + // Provide parent data for SubtreeWriter for further processing + writersData = getParentDataObjectUpdate(ctx, updates, writer); + } else { + // Skipping unaffected writer + // Alternative to this would be modification sort according to the order of writers + continue; + } + } + + LOG.debug("Performing update for: {}", writerType); + LOG.trace("Performing update with writer: {}", writer); + + for (DataObjectUpdate singleUpdate : writersData) { + try { + writer.update(singleUpdate.getId(), singleUpdate.getDataBefore(), singleUpdate.getDataAfter(), ctx); + processedNodes.add(singleUpdate.getId()); + LOG.trace("Update successful for type: {}", writerType); + LOG.debug("Update successful for: {}", singleUpdate); + } catch (Exception e) { + LOG.error("Error while processing data change of: {} (updates={})", writerType, writersData, e); + + final Reverter reverter = attemptRevert + ? new ReverterImpl(processedNodes, updates, writersOrder, ctx) + : () -> {}; // NOOP reverter + + // Find out which changes left unprocessed + final Set<InstanceIdentifier<?>> unprocessedChanges = updates.values().stream() + .map(DataObjectUpdate::getId) + .filter(id -> !processedNodes.contains(id)) + .collect(Collectors.toSet()); + throw new BulkUpdateException(unprocessedChanges, reverter, e); + } + } + } + } + + private void checkAllTypesCanBeHandled( + @Nonnull final Multimap<InstanceIdentifier<?>, ? extends DataObjectUpdate> updates) { + if (!handledTypes.containsAll(updates.keySet())) { + final Sets.SetView<InstanceIdentifier<?>> missingWriters = Sets.difference(updates.keySet(), handledTypes); + LOG.warn("Unable to process update. Missing writers for: {}", missingWriters); + throw new IllegalArgumentException("Unable to process update. Missing writers for: " + missingWriters); + } + } + + /** + * Check whether {@link SubtreeWriter} is affected by the updates. + * + * @return true if there are any updates to SubtreeWriter's child nodes (those marked by SubtreeWriter + * as being taken care of) + * */ + private static boolean isAffected(final SubtreeWriter<?> writer, + final Multimap<InstanceIdentifier<?>, ? extends DataObjectUpdate> updates) { + return !Sets.intersection(writer.getHandledChildTypes(), updates.keySet()).isEmpty(); + } + + private Writer<?> getWriter(@Nonnull final InstanceIdentifier<?> singleType) { + final Writer<?> writer = writers.get(singleType); + checkNotNull(writer, + "Unable to write %s. Missing writer. Current writers for: %s", singleType, writers.keySet()); + return writer; + } + + @Nonnull + @Override + public InstanceIdentifier<DataObject> getManagedDataObjectType() { + throw new UnsupportedOperationException("Registry has no managed type"); + } + + // FIXME unit test + private final class ReverterImpl implements Reverter { + + private final Collection<InstanceIdentifier<?>> processedNodes; + private final Multimap<InstanceIdentifier<?>, ? extends DataObjectUpdate> updates; + private final Set<InstanceIdentifier<?>> revertDeleteOrder; + private final WriteContext ctx; + + ReverterImpl(final Collection<InstanceIdentifier<?>> processedNodes, + final Multimap<InstanceIdentifier<?>, ? extends DataObjectUpdate> updates, + final Set<InstanceIdentifier<?>> writersOrderOriginal, + final WriteContext ctx) { + this.processedNodes = processedNodes; + this.updates = updates; + // Use opposite ordering when executing revert + this.revertDeleteOrder = writersOrderOriginal == FlatWriterRegistry.this.writersOrder + ? FlatWriterRegistry.this.writersOrderReversed + : FlatWriterRegistry.this.writersOrder; + this.ctx = ctx; + } + + @Override + public void revert() throws RevertFailedException { + Multimap<InstanceIdentifier<?>, DataObjectUpdate> updatesToRevert = + filterAndRevertProcessed(updates, processedNodes); + + LOG.info("Attempting revert for changes: {}", updatesToRevert); + try { + // Perform reversed bulk update without revert attempt + bulkUpdate(updatesToRevert, ctx, true, revertDeleteOrder); + LOG.info("Revert successful"); + } catch (BulkUpdateException e) { + LOG.error("Revert failed", e); + throw new RevertFailedException(e.getFailedIds(), e); + } + } + + /** + * Create new updates map, but only keep already processed changes. Switching before and after data for each + * update. + */ + private Multimap<InstanceIdentifier<?>, DataObjectUpdate> filterAndRevertProcessed( + final Multimap<InstanceIdentifier<?>, ? extends DataObjectUpdate> updates, + final Collection<InstanceIdentifier<?>> processedNodes) { + final Multimap<InstanceIdentifier<?>, DataObjectUpdate> filtered = HashMultimap.create(); + for (InstanceIdentifier<?> processedNode : processedNodes) { + final InstanceIdentifier<?> wildcardedIid = RWUtils.makeIidWildcarded(processedNode); + if (updates.containsKey(wildcardedIid)) { + updates.get(wildcardedIid).stream() + .filter(dataObjectUpdate -> processedNode.contains(dataObjectUpdate.getId())) + .forEach(dataObjectUpdate -> filtered.put(processedNode, dataObjectUpdate.reverse())); + } + } + return filtered; + } + } + +} diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryBuilder.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryBuilder.java new file mode 100644 index 000000000..f5d218f55 --- /dev/null +++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryBuilder.java @@ -0,0 +1,225 @@ +/* + * 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.translate.util.write.registry; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; +import io.fd.honeycomb.v3po.translate.util.RWUtils; +import io.fd.honeycomb.v3po.translate.write.ModifiableWriterRegistry; +import io.fd.honeycomb.v3po.translate.write.Writer; +import io.fd.honeycomb.v3po.translate.write.WriterRegistryBuilder; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import javax.annotation.concurrent.NotThreadSafe; +import org.jgrapht.experimental.dag.DirectedAcyclicGraph; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Builder for {@link FlatWriterRegistry} allowing users to specify inter-writer relationships. + */ +@NotThreadSafe +public final class FlatWriterRegistryBuilder implements ModifiableWriterRegistry, WriterRegistryBuilder, AutoCloseable { + + private static final Logger LOG = LoggerFactory.getLogger(FlatWriterRegistryBuilder.class); + + // Using directed acyclic graph to represent the ordering relationships between writers + private final DirectedAcyclicGraph<InstanceIdentifier<?>, WriterRelation> + writersRelations = new DirectedAcyclicGraph<>((sourceVertex, targetVertex) -> new WriterRelation()); + private final Map<InstanceIdentifier<?>, Writer<?>> writersMap = new HashMap<>(); + + /** + * AddWriter without any special relationship to any other type. + */ + @Override + public FlatWriterRegistryBuilder addWriter(@Nonnull final Writer<? extends DataObject> writer) { + // Make IID wildcarded just in case + // + the way InstanceIdentifier.create + equals work for Identifiable items is unexpected, meaning updates would + // not be matched to writers in registry + final InstanceIdentifier<?> targetType = RWUtils.makeIidWildcarded(writer.getManagedDataObjectType()); + checkWriterNotPresentYet(targetType); + writersRelations.addVertex(targetType); + writersMap.put(targetType, writer); + return this; + } + + /** + * AddWriter without any special relationship to any other type. + */ + @Override + public FlatWriterRegistryBuilder addSubtreeWriter(@Nonnull final Set<InstanceIdentifier<?>> handledChildren, + @Nonnull final Writer<? extends DataObject> writer) { + addWriter(SubtreeWriter.createForWriter(handledChildren, writer)); + return this; + } + + private void checkWriterNotPresentYet(final InstanceIdentifier<?> targetType) { + Preconditions.checkArgument(!writersMap.containsKey(targetType), + "Writer for type: %s already present: %s", targetType, writersMap.get(targetType)); + } + + /** + * Add writer with relationship: to be executed before writer handling relatedType. + */ + @Override + public FlatWriterRegistryBuilder addWriterBefore(@Nonnull final Writer<? extends DataObject> writer, + @Nonnull final InstanceIdentifier<?> relatedType) { + final InstanceIdentifier<?> targetType = RWUtils.makeIidWildcarded(writer.getManagedDataObjectType()); + final InstanceIdentifier<?> wildcardedRelatedType = RWUtils.makeIidWildcarded(relatedType); + checkWriterNotPresentYet(targetType); + writersRelations.addVertex(targetType); + writersRelations.addVertex(wildcardedRelatedType); + addEdge(targetType, wildcardedRelatedType); + writersMap.put(targetType, writer); + return this; + } + + @Override + public FlatWriterRegistryBuilder addSubtreeWriterBefore(@Nonnull final Set<InstanceIdentifier<?>> handledChildren, + @Nonnull final Writer<? extends DataObject> writer, + @Nonnull final InstanceIdentifier<?> relatedType) { + return addWriterBefore(SubtreeWriter.createForWriter(handledChildren, writer), relatedType); + } + + @Override + public FlatWriterRegistryBuilder addWriterBefore(@Nonnull final Writer<? extends DataObject> writer, + @Nonnull final Collection<InstanceIdentifier<?>> relatedTypes) { + final InstanceIdentifier<?> targetType = RWUtils.makeIidWildcarded(writer.getManagedDataObjectType()); + checkWriterNotPresentYet(targetType); + writersRelations.addVertex(targetType); + relatedTypes.stream() + .map(RWUtils::makeIidWildcarded) + .forEach(writersRelations::addVertex); + relatedTypes.stream() + .map(RWUtils::makeIidWildcarded) + .forEach(type -> addEdge(targetType, type)); + writersMap.put(targetType, writer); + return this; + } + + @Override + public FlatWriterRegistryBuilder addSubtreeWriterBefore(@Nonnull final Set<InstanceIdentifier<?>> handledChildren, + @Nonnull final Writer<? extends DataObject> writer, + @Nonnull final Collection<InstanceIdentifier<?>> relatedTypes) { + return addWriterBefore(SubtreeWriter.createForWriter(handledChildren, writer), relatedTypes); + } + + /** + * Add writer with relationship: to be executed after writer handling relatedType. + */ + @Override + public FlatWriterRegistryBuilder addWriterAfter(@Nonnull final Writer<? extends DataObject> writer, + @Nonnull final InstanceIdentifier<?> relatedType) { + final InstanceIdentifier<?> targetType = RWUtils.makeIidWildcarded(writer.getManagedDataObjectType()); + final InstanceIdentifier<?> wildcardedRelatedType = RWUtils.makeIidWildcarded(relatedType); + checkWriterNotPresentYet(targetType); + writersRelations.addVertex(targetType); + writersRelations.addVertex(wildcardedRelatedType); + // set edge to indicate before relationship, just reversed + addEdge(wildcardedRelatedType, targetType); + writersMap.put(targetType, writer); + return this; + } + + @Override + public FlatWriterRegistryBuilder addSubtreeWriterAfter(@Nonnull final Set<InstanceIdentifier<?>> handledChildren, + @Nonnull final Writer<? extends DataObject> writer, + @Nonnull final InstanceIdentifier<?> relatedType) { + return addWriterAfter(SubtreeWriter.createForWriter(handledChildren, writer), relatedType); + } + + @Override + public FlatWriterRegistryBuilder addWriterAfter(@Nonnull final Writer<? extends DataObject> writer, + @Nonnull final Collection<InstanceIdentifier<?>> relatedTypes) { + final InstanceIdentifier<?> targetType = RWUtils.makeIidWildcarded(writer.getManagedDataObjectType()); + checkWriterNotPresentYet(targetType); + writersRelations.addVertex(targetType); + relatedTypes.stream() + .map(RWUtils::makeIidWildcarded) + .forEach(writersRelations::addVertex); + // set edge to indicate before relationship, just reversed + relatedTypes.stream() + .map(RWUtils::makeIidWildcarded) + .forEach(type -> addEdge(type, targetType)); + writersMap.put(targetType, writer); + return this; + } + + @Override + public FlatWriterRegistryBuilder addSubtreeWriterAfter(@Nonnull final Set<InstanceIdentifier<?>> handledChildren, + @Nonnull final Writer<? extends DataObject> writer, + @Nonnull final Collection<InstanceIdentifier<?>> relatedTypes) { + return addWriterAfter(SubtreeWriter.createForWriter(handledChildren, writer), relatedTypes); + } + + + private void addEdge(final InstanceIdentifier<?> targetType, + final InstanceIdentifier<?> relatedType) { + try { + writersRelations.addDagEdge(targetType, relatedType); + } catch (DirectedAcyclicGraph.CycleFoundException e) { + throw new IllegalArgumentException(String.format( + "Unable to add writer with relation: %s -> %s. Loop detected", targetType, relatedType), e); + } + } + + /** + * Create FlatWriterRegistry with writers ordered according to submitted relationships. + */ + @Override + public FlatWriterRegistry build() { + final ImmutableMap<InstanceIdentifier<?>, Writer<?>> mappedWriters = getMappedWriters(); + LOG.debug("Building writer registry with writers: {}", + mappedWriters.keySet().stream() + .map(InstanceIdentifier::getTargetType) + .map(Class::getSimpleName) + .collect(Collectors.joining(", "))); + LOG.trace("Building writer registry with writers: {}", mappedWriters); + return new FlatWriterRegistry(mappedWriters); + } + + @VisibleForTesting + ImmutableMap<InstanceIdentifier<?>, Writer<?>> getMappedWriters() { + final ImmutableMap.Builder<InstanceIdentifier<?>, Writer<?>> builder = ImmutableMap.builder(); + // Iterate writer types according to their relationships from graph + writersRelations.iterator() + .forEachRemaining(writerType -> { + // There might be types stored just for relationship sake, no real writer, ignoring those + if (writersMap.containsKey(writerType)) { + builder.put(writerType, writersMap.get(writerType)); + } + }); + return builder.build(); + } + + @Override + public void close() throws Exception { + writersMap.clear(); + writersRelations.removeAllEdges(writersRelations.edgeSet()); + writersRelations.removeAllVertices(writersRelations.vertexSet()); + } + + // Represents edges in graph + private static final class WriterRelation {} +} diff --git a/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/SubtreeWriter.java b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/SubtreeWriter.java new file mode 100644 index 000000000..e395b29da --- /dev/null +++ b/v3po/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/SubtreeWriter.java @@ -0,0 +1,85 @@ +/* + * 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.translate.util.write.registry; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.collect.Iterables; +import io.fd.honeycomb.v3po.translate.write.WriteContext; +import io.fd.honeycomb.v3po.translate.write.WriteFailedException; +import io.fd.honeycomb.v3po.translate.write.Writer; +import java.util.HashSet; +import java.util.Set; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Simple writer delegate for subtree writers (writers handling also children nodes) providing a list of all the + * children nodes being handled. + */ +final class SubtreeWriter<D extends DataObject> implements Writer<D> { + + private final Writer<D> delegate; + private final Set<InstanceIdentifier<?>> handledChildTypes = new HashSet<>(); + + private SubtreeWriter(final Writer<D> delegate, Set<InstanceIdentifier<?>> handledTypes) { + this.delegate = delegate; + for (InstanceIdentifier<?> handledType : handledTypes) { + // Iid has to start with writer's handled root type + checkArgument(delegate.getManagedDataObjectType().getTargetType().equals( + handledType.getPathArguments().iterator().next().getType()), + "Handled node from subtree has to be identified by an instance identifier starting from: %s." + + "Instance identifier was: %s", getManagedDataObjectType().getTargetType(), handledType); + checkArgument(Iterables.size(handledType.getPathArguments()) > 1, + "Handled node from subtree identifier too short: %s", handledType); + handledChildTypes.add(InstanceIdentifier.create(Iterables.concat( + getManagedDataObjectType().getPathArguments(), Iterables.skip(handledType.getPathArguments(), 1)))); + } + } + + /** + * Return set of types also handled by this writer. All of the types are children of the type managed by this + * writer excluding the type of this writer. + */ + Set<InstanceIdentifier<?>> getHandledChildTypes() { + return handledChildTypes; + } + + @Override + public void update( + @Nonnull final InstanceIdentifier<? extends DataObject> id, + @Nullable final DataObject dataBefore, + @Nullable final DataObject dataAfter, @Nonnull final WriteContext ctx) throws WriteFailedException { + delegate.update(id, dataBefore, dataAfter, ctx); + } + + @Override + @Nonnull + public InstanceIdentifier<D> getManagedDataObjectType() { + return delegate.getManagedDataObjectType(); + } + + /** + * Wrap a writer as a subtree writer. + */ + static Writer<?> createForWriter(@Nonnull final Set<InstanceIdentifier<?>> handledChildren, + @Nonnull final Writer<? extends DataObject> writer) { + return new SubtreeWriter<>(writer, handledChildren); + } +} diff --git a/v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingReaderRegistryModule.java b/v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingReaderRegistryModule.java index 0eb5062d5..fccd6b1c2 100644 --- a/v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingReaderRegistryModule.java +++ b/v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingReaderRegistryModule.java @@ -42,6 +42,8 @@ public class DelegatingReaderRegistryModule extends org.opendaylight.yang.gen.v1 return new CloseableReaderRegistry(new DelegatingReaderRegistry(rootReadersDependency)); } + + // TODO move to translate-utils private static final class CloseableReaderRegistry implements ReaderRegistry, AutoCloseable { private final DelegatingReaderRegistry delegatingReaderRegistry; diff --git a/v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingReaderRegistryModuleFactory.java b/v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingReaderRegistryModuleFactory.java index 214ab0f90..24d6c50b8 100644 --- a/v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingReaderRegistryModuleFactory.java +++ b/v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingReaderRegistryModuleFactory.java @@ -8,6 +8,13 @@ * Do not modify this file unless it is present under src/main directory */ package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406; + +import org.opendaylight.controller.config.api.DynamicMBeanWithInstance; + public class DelegatingReaderRegistryModuleFactory extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406.AbstractDelegatingReaderRegistryModuleFactory { + @Override + public DelegatingReaderRegistryModule handleChangedClass(final DynamicMBeanWithInstance old) throws Exception { + return super.handleChangedClass(old); + } } diff --git a/v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingWriterRegistryModule.java b/v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingWriterRegistryModule.java index 0266ca900..7eadde80e 100644 --- a/v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingWriterRegistryModule.java +++ b/v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingWriterRegistryModule.java @@ -1,12 +1,6 @@ package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406; -import com.google.common.base.Function; -import com.google.common.collect.Lists; -import io.fd.honeycomb.v3po.translate.util.write.CloseableWriterRegistry; -import io.fd.honeycomb.v3po.translate.util.write.DelegatingWriterRegistry; -import io.fd.honeycomb.v3po.translate.write.Writer; -import java.util.List; -import org.opendaylight.yangtools.yang.binding.DataObject; +import io.fd.honeycomb.v3po.translate.util.write.registry.FlatWriterRegistryBuilder; public class DelegatingWriterRegistryModule extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406.AbstractDelegatingWriterRegistryModule { public DelegatingWriterRegistryModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { @@ -24,16 +18,9 @@ public class DelegatingWriterRegistryModule extends org.opendaylight.yang.gen.v1 @Override public java.lang.AutoCloseable createInstance() { - final List<Writer<? extends DataObject>> rootReadersDependency = Lists.transform(getRootWritersDependency(), - new Function<Writer, Writer<? extends DataObject>>() { - - @SuppressWarnings("unchecked") - @Override - public Writer<? extends DataObject> apply(final Writer input) { - return input; - } - }); - return new CloseableWriterRegistry(new DelegatingWriterRegistry(rootReadersDependency)); + final FlatWriterRegistryBuilder flatWriterRegistryBuilder = new FlatWriterRegistryBuilder(); + getWriterFactoryDependency().forEach(writerFactory -> writerFactory.init(flatWriterRegistryBuilder)); + return flatWriterRegistryBuilder; } } diff --git a/v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/NoopWriterRegistryModule.java b/v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/NoopWriterRegistryModule.java index 16c8af359..fedd069f1 100644 --- a/v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/NoopWriterRegistryModule.java +++ b/v3po/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/NoopWriterRegistryModule.java @@ -1,7 +1,8 @@ package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406; -import io.fd.honeycomb.v3po.translate.util.write.*; import io.fd.honeycomb.v3po.translate.util.write.NoopWriterRegistry; +import io.fd.honeycomb.v3po.translate.write.WriterRegistry; +import io.fd.honeycomb.v3po.translate.write.WriterRegistryBuilder; public class NoopWriterRegistryModule extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406.AbstractNoopWriterRegistryModule { public NoopWriterRegistryModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { @@ -19,7 +20,19 @@ public class NoopWriterRegistryModule extends org.opendaylight.yang.gen.v1.urn.h @Override public java.lang.AutoCloseable createInstance() { - return new CloseableWriterRegistry(new NoopWriterRegistry()); + return new NoopWriterRegistryBuilder(); } + private static final class NoopWriterRegistryBuilder implements AutoCloseable, WriterRegistryBuilder { + + @Override + public WriterRegistry build() { + return new NoopWriterRegistry(); + } + + @Override + public void close() throws Exception { + // Noop + } + } } |