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