Fix followerDistributedDataStore tear down
[controller.git] / opendaylight / md-sal / sal-distributed-datastore / src / main / java / org / opendaylight / controller / cluster / datastore / utils / PruningDataTreeModification.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.controller.cluster.datastore.utils;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.annotations.VisibleForTesting;
13 import com.google.common.collect.ForwardingObject;
14 import java.io.IOException;
15 import java.util.Optional;
16 import org.opendaylight.controller.cluster.datastore.node.utils.transformer.ReusableNormalizedNodePruner;
17 import org.opendaylight.controller.cluster.datastore.util.AbstractDataTreeModificationCursor;
18 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
19 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
20 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
21 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
22 import org.opendaylight.yangtools.yang.data.tree.api.DataTree;
23 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeModification;
24 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeModificationCursor;
25 import org.opendaylight.yangtools.yang.data.tree.api.SchemaValidationFailedException;
26 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 /**
31  * The PruningDataTreeModification first removes all entries from the data which do not belong in the schemaContext
32  * before delegating it to the actual DataTreeModification.
33  */
34 public abstract class PruningDataTreeModification extends ForwardingObject implements DataTreeModification {
35     /**
36      * A PruningDataTreeModification which always performs pruning before attempting an operation. This sacrifices
37      * performance to ensure all data has passed through the pruner -- such that data adaptations are performed.
38      */
39     public static final class Proactive extends PruningDataTreeModification {
40         public Proactive(final DataTreeModification delegate, final DataTree dataTree,
41                 final ReusableNormalizedNodePruner pruner) {
42             super(delegate, dataTree, pruner);
43         }
44
45         @Override
46         public void merge(final YangInstanceIdentifier path, final NormalizedNode data) {
47             pruneAndMergeNode(path, data);
48         }
49
50         @Override
51         public void write(final YangInstanceIdentifier path, final NormalizedNode data) {
52             pruneAndWriteNode(path, data);
53         }
54
55         @Override
56         PruningDataTreeModification createNew(final DataTreeModification delegate, final DataTree dataTree,
57                 final ReusableNormalizedNodePruner pruner) {
58             return new Proactive(delegate, dataTree, pruner);
59         }
60     }
61
62     /**
63      * A PruningDataTreeModification which performs pruning only when an operation results in an
64      * {@link SchemaValidationFailedException}. This offers superior performance in the normal case of not needing
65      * pruning.
66      */
67     public static final class Reactive extends PruningDataTreeModification {
68         public Reactive(final DataTreeModification delegate, final DataTree dataTree,
69                 final ReusableNormalizedNodePruner pruner) {
70             super(delegate, dataTree, pruner);
71         }
72
73         @Override
74         public void merge(final YangInstanceIdentifier path, final NormalizedNode data) {
75             if (path.isEmpty()) {
76                 pruneAndMergeNode(path, data);
77                 return;
78             }
79
80             try {
81                 delegate().merge(path, data);
82             } catch (SchemaValidationFailedException e) {
83                 LOG.warn("Node at path {} was pruned during merge due to validation error: {}", path, e.getMessage());
84                 pruneAndMergeNode(path, data);
85             }
86         }
87
88         @Override
89         public void write(final YangInstanceIdentifier path, final NormalizedNode data) {
90             if (path.isEmpty()) {
91                 pruneAndWriteNode(path, data);
92                 return;
93             }
94
95             try {
96                 delegate().write(path, data);
97             } catch (SchemaValidationFailedException e) {
98                 LOG.warn("Node at path : {} was pruned during write due to validation error: {}", path, e.getMessage());
99                 pruneAndWriteNode(path, data);
100             }
101         }
102
103         @Override
104         PruningDataTreeModification createNew(final DataTreeModification delegate, final DataTree dataTree,
105                 final ReusableNormalizedNodePruner pruner) {
106             return new Reactive(delegate, dataTree, pruner);
107         }
108     }
109
110     private static final Logger LOG = LoggerFactory.getLogger(PruningDataTreeModification.class);
111
112     private final ReusableNormalizedNodePruner pruner;
113     private final DataTree dataTree;
114
115     private DataTreeModification delegate;
116
117     PruningDataTreeModification(final DataTreeModification delegate, final DataTree dataTree,
118             final ReusableNormalizedNodePruner pruner) {
119         this.delegate = requireNonNull(delegate);
120         this.dataTree = requireNonNull(dataTree);
121         this.pruner = requireNonNull(pruner);
122     }
123
124     @Override
125     protected final DataTreeModification delegate() {
126         return delegate;
127     }
128
129     @Override
130     public final EffectiveModelContext getEffectiveModelContext() {
131         return delegate.getEffectiveModelContext();
132     }
133
134     @Override
135     public final void delete(final YangInstanceIdentifier path) {
136         try {
137             delegate.delete(path);
138         } catch (SchemaValidationFailedException e) {
139             LOG.warn("Node at path : {} does not exist ignoring delete", path);
140         }
141     }
142
143     final void pruneAndMergeNode(final YangInstanceIdentifier path, final NormalizedNode data) {
144         final NormalizedNode pruned = pruneNormalizedNode(path, data);
145         if (pruned != null) {
146             delegate.merge(path, pruned);
147         }
148     }
149
150     final void pruneAndWriteNode(final YangInstanceIdentifier path, final NormalizedNode data) {
151         final NormalizedNode pruned = pruneNormalizedNode(path, data);
152         if (pruned != null) {
153             delegate.write(path, pruned);
154         }
155     }
156
157     @Override
158     public final void ready() {
159         try {
160             delegate.ready();
161         } catch (SchemaValidationFailedException e) {
162             DataTreeModification newModification = dataTree.takeSnapshot().newModification();
163             delegate.applyToCursor(new PruningDataTreeModificationCursor(newModification, this));
164
165             delegate = newModification;
166             delegate.ready();
167         }
168     }
169
170     @Override
171     public final void applyToCursor(final DataTreeModificationCursor dataTreeModificationCursor) {
172         delegate.applyToCursor(dataTreeModificationCursor);
173     }
174
175     @Override
176     public final Optional<NormalizedNode> readNode(final YangInstanceIdentifier yangInstanceIdentifier) {
177         return delegate.readNode(yangInstanceIdentifier);
178     }
179
180     @Override
181     public final DataTreeModification newModification() {
182         return createNew(delegate.newModification(), dataTree, pruner.duplicate());
183     }
184
185     @VisibleForTesting
186     final NormalizedNode pruneNormalizedNode(final YangInstanceIdentifier path, final NormalizedNode input) {
187         pruner.initializeForPath(path);
188         try {
189             NormalizedNodeWriter.forStreamWriter(pruner).write(input);
190         } catch (IOException ioe) {
191             LOG.error("Unexpected IOException when pruning normalizedNode", ioe);
192             return null;
193         }
194
195         return pruner.getResult().orElse(null);
196     }
197
198     abstract PruningDataTreeModification createNew(DataTreeModification delegate, DataTree dataTree,
199             ReusableNormalizedNodePruner pruner);
200
201     private static final class PruningDataTreeModificationCursor extends AbstractDataTreeModificationCursor {
202         private final DataTreeModification toModification;
203         private final PruningDataTreeModification pruningModification;
204
205         PruningDataTreeModificationCursor(final DataTreeModification toModification,
206                 final PruningDataTreeModification pruningModification) {
207             this.toModification = toModification;
208             this.pruningModification = pruningModification;
209         }
210
211         @Override
212         public void write(final PathArgument child, final NormalizedNode data) {
213             final YangInstanceIdentifier path = current().node(child);
214             final NormalizedNode prunedNode = pruningModification.pruneNormalizedNode(path, data);
215             if (prunedNode != null) {
216                 toModification.write(path, prunedNode);
217             }
218         }
219
220         @Override
221         public void merge(final PathArgument child, final NormalizedNode data) {
222             final YangInstanceIdentifier path = current().node(child);
223             final NormalizedNode prunedNode = pruningModification.pruneNormalizedNode(path, data);
224             if (prunedNode != null) {
225                 toModification.merge(path, prunedNode);
226             }
227         }
228
229         @Override
230         public void delete(final PathArgument child) {
231             try {
232                 toModification.delete(current().node(child));
233             } catch (SchemaValidationFailedException e) {
234                 // Ignoring since we would've already logged this in the call to the original modification.
235             }
236         }
237     }
238 }