bdf5667b67d92568c5b538d294ee417aabbe76df
[controller.git] / opendaylight / md-sal / sal-dom-broker / src / main / java / org / opendaylight / controller / md / sal / dom / store / impl / tree / data / SchemaAwareApplyOperation.java
1 /*
2  * Copyright (c) 2014 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.md.sal.dom.store.impl.tree.data;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11
12 import java.util.List;
13
14 import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataPreconditionFailedException;
15 import org.opendaylight.controller.md.sal.dom.store.impl.tree.ModificationType;
16 import org.opendaylight.controller.md.sal.dom.store.impl.tree.data.DataNodeContainerModificationStrategy.ContainerModificationStrategy;
17 import org.opendaylight.controller.md.sal.dom.store.impl.tree.data.DataNodeContainerModificationStrategy.UnkeyedListItemModificationStrategy;
18 import org.opendaylight.controller.md.sal.dom.store.impl.tree.data.NormalizedNodeContainerModificationStrategy.ChoiceModificationStrategy;
19 import org.opendaylight.controller.md.sal.dom.store.impl.tree.data.NormalizedNodeContainerModificationStrategy.OrderedLeafSetModificationStrategy;
20 import org.opendaylight.controller.md.sal.dom.store.impl.tree.data.NormalizedNodeContainerModificationStrategy.OrderedMapModificationStrategy;
21 import org.opendaylight.controller.md.sal.dom.store.impl.tree.data.NormalizedNodeContainerModificationStrategy.UnorderedLeafSetModificationStrategy;
22 import org.opendaylight.controller.md.sal.dom.store.impl.tree.data.ValueNodeModificationStrategy.LeafModificationStrategy;
23 import org.opendaylight.controller.md.sal.dom.store.impl.tree.spi.TreeNode;
24 import org.opendaylight.controller.md.sal.dom.store.impl.tree.spi.TreeNodeFactory;
25 import org.opendaylight.controller.md.sal.dom.store.impl.tree.spi.Version;
26 import org.opendaylight.yangtools.yang.common.QName;
27 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
28 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.AugmentationIdentifier;
29 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier;
30 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
31 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
32 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
33 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
34 import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
35 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
36 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
37 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
38 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
39 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
40 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 import com.google.common.base.Optional;
45 import com.google.common.base.Preconditions;
46
47 abstract class SchemaAwareApplyOperation implements ModificationApplyOperation {
48     private static final Logger LOG = LoggerFactory.getLogger(SchemaAwareApplyOperation.class);
49
50     public static SchemaAwareApplyOperation from(final DataSchemaNode schemaNode) {
51         if (schemaNode instanceof ContainerSchemaNode) {
52             return new ContainerModificationStrategy((ContainerSchemaNode) schemaNode);
53         } else if (schemaNode instanceof ListSchemaNode) {
54             return fromListSchemaNode((ListSchemaNode) schemaNode);
55         } else if (schemaNode instanceof ChoiceNode) {
56             return new ChoiceModificationStrategy((ChoiceNode) schemaNode);
57         } else if (schemaNode instanceof LeafListSchemaNode) {
58             return fromLeafListSchemaNode((LeafListSchemaNode) schemaNode);
59         } else if (schemaNode instanceof LeafSchemaNode) {
60             return new LeafModificationStrategy((LeafSchemaNode) schemaNode);
61         }
62         throw new IllegalArgumentException("Not supported schema node type for " + schemaNode.getClass());
63     }
64
65     public static SchemaAwareApplyOperation from(final DataNodeContainer resolvedTree,
66             final AugmentationTarget augSchemas, final AugmentationIdentifier identifier) {
67         AugmentationSchema augSchema = null;
68
69         allAugments:
70             for (AugmentationSchema potential : augSchemas.getAvailableAugmentations()) {
71                 for (DataSchemaNode child : potential.getChildNodes()) {
72                     if (identifier.getPossibleChildNames().contains(child.getQName())) {
73                         augSchema = potential;
74                         break allAugments;
75                     }
76                 }
77             }
78
79         if (augSchema != null) {
80             return new DataNodeContainerModificationStrategy.AugmentationModificationStrategy(augSchema, resolvedTree);
81         }
82         return null;
83     }
84
85     public static boolean checkDataPrecondition(final InstanceIdentifier path, final boolean condition, final String message) throws DataPreconditionFailedException {
86         if(!condition) {
87             throw new DataPreconditionFailedException(path, message);
88         }
89         return condition;
90     }
91
92     private static SchemaAwareApplyOperation fromListSchemaNode(final ListSchemaNode schemaNode) {
93         List<QName> keyDefinition = schemaNode.getKeyDefinition();
94         if (keyDefinition == null || keyDefinition.isEmpty()) {
95             return new UnkeyedListModificationStrategy(schemaNode);
96         }
97         if (schemaNode.isUserOrdered()) {
98             return new OrderedMapModificationStrategy(schemaNode);
99         }
100
101         return new NormalizedNodeContainerModificationStrategy.UnorderedMapModificationStrategy(schemaNode);
102     }
103
104     private static SchemaAwareApplyOperation fromLeafListSchemaNode(final LeafListSchemaNode schemaNode) {
105         if(schemaNode.isUserOrdered()) {
106             return new OrderedLeafSetModificationStrategy(schemaNode);
107         } else {
108             return new UnorderedLeafSetModificationStrategy(schemaNode);
109         }
110     }
111
112     private static final void checkNotConflicting(final InstanceIdentifier path, final TreeNode original, final TreeNode current) throws DataPreconditionFailedException {
113         checkDataPrecondition(path, original.getVersion().equals(current.getVersion()),
114                 "Node was replaced by other transaction.");
115         checkDataPrecondition(path, original.getSubtreeVersion().equals(current.getSubtreeVersion()),
116                 "Node children was modified by other transaction");
117     }
118
119     protected final ModificationApplyOperation resolveChildOperation(final PathArgument child) {
120         Optional<ModificationApplyOperation> potential = getChild(child);
121         checkArgument(potential.isPresent(), "Operation for child %s is not defined.", child);
122         return potential.get();
123     }
124
125     @Override
126     public void verifyStructure(final ModifiedNode modification) throws IllegalArgumentException {
127         if (modification.getType() == ModificationType.WRITE) {
128             verifyWrittenStructure(modification.getWrittenValue());
129         }
130     }
131
132     @Override
133     public final void checkApplicable(final InstanceIdentifier path,final NodeModification modification, final Optional<TreeNode> current) throws DataPreconditionFailedException {
134         switch (modification.getType()) {
135         case DELETE:
136             checkDeleteApplicable(modification, current);
137         case SUBTREE_MODIFIED:
138             checkSubtreeModificationApplicable(path, modification, current);
139             return;
140         case WRITE:
141             checkWriteApplicable(path, modification, current);
142             return;
143         case MERGE:
144             checkMergeApplicable(path, modification, current);
145             return;
146         case UNMODIFIED:
147             return;
148         default:
149             throw new UnsupportedOperationException("Suplied modification type "+ modification.getType()+ "is not supported.");
150         }
151
152     }
153
154     protected void checkMergeApplicable(final InstanceIdentifier path, final NodeModification modification, final Optional<TreeNode> current) throws DataPreconditionFailedException {
155         Optional<TreeNode> original = modification.getOriginal();
156         if (original.isPresent() && current.isPresent()) {
157             /*
158              * We need to do conflict detection only and only if the value of leaf changed
159              * before two transactions. If value of leaf is unchanged between two transactions
160              * it should not cause transaction to fail, since result of this merge
161              * leads to same data.
162              */
163             if(!original.get().getData().equals(current.get().getData())) {
164                 checkNotConflicting(path, original.get(), current.get());
165             }
166         }
167     }
168
169     protected void checkWriteApplicable(final InstanceIdentifier path, final NodeModification modification, final Optional<TreeNode> current) throws DataPreconditionFailedException {
170         Optional<TreeNode> original = modification.getOriginal();
171         if (original.isPresent() && current.isPresent()) {
172             checkNotConflicting(path, original.get(), current.get());
173         } else if(original.isPresent()) {
174             throw new DataPreconditionFailedException(path,"Node was deleted by other transaction.");
175         }
176     }
177
178     private void checkDeleteApplicable(final NodeModification modification, final Optional<TreeNode> current) {
179         // Delete is always applicable, we do not expose it to subclasses
180         if (current.isPresent()) {
181             LOG.trace("Delete operation turned to no-op on missing node {}", modification);
182         }
183     }
184
185     @Override
186     public final Optional<TreeNode> apply(final ModifiedNode modification,
187             final Optional<TreeNode> currentMeta, final Version subtreeVersion) {
188
189         switch (modification.getType()) {
190         case DELETE:
191             return modification.storeSnapshot(Optional.<TreeNode> absent());
192         case SUBTREE_MODIFIED:
193             Preconditions.checkArgument(currentMeta.isPresent(), "Metadata not available for modification",
194                     modification);
195             return modification.storeSnapshot(Optional.of(applySubtreeChange(modification, currentMeta.get(),
196                     subtreeVersion)));
197         case MERGE:
198             if(currentMeta.isPresent()) {
199                 return modification.storeSnapshot(Optional.of(applyMerge(modification,currentMeta.get(),subtreeVersion)));
200             } // Fallback to write is intentional - if node is not preexisting merge is same as write
201         case WRITE:
202             return modification.storeSnapshot(Optional.of(applyWrite(modification, currentMeta, subtreeVersion)));
203         case UNMODIFIED:
204             return currentMeta;
205         default:
206             throw new IllegalArgumentException("Provided modification type is not supported.");
207         }
208     }
209
210     protected abstract TreeNode applyMerge(ModifiedNode modification,
211             TreeNode currentMeta, Version subtreeVersion);
212
213     protected abstract TreeNode applyWrite(ModifiedNode modification,
214             Optional<TreeNode> currentMeta, Version subtreeVersion);
215
216     protected abstract TreeNode applySubtreeChange(ModifiedNode modification,
217             TreeNode currentMeta, Version subtreeVersion);
218
219     protected abstract void checkSubtreeModificationApplicable(InstanceIdentifier path, final NodeModification modification,
220             final Optional<TreeNode> current) throws DataPreconditionFailedException;
221
222     protected abstract void verifyWrittenStructure(NormalizedNode<?, ?> writtenValue);
223
224     public static class UnkeyedListModificationStrategy extends SchemaAwareApplyOperation {
225
226         private final Optional<ModificationApplyOperation> entryStrategy;
227
228         protected UnkeyedListModificationStrategy(final ListSchemaNode schema) {
229             entryStrategy = Optional.<ModificationApplyOperation> of(new UnkeyedListItemModificationStrategy(schema));
230         }
231
232         @Override
233         protected TreeNode applyMerge(final ModifiedNode modification, final TreeNode currentMeta,
234                 final Version subtreeVersion) {
235             return applyWrite(modification, Optional.of(currentMeta), subtreeVersion);
236         }
237
238         @Override
239         protected TreeNode applySubtreeChange(final ModifiedNode modification,
240                 final TreeNode currentMeta, final Version subtreeVersion) {
241             throw new UnsupportedOperationException("UnkeyedList does not support subtree change.");
242         }
243
244         @Override
245         protected TreeNode applyWrite(final ModifiedNode modification,
246                 final Optional<TreeNode> currentMeta, final Version subtreeVersion) {
247             return TreeNodeFactory.createTreeNode(modification.getWrittenValue(), subtreeVersion);
248         }
249
250         @Override
251         public Optional<ModificationApplyOperation> getChild(final PathArgument child) {
252             if (child instanceof NodeIdentifier) {
253                 return entryStrategy;
254             }
255             return Optional.absent();
256         }
257
258         @Override
259         protected void verifyWrittenStructure(final NormalizedNode<?, ?> writtenValue) {
260
261         }
262
263         @Override
264         protected void checkSubtreeModificationApplicable(final InstanceIdentifier path, final NodeModification modification,
265                 final Optional<TreeNode> current) throws DataPreconditionFailedException {
266             throw new DataPreconditionFailedException(path, "Subtree modification is not allowed.");
267         }
268     }
269 }