Merge "Refactor Subnet.isSubnetOf - reduce number of 'if' statements. Added unitests."
[controller.git] / opendaylight / md-sal / sal-dom-broker / src / main / java / org / opendaylight / controller / sal / dom / broker / impl / SchemaAwareDataStoreAdapter.java
1 package org.opendaylight.controller.sal.dom.broker.impl;
2
3 import java.awt.PageAttributes.OriginType;
4 import java.util.ArrayList;
5 import java.util.Collections;
6 import java.util.Comparator;
7 import java.util.HashSet;
8 import java.util.Iterator;
9 import java.util.List;
10 import java.util.Map;
11 import java.util.Map.Entry;
12 import java.util.Set;
13 import java.util.concurrent.Future;
14 import java.util.concurrent.atomic.AtomicLong;
15
16 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
17 import org.opendaylight.controller.md.sal.common.api.data.DataModification;
18 import org.opendaylight.controller.md.sal.common.api.data.DataReader;
19 import org.opendaylight.controller.md.sal.common.impl.AbstractDataModification;
20 import org.opendaylight.controller.md.sal.common.impl.util.AbstractLockableDelegator;
21 import org.opendaylight.controller.sal.core.api.data.DataStore;
22 import org.opendaylight.controller.sal.core.api.model.SchemaServiceListener;
23 import org.opendaylight.yangtools.yang.common.QName;
24 import org.opendaylight.yangtools.yang.common.RpcResult;
25 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
26 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
27 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
28 import org.opendaylight.yangtools.yang.data.api.Node;
29 import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
30 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
31 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
32 import org.opendaylight.yangtools.yang.model.api.Module;
33 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
34 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
35 import org.opendaylight.yangtools.yang.util.YangDataOperations;
36 import org.opendaylight.yangtools.yang.util.YangSchemaUtils;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 import com.google.common.base.Predicate;
41 import com.google.common.collect.FluentIterable;
42
43 import static com.google.common.base.Preconditions.*;
44 import org.opendaylight.yangtools.yang.util.YangDataOperations;
45
46 public class SchemaAwareDataStoreAdapter extends AbstractLockableDelegator<DataStore> implements //
47         DataStore, //
48         SchemaServiceListener, //
49         AutoCloseable {
50
51     private final static Logger LOG = LoggerFactory.getLogger(SchemaAwareDataStoreAdapter.class);
52
53     private SchemaContext schema = null;
54     private boolean validationEnabled = false;
55     private DataReader<InstanceIdentifier, CompositeNode> reader = new MergeFirstLevelReader();
56
57     @Override
58     public boolean containsConfigurationPath(InstanceIdentifier path) {
59         try {
60             getDelegateReadLock().lock();
61             return getDelegate().containsConfigurationPath(path);
62
63         } finally {
64             getDelegateReadLock().unlock();
65         }
66     }
67
68     @Override
69     public boolean containsOperationalPath(InstanceIdentifier path) {
70         try {
71             getDelegateReadLock().lock();
72             return getDelegate().containsOperationalPath(path);
73
74         } finally {
75             getDelegateReadLock().unlock();
76         }
77     }
78
79     @Override
80     public Iterable<InstanceIdentifier> getStoredConfigurationPaths() {
81         try {
82             getDelegateReadLock().lock();
83             return getDelegate().getStoredConfigurationPaths();
84
85         } finally {
86             getDelegateReadLock().unlock();
87         }
88     }
89
90     @Override
91     public Iterable<InstanceIdentifier> getStoredOperationalPaths() {
92         try {
93             getDelegateReadLock().lock();
94             return getDelegate().getStoredOperationalPaths();
95
96         } finally {
97             getDelegateReadLock().unlock();
98         }
99     }
100
101     @Override
102     public CompositeNode readConfigurationData(InstanceIdentifier path) {
103         return reader.readConfigurationData(path);
104     }
105
106     @Override
107     public CompositeNode readOperationalData(InstanceIdentifier path) {
108         return reader.readOperationalData(path);
109     }
110
111     @Override
112     public org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction<InstanceIdentifier, CompositeNode> requestCommit(
113             DataModification<InstanceIdentifier, CompositeNode> modification) {
114         validateAgainstSchema(modification);
115         NormalizedDataModification cleanedUp = prepareMergedTransaction(modification);
116         cleanedUp.status = TransactionStatus.SUBMITED;
117         return retrieveDelegate().requestCommit(cleanedUp);
118     }
119
120     public boolean isValidationEnabled() {
121         return validationEnabled;
122     }
123
124     public void setValidationEnabled(boolean validationEnabled) {
125         this.validationEnabled = validationEnabled;
126     }
127
128     private void validateAgainstSchema(DataModification<InstanceIdentifier, CompositeNode> modification) {
129         if (!validationEnabled) {
130             return;
131         }
132
133         if (schema == null) {
134             LOG.info("Validation not performed for {}. Reason: YANG Schema not present.", modification.getIdentifier());
135             return;
136         }
137     }
138
139     @Override
140     protected void onDelegateChanged(DataStore oldDelegate, DataStore newDelegate) {
141         // NOOP
142     }
143
144     @Override
145     public void onGlobalContextUpdated(SchemaContext context) {
146         this.schema = context;
147     }
148
149     @Override
150     public void close() throws Exception {
151         this.schema = null;
152     }
153
154     protected CompositeNode mergeData(InstanceIdentifier path, CompositeNode stored, CompositeNode modified,
155             boolean config) {
156         long startTime = System.nanoTime();
157         try {
158             DataSchemaNode node = schemaNodeFor(path);
159             return YangDataOperations.merge(node, stored, modified, config);
160         } finally {
161             // System.out.println("Merge time: " + ((System.nanoTime() -
162             // startTime) / 1000.0d));
163         }
164     }
165
166     private DataSchemaNode schemaNodeFor(InstanceIdentifier path) {
167         checkState(schema != null, "YANG Schema is not available");
168         return YangSchemaUtils.getSchemaNode(schema, path);
169     }
170
171     private NormalizedDataModification prepareMergedTransaction(
172             DataModification<InstanceIdentifier, CompositeNode> original) {
173         // NOOP for now
174         NormalizedDataModification normalized = new NormalizedDataModification(original);
175         for (Entry<InstanceIdentifier, CompositeNode> entry : original.getUpdatedConfigurationData().entrySet()) {
176             normalized.putConfigurationData(entry.getKey(), entry.getValue());
177         }
178         for (Entry<InstanceIdentifier, CompositeNode> entry : original.getUpdatedOperationalData().entrySet()) {
179             normalized.putOperationalData(entry.getKey(), entry.getValue());
180         }
181         for (InstanceIdentifier entry : original.getRemovedConfigurationData()) {
182             normalized.removeConfigurationData(entry);
183         }
184         for (InstanceIdentifier entry : original.getRemovedOperationalData()) {
185             normalized.removeOperationalData(entry);
186         }
187         return normalized;
188     }
189
190     private final Comparator<Entry<InstanceIdentifier, CompositeNode>> preparationComparator = new Comparator<Entry<InstanceIdentifier, CompositeNode>>() {
191         @Override
192         public int compare(Entry<InstanceIdentifier, CompositeNode> o1, Entry<InstanceIdentifier, CompositeNode> o2) {
193             InstanceIdentifier o1Key = o1.getKey();
194             InstanceIdentifier o2Key = o2.getKey();
195             return Integer.compare(o1Key.getPath().size(), o2Key.getPath().size());
196         }
197     };
198
199     private class MergeFirstLevelReader implements DataReader<InstanceIdentifier, CompositeNode> {
200
201         @Override
202         public CompositeNode readConfigurationData(final InstanceIdentifier path) {
203             getDelegateReadLock().lock();
204             try {
205                 if (path.getPath().isEmpty()) {
206                     return null;
207                 }
208                 QName qname = null;
209                 CompositeNode original = getDelegate().readConfigurationData(path);
210                 ArrayList<Node<?>> childNodes = new ArrayList<Node<?>>();
211                 if (original != null) {
212                     childNodes.addAll(original.getChildren());
213                     qname = original.getNodeType();
214                 } else {
215                     qname = path.getPath().get(path.getPath().size() - 1).getNodeType();
216                 }
217
218                 FluentIterable<InstanceIdentifier> directChildren = FluentIterable.from(getStoredConfigurationPaths())
219                         .filter(new Predicate<InstanceIdentifier>() {
220                             @Override
221                             public boolean apply(InstanceIdentifier input) {
222                                 if (path.contains(input)) {
223                                     int nesting = input.getPath().size() - path.getPath().size();
224                                     if (nesting == 1) {
225                                         return true;
226                                     }
227                                 }
228                                 return false;
229                             }
230                         });
231                 for (InstanceIdentifier instanceIdentifier : directChildren) {
232                     childNodes.add(getDelegate().readConfigurationData(instanceIdentifier));
233                 }
234                 if (original == null && childNodes.isEmpty()) {
235                     return null;
236                 }
237
238                 return new CompositeNodeTOImpl(qname, null, childNodes);
239             } finally {
240                 getDelegateReadLock().unlock();
241             }
242         }
243
244         @Override
245         public CompositeNode readOperationalData(final InstanceIdentifier path) {
246             getDelegateReadLock().lock();
247             try {
248                 if (path.getPath().isEmpty()) {
249                     return null;
250                 }
251                 QName qname = null;
252                 CompositeNode original = getDelegate().readOperationalData(path);
253                 ArrayList<Node<?>> childNodes = new ArrayList<Node<?>>();
254                 if (original != null) {
255                     childNodes.addAll(original.getChildren());
256                     qname = original.getNodeType();
257                 } else {
258                     qname = path.getPath().get(path.getPath().size() - 1).getNodeType();
259                 }
260
261                 FluentIterable<InstanceIdentifier> directChildren = FluentIterable.from(getStoredOperationalPaths())
262                         .filter(new Predicate<InstanceIdentifier>() {
263                             @Override
264                             public boolean apply(InstanceIdentifier input) {
265                                 if (path.contains(input)) {
266                                     int nesting = input.getPath().size() - path.getPath().size();
267                                     if (nesting == 1) {
268                                         return true;
269                                     }
270                                 }
271                                 return false;
272                             }
273                         });
274
275                 for (InstanceIdentifier instanceIdentifier : directChildren) {
276                     childNodes.add(getDelegate().readOperationalData(instanceIdentifier));
277                 }
278                 if (original == null && childNodes.isEmpty()) {
279                     return null;
280                 }
281
282                 return new CompositeNodeTOImpl(qname, null, childNodes);
283             } finally {
284                 getDelegateReadLock().unlock();
285             }
286         }
287     }
288
289     private class NormalizedDataModification extends AbstractDataModification<InstanceIdentifier, CompositeNode> {
290
291         private Object identifier;
292         private TransactionStatus status;
293
294         public NormalizedDataModification(DataModification<InstanceIdentifier, CompositeNode> original) {
295             super(getDelegate());
296             identifier = original;
297             status = TransactionStatus.NEW;
298         }
299
300         @Override
301         public Object getIdentifier() {
302             return this.identifier;
303         }
304
305         @Override
306         public TransactionStatus getStatus() {
307             return status;
308         }
309
310         @Override
311         public Future<RpcResult<TransactionStatus>> commit() {
312             throw new UnsupportedOperationException("Commit should not be invoked on this");
313         }
314
315         @Override
316         protected CompositeNode mergeConfigurationData(InstanceIdentifier path, CompositeNode stored,
317                 CompositeNode modified) {
318             return mergeData(path, stored, modified, true);
319         }
320
321         @Override
322         protected CompositeNode mergeOperationalData(InstanceIdentifier path, CompositeNode stored,
323                 CompositeNode modified) {
324             // TODO Auto-generated method stub
325             return mergeData(path, stored, modified, false);
326         }
327     }
328
329 }