import javassist.ClassPool;
+import org.junit.After;
import org.junit.Before;
import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
import org.opendaylight.controller.sal.binding.impl.DataBrokerImpl;
import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingIndependentMappingService;
import org.opendaylight.controller.sal.binding.dom.serializer.impl.RuntimeGeneratedMappingServiceImpl;
import org.opendaylight.controller.sal.core.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.core.api.data.DataStore;
+import org.opendaylight.controller.sal.dom.broker.impl.DataStoreStatsWrapper;
import org.opendaylight.controller.sal.dom.broker.impl.HashMapDataStore;
+import org.opendaylight.controller.sal.dom.broker.impl.SchemaAwareDataStoreAdapter;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
import org.reflections.Reflections;
import org.reflections.scanners.ResourcesScanner;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import com.google.common.base.Predicate;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
-public abstract class AbstractDataServiceTest {
+public abstract class AbstractDataServiceTest {
+ private static Logger log = LoggerFactory.getLogger(AbstractDataServiceTest.class);
+
protected org.opendaylight.controller.sal.core.api.data.DataProviderService biDataService;
protected DataProviderService baDataService;
-
+
/**
* Workaround for JUNIT sharing classloaders
*
*/
protected static final ClassPool POOL = new ClassPool();
-
+
protected RuntimeGeneratedMappingServiceImpl mappingServiceImpl;
protected BindingIndependentMappingService mappingService;
protected DataBrokerImpl baDataImpl;
protected org.opendaylight.controller.sal.dom.broker.DataBrokerImpl biDataImpl;
protected ListeningExecutorService executor;
protected BindingIndependentDataServiceConnector connectorServiceImpl;
- protected HashMapDataStore dataStore;
-
-
+ protected HashMapDataStore rawDataStore;
+ private SchemaAwareDataStoreAdapter schemaAwareDataStore;
+ private DataStoreStatsWrapper dataStoreStats;
+
+ protected DataStore dataStore;
+
@Before
public void setUp() {
executor = MoreExecutors.sameThreadExecutor();
baDataImpl = new DataBrokerImpl();
baDataService = baDataImpl;
baDataImpl.setExecutor(executor);
-
+
biDataImpl = new org.opendaylight.controller.sal.dom.broker.DataBrokerImpl();
- biDataService = biDataImpl;
+ biDataService = biDataImpl;
biDataImpl.setExecutor(executor);
-
- dataStore = new HashMapDataStore();
- org.opendaylight.yangtools.yang.data.api.InstanceIdentifier treeRoot = org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.builder().toInstance();
+
+ rawDataStore = new HashMapDataStore();
+ schemaAwareDataStore = new SchemaAwareDataStoreAdapter();
+ schemaAwareDataStore.changeDelegate(rawDataStore);
+ dataStoreStats = new DataStoreStatsWrapper(schemaAwareDataStore);
+ dataStore = dataStoreStats;
+
+ org.opendaylight.yangtools.yang.data.api.InstanceIdentifier treeRoot = org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
+ .builder().toInstance();
biDataImpl.registerConfigurationReader(treeRoot, dataStore);
biDataImpl.registerOperationalReader(treeRoot, dataStore);
biDataImpl.registerCommitHandler(treeRoot, dataStore);
-
+
mappingServiceImpl = new RuntimeGeneratedMappingServiceImpl();
mappingServiceImpl.setPool(POOL);
mappingService = mappingServiceImpl;
File pathname = new File("target/gen-classes-debug");
- //System.out.println("Generated classes are captured in " + pathname.getAbsolutePath());
+ // System.out.println("Generated classes are captured in " +
+ // pathname.getAbsolutePath());
mappingServiceImpl.start(null);
- //mappingServiceImpl.getBinding().setClassFileCapturePath(pathname);
-
+ // mappingServiceImpl.getBinding().setClassFileCapturePath(pathname);
+
connectorServiceImpl = new BindingIndependentDataServiceConnector();
connectorServiceImpl.setBaDataService(baDataService);
connectorServiceImpl.setBiDataService(biDataService);
connectorServiceImpl.setMappingService(mappingServiceImpl);
connectorServiceImpl.start();
-
- String[] yangFiles= getModelFilenames();
- if(yangFiles != null && yangFiles.length > 0) {
- mappingServiceImpl.onGlobalContextUpdated(getContext(yangFiles));
+
+ String[] yangFiles = getModelFilenames();
+ if (yangFiles != null && yangFiles.length > 0) {
+ SchemaContext context = getContext(yangFiles);
+ mappingServiceImpl.onGlobalContextUpdated(context);
+ schemaAwareDataStore.onGlobalContextUpdated(context);
}
}
-
- protected String[] getModelFilenames() {
+ protected String[] getModelFilenames() {
return getAllModelFilenames();
}
-
+
public static String[] getAllModelFilenames() {
Predicate<String> predicate = new Predicate<String>() {
@Override
return input.endsWith(".yang");
}
};
- Reflections reflection= new Reflections("META-INF.yang", new ResourcesScanner());
+ Reflections reflection = new Reflections("META-INF.yang", new ResourcesScanner());
Set<String> result = reflection.getResources(predicate);
return (String[]) result.toArray(new String[result.size()]);
}
-
+
public static SchemaContext getContext(String[] yangFiles) {
ClassLoader loader = AbstractDataServiceTest.class.getClassLoader();
Set<Module> modules = parser.parseYangModelsFromStreams(streams);
return parser.resolveSchemaContext(modules);
}
+
+ @After
+ public void afterTest() {
+
+ log.info("BIDataStore Statistics: Configuration Read Count: {} TotalTime: {} ns AverageTime (ns): {} ns",
+ dataStoreStats.getConfigurationReadCount(), dataStoreStats.getConfigurationReadTotalTime(),
+ dataStoreStats.getConfigurationReadAverageTime());
+
+ log.info("BIDataStore Statistics: Operational Read Count: {} TotalTime: {} ns AverageTime (ns): {} ns",
+ dataStoreStats.getOperationalReadCount(), dataStoreStats.getOperationalReadTotalTime(),
+ dataStoreStats.getOperationalReadAverageTime());
+
+ log.info("BIDataStore Statistics: Request Commit Count: {} TotalTime: {} ns AverageTime (ns): {} ns",
+ dataStoreStats.getRequestCommitCount(), dataStoreStats.getRequestCommitTotalTime(),
+ dataStoreStats.getRequestCommitAverageTime());
+
+ }
}
public interface DataChangeEvent<P,D> extends DataChange<P, D>, Immutable {
+ /**
+ * Returns a orignal subtree of data, which starts at the path
+ * where listener was registered.
+ *
+ */
+ D getOriginalConfigurationSubtree();
+
/**
* Returns a new subtree of data, which starts at the path
* where listener was registered.
*
*/
+ D getOriginalOperationalSubtree();
+
+
+
+ /**
+ * Returns a updated subtree of data, which starts at the path
+ * where listener was registered.
+ *
+ */
D getUpdatedConfigurationSubtree();
/**
- * Returns a new subtree of data, which starts at the path
+ * Returns a udpated subtree of data, which starts at the path
* where listener was registered.
*
*/
import org.opendaylight.controller.md.sal.common.api.RegistrationListener
import org.opendaylight.yangtools.concepts.util.ListenerRegistry
import java.util.concurrent.atomic.AtomicLong
+import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent
abstract class AbstractDataBroker<P extends Path<P>, D, DCL extends DataChangeListener<P, D>> implements DataModificationTransactionFactory<P, D>, //
DataReader<P, D>, //
override final def registerDataChangeListener(P path, DCL listener) {
val reg = new DataChangeListenerRegistration(path, listener, this);
listeners.put(path, reg);
+ val initialConfig = dataReadRouter.readConfigurationData(path);
+ val initialOperational = dataReadRouter.readOperationalData(path);
+ val event = createInitialListenerEvent(path,initialConfig,initialOperational);
+ listener.onDataChanged(event);
return reg;
}
return ret;
}
+ protected def DataChangeEvent<P,D> createInitialListenerEvent(P path,D initialConfig,D initialOperational) {
+ return new InitialDataChangeEventImpl<P, D>(initialConfig,initialOperational);
+
+ }
protected final def removeListener(DataChangeListenerRegistration<P, D, DCL> registration) {
listeners.remove(registration.path, registration);
private final DataChange<P, D> dataChange;
private final D originalConfigurationSubtree;
+
+
private final D originalOperationalSubtree;
private final D updatedOperationalSubtree;
private final D updatedConfigurationSubtree;
this.updatedConfigurationSubtree = updatedConfigurationSubtree;
}
+ @Override
+ public D getOriginalConfigurationSubtree() {
+ return originalConfigurationSubtree;
+ }
+
+ @Override
+ public D getOriginalOperationalSubtree() {
+ return originalOperationalSubtree;
+ }
+
@Override
public D getUpdatedOperationalSubtree() {
return updatedOperationalSubtree;
--- /dev/null
+package org.opendaylight.controller.md.sal.common.impl.service;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent;
+
+public class InitialDataChangeEventImpl<P,D> implements DataChangeEvent<P, D> {
+
+ private final D originalOperationalTree;
+ private final D originalConfigurationTree;
+
+ public InitialDataChangeEventImpl(D configTree, D operTree) {
+ originalConfigurationTree = configTree;
+ originalOperationalTree = operTree;
+ }
+
+ @Override
+ public Map<P, D> getCreatedConfigurationData() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public Map<P, D> getCreatedOperationalData() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public Map<P, D> getOriginalConfigurationData() {
+ return Collections.emptyMap();
+ }
+ @Override
+ public Map<P, D> getOriginalOperationalData() {
+ return Collections.emptyMap();
+ }
+ @Override
+ public Set<P> getRemovedConfigurationData() {
+ return Collections.emptySet();
+ }
+ @Override
+ public Set<P> getRemovedOperationalData() {
+ return Collections.emptySet();
+ }
+ @Override
+ public Map<P, D> getUpdatedConfigurationData() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public D getUpdatedConfigurationSubtree() {
+ return originalConfigurationTree;
+ }
+ @Override
+ public D getUpdatedOperationalSubtree() {
+ return originalOperationalTree;
+ }
+
+ @Override
+ public D getOriginalConfigurationSubtree() {
+ return originalConfigurationTree;
+ }
+
+ @Override
+ public D getOriginalOperationalSubtree() {
+ return originalOperationalTree;
+ }
+
+ @Override
+ public Map<P, D> getUpdatedOperationalData() {
+ return Collections.emptyMap();
+ }
+
+
+}
--- /dev/null
+package org.opendaylight.controller.md.sal.common.impl.util;
+
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
+
+import org.opendaylight.yangtools.concepts.Delegator;
+
+import com.google.common.base.Preconditions;
+
+public class AbstractLockableDelegator<T> implements Delegator<T> {
+
+ private final ReentrantReadWriteLock delegateLock = new ReentrantReadWriteLock();
+ private final ReadLock delegateReadLock = delegateLock.readLock();
+ private final WriteLock delegateWriteLock = delegateLock.writeLock();
+
+
+ protected Lock getDelegateReadLock() {
+ return delegateReadLock;
+ }
+
+ private T delegate;
+
+ public AbstractLockableDelegator() {
+ // NOOP
+ }
+
+ public AbstractLockableDelegator(T initialDelegate) {
+ delegate = initialDelegate;
+ }
+
+ @Override
+ public T getDelegate() {
+ try {
+ delegateReadLock.lock();
+ return delegate;
+ } finally {
+ delegateReadLock.unlock();
+ }
+ }
+
+ public T retrieveDelegate() {
+ try {
+ delegateReadLock.lock();
+ Preconditions.checkState(delegate != null,"Delegate is null");
+ return delegate;
+ } finally {
+ delegateReadLock.unlock();
+ }
+ }
+
+ /**
+ *
+ * @param newDelegate
+ * @return oldDelegate
+ */
+ public final T changeDelegate(T newDelegate) {
+ try {
+ delegateWriteLock.lock();
+ T oldDelegate = delegate;
+ delegate = newDelegate;
+ onDelegateChanged(oldDelegate, newDelegate);
+ return oldDelegate;
+ } finally {
+ delegateWriteLock.unlock();
+ }
+ }
+
+
+ protected void onDelegateChanged(T oldDelegate, T newDelegate) {
+ // NOOP in abstract calss;
+ }
+}
public interface DataStore extends //
DataReader<InstanceIdentifier, CompositeNode>,
DataCommitHandler<InstanceIdentifier, CompositeNode> {
+
+
+ Iterable<InstanceIdentifier> getStoredConfigurationPaths();
+ Iterable<InstanceIdentifier> getStoredOperationalPaths();
+
+ boolean containsConfigurationPath(InstanceIdentifier path);
+ boolean containsOperationalPath(InstanceIdentifier path);
}
import org.opendaylight.controller.sal.dom.broker.BrokerConfigActivator;
import org.opendaylight.controller.sal.dom.broker.BrokerImpl;
import org.osgi.framework.BundleContext;
+import static com.google.common.base.Preconditions.*;
/**
*
@Override
public void validate(){
super.validate();
- // Add custom validation for module attributes here.
+ checkArgument(getDataStore() != null, "Data Store needs to be provided for DomBroker");
}
+
+
@Override
public java.lang.AutoCloseable createInstance() {
import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
import org.opendaylight.controller.sal.core.api.data.DataStore
+import org.opendaylight.controller.sal.dom.broker.impl.SchemaAwareDataStoreAdapter
class BrokerConfigActivator implements AutoCloseable {
private var SchemaServiceImpl schemaService;
private var DataBrokerImpl dataService;
private var MountPointManagerImpl mountService;
+
+ SchemaAwareDataStoreAdapter wrappedStore
public def void start(BrokerImpl broker,DataStore store,BundleContext context) {
val emptyProperties = new Hashtable<String, String>();
dataReg = context.registerService(DataBrokerService, dataService, emptyProperties);
dataProviderReg = context.registerService(DataProviderService, dataService, emptyProperties);
- dataService.registerConfigurationReader(ROOT, store);
- dataService.registerCommitHandler(ROOT, store);
- dataService.registerOperationalReader(ROOT, store);
+ wrappedStore = new SchemaAwareDataStoreAdapter();
+ wrappedStore.changeDelegate(store);
+ wrappedStore.setValidationEnabled(false);
+
+ dataService.registerConfigurationReader(ROOT, wrappedStore);
+ dataService.registerCommitHandler(ROOT, wrappedStore);
+ dataService.registerOperationalReader(ROOT, wrappedStore);
mountService = new MountPointManagerImpl();
mountService.setDataBroker(dataService);
import java.util.concurrent.atomic.AtomicLong;
+import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent;
import org.opendaylight.controller.md.sal.common.api.data.DataReader;
import org.opendaylight.controller.md.sal.common.impl.service.AbstractDataBroker;
import org.opendaylight.controller.sal.common.DataStoreIdentifier;
--- /dev/null
+package org.opendaylight.controller.sal.dom.broker.impl;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.opendaylight.controller.md.sal.common.api.data.DataModification;
+import org.opendaylight.controller.sal.core.api.data.DataStore;
+import org.opendaylight.yangtools.concepts.Delegator;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+
+public class DataStoreStatsWrapper implements Delegator<DataStore>, DataStore {
+
+ private final DataStore delegate;
+
+ private AtomicLong cfgReadCount = new AtomicLong();
+ private AtomicLong cfgReadTimeTotal = new AtomicLong();
+
+ private AtomicLong operReadCount = new AtomicLong();
+ private AtomicLong operReadTimeTotal = new AtomicLong();
+
+ private AtomicLong requestCommitCount = new AtomicLong();
+ private AtomicLong requestCommitTimeTotal = new AtomicLong();
+
+ public DataStoreStatsWrapper(DataStore store) {
+ delegate = store;
+ }
+
+ @Override
+ public DataStore getDelegate() {
+ return delegate;
+ }
+
+ @Override
+ public CompositeNode readConfigurationData(InstanceIdentifier path) {
+ cfgReadCount.incrementAndGet();
+ final long startTime = System.nanoTime();
+ try {
+ return delegate.readConfigurationData(path);
+ } finally {
+ final long endTime = System.nanoTime();
+ final long runTime = endTime - startTime;
+ cfgReadTimeTotal.addAndGet(runTime);
+ }
+ }
+
+ @Override
+ public CompositeNode readOperationalData(InstanceIdentifier path) {
+ operReadCount.incrementAndGet();
+ final long startTime = System.nanoTime();
+ try {
+ return delegate.readOperationalData(path);
+ } finally {
+ final long endTime = System.nanoTime();
+ final long runTime = endTime - startTime;
+ cfgReadTimeTotal.addAndGet(runTime);
+ }
+ }
+
+ public DataCommitTransaction<InstanceIdentifier, CompositeNode> requestCommit(
+ DataModification<InstanceIdentifier, CompositeNode> modification) {
+ requestCommitCount.incrementAndGet();
+ final long startTime = System.nanoTime();
+ try {
+ return delegate.requestCommit(modification);
+ } finally {
+ final long endTime = System.nanoTime();
+ final long runTime = endTime - startTime;
+ requestCommitTimeTotal.addAndGet(runTime);
+ }
+ };
+
+ @Override
+ public boolean containsConfigurationPath(InstanceIdentifier path) {
+ return delegate.containsConfigurationPath(path);
+ }
+
+ public Iterable<InstanceIdentifier> getStoredConfigurationPaths() {
+ return delegate.getStoredConfigurationPaths();
+ }
+
+ public Iterable<InstanceIdentifier> getStoredOperationalPaths() {
+ return delegate.getStoredOperationalPaths();
+ }
+
+ public boolean containsOperationalPath(InstanceIdentifier path) {
+ return delegate.containsOperationalPath(path);
+ }
+
+ public final long getConfigurationReadCount() {
+ return cfgReadCount.get();
+ }
+
+ public final long getOperationalReadCount() {
+ return operReadCount.get();
+ }
+
+ public final long getRequestCommitCount() {
+ return requestCommitCount.get();
+ }
+
+ public final long getConfigurationReadTotalTime() {
+ return cfgReadTimeTotal.get();
+ }
+
+ public final long getOperationalReadTotalTime() {
+ return operReadTimeTotal.get();
+ }
+
+ public final long getRequestCommitTotalTime() {
+ return requestCommitTimeTotal.get();
+ }
+
+ public final long getConfigurationReadAverageTime() {
+ long readCount = cfgReadCount.get();
+ if(readCount == 0) {
+ return 0;
+ }
+ return cfgReadTimeTotal.get() / readCount;
+ }
+
+ public final long getOperationalReadAverageTime() {
+ long readCount = operReadCount.get();
+ if(readCount == 0) {
+ return 0;
+ }
+ return operReadTimeTotal.get() / readCount;
+ }
+
+ public final long getRequestCommitAverageTime() {
+ long count = requestCommitCount.get();
+ if(count == 0) {
+ return 0;
+ }
+ return requestCommitTimeTotal.get() / count;
+ }
+
+}
+++ /dev/null
-package org.opendaylight.controller.sal.dom.broker.impl
-
-import org.opendaylight.yangtools.yang.data.api.CompositeNode
-import java.util.Map
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
-import java.util.Map.Entry
-import java.util.HashSet
-import java.util.ArrayList
-import org.opendaylight.yangtools.yang.data.api.Node
-import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl
-
-class DataUtils {
-
- static def CompositeNode read(Map<InstanceIdentifier, CompositeNode> map, InstanceIdentifier path) {
- val root = map.get(path);
- val childs = map.getChilds(path);
- if(root === null && childs.empty) {
- return null;
- }
-
- return merge(path, root, childs);
- }
-
- static def CompositeNode merge(InstanceIdentifier path, CompositeNode node,
- HashSet<Entry<InstanceIdentifier, CompositeNode>> entries) {
- val it = new ArrayList<Node<?>>();
- val qname = path.path.last.nodeType;
- if (node != null) {
- addAll(node.children);
- }
- for (entry : entries) {
- val nesting = entry.key.path.size - path.path.size;
- if (nesting === 1) {
- add(entry.value);
- }
- }
- return new CompositeNodeTOImpl(qname, null, it);
- }
-
- static def getChilds(Map<InstanceIdentifier, CompositeNode> map, InstanceIdentifier path) {
- val it = new HashSet<Entry<InstanceIdentifier, CompositeNode>>();
- for (entry : map.entrySet) {
- if (path.contains(entry.key)) {
- add(entry);
- }
- }
- return it;
- }
-
-}
import java.util.Collections
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
import org.opendaylight.yangtools.yang.data.api.CompositeNode
-import static extension org.opendaylight.controller.sal.dom.broker.impl.DataUtils.*;
import org.opendaylight.controller.sal.core.api.data.DataStore
import java.util.HashSet
class HashMapDataStore implements DataStore, AutoCloseable {
+
val Map<InstanceIdentifier, CompositeNode> configuration = new ConcurrentHashMap();
val Map<InstanceIdentifier, CompositeNode> operational = new ConcurrentHashMap();
+
+
+
+ override containsConfigurationPath(InstanceIdentifier path) {
+ throw new UnsupportedOperationException("TODO: auto-generated method stub")
+
+ }
+
+ override containsOperationalPath(InstanceIdentifier path) {
+ throw new UnsupportedOperationException("TODO: auto-generated method stub")
+ }
+
+ override getStoredConfigurationPaths() {
+ configuration.keySet
+ }
+
+ override getStoredOperationalPaths() {
+ operational.keySet
+ }
override readConfigurationData(InstanceIdentifier path) {
- configuration.read(path);
+ configuration.get(path);
}
override readOperationalData(InstanceIdentifier path) {
- operational.read(path);
+ operational.get(path);
}
-
--- /dev/null
+package org.opendaylight.controller.sal.dom.broker.impl
+
+import org.opendaylight.yangtools.yang.model.api.SchemaContext
+import org.opendaylight.yangtools.yang.data.api.CompositeNode
+
+class SchemaAwareDataMerger {
+
+ private SchemaContext schema;
+
+
+
+
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.sal.dom.broker.impl;
+
+import java.awt.PageAttributes.OriginType;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.opendaylight.controller.md.sal.common.api.data.DataModification;
+import org.opendaylight.controller.md.sal.common.api.data.DataReader;
+import org.opendaylight.controller.md.sal.common.impl.util.AbstractLockableDelegator;
+import org.opendaylight.controller.sal.core.api.data.DataStore;
+import org.opendaylight.controller.sal.core.api.model.SchemaServiceListener;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.FluentIterable;
+
+import static com.google.common.base.Preconditions.*;
+
+public class SchemaAwareDataStoreAdapter extends AbstractLockableDelegator<DataStore> implements //
+ DataStore, //
+ SchemaServiceListener, //
+ AutoCloseable {
+
+ private final static Logger LOG = LoggerFactory.getLogger(SchemaAwareDataStoreAdapter.class);
+
+ private SchemaContext schema = null;
+ private boolean validationEnabled = false;
+ private SchemaAwareDataMerger dataMerger = null;
+ private DataReader<InstanceIdentifier, CompositeNode> reader = new MergeFirstLevelReader();
+
+ @Override
+ public boolean containsConfigurationPath(InstanceIdentifier path) {
+ try {
+ getDelegateReadLock().lock();
+ return getDelegate().containsConfigurationPath(path);
+
+ } finally {
+ getDelegateReadLock().unlock();
+ }
+ }
+
+ @Override
+ public boolean containsOperationalPath(InstanceIdentifier path) {
+ try {
+ getDelegateReadLock().lock();
+ return getDelegate().containsOperationalPath(path);
+
+ } finally {
+ getDelegateReadLock().unlock();
+ }
+ }
+
+ @Override
+ public Iterable<InstanceIdentifier> getStoredConfigurationPaths() {
+ try {
+ getDelegateReadLock().lock();
+ return getDelegate().getStoredConfigurationPaths();
+
+ } finally {
+ getDelegateReadLock().unlock();
+ }
+ }
+
+ @Override
+ public Iterable<InstanceIdentifier> getStoredOperationalPaths() {
+ try {
+ getDelegateReadLock().lock();
+ return getDelegate().getStoredOperationalPaths();
+
+ } finally {
+ getDelegateReadLock().unlock();
+ }
+ }
+
+ @Override
+ public CompositeNode readConfigurationData(InstanceIdentifier path) {
+ return reader.readConfigurationData(path);
+ }
+
+ @Override
+ public CompositeNode readOperationalData(InstanceIdentifier path) {
+ return reader.readOperationalData(path);
+ }
+
+ @Override
+ public org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction<InstanceIdentifier, CompositeNode> requestCommit(
+ DataModification<InstanceIdentifier, CompositeNode> modification) {
+ validateAgainstSchema(modification);
+ DataModification<InstanceIdentifier, CompositeNode> cleanedUp = prepareMergedTransaction(modification);
+ return retrieveDelegate().requestCommit(cleanedUp);
+ }
+
+ public boolean isValidationEnabled() {
+ return validationEnabled;
+ }
+
+ public void setValidationEnabled(boolean validationEnabled) {
+ this.validationEnabled = validationEnabled;
+ }
+
+ private void validateAgainstSchema(DataModification<InstanceIdentifier, CompositeNode> modification) {
+ if (!validationEnabled) {
+ return;
+ }
+
+ if (schema == null) {
+ LOG.info("Validation not performed for {}. Reason: YANG Schema not present.", modification.getIdentifier());
+ return;
+ }
+ }
+
+ @Override
+ protected void onDelegateChanged(DataStore oldDelegate, DataStore newDelegate) {
+ // NOOP
+ }
+
+ @Override
+ public void onGlobalContextUpdated(SchemaContext context) {
+ this.schema = context;
+ }
+
+ @Override
+ public void close() throws Exception {
+ this.schema = null;
+ }
+
+ private DataModification<InstanceIdentifier, CompositeNode> prepareMergedTransaction(
+ DataModification<InstanceIdentifier, CompositeNode> original) {
+ // NOOP for now
+ return original;
+ }
+
+ private final Comparator<Entry<InstanceIdentifier, CompositeNode>> preparationComparator = new Comparator<Entry<InstanceIdentifier, CompositeNode>>() {
+ @Override
+ public int compare(Entry<InstanceIdentifier, CompositeNode> o1, Entry<InstanceIdentifier, CompositeNode> o2) {
+ InstanceIdentifier o1Key = o1.getKey();
+ InstanceIdentifier o2Key = o2.getKey();
+ return Integer.compare(o1Key.getPath().size(), o2Key.getPath().size());
+ }
+ };
+
+ private class MergeFirstLevelReader implements DataReader<InstanceIdentifier, CompositeNode> {
+
+ @Override
+ public CompositeNode readConfigurationData(final InstanceIdentifier path) {
+ getDelegateReadLock().lock();
+ try {
+ if (path.getPath().isEmpty()) {
+ return null;
+ }
+ QName qname = null;
+ CompositeNode original = getDelegate().readConfigurationData(path);
+ ArrayList<Node<?>> childNodes = new ArrayList<Node<?>>();
+ if (original != null) {
+ childNodes.addAll(original.getChildren());
+ qname = original.getNodeType();
+ } else {
+ qname = path.getPath().get(path.getPath().size() - 1).getNodeType();
+ }
+
+ FluentIterable<InstanceIdentifier> directChildren = FluentIterable.from(getStoredConfigurationPaths())
+ .filter(new Predicate<InstanceIdentifier>() {
+ @Override
+ public boolean apply(InstanceIdentifier input) {
+ if (path.contains(input)) {
+ int nesting = input.getPath().size() - path.getPath().size();
+ if (nesting == 1) {
+ return true;
+ }
+ }
+ return false;
+ }
+ });
+ for (InstanceIdentifier instanceIdentifier : directChildren) {
+ childNodes.add(getDelegate().readConfigurationData(instanceIdentifier));
+ }
+ if (original == null && childNodes.isEmpty()) {
+ return null;
+ }
+
+ return new CompositeNodeTOImpl(qname, null, childNodes);
+ } finally {
+ getDelegateReadLock().unlock();
+ }
+ }
+
+ @Override
+ public CompositeNode readOperationalData(final InstanceIdentifier path) {
+ getDelegateReadLock().lock();
+ try {
+ if (path.getPath().isEmpty()) {
+ return null;
+ }
+ QName qname = null;
+ CompositeNode original = getDelegate().readOperationalData(path);
+ ArrayList<Node<?>> childNodes = new ArrayList<Node<?>>();
+ if (original != null) {
+ childNodes.addAll(original.getChildren());
+ qname = original.getNodeType();
+ } else {
+ qname = path.getPath().get(path.getPath().size() - 1).getNodeType();
+ }
+
+ FluentIterable<InstanceIdentifier> directChildren = FluentIterable.from(getStoredOperationalPaths())
+ .filter(new Predicate<InstanceIdentifier>() {
+ @Override
+ public boolean apply(InstanceIdentifier input) {
+ if (path.contains(input)) {
+ int nesting = input.getPath().size() - path.getPath().size();
+ if (nesting == 1) {
+ return true;
+ }
+ }
+ return false;
+ }
+ });
+
+ for (InstanceIdentifier instanceIdentifier : directChildren) {
+ childNodes.add(getDelegate().readOperationalData(instanceIdentifier));
+ }
+ if (original == null && childNodes.isEmpty()) {
+ return null;
+ }
+
+ return new CompositeNodeTOImpl(qname, null, childNodes);
+ } finally {
+ getDelegateReadLock().unlock();
+ }
+ }
+ }
+}