2 * Copyright (c) 2016 Cisco and/or its affiliates.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package io.fd.honeycomb.v3po.translate.util.write;
19 import static com.google.common.base.Preconditions.checkArgument;
20 import static com.google.common.base.Preconditions.checkNotNull;
22 import com.google.common.collect.HashMultimap;
23 import com.google.common.collect.Iterables;
24 import com.google.common.collect.Lists;
25 import com.google.common.collect.Multimap;
26 import io.fd.honeycomb.v3po.translate.util.RWUtils;
27 import io.fd.honeycomb.v3po.translate.write.WriteContext;
28 import io.fd.honeycomb.v3po.translate.write.WriteFailedException;
29 import io.fd.honeycomb.v3po.translate.write.Writer;
30 import io.fd.honeycomb.v3po.translate.write.WriterRegistry;
31 import java.util.LinkedList;
32 import java.util.List;
34 import javax.annotation.Nonnull;
35 import javax.annotation.Nullable;
36 import org.opendaylight.yangtools.yang.binding.DataObject;
37 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
42 * Simple writer registry able to perform and aggregated write (ROOT write) on top of all provided writers. Also able to
43 * delegate a specific write to one of the delegate writers.
45 * This could serve as a utility to hold & hide all available writers in upper layers.
47 public final class DelegatingWriterRegistry implements WriterRegistry {
49 private static final Logger LOG = LoggerFactory.getLogger(DelegatingWriterRegistry.class);
51 private final Map<Class<? extends DataObject>, Writer<? extends DataObject>> rootWriters;
54 * Create new {@link DelegatingWriterRegistry}
56 * @param rootWriters List of delegate writers
58 public DelegatingWriterRegistry(@Nonnull final List<Writer<? extends DataObject>> rootWriters) {
59 this.rootWriters = RWUtils.uniqueLinkedIndex(checkNotNull(rootWriters), RWUtils.MANAGER_CLASS_FUNCTION);
63 * @throws UnsupportedOperationException This getter is not supported for writer registry since it does not manage a
68 public InstanceIdentifier<DataObject> getManagedDataObjectType() {
69 throw new UnsupportedOperationException("Root registry has no type");
73 public void update(@Nonnull final InstanceIdentifier<? extends DataObject> id,
74 @Nullable final DataObject dataBefore,
75 @Nullable final DataObject dataAfter,
76 @Nonnull final WriteContext ctx) throws WriteFailedException {
77 final InstanceIdentifier.PathArgument first = checkNotNull(
78 Iterables.getFirst(id.getPathArguments(), null), "Empty id");
79 final Writer<? extends DataObject> writer = rootWriters.get(first.getType());
81 "Unable to write %s. Missing writer. Current writers for: %s", id, rootWriters.keySet());
82 writer.update(id, dataBefore, dataAfter, ctx);
86 public void update(@Nonnull final Map<InstanceIdentifier<?>, DataObject> nodesBefore,
87 @Nonnull final Map<InstanceIdentifier<?>, DataObject> nodesAfter,
88 @Nonnull final WriteContext ctx) throws WriteFailedException {
90 Multimap<InstanceIdentifier<?>, InstanceIdentifier<?>> rootIdToNestedIds = HashMultimap.create();
92 checkAllWritersPresent(nodesBefore, rootIdToNestedIds);
93 checkAllWritersPresent(nodesAfter, rootIdToNestedIds);
94 } catch (IllegalArgumentException e) {
95 LOG.warn("Unable to process update", e);
99 final List<InstanceIdentifier<?>> processedNodes = Lists.newArrayList();
101 for (Map.Entry<Class<? extends DataObject>, Writer<? extends DataObject>> rootWriterEntry : rootWriters
104 final InstanceIdentifier<? extends DataObject> id = rootWriterEntry.getValue().getManagedDataObjectType();
105 // FIXME !! this is not ideal, we are not handling nested updates in expected order
106 // Root writers are invoked in order they were registered, but nested updates are not, since they are
109 for (InstanceIdentifier<?> specificInstanceIdentifier : rootIdToNestedIds.get(id)) {
110 final DataObject dataBefore = nodesBefore.get(specificInstanceIdentifier);
111 final DataObject dataAfter = nodesAfter.get(specificInstanceIdentifier);
113 // No change to current writer
114 if (dataBefore == null && dataAfter == null) {
119 LOG.debug("ChangesProcessor.applyChanges() processing dataBefore={}, dataAfter={}", dataBefore, dataAfter);
120 update(specificInstanceIdentifier, dataBefore, dataAfter, ctx);
121 processedNodes.add(id);
122 } catch (Exception e) {
123 LOG.error("Error while processing data change of: {} (before={}, after={})", id, dataBefore, dataAfter, e);
124 throw new BulkUpdateException(id, new ReverterImpl(this, processedNodes, nodesBefore, nodesAfter, ctx), e);
130 private void checkAllWritersPresent(final @Nonnull Map<InstanceIdentifier<?>, DataObject> nodesBefore,
131 final @Nonnull Multimap<InstanceIdentifier<?>, InstanceIdentifier<?>> rootIdToNestedIds) {
132 for (final InstanceIdentifier<?> changeId : nodesBefore.keySet()) {
133 final InstanceIdentifier.PathArgument first = Iterables.getFirst(changeId.getPathArguments(), null);
134 checkNotNull(first, "Empty identifier detected");
135 final InstanceIdentifier<? extends DataObject> rootId = InstanceIdentifier.create(first.getType());
136 checkArgument(rootWriters.keySet().contains(first.getType()),
137 "Unable to handle change. Missing dedicated writer for: %s", first.getType());
138 rootIdToNestedIds.put(rootId, changeId);
142 private static final class ReverterImpl implements Reverter {
143 private final WriterRegistry delegatingWriterRegistry;
144 private final List<InstanceIdentifier<?>> processedNodes;
145 private final Map<InstanceIdentifier<?>, DataObject> nodesBefore;
146 private final Map<InstanceIdentifier<?>, DataObject> nodesAfter;
147 private final WriteContext ctx;
149 ReverterImpl(final WriterRegistry delegatingWriterRegistry,
150 final List<InstanceIdentifier<?>> processedNodes,
151 final Map<InstanceIdentifier<?>, DataObject> nodesBefore,
152 final Map<InstanceIdentifier<?>, DataObject> nodesAfter, final WriteContext ctx) {
153 this.delegatingWriterRegistry = delegatingWriterRegistry;
154 this.processedNodes = processedNodes;
155 this.nodesBefore = nodesBefore;
156 this.nodesAfter = nodesAfter;
161 public void revert() throws RevertFailedException {
162 final LinkedList<InstanceIdentifier<?>> notReverted = new LinkedList<>(processedNodes);
164 while (notReverted.size() > 0) {
165 final InstanceIdentifier<?> node = notReverted.peekLast();
166 LOG.debug("ChangesProcessor.revertChanges() processing node={}", node);
168 final DataObject dataBefore = nodesBefore.get(node);
169 final DataObject dataAfter = nodesAfter.get(node);
171 // revert a change by invoking writer with reordered arguments
173 delegatingWriterRegistry.update(node, dataAfter, dataBefore, ctx);
174 notReverted.removeLast(); // change was successfully reverted
175 } catch (Exception e) {
176 throw new RevertFailedException(notReverted, e);