7eba98a07ea615a734fe8e96c556a520ec9552c4
[hc2vpp.git] /
1 /*
2  * Copyright (c) 2016 Cisco and/or its affiliates.
3  *
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:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package io.fd.honeycomb.v3po.translate.util.write;
18
19 import static com.google.common.base.Preconditions.checkArgument;
20 import static com.google.common.base.Preconditions.checkNotNull;
21
22 import com.google.common.base.Function;
23 import com.google.common.collect.Collections2;
24 import com.google.common.collect.Iterables;
25 import com.google.common.collect.Lists;
26 import com.google.common.collect.Sets;
27 import io.fd.honeycomb.v3po.translate.util.RWUtils;
28 import io.fd.honeycomb.v3po.translate.write.WriteContext;
29 import io.fd.honeycomb.v3po.translate.write.WriteFailedException;
30 import io.fd.honeycomb.v3po.translate.write.Writer;
31 import io.fd.honeycomb.v3po.translate.write.WriterRegistry;
32 import java.util.LinkedList;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Set;
36 import javax.annotation.Nonnull;
37 import javax.annotation.Nullable;
38 import org.opendaylight.yangtools.yang.binding.DataObject;
39 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43 /**
44  * Simple writer registry able to perform and aggregated write (ROOT write) on top of all provided writers. Also able to
45  * delegate a specific write to one of the delegate writers.
46  *
47  * This could serve as a utility to hold & hide all available writers in upper layers.
48  */
49 public final class DelegatingWriterRegistry implements WriterRegistry {
50
51     private static final Logger LOG = LoggerFactory.getLogger(DelegatingWriterRegistry.class);
52
53     private static final Function<InstanceIdentifier<?>, Class<? extends DataObject>> ID_TO_CLASS =
54             new Function<InstanceIdentifier<?>, Class<? extends DataObject>>() {
55                 @Override
56                 public Class<? extends DataObject> apply(final InstanceIdentifier<?> input) {
57                     return input.getTargetType();
58                 }
59             };
60
61     private final Map<Class<? extends DataObject>, Writer<? extends DataObject>> rootWriters;
62
63     /**
64      * Create new {@link DelegatingWriterRegistry}
65      *
66      * @param rootWriters List of delegate writers
67      */
68     public DelegatingWriterRegistry(@Nonnull final List<Writer<? extends DataObject>> rootWriters) {
69         this.rootWriters = RWUtils.uniqueLinkedIndex(checkNotNull(rootWriters), RWUtils.MANAGER_CLASS_FUNCTION);
70     }
71
72     /**
73      * @throws UnsupportedOperationException This getter is not supported for writer registry since it does not manage a
74      *                                       specific node type
75      */
76     @Nonnull
77     @Override
78     public InstanceIdentifier<DataObject> getManagedDataObjectType() {
79         throw new UnsupportedOperationException("Root registry has no type");
80     }
81
82     @Override
83     public void update(@Nonnull final InstanceIdentifier<? extends DataObject> id,
84                        @Nullable final DataObject dataBefore,
85                        @Nullable final DataObject dataAfter,
86                        @Nonnull final WriteContext ctx) throws WriteFailedException {
87         final InstanceIdentifier.PathArgument first = checkNotNull(
88                 Iterables.getFirst(id.getPathArguments(), null), "Empty id");
89         final Writer<? extends DataObject> writer = rootWriters.get(first.getType());
90         checkNotNull(writer,
91                 "Unable to write %s. Missing writer. Current writers for: %s", id, rootWriters.keySet());
92         writer.update(id, dataBefore, dataAfter, ctx);
93     }
94
95     @Override
96     public void update(@Nonnull final Map<InstanceIdentifier<?>, DataObject> nodesBefore,
97                        @Nonnull final Map<InstanceIdentifier<?>, DataObject> nodesAfter,
98                        @Nonnull final WriteContext ctx) throws WriteFailedException {
99         checkAllWritersPresent(nodesBefore);
100         checkAllWritersPresent(nodesAfter);
101
102         final List<InstanceIdentifier<?>> processedNodes = Lists.newArrayList();
103
104         for (Map.Entry<Class<? extends DataObject>, Writer<? extends DataObject>> rootWriterEntry : rootWriters
105                 .entrySet()) {
106
107             final InstanceIdentifier<? extends DataObject> id = rootWriterEntry.getValue().getManagedDataObjectType();
108
109             final DataObject dataBefore = nodesBefore.get(id);
110             final DataObject dataAfter = nodesAfter.get(id);
111
112             // No change to current writer
113             if (dataBefore == null && dataAfter == null) {
114                 continue;
115             }
116
117             LOG.debug("ChangesProcessor.applyChanges() processing dataBefore={}, dataAfter={}", dataBefore, dataAfter);
118
119             try {
120                 update(id, dataBefore, dataAfter, ctx);
121                 processedNodes.add(id);
122             } catch (Exception e) {
123                 LOG.error("Error while processing data change of: {} (before={}, after={})",
124                         id, dataBefore, dataAfter, e);
125                 throw new BulkUpdateException(
126                         id, new ReverterImpl(this, processedNodes, nodesBefore, nodesAfter, ctx), e);
127             }
128         }
129     }
130
131     private void checkAllWritersPresent(final @Nonnull Map<InstanceIdentifier<?>, DataObject> nodesBefore) {
132         final Set<Class<? extends DataObject>> nodesBeforeClasses =
133             Sets.newHashSet(Collections2.transform(nodesBefore.keySet(), ID_TO_CLASS));
134         checkArgument(rootWriters.keySet().containsAll(nodesBeforeClasses),
135                 "Unable to handle all changes. Missing dedicated writers for: %s",
136                 Sets.difference(nodesBeforeClasses, rootWriters.keySet()));
137     }
138
139     private static final class ReverterImpl implements Reverter {
140         private final WriterRegistry delegatingWriterRegistry;
141         private final List<InstanceIdentifier<?>> processedNodes;
142         private final Map<InstanceIdentifier<?>, DataObject> nodesBefore;
143         private final Map<InstanceIdentifier<?>, DataObject> nodesAfter;
144         private final WriteContext ctx;
145
146         ReverterImpl(final WriterRegistry delegatingWriterRegistry,
147                             final List<InstanceIdentifier<?>> processedNodes,
148                             final Map<InstanceIdentifier<?>, DataObject> nodesBefore,
149                             final Map<InstanceIdentifier<?>, DataObject> nodesAfter, final WriteContext ctx) {
150             this.delegatingWriterRegistry = delegatingWriterRegistry;
151             this.processedNodes = processedNodes;
152             this.nodesBefore = nodesBefore;
153             this.nodesAfter = nodesAfter;
154             this.ctx = ctx;
155         }
156
157         @Override
158         public void revert() throws RevertFailedException {
159             final LinkedList<InstanceIdentifier<?>> notReverted = new LinkedList<>(processedNodes);
160
161             while (notReverted.size() > 0) {
162                 final InstanceIdentifier<?> node = notReverted.peekLast();
163                 LOG.debug("ChangesProcessor.revertChanges() processing node={}", node);
164
165                 final DataObject dataBefore = nodesBefore.get(node);
166                 final DataObject dataAfter = nodesAfter.get(node);
167
168                 // revert a change by invoking writer with reordered arguments
169                 try {
170                     delegatingWriterRegistry.update(node, dataAfter, dataBefore, ctx);
171                     notReverted.removeLast(); // change was successfully reverted
172                 } catch (Exception e) {
173                     throw new RevertFailedException(notReverted, e);
174                 }
175
176             }
177         }
178     }
179 }