4f4fadcc443a755809394998246fccfeb7845aa8
[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, boolean config) {
155         long startTime = System.nanoTime();
156         try {
157         DataSchemaNode node = schemaNodeFor(path);
158         return YangDataOperations.merge(node,stored,modified,config);
159         } finally {
160             //System.out.println("Merge time: " + ((System.nanoTime() - startTime) / 1000.0d));
161         }
162     }
163     
164     
165     private DataSchemaNode schemaNodeFor(InstanceIdentifier path) {
166         checkState(schema != null,"YANG Schema is not available");
167         return YangSchemaUtils.getSchemaNode(schema, path);
168     }
169
170     private NormalizedDataModification prepareMergedTransaction(
171             DataModification<InstanceIdentifier, CompositeNode> original) {
172         // NOOP for now
173         NormalizedDataModification normalized = new NormalizedDataModification(original);
174         for (Entry<InstanceIdentifier,CompositeNode> entry : original.getUpdatedConfigurationData().entrySet()) {
175             normalized.putConfigurationData(entry.getKey(), entry.getValue());
176         }
177         for (Entry<InstanceIdentifier,CompositeNode> entry : original.getUpdatedOperationalData().entrySet()) {
178             normalized.putOperationalData(entry.getKey(), entry.getValue());
179         }
180         for (InstanceIdentifier entry : original.getRemovedConfigurationData()) {
181             normalized.removeConfigurationData(entry);
182         }
183         for(InstanceIdentifier entry : original.getRemovedOperationalData()) {
184             normalized.removeOperationalData(entry);
185         }
186         return normalized;
187     }
188
189     private final Comparator<Entry<InstanceIdentifier, CompositeNode>> preparationComparator = new Comparator<Entry<InstanceIdentifier, CompositeNode>>() {
190         @Override
191         public int compare(Entry<InstanceIdentifier, CompositeNode> o1, Entry<InstanceIdentifier, CompositeNode> o2) {
192             InstanceIdentifier o1Key = o1.getKey();
193             InstanceIdentifier o2Key = o2.getKey();
194             return Integer.compare(o1Key.getPath().size(), o2Key.getPath().size());
195         }
196     };
197
198     private class MergeFirstLevelReader implements DataReader<InstanceIdentifier, CompositeNode> {
199
200         @Override
201         public CompositeNode readConfigurationData(final InstanceIdentifier path) {
202             getDelegateReadLock().lock();
203             try {
204                 if (path.getPath().isEmpty()) {
205                     return null;
206                 }
207                 QName qname = null;
208                 CompositeNode original = getDelegate().readConfigurationData(path);
209                 ArrayList<Node<?>> childNodes = new ArrayList<Node<?>>();
210                 if (original != null) {
211                     childNodes.addAll(original.getChildren());
212                     qname = original.getNodeType();
213                 } else {
214                     qname = path.getPath().get(path.getPath().size() - 1).getNodeType();
215                 }
216
217                 FluentIterable<InstanceIdentifier> directChildren = FluentIterable.from(getStoredConfigurationPaths())
218                         .filter(new Predicate<InstanceIdentifier>() {
219                             @Override
220                             public boolean apply(InstanceIdentifier input) {
221                                 if (path.contains(input)) {
222                                     int nesting = input.getPath().size() - path.getPath().size();
223                                     if (nesting == 1) {
224                                         return true;
225                                     }
226                                 }
227                                 return false;
228                             }
229                         });
230                 for (InstanceIdentifier instanceIdentifier : directChildren) {
231                     childNodes.add(getDelegate().readConfigurationData(instanceIdentifier));
232                 }
233                 if (original == null && childNodes.isEmpty()) {
234                     return null;
235                 }
236
237                 return new CompositeNodeTOImpl(qname, null, childNodes);
238             } finally {
239                 getDelegateReadLock().unlock();
240             }
241         }
242
243         @Override
244         public CompositeNode readOperationalData(final InstanceIdentifier path) {
245             getDelegateReadLock().lock();
246             try {
247                 if (path.getPath().isEmpty()) {
248                     return null;
249                 }
250                 QName qname = null;
251                 CompositeNode original = getDelegate().readOperationalData(path);
252                 ArrayList<Node<?>> childNodes = new ArrayList<Node<?>>();
253                 if (original != null) {
254                     childNodes.addAll(original.getChildren());
255                     qname = original.getNodeType();
256                 } else {
257                     qname = path.getPath().get(path.getPath().size() - 1).getNodeType();
258                 }
259
260                 FluentIterable<InstanceIdentifier> directChildren = FluentIterable.from(getStoredOperationalPaths())
261                         .filter(new Predicate<InstanceIdentifier>() {
262                             @Override
263                             public boolean apply(InstanceIdentifier input) {
264                                 if (path.contains(input)) {
265                                     int nesting = input.getPath().size() - path.getPath().size();
266                                     if (nesting == 1) {
267                                         return true;
268                                     }
269                                 }
270                                 return false;
271                             }
272                         });
273
274                 for (InstanceIdentifier instanceIdentifier : directChildren) {
275                     childNodes.add(getDelegate().readOperationalData(instanceIdentifier));
276                 }
277                 if (original == null && childNodes.isEmpty()) {
278                     return null;
279                 }
280
281                 return new CompositeNodeTOImpl(qname, null, childNodes);
282             } finally {
283                 getDelegateReadLock().unlock();
284             }
285         }
286     }
287     
288     private class NormalizedDataModification extends AbstractDataModification<InstanceIdentifier, CompositeNode> {
289
290         private Object identifier;
291         private TransactionStatus status;
292
293         public NormalizedDataModification(DataModification<InstanceIdentifier, CompositeNode> original) {
294             super(getDelegate());
295             identifier = original;
296             status = TransactionStatus.NEW;
297         }
298         
299         @Override
300         public Object getIdentifier() {
301             return this.identifier;
302         }
303         
304         @Override
305         public TransactionStatus getStatus() {
306             return status;
307         }
308
309         @Override
310         public Future<RpcResult<TransactionStatus>> commit() {
311             throw new UnsupportedOperationException("Commit should not be invoked on this");
312         }
313         
314         @Override
315         protected CompositeNode mergeConfigurationData(InstanceIdentifier path,CompositeNode stored, CompositeNode modified) {
316             return mergeData(path,stored, modified,true);
317         }
318         
319         @Override
320         protected CompositeNode mergeOperationalData(InstanceIdentifier path,CompositeNode stored, CompositeNode modified) {
321             // TODO Auto-generated method stub
322             return mergeData(path,stored,modified,false);
323         }
324         
325     }
326
327 }