--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+
+ This program and the accompanying materials are made available under the
+ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ and is available at http://www.eclipse.org/legal/epl-v10.html
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>mdsal-artifacts</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ <packaging>pom</packaging>
+
+ <dependencyManagement>
+ <dependencies>
+ <!-- Core API/implementation -->
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>sal-common-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>sal-common-impl</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>sal-common-util</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>sal-core-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>sal-core-spi</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>sal-broker-impl</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>sal-binding-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>sal-binding-broker-impl</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>sal-binding-util</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>sal-connector-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>sal-inmemory-datastore</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>sal-dom-config</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>sal-dom-broker-config</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>sal-binding-config</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>md-sal-config</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <!-- Test support -->
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>sal-test-model</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>sal-binding-it</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>sal-binding-broker-impl</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+
+ <!-- Features -->
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>features-mdsal</artifactId>
+ <version>${project.version}</version>
+ <classifier>features</classifier>
+ <type>xml</type>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>features-restconf</artifactId>
+ <version>${project.version}</version>
+ <classifier>features</classifier>
+ <type>xml</type>
+ <scope>runtime</scope>
+ </dependency>
+
+ <!-- Base model augmentations -->
+ <dependency>
+ <groupId>org.opendaylight.controller.model</groupId>
+ <artifactId>model-inventory</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.model</groupId>
+ <artifactId>model-topology</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <!-- Clustered implementation -->
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>sal-clustering-commons</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>sal-clustering-config</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>sal-distributed-datastore</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>sal-remoterpc-connector</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-akka-raft</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-akka-raft</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-akka-raft-example</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <!-- RESTCONF -->
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>sal-remote</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-rest-connector</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-rest-connector-config</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-restconf-broker</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-rest-docgen</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-rest-docgen-maven</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <!-- Toaster -->
+ <dependency>
+ <groupId>org.opendaylight.controller.samples</groupId>
+ <artifactId>sample-toaster</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.samples</groupId>
+ <artifactId>sample-toaster-consumer</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.samples</groupId>
+ <artifactId>sample-toaster-provider</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.samples</groupId>
+ <artifactId>toaster-config</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.samples</groupId>
+ <artifactId>features-toaster</artifactId>
+ <version>${project.version}</version>
+ <classifier>features</classifier>
+ <type>xml</type>
+ <scope>runtime</scope>
+ </dependency>
+
+ <!-- XSQL support -->
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>sal-dom-xsql</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>sal-karaf-xsql</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>sal-dom-xsql-config</artifactId>
+ <version>${project.version}</version>
+ <classifier>config</classifier>
+ <type>xml</type>
+ </dependency>
+
+ <!-- MessageBus -->
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>messagebus-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>messagebus-spi</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>messagebus-impl</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>messagebus-util</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ </dependencies>
+ </dependencyManagement>
+</project>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-parent</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>sal-binding-api</artifactId>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-common-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>concepts</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-binding</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-common</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+ <scm>
+ <connection>scm:git:http://git.opendaylight.org/gerrit/controller.git</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+ <tag>HEAD</tag>
+ <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
+ </scm>
+</project>
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import org.opendaylight.controller.sal.binding.api.BindingAwareService;
+
+/**
+ *
+ * Marker interface for MD-SAL services which are available for users of MD-SAL.
+ *
+ * BindingService is marker interface for infrastructure services provided by
+ * the SAL. These services may be session-specific, and wrapped by custom
+ * delegator patterns in order to introduce additional semantics / checks
+ * to the system.
+ *
+ * This interface extends {@link BindingAwareService}, order to be make
+ * new services available via
+ * {@link org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext}
+ * and via
+ * {@link org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerContext}
+ *
+ */
+public interface BindingService extends BindingAwareService {
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * A chain of transactions.
+ * <p>
+ * For more information about transaction chaining and transaction chains
+ * see {@link TransactionChain}.
+ *
+ * @see TransactionChain
+ *
+ */
+public interface BindingTransactionChain extends TransactionFactory, TransactionChain<InstanceIdentifier<?>, DataObject> {
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ ReadOnlyTransaction newReadOnlyTransaction();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ ReadWriteTransaction newReadWriteTransaction();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ WriteTransaction newWriteOnlyTransaction();
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+
+package org.opendaylight.controller.md.sal.binding.api;
+
+/**
+ * <p>
+ * ClusteredDataChangeListener is a marker interface to enable data change notifications on all instances in a cluster,
+ * where this listener is registered.
+ * </p>
+ *
+ * <p>Applications should implement ClusteredDataChangeListener instead of DataChangeListener, if they want to listen
+ * to data change notifications on any node of clustered datastore. DataChangeListener enables data change notifications
+ * only at leader of the datastore shard.</p>
+ *
+ */
+
+public interface ClusteredDataChangeListener extends DataChangeListener{
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainFactory;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Provides access to a conceptual data tree store and also provides the ability to
+ * subscribe for changes to data under a given branch of the tree.
+ * <p>
+ * For more information on usage, please see the documentation in {@link AsyncDataBroker}.
+ *
+ * @see AsyncDataBroker
+ * @see TransactionChainFactory
+ */
+public interface DataBroker extends AsyncDataBroker<InstanceIdentifier<?>, DataObject, DataChangeListener>,
+ TransactionChainFactory<InstanceIdentifier<?>, DataObject>, TransactionFactory, BindingService, DataTreeChangeService {
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ ReadOnlyTransaction newReadOnlyTransaction();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ ReadWriteTransaction newReadWriteTransaction();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ WriteTransaction newWriteOnlyTransaction();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ ListenerRegistration<DataChangeListener> registerDataChangeListener(LogicalDatastoreType store,
+ InstanceIdentifier<?> path, DataChangeListener listener, DataChangeScope triggeringScope);
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ BindingTransactionChain createTransactionChain(TransactionChainListener listener);
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/*
+ * DataChangeListener enables data change notifications only at leader of the datastore shard
+ */
+
+public interface DataChangeListener extends AsyncDataChangeListener<InstanceIdentifier<?>, DataObject> {
+ @Override
+ void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.binding.api;
+
+import java.util.Collection;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.ChildOf;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.Identifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
+
+/**
+ * Modified Data Object.
+ *
+ * Represents modification of Data Object.
+ *
+ */
+public interface DataObjectModification<T extends DataObject> extends org.opendaylight.yangtools.concepts.Identifiable<PathArgument> {
+
+ enum ModificationType {
+ /**
+ *
+ * Child node (direct or indirect) was modified.
+ *
+ */
+ SUBTREE_MODIFIED,
+ /**
+ *
+ * Node was explicitly created / overwritten.
+ *
+ */
+ WRITE,
+ /**
+ *
+ * Node was deleted.
+ *
+ */
+ DELETE
+ }
+
+ @Override
+ PathArgument getIdentifier();
+
+ /**
+ * Returns type of modified object.
+ *
+ * @return type of modified object.
+ */
+ @Nonnull Class<T> getDataType();
+
+ /**
+ *
+ * Returns type of modification
+ *
+ * @return type Type of performed modification.
+ */
+ @Nonnull ModificationType getModificationType();
+
+ /**
+ * Returns before-state of top level container. Implementations are encouraged,
+ * but not required to provide this state.
+ *
+ * @param root Class representing data container
+ * @return State of object before modification. Null if subtree was not present,
+ * or the implementation cannot provide the state.
+ */
+ @Nullable T getDataBefore();
+
+ /**
+ * Returns after-state of top level container.
+ *
+ * @param root Class representing data container
+ * @return State of object after modification. Null if subtree is not present.
+ */
+ @Nullable T getDataAfter();
+
+ /**
+ * Returns unmodifiable collection of modified direct children.
+ *
+ * @return unmodifiable collection of modified direct children.
+ */
+ @Nonnull Collection<DataObjectModification<? extends DataObject>> getModifiedChildren();
+
+ /**
+ * Returns container child modification if {@code child} was modified by this
+ * modification.
+ *
+ * For accessing all modified list items consider iterating over {@link #getModifiedChildren()}.
+ *
+ * @param child Type of child - must be only container
+ * @return Modification of {@code child} if {@code child} was modified, null otherwise.
+ * @throws IllegalArgumentException If supplied {@code child} class is not valid child according
+ * to generated model.
+ */
+ @Nullable <C extends ChildOf<? super T>> DataObjectModification<C> getModifiedChildContainer(@Nonnull Class<C> child);
+
+ /**
+ * Returns augmentation child modification if {@code augmentation} was modified by this
+ * modification.
+ *
+ * For accessing all modified list items consider iterating over {@link #getModifiedChildren()}.
+ *
+ * @param augmentation Type of augmentation - must be only container
+ * @return Modification of {@code augmentation} if {@code augmentation} was modified, null otherwise.
+ * @throws IllegalArgumentException If supplied {@code augmentation} class is not valid augmentation
+ * according to generated model.
+ */
+ @Nullable <C extends Augmentation<T> & DataObject> DataObjectModification<C> getModifiedAugmentation(@Nonnull Class<C> augmentation);
+
+
+ /**
+ * Returns child list item modification if {@code child} was modified by this modification.
+ *
+ * @param listItem Type of list item - must be list item with key
+ * @param listKey List item key
+ * @return Modification of {@code child} if {@code child} was modified, null otherwise.
+ * @throws IllegalArgumentException If supplied {@code listItem} class is not valid child according
+ * to generated model.
+ */
+ <C extends Identifiable<K> & ChildOf<? super T>, K extends Identifier<C>> DataObjectModification<C> getModifiedChildListItem(
+ @Nonnull Class<C> listItem,@Nonnull K listKey);
+
+ /**
+ * Returns a child modification if a node identified by {@code childArgument} was modified by
+ * this modification.
+ *
+ * @param childArgument Path Argument of child node
+ * @return Modification of child identified by {@code childArgument} if {@code childArgument}
+ * was modified, null otherwise.
+ * @throws IllegalArgumentException If supplied path argument is not valid child according to
+ * generated model.
+ *
+ */
+ @Nullable DataObjectModification<? extends DataObject> getModifiedChild(PathArgument childArgument);
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import java.util.Collection;
+import java.util.EventListener;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+/**
+ * Interface implemented by classes interested in receiving notifications about
+ * data tree changes. This interface differs from {@link DataChangeListener}
+ * in that it provides a cursor-based view of the change, which has potentially
+ * lower overhead and allow more flexible consumption of change event.
+ */
+public interface DataTreeChangeListener<T extends DataObject> extends EventListener {
+ /**
+ * Invoked when there was data change for the supplied path, which was used
+ * to register this listener.
+ *
+ * <p>
+ * This method may be also invoked during registration of the listener if
+ * there is any pre-existing data in the conceptual data tree for supplied
+ * path. This initial event will contain all pre-existing data as created.
+ *
+ * <p>
+ * A data change event may be triggered spuriously, e.g. such that data before
+ * and after compare as equal. Implementations of this interface are expected
+ * to recover from such events. Event producers are expected to exert reasonable
+ * effort to suppress such events.
+ *
+ * In other words, it is completely acceptable to observe
+ * a {@link DataObjectModification}, while the state observed before and
+ * after- data items compare as equal.
+ *
+ * @param changes Collection of change events, may not be null or empty.
+ */
+ void onDataTreeChanged(@Nonnull Collection<DataTreeModification<T>> changes);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+/**
+ * A {@link DOMService} which allows users to register for changes to a
+ * subtree.
+ */
+public interface DataTreeChangeService extends BindingService {
+ /**
+ * Registers a {@link DataTreeChangeListener} to receive
+ * notifications when data changes under a given path in the conceptual data
+ * tree.
+ * <p>
+ * You are able to register for notifications for any node or subtree
+ * which can be represented using {@link DataTreeIdentifier}.
+ * <p>
+ *
+ * You are able to register for data change notifications for a subtree or leaf
+ * even if it does not exist. You will receive notification once that node is
+ * created.
+ * <p>
+ * If there is any pre-existing data in the data tree for the path for which you are
+ * registering, you will receive an initial data change event, which will
+ * contain all pre-existing data, marked as created.
+ *
+ * <p>
+ * This method returns a {@link ListenerRegistration} object. To
+ * "unregister" your listener for changes call the {@link ListenerRegistration#close()}
+ * method on the returned object.
+ * <p>
+ * You MUST explicitly unregister your listener when you no longer want to receive
+ * notifications. This is especially true in OSGi environments, where failure to
+ * do so during bundle shutdown can lead to stale listeners being still registered.
+ *
+ * @param treeId
+ * Data tree identifier of the subtree which should be watched for
+ * changes.
+ * @param listener
+ * Listener instance which is being registered
+ * @return Listener registration object, which may be used to unregister
+ * your listener using {@link ListenerRegistration#close()} to stop
+ * delivery of change events.
+ */
+ @Nonnull <T extends DataObject,L extends DataTreeChangeListener<T>> ListenerRegistration<L> registerDataTreeChangeListener(@Nonnull DataTreeIdentifier<T> treeId, @Nonnull L listener);
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import com.google.common.base.Preconditions;
+import java.io.Serializable;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.concepts.Path;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * A unique identifier for a particular subtree. It is composed of the logical
+ * data store type and the instance identifier of the root node.
+ */
+public final class DataTreeIdentifier<T extends DataObject> implements Immutable, Path<DataTreeIdentifier<?>>, Serializable {
+ private static final long serialVersionUID = 1L;
+ private final InstanceIdentifier<T> rootIdentifier;
+ private final LogicalDatastoreType datastoreType;
+
+ public DataTreeIdentifier(final LogicalDatastoreType datastoreType, final InstanceIdentifier<T> rootIdentifier) {
+ this.datastoreType = Preconditions.checkNotNull(datastoreType);
+ this.rootIdentifier = Preconditions.checkNotNull(rootIdentifier);
+ }
+
+ /**
+ * Return the logical data store type.
+ *
+ * @return Logical data store type. Guaranteed to be non-null.
+ */
+ public @Nonnull LogicalDatastoreType getDatastoreType() {
+ return datastoreType;
+ }
+
+ /**
+ * Return the {@link YangInstanceIdentifier} of the root node.
+ *
+ * @return Instance identifier corresponding to the root node.
+ */
+ public @Nonnull InstanceIdentifier<T> getRootIdentifier() {
+ return rootIdentifier;
+ }
+
+ @Override
+ public boolean contains(final DataTreeIdentifier<?> other) {
+ return datastoreType == other.datastoreType && rootIdentifier.contains(other.rootIdentifier);
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + datastoreType.hashCode();
+ result = prime * result + rootIdentifier.hashCode();
+ return result;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof DataTreeIdentifier)) {
+ return false;
+ }
+ final DataTreeIdentifier<?> other = (DataTreeIdentifier<?>) obj;
+ if (datastoreType != other.datastoreType) {
+ return false;
+ }
+ return rootIdentifier.equals(other.rootIdentifier);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.binding.api;
+
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+/**
+ * Represent root of modification.
+ *
+ * @author Tony Tkacik <ttkacik@cisco.com>
+ *
+ */
+public interface DataTreeModification<T extends DataObject> {
+
+ /**
+ * Get the modification root path. This is the path of the root node
+ * relative to the root of InstanceIdentifier namespace.
+ *
+ * @return absolute path of the root node
+ */
+ @Nonnull DataTreeIdentifier<T> getRootPath();
+
+ /**
+ * Get the modification root node.
+ *
+ * @return modification root node
+ */
+ @Nonnull DataObjectModification<T> getRootNode();
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import com.google.common.base.Optional;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public interface MountPoint extends Identifiable<InstanceIdentifier<?>>{
+
+ <T extends BindingService> Optional<T> getService(Class<T> service);
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import com.google.common.base.Optional;
+import java.util.EventListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public interface MountPointService extends BindingService {
+
+ Optional<MountPoint> getMountPoint(InstanceIdentifier<?> mountPoint);
+
+ <T extends MountPointListener> ListenerRegistration<T> registerListener(InstanceIdentifier<?> path, T listener);
+
+
+ public interface MountPointListener extends EventListener {
+
+ void onMountPointCreated(InstanceIdentifier<?> path);
+
+ void onMountPointRemoved(InstanceIdentifier<?> path);
+
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.concurrent.TimeUnit;
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+/**
+ * A {@link NotificationService} which also allows its users to
+ * submit YANG-modeled notifications for delivery. There are three
+ * methods of submission, following the patters from {@link java.util.concurrent.BlockingQueue}:
+ * - {@link #putNotification(Notification)}, which may block indefinitely
+ * if the implementation cannot allocate resources to accept the notification,
+ * - {@link #offerNotification(Notification)}, which does not block if face
+ * of resource starvation,
+ * - {@link #offerNotification(Notification, int, TimeUnit)}, which may block
+ * for specified time if resources are thin.
+ *
+ * The actual delivery to listeners is asynchronous and implementation-specific.
+ * Users of this interface should not make any assumptions as to whether the
+ * notification has or has not been seen.
+ */
+public interface NotificationPublishService extends BindingService {
+
+ /**
+ * Well-known value indicating that the binding-aware implementation is currently not
+ * able to accept a notification.
+ */
+ ListenableFuture<Object> REJECTED = Futures.immediateFailedFuture(new NotificationRejectedException("Rejected due to resource constraints."));
+
+ /**
+ * Publishes a notification to subscribed listeners. This initiates
+ * the process of sending the notification, but delivery to the
+ * listeners can happen asynchronously, potentially after a call to
+ * this method returns.
+ *
+ * <b>Note:</b> This call will block when the notification queue is full.
+ *
+ * @param notification
+ * the notification to publish.
+ * @throws InterruptedException if interrupted while waiting
+ * @throws NullPointerException if the notification is null
+ */
+ void putNotification(Notification notification) throws InterruptedException;
+
+ /**
+ * Publishes a notification to subscribed listeners. This initiates
+ * the process of sending the notification, but delivery to the
+ * listeners can happen asynchronously, potentially after a call to
+ * this method returns.
+ *
+ * Still guaranteed not to block. Returns Listenable Future which will complete once.
+ *
+ * @param notification
+ * the notification to publish.
+ * @return A listenable future which will report completion when the service has finished
+ * propagating the notification to its immediate registrants, or {@value #REJECTED} if resource
+ * constraints prevent
+ * @throws NullPointerException if the notification is null
+ */
+ ListenableFuture<? extends Object> offerNotification(Notification notification);
+
+ /**
+ * Publishes a notification to subscribed listeners. This initiates
+ * the process of sending the notification, but delivery to the
+ * listeners can happen asynchronously, potentially after a call to
+ * this method returns. This method is guaranteed not to block more
+ * than the specified timeout.
+ *
+ * @param notification
+ * the notification to publish.
+ * @param timeout how long to wait before giving up, in units of unit
+ * @param unit a TimeUnit determining how to interpret the
+ * timeout parameter
+ * @return A listenable future which will report completion when the service has finished
+ * propagating the notification to its immediate registrants, or {@value #REJECTED} if resource
+ * constraints prevent
+ * @throws InterruptedException if interrupted while waiting
+ * @throws NullPointerException if the notification or unit is null
+ * @throws IllegalArgumentException if timeout is negative.
+ */
+ ListenableFuture<? extends Object> offerNotification(Notification notification, int timeout, TimeUnit unit)
+ throws InterruptedException;
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+/**
+ * <p>
+ * This exception indicates that given notification can not be processed by corresponding mechanism.
+ * More info can be provided in message.
+ * </p>
+ * <p>
+ * Expected use: {@link NotificationPublishService}
+ * </p>
+ */
+public class NotificationRejectedException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public NotificationRejectedException(final String message) {
+ super(message);
+ }
+
+ public NotificationRejectedException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.NotificationListener;
+
+/**
+ * Notification broker which allows clients to subscribe for and publish YANG-modeled notifications.
+ *
+ * Each YANG module which defines notifications results in a generated interface <code>{ModuleName}Listener</code>
+ * which handles all the notifications defined in the YANG model. Each notification type translates to
+ * a specific method of the form <code>on{NotificationType}</code> on the generated interface.
+ * The generated interface also extends the
+ * {@link org.opendaylight.yangtools.yang.binding.NotificationListener} interface and implementations
+ * are registered using {@link #registerNotificationListener(org.opendaylight.yangtools.yang.binding.NotificationListener)}
+ * method.
+ *
+ * <h5>Dispatch Listener Example</h5>
+ * <p>
+ * Lets assume we have following YANG model:
+ *
+ * <pre>
+ * module example {
+ * ...
+ *
+ * notification start {
+ * ...
+ * }
+ *
+ * notification stop {
+ * ...
+ * }
+ * }
+ * </pre>
+ *
+ * The generated interface will be:
+ * <pre>
+ * public interface ExampleListener extends NotificationListener {
+ * void onStart(Start notification);
+ * void onStop(Stop notification);
+ * }
+ * </pre>
+ * The following defines an implementation of the generated interface:
+ * <pre>
+ * public class MyExampleListener implements ExampleListener {
+ * public void onStart(Start notification) {
+ * // do something
+ * }
+ *
+ * public void onStop(Stop notification) {
+ * // do something
+ * }
+ * }
+ * </pre>
+ * The implementation is registered as follows:
+ * <pre>
+ * MyExampleListener listener = new MyExampleListener();
+ * ListenerRegistration<NotificationListener> reg = service.registerNotificationListener( listener );
+ * </pre>
+ * The <code>onStart</code> method will be invoked when someone publishes a <code>Start</code> notification and
+ * the <code>onStop</code> method will be invoked when someone publishes a <code>Stop</code> notification.
+ */
+public interface NotificationService extends BindingService {
+ /**
+ * Registers a listener which implements a YANG-generated notification interface derived from
+ * {@link NotificationListener}. The listener is registered for all notifications present in
+ * the implemented interface.
+ *
+ * @param listener the listener implementation that will receive notifications.
+ * @return a {@link ListenerRegistration} instance that should be used to unregister the listener
+ * by invoking the {@link ListenerRegistration#close()} method when no longer needed.
+ */
+ <T extends NotificationListener> ListenerRegistration<T> registerNotificationListener(T listener);
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncReadOnlyTransaction;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * A transaction that provides a stateful read-only view of the data tree.
+ * <p>
+ * For more information on usage and examples, please see the documentation in
+ * {@link org.opendaylight.controller.md.sal.common.api.data.AsyncReadTransaction}.
+ */
+public interface ReadOnlyTransaction extends ReadTransaction, AsyncReadOnlyTransaction<InstanceIdentifier<?>, DataObject> {
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncReadTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+
+/**
+ * A transaction that provides read access to a logical data store.
+ * <p>
+ * For more information on usage and examples, please see the documentation in {@link AsyncReadTransaction}.
+ */
+public interface ReadTransaction extends AsyncReadTransaction<InstanceIdentifier<?>, DataObject> {
+
+ /**
+ * Reads data from the provided logical data store located at the provided path.
+ *<p>
+ * If the target is a subtree, then the whole subtree is read (and will be
+ * accessible from the returned data object).
+ *
+ * @param store
+ * Logical data store from which read should occur.
+ * @param path
+ * Path which uniquely identifies subtree which client want to
+ * read
+ * @return a CheckFuture containing the result of the read. The Future blocks until the
+ * commit operation is complete. Once complete:
+ * <ul>
+ * <li>If the data at the supplied path exists, the Future returns an Optional object
+ * containing the data.</li>
+ * <li>If the data at the supplied path does not exist, the Future returns
+ * Optional#absent().</li>
+ * <li>If the read of the data fails, the Future will fail with a
+ * {@link ReadFailedException} or an exception derived from ReadFailedException.</li>
+ * </ul>
+ */
+ <T extends DataObject> CheckedFuture<Optional<T>,ReadFailedException> read(
+ LogicalDatastoreType store, InstanceIdentifier<T> path);
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncReadWriteTransaction;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * A transaction that enables combined read/write capabilities.
+ * <p>
+ * For more information on usage and examples, please see the documentation in {@link AsyncReadWriteTransaction}.
+ */
+public interface ReadWriteTransaction extends ReadTransaction, WriteTransaction, AsyncReadWriteTransaction<InstanceIdentifier<?>, DataObject> {
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.binding.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataTransactionFactory;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public interface TransactionFactory extends AsyncDataTransactionFactory<InstanceIdentifier<?>, DataObject>{
+
+ @Override
+ ReadOnlyTransaction newReadOnlyTransaction();
+
+ @Override
+ ReadWriteTransaction newReadWriteTransaction();
+
+ @Override
+ WriteTransaction newWriteOnlyTransaction();
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncWriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * A transaction that provides mutation capabilities on a data tree.
+ * <p>
+ * For more information on usage and examples, please see the documentation in {@link AsyncWriteTransaction}.
+ */
+public interface WriteTransaction extends AsyncWriteTransaction<InstanceIdentifier<?>, DataObject> {
+
+ /**
+ * Stores a piece of data at the specified path. This acts as an add / replace
+ * operation, which is to say that whole subtree will be replaced by the specified data.
+ * * <p>
+ * This method does not automatically create missing parent nodes. It is equivalent to invoking
+ * {@link #put(LogicalDatastoreType, InstanceIdentifier, DataObject, boolean)}
+ * with <code>createMissingParents</code> set to false.
+ * <p>
+ * For more information on usage and examples, please see the documentation in {@link AsyncWriteTransaction}.
+ * <p>
+ * If you need to make sure that a parent object exists but you do not want modify
+ * its pre-existing state by using put, consider using {@link #merge} instead.
+ *
+ * @param store
+ * the logical data store which should be modified
+ * @param path
+ * the data object path
+ * @param data
+ * the data object to be written to the specified path
+ * @throws IllegalStateException
+ * if the transaction has already been submitted
+ */
+ <T extends DataObject> void put(LogicalDatastoreType store, InstanceIdentifier<T> path, T data);
+
+
+ /**
+ * Stores a piece of data at the specified path. This acts as an add /
+ * replace operation, which is to say that whole subtree will be replaced by
+ * the specified data.
+ * <p>
+ * For more information on usage and examples, please see the documentation
+ * in {@link AsyncWriteTransaction}.
+ * <p>
+ * If you need to make sure that a parent object exists but you do not want
+ * modify its pre-existing state by using put, consider using {@link #merge}
+ * instead.
+ *
+ * Note: Using <code>createMissingParents</code> with value true, may
+ * introduce garbage in data store, or recreate nodes, which were deleted by
+ * previous transaction.
+ *
+ * @param store
+ * the logical data store which should be modified
+ * @param path
+ * the data object path
+ * @param data
+ * the data object to be written to the specified path
+ * @param createMissingParents
+ * if true, any missing parent nodes will be automatically
+ * created using a merge operation.
+ * @throws IllegalStateException
+ * if the transaction has already been submitted
+ */
+ <T extends DataObject> void put(LogicalDatastoreType store, InstanceIdentifier<T> path, T data,
+ boolean createMissingParents);
+
+ /**
+ * Merges a piece of data with the existing data at a specified path. Any pre-existing data
+ * which is not explicitly overwritten will be preserved. This means that if you store a container,
+ * its child lists will be merged.
+ * <p>
+ * This method does not automatically create missing parent nodes. It is equivalent to invoking
+ * {@link #merge(LogicalDatastoreType, InstanceIdentifier, DataObject, boolean)}
+ * with <code>createMissingParents</code> set to false.
+ * <p>
+ * For more information on usage and examples, please see the documentation in {@link AsyncWriteTransaction}.
+ *<p>
+ * If you require an explicit replace operation, use {@link #put} instead.
+ * @param store
+ * the logical data store which should be modified
+ * @param path
+ * the data object path
+ * @param data
+ * the data object to be merged to the specified path
+ * @throws IllegalStateException
+ * if the transaction has already been submitted
+ */
+ <T extends DataObject> void merge(LogicalDatastoreType store, InstanceIdentifier<T> path, T data);
+
+ /**
+ * Merges a piece of data with the existing data at a specified path. Any
+ * pre-existing data which is not explicitly overwritten will be preserved.
+ * This means that if you store a container, its child lists will be merged.
+ * <p>
+ * For more information on usage and examples, please see the documentation
+ * in {@link AsyncWriteTransaction}.
+ * <p>
+ * If you require an explicit replace operation, use {@link #put} instead.
+ *
+ * @param store
+ * the logical data store which should be modified
+ * @param path
+ * the data object path
+ * @param data
+ * the data object to be merged to the specified path
+ * @param createMissingParents
+ * if true, any missing parent nodes will be automatically created
+ * using a merge operation.
+ * @throws IllegalStateException
+ * if the transaction has already been submitted
+ */
+ <T extends DataObject> void merge(LogicalDatastoreType store, InstanceIdentifier<T> path, T data,
+ boolean createMissingParents);
+
+ @Override
+ void delete(LogicalDatastoreType store, InstanceIdentifier<?> path);
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api;
+
+import org.osgi.framework.BundleContext;
+
+@Deprecated
+public abstract class AbstractBindingAwareConsumer extends AbstractBrokerAwareActivator implements BindingAwareConsumer {
+
+ @Override
+ protected final void onBrokerAvailable(BindingAwareBroker broker, BundleContext context) {
+ broker.registerConsumer(this, context);
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api;
+
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
+import org.osgi.framework.BundleContext;
+
+@Deprecated
+public abstract class AbstractBindingAwareProvider extends AbstractBrokerAwareActivator implements BindingAwareProvider {
+
+ @Override
+ protected final void onBrokerAvailable(BindingAwareBroker broker, BundleContext context) {
+ ProviderContext ctx = broker.registerProvider(this, context);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+public abstract class AbstractBrokerAwareActivator implements BundleActivator {
+
+ private static final ExecutorService mdActivationPool = Executors.newCachedThreadPool();
+ private BundleContext context;
+ private ServiceTracker<BindingAwareBroker, BindingAwareBroker> tracker;
+ private BindingAwareBroker broker;
+ private ServiceTrackerCustomizer<BindingAwareBroker, BindingAwareBroker> customizer = new ServiceTrackerCustomizer<BindingAwareBroker, BindingAwareBroker>() {
+
+ @Override
+ public BindingAwareBroker addingService(ServiceReference<BindingAwareBroker> reference) {
+ broker = context.getService(reference);
+ mdActivationPool.execute(new Runnable() {
+
+ @Override
+ public void run() {
+ onBrokerAvailable(broker, context);
+ }
+ });
+ return broker;
+ }
+
+ @Override
+ public void modifiedService(ServiceReference<BindingAwareBroker> reference, BindingAwareBroker service) {
+ removedService(reference, service);
+ addingService(reference);
+ }
+
+ @Override
+ public void removedService(ServiceReference<BindingAwareBroker> reference, BindingAwareBroker service) {
+ broker = context.getService(reference);
+ mdActivationPool.execute(new Runnable() {
+
+ @Override
+ public void run() {
+ onBrokerRemoved(broker, context);
+ }
+ });
+ }
+
+ };
+
+
+ @Override
+ public final void start(BundleContext context) throws Exception {
+ this.context = context;
+ startImpl(context);
+ tracker = new ServiceTracker<>(context, BindingAwareBroker.class, customizer);
+ tracker.open();
+
+ }
+
+
+
+ @Override
+ public final void stop(BundleContext context) throws Exception {
+ tracker.close();
+ stopImpl(context);
+ }
+
+
+ /**
+ * Called when this bundle is started (before
+ * {@link #onSessionInitiated(ProviderContext)} so the Framework can perform
+ * the bundle-specific activities necessary to start this bundle. This
+ * method can be used to register services or to allocate any resources that
+ * this bundle needs.
+ *
+ * <p>
+ * This method must complete and return to its caller in a timely manner.
+ *
+ * @param context
+ * The execution context of the bundle being started.
+ * @throws Exception
+ * If this method throws an exception, this bundle is marked as
+ * stopped and the Framework will remove this bundle's
+ * listeners, unregister all services registered by this bundle,
+ * and release all services used by this bundle.
+ */
+ protected void startImpl(BundleContext context) {
+ // NOOP
+ }
+
+ /**
+ * Called when this bundle is stopped so the Framework can perform the
+ * bundle-specific activities necessary to stop the bundle. In general, this
+ * method should undo the work that the {@code BundleActivator.start} method
+ * started. There should be no active threads that were started by this
+ * bundle when this bundle returns. A stopped bundle must not call any
+ * Framework objects.
+ *
+ * <p>
+ * This method must complete and return to its caller in a timely manner.
+ *
+ * @param context The execution context of the bundle being stopped.
+ * @throws Exception If this method throws an exception, the bundle is still
+ * marked as stopped, and the Framework will remove the bundle's
+ * listeners, unregister all services registered by the bundle, and
+ * release all services used by the bundle.
+ */
+ protected void stopImpl(BundleContext context) {
+ // NOOP
+ }
+
+
+ protected abstract void onBrokerAvailable(BindingAwareBroker broker, BundleContext context);
+
+ protected void onBrokerRemoved(BindingAwareBroker broker, BundleContext context) {
+ stopImpl(context);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api;
+
+import org.opendaylight.controller.md.sal.common.api.routing.RoutedRegistration;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import org.opendaylight.yangtools.yang.binding.BaseIdentity;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.osgi.framework.BundleContext;
+
+/**
+ * Binding-aware core of the SAL layer responsible for wiring the SAL consumers.
+ *
+ * The responsibility of the broker is to maintain registration of SAL
+ * functionality {@link Consumer}s and {@link Provider}s, store provider and
+ * consumer specific context and functionality registration via
+ * {@link ConsumerContext} and provide access to infrastructure services, which
+ * removes direct dependencies between providers and consumers.
+ *
+ * The Binding-aware broker is also responsible for translation from Java
+ * classes modeling the functionality and data to binding-independent form which
+ * is used in SAL Core.
+ *
+ *
+ * <h3>Infrastructure services</h3> Some examples of infrastructure services:
+ *
+ * <ul>
+ * <li>YANG Module service - see {@link ConsumerContext#getRpcService(Class)},
+ * {@link ProviderContext}
+ * <li>Notification Service - see {@link NotificationService} and
+ * {@link NotificationProviderService}
+ * <li>Functionality and Data model
+ * <li>Data Store access and modification - see {@link org.opendaylight.controller.sal.binding.api.data.DataBrokerService} and
+ * {@link org.opendaylight.controller.sal.binding.api.data.DataProviderService}
+ * </ul>
+ *
+ * The services are exposed via session.
+ *
+ * <h3>Session-based access</h3>
+ *
+ * The providers and consumers needs to register in order to use the
+ * binding-independent SAL layer and to expose functionality via SAL layer.
+ *
+ * For more information about session-based access see {@link ConsumerContext}
+ * and {@link ProviderContext}
+ */
+public interface BindingAwareBroker {
+ /*
+ * @deprecated Use registerConsumer(BindingAwareConsumer cons) instead (BundleContext is no longer used)
+ */
+ @Deprecated
+ ConsumerContext registerConsumer(BindingAwareConsumer consumer, BundleContext ctx);
+
+ /**
+ * Registers the {@link BindingAwareConsumer}, which will use the SAL layer.
+ *
+ * <p>
+ * Note that consumer could register additional functionality at later point
+ * by using service and functionality specific APIs.
+ *
+ * <p>
+ * The consumer is required to use returned session for all communication
+ * with broker or one of the broker services. The session is announced to
+ * the consumer by invoking
+ * {@link Consumer#onSessionInitiated(ConsumerContext)}.
+ *
+ * @param cons
+ * Consumer to be registered.
+ * @return a session specific to consumer registration
+ * @throws IllegalArgumentException
+ * If the consumer is <code>null</code>.
+ * @throws IllegalStateException
+ * If the consumer is already registered.
+ */
+ ConsumerContext registerConsumer(BindingAwareConsumer consumer);
+
+ /*
+ * @deprecated Use registerProvider(BindingAwareProvider prov) instead (BundleContext is no longer used)
+ */
+ @Deprecated
+ ProviderContext registerProvider(BindingAwareProvider provider, BundleContext ctx);
+
+ /**
+ * Registers the {@link BindingAwareProvider}, which will use the SAL layer.
+ *
+ * <p>
+ * During the registration, the broker obtains the initial functionality
+ * from consumer, using the
+ * {@link BindingAwareProvider#getImplementations()}, and register that
+ * functionality into system and concrete infrastructure services.
+ *
+ * <p>
+ * Note that provider could register additional functionality at later point
+ * by using service and functionality specific APIs.
+ *
+ * <p>
+ * The consumer is <b>required to use</b> returned session for all
+ * communication with broker or one of the broker services. The session is
+ * announced to the consumer by invoking
+ * {@link BindingAwareProvider#onSessionInitiated(ProviderContext)}.
+ *
+ *
+ * @param prov
+ * Provider to be registered.
+ * @return a session unique to the provider registration.
+ * @throws IllegalArgumentException
+ * If the provider is <code>null</code>.
+ * @throws IllegalStateException
+ * If the consumer is already registered.
+ */
+ ProviderContext registerProvider(BindingAwareProvider provider);
+
+ /**
+ * {@link BindingAwareConsumer} specific access to the SAL functionality.
+ *
+ * <p>
+ * ConsumerSession is {@link BindingAwareConsumer}-specific access to the
+ * SAL functionality and infrastructure services.
+ *
+ * <p>
+ * The session serves to store SAL context (e.g. registration of
+ * functionality) for the consumer and provides access to the SAL
+ * infrastructure services and other functionality provided by
+ * {@link Provider}s.
+ */
+ public interface ConsumerContext extends RpcConsumerRegistry {
+
+ /**
+ * Returns a session specific instance (implementation) of requested
+ * binding-aware infrastructural service
+ *
+ * @param service
+ * Broker service
+ * @return Session specific implementation of service
+ */
+ <T extends BindingAwareService> T getSALService(Class<T> service);
+ }
+
+ /**
+ * {@link BindingAwareProvider} specific access to the SAL functionality.
+ *
+ * <p>
+ * ProviderSession is {@link BindingAwareProvider}-specific access to the
+ * SAL functionality and infrastructure services, which also allows for
+ * exposing the provider's functionality to the other
+ * {@link BindingAwareConsumer}s.
+ *
+ * <p>
+ * The session serves to store SAL context (e.g. registration of
+ * functionality) for the providers and exposes access to the SAL
+ * infrastructure services, dynamic functionality registration and any other
+ * functionality provided by other {@link BindingAwareConsumer}s.
+ *
+ */
+ public interface ProviderContext extends ConsumerContext, RpcProviderRegistry {
+
+ }
+
+ /**
+ * Represents an RPC implementation registration. Users should call the
+ * {@link ObjectRegistration#close close} method when the registration is no longer needed.
+ *
+ * @param <T> the implemented RPC service interface
+ */
+ public interface RpcRegistration<T extends RpcService> extends ObjectRegistration<T> {
+
+ /**
+ * Returns the implemented RPC service interface.
+ */
+ Class<T> getServiceType();
+
+ @Override
+ void close();
+ }
+
+ /**
+ * Represents a routed RPC implementation registration. Users should call the
+ * {@link RoutedRegistration#close close} method when the registration is no longer needed.
+ *
+ * @param <T> the implemented RPC service interface
+ */
+ public interface RoutedRpcRegistration<T extends RpcService> extends RpcRegistration<T>,
+ RoutedRegistration<Class<? extends BaseIdentity>, InstanceIdentifier<?>, T> {
+
+ /**
+ * Register particular instance identifier to be processed by this
+ * RpcService
+ *
+ * Deprecated in favor of {@link RoutedRegistration#registerPath(Object, Object)}.
+ *
+ * @param context
+ * @param instance
+ */
+ @Deprecated
+ void registerInstance(Class<? extends BaseIdentity> context, InstanceIdentifier<?> instance);
+
+ /**
+ * Unregister particular instance identifier to be processed by this
+ * RpcService
+ *
+ * Deprecated in favor of {@link RoutedRegistration#unregisterPath(Object, Object)}.
+ *
+ * @param context
+ * @param instance
+ */
+ @Deprecated
+ void unregisterInstance(Class<? extends BaseIdentity> context, InstanceIdentifier<?> instance);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api;
+
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerContext;
+
+/**
+*
+* A developer implemented component that gets registered with the Broker.
+*
+* Semantically, a consumer may:
+*
+* <ol>
+* <li>Subscribe for Notifications </li>
+* <li>Invoke RPCs</li>
+* <li>Read from either the operational or config data tree</li>
+* <li>Write to the config data tree</li>
+* </ol>
+* If you need to:
+* <ol>
+* <li> Emit Notifications</li>
+* <li> Provide the implementation of RPCs </li>
+* <li> Write to the operational data tree </li>
+* </ol>
+*
+* Consider using a BindingAwareProvider
+*
+* Examples:
+*
+* To get a NotificationService:
+*
+* {code
+* public void onSessionInitiated(ProviderContext session) {
+* NotificationProviderService notificationService = session.getSALService(NotificationProviderService.class);
+* notificationService.publish(notification)
+* }
+* where notification is an instance of a modeled Notification.
+* For more information on sending notifications via the NotificationProviderService
+* @see org.opendaylight.controller.sal.binding.api.NotificationProviderService
+*
+*
+* A consumer can *invoke* and RPC ( ie, call foo(fooArgs)) but it cannot register an RPC
+* implementation with the MD-SAL that others can invoke(call).
+* To get an invokable RPC:
+*
+* {code
+* public void onSessionInitiated(ProviderContext session) {
+* MyService rpcFlowSalService = session.getRpcService(MyService.class);
+* }
+*
+* Where MyService.class is a Service interface generated from a yang model with RPCs modeled in it. The returned
+* rpcFlowSalService can be used like any other object by invoking its methods. Note, nothing special needs to be done
+* for RoutedRPCs. They just work.
+*
+* To get a DataBroker to allow access to the data tree:
+*
+* {code
+* public void onSessionInitiated(final ProviderContext session) {
+* DataBroker databroker = session.getSALService(BindingDataBroker.class);
+* }
+* }
+* @see org.opendaylight.controller.md.sal.common.api.data.BindingDataBroker
+* for more info on using the DataBroker.
+*
+*/
+public interface BindingAwareConsumer {
+
+ /**
+ * Callback signaling initialization of the consumer session to the SAL.
+ *
+ * The consumer MUST use the session for all communication with SAL or
+ * retrieving SAL infrastructure services.
+ *
+ * This method is invoked by
+ * {@link BindingAwareBroker#registerConsumer(BindingAwareConsumer)}
+ *
+ * @param session
+ * Unique session between consumer and SAL.
+ */
+ void onSessionInitialized(ConsumerContext session);
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api;
+
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
+
+/**
+ *
+ * A developer implemented component that gets registered with the Broker.
+ *
+ * Semantically, a provider may:
+ *
+ * <ol>
+ * <li> Emit Notifications</li>
+ * <li> Provide the implementation of RPCs </li>
+ * <li> Write to the operational data tree </li>
+ * </ol>
+ *
+ * If a class is not doing at least one of those three, consider using
+ * a BindingAwareConsumer instead:
+ * @see org.opendaylight.controller.sal.binding.api.BindingAwareConsumer
+ *
+ * <p>
+ *
+ *In addition, a BindingAwareProvider can in pursuit of its goals:
+ *
+ * <ol>
+ * <li>Subscribe for Notifications </li>
+ * <li>Invoke RPCs</li>
+ * <li>Read from either the operational or config data tree</li>
+ * <li>Write to the config data tree</li>
+ * </ol>
+ * (All of the above are things a Consumer can also do).
+ *
+ *<p>
+ *
+ * Examples:
+ *
+ *<p>
+ *
+ * To get a NotificationService:
+ *
+ * {code
+ * public void onSessionInitiated(ProviderContext session) {
+ * NotificationProviderService notificationService = session.getSALService(NotificationProviderService.class);
+ * }
+ * For more information on sending notifications via the NotificationProviderService
+ * @see org.opendaylight.controller.sal.binding.api.NotificationProviderService
+ *
+ * To register an RPC implementation:
+ *
+ * {code
+ * public void onSessionInitiated(ProviderContext session) {
+ * RpcRegistration<MyService> registration = session.addRpcImplementation(MyService.class, myImplementationInstance);
+ * }
+ *
+ * <p>
+ *
+ * Where MyService.class is a Service interface generated from a yang model with RPCs modeled in it and myImplementationInstance
+ * is an instance of a class that implements MyService.
+ *
+ * To register a Routed RPC Implementation:
+ * {code
+ * public void onSessionInitiated(ProviderContext session) {
+ * RoutedRpcRegistration<SalFlowService> flowRegistration = session.addRoutedRpcImplementation(SalFlowService.class, salFlowServiceImplementationInstance);
+ flowRegistration.registerPath(NodeContext.class, nodeInstanceId);
+ * }
+ * }
+ *
+ * Where SalFlowService.class is a Service interface generated from a yang model with RPCs modeled in it and salFlowServiceImplementationInstance is an instance
+ * of a class that implements SalFlowService.
+ * <p>
+ * The line:
+ * {code
+ * flowRegistration.registerPath(NodeContext.class, nodeInstanceId);
+ * }
+ * Is indicating that the RPC implementation is registered to handle RPC invocations that have their NodeContext pointing to the node with instance id nodeInstanceId.
+ * This bears a bit of further explanation. RoutedRPCs can be 'routed' to an implementation based upon 'context'. 'context' is a pointer (instanceId) to some place
+ * in the data tree. In this example, the 'context' is a pointer to a Node. In this way, a provider can register its ability to provide a service for a particular
+ * Node, but not *all* Nodes. The Broker routes the RPC by 'context' to the correct implementation, without the caller having to do extra work. Because of this when
+ * a RoutedRPC is registered, it needs to also be able to indicate for which 'contexts' it is providing an implementation.
+ *
+ * An example of a Routed RPC would be an updateFlow(node, flow) that would be routed based on node to the provider which had registered to provide
+ * it *for that node*.
+ *
+ *<p>
+ *
+ * To get a DataBroker to allow access to the data tree:
+ *
+ * {code
+ * public void onSessionInitiated(final ProviderContext session) {
+ * DataBroker databroker = session.getSALService(BindingDataBroker.class);
+ * }
+ * }
+ * @see org.opendaylight.controller.md.sal.common.api.data.BindingDataBroker
+ * for more info on using the DataBroker.
+ *
+ */
+public interface BindingAwareProvider {
+
+ /**
+ * Callback signaling initialization of the consumer session to the SAL.
+ *
+ * The consumer MUST use the session for all communication with SAL or
+ * retrieving SAL infrastructure services.
+ *
+ * This method is invoked by
+ * {@link BindingAwareBroker#registerProvider(BindingAwareProvider)}
+ *
+ * @param session Unique session between consumer and SAL.
+ */
+ void onSessionInitiated(ProviderContext session);
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api;
+
+/**
+ * Session-specific instance of the broker functionality.
+ *
+ * <p>
+ * BindingAwareService is marker interface for infrastructure services provided
+ * by the SAL. These services are session-specific, each
+ * {@link BindingAwareConsumer} and {@link BindingAwareProvider} usually has own
+ * instance of the service with it's own context.
+ *
+ * <p>
+ * The consumer's (or provider's) instance of specific service could be obtained
+ * by invoking {@link org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerContext#getSALService(Class)} method on session
+ * assigned to the consumer.
+ *
+ * <p>
+ * {@link org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerContext} and {@link BindingAwareProvider} may seem
+ * similar, but provider provides YANG model-based functionality and
+ * {@link BindingAwareProvider} exposes the necessary supporting functionality
+ * to implement specific functionality of YANG and to reuse it in the
+ * development of {@link BindingAwareConsumer}s and {@link BindingAwareProvider}s.
+ */
+public interface BindingAwareService {
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api;
+
+import java.util.EventListener;
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+/**
+ * Interface for a generic listener that is interested in receiving YANG modeled notifications.
+ * This interface acts as a base interface for specific listeners which usually are a type
+ * capture of this interface.
+ *
+ * @param <T> the interested notification type
+ * @deprecated Deprecated unused API.
+ */
+@Deprecated
+public interface NotificationListener<T extends Notification> extends EventListener {
+ /**
+ * Invoked to deliver a notification.
+ * <p>
+ * Note that this method may be invoked from a shared thread pool, so implementations SHOULD NOT
+ * perform CPU-intensive operations and MUST NOT invoke any potentially blocking operations.
+ *
+ * @param notification the notification.
+ */
+ void onNotification(T notification);
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api;
+
+import java.util.EventListener;
+import java.util.concurrent.ExecutorService;
+import org.opendaylight.controller.md.sal.common.api.notify.NotificationPublishService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+/**
+ * Interface for a notification service that provides publish/subscribe capabilities for YANG
+ * modeled notifications. This interface is a combination of the {@link NotificationService} and
+ * {@link NotificationPublishService} interfaces.
+ *
+ * @deprecated Please use {@link org.opendaylight.controller.md.sal.binding.api.NotificationPublishService}.
+ */
+@Deprecated
+public interface NotificationProviderService extends NotificationService, NotificationPublishService<Notification> {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void publish(Notification notification);
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ void publish(Notification notification, ExecutorService executor);
+
+ /**
+ * Registers a listener to be notified about notification subscriptions. This
+ * enables a component to know when there is a notification listener subscribed
+ * for a particular notification type.
+ * <p>
+ * On registration of this listener, the
+ * {@link NotificationInterestListener#onNotificationSubscribtion(Class)} method
+ * will be invoked for every notification type that currently has a notification listener
+ * subscribed.
+ *
+ * @param interestListener the listener that will be notified when subscriptions
+ * for new notification types occur.
+ * @return a {@link ListenerRegistration} instance that should be used to unregister the listener
+ * by invoking the {@link ListenerRegistration#close()} method when no longer needed.
+ */
+ ListenerRegistration<NotificationInterestListener> registerInterestListener(
+ NotificationInterestListener interestListener);
+
+ /**
+ * Interface for a listener interested in being notified about notification subscriptions.
+ */
+ public interface NotificationInterestListener extends EventListener {
+
+ /**
+ * Callback that is invoked when a notification listener subscribes for a
+ * particular notification type.
+ * <p>
+ * This method is only called for the first subscription that occurs for a
+ * particular notification type. Subsequent subscriptions for the same
+ * notification type do not trigger invocation of this method.
+ * <p>
+ * <b>Note:</b>This callback is delivered from thread not owned by this listener,
+ * all processing should be as fast as possible and implementations should
+ * not do any blocking calls or block this thread.
+ *
+ * @param notificationType the notification type for the subscription that occurred.
+ */
+ void onNotificationSubscribtion(Class<? extends Notification> notificationType);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api;
+
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+/**
+ * Notification broker which allows clients to subscribe for and publish YANG-modeled notifications.
+ *
+ *<p>
+ * Two styles of listeners are supported:
+ * <ul>
+ * <li>Generic listener</li>
+ * <li>Dispatch listener - listener, which implements <code>{ModelName}Listener</code> interface,
+ * which has dispatch methods for each defined notification. Methods are invoked based on notification type (class).
+ * </li>
+ *
+ * <h3>Generic Listener</h3>
+ * <p>
+ * A generic listener implements the {@link NotificationListener} interface which has one callback method
+ * <code>onNotification</code> that is invoked for any notification type the listener is subscribed to.
+ * <p>
+ * A generic listener is subscribed using the {@link #registerNotificationListener(Class, NotificationListener)}
+ * method by which you specify the type of notification to receive. A generic listener may be registered for
+ * multiple notification types via multiple subscriptions.
+ * <p>
+ * Generic listeners allow for a more flexible approach, allowing you to subscribe for just
+ * one type of notification from a YANG model. You could also have a general subscription
+ * for all notification in the system via
+ * <pre>
+ * service.registerNotificationListener(Notification.class, listener);
+ * </pre>
+ *
+ * <h3>Dispatch Listener</h3>
+ * <p>
+ * A dispatch listener implements a YANG-generated module interface <code>{ModuleName}Listener</code>
+ * which handles all the notifications defined in the YANG model. Each notification type translates to
+ * a specific method of the form <code>on{NotificationType}</code> on the generated interface.
+ * The generated interface also extends the
+ * {@link org.opendaylight.yangtools.yang.binding.NotificationListener} interface and implementations
+ * are registered using {@link #registerNotificationListener(org.opendaylight.yangtools.yang.binding.NotificationListener)}
+ * method.
+ *
+ * <h5>Dispatch Listener Example</h5>
+ * <p>
+ * Lets assume we have following YANG model:
+ *
+ * <pre>
+ * module example {
+ * ...
+ *
+ * notification start {
+ * ...
+ * }
+ *
+ * notification stop {
+ * ...
+ * }
+ * }
+ * </pre>
+ *
+ * The generated interface will be:
+ * <pre>
+ * public interface ExampleListener extends NotificationListener {
+ * void onStart(Start notification);
+ * void onStop(Stop notification);
+ * }
+ * </pre>
+ * The following defines an implementation of the generated interface:
+ * <pre>
+ * public class MyExampleListener implements ExampleListener {
+ * public void onStart(Start notification) {
+ * // do something
+ * }
+ *
+ * public void onStop(Stop notification) {
+ * // do something
+ * }
+ * }
+ * </pre>
+ * The implementation is registered as follows:
+ * <pre>
+ * MyExampleListener listener = new MyExampleListener();
+ * ListenerRegistration<NotificationListener> reg = service.registerNotificationListener( listener );
+ * </pre>
+ * The <code>onStart</code> method will be invoked when someone publishes a <code>Start</code> notification and
+ * the <code>onStop</code> method will be invoked when someone publishes a <code>Stop</code> notification.
+ *
+ * @deprecated Please use {@link org.opendaylight.controller.md.sal.binding.api.NotificationService} instead.
+ */
+@Deprecated
+public interface NotificationService extends BindingAwareService {
+ /**
+ * Registers a generic listener implementation for a specified notification type.
+ *
+ * @param notificationType the YANG-generated interface of the notification type.
+ * @param listener the listener implementation that will receive notifications.
+ * @return a {@link ListenerRegistration} instance that should be used to unregister the listener
+ * by invoking the {@link ListenerRegistration#close()} method when no longer needed.
+ */
+ <T extends Notification> ListenerRegistration<NotificationListener<T>> registerNotificationListener(
+ Class<T> notificationType, NotificationListener<T> listener);
+
+ /**
+ * Registers a listener which implements a YANG-generated notification interface derived from
+ * {@link org.opendaylight.yangtools.yang.binding.NotificationListener}.
+ * The listener is registered for all notifications present in the implemented interface.
+ *
+ * @param listener the listener implementation that will receive notifications.
+ * @return a {@link ListenerRegistration} instance that should be used to unregister the listener
+ * by invoking the {@link ListenerRegistration#close()} method when no longer needed.
+ */
+ ListenerRegistration<org.opendaylight.yangtools.yang.binding.NotificationListener> registerNotificationListener(
+ org.opendaylight.yangtools.yang.binding.NotificationListener listener);
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api;
+
+public interface RpcAvailabilityListener {
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api;
+
+import org.opendaylight.controller.md.sal.binding.api.BindingService;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+/**
+ * Provides access to registered Remote Procedure Call (RPC) service implementations. The RPCs are
+ * defined in YANG models.
+ * <p>
+ * RPC implementations are registered using the {@link RpcProviderRegistry}.
+ *
+ */
+public interface RpcConsumerRegistry extends BindingAwareService, BindingService {
+ /**
+ * Returns an implementation of a requested RPC service.
+ *
+ * <p>
+ * The returned instance is not an actual implementation of the RPC service
+ * interface, but a proxy implementation of the interface that forwards to
+ * an actual implementation, if any.
+ * <p>
+ *
+ * The following describes the behavior of the proxy when invoking RPC methods:
+ * <ul>
+ * <li>If an actual implementation is registered with the MD-SAL, all invocations are
+ * forwarded to the registered implementation.</li>
+ * <li>If no actual implementation is registered, all invocations will fail by
+ * throwing {@link IllegalStateException}.</li>
+ * <li>Prior to invoking the actual implementation, the method arguments are are validated.
+ * If any are invalid, an {@link IllegalArgumentException} is thrown.
+ * </ul>
+ *
+ * The returned proxy is automatically updated with the most recent
+ * registered implementation.
+ * <p>
+ * The generated RPC method APIs require implementors to return a {@link java.util.concurrent.Future Future}
+ * instance that wraps the {@link org.opendaylight.yangtools.yang.common.RpcResult RpcResult}. Since
+ * RPC methods may be implemented asynchronously, callers should avoid blocking on the
+ * {@link java.util.concurrent.Future Future} result. Instead, it is recommended to use
+ * {@link com.google.common.util.concurrent.JdkFutureAdapters#listenInPoolThread(java.util.concurrent.Future)}
+ * or {@link com.google.common.util.concurrent.JdkFutureAdapters#listenInPoolThread(java.util.concurrent.Future, java.util.concurrent.Executor)}
+ * to listen for Rpc Result. This will asynchronously listen for future result in executor and
+ * will not block current thread.
+ *
+ * <pre>
+ * final Future<RpcResult<SomeRpcOutput>> future = someRpcService.someRpc( ... );
+ * Futures.addCallback(JdkFutureAdapters.listenInThreadPool(future), new FutureCallback<RpcResult<SomeRpcOutput>>() {
+ *
+ * public void onSuccess(RpcResult<SomeRpcOutput> result) {
+ * // process result ...
+ * }
+ *
+ * public void onFailure(Throwable t) {
+ * // RPC failed
+ * }
+ * );
+ * </pre>
+ * @param serviceInterface the interface of the RPC Service. Typically this is an interface generated
+ * from a YANG model.
+ * @return the proxy for the requested RPC service. This method never returns null.
+ */
+ <T extends RpcService> T getRpcService(Class<T> serviceInterface);
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api;
+
+import org.opendaylight.controller.md.sal.common.api.routing.RouteChangePublisher;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration;
+import org.opendaylight.controller.sal.binding.api.rpc.RpcContextIdentifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+/**
+ * Provides a registry for Remote Procedure Call (RPC) service implementations. The RPCs are
+ * defined in YANG models.
+ * <p>
+ * There are 2 types of RPCs:
+ * <ul>
+ * <li>Global</li>
+ * <li>Routed</li>
+ * </ul>
+ *
+ * <h2>Global RPC</h2>
+ * <p>
+ * An RPC is global if there is intended to be only 1 registered implementation. A global RPC is not
+ * explicitly declared as such, essentially any RPC that is not defined to be routed is considered global.
+ * <p>
+ * Global RPCs are registered using the
+ * {@link #addRpcImplementation(Class, RpcService)} method.
+ *
+ * <h2>Routed RPC</h2>
+ * <p>
+ * MD-SAL supports routing of RPC between multiple implementations where the appropriate
+ * implementation is selected at run time based on the content of the RPC message as described in
+ * YANG model.
+ * <p>
+ * RPC routing is based on:
+ * <ul>
+ * <li><b>Route identifier</b> -
+ * An {@link org.opendaylight.yangtools.yang.binding.InstanceIdentifier InstanceIdentifier} value
+ * which is part of the RPC input. This value is used to select the correct
+ * implementation at run time.</li>
+ * <li><b>Context Type</b> - A YANG-defined construct which constrains the subset of
+ * valid route identifiers for a particular RPC.</li>
+ * </ul>
+ *
+ * <h3>Context type</h3>
+ * <p>
+ * A context type is modeled in YANG using a combination of a YANG <code>identity</code>
+ * and Opendaylight specific extensions from <code>yang-ext</code> module. These extensions are:
+ * <ul>
+ * <li><b>context-instance</b> - This is used in the data tree part of a YANG model to
+ * define a context type that associates nodes with a specified context <code>identity</code>.
+ * Instance identifiers that reference these nodes are valid route identifiers for RPCs that
+ * reference this context type.</li>
+ * <li><b>context-reference</b> - This is used in RPC input to mark a leaf of type
+ * <code>instance-identifier</code> as a reference to the particular context type defined by the
+ * specified context <code>identity</code>. The value of this
+ * leaf is used by the RPC broker at run time to route the RPC request to the correct implementation.
+ * Note that <code>context-reference</code> may only be used on leaf elements of type
+ * <code>instance-identifier</code> or a type derived from <code>instance-identifier</code>.</li>
+ * </ul>
+ *
+ *
+ * <h3>Routed RPC example</h3>
+ * <p>
+ * <h5>1. Defining a Context Type</h5>
+ * <p>
+ * The following snippet declares a simple YANG <code>identity</code> named <code>example-context</code>:
+ *
+ * <pre>
+ * module example {
+ * ...
+ * identity example-context {
+ * description "Identity used to define an example-context type";
+ * }
+ * ...
+ * }
+ * </pre>
+ * <p>
+ * We then use the declared identity to define a context type by using it in combination
+ * with the <code>context-instance</code> YANG extension. We'll associate the context type
+ * with a list element in the data tree. This defines the set of nodes whose instance
+ * identifiers are valid for the <code>example-context</code> context type.
+ * <p>
+ * The following YANG snippet imports the <code>yang-ext</code> module and defines the list
+ * element named <code>item</code> inside a container named <code>foo</code>:
+ *
+ * <pre>
+ * module foo {
+ * ...
+ * import yang-ext {prefix ext;}
+ * ...
+ * container foo {
+ * list item {
+ * key "id";
+ * leaf id {type string;}
+ * ext:context-instance "example-context";
+ * }
+ * }
+ * ...
+ * }
+ * </pre>
+ * <p>
+ * The statement <code>ext:context-instance "example-context";</code> inside the list element
+ * declares that any instance identifier referencing <code>item</code> in the data
+ * tree is valid for <code>example-context</code>. For example, the following instance
+ * identifier:
+ * <pre>
+ * InstanceIdentifier.create(Foo.class).child(Item.class,new ItemKey("Foo"))
+ * </pre>
+ * is valid for <code>example-context</code>. However the following:
+ * <pre>
+ * InstanceIdentifier.create(Example.class)
+ * </pre>
+ * is not valid.
+ * <p>
+ * So using an <code>identity</code> in combination with <code>context-instance</code> we
+ * have effectively defined a context type that can be referenced in a YANG RPC input.
+ *
+ * <h5>2. Defining an RPC to use the Context Type</h5>
+ * <p>
+ * To define an RPC to be routed based on the context type we need to add an input leaf element
+ * that references the context type which will hold an instance identifier value to be
+ * used to route the RPC.
+ * <p>
+ * The following snippet defines an RPC named <code>show-item</code> with 2 leaf elements
+ * as input: <code>item</code> of type <code>instance-identifier</code> and <code>description</code>:
+ *
+ * <pre>
+ * module foo {
+ * ...
+ * import yang-ext {prefix ext;}
+ * ...
+ * rpc show-item {
+ * input {
+ * leaf item {
+ * type instance-identifier;
+ * ext:context-reference example-context;
+ * }
+ * leaf description {
+ * type "string";
+ * }
+ * }
+ * }
+ * }
+ * </pre>
+ * <p>
+ * We mark the <code>item</code> leaf with a <code>context-reference</code> statement that
+ * references the <code>example-context</code> context type. RPC calls will then be routed
+ * based on the instance identifier value contained in <code>item</code>. Only instance
+ * identifiers that point to a <code>foo/item</code> node are valid as input.
+ * <p>
+ * The generated RPC Service interface for the module is:
+ *
+ * <pre>
+ * interface FooService implements RpcService {
+ * Future<RpcResult<Void>> showItem(ShowItemInput input);
+ * }
+ * </pre>
+ * <p>
+ * For constructing the RPC input, there are generated classes ShowItemInput and ShowItemInputBuilder.
+ *
+ * <h5>3. Registering a routed RPC implementation</h5>
+ * <p>
+ * To register a routed implementation for the <code>show-item</code> RPC, we must use the
+ * {@link #addRoutedRpcImplementation(Class, RpcService)} method. This
+ * will return a {@link RoutedRpcRegistration} instance which can then be used to register /
+ * unregister routed paths associated with the registered implementation.
+ * <p>
+ * The following snippet registers <code>myImpl</code> as the RPC implementation for an
+ * <code>item</code> with key <code>"foo"</code>:
+ * <pre>
+ * // Create the instance identifier path for item "foo"
+ * InstanceIdentifier path = InstanceIdentifier.create(Foo.class).child(Item.class, new ItemKey("foo"));
+ *
+ * // Register myImpl as the implementation for the FooService RPC interface
+ * RoutedRpcRegistration reg = rpcRegistry.addRoutedRpcImplementation(FooService.class, myImpl);
+ *
+ * // Now register for the context type and specific path ID. The context type is specified by the
+ * // YANG-generated class for the example-context identity.
+ * reg.registerPath(ExampleContext.class, path);
+ * </pre>
+ * <p>
+ * It is also possible to register the same implementation for multiple paths:
+ *
+ * <pre>
+ * InstanceIdentifier one = InstanceIdentifier.create(Foo.class).child(Item.class, new ItemKey("One"));
+ * InstanceIdentifier two = InstanceIdentifier.create(Foo.class).child(Item.class, new ItemKey("Two"));
+ *
+ * RoutedRpcRegistration reg = rpcRegistry.addRoutedRpcImplementation(FooService.class, myImpl);
+ * reg.registerPath(ExampleContext.class, one);
+ * reg.registerPath(ExampleContext.class, two);
+ * </pre>
+ *
+ * <p>
+ * When another client invokes the <code>showItem(ShowItemInput)</code> method on the proxy instance
+ * retrieved via {@link RpcConsumerRegistry#getRpcService(Class)}, the proxy will inspect the
+ * arguments in ShowItemInput, extract the InstanceIdentifier value of the <code>item</code> leaf and select
+ * the implementation whose registered path matches the InstanceIdentifier value of the <code>item</code> leaf.
+ *
+ * <h2>Notes for RPC Implementations</h2>
+ *
+ * <h3>RpcResult</h3>
+ * <p>
+ * The generated interfaces require implementors to return
+ * {@link java.util.concurrent.Future Future}<{@link org.opendaylight.yangtools.yang.common.RpcResult RpcResult}<{RpcName}Output>> instances.
+ *
+ * Implementations should do processing of RPC calls asynchronously and update the
+ * returned {@link java.util.concurrent.Future Future} instance when processing is complete.
+ * However using {@link com.google.common.util.concurrent.Futures#immediateFuture(Object) Futures.immediateFuture}
+ * is valid only if the result is immediately available and asynchronous processing is unnecessary and
+ * would only introduce additional complexity.
+ *
+ * <p>
+ * The {@link org.opendaylight.yangtools.yang.common.RpcResult RpcResult} is a generic
+ * wrapper for the RPC output payload, if any, and also allows for attaching error or
+ * warning information (possibly along with the payload) should the RPC processing partially
+ * or completely fail. This is intended to provide additional human readable information
+ * for users of the API and to transfer warning / error information across the system
+ * so it may be visible via other external APIs such as Restconf.
+ * <p>
+ * It is recommended to use the {@link org.opendaylight.yangtools.yang.common.RpcResult RpcResult}
+ * for conveying appropriate error information
+ * on failure rather than purposely throwing unchecked exceptions if at all possible.
+ * While unchecked exceptions will fail the returned {@link java.util.concurrent.Future Future},
+ * using the intended RpcResult to convey the error information is more user-friendly.
+ */
+public interface RpcProviderRegistry extends //
+ RpcConsumerRegistry, //
+ RouteChangePublisher<RpcContextIdentifier, InstanceIdentifier<?>> {
+ /**
+ * Registers a global implementation of the provided RPC service interface.
+ * All methods of the interface are required to be implemented.
+ *
+ * @param serviceInterface the YANG-generated interface of the RPC Service for which to register.
+ * @param implementation "the implementation of the RPC service interface.
+ * @return an RpcRegistration instance that should be used to unregister the RPC implementation
+ * when no longer needed by calling {@link RpcRegistration#close()}.
+ *
+ * @throws IllegalStateException
+ * if the supplied RPC interface is a routed RPC type.
+ */
+ <T extends RpcService> RpcRegistration<T> addRpcImplementation(Class<T> serviceInterface, T implementation)
+ throws IllegalStateException;
+
+ /**
+ * Registers an implementation of the given routed RPC service interface.
+ * <p>
+ * See the {@link RpcProviderRegistry class} documentation for information and example on
+ * how to use routed RPCs.
+ *
+ * @param serviceInterface the YANG-generated interface of the RPC Service for which to register.
+ * @param implementation the implementation instance to register.
+ * @return a RoutedRpcRegistration instance which can be used to register paths for the RPC
+ * implementation via invoking {@link RoutedRpcRegistration#registerPath(....).
+ * {@link RoutedRpcRegistration#close()} should be called to unregister the implementation
+ * and all previously registered paths when no longer needed.
+ *
+ * @throws IllegalStateException
+ * if the supplied RPC interface is not a routed RPC type.
+ */
+ <T extends RpcService> RoutedRpcRegistration<T> addRoutedRpcImplementation(Class<T> serviceInterface,
+ T implementation)
+ throws IllegalStateException;
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api.data;
+
+import org.opendaylight.controller.md.sal.common.api.data.DataChangePublisher;
+import org.opendaylight.controller.md.sal.common.api.data.DataModificationTransactionFactory;
+import org.opendaylight.controller.md.sal.common.api.data.DataReader;
+import org.opendaylight.controller.sal.binding.api.BindingAwareService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * DataBrokerService provides unified access to the data stores available in the
+ * system.
+ *
+ *
+ * @see DataProviderService
+ * @deprecated Replaced by newer better documented version {@link org.opendaylight.controller.md.sal.binding.api.DataBroker}
+ */
+@Deprecated
+public interface DataBrokerService extends //
+ BindingAwareService, //
+ DataModificationTransactionFactory<InstanceIdentifier<? extends DataObject>, DataObject>, //
+ DataReader<InstanceIdentifier<? extends DataObject>, DataObject>, //
+ DataChangePublisher<InstanceIdentifier<? extends DataObject>, DataObject, DataChangeListener> {
+ /**
+ * Creates a data modification transaction.
+ *
+ * @return new blank data modification transaction.
+ * @deprecated Replaced by more specific transaction types. Please use
+ * {@link org.opendaylight.controller.md.sal.binding.api.DataBroker#newReadOnlyTransaction(),
+ * {@link org.opendaylight.controller.md.sal.binding.api.DataBroker#newReadWriteTransaction()
+ * or
+ * {@link org.opendaylight.controller.md.sal.binding.api.DataBroker#newWriteOnlyTransaction().
+ */
+ @Deprecated
+ @Override
+ DataModificationTransaction beginTransaction();
+
+ /**
+ * Reads data subtree from configurational store.
+ * (Store which is populated by consumer, which is usually used to
+ * inject state into providers. E.g. Flow configuration)
+ *
+ *
+ * @deprecated Please use {@link org.opendaylight.controller.md.sal.binding.api.DataBroker#newReadOnlyTransaction()}
+ *
+ */
+ @Deprecated
+ @Override
+ public DataObject readConfigurationData(InstanceIdentifier<? extends DataObject> path);
+
+ /**
+ * Reads data subtree from operational store.
+ * (Store which is populated by providers, which is usually used to
+ * capture state of providers. E.g. Topology)
+ *
+ * @deprecated Please use {@link org.opendaylight.controller.md.sal.binding.api.DataBroker#newReadOnlyTransaction()}
+ */
+ @Deprecated
+ @Override
+ public DataObject readOperationalData(InstanceIdentifier<? extends DataObject> path);
+
+ /**
+ * Register a data change listener for particular subtree.
+ *
+ * Callback is invoked each time data in subtree changes.
+ *
+ * @deprecated Please use {@link org.opendaylight.controller.md.sal.binding.api.DataBroker#registerDataChangeListener(org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType, InstanceIdentifier, org.opendaylight.controller.md.sal.binding.api.DataChangeListener, org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope)}
+ * which provides more fine-grained registration options.
+ */
+ @Deprecated
+ @Override
+ public ListenerRegistration<DataChangeListener> registerDataChangeListener(
+ InstanceIdentifier<? extends DataObject> path, DataChangeListener listener);
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api.data;
+
+import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ *
+ * @deprecated Replaced by {@link org.opendaylight.controller.md.sal.binding.api.DataChangeListener}
+ *
+ */
+@Deprecated
+public interface DataChangeListener extends
+ org.opendaylight.controller.md.sal.common.api.data.DataChangeListener<InstanceIdentifier<? extends DataObject>, DataObject> {
+
+ @Override
+ public void onDataChanged(DataChangeEvent<InstanceIdentifier<?>, DataObject> change);
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api.data;
+
+import java.util.EventListener;
+import java.util.concurrent.Future;
+
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.DataModification;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+/**
+ *
+ *
+ * @deprecated Replaced by more specific transaction types. Please use
+ * {@link org.opendaylight.controller.md.sal.binding.api.DataBroker#newReadOnlyTransaction(),
+ * {@link org.opendaylight.controller.md.sal.binding.api.DataBroker#newReadWriteTransaction()
+ * or
+ * {@link org.opendaylight.controller.md.sal.binding.api.DataBroker#newWriteOnlyTransaction().
+ *
+ *
+ */
+@Deprecated
+public interface DataModificationTransaction extends DataModification<InstanceIdentifier<? extends DataObject>, DataObject> {
+ /**
+ * Returns an unique identifier for transaction
+ *
+ */
+ @Override
+ Object getIdentifier();
+
+ /**
+ * Initiates a two-phase commit of candidate data.
+ *
+ * <p>
+ * The {@link Consumer} could initiate a commit of candidate data
+ *
+ * <p>
+ * The successful commit changes the state of the system and may affect
+ * several components.
+ *
+ * <p>
+ * The effects of successful commit of data are described in the
+ * specifications and YANG models describing the {@link Provider} components
+ * of controller. It is assumed that {@link Consumer} has an understanding
+ * of this changes.
+ *
+ *
+ * @see org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler for further information how two-phase commit is
+ * processed.
+ * @param store
+ * Identifier of the store, where commit should occur.
+ * @return Result of the commit, containing success information or list of
+ * encountered errors, if commit was not successful.
+ */
+ @Override
+ Future<RpcResult<TransactionStatus>> commit();
+
+ /**
+ * Register a listener for transaction
+ *
+ * @param listener
+ * @return
+ */
+ ListenerRegistration<DataTransactionListener> registerListener(DataTransactionListener listener);
+
+ /**
+ * Listener for transaction state changes
+ */
+ public interface DataTransactionListener extends EventListener {
+ /**
+ * Callback is invoked after each transaction status change.
+ *
+ * @param transaction Transaction
+ * @param status New status
+ */
+ void onStatusUpdated(DataModificationTransaction transaction,TransactionStatus status);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api.data;
+
+import org.opendaylight.controller.md.sal.common.api.data.DataProvisionService;
+import org.opendaylight.controller.md.sal.common.api.data.DataReader;
+import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * DataProviderService is common access point for {@link org.opendaylight.controller.sal.binding.api.BindingAwareProvider} providers
+ * to access data trees described by the YANG model.
+ *
+ * @deprecated Replaced by {@link org.opendaylight.controller.md.sal.common.api.data.AsyncConfigurationCommitCoordinator} service.
+ */
+@Deprecated
+public interface DataProviderService extends DataBrokerService, DataProvisionService<InstanceIdentifier<? extends DataObject>, DataObject> {
+ /**
+ * Registers a data reader for particular subtree of overal YANG data tree.
+ *
+ * Registered data reader is called if anyone tries to read data from
+ * paths which are nested to provided path.
+ *
+ * @param path Subpath which is handled by registered data reader
+ * @param reader Instance of reader which
+ * @return Registration object for reader. Invoking {@link Registration#close()} will unregister reader.
+ * @deprecated Data Reader contract is removed from capabilities of MD-SAL and is replaced by replaced by org.opendaylight.controller.sal.core.spi.data.DOMStore contract.
+ */
+ @Deprecated
+ Registration registerDataReader(InstanceIdentifier<? extends DataObject> path,DataReader<InstanceIdentifier<? extends DataObject>,DataObject> reader);
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api.data;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Future;
+
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.yangtools.concepts.Delegator;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * Synchronized wrapper for DataModificationTransaction.
+ *
+ * To get instance of synchronized wrapper use {@link #from(DataModificationTransaction)}
+ *
+ * @deprecated Replaced by more specific transaction types. Please use
+ * {@link org.opendaylight.controller.md.sal.binding.api.DataBroker#newReadOnlyTransaction(),
+ * {@link org.opendaylight.controller.md.sal.binding.api.DataBroker#newReadWriteTransaction()
+ * or
+ * {@link org.opendaylight.controller.md.sal.binding.api.DataBroker#newWriteOnlyTransaction().
+ *
+ */
+@Deprecated
+public final class SynchronizedTransaction implements DataModificationTransaction,Delegator<DataModificationTransaction> {
+
+ private final DataModificationTransaction delegate;
+
+ private SynchronizedTransaction(final DataModificationTransaction delegate) {
+ this.delegate = delegate;
+ }
+
+ /**
+ * Returns synchronized wrapper on supplied transaction.
+ *
+ * @param transaction Transaction for which synchronized wrapper should be created.
+ * @return Synchronized wrapper over transaction.
+ */
+ public static final SynchronizedTransaction from(final DataModificationTransaction transaction) {
+ Preconditions.checkArgument(transaction != null, "Transaction must not be null.");
+ if (transaction instanceof SynchronizedTransaction) {
+ return (SynchronizedTransaction) transaction;
+ }
+ return new SynchronizedTransaction(transaction);
+ }
+
+ @Override
+ public synchronized Map<InstanceIdentifier<? extends DataObject>, DataObject> getCreatedOperationalData() {
+ return delegate.getCreatedOperationalData();
+ }
+
+ @Override
+ public synchronized Map<InstanceIdentifier<? extends DataObject>, DataObject> getCreatedConfigurationData() {
+ return delegate.getCreatedConfigurationData();
+ }
+
+ @Override
+ public synchronized DataObject readOperationalData(final InstanceIdentifier<? extends DataObject> path) {
+ return delegate.readOperationalData(path);
+ }
+
+ @Override
+ public synchronized TransactionStatus getStatus() {
+ return delegate.getStatus();
+ }
+
+ @Override
+ public synchronized Map<InstanceIdentifier<? extends DataObject>, DataObject> getUpdatedOperationalData() {
+ return delegate.getUpdatedOperationalData();
+ }
+
+ @Override
+ public synchronized Object getIdentifier() {
+ return delegate.getIdentifier();
+ }
+
+ @Override
+ public synchronized DataObject readConfigurationData(final InstanceIdentifier<? extends DataObject> path) {
+ return delegate.readConfigurationData(path);
+ }
+
+ @Override
+ public synchronized Future<RpcResult<TransactionStatus>> commit() {
+ return delegate.commit();
+ }
+
+ @Override
+ public synchronized void putOperationalData(final InstanceIdentifier<? extends DataObject> path, final DataObject data) {
+ delegate.putOperationalData(path, data);
+ }
+
+ @Override
+ public synchronized void putConfigurationData(final InstanceIdentifier<? extends DataObject> path, final DataObject data) {
+ delegate.putConfigurationData(path, data);
+ }
+
+ @Override
+ public synchronized Map<InstanceIdentifier<? extends DataObject>, DataObject> getUpdatedConfigurationData() {
+ return delegate.getUpdatedConfigurationData();
+ }
+
+ @Override
+ public synchronized void removeOperationalData(final InstanceIdentifier<? extends DataObject> path) {
+ delegate.removeOperationalData(path);
+ }
+
+ @Override
+ public synchronized void removeConfigurationData(final InstanceIdentifier<? extends DataObject> path) {
+ delegate.removeConfigurationData(path);
+ }
+
+ @Override
+ public synchronized Set<InstanceIdentifier<? extends DataObject>> getRemovedConfigurationData() {
+ return delegate.getRemovedConfigurationData();
+ }
+
+ @Override
+ public synchronized Set<InstanceIdentifier<? extends DataObject>> getRemovedOperationalData() {
+ return delegate.getRemovedOperationalData();
+ }
+
+ @Override
+ public synchronized Map<InstanceIdentifier<? extends DataObject>, DataObject> getOriginalConfigurationData() {
+ return delegate.getOriginalConfigurationData();
+ }
+
+ @Override
+ public synchronized ListenerRegistration<DataTransactionListener> registerListener(final DataTransactionListener listener) {
+ return delegate.registerListener(listener);
+ }
+
+ @Override
+ public synchronized Map<InstanceIdentifier<? extends DataObject>, DataObject> getOriginalOperationalData() {
+ return delegate.getOriginalOperationalData();
+ }
+
+ @Override
+ public synchronized DataModificationTransaction getDelegate() {
+ return delegate;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((delegate == null) ? 0 : delegate.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()){
+ return false;
+ }
+ SynchronizedTransaction other = (SynchronizedTransaction) obj;
+ if (delegate == null) {
+ if (other.delegate != null) {
+ return false;
+ }
+ } else if (!delegate.equals(other.delegate)) {
+ return false;
+ }
+ return true;
+ }
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api.mount;
+
+import org.opendaylight.controller.sal.binding.api.NotificationService;
+import org.opendaylight.controller.sal.binding.api.RpcConsumerRegistry;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public interface MountInstance //
+ extends //
+ RpcConsumerRegistry, //
+ Identifiable<InstanceIdentifier<?>>, //
+ NotificationService, //
+ DataBrokerService {
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api.mount;
+
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
+import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
+
+/**
+ * Provider's version of Mount Point, this version allows access to MD-SAL
+ * services specific for this mountpoint and registration / provision of
+ * interfaces for mount point.
+ *
+ * @author ttkacik
+ *
+ */
+public interface MountProviderInstance //
+ extends //
+ MountInstance, //
+ DataProviderService, //
+ RpcProviderRegistry, //
+ NotificationProviderService {
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api.mount;
+
+import java.util.EventListener;
+
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Provider MountProviderService, this version allows access to MD-SAL services
+ * specific for this mountpoint and registration / provision of interfaces for
+ * mount point.
+ *
+ * @author ttkacik
+ *
+ */
+public interface MountProviderService extends MountService {
+
+ @Override
+ public MountProviderInstance getMountPoint(InstanceIdentifier<?> path);
+
+ MountProviderInstance createMountPoint(InstanceIdentifier<?> path);
+
+ MountProviderInstance createOrGetMountPoint(InstanceIdentifier<?> path);
+
+ ListenerRegistration<MountProvisionListener> registerProvisionListener(MountProvisionListener listener);
+
+ public interface MountProvisionListener extends EventListener {
+
+ void onMountPointCreated(InstanceIdentifier<?> path);
+
+ void onMountPointRemoved(InstanceIdentifier<?> path);
+
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api.mount;
+
+import org.opendaylight.controller.sal.binding.api.BindingAwareService;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public interface MountService extends BindingAwareService {
+
+ MountInstance getMountPoint(InstanceIdentifier<?> path);
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api;
+
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api.rpc;
+
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.binding.BaseIdentity;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+public final class RpcContextIdentifier implements Immutable{
+
+ public final Class<? extends RpcService> rpcService;
+ public final Class<? extends BaseIdentity> routingContext;
+
+ private RpcContextIdentifier(Class<? extends RpcService> rpcService, Class<? extends BaseIdentity> routingContext) {
+ super();
+ this.rpcService = rpcService;
+ this.routingContext = routingContext;
+ }
+
+ public Class<? extends RpcService> getRpcService() {
+ return rpcService;
+ }
+
+ public Class<? extends BaseIdentity> getRoutingContext() {
+ return routingContext;
+ }
+
+ public static final RpcContextIdentifier contextForGlobalRpc(Class<? extends RpcService> serviceType) {
+ return new RpcContextIdentifier(serviceType, null);
+ }
+
+ public static final RpcContextIdentifier contextFor(Class<? extends RpcService> serviceType,Class<? extends BaseIdentity> routingContext) {
+ return new RpcContextIdentifier(serviceType, routingContext);
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((routingContext == null) ? 0 : routingContext.hashCode());
+ result = prime * result + ((rpcService == null) ? 0 : rpcService.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ RpcContextIdentifier other = (RpcContextIdentifier) obj;
+ if (routingContext == null) {
+ if (other.routingContext != null)
+ return false;
+ } else if (!routingContext.equals(other.routingContext))
+ return false;
+ if (rpcService == null) {
+ if (other.rpcService != null)
+ return false;
+ } else if (!rpcService.equals(other.rpcService))
+ return false;
+ return true;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api.rpc;
+
+import java.util.Set;
+
+import org.opendaylight.controller.md.sal.common.api.routing.RouteChangePublisher;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration;
+import org.opendaylight.yangtools.yang.binding.BaseIdentity;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+/**
+ * RpcRouter is responsible for selecting RpcService based on provided routing
+ * context identifier {@link RpcRoutingTable#getContextIdentifier()} and path in
+ * overall data tree (@link {@link InstanceIdentifier}.
+ *
+ *
+ * @author Tony Tkacik <ttkacik@cisco.com>
+ *
+ * @param <T>
+ * Type of RpcService for which router provides routing information
+ * and route selection.
+ */
+public interface RpcRouter<T extends RpcService> extends //
+ RouteChangePublisher<Class<? extends BaseIdentity>, InstanceIdentifier<?>> {
+
+ /**
+ * Returns a type of RpcService which is served by this instance of router.
+ *
+ * @return type of RpcService which is served by this instance of router.
+ */
+ Class<T> getServiceType();
+
+
+ /**
+ * Returns a instance of T which is associated with this router instance
+ * and routes messages based on routing tables.
+ *
+ * @return type of RpcService which is served by this instance of router.
+ */
+ T getInvocationProxy();
+
+ /**
+ * Returns a routing table for particular route context
+ *
+ * @param routeContext
+ * @return Routing Table for particular route context.
+ */
+ <C extends BaseIdentity> RpcRoutingTable<C, T> getRoutingTable(Class<C> routeContext);
+
+ /**
+ * Returns an instance of RpcService which is responsible for processing
+ * particular path.
+ *
+ * @param context
+ * Rpc Routing Context
+ * @param path
+ * Instance Identifier which is used as a selector of instance.
+ * @return instance of RpcService which is responsible for processing
+ * particular path.
+ */
+ T getService(Class<? extends BaseIdentity> context, InstanceIdentifier<?> path);
+
+ /**
+ * Returns a default fallback instance of RpcService which is responsible
+ * for handling all unknown imports.
+ *
+ * @return default instance responsible for processing RPCs.
+ */
+ T getDefaultService();
+
+ Set<Class<? extends BaseIdentity>> getContexts();
+
+ RoutedRpcRegistration<T> addRoutedRpcImplementation(T service);
+
+ RpcRegistration<T> registerDefaultService(T service);
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api.rpc;
+
+import org.opendaylight.yangtools.yang.binding.BaseIdentity;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+public interface RpcRoutingContext<C extends BaseIdentity,S extends RpcService> {
+
+ Class<C> getContextType();
+ Class<S> getServiceType();
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.api.rpc;
+
+import java.util.Map;
+
+import org.opendaylight.controller.md.sal.common.api.routing.MutableRoutingTable;
+import org.opendaylight.yangtools.yang.binding.BaseIdentity;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+public interface RpcRoutingTable<C extends BaseIdentity, S extends RpcService> extends
+ MutableRoutingTable<Class<? extends C>, InstanceIdentifier<?>, S> {
+
+ Class<C> getIdentifier();
+
+ /**
+ * Updates route for particular path to specified instance of
+ * {@link RpcService}.
+ *
+ * @param path
+ * Path for which RpcService routing is to be updated
+ * @param service
+ * Instance of RpcService which is responsible for processing Rpc
+ * Requests.
+ */
+ void updateRoute(InstanceIdentifier<?> path, S service);
+
+ /**
+ * Deletes a route for particular path
+ *
+ * @param path
+ * Path for which
+ */
+ void removeRoute(InstanceIdentifier<?> path);
+
+ /**
+ *
+ */
+ S getRoute(InstanceIdentifier<?> nodeInstance);
+
+ /**
+ *
+ * @return
+ */
+ Map<InstanceIdentifier<?>, S> getRoutes();
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-parent</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>sal-binding-broker-impl</artifactId>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.javassist</groupId>
+ <artifactId>javassist</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-util</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-broker-impl</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-common-impl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-common-util</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-core-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>binding-generator-impl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>binding-data-codec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-impl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-model-util</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools.model</groupId>
+ <artifactId>ietf-inet-types</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-parser-impl</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools.model</groupId>
+ <artifactId>ietf-topology-l3-unicast-igp</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-test-model</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name>
+ <Export-Package>
+ org.opendaylight.controller.sal.binding.impl,
+ org.opendaylight.controller.md.sal.binding.impl,
+ org.opendaylight.controller.md.sal.binding.compat,
+ org.opendaylight.controller.md.sal.binding.spi,
+ org.opendaylight.controller.sal.binding.codegen.impl,
+ </Export-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>test-jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.jacoco</groupId>
+ <artifactId>jacoco-maven-plugin</artifactId>
+ <configuration>
+ <includes>
+ <include>org.opendaylight.controller.*</include>
+ </includes>
+ </configuration>
+ <executions>
+ <execution>
+ <id>pre-test</id>
+ <goals>
+ <goal>prepare-agent</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>post-test</id>
+ <goals>
+ <goal>report</goal>
+ </goals>
+ <phase>test</phase>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>generate-sources</goal>
+ </goals>
+ <configuration>
+ <codeGenerators>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator</codeGeneratorClass>
+ <outputBaseDir>${jmxGeneratorPath}</outputBaseDir>
+ <additionalConfiguration>
+ <namespaceToPackage1>urn:opendaylight:params:xml:ns:yang:controller==org.opendaylight.controller.config.yang</namespaceToPackage1>
+ </additionalConfiguration>
+ </generator>
+
+ <generator>
+ <codeGeneratorClass>org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl</codeGeneratorClass>
+ <outputBaseDir>${salGeneratorPath}</outputBaseDir>
+ </generator>
+
+ <generator>
+ <codeGeneratorClass>org.opendaylight.yangtools.yang.unified.doc.generator.maven.DocumentationGeneratorImpl</codeGeneratorClass>
+ <outputBaseDir>target/site/models</outputBaseDir>
+ </generator>
+ </codeGenerators>
+ <inspectDependencies>true</inspectDependencies>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ <scm>
+ <connection>scm:git:http://git.opendaylight.org/gerrit/controller.git</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+ <tag>HEAD</tag>
+ <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
+ </scm>
+</project>
--- /dev/null
+/xtend-gen
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.compat;
+
+import org.opendaylight.controller.sal.binding.api.NotificationListener;
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * Abstract implementation of {@link NotificationListenerRegistration}.
+ *
+ * @param <T> Notification type
+ */
+abstract class AbstractNotificationListenerRegistration<T extends Notification> extends AbstractListenerRegistration<NotificationListener<T>> implements NotificationListenerRegistration<T> {
+ private final Class<? extends Notification> type;
+
+ protected AbstractNotificationListenerRegistration(final Class<? extends Notification> type, final NotificationListener<T> listener) {
+ super(listener);
+ this.type = Preconditions.checkNotNull(type);
+ }
+
+ @Override
+ public Class<? extends Notification> getType() {
+ return type;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void notify(final Notification notification) {
+ if (!isClosed()) {
+ getInstance().onNotification((T)notification);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.compat;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.sal.binding.api.NotificationListener;
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+/**
+ * An aggregated listener registration. This is a result of registering an invoker which can handle multiple
+ * interfaces at the same time. In order to support correct delivery, we need to maintain per-type registrations
+ * which get squashed if a notification which implements multiple interfaces is encountered.
+ *
+ * We take care of that by implementing alternate {@link #hashCode()}/{@link #equals(Object)}, which resolve
+ * to the backing aggregator.
+ *
+ * @param <N> Notification type
+ * @param <A> Aggregator type
+ */
+abstract class AggregatedNotificationListenerRegistration<N extends Notification, A> extends AbstractNotificationListenerRegistration<N> {
+ private final A aggregator;
+
+ protected AggregatedNotificationListenerRegistration(final Class<? extends Notification> type, final NotificationListener<N> listener, final A aggregator) {
+ super(type, listener);
+ this.aggregator = Preconditions.checkNotNull(aggregator);
+ }
+
+ protected A getAggregator() {
+ return aggregator;
+ }
+
+ @Override
+ public int hashCode() {
+ return aggregator.hashCode();
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!this.getClass().equals(obj.getClass())) {
+ return false;
+ }
+
+ return aggregator.equals(((AggregatedNotificationListenerRegistration<?, ?>)obj).aggregator);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.compat;
+
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableSet;
+import java.util.HashMap;
+import java.util.Map;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMRpcProviderServiceAdapter;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import org.opendaylight.yangtools.yang.binding.BaseIdentity;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+final class CompositeRoutedRpcRegistration<T extends RpcService> implements RoutedRpcRegistration<T> {
+
+ private final Class<T> type;
+ private final T instance;
+ private final BindingDOMRpcProviderServiceAdapter adapter;
+ private final Map<InstanceIdentifier<?>, ObjectRegistration<T>> registrations = new HashMap<>(2);
+
+ CompositeRoutedRpcRegistration(final Class<T> type, final T impl, final BindingDOMRpcProviderServiceAdapter providerAdapter) {
+ this.type = type;
+ this.instance = impl;
+ this.adapter = providerAdapter;
+ }
+
+ @Override
+ public Class<T> getServiceType() {
+ return type;
+ }
+
+ @Override
+ public T getInstance() {
+ return instance;
+ }
+
+ @Deprecated
+ @Override
+ public void registerInstance(final Class<? extends BaseIdentity> context, final InstanceIdentifier<?> path) {
+ registerPath(context, path);
+ }
+
+ @Override
+ public synchronized void registerPath(final Class<? extends BaseIdentity> context, final InstanceIdentifier<?> path) {
+ if(!registrations.containsKey(path)) {
+ registrations.put(path, adapter.registerRpcImplementation(type, instance, ImmutableSet.<InstanceIdentifier<?>>of(path)));
+ }
+ }
+
+
+ @Override
+ @Deprecated
+ public void unregisterInstance(final Class<? extends BaseIdentity> context, final InstanceIdentifier<?> path) {
+ unregisterPath(context, path);
+ }
+
+ @Override
+ public synchronized void unregisterPath(final Class<? extends BaseIdentity> context, final InstanceIdentifier<?> path) {
+ final ObjectRegistration<T> reg = registrations.remove(path);
+ if(reg != null) {
+ try {
+ reg.close();
+ } catch (final Exception e) {
+ // FIXME: Once we have proper subclass of ObjectRegistrationo
+ throw Throwables.propagate(e);
+ }
+ }
+ }
+
+ @Override
+ public synchronized void close() {
+ try {
+ for(final ObjectRegistration<T> reg : registrations.values()) {
+ reg.close();
+ }
+ } catch (final Exception e) {
+ throw Throwables.propagate(e);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.compat;
+
+import com.google.common.base.Throwables;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+final class DelegatedRootRpcRegistration<T extends RpcService> implements RpcRegistration<T> {
+
+ private final ObjectRegistration<T> delegate;
+ private final Class<T> type;
+
+ public DelegatedRootRpcRegistration(final Class<T> type,final ObjectRegistration<T> impl) {
+ this.delegate = impl;
+ this.type = type;
+ }
+
+
+ @Override
+ public void close() {
+ try {
+ // FIXME: Should use more specific registration object.
+ delegate.close();
+ } catch (final Exception e) {
+ throw Throwables.propagate(e);
+ }
+ }
+
+ @Override
+ public T getInstance() {
+ return delegate.getInstance();
+ }
+
+ @Override
+ public Class<T> getServiceType() {
+ return type;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.compat;
+
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.binding.impl.LazySerializedDOMNotification;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListener;
+import org.opendaylight.controller.sal.binding.api.NotificationListener;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+final class FunctionalNotificationListenerAdapter<N extends Notification> implements DOMNotificationListener {
+
+ private final BindingNormalizedNodeSerializer codec;
+ private final NotificationListener<N> delegate;
+ private final Class<N> type;
+
+ public FunctionalNotificationListenerAdapter(final BindingNormalizedNodeSerializer codec, final Class<N> type, final NotificationListener<N> delegate) {
+ this.codec = codec;
+ this.type = type;
+ this.delegate = delegate;
+ }
+
+ @Override
+ public void onNotification(@Nonnull final DOMNotification notification) {
+ delegate.onNotification( type.cast(deserialize(notification)));
+ }
+
+ private Notification deserialize(final DOMNotification notification) {
+ if(notification instanceof LazySerializedDOMNotification) {
+ return ((LazySerializedDOMNotification) notification).getBindingData();
+ }
+ return codec.fromNormalizedNodeNotification(notification.getType(), notification.getBody());
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.compat;
+
+import java.util.concurrent.ExecutorService;
+import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
+import org.opendaylight.controller.md.sal.binding.api.NotificationService;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.Notification;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class HeliumNotificationProviderServiceAdapter extends HeliumNotificationServiceAdapter implements NotificationProviderService, AutoCloseable {
+ private static final Logger LOG = LoggerFactory.getLogger(HeliumNotificationProviderServiceAdapter.class);
+
+ private final NotificationPublishService notificationPublishService;
+
+ public HeliumNotificationProviderServiceAdapter(NotificationPublishService notificationPublishService,
+ NotificationService notificationService) {
+ super(notificationService);
+ this.notificationPublishService = notificationPublishService;
+ }
+
+ @Override
+ public void publish(final Notification notification) {
+ try {
+ notificationPublishService.putNotification(notification);
+ } catch (InterruptedException e) {
+ LOG.error("Notification publication was interupted: " + e);
+ }
+ }
+
+ @Override
+ public void publish(final Notification notification, final ExecutorService executor) {
+ try {
+ notificationPublishService.putNotification(notification);
+ } catch (InterruptedException e) {
+ LOG.error("Notification publication was interupted: " + e);
+ }
+ }
+
+ @Override
+ public ListenerRegistration<NotificationInterestListener> registerInterestListener(
+ NotificationInterestListener interestListener) {
+ throw new UnsupportedOperationException("InterestListener is not supported.");
+ }
+
+ @Override
+ public void close() throws Exception {
+
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.compat;
+
+import com.google.common.collect.Sets;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Set;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMNotificationPublishServiceAdapter;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMNotificationServiceAdapter;
+import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService;
+import org.opendaylight.controller.md.sal.dom.spi.DOMNotificationSubscriptionListener;
+import org.opendaylight.controller.md.sal.dom.spi.DOMNotificationSubscriptionListenerRegistry;
+import org.opendaylight.controller.sal.binding.api.NotificationListener;
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.util.ListenerRegistry;
+import org.opendaylight.yangtools.yang.binding.Notification;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class HeliumNotificationProviderServiceWithInterestListeners extends HeliumNotificationProviderServiceAdapter {
+
+ private static final Logger LOG = LoggerFactory.getLogger(HeliumNotificationProviderServiceWithInterestListeners.class);
+
+ private final ListenerRegistry<NotificationInterestListener> interestListeners = ListenerRegistry.create();
+ private final ListenerRegistration<Listener> domListener;
+ private final DOMNotificationService domService;
+ private final BindingToNormalizedNodeCodec codec;
+
+ public HeliumNotificationProviderServiceWithInterestListeners(
+ final BindingDOMNotificationPublishServiceAdapter publishService, final BindingDOMNotificationServiceAdapter listenService, final DOMNotificationSubscriptionListenerRegistry registry) {
+ super(publishService, listenService);
+ this.codec = publishService.getCodecRegistry();
+ this.domListener = registry.registerSubscriptionListener(new Listener());
+ this.domService = listenService.getDomService();
+ }
+
+ @Override
+ public ListenerRegistration<NotificationInterestListener> registerInterestListener(
+ final NotificationInterestListener listener) {
+ notifyListener(listener, translate(domListener.getInstance().getAllObserved()));
+ return interestListeners.register(listener);
+ }
+
+ private Set<Class<? extends Notification>> translate(final Set<SchemaPath> added) {
+ return codec.getNotificationClasses(added);
+ }
+
+ private void notifyAllListeners(final Set<SchemaPath> added) {
+ final Iterator<ListenerRegistration<NotificationInterestListener>> listeners = interestListeners.iterator();
+ if(listeners.hasNext()) {
+ final Set<Class<? extends Notification>> baEvent = translate(added);
+ while(listeners.hasNext()) {
+ final NotificationInterestListener listenerRef = listeners.next().getInstance();
+ try {
+ notifyListener(listenerRef,baEvent);
+ } catch (final Exception e) {
+ LOG.warn("Unhandled exception during invoking listener {}",e, listenerRef);
+ }
+ }
+ }
+ }
+
+ @Override
+ public <T extends Notification> ListenerRegistration<NotificationListener<T>> registerNotificationListener(
+ final Class<T> type, final NotificationListener<T> listener) {
+
+ final FunctionalNotificationListenerAdapter<T> adapter = new FunctionalNotificationListenerAdapter<>(codec, type, listener);
+ final SchemaPath domType = SchemaPath.create(true, BindingReflections.findQName(type));
+ final ListenerRegistration<?> domReg = domService.registerNotificationListener(adapter, domType);
+ return new AbstractListenerRegistration<NotificationListener<T>>(listener) {
+ @Override
+ protected void removeRegistration() {
+ domReg.close();
+ }
+
+ };
+ }
+
+ private void notifyListener(final NotificationInterestListener listener, final Set<Class<? extends Notification>> baEvent) {
+ for(final Class<? extends Notification> event: baEvent) {
+ listener.onNotificationSubscribtion(event);
+ }
+ }
+
+ private final class Listener implements DOMNotificationSubscriptionListener {
+
+ private volatile Set<SchemaPath> allObserved = Collections.emptySet();
+
+ @Override
+ public void onSubscriptionChanged(final Set<SchemaPath> currentTypes) {
+ final Set<SchemaPath> added = Sets.difference(currentTypes, allObserved).immutableCopy();
+ notifyAllListeners(added);
+ allObserved = Sets.union(allObserved, added).immutableCopy();
+ }
+
+ Set<SchemaPath> getAllObserved() {
+ return allObserved;
+ }
+ }
+
+ @Override
+ public void close() throws Exception {
+ super.close();
+ domListener.close();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.compat;
+
+import org.opendaylight.controller.md.sal.binding.api.NotificationService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.Notification;
+import org.opendaylight.yangtools.yang.binding.NotificationListener;
+
+public class HeliumNotificationServiceAdapter implements org.opendaylight.controller.sal.binding.api.NotificationService, AutoCloseable {
+
+ private final NotificationService notificationService;
+
+ public HeliumNotificationServiceAdapter(NotificationService notificationService) {
+ this.notificationService = notificationService;
+ }
+
+ @Override
+ public <T extends Notification> ListenerRegistration<org.opendaylight.controller.sal.binding.api.NotificationListener<T>> registerNotificationListener(
+ final Class<T> notificationType, final org.opendaylight.controller.sal.binding.api.NotificationListener<T> listener) {
+ throw new UnsupportedOperationException("Not supported type of listener.");
+ }
+
+ @Override
+ public ListenerRegistration<NotificationListener> registerNotificationListener(
+ final NotificationListener listener) {
+ return notificationService.registerNotificationListener(listener);
+ }
+
+ @Override
+ public void close() throws Exception {
+
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.compat;
+
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMRpcProviderServiceAdapter;
+import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration;
+import org.opendaylight.controller.sal.binding.api.RpcConsumerRegistry;
+import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
+import org.opendaylight.controller.sal.binding.api.rpc.RpcContextIdentifier;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+public class HeliumRpcProviderRegistry implements RpcProviderRegistry {
+
+ private final RpcConsumerRegistry consumerRegistry;
+ private final BindingDOMRpcProviderServiceAdapter providerAdapter;
+
+ public HeliumRpcProviderRegistry(final RpcConsumerRegistry consumerRegistry,
+ final BindingDOMRpcProviderServiceAdapter providerAdapter) {
+ this.consumerRegistry = consumerRegistry;
+ this.providerAdapter = providerAdapter;
+ }
+
+ @Override
+ public <T extends RpcService> RoutedRpcRegistration<T> addRoutedRpcImplementation(final Class<T> type, final T impl)
+ throws IllegalStateException {
+ return new CompositeRoutedRpcRegistration<>(type,impl,providerAdapter);
+ }
+
+ @Override
+ public <T extends RpcService> RpcRegistration<T> addRpcImplementation(final Class<T> type, final T impl)
+ throws IllegalStateException {
+ final ObjectRegistration<T> reg = providerAdapter.registerRpcImplementation(type, impl);
+ return new DelegatedRootRpcRegistration<>(type,reg);
+ }
+
+ @Override
+ public <T extends RpcService> T getRpcService(final Class<T> type) {
+ return consumerRegistry.getRpcService(type);
+ }
+
+ @Override
+ public <L extends RouteChangeListener<RpcContextIdentifier, InstanceIdentifier<?>>> ListenerRegistration<L> registerRouteChangeListener(
+ final L arg0) {
+ // FIXME: Implement this only if necessary
+ return null;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.compat;
+
+import com.google.common.base.Function;
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutionException;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.RegistrationListener;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler;
+import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandlerRegistration;
+import org.opendaylight.controller.md.sal.common.api.data.DataReader;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.impl.service.AbstractDataTransaction;
+import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
+import org.opendaylight.controller.sal.binding.codegen.impl.SingletonHolder;
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
+import org.opendaylight.yangtools.concepts.Delegator;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.util.ListenerRegistry;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Deprecated
+public class HydrogenDataBrokerAdapter implements DataProviderService, AutoCloseable {
+
+ private static final Logger LOG = LoggerFactory.getLogger(HydrogenDataBrokerAdapter.class);
+
+ private final ConcurrentMap<InstanceIdentifier<?>, CommitHandlerRegistrationImpl> commitHandlers =
+ new ConcurrentHashMap<>();
+ private final ListeningExecutorService executorService = SingletonHolder.getDefaultCommitExecutor();
+
+ private final DataBroker delegate;
+
+ public HydrogenDataBrokerAdapter(final DataBroker dataBroker) {
+ delegate = dataBroker;
+ LOG.info("ForwardedBackwardsCompatibleBroker started.");
+ }
+
+ @Override
+ public DataModificationTransaction beginTransaction() {
+ return new ForwardedBackwardsCompatibleTransacion(delegate.newReadWriteTransaction());
+ }
+
+ @Override
+ public DataObject readConfigurationData(final InstanceIdentifier<? extends DataObject> path) {
+ final DataModificationTransaction tx = beginTransaction();
+ return tx.readConfigurationData(path);
+ }
+
+ @Override
+ public DataObject readOperationalData(final InstanceIdentifier<? extends DataObject> path) {
+ final DataModificationTransaction tx = beginTransaction();
+ return tx.readOperationalData(path);
+ }
+
+ @Override
+ public Registration registerCommitHandler(
+ final InstanceIdentifier<? extends DataObject> path,
+ final DataCommitHandler<InstanceIdentifier<? extends DataObject>, DataObject> commitHandler) {
+ final CommitHandlerRegistrationImpl reg = new CommitHandlerRegistrationImpl(path, commitHandler);
+ commitHandlers.put(path, reg);
+ return reg;
+ }
+
+ @Override
+ @Deprecated
+ public ListenerRegistration<RegistrationListener<DataCommitHandlerRegistration<InstanceIdentifier<? extends DataObject>, DataObject>>> registerCommitHandlerListener(
+ final RegistrationListener<DataCommitHandlerRegistration<InstanceIdentifier<? extends DataObject>, DataObject>> commitHandlerListener) {
+ throw new UnsupportedOperationException("Not supported contract.");
+ }
+
+ @Override
+ public ListenerRegistration<DataChangeListener> registerDataChangeListener(
+ final InstanceIdentifier<? extends DataObject> path, final DataChangeListener listener) {
+
+
+ final org.opendaylight.controller.md.sal.binding.api.DataChangeListener asyncOperListener = new BackwardsCompatibleOperationalDataChangeInvoker(listener);
+ final org.opendaylight.controller.md.sal.binding.api.DataChangeListener asyncCfgListener = new BackwardsCompatibleConfigurationDataChangeInvoker(listener);
+
+ final ListenerRegistration<org.opendaylight.controller.md.sal.binding.api.DataChangeListener> cfgReg = delegate.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION, path, asyncCfgListener, DataChangeScope.SUBTREE);
+ final ListenerRegistration<org.opendaylight.controller.md.sal.binding.api.DataChangeListener> operReg = delegate.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL, path, asyncOperListener, DataChangeScope.SUBTREE);
+
+ return new LegacyListenerRegistration(listener,cfgReg,operReg);
+ }
+
+ @Override
+ public Registration registerDataReader(
+ final InstanceIdentifier<? extends DataObject> path,
+ final DataReader<InstanceIdentifier<? extends DataObject>, DataObject> reader) {
+ throw new UnsupportedOperationException("Data reader contract is not supported.");
+ }
+
+ public ListenableFuture<RpcResult<TransactionStatus>> commit(final ForwardedBackwardsCompatibleTransacion tx) {
+
+ final List<DataCommitTransaction<InstanceIdentifier<? extends DataObject>, DataObject>> subTrans = new ArrayList<>();
+ LOG.debug("Tx: {} Submitted.",tx.getIdentifier());
+ final ListenableFuture<Boolean> requestCommit = executorService.submit(new Callable<Boolean>() {
+
+ @Override
+ public Boolean call() throws Exception {
+ try {
+ for (final CommitHandlerRegistrationImpl handler : commitHandlers.values()) {
+
+ final DataCommitTransaction<InstanceIdentifier<? extends DataObject>, DataObject> subTx = handler
+ .getInstance().requestCommit(tx);
+ subTrans.add(subTx);
+ }
+ } catch (final Exception e) {
+ LOG.error("Tx: {} Rollback.",tx.getIdentifier(),e);
+ for (final DataCommitTransaction<InstanceIdentifier<? extends DataObject>, DataObject> subTx : subTrans) {
+ subTx.rollback();
+ }
+ return false;
+ }
+ LOG.debug("Tx: {} Can Commit True.",tx.getIdentifier());
+ return true;
+ }
+
+ });
+
+ final ListenableFuture<RpcResult<TransactionStatus>> dataStoreCommit = Futures.transform(requestCommit, new AsyncFunction<Boolean, RpcResult<TransactionStatus>>() {
+
+ @Override
+ public ListenableFuture<RpcResult<TransactionStatus>> apply(final Boolean requestCommitSuccess) throws Exception {
+ if(requestCommitSuccess) {
+ return AbstractDataTransaction.convertToLegacyCommitFuture(tx.delegate.submit());
+ }
+ return Futures.immediateFuture(RpcResultBuilder.<TransactionStatus>failed().withResult(TransactionStatus.FAILED).build());
+ }
+ });
+
+ return Futures.transform(dataStoreCommit, new Function<RpcResult<TransactionStatus>,RpcResult<TransactionStatus>>() {
+ @Override
+ public RpcResult<TransactionStatus> apply(final RpcResult<TransactionStatus> input) {
+ if(input.isSuccessful()) {
+ for(final DataCommitTransaction<InstanceIdentifier<? extends DataObject>, DataObject> subTx : subTrans ) {
+ subTx.finish();
+ }
+ } else {
+ LOG.error("Tx: {} Rollback - Datastore commit failed.",tx.getIdentifier());
+ for(final DataCommitTransaction<InstanceIdentifier<? extends DataObject>, DataObject> subTx : subTrans ) {
+ subTx.rollback();
+ }
+ }
+ return input;
+ }
+ });
+ }
+
+ @Deprecated
+ private class ForwardedBackwardsCompatibleTransacion implements DataModificationTransaction {
+
+ private final ListenerRegistry<DataTransactionListener> listeners = ListenerRegistry.create();
+ private final Map<InstanceIdentifier<? extends DataObject>, DataObject> updated = new HashMap<>();
+ private final Map<InstanceIdentifier<? extends DataObject>, DataObject> created = new HashMap<>();
+ private final Set<InstanceIdentifier<? extends DataObject>> removed = new HashSet<>();
+ private final Map<InstanceIdentifier<? extends DataObject>, DataObject> original = new HashMap<>();
+ private TransactionStatus status = TransactionStatus.NEW;
+
+ private final Set<InstanceIdentifier<? extends DataObject>> posponedRemovedOperational = new HashSet<>();
+ private final Set<InstanceIdentifier<? extends DataObject>> posponedRemovedConfiguration = new HashSet<>();
+
+ private final ReadWriteTransaction delegate;
+
+
+ @Override
+ public final TransactionStatus getStatus() {
+ return status;
+ }
+
+ protected ForwardedBackwardsCompatibleTransacion(final ReadWriteTransaction delegate) {
+ this.delegate = delegate;
+ LOG.debug("Tx {} allocated.",getIdentifier());
+ }
+
+ @Override
+ public void putOperationalData(final InstanceIdentifier<? extends DataObject> path, final DataObject data) {
+ final boolean previouslyRemoved = posponedRemovedOperational.remove(path);
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ final InstanceIdentifier<DataObject> castedPath = (InstanceIdentifier) path;
+ if(previouslyRemoved) {
+ delegate.put(LogicalDatastoreType.OPERATIONAL, castedPath, data,true);
+ } else {
+ delegate.merge(LogicalDatastoreType.OPERATIONAL, castedPath, data,true);
+ }
+ }
+
+ @Override
+ public void putConfigurationData(final InstanceIdentifier<? extends DataObject> path, final DataObject data) {
+ final boolean previouslyRemoved = posponedRemovedConfiguration.remove(path);
+ final DataObject originalObj = readConfigurationData(path);
+ if (originalObj != null) {
+ original.put(path, originalObj);
+
+ } else {
+ created.put(path, data);
+ }
+ updated.put(path, data);
+ @SuppressWarnings({"rawtypes","unchecked"})
+ final InstanceIdentifier<DataObject> castedPath = (InstanceIdentifier) path;
+ if(previouslyRemoved) {
+ delegate.put(LogicalDatastoreType.CONFIGURATION, castedPath, data,true);
+ } else {
+ delegate.merge(LogicalDatastoreType.CONFIGURATION, castedPath, data,true);
+ }
+ }
+
+ @Override
+ public void removeOperationalData(final InstanceIdentifier<? extends DataObject> path) {
+ posponedRemovedOperational.add(path);
+ }
+
+ @Override
+ public void removeConfigurationData(final InstanceIdentifier<? extends DataObject> path) {
+ posponedRemovedConfiguration.add(path);
+ }
+
+ @Override
+ public Map<InstanceIdentifier<? extends DataObject>, DataObject> getCreatedOperationalData() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public Map<InstanceIdentifier<? extends DataObject>, DataObject> getCreatedConfigurationData() {
+ return created;
+ }
+
+ @Override
+ public Map<InstanceIdentifier<? extends DataObject>, DataObject> getUpdatedOperationalData() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public Map<InstanceIdentifier<? extends DataObject>, DataObject> getUpdatedConfigurationData() {
+ return updated;
+ }
+
+ @Override
+ public Set<InstanceIdentifier<? extends DataObject>> getRemovedConfigurationData() {
+ return removed;
+ }
+
+ @Override
+ public Set<InstanceIdentifier<? extends DataObject>> getRemovedOperationalData() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Map<InstanceIdentifier<? extends DataObject>, DataObject> getOriginalConfigurationData() {
+ return original;
+ }
+
+ @Override
+ public Map<InstanceIdentifier<? extends DataObject>, DataObject> getOriginalOperationalData() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public DataObject readOperationalData(final InstanceIdentifier<? extends DataObject> path) {
+ try {
+ return delegate.read(LogicalDatastoreType.OPERATIONAL, path).get().orNull();
+ } catch (InterruptedException | ExecutionException e) {
+ LOG.error("Read of {} failed.", path,e);
+ return null;
+ }
+ }
+
+ @Override
+ public DataObject readConfigurationData(final InstanceIdentifier<? extends DataObject> path) {
+ try {
+ return delegate.read(LogicalDatastoreType.CONFIGURATION, path).get().orNull();
+ } catch (InterruptedException | ExecutionException e) {
+ LOG.error("Read of {} failed.", path,e);
+ return null;
+ }
+ }
+
+ private void changeStatus(final TransactionStatus status) {
+ LOG.trace("Transaction {} changed status to {}", getIdentifier(), status);
+ this.status = status;
+
+ for(final ListenerRegistration<DataTransactionListener> listener : listeners) {
+ try {
+ listener.getInstance().onStatusUpdated(this, status);
+ } catch (final Exception e) {
+ LOG.error("Error during invoking transaction listener {}",listener.getInstance(),e);
+ }
+ }
+ }
+
+ @Override
+ public ListenableFuture<RpcResult<TransactionStatus>> commit() {
+
+ for(final InstanceIdentifier<? extends DataObject> path : posponedRemovedConfiguration) {
+ delegate.delete(LogicalDatastoreType.CONFIGURATION, path);
+ }
+
+ for(final InstanceIdentifier<? extends DataObject> path : posponedRemovedOperational) {
+ delegate.delete(LogicalDatastoreType.OPERATIONAL, path);
+ }
+
+ changeStatus(TransactionStatus.SUBMITED);
+
+ final ListenableFuture<RpcResult<TransactionStatus>> f = HydrogenDataBrokerAdapter.this.commit(this);
+
+ Futures.addCallback(f, new FutureCallback<RpcResult<TransactionStatus>>() {
+ @Override
+ public void onSuccess(final RpcResult<TransactionStatus> result) {
+ changeStatus(result.getResult());
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ LOG.error("Transaction {} failed to complete", getIdentifier(), t);
+ changeStatus(TransactionStatus.FAILED);
+ }
+ });
+
+ return f;
+ }
+
+ @Override
+ public ListenerRegistration<DataTransactionListener> registerListener(final DataTransactionListener listener) {
+ return listeners.register(listener);
+ }
+
+ @Override
+ public Object getIdentifier() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ }
+
+ private class CommitHandlerRegistrationImpl extends
+ AbstractObjectRegistration<DataCommitHandler<InstanceIdentifier<? extends DataObject>, DataObject>> {
+
+ private final InstanceIdentifier<? extends DataObject> path;
+
+ public CommitHandlerRegistrationImpl(final InstanceIdentifier<? extends DataObject> path,
+ final DataCommitHandler<InstanceIdentifier<? extends DataObject>, DataObject> commitHandler) {
+ super(commitHandler);
+ this.path = path;
+ }
+
+ @Override
+ protected void removeRegistration() {
+ commitHandlers.remove(path, this);
+ }
+
+ }
+
+
+ private static final class LegacyListenerRegistration implements ListenerRegistration<DataChangeListener> {
+
+ private final DataChangeListener instance;
+ private final ListenerRegistration<org.opendaylight.controller.md.sal.binding.api.DataChangeListener> cfgReg;
+ private final ListenerRegistration<org.opendaylight.controller.md.sal.binding.api.DataChangeListener> operReg;
+
+ public LegacyListenerRegistration(final DataChangeListener listener,
+ final ListenerRegistration<org.opendaylight.controller.md.sal.binding.api.DataChangeListener> cfgReg,
+ final ListenerRegistration<org.opendaylight.controller.md.sal.binding.api.DataChangeListener> operReg) {
+ this.instance = listener;
+ this.cfgReg = cfgReg;
+ this.operReg = operReg;
+ }
+
+ @Override
+ public DataChangeListener getInstance() {
+ return instance;
+ }
+
+ @Override
+ public void close() {
+ cfgReg.close();
+ operReg.close();
+ }
+
+ }
+
+ private static class BackwardsCompatibleOperationalDataChangeInvoker implements org.opendaylight.controller.md.sal.binding.api.DataChangeListener, Delegator<DataChangeListener> {
+
+ private final org.opendaylight.controller.md.sal.common.api.data.DataChangeListener<?,?> delegate;
+
+
+ public BackwardsCompatibleOperationalDataChangeInvoker(final DataChangeListener listener) {
+ this.delegate = listener;
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ @Override
+ public void onDataChanged(final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+
+ final DataChangeEvent legacyChange = HydrogenDataChangeEvent.createOperational(change);
+ delegate.onDataChanged(legacyChange);
+
+ }
+
+ @Override
+ public DataChangeListener getDelegate() {
+ return (DataChangeListener) delegate;
+ }
+
+ }
+
+ private static class BackwardsCompatibleConfigurationDataChangeInvoker implements org.opendaylight.controller.md.sal.binding.api.DataChangeListener, Delegator<DataChangeListener> {
+ private final org.opendaylight.controller.md.sal.common.api.data.DataChangeListener<?,?> delegate;
+
+ public BackwardsCompatibleConfigurationDataChangeInvoker(final DataChangeListener listener) {
+ this.delegate = listener;
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ @Override
+ public void onDataChanged(final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+
+ final DataChangeEvent legacyChange = HydrogenDataChangeEvent.createConfiguration(change);
+
+ delegate.onDataChanged(legacyChange);
+
+ }
+
+ @Override
+ public DataChangeListener getDelegate() {
+ return (DataChangeListener) delegate;
+ }
+
+ }
+
+ @Override
+ public void close() throws Exception {
+ // TODO Auto-generated method stub
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.compat;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+@Deprecated
+public abstract class HydrogenDataChangeEvent implements
+ DataChangeEvent<InstanceIdentifier<? extends DataObject>, DataObject> {
+
+ private HydrogenDataChangeEvent() {
+ }
+
+ public static final DataChangeEvent<InstanceIdentifier<?>, DataObject> createOperational(
+ final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+ return new OperationalChangeEvent(change);
+ }
+
+ public static final DataChangeEvent<InstanceIdentifier<?>, DataObject> createConfiguration(
+ final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+ return new ConfigurationChangeEvent(change);
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, DataObject> getCreatedOperationalData() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, DataObject> getCreatedConfigurationData() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, DataObject> getUpdatedOperationalData() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, DataObject> getUpdatedConfigurationData() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public Set<InstanceIdentifier<?>> getRemovedConfigurationData() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Set<InstanceIdentifier<?>> getRemovedOperationalData() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, DataObject> getOriginalConfigurationData() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, DataObject> getOriginalOperationalData() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public DataObject getOriginalConfigurationSubtree() {
+ return null;
+ }
+
+ @Override
+ public DataObject getOriginalOperationalSubtree() {
+ return null;
+ }
+
+ @Override
+ public DataObject getUpdatedConfigurationSubtree() {
+ return null;
+ }
+
+ @Override
+ public DataObject getUpdatedOperationalSubtree() {
+ return null;
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ private static final class OperationalChangeEvent extends HydrogenDataChangeEvent {
+
+ private final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> delegate;
+ private Map<InstanceIdentifier<?>, DataObject> updatedCache;
+
+ public OperationalChangeEvent(final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+ this.delegate = change;
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, DataObject> getCreatedOperationalData() {
+ return delegate.getCreatedData();
+ }
+
+ @Override
+ public Set<InstanceIdentifier<?>> getRemovedOperationalData() {
+ return delegate.getRemovedPaths();
+ }
+
+ @Override
+ public DataObject getOriginalOperationalSubtree() {
+ return delegate.getOriginalSubtree();
+ }
+
+ @Override
+ public DataObject getUpdatedOperationalSubtree() {
+ return delegate.getUpdatedSubtree();
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, DataObject> getOriginalOperationalData() {
+ return (Map) delegate.getOriginalData();
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, DataObject> getUpdatedOperationalData() {
+ if(updatedCache == null) {
+ final Map<InstanceIdentifier<?>, DataObject> created = delegate.getCreatedData();
+ final Map<InstanceIdentifier<?>, DataObject> updated = delegate.getUpdatedData();
+ final Map<InstanceIdentifier<?>, DataObject> updatedComposite =
+ new HashMap<>(created.size() + updated.size());
+ updatedComposite.putAll(created);
+ updatedComposite.putAll(updated);
+ updatedCache = Collections.unmodifiableMap(updatedComposite);
+ }
+ return updatedCache;
+ }
+
+ @Override
+ public String toString() {
+ return "OperationalChangeEvent [delegate=" + delegate + "]";
+ }
+
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ private static final class ConfigurationChangeEvent extends HydrogenDataChangeEvent {
+
+ private final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> delegate;
+ private Map<InstanceIdentifier<?>, DataObject> updatedCache;
+
+ public ConfigurationChangeEvent(final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+ this.delegate = change;
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, DataObject> getCreatedConfigurationData() {
+ return delegate.getCreatedData();
+ }
+
+ @Override
+ public Set<InstanceIdentifier<?>> getRemovedConfigurationData() {
+ return delegate.getRemovedPaths();
+ }
+
+ @Override
+ public DataObject getOriginalConfigurationSubtree() {
+ return delegate.getOriginalSubtree();
+ }
+
+ @Override
+ public DataObject getUpdatedConfigurationSubtree() {
+ return delegate.getUpdatedSubtree();
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, DataObject> getOriginalConfigurationData() {
+ return (Map) delegate.getOriginalData();
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, DataObject> getUpdatedConfigurationData() {
+ if(updatedCache == null) {
+ final Map<InstanceIdentifier<?>, DataObject> created = delegate.getCreatedData();
+ final Map<InstanceIdentifier<?>, DataObject> updated = delegate.getUpdatedData();
+ final Map<InstanceIdentifier<?>, DataObject> updatedComposite =
+ new HashMap<>(created.size() + updated.size());
+ updatedComposite.putAll(created);
+ updatedComposite.putAll(updated);
+ updatedCache = Collections.unmodifiableMap(updatedComposite);
+ }
+ return updatedCache;
+ }
+
+ @Override
+ public String toString() {
+ return "ConfigurationChangeEvent [delegate=" + delegate + "]";
+ }
+
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.compat;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.ImmutableClassToInstanceMap;
+import java.util.concurrent.ExecutorService;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.MountPoint;
+import org.opendaylight.controller.md.sal.common.api.RegistrationListener;
+import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler;
+import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandlerRegistration;
+import org.opendaylight.controller.md.sal.common.api.data.DataReader;
+import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration;
+import org.opendaylight.controller.sal.binding.api.BindingAwareService;
+import org.opendaylight.controller.sal.binding.api.NotificationListener;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.controller.sal.binding.api.NotificationService;
+import org.opendaylight.controller.sal.binding.api.RpcConsumerRegistry;
+import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
+import org.opendaylight.controller.sal.binding.api.mount.MountProviderInstance;
+import org.opendaylight.controller.sal.binding.api.rpc.RpcContextIdentifier;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.Notification;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+@Deprecated
+public class HydrogenMountInstanceAdapter implements MountProviderInstance {
+
+ private final ClassToInstanceMap<BindingAwareService> services;
+ private final InstanceIdentifier<?> identifier;
+
+
+ public HydrogenMountInstanceAdapter(final MountPoint key) {
+ this.identifier = key.getIdentifier();
+ final ImmutableClassToInstanceMap.Builder<BindingAwareService> builder = ImmutableClassToInstanceMap.builder();
+
+ final Optional<DataBroker> dataBroker = key.getService(DataBroker.class);
+ if(dataBroker.isPresent()) {
+ builder.put(DataBrokerService.class, new HydrogenDataBrokerAdapter(dataBroker.get()));
+ }
+ final Optional<org.opendaylight.controller.md.sal.binding.api.NotificationService> notificationService = key.getService(org.opendaylight.controller.md.sal.binding.api.NotificationService.class);
+ if(notificationService.isPresent()) {
+ builder.put(NotificationService.class, new HeliumNotificationServiceAdapter(notificationService.get()));
+ }
+ final Optional<RpcConsumerRegistry> rpcRegistry = key.getService(RpcConsumerRegistry.class);
+ if(rpcRegistry.isPresent()) {
+ builder.put(RpcConsumerRegistry.class, rpcRegistry.get());
+ }
+ services = builder.build();
+ }
+
+
+ private <T extends BindingAwareService> T service(final Class<T> service) {
+ final T potential = services.getInstance(service);
+ Preconditions.checkState(potential != null, "Service %s is not supported by mount point %s",service,this.getIdentifier());
+ return potential;
+ }
+
+ @Override
+ public <T extends RpcService> T getRpcService(final Class<T> serviceInterface) {
+ return service(RpcConsumerRegistry.class).getRpcService(serviceInterface);
+ }
+
+ @Override
+ public InstanceIdentifier<?> getIdentifier() {
+ return identifier;
+ }
+
+ @Override
+ public <T extends Notification> ListenerRegistration<NotificationListener<T>> registerNotificationListener(
+ final Class<T> notificationType, final NotificationListener<T> listener) {
+ return service(NotificationService.class).registerNotificationListener(notificationType, listener);
+ }
+
+ @Override
+ public ListenerRegistration<org.opendaylight.yangtools.yang.binding.NotificationListener> registerNotificationListener(
+ final org.opendaylight.yangtools.yang.binding.NotificationListener listener) {
+ return service(NotificationService.class).registerNotificationListener(listener);
+ }
+
+ @Override
+ public DataModificationTransaction beginTransaction() {
+ return service(DataBrokerService.class).beginTransaction();
+ }
+
+ @Override
+ public DataObject readConfigurationData(final InstanceIdentifier<? extends DataObject> path) {
+ return service(DataBrokerService.class).readConfigurationData(path);
+ }
+
+ @Override
+ public DataObject readOperationalData(final InstanceIdentifier<? extends DataObject> path) {
+ return service(DataBrokerService.class).readOperationalData(path);
+ }
+
+ @Override
+ public ListenerRegistration<DataChangeListener> registerDataChangeListener(
+ final InstanceIdentifier<? extends DataObject> path, final DataChangeListener listener) {
+ return service(DataBrokerService.class).registerDataChangeListener(path,listener);
+ }
+
+ @Override
+ public <T extends RpcService> RoutedRpcRegistration<T> addRoutedRpcImplementation(final Class<T> serviceInterface,
+ final T implementation) throws IllegalStateException {
+ return service(RpcProviderRegistry.class).addRoutedRpcImplementation(serviceInterface, implementation);
+ }
+
+ @Override
+ public <T extends RpcService> RpcRegistration<T> addRpcImplementation(final Class<T> serviceInterface, final T implementation)
+ throws IllegalStateException {
+ return service(RpcProviderRegistry.class).addRpcImplementation(serviceInterface, implementation);
+ }
+
+ @Override
+ public void publish(final Notification notification) {
+ service(NotificationProviderService.class).publish(notification);
+ }
+
+ @Override
+ public void publish(final Notification notification, final ExecutorService executor) {
+ service(NotificationProviderService.class).publish(notification);
+ }
+
+ @Override
+ public Registration registerCommitHandler(final InstanceIdentifier<? extends DataObject> arg0,
+ final DataCommitHandler<InstanceIdentifier<? extends DataObject>, DataObject> arg1) {
+ return service(DataProviderService.class).registerCommitHandler(arg0, arg1);
+ }
+
+ @Override
+ public ListenerRegistration<RegistrationListener<DataCommitHandlerRegistration<InstanceIdentifier<? extends DataObject>, DataObject>>> registerCommitHandlerListener(
+ final RegistrationListener<DataCommitHandlerRegistration<InstanceIdentifier<? extends DataObject>, DataObject>> arg0) {
+ return service(DataProviderService.class).registerCommitHandlerListener(arg0);
+ }
+
+ @Override
+ public Registration registerDataReader(final InstanceIdentifier<? extends DataObject> path,
+ final DataReader<InstanceIdentifier<? extends DataObject>, DataObject> reader) {
+ return service(DataProviderService.class).registerDataReader(path, reader);
+ }
+
+ @Override
+ public ListenerRegistration<NotificationInterestListener> registerInterestListener(
+ final NotificationInterestListener interestListener) {
+ return service(NotificationProviderService.class).registerInterestListener(interestListener);
+ }
+
+ @Override
+ public <L extends RouteChangeListener<RpcContextIdentifier, InstanceIdentifier<?>>> ListenerRegistration<L> registerRouteChangeListener(
+ final L arg0) {
+ return service(RpcProviderRegistry.class).registerRouteChangeListener(arg0);
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.compat;
+
+import com.google.common.base.Optional;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import org.opendaylight.controller.md.sal.binding.api.MountPoint;
+import org.opendaylight.controller.md.sal.binding.api.MountPointService;
+import org.opendaylight.controller.sal.binding.api.mount.MountService;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+@Deprecated
+public class HydrogenMountPointServiceAdapter implements MountService {
+
+ private final MountPointService delegate;
+
+ public HydrogenMountPointServiceAdapter(final MountPointService mountService) {
+ delegate = mountService;
+ }
+
+ private final LoadingCache<MountPoint, HydrogenMountInstanceAdapter> mountAdapters = CacheBuilder.newBuilder().weakKeys()
+ .build(new CacheLoader<MountPoint, HydrogenMountInstanceAdapter>() {
+
+ @Override
+ public HydrogenMountInstanceAdapter load(final MountPoint key) throws Exception {
+ return new HydrogenMountInstanceAdapter(key);
+ }
+ });
+
+ @Override
+ public HydrogenMountInstanceAdapter getMountPoint(final InstanceIdentifier<?> path) {
+ final Optional<MountPoint> mount = delegate.getMountPoint(path);
+ if (mount.isPresent()) {
+ return mountAdapters.getUnchecked(mount.get());
+ }
+ return null;
+ }
+
+ MountPointService getDelegate() {
+ return delegate;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.compat;
+
+import org.opendaylight.controller.md.sal.binding.api.MountPointService;
+import org.opendaylight.controller.sal.binding.api.mount.MountProviderInstance;
+import org.opendaylight.controller.sal.binding.api.mount.MountProviderService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+@Deprecated
+public class HydrogenMountProvisionServiceAdapter extends HydrogenMountPointServiceAdapter implements MountProviderService {
+
+ public HydrogenMountProvisionServiceAdapter(final MountPointService mountService) {
+ super(mountService);
+ }
+
+ @Override
+ public MountProviderInstance createMountPoint(final InstanceIdentifier<?> path) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Override
+ public MountProviderInstance createOrGetMountPoint(final InstanceIdentifier<?> path) {
+ return getMountPoint(path);
+ }
+
+ @Override
+ public ListenerRegistration<MountProvisionListener> registerProvisionListener(final MountProvisionListener listener) {
+ return new ListenerRegistration<MountProvisionListener>() {
+
+ @Override
+ public MountProvisionListener getInstance() {
+ return listener;
+ }
+
+ @Override
+ public void close() {
+ }
+ };
+ }
+
+}
--- /dev/null
+/**
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.compat;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.atomic.AtomicReference;
+import javax.annotation.concurrent.GuardedBy;
+import org.opendaylight.controller.sal.binding.api.NotificationListener;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.util.ListenerRegistry;
+import org.opendaylight.yangtools.yang.binding.Notification;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Deprecated
+public class HydrogenNotificationBrokerImpl implements NotificationProviderService, AutoCloseable {
+ private static final Logger LOG = LoggerFactory.getLogger(HydrogenNotificationBrokerImpl.class);
+
+ private final ListenerRegistry<NotificationInterestListener> interestListeners =
+ ListenerRegistry.create();
+ private final AtomicReference<ListenerMapGeneration> listeners = new AtomicReference<>(new ListenerMapGeneration());
+ private final ExecutorService executor;
+
+ public HydrogenNotificationBrokerImpl(final ExecutorService executor) {
+ this.executor = Preconditions.checkNotNull(executor);
+ }
+
+ @Override
+ public void publish(final Notification notification) {
+ publish(notification, executor);
+ }
+
+ @Override
+ public void publish(final Notification notification, final ExecutorService service) {
+ for (final NotificationListenerRegistration<?> r : listeners.get().listenersFor(notification)) {
+ service.submit(new NotifyTask(r, notification));
+ }
+ }
+
+ @GuardedBy("this")
+ private Multimap<Class<? extends Notification>, NotificationListenerRegistration<?>> mutableListeners() {
+ return HashMultimap.create(listeners.get().getListeners());
+ }
+
+ private void addRegistrations(final NotificationListenerRegistration<?>... registrations) {
+ synchronized (this) {
+ final Multimap<Class<? extends Notification>, NotificationListenerRegistration<?>> newListeners =
+ mutableListeners();
+ for (final NotificationListenerRegistration<?> reg : registrations) {
+ newListeners.put(reg.getType(), reg);
+ }
+
+ listeners.set(new ListenerMapGeneration(newListeners));
+ }
+
+ // Notifications are dispatched out of lock...
+ for (final NotificationListenerRegistration<?> reg : registrations) {
+ announceNotificationSubscription(reg.getType());
+ }
+ }
+
+ private synchronized void removeRegistrations(final NotificationListenerRegistration<?>... registrations) {
+ final Multimap<Class<? extends Notification>, NotificationListenerRegistration<?>> newListeners =
+ mutableListeners();
+
+ for (final NotificationListenerRegistration<?> reg : registrations) {
+ newListeners.remove(reg.getType(), reg);
+ }
+
+ listeners.set(new ListenerMapGeneration(newListeners));
+ }
+
+ private void announceNotificationSubscription(final Class<? extends Notification> notification) {
+ for (final ListenerRegistration<NotificationInterestListener> listener : interestListeners) {
+ try {
+ listener.getInstance().onNotificationSubscribtion(notification);
+ } catch (final Exception e) {
+ LOG.warn("Listener {} reported unexpected error on notification {}",
+ listener.getInstance(), notification, e);
+ }
+ }
+ }
+
+ @Override
+ public ListenerRegistration<NotificationInterestListener> registerInterestListener(final NotificationInterestListener interestListener) {
+ final ListenerRegistration<NotificationInterestListener> registration = this.interestListeners.register(interestListener);
+
+ for (final Class<? extends Notification> notification : listeners.get().getKnownTypes()) {
+ interestListener.onNotificationSubscribtion(notification);
+ }
+ return registration;
+ }
+
+ @Override
+ public <T extends Notification> NotificationListenerRegistration<T> registerNotificationListener(final Class<T> notificationType, final NotificationListener<T> listener) {
+ final NotificationListenerRegistration<T> reg = new AbstractNotificationListenerRegistration<T>(notificationType, listener) {
+ @Override
+ protected void removeRegistration() {
+ removeRegistrations(this);
+ }
+ };
+
+ addRegistrations(reg);
+ return reg;
+ }
+
+ @Override
+ public ListenerRegistration<org.opendaylight.yangtools.yang.binding.NotificationListener> registerNotificationListener(final org.opendaylight.yangtools.yang.binding.NotificationListener listener) {
+ final NotificationInvoker invoker = NotificationInvoker.invokerFor(listener);
+ final Set<Class<? extends Notification>> types = invoker.getSupportedNotifications();
+ final NotificationListenerRegistration<?>[] regs = new NotificationListenerRegistration<?>[types.size()];
+
+ // Populate the registrations...
+ int i = 0;
+ for (final Class<? extends Notification> type : types) {
+ regs[i] = new AggregatedNotificationListenerRegistration<Notification, Object>(type, invoker, regs) {
+ @Override
+ protected void removeRegistration() {
+ // Nothing to do, will be cleaned up by parent (below)
+ }
+ };
+ ++i;
+ }
+
+ // ... now put them to use ...
+ addRegistrations(regs);
+
+ // ... finally return the parent registration
+ return new AbstractListenerRegistration<org.opendaylight.yangtools.yang.binding.NotificationListener>(listener) {
+ @Override
+ protected void removeRegistration() {
+ removeRegistrations(regs);
+ for (final ListenerRegistration<?> reg : regs) {
+ reg.close();
+ }
+ }
+ };
+ }
+
+ @Override
+ public void close() {
+ }
+
+}
--- /dev/null
+/**
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.compat;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+import com.google.common.base.Predicate;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
+
+/**
+ * An immutable view of the current generation of listeners.
+ */
+final class ListenerMapGeneration {
+ private static final int CACHE_MAX_ENTRIES = 1000;
+
+ /**
+ * Constant map of notification type to subscribed listeners.
+ */
+ private final Multimap<Class<? extends Notification>, NotificationListenerRegistration<?>> typeToListeners;
+
+ /**
+ * Dynamic cache of notification implementation to matching listeners. This cache loads entries based on
+ * the contents of the {@link #typeToListeners} map.
+ */
+ private final LoadingCache<Class<?>, Iterable<NotificationListenerRegistration<?>>> implementationToListeners =
+ CacheBuilder.newBuilder()
+ .weakKeys()
+ .maximumSize(CACHE_MAX_ENTRIES)
+ .build(new CacheLoader<Class<?>, Iterable<NotificationListenerRegistration<?>>>() {
+ @Override
+ public Iterable<NotificationListenerRegistration<?>> load(final Class<?> key) {
+ final Set<NotificationListenerRegistration<?>> regs = new HashSet<>();
+
+ for (final Class<?> type : getNotificationTypes(key)) {
+ @SuppressWarnings("unchecked")
+ final Collection<NotificationListenerRegistration<?>> l = typeToListeners.get((Class<? extends Notification>) type);
+ if (l != null) {
+ regs.addAll(l);
+ }
+ }
+
+ return ImmutableSet.copyOf(regs);
+ }
+ });
+
+ ListenerMapGeneration() {
+ typeToListeners = ImmutableMultimap.of();
+ }
+
+ ListenerMapGeneration(final Multimap<Class<? extends Notification>, NotificationListenerRegistration<?>> listeners) {
+ this.typeToListeners = ImmutableMultimap.copyOf(listeners);
+ }
+
+ /**
+ * Current listeners. Exposed for creating the next generation.
+ *
+ * @return Current type-to-listener map.
+ */
+ Multimap<Class<? extends Notification>, NotificationListenerRegistration<?>> getListeners() {
+ return typeToListeners;
+ }
+
+ /**
+ * Look up the listeners which need to see this notification delivered.
+ *
+ * @param notification Notification object
+ * @return Iterable of listeners, guaranteed to be nonnull.
+ */
+ public Iterable<NotificationListenerRegistration<?>> listenersFor(final Notification notification) {
+ // Safe to use, as our loader does not throw checked exceptions
+ return implementationToListeners.getUnchecked(notification.getClass());
+ }
+
+ public Iterable<Class<? extends Notification>> getKnownTypes() {
+ return typeToListeners.keySet();
+ }
+
+ private static Iterable<Class<?>> getNotificationTypes(final Class<?> cls) {
+ final Class<?>[] ifaces = cls.getInterfaces();
+ return Iterables.filter(Arrays.asList(ifaces), new Predicate<Class<?>>() {
+ @Override
+ public boolean apply(final Class<?> input) {
+ if (Notification.class.equals(input)) {
+ return false;
+ }
+ return Notification.class.isAssignableFrom(input);
+ }
+ });
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.compat;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.reflect.TypeToken;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.binding.Notification;
+import org.opendaylight.yangtools.yang.binding.NotificationListener;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.binding.util.NotificationListenerInvoker;
+import org.opendaylight.yangtools.yang.common.QName;
+
+final class NotificationInvoker implements org.opendaylight.controller.sal.binding.api.NotificationListener<Notification> {
+
+ private final NotificationListener delegate;
+ private final Map<Class<? extends Notification>,InvokerContext> invokers;
+
+
+ private NotificationInvoker(final NotificationListener listener) {
+ delegate = listener;
+ final Map<Class<? extends Notification>, InvokerContext> builder = new HashMap<>();
+ for(final TypeToken<?> ifaceToken : TypeToken.of(listener.getClass()).getTypes().interfaces()) {
+ final Class<?> iface = ifaceToken.getRawType();
+ if(NotificationListener.class.isAssignableFrom(iface) && BindingReflections.isBindingClass(iface)) {
+ @SuppressWarnings("unchecked")
+ final Class<? extends NotificationListener> listenerType = (Class<? extends NotificationListener>) iface;
+ final NotificationListenerInvoker invoker = NotificationListenerInvoker.from(listenerType);
+ for(final Class<? extends Notification> type : getNotificationTypes(listenerType)) {
+ builder.put(type, new InvokerContext(BindingReflections.findQName(type) , invoker));
+ }
+ }
+ }
+ invokers = ImmutableMap.copyOf(builder);
+ }
+
+ public static NotificationInvoker invokerFor(final NotificationListener listener) {
+ return new NotificationInvoker(listener);
+ }
+
+ public Set<Class<? extends Notification>> getSupportedNotifications() {
+ return invokers.keySet();
+ }
+
+ @Override
+ public void onNotification(final Notification notification) {
+ getContext(notification.getImplementedInterface()).invoke(notification);
+ }
+
+ private InvokerContext getContext(final Class<?> type) {
+ return invokers.get(type);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Set<Class<? extends Notification>> getNotificationTypes(final Class<? extends org.opendaylight.yangtools.yang.binding.NotificationListener> type) {
+ // TODO: Investigate possibility and performance impact if we cache this or expose
+ // it from NotificationListenerInvoker
+ final Set<Class<? extends Notification>> ret = new HashSet<>();
+ for(final Method method : type.getMethods()) {
+ if(BindingReflections.isNotificationCallback(method)) {
+ final Class<? extends Notification> notification = (Class<? extends Notification>) method.getParameterTypes()[0];
+ ret.add(notification);
+ }
+ }
+ return ret;
+ }
+
+ private final class InvokerContext {
+
+ private final QName name;
+ private final NotificationListenerInvoker invoker;
+
+ private InvokerContext(final QName name, final NotificationListenerInvoker invoker) {
+ this.name = name;
+ this.invoker = invoker;
+ }
+
+ public void invoke(final Notification notification) {
+ invoker.invokeNotification(delegate, name, notification);
+ }
+
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.compat;
+
+import org.opendaylight.controller.sal.binding.api.NotificationListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+/**
+ * A registration of a {@link NotificationListener}. Allows query of the type
+ * of the notification and dispatching the notification atomically with regard
+ * to unregistration.
+ *
+ * @param <T> Type of notification
+ */
+interface NotificationListenerRegistration<T extends Notification> extends ListenerRegistration<NotificationListener<T>> {
+ /**
+ * Return the interface class of the notification type.
+ *
+ * @return Notification type.
+ */
+ Class<? extends Notification> getType();
+
+ /**
+ * Dispatch a notification to the listener.
+ *
+ * @param notification Notification to be dispatched
+ */
+ void notify(Notification notification);
+}
--- /dev/null
+/**
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.compat;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import org.opendaylight.yangtools.yang.binding.Notification;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class NotifyTask implements Runnable {
+ private static final Logger LOG = LoggerFactory.getLogger(NotifyTask.class);
+
+ private final NotificationListenerRegistration<?> registration;
+ private final Notification notification;
+
+ public NotifyTask(final NotificationListenerRegistration<?> registration, final Notification notification) {
+ this.registration = Preconditions.checkNotNull(registration);
+ this.notification = Preconditions.checkNotNull(notification);
+ }
+
+ @SuppressWarnings("unchecked")
+ private <T extends Notification> NotificationListenerRegistration<T> getRegistration() {
+ return (NotificationListenerRegistration<T>)registration;
+ }
+
+ @Override
+ public void run() {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Delivering notification {} to {}", notification, registration.getInstance());
+ } else {
+ LOG.trace("Delivering notification {} to {}", notification.getClass().getName(), registration.getInstance());
+ }
+
+ try {
+ getRegistration().notify(notification);
+ } catch (final Exception e) {
+ LOG.error("Unhandled exception thrown by listener: {}", registration.getInstance(), e);
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Notification delivered {} to {}", notification, registration.getInstance());
+ } else {
+ LOG.trace("Notification delivered {} to {}", notification.getClass().getName(), registration.getInstance());
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((registration== null) ? 0 : registration.hashCode());
+ result = prime * result + ((notification== null) ? 0 : notification.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final NotifyTask other = (NotifyTask) obj;
+ if (registration == null) {
+ if (other.registration != null) {
+ return false;
+ }
+ } else if (!registration.equals(other.registration)) {
+ return false;
+ }
+ if (notification == null) {
+ if (other.notification != null) {
+ return false;
+ }
+ } else if (!notification.equals(other.notification)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return Objects.toStringHelper(this)
+ .add("listener", registration)
+ .add("notification", notification.getClass())
+ .toString();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Optional;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.opendaylight.controller.md.sal.binding.api.ClusteredDataChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
+import org.opendaylight.controller.md.sal.dom.api.ClusteredDOMDataChangeListener;
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
+import org.opendaylight.yangtools.concepts.Delegator;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractForwardedDataBroker implements Delegator<DOMDataBroker>, AutoCloseable {
+
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractForwardedDataBroker.class);
+ // The Broker to whom we do all forwarding
+ private final DOMDataBroker domDataBroker;
+
+ private final BindingToNormalizedNodeCodec codec;
+
+ protected AbstractForwardedDataBroker(final DOMDataBroker domDataBroker, final BindingToNormalizedNodeCodec codec,
+ final SchemaService schemaService) {
+ this.domDataBroker = domDataBroker;
+ this.codec = codec;
+ }
+
+ protected AbstractForwardedDataBroker(final DOMDataBroker domDataBroker, final BindingToNormalizedNodeCodec codec) {
+ this.domDataBroker = domDataBroker;
+ this.codec = codec;
+ }
+
+ protected BindingToNormalizedNodeCodec getCodec() {
+ return codec;
+ }
+
+ @Override
+ public DOMDataBroker getDelegate() {
+ return domDataBroker;
+ }
+
+ public ListenerRegistration<DataChangeListener> registerDataChangeListener(final LogicalDatastoreType store,
+ final InstanceIdentifier<?> path, final DataChangeListener listener, final DataChangeScope triggeringScope) {
+ final DOMDataChangeListener domDataChangeListener;
+
+ if(listener instanceof ClusteredDataChangeListener) {
+ domDataChangeListener = new TranslatingClusteredDataChangeInvoker(store, path, listener, triggeringScope);
+ } else {
+ domDataChangeListener = new TranslatingDataChangeInvoker(store, path, listener,
+ triggeringScope);
+ }
+ final YangInstanceIdentifier domPath = codec.toYangInstanceIdentifierBlocking(path);
+ final ListenerRegistration<DOMDataChangeListener> domRegistration = domDataBroker.registerDataChangeListener(store,
+ domPath, domDataChangeListener, triggeringScope);
+ return new ListenerRegistrationImpl(listener, domRegistration);
+ }
+
+ protected Map<InstanceIdentifier<?>, DataObject> toBinding(final InstanceIdentifier<?> path,
+ final Map<YangInstanceIdentifier, ? extends NormalizedNode<?, ?>> normalized) {
+ final Map<InstanceIdentifier<?>, DataObject> newMap = new HashMap<>();
+
+ for (final Map.Entry<YangInstanceIdentifier, ? extends NormalizedNode<?, ?>> entry : normalized.entrySet()) {
+ try {
+ final Optional<Entry<InstanceIdentifier<? extends DataObject>, DataObject>> potential = getCodec().toBinding(entry);
+ if (potential.isPresent()) {
+ final Entry<InstanceIdentifier<? extends DataObject>, DataObject> binding = potential.get();
+ newMap.put(binding.getKey(), binding.getValue());
+ }
+ } catch (final DeserializationException e) {
+ LOG.warn("Failed to transform {}, omitting it", entry, e);
+ }
+ }
+ return newMap;
+ }
+
+ protected Set<InstanceIdentifier<?>> toBinding(final InstanceIdentifier<?> path,
+ final Set<YangInstanceIdentifier> normalized) {
+ final Set<InstanceIdentifier<?>> hashSet = new HashSet<>();
+ for (final YangInstanceIdentifier normalizedPath : normalized) {
+ try {
+ final Optional<InstanceIdentifier<? extends DataObject>> potential = getCodec().toBinding(normalizedPath);
+ if (potential.isPresent()) {
+ final InstanceIdentifier<? extends DataObject> binding = potential.get();
+ hashSet.add(binding);
+ } else if (normalizedPath.getLastPathArgument() instanceof YangInstanceIdentifier.AugmentationIdentifier) {
+ hashSet.add(path);
+ }
+ } catch (final DeserializationException e) {
+ LOG.warn("Failed to transform {}, omitting it", normalizedPath, e);
+ }
+ }
+ return hashSet;
+ }
+
+ protected Optional<DataObject> toBindingData(final InstanceIdentifier<?> path, final NormalizedNode<?, ?> data) {
+ if (path.isWildcarded()) {
+ return Optional.absent();
+ }
+ return (Optional<DataObject>) getCodec().deserializeFunction(path).apply(Optional.<NormalizedNode<?, ?>> of(data));
+ }
+
+ private class TranslatingDataChangeInvoker implements DOMDataChangeListener {
+ private final DataChangeListener bindingDataChangeListener;
+ private final LogicalDatastoreType store;
+ private final InstanceIdentifier<?> path;
+ private final DataChangeScope triggeringScope;
+
+ public TranslatingDataChangeInvoker(final LogicalDatastoreType store, final InstanceIdentifier<?> path,
+ final DataChangeListener bindingDataChangeListener, final DataChangeScope triggeringScope) {
+ this.store = store;
+ this.path = path;
+ this.bindingDataChangeListener = bindingDataChangeListener;
+ this.triggeringScope = triggeringScope;
+ }
+
+ @Override
+ public void onDataChanged(final AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change) {
+ bindingDataChangeListener.onDataChanged(new TranslatedDataChangeEvent(change, path));
+ }
+
+ @Override
+ public String toString() {
+ return bindingDataChangeListener.getClass().getName();
+ }
+ }
+
+ /**
+ * Translator for ClusteredDataChangeListener
+ */
+
+ private class TranslatingClusteredDataChangeInvoker extends TranslatingDataChangeInvoker implements
+ ClusteredDOMDataChangeListener {
+
+ public TranslatingClusteredDataChangeInvoker(LogicalDatastoreType store, InstanceIdentifier<?> path,
+ DataChangeListener bindingDataChangeListener,
+ DataChangeScope triggeringScope) {
+ super(store, path, bindingDataChangeListener, triggeringScope);
+ }
+ }
+
+ private class TranslatedDataChangeEvent implements AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> {
+ private final AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> domEvent;
+ private final InstanceIdentifier<?> path;
+
+ private Map<InstanceIdentifier<?>, DataObject> createdCache;
+ private Map<InstanceIdentifier<?>, DataObject> updatedCache;
+ private Map<InstanceIdentifier<?>, DataObject> originalCache;
+ private Set<InstanceIdentifier<?>> removedCache;
+ private Optional<DataObject> originalDataCache;
+ private Optional<DataObject> updatedDataCache;
+
+ public TranslatedDataChangeEvent(
+ final AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change,
+ final InstanceIdentifier<?> path) {
+ this.domEvent = change;
+ this.path = path;
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, DataObject> getCreatedData() {
+ if (createdCache == null) {
+ createdCache = Collections.unmodifiableMap(toBinding(path, domEvent.getCreatedData()));
+ }
+ return createdCache;
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, DataObject> getUpdatedData() {
+ if (updatedCache == null) {
+ updatedCache = Collections.unmodifiableMap(toBinding(path, domEvent.getUpdatedData()));
+ }
+ return updatedCache;
+
+ }
+
+ @Override
+ public Set<InstanceIdentifier<?>> getRemovedPaths() {
+ if (removedCache == null) {
+ removedCache = Collections.unmodifiableSet(toBinding(path, domEvent.getRemovedPaths()));
+ }
+ return removedCache;
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, DataObject> getOriginalData() {
+ if (originalCache == null) {
+ originalCache = Collections.unmodifiableMap(toBinding(path, domEvent.getOriginalData()));
+ }
+ return originalCache;
+
+ }
+
+ @Override
+ public DataObject getOriginalSubtree() {
+ if (originalDataCache == null) {
+ if (domEvent.getOriginalSubtree() != null) {
+ originalDataCache = toBindingData(path, domEvent.getOriginalSubtree());
+ } else {
+ originalDataCache = Optional.absent();
+ }
+ }
+ return originalDataCache.orNull();
+ }
+
+ @Override
+ public DataObject getUpdatedSubtree() {
+ if (updatedDataCache == null) {
+ if (domEvent.getUpdatedSubtree() != null) {
+ updatedDataCache = toBindingData(path, domEvent.getUpdatedSubtree());
+ } else {
+ updatedDataCache = Optional.absent();
+ }
+ }
+ return updatedDataCache.orNull();
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(TranslatedDataChangeEvent.class) //
+ .add("created", getCreatedData()) //
+ .add("updated", getUpdatedData()) //
+ .add("removed", getRemovedPaths()) //
+ .add("dom", domEvent) //
+ .toString();
+ }
+ }
+
+ private static class ListenerRegistrationImpl extends AbstractListenerRegistration<DataChangeListener> {
+ private final ListenerRegistration<DOMDataChangeListener> registration;
+
+ public ListenerRegistrationImpl(final DataChangeListener listener,
+ final ListenerRegistration<DOMDataChangeListener> registration) {
+ super(listener);
+ this.registration = registration;
+ }
+
+ @Override
+ protected void removeRegistration() {
+ registration.close();
+ }
+ }
+
+ @Override
+ public void close() {
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
+import org.opendaylight.yangtools.concepts.Delegator;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.util.concurrent.MappingCheckedFuture;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+abstract class AbstractForwardedTransaction<T extends AsyncTransaction<YangInstanceIdentifier, NormalizedNode<?, ?>>>
+ implements Delegator<T>, Identifiable<Object> {
+
+ private final T delegate;
+ private final BindingToNormalizedNodeCodec codec;
+
+ public AbstractForwardedTransaction(final T delegateTx, final BindingToNormalizedNodeCodec codec) {
+ this.delegate = Preconditions.checkNotNull(delegateTx, "Delegate must not be null");
+ this.codec = Preconditions.checkNotNull(codec, "Codec must not be null");
+ }
+
+
+ @Override
+ public final Object getIdentifier() {
+ return delegate.getIdentifier();
+ }
+
+ @Override
+ public final T getDelegate() {
+ return delegate;
+ }
+
+ @SuppressWarnings("unchecked")
+ protected final <S extends AsyncTransaction<YangInstanceIdentifier, NormalizedNode<?, ?>>> S getDelegateChecked(final Class<S> txType) {
+ Preconditions.checkState(txType.isInstance(delegate));
+ return (S) delegate;
+ }
+
+ protected final BindingToNormalizedNodeCodec getCodec() {
+ return codec;
+ }
+
+ protected final <D extends DataObject> CheckedFuture<Optional<D>,ReadFailedException> doRead(
+ final DOMDataReadTransaction readTx, final LogicalDatastoreType store,
+ final InstanceIdentifier<D> path) {
+ Preconditions.checkArgument(!path.isWildcarded(), "Invalid read of wildcarded path %s", path);
+
+ return MappingCheckedFuture.create(
+ Futures.transform(readTx.read(store, codec.toYangInstanceIdentifierBlocking(path)),
+ codec.deserializeFunction(path)),
+ ReadFailedException.MAPPER);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationException;
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationOperation;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class AbstractReadWriteTransaction extends AbstractWriteTransaction<DOMDataReadWriteTransaction> {
+
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractReadWriteTransaction.class);
+
+ public AbstractReadWriteTransaction(final DOMDataReadWriteTransaction delegate, final BindingToNormalizedNodeCodec codec) {
+ super(delegate, codec);
+ }
+
+ @Override
+ protected final void ensureParentsByMerge(final LogicalDatastoreType store,
+ final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier normalizedPath,
+ final InstanceIdentifier<?> path) {
+ List<PathArgument> currentArguments = new ArrayList<>();
+ DataNormalizationOperation<?> currentOp = getCodec().getDataNormalizer().getRootOperation();
+ Iterator<PathArgument> iterator = normalizedPath.getPathArguments().iterator();
+ while (iterator.hasNext()) {
+ PathArgument currentArg = iterator.next();
+ try {
+ currentOp = currentOp.getChild(currentArg);
+ } catch (DataNormalizationException e) {
+ throw new IllegalArgumentException(String.format("Invalid child encountered in path %s", path), e);
+ }
+ currentArguments.add(currentArg);
+ org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier currentPath = org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.create(
+ currentArguments);
+
+ final Boolean exists;
+ try {
+ exists = getDelegate().exists(store, currentPath).checkedGet();
+ } catch (ReadFailedException e) {
+ LOG.error("Failed to read pre-existing data from store {} path {}", store, currentPath, e);
+ throw new IllegalStateException("Failed to read pre-existing data", e);
+ }
+
+ if (!exists && iterator.hasNext()) {
+ getDelegate().merge(store, currentPath, currentOp.createDefault(currentArg));
+ }
+ }
+ }
+
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.CheckedFuture;
+import java.util.Map.Entry;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ *
+ * Abstract Base Transaction for transactions which are backed by
+ * {@link DOMDataWriteTransaction}
+ */
+public abstract class AbstractWriteTransaction<T extends DOMDataWriteTransaction> extends
+ AbstractForwardedTransaction<T> {
+
+ protected AbstractWriteTransaction(final T delegate, final BindingToNormalizedNodeCodec codec) {
+ super(delegate, codec);
+ }
+
+ public final <U extends DataObject> void put(final LogicalDatastoreType store,
+ final InstanceIdentifier<U> path, final U data, final boolean createParents) {
+ Preconditions.checkArgument(!path.isWildcarded(), "Cannot put data into wildcarded path %s", path);
+
+ final Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> normalized = getCodec().toNormalizedNode(path, data);
+ if (createParents) {
+ ensureParentsByMerge(store, normalized.getKey(), path);
+ } else {
+ ensureListParentIfNeeded(store,path,normalized);
+ }
+
+ getDelegate().put(store, normalized.getKey(), normalized.getValue());
+ }
+
+ public final <U extends DataObject> void merge(final LogicalDatastoreType store,
+ final InstanceIdentifier<U> path, final U data,final boolean createParents) {
+ Preconditions.checkArgument(!path.isWildcarded(), "Cannot merge data into wildcarded path %s", path);
+
+ final Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> normalized = getCodec().toNormalizedNode(path, data);
+ if (createParents) {
+ ensureParentsByMerge(store, normalized.getKey(), path);
+ } else {
+ ensureListParentIfNeeded(store,path,normalized);
+ }
+
+ getDelegate().merge(store, normalized.getKey(), normalized.getValue());
+ }
+
+ /**
+ *
+ * Ensures list parent if item is list, otherwise noop.
+ *
+ * <p>
+ * One of properties of binding specification is that it is imposible
+ * to represent list as a whole and thus it is impossible to write
+ * empty variation of MapNode without creating parent node, with
+ * empty list.
+ *
+ * <p>
+ * This actually makes writes such as
+ * <pre>
+ * put("Nodes", new NodesBuilder().build());
+ * put("Nodes/Node[key]", new NodeBuilder().setKey("key").build());
+ * </pre>
+ * To result in three DOM operations:
+ * <pre>
+ * put("/nodes",domNodes);
+ * merge("/nodes/node",domNodeList);
+ * put("/nodes/node/node[key]",domNode);
+ * </pre>
+ *
+ *
+ * In order to allow that to be inserted if necessary, if we know
+ * item is list item, we will try to merge empty MapNode or OrderedNodeMap
+ * to ensure list exists.
+ *
+ * @param store Data Store type
+ * @param path Path to data (Binding Aware)
+ * @param normalized Normalized version of data to be written
+ */
+ private void ensureListParentIfNeeded(final LogicalDatastoreType store, final InstanceIdentifier<?> path,
+ final Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> normalized) {
+ if (Identifiable.class.isAssignableFrom(path.getTargetType())) {
+ YangInstanceIdentifier parentMapPath = normalized.getKey().getParent();
+ Preconditions.checkArgument(parentMapPath != null, "Map path %s does not have a parent", path);
+
+ NormalizedNode<?, ?> emptyParent = getCodec().getDefaultNodeFor(parentMapPath);
+ getDelegate().merge(store, parentMapPath, emptyParent);
+ }
+ }
+
+ /**
+ * @deprecated Use {@link YangInstanceIdentifier#getParent()} instead.
+ */
+ @Deprecated
+ protected static Optional<YangInstanceIdentifier> getParent(final YangInstanceIdentifier child) {
+ return Optional.fromNullable(child.getParent());
+ }
+
+ /**
+ * Subclasses of this class are required to implement creation of parent
+ * nodes based on behaviour of their underlying transaction.
+ *
+ * @param store
+ * @param key
+ * @param path
+ */
+ protected abstract void ensureParentsByMerge(LogicalDatastoreType store,
+ YangInstanceIdentifier key, InstanceIdentifier<?> path);
+
+ protected final void doDelete(final LogicalDatastoreType store,
+ final InstanceIdentifier<?> path) {
+ Preconditions.checkArgument(!path.isWildcarded(), "Cannot delete wildcarded path %s", path);
+
+ final YangInstanceIdentifier normalized = getCodec().toYangInstanceIdentifierBlocking(path);
+ getDelegate().delete(store, normalized);
+ }
+
+ protected final CheckedFuture<Void,TransactionCommitFailedException> doSubmit() {
+ return getDelegate().submit();
+ }
+
+ protected final boolean doCancel() {
+ return getDelegate().cancel();
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ClassToInstanceMap;
+import org.opendaylight.controller.md.sal.binding.api.BindingService;
+import org.opendaylight.controller.md.sal.binding.spi.AdapterBuilder;
+import org.opendaylight.controller.md.sal.dom.api.DOMService;
+
+abstract class BindingDOMAdapterBuilder<T extends BindingService> extends AdapterBuilder<T, DOMService> {
+
+ interface Factory<T extends BindingService> {
+
+ BindingDOMAdapterBuilder<T> newBuilder();
+
+ }
+
+ private BindingToNormalizedNodeCodec codec;
+
+ public void setCodec(final BindingToNormalizedNodeCodec codec) {
+ this.codec = codec;
+ }
+
+ @Override
+ protected final T createInstance(final ClassToInstanceMap<DOMService> delegates) {
+ Preconditions.checkState(codec != null);
+ return createInstance(codec,delegates);
+ }
+
+ protected abstract T createInstance(BindingToNormalizedNodeCodec codec2, ClassToInstanceMap<DOMService> delegates);
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+import org.opendaylight.controller.md.sal.binding.api.BindingService;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
+import org.opendaylight.controller.md.sal.binding.api.NotificationService;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMAdapterBuilder.Factory;
+import org.opendaylight.controller.md.sal.binding.spi.AdapterBuilder;
+import org.opendaylight.controller.md.sal.binding.spi.AdapterLoader;
+import org.opendaylight.controller.md.sal.dom.api.DOMService;
+import org.opendaylight.controller.sal.binding.api.RpcConsumerRegistry;
+
+public abstract class BindingDOMAdapterLoader extends AdapterLoader<BindingService, DOMService> {
+
+
+ private static final Map<Class<?>,BindingDOMAdapterBuilder.Factory<?>> FACTORIES = ImmutableMap.<Class<?>,BindingDOMAdapterBuilder.Factory<?>>builder()
+ .put(NotificationService.class,BindingDOMNotificationServiceAdapter.BUILDER_FACTORY)
+ .put(NotificationPublishService.class,BindingDOMNotificationPublishServiceAdapter.BUILDER_FACTORY)
+ .put(DataBroker.class,BindingDOMDataBrokerAdapter.BUILDER_FACTORY)
+ .put(RpcConsumerRegistry.class,BindingDOMRpcServiceAdapter.BUILDER_FACTORY)
+ .build();
+
+ private final BindingToNormalizedNodeCodec codec;
+
+ public BindingDOMAdapterLoader(final BindingToNormalizedNodeCodec codec) {
+ super();
+ this.codec = codec;
+ }
+
+ @Override
+ protected final AdapterBuilder<? extends BindingService, DOMService> createBuilder(final Class<? extends BindingService> key) {
+ final Factory<?> factory = FACTORIES.get(key);
+ Preconditions.checkArgument(factory != null, "Unsupported service type %s", key);
+ final BindingDOMAdapterBuilder<?> builder = factory.newBuilder();
+ builder.setCodec(codec);
+ return builder;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.ImmutableSet;
+import java.util.Set;
+import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeService;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMAdapterBuilder.Factory;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
+import org.opendaylight.controller.md.sal.dom.api.DOMService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+/**
+ * The DataBrokerImpl simply defers to the DOMDataBroker for all its operations.
+ * All transactions and listener registrations are wrapped by the DataBrokerImpl
+ * to allow binding aware components to use the DataBroker transparently.
+ *
+ * Besides this the DataBrokerImpl and it's collaborators also cache data that
+ * is already transformed from the binding independent to binding aware format
+ *
+
+ */
+public class BindingDOMDataBrokerAdapter extends AbstractForwardedDataBroker implements DataBroker, DataTreeChangeService {
+
+
+ static final Factory<DataBroker> BUILDER_FACTORY = new BindingDOMAdapterBuilder.Factory<DataBroker>() {
+
+ @Override
+ public BindingDOMAdapterBuilder<DataBroker> newBuilder() {
+ return new Builder();
+ }
+
+ };
+ private final DataTreeChangeService treeChangeService;
+
+ public BindingDOMDataBrokerAdapter(final DOMDataBroker domDataBroker, final BindingToNormalizedNodeCodec codec) {
+ super(domDataBroker, codec);
+ final DOMDataTreeChangeService domTreeChange = (DOMDataTreeChangeService) domDataBroker.getSupportedExtensions().get(DOMDataTreeChangeService.class);
+ if(domTreeChange != null) {
+ treeChangeService = BindingDOMDataTreeChangeServiceAdapter.create(codec, domTreeChange);
+ } else {
+ treeChangeService = null;
+ }
+ }
+
+ @Override
+
+ public ReadOnlyTransaction newReadOnlyTransaction() {
+ return new BindingDOMReadTransactionAdapter(getDelegate().newReadOnlyTransaction(),getCodec());
+ }
+
+ @Override
+ public ReadWriteTransaction newReadWriteTransaction() {
+ return new BindingDOMReadWriteTransactionAdapter(getDelegate().newReadWriteTransaction(),getCodec());
+ }
+
+ @Override
+ public WriteTransaction newWriteOnlyTransaction() {
+ return new BindingDOMWriteTransactionAdapter<>(getDelegate().newWriteOnlyTransaction(),getCodec());
+ }
+
+ @Override
+ public BindingTransactionChain createTransactionChain(final TransactionChainListener listener) {
+ return new BindingDOMTransactionChainAdapter(getDelegate(), getCodec(), listener);
+ }
+
+ private static class Builder extends BindingDOMAdapterBuilder<DataBroker> {
+
+ @Override
+ public Set<? extends Class<? extends DOMService>> getRequiredDelegates() {
+ return ImmutableSet.of(DOMDataBroker.class);
+ }
+
+ @Override
+ protected DataBroker createInstance(final BindingToNormalizedNodeCodec codec,
+ final ClassToInstanceMap<DOMService> delegates) {
+ final DOMDataBroker domDataBroker = delegates.getInstance(DOMDataBroker.class);
+ return new BindingDOMDataBrokerAdapter(domDataBroker, codec);
+ }
+
+ }
+
+ @Override
+ public <T extends DataObject, L extends DataTreeChangeListener<T>> ListenerRegistration<L> registerDataTreeChangeListener(
+ final DataTreeIdentifier<T> treeId, final L listener) {
+ if(treeChangeService == null) {
+ throw new UnsupportedOperationException("Underlying data broker does not expose DOMDataTreeChangeService.");
+ }
+ return treeChangeService.registerDataTreeChangeListener(treeId, listener);
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Preconditions;
+import java.util.Collection;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+
+/**
+ * Adapter wrapping Binding {@link DataTreeChangeListener} and exposing
+ * it as {@link DOMDataTreeChangeListener} and translated DOM events
+ * to their Binding equivalent.
+ *
+ */
+final class BindingDOMDataTreeChangeListenerAdapter<T extends DataObject> implements DOMDataTreeChangeListener {
+
+ private final BindingToNormalizedNodeCodec codec;
+ private final DataTreeChangeListener<T> listener;
+ private final LogicalDatastoreType store;
+
+ BindingDOMDataTreeChangeListenerAdapter(final BindingToNormalizedNodeCodec codec, final DataTreeChangeListener<T> listener,
+ final LogicalDatastoreType store) {
+ this.codec = Preconditions.checkNotNull(codec);
+ this.listener = Preconditions.checkNotNull(listener);
+ this.store = Preconditions.checkNotNull(store);
+ }
+
+ @Override
+ public void onDataTreeChanged(final Collection<DataTreeCandidate> domChanges) {
+ final Collection<DataTreeModification<T>> bindingChanges = LazyDataTreeModification.from(codec, domChanges, store);
+ listener.onDataTreeChanged(bindingChanges);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeService;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+
+/**
+ *
+ * Adapter exposing Binding {@link DataTreeChangeService} and wrapping
+ * {@link DOMDataTreeChangeService} and is responsible for translation
+ * and instantiation of {@link BindingDOMDataTreeChangeListenerAdapter}
+ * adapters.
+ *
+ * Each registered {@link DataTreeChangeListener} is wrapped using
+ * adapter and registered directly to DOM service.
+ */
+final class BindingDOMDataTreeChangeServiceAdapter implements DataTreeChangeService {
+
+ private final BindingToNormalizedNodeCodec codec;
+ private final DOMDataTreeChangeService dataTreeChangeService;
+
+ private BindingDOMDataTreeChangeServiceAdapter(final BindingToNormalizedNodeCodec codec,
+ final DOMDataTreeChangeService dataTreeChangeService) {
+ this.codec = Preconditions.checkNotNull(codec);
+ this.dataTreeChangeService = Preconditions.checkNotNull(dataTreeChangeService);
+ }
+
+ static DataTreeChangeService create(final BindingToNormalizedNodeCodec codec,
+ final DOMDataTreeChangeService dataTreeChangeService) {
+ return new BindingDOMDataTreeChangeServiceAdapter(codec, dataTreeChangeService);
+ }
+
+ @Override
+ public <T extends DataObject, L extends DataTreeChangeListener<T>> ListenerRegistration<L> registerDataTreeChangeListener(
+ final DataTreeIdentifier<T> treeId, final L listener) {
+ final DOMDataTreeIdentifier domIdentifier = toDomTreeIdentifier(treeId);
+ final BindingDOMDataTreeChangeListenerAdapter<T> domListener = new BindingDOMDataTreeChangeListenerAdapter<>(codec,listener, treeId.getDatastoreType());
+ final ListenerRegistration<BindingDOMDataTreeChangeListenerAdapter<T>> domReg = dataTreeChangeService.registerDataTreeChangeListener(domIdentifier, domListener);
+ return new BindingDataTreeChangeListenerRegistration<>(listener,domReg);
+ }
+
+ private DOMDataTreeIdentifier toDomTreeIdentifier(final DataTreeIdentifier<?> treeId) {
+ final YangInstanceIdentifier domPath = codec.toYangInstanceIdentifierBlocking(treeId.getRootIdentifier());
+ return new DOMDataTreeIdentifier(treeId.getDatastoreType(), domPath);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Optional;
+import org.opendaylight.controller.md.sal.binding.api.MountPointService.MountPointListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
+
+final class BindingDOMMountPointListenerAdapter<T extends MountPointListener> implements ListenerRegistration<T>, MountProvisionListener {
+
+ private final T listener;
+ private final ListenerRegistration<MountProvisionListener> registration;
+ private final BindingToNormalizedNodeCodec codec;
+
+ public BindingDOMMountPointListenerAdapter(final T listener, final BindingToNormalizedNodeCodec codec, final DOMMountPointService mountService) {
+ this.listener = listener;
+ this.codec = codec;
+ this.registration = mountService.registerProvisionListener(this);
+ }
+
+ @Override
+ public T getInstance() {
+ return listener;
+ }
+
+ @Override
+ public void close() {
+ registration.close();
+ }
+
+ @Override
+ public void onMountPointCreated(final YangInstanceIdentifier path) {
+ try {
+ final InstanceIdentifier<? extends DataObject> bindingPath = toBinding(path);
+ listener.onMountPointCreated(bindingPath);
+ } catch (final DeserializationException e) {
+ BindingDOMMountPointServiceAdapter.LOG.error("Unable to translate mountPoint path {}. Omitting event.",path,e);
+ }
+
+ }
+
+ private InstanceIdentifier<? extends DataObject> toBinding(final YangInstanceIdentifier path) throws DeserializationException {
+ final Optional<InstanceIdentifier<? extends DataObject>> instanceIdentifierOptional = codec.toBinding(path);
+ if(instanceIdentifierOptional.isPresent()) {
+ return instanceIdentifierOptional.get();
+ } else {
+ throw new DeserializationException("Deserialization unsuccessful, " + instanceIdentifierOptional);
+ }
+ }
+
+ @Override
+ public void onMountPointRemoved(final YangInstanceIdentifier path) {
+ try {
+ final InstanceIdentifier<? extends DataObject> bindingPath = toBinding(path);
+ listener.onMountPointRemoved(bindingPath);
+ } catch (final DeserializationException e) {
+ BindingDOMMountPointServiceAdapter.LOG.error("Unable to translate mountPoint path {}. Omitting event.",path,e);
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Optional;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import org.opendaylight.controller.md.sal.binding.api.MountPoint;
+import org.opendaylight.controller.md.sal.binding.api.MountPointService;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class BindingDOMMountPointServiceAdapter implements MountPointService {
+
+ public static final Logger LOG = LoggerFactory.getLogger(BindingDOMMountPointServiceAdapter.class);
+
+ private final BindingToNormalizedNodeCodec codec;
+ private final DOMMountPointService mountService;
+ private final LoadingCache<DOMMountPoint, BindingMountPointAdapter> bindingMountpoints = CacheBuilder.newBuilder()
+ .weakKeys().build(new CacheLoader<DOMMountPoint, BindingMountPointAdapter>() {
+
+ @Override
+ public BindingMountPointAdapter load(DOMMountPoint key) throws Exception {
+ return new BindingMountPointAdapter(codec,key);
+ }
+ });
+
+ public BindingDOMMountPointServiceAdapter(DOMMountPointService mountService,BindingToNormalizedNodeCodec codec) {
+ this.codec = codec;
+ this.mountService = mountService;
+ }
+
+ @Override
+ public Optional<MountPoint> getMountPoint(InstanceIdentifier<?> mountPoint) {
+
+ YangInstanceIdentifier domPath = codec.toYangInstanceIdentifierBlocking(mountPoint);
+ Optional<DOMMountPoint> domMount = mountService.getMountPoint(domPath);
+ if(domMount.isPresent()) {
+ return Optional.<MountPoint>fromNullable(bindingMountpoints.getUnchecked(domMount.get()));
+ }
+ return Optional.absent();
+ }
+
+ @Override
+ public <T extends MountPointListener> ListenerRegistration<T> registerListener(InstanceIdentifier<?> path,
+ T listener) {
+ return new BindingDOMMountPointListenerAdapter<T>(listener,codec,mountService);
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.reflect.TypeToken;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListener;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.yangtools.yang.binding.Notification;
+import org.opendaylight.yangtools.yang.binding.NotificationListener;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.binding.util.NotificationListenerInvoker;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+class BindingDOMNotificationListenerAdapter implements DOMNotificationListener {
+
+ private final BindingNormalizedNodeSerializer codec;
+ private final NotificationListener delegate;
+ private final Map<SchemaPath,NotificationListenerInvoker> invokers;
+
+ public BindingDOMNotificationListenerAdapter(final BindingNormalizedNodeSerializer codec, final NotificationListener delegate) {
+ this.codec = codec;
+ this.delegate = delegate;
+ this.invokers = createInvokerMapFor(delegate.getClass());
+ }
+
+ @Override
+ public void onNotification(@Nonnull final DOMNotification notification) {
+ final Notification baNotification = deserialize(notification);
+ final QName notificationQName = notification.getType().getLastComponent();
+ getInvoker(notification.getType()).invokeNotification(delegate, notificationQName, baNotification);
+ }
+
+ private Notification deserialize(final DOMNotification notification) {
+ if(notification instanceof LazySerializedDOMNotification) {
+ return ((LazySerializedDOMNotification) notification).getBindingData();
+ }
+ return codec.fromNormalizedNodeNotification(notification.getType(), notification.getBody());
+ }
+
+ private NotificationListenerInvoker getInvoker(final SchemaPath type) {
+ return invokers.get(type);
+ }
+
+ protected Set<SchemaPath> getSupportedNotifications() {
+ return invokers.keySet();
+ }
+
+ public static Map<SchemaPath, NotificationListenerInvoker> createInvokerMapFor(final Class<? extends NotificationListener> implClz) {
+ final Map<SchemaPath, NotificationListenerInvoker> builder = new HashMap<>();
+ for(final TypeToken<?> ifaceToken : TypeToken.of(implClz).getTypes().interfaces()) {
+ Class<?> iface = ifaceToken.getRawType();
+ if(NotificationListener.class.isAssignableFrom(iface) && BindingReflections.isBindingClass(iface)) {
+ @SuppressWarnings("unchecked")
+ final Class<? extends NotificationListener> listenerType = (Class<? extends NotificationListener>) iface;
+ final NotificationListenerInvoker invoker = NotificationListenerInvoker.from(listenerType);
+ for(final SchemaPath path : getNotificationTypes(listenerType)) {
+ builder.put(path, invoker);
+ }
+ }
+ }
+ return ImmutableMap.copyOf(builder);
+ }
+
+ private static Set<SchemaPath> getNotificationTypes(final Class<? extends NotificationListener> type) {
+ // TODO: Investigate possibility and performance impact if we cache this or expose
+ // it from NotificationListenerInvoker
+ final Set<SchemaPath> ret = new HashSet<>();
+ for(final Method method : type.getMethods()) {
+ if(BindingReflections.isNotificationCallback(method)) {
+ final Class<?> notification = method.getParameterTypes()[0];
+ final QName name = BindingReflections.findQName(notification);
+ ret.add(SchemaPath.create(true, name));
+ }
+ }
+ return ret;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMAdapterBuilder.Factory;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationPublishService;
+import org.opendaylight.controller.md.sal.dom.api.DOMService;
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+public class BindingDOMNotificationPublishServiceAdapter implements NotificationPublishService, AutoCloseable {
+
+ static final Factory<NotificationPublishService> BUILDER_FACTORY = new BindingDOMAdapterBuilder.Factory<NotificationPublishService>() {
+
+ @Override
+ public BindingDOMAdapterBuilder<NotificationPublishService> newBuilder() {
+ return new Builder();
+ }
+
+ };
+
+ private final BindingToNormalizedNodeCodec codecRegistry;
+ private final DOMNotificationPublishService domPublishService;
+
+ public BindingDOMNotificationPublishServiceAdapter(final BindingToNormalizedNodeCodec codec, final DOMNotificationPublishService domPublishService) {
+ this.codecRegistry = codec;
+ this.domPublishService = domPublishService;
+ }
+
+ public BindingToNormalizedNodeCodec getCodecRegistry() {
+ return codecRegistry;
+ }
+
+ public DOMNotificationPublishService getDomPublishService() {
+ return domPublishService;
+ }
+
+ @Override
+ public void putNotification(final Notification notification) throws InterruptedException {
+ domPublishService.putNotification(toDomNotification(notification));
+ }
+
+ @Override
+ public ListenableFuture<? extends Object> offerNotification(final Notification notification) {
+ ListenableFuture<?> offerResult = domPublishService.offerNotification(toDomNotification(notification));
+ return DOMNotificationPublishService.REJECTED.equals(offerResult)
+ ? NotificationPublishService.REJECTED
+ : offerResult;
+ }
+
+ @Override
+ public ListenableFuture<? extends Object> offerNotification(final Notification notification, final int timeout, final TimeUnit unit) throws InterruptedException {
+ ListenableFuture<?> offerResult = domPublishService.offerNotification(toDomNotification(notification), timeout, unit);
+ return DOMNotificationPublishService.REJECTED.equals(offerResult)
+ ? NotificationPublishService.REJECTED
+ : offerResult;
+ }
+
+ private DOMNotification toDomNotification(final Notification notification) {
+ return LazySerializedDOMNotification.create(codecRegistry, notification);
+ }
+
+ @Override
+ public void close() throws Exception {
+
+ }
+
+ protected static class Builder extends BindingDOMAdapterBuilder<NotificationPublishService> {
+
+ @Override
+ public Set<Class<? extends DOMService>> getRequiredDelegates() {
+ return ImmutableSet.<Class<? extends DOMService>>of(DOMNotificationPublishService.class);
+ }
+
+ @Override
+ protected NotificationPublishService createInstance(final BindingToNormalizedNodeCodec codec,
+ final ClassToInstanceMap<DOMService> delegates) {
+ final DOMNotificationPublishService domPublish = delegates.getInstance(DOMNotificationPublishService.class);
+ return new BindingDOMNotificationPublishServiceAdapter(codec, domPublish);
+ }
+
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.ImmutableSet;
+import java.util.Set;
+import org.opendaylight.controller.md.sal.binding.api.NotificationService;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMAdapterBuilder.Factory;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService;
+import org.opendaylight.controller.md.sal.dom.api.DOMService;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.NotificationListener;
+
+public class BindingDOMNotificationServiceAdapter implements NotificationService, AutoCloseable {
+
+ public static final Factory<NotificationService> BUILDER_FACTORY = new Factory<NotificationService>() {
+
+ @Override
+ public BindingDOMAdapterBuilder<NotificationService> newBuilder() {
+ return new Builder();
+ }
+
+ };
+ private final BindingNormalizedNodeSerializer codec;
+ private final DOMNotificationService domNotifService;
+
+ public BindingDOMNotificationServiceAdapter(final BindingNormalizedNodeSerializer codec, final DOMNotificationService domNotifService) {
+ this.codec = codec;
+ this.domNotifService = domNotifService;
+ }
+
+ @Override
+ public <T extends NotificationListener> ListenerRegistration<T> registerNotificationListener(final T listener) {
+ final BindingDOMNotificationListenerAdapter domListener = new BindingDOMNotificationListenerAdapter(codec, listener);
+ final ListenerRegistration<BindingDOMNotificationListenerAdapter> domRegistration =
+ domNotifService.registerNotificationListener(domListener, domListener.getSupportedNotifications());
+ return new ListenerRegistrationImpl<>(listener, domRegistration);
+ }
+
+ @Override
+ public void close() throws Exception {
+
+ }
+
+ private static class ListenerRegistrationImpl<T extends NotificationListener> extends AbstractListenerRegistration<T> {
+ private final ListenerRegistration<?> listenerRegistration;
+
+ public ListenerRegistrationImpl(final T listener, final ListenerRegistration<?> listenerRegistration) {
+ super(listener);
+ this.listenerRegistration = listenerRegistration;
+ }
+
+ @Override
+ protected void removeRegistration() {
+ listenerRegistration.close();
+ }
+ }
+
+ private static class Builder extends BindingDOMAdapterBuilder<NotificationService> {
+
+ @Override
+ protected NotificationService createInstance(final BindingToNormalizedNodeCodec codec,
+ final ClassToInstanceMap<DOMService> delegates) {
+ final DOMNotificationService domNotification = delegates.getInstance(DOMNotificationService.class);
+ return new BindingDOMNotificationServiceAdapter(codec.getCodecRegistry(), domNotification);
+ }
+
+ @Override
+ public Set<? extends Class<? extends DOMService>> getRequiredDelegates() {
+ return ImmutableSet.of(DOMNotificationService.class);
+ }
+ }
+
+ public DOMNotificationService getDomService() {
+ return domNotifService;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+
+class BindingDOMReadTransactionAdapter extends AbstractForwardedTransaction<DOMDataReadOnlyTransaction> implements
+ ReadOnlyTransaction {
+
+ protected BindingDOMReadTransactionAdapter(final DOMDataReadOnlyTransaction delegate,
+ final BindingToNormalizedNodeCodec codec) {
+ super(delegate, codec);
+ }
+
+ @Override
+ public <T extends DataObject> CheckedFuture<Optional<T>,ReadFailedException> read(
+ final LogicalDatastoreType store, final InstanceIdentifier<T> path) {
+ return doRead(getDelegate(),store, path);
+ }
+
+ @Override
+ public void close() {
+ getDelegate().close();
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+
+class BindingDOMReadWriteTransactionAdapter extends
+ BindingDOMWriteTransactionAdapter<DOMDataReadWriteTransaction> implements ReadWriteTransaction {
+
+ protected BindingDOMReadWriteTransactionAdapter(final DOMDataReadWriteTransaction delegate,
+ final BindingToNormalizedNodeCodec codec) {
+ super(delegate, codec);
+ }
+
+ @Override
+ public <T extends DataObject> CheckedFuture<Optional<T>,ReadFailedException> read(
+ final LogicalDatastoreType store, final InstanceIdentifier<T> path) {
+ return doRead(getDelegate(), store, path);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationRegistration;
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+class BindingDOMRpcAdapterRegistration<T extends RpcService> extends AbstractObjectRegistration<T>{
+
+ private final DOMRpcImplementationRegistration<?> reg;
+
+ public BindingDOMRpcAdapterRegistration(T instance, DOMRpcImplementationRegistration<?> reg) {
+ super(instance);
+ this.reg = reg;
+ }
+
+ @Override
+ protected void removeRegistration() {
+ reg.close();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.JdkFutureAdapters;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementation;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.binding.util.RpcServiceInvoker;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+public class BindingDOMRpcImplementationAdapter implements DOMRpcImplementation {
+
+ private static final Cache<Class<?>, RpcServiceInvoker> SERVICE_INVOKERS = CacheBuilder.newBuilder().weakKeys().build();
+
+ private final BindingNormalizedNodeCodecRegistry codec;
+ private final RpcServiceInvoker invoker;
+ private final RpcService delegate;
+ private final QName inputQname;
+
+ public <T extends RpcService> BindingDOMRpcImplementationAdapter(final BindingNormalizedNodeCodecRegistry codec, final Class<T> type, final Map<SchemaPath, Method> localNameToMethod, final T delegate) {
+ try {
+ this.invoker = SERVICE_INVOKERS.get(type, new Callable<RpcServiceInvoker>() {
+ @Override
+ public RpcServiceInvoker call() {
+ final Map<QName, Method> map = new HashMap<>();
+ for (Entry<SchemaPath, Method> e : localNameToMethod.entrySet()) {
+ map.put(e.getKey().getLastComponent(), e.getValue());
+ }
+
+ return RpcServiceInvoker.from(map);
+ }
+ });
+ } catch (ExecutionException e) {
+ throw new IllegalArgumentException("Failed to create invokers for type " + type, e);
+ }
+
+ this.codec = Preconditions.checkNotNull(codec);
+ this.delegate = Preconditions.checkNotNull(delegate);
+ inputQname = QName.cachedReference(QName.create(BindingReflections.getQNameModule(type), "input"));
+ }
+
+ @Override
+ public CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(final DOMRpcIdentifier rpc, final NormalizedNode<?, ?> input) {
+ final SchemaPath schemaPath = rpc.getType();
+ final DataObject bindingInput = input != null ? deserilialize(rpc.getType(),input) : null;
+ final ListenableFuture<RpcResult<?>> bindingResult = invoke(schemaPath,bindingInput);
+ return transformResult(schemaPath,bindingResult);
+ }
+
+ private DataObject deserilialize(final SchemaPath rpcPath, final NormalizedNode<?, ?> input) {
+ if (input instanceof LazySerializedContainerNode) {
+ return ((LazySerializedContainerNode) input).bindingData();
+ }
+ final SchemaPath inputSchemaPath = rpcPath.createChild(inputQname);
+ return codec.fromNormalizedNodeRpcData(inputSchemaPath, (ContainerNode) input);
+ }
+
+ private ListenableFuture<RpcResult<?>> invoke(final SchemaPath schemaPath, final DataObject input) {
+ return JdkFutureAdapters.listenInPoolThread(invoker.invokeRpc(delegate, schemaPath.getLastComponent(), input));
+ }
+
+ private CheckedFuture<DOMRpcResult, DOMRpcException> transformResult(final SchemaPath schemaPath,
+ final ListenableFuture<RpcResult<?>> bindingResult) {
+ return LazyDOMRpcResultFuture.create(codec, bindingResult);
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.collect.ImmutableSet;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationRegistration;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcProviderService;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+public class BindingDOMRpcProviderServiceAdapter {
+
+ private static final Set<YangInstanceIdentifier> GLOBAL = ImmutableSet.of(YangInstanceIdentifier.builder().build());
+ private final BindingToNormalizedNodeCodec codec;
+ private final DOMRpcProviderService domRpcRegistry;
+
+ public BindingDOMRpcProviderServiceAdapter(final DOMRpcProviderService domRpcRegistry, final BindingToNormalizedNodeCodec codec) {
+ this.codec = codec;
+ this.domRpcRegistry = domRpcRegistry;
+ }
+
+ public <S extends RpcService, T extends S> ObjectRegistration<T> registerRpcImplementation(final Class<S> type,
+ final T implementation) {
+ return register(type, implementation, GLOBAL);
+ }
+
+ public <S extends RpcService, T extends S> ObjectRegistration<T> registerRpcImplementation(final Class<S> type,
+ final T implementation, final Set<InstanceIdentifier<?>> paths) {
+ return register(type, implementation, toYangInstanceIdentifiers(paths));
+ }
+
+ private <S extends RpcService, T extends S> ObjectRegistration<T> register(final Class<S> type, final T implementation, final Collection<YangInstanceIdentifier> rpcContextPaths) {
+ final Map<SchemaPath, Method> rpcs = codec.getRpcMethodToSchemaPath(type).inverse();
+
+ final BindingDOMRpcImplementationAdapter adapter = new BindingDOMRpcImplementationAdapter(codec.getCodecRegistry(), type, rpcs, implementation);
+ final Set<DOMRpcIdentifier> domRpcs = createDomRpcIdentifiers(rpcs.keySet(), rpcContextPaths);
+ final DOMRpcImplementationRegistration<?> domReg = domRpcRegistry.registerRpcImplementation(adapter, domRpcs);
+ return new BindingRpcAdapterRegistration<>(implementation, domReg);
+ }
+
+ private static Set<DOMRpcIdentifier> createDomRpcIdentifiers(final Set<SchemaPath> rpcs, final Collection<YangInstanceIdentifier> paths) {
+ final Set<DOMRpcIdentifier> ret = new HashSet<>();
+ for (final YangInstanceIdentifier path : paths) {
+ for (final SchemaPath rpc : rpcs) {
+ ret.add(DOMRpcIdentifier.create(rpc, path));
+ }
+ }
+ return ret;
+ }
+
+ private Collection<YangInstanceIdentifier> toYangInstanceIdentifiers(final Set<InstanceIdentifier<?>> identifiers) {
+ final Collection<YangInstanceIdentifier> ret = new ArrayList<>(identifiers.size());
+ for (final InstanceIdentifier<?> binding : identifiers) {
+ ret.add(codec.toYangInstanceIdentifierCached(binding));
+ }
+ return ret;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.ImmutableSet;
+import java.util.Set;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMAdapterBuilder.Factory;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
+import org.opendaylight.controller.md.sal.dom.api.DOMService;
+import org.opendaylight.controller.sal.binding.api.RpcConsumerRegistry;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+
+public class BindingDOMRpcServiceAdapter implements RpcConsumerRegistry {
+
+ protected static final Factory<RpcConsumerRegistry> BUILDER_FACTORY = new Factory<RpcConsumerRegistry>() {
+
+ @Override
+ public BindingDOMAdapterBuilder<RpcConsumerRegistry> newBuilder() {
+ return new Builder();
+ }
+
+ };
+
+ private final LoadingCache<Class<? extends RpcService>, RpcServiceAdapter> proxies = CacheBuilder.newBuilder()
+ .weakKeys()
+ .build(new CacheLoader<Class<? extends RpcService>, RpcServiceAdapter>() {
+
+ @Override
+ public RpcServiceAdapter load(final Class<? extends RpcService> key) throws Exception {
+ return createProxy(key);
+ }
+
+ });
+
+ private final DOMRpcService domService;
+ private final BindingToNormalizedNodeCodec codec;
+
+ public BindingDOMRpcServiceAdapter(final DOMRpcService domService, final BindingToNormalizedNodeCodec codec) {
+ super();
+ this.domService = domService;
+ this.codec = codec;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T extends RpcService> T getRpcService(final Class<T> rpcService) {
+ Preconditions.checkArgument(rpcService != null, "Rpc Service needs to be specied.");
+ return (T) proxies.getUnchecked(rpcService).getProxy();
+ }
+
+ private RpcServiceAdapter createProxy(final Class<? extends RpcService> key) {
+ Preconditions.checkArgument(BindingReflections.isBindingClass(key));
+ Preconditions.checkArgument(key.isInterface(), "Supplied RPC service type must be interface.");
+ return new RpcServiceAdapter(key, codec, domService);
+ }
+
+ private static final class Builder extends BindingDOMAdapterBuilder<RpcConsumerRegistry> {
+
+ @Override
+ protected RpcConsumerRegistry createInstance(final BindingToNormalizedNodeCodec codec,
+ final ClassToInstanceMap<DOMService> delegates) {
+ final DOMRpcService domRpc = delegates.getInstance(DOMRpcService.class);
+ return new BindingDOMRpcServiceAdapter(domRpc , codec);
+ }
+
+ @Override
+ public Set<? extends Class<? extends DOMService>> getRequiredDelegates() {
+ return ImmutableSet.of(DOMRpcService.class);
+ }
+
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
+import org.opendaylight.yangtools.concepts.Delegator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class BindingDOMTransactionChainAdapter implements BindingTransactionChain, Delegator<DOMTransactionChain> {
+
+ private static final Logger LOG = LoggerFactory.getLogger(BindingDOMTransactionChainAdapter.class);
+
+ private final DOMTransactionChain delegate;
+ private final BindingToNormalizedNodeCodec codec;
+ private final DelegateChainListener domListener;
+ private final TransactionChainListener bindingListener;
+
+ public BindingDOMTransactionChainAdapter(final DOMDataBroker chainFactory,
+ final BindingToNormalizedNodeCodec codec, final TransactionChainListener listener) {
+ Preconditions.checkNotNull(chainFactory, "DOM Transaction chain factory must not be null");
+ this.domListener = new DelegateChainListener();
+ this.bindingListener = listener;
+ this.delegate = chainFactory.createTransactionChain(domListener);
+ this.codec = codec;
+ }
+
+ @Override
+ public DOMTransactionChain getDelegate() {
+ return delegate;
+ }
+
+ @Override
+ public ReadOnlyTransaction newReadOnlyTransaction() {
+ final DOMDataReadOnlyTransaction delegateTx = delegate.newReadOnlyTransaction();
+ return new BindingDOMReadTransactionAdapter(delegateTx, codec);
+ }
+
+ @Override
+ public ReadWriteTransaction newReadWriteTransaction() {
+ final DOMDataReadWriteTransaction delegateTx = delegate.newReadWriteTransaction();
+ return new BindingDOMReadWriteTransactionAdapter(delegateTx, codec) {
+
+ @Override
+ public CheckedFuture<Void, TransactionCommitFailedException> submit() {
+ return listenForFailure(this,super.submit());
+ }
+
+ };
+ }
+
+ @Override
+ public WriteTransaction newWriteOnlyTransaction() {
+ final DOMDataWriteTransaction delegateTx = delegate.newWriteOnlyTransaction();
+ return new BindingDOMWriteTransactionAdapter<DOMDataWriteTransaction>(delegateTx, codec) {
+
+ @Override
+ public CheckedFuture<Void,TransactionCommitFailedException> submit() {
+ return listenForFailure(this,super.submit());
+ }
+
+ };
+ }
+
+ private CheckedFuture<Void, TransactionCommitFailedException> listenForFailure(
+ final WriteTransaction tx, final CheckedFuture<Void, TransactionCommitFailedException> future) {
+ Futures.addCallback(future, new FutureCallback<Void>() {
+ @Override
+ public void onFailure(final Throwable t) {
+ failTransactionChain(tx,t);
+ }
+
+ @Override
+ public void onSuccess(final Void result) {
+ // Intentionally NOOP
+ }
+ });
+
+ return future;
+ }
+
+ private void failTransactionChain(final WriteTransaction tx, final Throwable t) {
+ /*
+ * We asume correct state change for underlaying transaction
+ *
+ * chain, so we are not changing any of our internal state
+ * to mark that we failed.
+ */
+ this.bindingListener.onTransactionChainFailed(this, tx, t);
+ }
+
+ @Override
+ public void close() {
+ delegate.close();
+ }
+
+ private final class DelegateChainListener implements TransactionChainListener {
+
+ @Override
+ public void onTransactionChainFailed(final TransactionChain<?, ?> chain,
+ final AsyncTransaction<?, ?> transaction, final Throwable cause) {
+ Preconditions.checkState(delegate.equals(chain),
+ "Illegal state - listener for %s was invoked for incorrect chain %s.", delegate, chain);
+ /*
+ * Intentionally NOOP, callback for failure, since we
+ * are also listening on each transaction future for failure,
+ * in order to have reference to Binding Transaction (which was seen by client
+ * of this transaction chain), instead of DOM transaction
+ * which is known only to this chain, binding transaction implementation
+ * and underlying transaction chain.
+ *
+ */
+ LOG.debug("Transaction chain {} failed. Failed DOM Transaction {}",this,transaction,cause);
+ }
+
+ @Override
+ public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
+ Preconditions.checkState(delegate.equals(chain),
+ "Illegal state - listener for %s was invoked for incorrect chain %s.", delegate, chain);
+ bindingListener.onTransactionChainSuccessful(BindingDOMTransactionChainAdapter.this);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.common.impl.service.AbstractDataTransaction;
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationException;
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationOperation;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.ListenableFuture;
+
+class BindingDOMWriteTransactionAdapter<T extends DOMDataWriteTransaction> extends
+ AbstractWriteTransaction<T> implements WriteTransaction {
+
+ protected BindingDOMWriteTransactionAdapter(final T delegateTx, final BindingToNormalizedNodeCodec codec) {
+ super(delegateTx, codec);
+ }
+
+ @Override
+ public <U extends DataObject> void put(final LogicalDatastoreType store, final InstanceIdentifier<U> path,
+ final U data) {
+ put(store, path, data,false);
+ }
+
+ @Override
+ public <D extends DataObject> void merge(final LogicalDatastoreType store, final InstanceIdentifier<D> path,
+ final D data) {
+ merge(store, path, data,false);
+ }
+
+
+ @Override
+ protected void ensureParentsByMerge(final LogicalDatastoreType store,
+ final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier normalizedPath, final InstanceIdentifier<?> path) {
+ List<PathArgument> currentArguments = new ArrayList<>();
+ DataNormalizationOperation<?> currentOp = getCodec().getDataNormalizer().getRootOperation();
+ Iterator<PathArgument> iterator = normalizedPath.getPathArguments().iterator();
+ while (iterator.hasNext()) {
+ PathArgument currentArg = iterator.next();
+ try {
+ currentOp = currentOp.getChild(currentArg);
+ } catch (DataNormalizationException e) {
+ throw new IllegalArgumentException(String.format("Invalid child encountered in path %s", path), e);
+ }
+ currentArguments.add(currentArg);
+ org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier currentPath = org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.create(
+ currentArguments);
+
+ getDelegate().merge(store, currentPath, currentOp.createDefault(currentArg));
+ }
+ }
+
+ @Override
+ public void delete(final LogicalDatastoreType store, final InstanceIdentifier<?> path) {
+ doDelete( store, path);
+ }
+
+ @Override
+ public ListenableFuture<RpcResult<TransactionStatus>> commit() {
+ return AbstractDataTransaction.convertToLegacyCommitFuture(submit());
+ }
+
+ @Override
+ public CheckedFuture<Void,TransactionCommitFailedException> submit() {
+ return doSubmit();
+ }
+
+ @Override
+ public boolean cancel() {
+ return doCancel();
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+
+class BindingDataTreeChangeListenerRegistration<L extends DataTreeChangeListener<?>> extends AbstractListenerRegistration<L> {
+
+ private final ListenerRegistration<?> domReg;
+
+ BindingDataTreeChangeListenerRegistration(final L listener, final ListenerRegistration<?> domReg) {
+ super(listener);
+ this.domReg = Preconditions.checkNotNull(domReg);
+ }
+
+ @Override
+ protected void removeRegistration() {
+ domReg.close();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Optional;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.LoadingCache;
+import org.opendaylight.controller.md.sal.binding.api.BindingService;
+import org.opendaylight.controller.md.sal.binding.api.MountPoint;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
+import org.opendaylight.controller.md.sal.dom.api.DOMService;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class BindingMountPointAdapter implements MountPoint {
+
+ private final InstanceIdentifier<?> identifier;
+ private LoadingCache<Class<? extends BindingService>, Optional<BindingService>> services;
+
+ public BindingMountPointAdapter(final BindingToNormalizedNodeCodec codec, final DOMMountPoint domMountPoint) {
+ identifier = codec.getCodecRegistry().fromYangInstanceIdentifier(domMountPoint.getIdentifier());
+ services = CacheBuilder.newBuilder().build(new BindingDOMAdapterLoader(codec) {
+
+ @Override
+ protected DOMService getDelegate(Class<? extends DOMService> reqDeleg) {
+ return domMountPoint.getService(reqDeleg).orNull();
+ }
+ });
+ }
+
+ @Override
+ public InstanceIdentifier<?> getIdentifier() {
+ return identifier;
+ }
+
+ @Override
+ public <T extends BindingService> Optional<T> getService(Class<T> service) {
+ Optional<BindingService> potential = services.getUnchecked(service);
+ if(potential.isPresent()) {
+ return Optional.of(service.cast(potential.get()));
+ }
+ return Optional.absent();
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationRegistration;
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+class BindingRpcAdapterRegistration<T extends RpcService> extends AbstractObjectRegistration<T>{
+
+ private final DOMRpcImplementationRegistration<?> reg;
+
+ public BindingRpcAdapterRegistration(T instance, DOMRpcImplementationRegistration<?> reg) {
+ super(instance);
+ this.reg = reg;
+ }
+
+ @Override
+ protected void removeRegistration() {
+ reg.close();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Optional;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+
+/**
+ *
+ * Defines structural mapping of Normalized Node to Binding data
+ * addressable by Instance Identifier.
+ *
+ * Not all binding data are addressable by instance identifier
+ * and there are some differences.
+ *
+ * See {@link #NOT_ADDRESSABLE},{@link #INVISIBLE_CONTAINER},{@link #VISIBLE_CONTAINER}
+ * for more details.
+ *
+ *
+ */
+enum BindingStructuralType {
+
+ /**
+ * DOM Item is not addressable in Binding Instance Identifier,
+ * data is not lost, but are available only via parent object.
+ *
+ * Such types of data are leaf-lists, leafs, list without keys
+ * or anyxml.
+ *
+ */
+ NOT_ADDRESSABLE,
+ /**
+ * Data container is addressable in NormalizedNode format,
+ * but in Binding it is not represented in Instance Identifier.
+ *
+ * This are choice / case nodes.
+ *
+ * This data is still accessible using parent object and their
+ * children are addressable.
+ *
+ */
+ INVISIBLE_CONTAINER,
+ /**
+ * Data container is addressable in NormalizedNode format,
+ * but in Binding it is not represented in Instance Identifier.
+ *
+ * This are list nodes.
+ *
+ * This data is still accessible using parent object and their
+ * children are addressable.
+ *
+ */
+ INVISIBLE_LIST,
+ /**
+ * Data container is addressable in Binding Instance Identifier format
+ * and also YangInstanceIdentifier format.
+ *
+ */
+ VISIBLE_CONTAINER,
+ /**
+ * Mapping algorithm was unable to detect type or was not updated after introduction
+ * of new NormalizedNode type.
+ */
+ UNKNOWN;
+
+ static BindingStructuralType from(final DataTreeCandidateNode domChildNode) {
+ final Optional<NormalizedNode<?, ?>> dataBased = domChildNode.getDataAfter().or(domChildNode.getDataBefore());
+ if(dataBased.isPresent()) {
+ return from(dataBased.get());
+ }
+ return from(domChildNode.getIdentifier());
+ }
+
+ private static BindingStructuralType from(final PathArgument identifier) {
+ if(identifier instanceof NodeIdentifierWithPredicates || identifier instanceof AugmentationIdentifier) {
+ return VISIBLE_CONTAINER;
+ }
+ if(identifier instanceof NodeWithValue) {
+ return NOT_ADDRESSABLE;
+ }
+ return UNKNOWN;
+ }
+
+ static BindingStructuralType from(final NormalizedNode<?, ?> data) {
+ if(isNotAddressable(data)) {
+ return NOT_ADDRESSABLE;
+ }
+ if(data instanceof MapNode) {
+ return INVISIBLE_LIST;
+ }
+ if(data instanceof ChoiceNode) {
+ return INVISIBLE_CONTAINER;
+ }
+ if(isVisibleContainer(data)) {
+ return VISIBLE_CONTAINER;
+ }
+ return UNKNOWN;
+ }
+
+ private static boolean isVisibleContainer(final NormalizedNode<?, ?> data) {
+ return data instanceof MapEntryNode || data instanceof ContainerNode || data instanceof AugmentationNode;
+ }
+
+ private static boolean isNotAddressable(final NormalizedNode<?, ?> d) {
+ return d instanceof LeafNode
+ || d instanceof AnyXmlNode
+ || d instanceof LeafSetNode
+ || d instanceof LeafSetEntryNode;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableBiMap;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationException;
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationOperation;
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingCodecTree;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingCodecTreeFactory;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingCodecTreeNode;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry;
+import org.opendaylight.yangtools.binding.data.codec.impl.MissingSchemaException;
+import org.opendaylight.yangtools.sal.binding.generator.impl.GeneratedClassLoadingStrategy;
+import org.opendaylight.yangtools.sal.binding.generator.util.BindingRuntimeContext;
+import org.opendaylight.yangtools.yang.binding.BindingMapping;
+import org.opendaylight.yangtools.yang.binding.DataContainer;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.Notification;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class BindingToNormalizedNodeCodec implements BindingCodecTreeFactory, BindingNormalizedNodeSerializer, SchemaContextListener, AutoCloseable {
+
+ private static final long WAIT_DURATION_SEC = 5;
+ private static final Logger LOG = LoggerFactory.getLogger(BindingToNormalizedNodeCodec.class);
+
+ private final BindingNormalizedNodeCodecRegistry codecRegistry;
+
+ private final GeneratedClassLoadingStrategy classLoadingStrategy;
+ private final FutureSchema futureSchema;
+ private final LoadingCache<InstanceIdentifier<?>, YangInstanceIdentifier> iiCache = CacheBuilder.newBuilder()
+ .softValues().build(new CacheLoader<InstanceIdentifier<?>, YangInstanceIdentifier>() {
+
+ @Override
+ public YangInstanceIdentifier load(final InstanceIdentifier<?> key) throws Exception {
+ return toYangInstanceIdentifierBlocking(key);
+ }
+
+ });
+
+ private BindingRuntimeContext runtimeContext;
+ private DataNormalizer legacyToNormalized;
+
+ public BindingToNormalizedNodeCodec(final GeneratedClassLoadingStrategy classLoadingStrategy,
+ final BindingNormalizedNodeCodecRegistry codecRegistry) {
+ this(classLoadingStrategy,codecRegistry,false);
+
+ }
+
+ public BindingToNormalizedNodeCodec(final GeneratedClassLoadingStrategy classLoadingStrategy,
+ final BindingNormalizedNodeCodecRegistry codecRegistry,final boolean waitForSchema) {
+ this.classLoadingStrategy = Preconditions.checkNotNull(classLoadingStrategy,"classLoadingStrategy");
+ this.codecRegistry = Preconditions.checkNotNull(codecRegistry,"codecRegistry");
+ this.futureSchema = waitForSchema ? new FutureSchema(WAIT_DURATION_SEC, TimeUnit.SECONDS) : null;
+ }
+
+ YangInstanceIdentifier toYangInstanceIdentifierBlocking(final InstanceIdentifier<? extends DataObject> binding) {
+ try {
+ return codecRegistry.toYangInstanceIdentifier(binding);
+ } catch (final MissingSchemaException e) {
+ waitForSchema(decompose(binding),e);
+ return codecRegistry.toYangInstanceIdentifier(binding);
+ }
+ }
+
+ /**
+ * Translates supplied Binding Instance Identifier into NormalizedNode
+ * instance identifier.
+ *
+ * @param binding
+ * Binding Instance Identifier
+ * @return DOM Instance Identifier
+ * @throws IllegalArgumentException
+ * If supplied Instance Identifier is not valid.
+ */
+ public YangInstanceIdentifier toNormalized(final InstanceIdentifier<? extends DataObject> binding) {
+ return codecRegistry.toYangInstanceIdentifier(binding);
+ }
+
+ @Override
+ public YangInstanceIdentifier toYangInstanceIdentifier(final InstanceIdentifier<?> binding) {
+ return codecRegistry.toYangInstanceIdentifier(binding);
+ }
+
+
+ YangInstanceIdentifier toYangInstanceIdentifierCached(final InstanceIdentifier<?> binding) {
+ return iiCache .getUnchecked(binding);
+ }
+
+ @Override
+ public <T extends DataObject> Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> toNormalizedNode(
+ final InstanceIdentifier<T> path, final T data) {
+ return codecRegistry.toNormalizedNode(path, data);
+ }
+
+ /**
+ * Converts Binding Map.Entry to DOM Map.Entry
+ *
+ * Same as {@link #toNormalizedNode(InstanceIdentifier, DataObject)}.
+ *
+ * @param binding Map Entry with InstanceIdentifier as key and DataObject as value.
+ * @return DOM Map Entry with {@link YangInstanceIdentifier} as key and {@link NormalizedNode}
+ * as value.
+ */
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ public Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> toNormalizedNode(
+ final Entry<InstanceIdentifier<? extends DataObject>, DataObject> binding) {
+ return toNormalizedNode((InstanceIdentifier) binding.getKey(),binding.getValue());
+ }
+
+ @Override
+ public Entry<InstanceIdentifier<?>, DataObject> fromNormalizedNode(final YangInstanceIdentifier path,
+ final NormalizedNode<?, ?> data) {
+ return codecRegistry.fromNormalizedNode(path, data);
+ }
+
+ @Override
+ public Notification fromNormalizedNodeNotification(final SchemaPath path, final ContainerNode data) {
+ return codecRegistry.fromNormalizedNodeNotification(path, data);
+ }
+
+ @Override
+ public DataObject fromNormalizedNodeRpcData(final SchemaPath path, final ContainerNode data) {
+ return codecRegistry.fromNormalizedNodeRpcData(path, data);
+ }
+
+ @Override
+ public InstanceIdentifier<?> fromYangInstanceIdentifier(final YangInstanceIdentifier dom) {
+ return codecRegistry.fromYangInstanceIdentifier(dom);
+ }
+
+ @Override
+ public ContainerNode toNormalizedNodeNotification(final Notification data) {
+ return codecRegistry.toNormalizedNodeNotification(data);
+ }
+
+ @Override
+ public ContainerNode toNormalizedNodeRpcData(final DataContainer data) {
+ return codecRegistry.toNormalizedNodeRpcData(data);
+ }
+
+ /**
+ *
+ * Returns a Binding-Aware instance identifier from normalized
+ * instance-identifier if it is possible to create representation.
+ *
+ * Returns Optional.absent for cases where target is mixin node except
+ * augmentation.
+ *
+ */
+ public Optional<InstanceIdentifier<? extends DataObject>> toBinding(final YangInstanceIdentifier normalized)
+ throws DeserializationException {
+ try {
+ return Optional.<InstanceIdentifier<? extends DataObject>>fromNullable(codecRegistry.fromYangInstanceIdentifier(normalized));
+ } catch (final IllegalArgumentException e) {
+ return Optional.absent();
+ }
+ }
+
+ public DataNormalizer getDataNormalizer() {
+ return legacyToNormalized;
+ }
+
+ public Optional<Entry<InstanceIdentifier<? extends DataObject>, DataObject>> toBinding(
+ @Nonnull final Entry<YangInstanceIdentifier, ? extends NormalizedNode<?, ?>> normalized)
+ throws DeserializationException {
+ try {
+ /*
+ * This cast is required, due to generics behaviour in openjdk / oracle javac
+ *
+ * InstanceIdentifier has definition InstanceIdentifier<T extends DataObject>,
+ * this means '?' is always  <? extends DataObject>. Eclipse compiler
+ * is able to determine this relationship and treats
+ * Entry<InstanceIdentifier<?>,DataObject> and Entry<InstanceIdentifier<? extends DataObject,DataObject>
+ * as assignable. However openjdk / oracle javac treats this two types
+ * as incompatible and issues a compile error.
+ *
+ * It is safe to loose generic information and cast it to other generic signature.
+ *
+ */
+ @SuppressWarnings("unchecked")
+ final Entry<InstanceIdentifier<? extends DataObject>, DataObject> binding = Entry.class.cast(codecRegistry.fromNormalizedNode(normalized.getKey(), normalized.getValue()));
+ return Optional.fromNullable(binding);
+ } catch (final IllegalArgumentException e) {
+ return Optional.absent();
+ }
+ }
+
+ @Override
+ public void onGlobalContextUpdated(final SchemaContext arg0) {
+ legacyToNormalized = new DataNormalizer(arg0);
+ runtimeContext = BindingRuntimeContext.create(classLoadingStrategy, arg0);
+ codecRegistry.onBindingRuntimeContextUpdated(runtimeContext);
+ if(futureSchema != null) {
+ futureSchema.onRuntimeContextUpdated(runtimeContext);
+ }
+ }
+
+ public <T extends DataObject> Function<Optional<NormalizedNode<?, ?>>, Optional<T>> deserializeFunction(final InstanceIdentifier<T> path) {
+ return codecRegistry.deserializeFunction(path);
+ }
+
+ /**
+ * Returns an default object according to YANG schema for supplied path.
+ *
+ * @param path DOM Path
+ * @return Node with defaults set on.
+ */
+ public NormalizedNode<?, ?> getDefaultNodeFor(final YangInstanceIdentifier path) {
+ final Iterator<PathArgument> iterator = path.getPathArguments().iterator();
+ DataNormalizationOperation<?> currentOp = legacyToNormalized.getRootOperation();
+ while (iterator.hasNext()) {
+ final PathArgument currentArg = iterator.next();
+ try {
+ currentOp = currentOp.getChild(currentArg);
+ } catch (final DataNormalizationException e) {
+ throw new IllegalArgumentException(String.format("Invalid child encountered in path %s", path), e);
+ }
+ }
+ return currentOp.createDefault(path.getLastPathArgument());
+ }
+
+ public BindingNormalizedNodeCodecRegistry getCodecRegistry() {
+ return codecRegistry;
+ }
+
+ @Override
+ public void close() {
+ // NOOP Intentionally
+ }
+
+ public BindingNormalizedNodeCodecRegistry getCodecFactory() {
+ return codecRegistry;
+ }
+
+ // FIXME: This should be probably part of Binding Runtime context
+ public ImmutableBiMap<Method, SchemaPath> getRpcMethodToSchemaPath(final Class<? extends RpcService> key) {
+ final Module module = getModuleBlocking(key);
+ final ImmutableBiMap.Builder<Method, SchemaPath> ret = ImmutableBiMap.<Method, SchemaPath>builder();
+ try {
+ for (final RpcDefinition rpcDef : module.getRpcs()) {
+ final Method method = findRpcMethod(key, rpcDef);
+ ret.put(method, rpcDef.getPath());
+ }
+ } catch (final NoSuchMethodException e) {
+ throw new IllegalStateException("Rpc defined in model does not have representation in generated class.", e);
+ }
+ return ret.build();
+ }
+
+ protected ImmutableBiMap<Method, RpcDefinition> getRpcMethodToSchema(final Class<? extends RpcService> key) {
+ final Module module = getModuleBlocking(key);
+ final ImmutableBiMap.Builder<Method, RpcDefinition> ret = ImmutableBiMap.builder();
+ try {
+ for (final RpcDefinition rpcDef : module.getRpcs()) {
+ final Method method = findRpcMethod(key, rpcDef);
+ ret.put(method, rpcDef);
+ }
+ } catch (final NoSuchMethodException e) {
+ throw new IllegalStateException("Rpc defined in model does not have representation in generated class.", e);
+ }
+ return ret.build();
+ }
+
+ private Module getModuleBlocking(final Class<?> modeledClass) {
+ final QNameModule moduleName = BindingReflections.getQNameModule(modeledClass);
+ final URI namespace = moduleName.getNamespace();
+ final Date revision = moduleName.getRevision();
+ Module module = runtimeContext.getSchemaContext().findModuleByNamespaceAndRevision(namespace, revision);
+ if(module == null && futureSchema != null && futureSchema.waitForSchema(namespace,revision)) {
+ module = runtimeContext.getSchemaContext().findModuleByNamespaceAndRevision(namespace, revision);
+ }
+ Preconditions.checkState(module != null, "Schema for %s is not available.", modeledClass);
+ return module;
+ }
+
+ private void waitForSchema(final Collection<Class<?>> binding, final MissingSchemaException e) {
+ if(futureSchema != null) {
+ LOG.warn("Blocking thread to wait for schema convergence updates for {} {}",futureSchema.getDuration(), futureSchema.getUnit());
+ if(!futureSchema.waitForSchema(binding)) {
+ return;
+ }
+ }
+ throw e;
+ }
+
+ private Method findRpcMethod(final Class<? extends RpcService> key, final RpcDefinition rpcDef) throws NoSuchMethodException {
+ final String methodName = BindingMapping.getMethodName(rpcDef.getQName());
+ if(rpcDef.getInput() != null) {
+ final Class<?> inputClz = runtimeContext.getClassForSchema(rpcDef.getInput());
+ return key.getMethod(methodName, inputClz);
+ }
+ return key.getMethod(methodName);
+ }
+
+ @Override
+ public BindingCodecTree create(final BindingRuntimeContext context) {
+ return codecRegistry.create(context);
+ }
+
+ @Override
+ public BindingCodecTree create(final SchemaContext context, final Class<?>... bindingClasses) {
+ return codecRegistry.create(context, bindingClasses);
+ }
+
+ @Nonnull
+ protected Map.Entry<InstanceIdentifier<?>, BindingCodecTreeNode<?>> getSubtreeCodec(
+ final YangInstanceIdentifier domIdentifier) {
+
+ final BindingCodecTree currentCodecTree = codecRegistry.getCodecContext();
+ final InstanceIdentifier<?> bindingPath = codecRegistry.fromYangInstanceIdentifier(domIdentifier);
+ Preconditions.checkArgument(bindingPath != null);
+ /**
+ * If we are able to deserialize YANG instance identifier, getSubtreeCodec must
+ * return non-null value.
+ */
+ final BindingCodecTreeNode<?> codecContext = currentCodecTree.getSubtreeCodec(bindingPath);
+ return new SimpleEntry<InstanceIdentifier<?>, BindingCodecTreeNode<?>>(bindingPath, codecContext);
+ }
+
+ @SuppressWarnings("unchecked")
+ public Set<Class<? extends Notification>> getNotificationClasses(final Set<SchemaPath> interested) {
+ final Set<Class<? extends Notification>> result = new HashSet<>();
+ final Set<NotificationDefinition> knownNotifications = runtimeContext.getSchemaContext().getNotifications();
+ for (final NotificationDefinition notification : knownNotifications) {
+ if (interested.contains(notification.getPath())) {
+ try {
+ result.add((Class<? extends Notification>) runtimeContext.getClassForSchema(notification));
+ } catch (final IllegalStateException e) {
+ // Ignore
+ LOG.warn("Class for {} is currently not known.",notification.getPath(),e);
+ }
+ }
+ }
+ return result;
+ }
+
+ private static Collection<Class<?>> decompose(final InstanceIdentifier<?> path) {
+ final Set<Class<?>> clazzes = new HashSet<>();
+ for(final InstanceIdentifier.PathArgument arg : path.getPathArguments()) {
+ clazzes.add(arg.getType());
+ }
+ return clazzes;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import java.lang.reflect.Method;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.annotations.RoutingContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+abstract class ContextReferenceExtractor {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ContextReferenceExtractor.class);
+ private static final ContextReferenceExtractor NULL_EXTRACTOR = new ContextReferenceExtractor() {
+
+ @Override
+ InstanceIdentifier<?> extract(final DataObject obj) {
+ return null;
+ }
+ };
+
+
+ private static final LoadingCache<Class<?>, ContextReferenceExtractor> EXTRACTORS = CacheBuilder.newBuilder()
+ .weakKeys().build(new CacheLoader<Class<?>, ContextReferenceExtractor>() {
+
+ @Override
+ public ContextReferenceExtractor load(final Class<?> key) throws Exception {
+ return create(key);
+ }
+ });
+
+
+ private static final String GET_VALUE_NAME = "getValue";
+
+ static ContextReferenceExtractor from(final Class<?> obj) {
+ return EXTRACTORS.getUnchecked(obj);
+ }
+
+ /**
+ * Extract context-reference (Instance Identifier) from
+ * Binding DataObject.
+ *
+ * @param obj DataObject from which context reference
+ * should be extracted.
+ *
+ * @return Instance Identifier representing context reference
+ * or null, if data object does not contain context reference.
+ */
+ abstract @Nullable InstanceIdentifier<?> extract(DataObject obj);
+
+ @Nonnull
+ private static ContextReferenceExtractor create(final Class<?> key) {
+ final Method contextGetter = getContextGetter(key);
+ if (contextGetter == null) {
+ return NULL_EXTRACTOR;
+ }
+ final Class<?> returnType = contextGetter.getReturnType();
+ try {
+ if (InstanceIdentifier.class.isAssignableFrom(returnType)) {
+ return DirectGetterRouteContextExtractor.create(contextGetter);
+ }
+ final Method getValueMethod = findGetValueMethod(returnType,InstanceIdentifier.class);
+ if (getValueMethod != null) {
+ return GetValueRouteContextExtractor.create(contextGetter, getValueMethod);
+ } else {
+ LOG.warn("Class {} can not be used to determine context, falling back to NULL_EXTRACTOR.",returnType);
+ }
+ } catch (final IllegalAccessException e) {
+ LOG.warn("Class {} does not conform to Binding Specification v1. Falling back to NULL_EXTRACTOR", e);
+ }
+ return NULL_EXTRACTOR;
+ }
+
+ @Nullable
+ private static Method findGetValueMethod(final Class<?> type, final Class<?> returnType) {
+ try {
+ final Method method = type.getMethod(GET_VALUE_NAME);
+ if(returnType.equals(method.getReturnType())) {
+ return method;
+ }
+ } catch (final NoSuchMethodException e) {
+ LOG.warn("Value class {} does not comform to Binding Specification v1.", type, e);
+ }
+ return null;
+ }
+
+ private static Method getContextGetter(final Class<?> key) {
+ for (final Method method : key.getMethods()) {
+ if (method.getAnnotation(RoutingContext.class) != null) {
+ return method;
+ }
+ }
+ return null;
+ }
+
+
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Throwables;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+final class DirectGetterRouteContextExtractor extends ContextReferenceExtractor {
+
+ private final static Lookup PUBLIC_LOOKUP = MethodHandles.publicLookup();
+ private final MethodHandle handle;
+
+ private DirectGetterRouteContextExtractor(final MethodHandle rawHandle) {
+ handle = rawHandle.asType(MethodType.methodType(InstanceIdentifier.class, DataObject.class));
+ }
+
+ static final ContextReferenceExtractor create(final Method getterMethod) throws IllegalAccessException {
+ final MethodHandle getterHandle = PUBLIC_LOOKUP.unreflect(getterMethod);
+ return new DirectGetterRouteContextExtractor(getterHandle);
+ }
+
+ @Override
+ InstanceIdentifier<?> extract(final DataObject obj) {
+ try {
+ return (InstanceIdentifier<?>) handle.invokeExact(obj);
+ } catch (final Throwable e) {
+ throw Throwables.propagate(e);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Throwables;
+import com.google.common.util.concurrent.SettableFuture;
+import java.net.URI;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import org.opendaylight.yangtools.sal.binding.generator.util.BindingRuntimeContext;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+
+class FutureSchema implements AutoCloseable {
+
+ private final List<FutureSchemaPredicate> postponedOperations = new CopyOnWriteArrayList<>();
+ private final long duration;
+ private final TimeUnit unit;
+
+ protected FutureSchema(final long time, final TimeUnit unit) {
+ this.duration = time;
+ this.unit = unit;
+ }
+
+ void onRuntimeContextUpdated(final BindingRuntimeContext context) {
+ for (final FutureSchemaPredicate op : postponedOperations) {
+ op.unlockIfPossible(context);
+ }
+ }
+
+ long getDuration() {
+ return duration;
+ }
+
+ TimeUnit getUnit() {
+ return unit;
+ }
+
+ @Override
+ public void close() {
+ for (final FutureSchemaPredicate op : postponedOperations) {
+ op.cancel();
+ }
+ }
+
+ private static boolean isSchemaAvailable(final Class<?> clz, final BindingRuntimeContext context) {
+ final Object schema;
+ if (Augmentation.class.isAssignableFrom(clz)) {
+ schema = context.getAugmentationDefinition(clz);
+ } else {
+ schema = context.getSchemaDefinition(clz);
+ }
+ return schema != null;
+ }
+
+ boolean waitForSchema(final URI namespace, final Date revision) {
+ final FutureSchemaPredicate postponedOp = new FutureSchemaPredicate() {
+
+ @Override
+ public boolean apply(final BindingRuntimeContext input) {
+ return input.getSchemaContext().findModuleByNamespaceAndRevision(namespace, revision) != null;
+ }
+ };
+ return postponedOp.waitForSchema();
+ }
+
+ boolean waitForSchema(final Collection<Class<?>> bindingClasses) {
+ final FutureSchemaPredicate postponedOp = new FutureSchemaPredicate() {
+
+ @Override
+ public boolean apply(final BindingRuntimeContext context) {
+ for (final Class<?> clz : bindingClasses) {
+ if (!isSchemaAvailable(clz, context)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ };
+ return postponedOp.waitForSchema();
+ }
+
+ private abstract class FutureSchemaPredicate implements Predicate<BindingRuntimeContext> {
+
+ final boolean waitForSchema() {
+ try {
+ schemaPromise.get(duration, unit);
+ return true;
+ } catch (final InterruptedException | ExecutionException e) {
+ throw Throwables.propagate(e);
+ } catch (final TimeoutException e) {
+ return false;
+ } finally {
+ postponedOperations.remove(this);
+ }
+ }
+
+ final void unlockIfPossible(final BindingRuntimeContext context) {
+ if (!schemaPromise.isDone() && apply(context)) {
+ schemaPromise.set(null);
+ }
+ }
+
+ final void cancel() {
+ schemaPromise.cancel(true);
+ }
+
+ private final SettableFuture<?> schemaPromise = SettableFuture.create();
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Throwables;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+final class GetValueRouteContextExtractor extends ContextReferenceExtractor {
+
+ private final static Lookup PUBLIC_LOOKUP = MethodHandles.publicLookup();
+ private final MethodHandle contextHandle;
+ private final MethodHandle valueHandle;
+
+ private GetValueRouteContextExtractor(final MethodHandle rawContextHandle, final MethodHandle rawValueHandle) {
+ contextHandle = rawContextHandle.asType(MethodType.methodType(Object.class, DataObject.class));
+ valueHandle = rawValueHandle.asType(MethodType.methodType(InstanceIdentifier.class, Object.class));
+ }
+
+ public static ContextReferenceExtractor create(final Method contextGetter, final Method getValueMethod)
+ throws IllegalAccessException {
+ final MethodHandle rawContextHandle = PUBLIC_LOOKUP.unreflect(contextGetter);
+ final MethodHandle rawValueHandle = PUBLIC_LOOKUP.unreflect(getValueMethod);
+ return new GetValueRouteContextExtractor(rawContextHandle, rawValueHandle);
+ }
+
+ @Override
+ InstanceIdentifier<?> extract(final DataObject obj) {
+ try {
+ final Object ctx = contextHandle.invokeExact(obj);
+ if (ctx != null) {
+ return (InstanceIdentifier<?>) valueHandle.invokeExact(ctx);
+ }
+ return null;
+ } catch (final Throwable e) {
+ throw Throwables.propagate(e);
+ }
+ }
+
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Throwables;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult;
+import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry;
+import org.opendaylight.yangtools.yang.binding.DataContainer;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+final class LazyDOMRpcResultFuture implements CheckedFuture<DOMRpcResult, DOMRpcException> {
+
+ private final ListenableFuture<RpcResult<?>> bindingFuture;
+ private final BindingNormalizedNodeCodecRegistry codec;
+ private volatile DOMRpcResult result;
+
+ private LazyDOMRpcResultFuture(final ListenableFuture<RpcResult<?>> delegate,
+ final BindingNormalizedNodeCodecRegistry codec) {
+ this.bindingFuture = Preconditions.checkNotNull(delegate, "delegate");
+ this.codec = Preconditions.checkNotNull(codec, "codec");
+ }
+
+ static CheckedFuture<DOMRpcResult, DOMRpcException> create(final BindingNormalizedNodeCodecRegistry codec,
+ final ListenableFuture<RpcResult<?>> bindingResult) {
+ return new LazyDOMRpcResultFuture(bindingResult, codec);
+ }
+
+ ListenableFuture<RpcResult<?>> getBindingFuture() {
+ return bindingFuture;
+ }
+
+ @Override
+ public boolean cancel(final boolean mayInterruptIfRunning) {
+ return bindingFuture.cancel(mayInterruptIfRunning);
+ }
+
+ @Override
+ public void addListener(final Runnable listener, final Executor executor) {
+ bindingFuture.addListener(listener, executor);
+ }
+
+ @Override
+ public DOMRpcResult get() throws InterruptedException, ExecutionException {
+ if (result != null) {
+ return result;
+ }
+ return transformIfNecessary(bindingFuture.get());
+ }
+
+ @Override
+ public DOMRpcResult get(final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException,
+ TimeoutException {
+ if (result != null) {
+ return result;
+ }
+ return transformIfNecessary(bindingFuture.get(timeout, unit));
+ }
+
+ @Override
+ public DOMRpcResult checkedGet() throws DOMRpcException {
+ try {
+ return get();
+ } catch (InterruptedException | ExecutionException e) {
+ // FIXME: Add exception mapping
+ throw Throwables.propagate(e);
+ }
+ }
+
+ @Override
+ public DOMRpcResult checkedGet(final long timeout, final TimeUnit unit) throws TimeoutException, DOMRpcException {
+ try {
+ return get(timeout, unit);
+ } catch (InterruptedException | ExecutionException e) {
+ // FIXME: Add exception mapping
+ throw Throwables.propagate(e);
+ }
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return bindingFuture.isCancelled();
+ }
+
+ @Override
+ public boolean isDone() {
+ return bindingFuture.isDone();
+ }
+
+ private synchronized DOMRpcResult transformIfNecessary(final RpcResult<?> input) {
+ if (result == null) {
+ result = transform(input);
+ }
+ return result;
+ }
+
+ private DOMRpcResult transform(final RpcResult<?> input) {
+ if (input.isSuccessful()) {
+ final Object inputData = input.getResult();
+ if (inputData instanceof DataContainer) {
+ return new DefaultDOMRpcResult(codec.toNormalizedNodeRpcData((DataContainer) inputData));
+ } else {
+ return new DefaultDOMRpcResult((NormalizedNode<?, ?>) null);
+ }
+ }
+ return new DefaultDOMRpcResult(input.getErrors());
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingCodecTreeNode;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.ChildOf;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.Identifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Lazily translated {@link DataObjectModification} based on {@link DataTreeCandidateNode}.
+ *
+ * {@link LazyDataObjectModification} represents Data tree change event,
+ * but whole tree is not translated or resolved eagerly, but only child nodes
+ * which are directly accessed by user of data object modification.
+ *
+ * @param <T> Type of Binding Data Object
+ */
+final class LazyDataObjectModification<T extends DataObject> implements DataObjectModification<T> {
+
+ private final static Logger LOG = LoggerFactory.getLogger(LazyDataObjectModification.class);
+
+ private final BindingCodecTreeNode<T> codec;
+ private final DataTreeCandidateNode domData;
+ private final PathArgument identifier;
+ private Collection<DataObjectModification<? extends DataObject>> childNodesCache;
+
+ private LazyDataObjectModification(final BindingCodecTreeNode<T> codec, final DataTreeCandidateNode domData) {
+ this.codec = Preconditions.checkNotNull(codec);
+ this.domData = Preconditions.checkNotNull(domData);
+ this.identifier = codec.deserializePathArgument(domData.getIdentifier());
+ }
+
+ static <T extends DataObject> DataObjectModification<T> create(final BindingCodecTreeNode<T> codec,
+ final DataTreeCandidateNode domData) {
+ return new LazyDataObjectModification<>(codec,domData);
+ }
+
+ private static Collection<DataObjectModification<? extends DataObject>> from(final BindingCodecTreeNode<?> parentCodec,
+ final Collection<DataTreeCandidateNode> domChildNodes) {
+ final List<DataObjectModification<? extends DataObject>> result = new ArrayList<>(domChildNodes.size());
+ populateList(result, parentCodec, domChildNodes);
+ return result;
+ }
+
+ private static void populateList(final List<DataObjectModification<? extends DataObject>> result,
+ final BindingCodecTreeNode<?> parentCodec, final Collection<DataTreeCandidateNode> domChildNodes) {
+ for (final DataTreeCandidateNode domChildNode : domChildNodes) {
+ final BindingStructuralType type = BindingStructuralType.from(domChildNode);
+ if (type != BindingStructuralType.NOT_ADDRESSABLE) {
+ /*
+ * Even if type is UNKNOWN, from perspective of BindingStructuralType
+ * we try to load codec for it. We will use that type to further specify
+ * debug log.
+ */
+ try {
+ final BindingCodecTreeNode<?> childCodec =
+ parentCodec.yangPathArgumentChild(domChildNode.getIdentifier());
+ populateList(result,type, childCodec, domChildNode);
+ } catch (final IllegalArgumentException e) {
+ if (type == BindingStructuralType.UNKNOWN) {
+ LOG.debug("Unable to deserialize unknown DOM node {}",domChildNode,e);
+ } else {
+ LOG.debug("Binding representation for DOM node {} was not found",domChildNode,e);
+ }
+ }
+ }
+ }
+ }
+
+ private static void populateList(final List<DataObjectModification<? extends DataObject>> result,
+ final BindingStructuralType type, final BindingCodecTreeNode<?> childCodec,
+ final DataTreeCandidateNode domChildNode) {
+ switch (type) {
+ case INVISIBLE_LIST:
+ // We use parent codec intentionally.
+ populateListWithSingleCodec(result, childCodec, domChildNode.getChildNodes());
+ break;
+ case INVISIBLE_CONTAINER:
+ populateList(result, childCodec, domChildNode.getChildNodes());
+ break;
+ case UNKNOWN:
+ case VISIBLE_CONTAINER:
+ result.add(create(childCodec, domChildNode));
+ default:
+ break;
+ }
+ }
+
+ private static void populateListWithSingleCodec(final List<DataObjectModification<? extends DataObject>> result,
+ final BindingCodecTreeNode<?> codec, final Collection<DataTreeCandidateNode> childNodes) {
+ for (final DataTreeCandidateNode child : childNodes) {
+ result.add(create(codec, child));
+ }
+ }
+
+ @Override
+ public T getDataBefore() {
+ return deserialize(domData.getDataBefore());
+ }
+
+ @Override
+ public T getDataAfter() {
+ return deserialize(domData.getDataAfter());
+ }
+
+ @Override
+ public Class<T> getDataType() {
+ return codec.getBindingClass();
+ }
+
+ @Override
+ public PathArgument getIdentifier() {
+ return identifier;
+ }
+
+ @Override
+ public DataObjectModification.ModificationType getModificationType() {
+ switch(domData.getModificationType()) {
+ case WRITE:
+ return DataObjectModification.ModificationType.WRITE;
+ case SUBTREE_MODIFIED:
+ return DataObjectModification.ModificationType.SUBTREE_MODIFIED;
+ case DELETE:
+ return DataObjectModification.ModificationType.DELETE;
+
+ default:
+ // TODO: Should we lie about modification type instead of exception?
+ throw new IllegalStateException("Unsupported DOM Modification type " + domData.getModificationType());
+ }
+ }
+
+ @Override
+ public Collection<DataObjectModification<? extends DataObject>> getModifiedChildren() {
+ if (childNodesCache == null) {
+ childNodesCache = from(codec, domData.getChildNodes());
+ }
+ return childNodesCache;
+ }
+
+ @Override
+ public DataObjectModification<? extends DataObject> getModifiedChild(final PathArgument arg) {
+ final List<YangInstanceIdentifier.PathArgument> domArgumentList = new ArrayList<>();
+ final BindingCodecTreeNode<?> childCodec = codec.bindingPathArgumentChild(arg, domArgumentList);
+ final Iterator<YangInstanceIdentifier.PathArgument> toEnter = domArgumentList.iterator();
+ DataTreeCandidateNode current = domData;
+ while (toEnter.hasNext() && current != null) {
+ current = current.getModifiedChild(toEnter.next());
+ }
+ if (current != null) {
+ return create(childCodec, current);
+ }
+ return null;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <C extends Identifiable<K> & ChildOf<? super T>, K extends Identifier<C>> DataObjectModification<C> getModifiedChildListItem(
+ final Class<C> listItem, final K listKey) {
+ return (DataObjectModification<C>) getModifiedChild(new InstanceIdentifier.IdentifiableItem<>(listItem, listKey));
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <C extends ChildOf<? super T>> DataObjectModification<C> getModifiedChildContainer(final Class<C> arg) {
+ return (DataObjectModification<C>) getModifiedChild(new InstanceIdentifier.Item<>(arg));
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <C extends Augmentation<T> & DataObject> DataObjectModification<C> getModifiedAugmentation(
+ final Class<C> augmentation) {
+ return (DataObjectModification<C>) getModifiedChild(new InstanceIdentifier.Item<>(augmentation));
+ }
+
+ private T deserialize(final Optional<NormalizedNode<?, ?>> dataAfter) {
+ if (dataAfter.isPresent()) {
+ return codec.deserialize(dataAfter.get());
+ }
+ return null;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map.Entry;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingCodecTreeNode;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+
+/**
+ * Lazily translated {@link DataTreeModification} based on {@link DataTreeCandidate}.
+ *
+ * {@link DataTreeModification} represents Data tree change event,
+ * but whole tree is not translated or resolved eagerly, but only child nodes
+ * which are directly accessed by user of data object modification.
+ *
+ */
+class LazyDataTreeModification<T extends DataObject> implements DataTreeModification<T> {
+
+ private final DataTreeIdentifier<T> path;
+ private final DataObjectModification<T> rootNode;
+
+ LazyDataTreeModification(final LogicalDatastoreType datastoreType, final InstanceIdentifier<T> path, final BindingCodecTreeNode<T> codec, final DataTreeCandidate domChange) {
+ this.path = new DataTreeIdentifier<>(datastoreType, path);
+ this.rootNode = LazyDataObjectModification.create(codec, domChange.getRootNode());
+ }
+
+ @Override
+ public DataObjectModification<T> getRootNode() {
+ return rootNode;
+ }
+
+ @Override
+ public DataTreeIdentifier<T> getRootPath() {
+ return path;
+ }
+
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ static <T extends DataObject> DataTreeModification<T> create(final BindingToNormalizedNodeCodec codec, final DataTreeCandidate domChange,
+ final LogicalDatastoreType datastoreType) {
+ final Entry<InstanceIdentifier<?>, BindingCodecTreeNode<?>> codecCtx =
+ codec.getSubtreeCodec(domChange.getRootPath());
+ return (DataTreeModification<T>) new LazyDataTreeModification(datastoreType, codecCtx.getKey(), codecCtx.getValue(), domChange);
+ }
+
+ static <T extends DataObject> Collection<DataTreeModification<T>> from(final BindingToNormalizedNodeCodec codec,
+ final Collection<DataTreeCandidate> domChanges, final LogicalDatastoreType datastoreType) {
+ final List<DataTreeModification<T>> result = new ArrayList<>(domChanges.size());
+ for (final DataTreeCandidate domChange : domChanges) {
+ result.add(LazyDataTreeModification.<T>create(codec, domChange, datastoreType));
+ }
+ return result;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Optional;
+import java.util.Collection;
+import java.util.Map;
+import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+/**
+ *
+ * FIXME: Should be this moved to binding-data-codec?
+ *
+ */
+class LazySerializedContainerNode implements ContainerNode {
+
+ private final NodeIdentifier identifier;
+ private final DataObject bindingData;
+
+ private BindingNormalizedNodeCodecRegistry registry;
+ private ContainerNode domData;
+
+ private LazySerializedContainerNode(final QName identifier, final DataObject binding,
+ final BindingNormalizedNodeCodecRegistry registry) {
+ this.identifier = new NodeIdentifier(identifier);
+ this.bindingData = binding;
+ this.registry = registry;
+ this.domData = null;
+ }
+
+ static NormalizedNode<?, ?> create(final SchemaPath rpcName, final DataObject data,
+ final BindingNormalizedNodeCodecRegistry codec) {
+ return new LazySerializedContainerNode(rpcName.getLastComponent(), data, codec);
+ }
+
+ static NormalizedNode<?, ?> withContextRef(final SchemaPath rpcName, final DataObject data,
+ final LeafNode<?> contextRef, final BindingNormalizedNodeCodecRegistry codec) {
+ return new WithContextRef(rpcName.getLastComponent(), data, contextRef, codec);
+ }
+
+ @Override
+ public Map<QName, String> getAttributes() {
+ return delegate().getAttributes();
+ }
+
+ private ContainerNode delegate() {
+ if (domData == null) {
+ domData = registry.toNormalizedNodeRpcData(bindingData);
+ registry = null;
+ }
+ return domData;
+ }
+
+ @Override
+ public final QName getNodeType() {
+ return identifier.getNodeType();
+ }
+
+ @Override
+ public final Collection<DataContainerChild<? extends PathArgument, ?>> getValue() {
+ return delegate().getValue();
+ }
+
+ @Override
+ public final NodeIdentifier getIdentifier() {
+ return identifier;
+ }
+
+ @Override
+ public Optional<DataContainerChild<? extends PathArgument, ?>> getChild(final PathArgument child) {
+ return delegate().getChild(child);
+ }
+
+ @Override
+ public final Object getAttributeValue(final QName name) {
+ return delegate().getAttributeValue(name);
+ }
+
+ final DataObject bindingData() {
+ return bindingData;
+ }
+
+ /**
+ * Lazy Serialized Node with pre-cached serialized leaf holding routing information.
+ *
+ */
+ private static final class WithContextRef extends LazySerializedContainerNode {
+
+ private final LeafNode<?> contextRef;
+
+ protected WithContextRef(final QName identifier, final DataObject binding, final LeafNode<?> contextRef,
+ final BindingNormalizedNodeCodecRegistry registry) {
+ super(identifier, binding, registry);
+ this.contextRef = contextRef;
+ }
+
+ @Override
+ public Optional<DataContainerChild<? extends PathArgument, ?>> getChild(final PathArgument child) {
+ /*
+ * Use precached value of routing field and do not run full serialization if we are
+ * accessing it.
+ */
+ if (contextRef.getIdentifier().equals(child)) {
+ return Optional.<DataContainerChild<? extends PathArgument, ?>>of(contextRef);
+ }
+ return super.getChild(child);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.yangtools.yang.binding.Notification;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+/**
+ * Lazy serialized implementation of DOM Notification.
+ *
+ * This implementation performs serialization of data, only if receiver
+ * of notification actually accessed data from notification.
+ *
+ */
+public final class LazySerializedDOMNotification implements DOMNotification {
+
+ private final BindingNormalizedNodeSerializer codec;
+ private final Notification data;
+ private final SchemaPath type;
+
+ private ContainerNode domBody;
+
+ private LazySerializedDOMNotification(final BindingNormalizedNodeSerializer codec, final Notification data, final SchemaPath type) {
+ super();
+ this.codec = codec;
+ this.data = data;
+ this.type = type;
+ }
+
+ static DOMNotification create(final BindingNormalizedNodeSerializer codec, final Notification data) {
+ final SchemaPath type = SchemaPath.create(true, BindingReflections.findQName(data.getImplementedInterface()));
+ return new LazySerializedDOMNotification(codec, data, type);
+ }
+
+ @Override
+ public SchemaPath getType() {
+ return type;
+ }
+
+ @Override
+ public ContainerNode getBody() {
+ if (domBody == null) {
+ domBody = codec.toNormalizedNodeNotification(data);
+ }
+ return domBody;
+ }
+
+ public Notification getBindingData() {
+ return data;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Map.Entry;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
+import org.opendaylight.controller.md.sal.dom.broker.spi.rpc.RpcRoutingStrategy;
+import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry;
+import org.opendaylight.yangtools.yang.binding.DataContainer;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+class RpcServiceAdapter implements InvocationHandler {
+
+ private final ImmutableMap<Method, RpcInvocationStrategy> rpcNames;
+ private final Class<? extends RpcService> type;
+ private final BindingToNormalizedNodeCodec codec;
+ private final DOMRpcService delegate;
+ private final RpcService proxy;
+
+ RpcServiceAdapter(final Class<? extends RpcService> type, final BindingToNormalizedNodeCodec codec,
+ final DOMRpcService domService) {
+ this.type = Preconditions.checkNotNull(type);
+ this.codec = Preconditions.checkNotNull(codec);
+ this.delegate = Preconditions.checkNotNull(domService);
+ final ImmutableMap.Builder<Method, RpcInvocationStrategy> rpcBuilder = ImmutableMap.builder();
+ for (final Entry<Method, RpcDefinition> rpc : codec.getRpcMethodToSchema(type).entrySet()) {
+ rpcBuilder.put(rpc.getKey(), createStrategy(rpc.getKey(), rpc.getValue()));
+ }
+ rpcNames = rpcBuilder.build();
+ proxy = (RpcService) Proxy.newProxyInstance(type.getClassLoader(), new Class[] {type}, this);
+ }
+
+ private ListenableFuture<RpcResult<?>> invoke0(final SchemaPath schemaPath, final NormalizedNode<?, ?> input) {
+ final CheckedFuture<DOMRpcResult, DOMRpcException> result = delegate.invokeRpc(schemaPath, input);
+ if(result instanceof LazyDOMRpcResultFuture) {
+ return ((LazyDOMRpcResultFuture) result).getBindingFuture();
+ }
+
+ return transformFuture(schemaPath, result, codec.getCodecFactory());
+ }
+
+ private RpcInvocationStrategy createStrategy(final Method method, final RpcDefinition schema) {
+ final RpcRoutingStrategy strategy = RpcRoutingStrategy.from(schema);
+ if (strategy.isContextBasedRouted()) {
+ return new RoutedStrategy(schema.getPath(), method, strategy.getLeaf());
+ }
+ return new NonRoutedStrategy(schema.getPath());
+ }
+
+ RpcService getProxy() {
+ return proxy;
+ }
+
+ @Override
+ public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
+
+ final RpcInvocationStrategy rpc = rpcNames.get(method);
+ if (rpc != null) {
+ if (method.getParameterTypes().length == 0) {
+ return rpc.invokeEmpty();
+ }
+ if (args.length != 1) {
+ throw new IllegalArgumentException("Input must be provided.");
+ }
+ return rpc.invoke((DataObject) args[0]);
+ }
+
+ if (isObjectMethod(method)) {
+ return callObjectMethod(proxy, method, args);
+ }
+ throw new UnsupportedOperationException("Method " + method.toString() + "is unsupported.");
+ }
+
+ private static boolean isObjectMethod(final Method m) {
+ switch (m.getName()) {
+ case "toString":
+ return (m.getReturnType().equals(String.class) && m.getParameterTypes().length == 0);
+ case "hashCode":
+ return (m.getReturnType().equals(int.class) && m.getParameterTypes().length == 0);
+ case "equals":
+ return (m.getReturnType().equals(boolean.class) && m.getParameterTypes().length == 1 && m
+ .getParameterTypes()[0] == Object.class);
+ default:
+ return false;
+ }
+ }
+
+ private Object callObjectMethod(final Object self, final Method m, final Object[] args) {
+ switch (m.getName()) {
+ case "toString":
+ return type.getName() + "$Adapter{delegate=" + delegate.toString() + "}";
+ case "hashCode":
+ return System.identityHashCode(self);
+ case "equals":
+ return (self == args[0]);
+ default:
+ return null;
+ }
+ }
+
+ private static ListenableFuture<RpcResult<?>> transformFuture(final SchemaPath rpc,
+ final ListenableFuture<DOMRpcResult> domFuture, final BindingNormalizedNodeCodecRegistry codec) {
+ return Futures.transform(domFuture, new Function<DOMRpcResult, RpcResult<?>>() {
+ @Override
+ public RpcResult<?> apply(final DOMRpcResult input) {
+ final NormalizedNode<?, ?> domData = input.getResult();
+ final DataObject bindingResult;
+ if (domData != null) {
+ final SchemaPath rpcOutput = rpc.createChild(QName.create(rpc.getLastComponent(), "output"));
+ bindingResult = codec.fromNormalizedNodeRpcData(rpcOutput, (ContainerNode) domData);
+ } else {
+ bindingResult = null;
+ }
+ return RpcResult.class.cast(RpcResultBuilder.success(bindingResult).build());
+ }
+ });
+ }
+
+ private abstract class RpcInvocationStrategy {
+
+ private final SchemaPath rpcName;
+
+ protected RpcInvocationStrategy(final SchemaPath path) {
+ rpcName = path;
+ }
+
+ final ListenableFuture<RpcResult<?>> invoke(final DataObject input) {
+ return invoke0(rpcName, serialize(input));
+ }
+
+ abstract NormalizedNode<?, ?> serialize(DataObject input);
+
+ final ListenableFuture<RpcResult<?>> invokeEmpty() {
+ return invoke0(rpcName, null);
+ }
+
+ final SchemaPath getRpcName() {
+ return rpcName;
+ }
+
+ }
+
+ private final class NonRoutedStrategy extends RpcInvocationStrategy {
+
+ protected NonRoutedStrategy(final SchemaPath path) {
+ super(path);
+ }
+
+ @Override
+ NormalizedNode<?, ?> serialize(final DataObject input) {
+ return LazySerializedContainerNode.create(getRpcName(), input, codec.getCodecRegistry());
+ }
+
+ }
+
+ private final class RoutedStrategy extends RpcInvocationStrategy {
+
+ private final ContextReferenceExtractor refExtractor;
+ private final NodeIdentifier contextName;
+
+ protected RoutedStrategy(final SchemaPath path, final Method rpcMethod, final QName leafName) {
+ super(path);
+ final Class<? extends DataContainer> inputType = BindingReflections.resolveRpcInputClass(rpcMethod).get();
+ refExtractor = ContextReferenceExtractor.from(inputType);
+ this.contextName = new NodeIdentifier(leafName);
+ }
+
+ @Override
+ NormalizedNode<?, ?> serialize(final DataObject input) {
+ final InstanceIdentifier<?> bindingII = refExtractor.extract(input);
+ if (bindingII != null) {
+ final YangInstanceIdentifier yangII = codec.toYangInstanceIdentifierCached(bindingII);
+ final LeafNode<?> contextRef = ImmutableNodes.leafNode(contextName, yangII);
+ return LazySerializedContainerNode.withContextRef(getRpcName(), input, contextRef,
+ codec.getCodecRegistry());
+ }
+ return LazySerializedContainerNode.create(getRpcName(), input, codec.getCodecRegistry());
+ }
+
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.spi;
+
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.ImmutableClassToInstanceMap;
+import com.google.common.collect.MutableClassToInstanceMap;
+import java.util.Set;
+import org.opendaylight.yangtools.concepts.Builder;
+
+public abstract class AdapterBuilder<T,D> implements Builder<T> {
+
+ private final ClassToInstanceMap<D> delegates = MutableClassToInstanceMap.create();
+
+ public abstract Set<? extends Class<? extends D>> getRequiredDelegates();
+
+ protected abstract T createInstance(ClassToInstanceMap<D> delegates);
+
+ private void checkAllRequiredServices() {
+ for(final Class<? extends D> type : getRequiredDelegates()) {
+ Preconditions.checkState(delegates.get(type) != null, "Requires service %s is not defined.",type);
+ }
+ }
+
+ public final <V extends D>void addDelegate(final Class<V> type,final D impl) {
+ delegates.put(type,impl);
+ }
+
+ @Override
+ public final T build() {
+ checkAllRequiredServices();
+ return createInstance(ImmutableClassToInstanceMap.<D,D>copyOf(delegates));
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.spi;
+
+import com.google.common.base.Optional;
+import com.google.common.cache.CacheLoader;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+public abstract class AdapterLoader<T, D> extends CacheLoader<Class<? extends T>, Optional<T>> {
+
+ @Override
+ public Optional<T> load(final Class<? extends T> key) {
+
+ final AdapterBuilder<? extends T, D> builder = createBuilder(key);
+ for(final Class<? extends D> reqDeleg : builder.getRequiredDelegates()) {
+ final D deleg = getDelegate(reqDeleg);
+ if(deleg != null) {
+ builder.addDelegate(reqDeleg,deleg);
+ } else {
+ return Optional.absent();
+ }
+ }
+ return Optional.<T>of(builder.build());
+ }
+
+ @Nullable
+ protected abstract D getDelegate(Class<? extends D> reqDeleg);
+
+ @Nonnull
+ protected abstract AdapterBuilder<? extends T, D> createBuilder(Class<? extends T> key);
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.codegen.impl;
+
+import com.google.common.util.concurrent.ForwardingBlockingQueue;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import javassist.ClassPool;
+import org.apache.commons.lang3.StringUtils;
+import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SingletonHolder {
+ private static final Logger logger = LoggerFactory.getLogger(SingletonHolder.class);
+
+ public static final ClassPool CLASS_POOL = ClassPool.getDefault();
+ public static final JavassistUtils JAVASSIST = JavassistUtils.forClassPool(CLASS_POOL);
+
+ public static final int CORE_NOTIFICATION_THREADS = 4;
+ public static final int MAX_NOTIFICATION_THREADS = 32;
+ // block caller thread after MAX_NOTIFICATION_THREADS + MAX_NOTIFICATION_QUEUE_SIZE pending notifications
+ public static final int MAX_NOTIFICATION_QUEUE_SIZE = 1000;
+ public static final int NOTIFICATION_THREAD_LIFE = 15;
+ private static final String NOTIFICATION_QUEUE_SIZE_PROPERTY = "mdsal.notificationqueue.size";
+
+ private static ListeningExecutorService NOTIFICATION_EXECUTOR = null;
+ private static ListeningExecutorService COMMIT_EXECUTOR = null;
+ private static ListeningExecutorService CHANGE_EVENT_EXECUTOR = null;
+
+ /**
+ * @deprecated This method is only used from configuration modules and thus callers of it
+ * should use service injection to make the executor configurable.
+ */
+ @Deprecated
+ public static synchronized ListeningExecutorService getDefaultNotificationExecutor() {
+
+ if (NOTIFICATION_EXECUTOR == null) {
+ int queueSize = MAX_NOTIFICATION_QUEUE_SIZE;
+ final String queueValue = System.getProperty(NOTIFICATION_QUEUE_SIZE_PROPERTY);
+ if (StringUtils.isNotBlank(queueValue)) {
+ try {
+ queueSize = Integer.parseInt(queueValue);
+ logger.trace("Queue size was set to {}", queueSize);
+ } catch (final NumberFormatException e) {
+ logger.warn("Cannot parse {} as set by {}, using default {}", queueValue,
+ NOTIFICATION_QUEUE_SIZE_PROPERTY, queueSize);
+ }
+ }
+
+ // Overriding the queue:
+ // ThreadPoolExecutor would not create new threads if the queue is not full, thus adding
+ // occurs in RejectedExecutionHandler.
+ // This impl saturates threadpool first, then queue. When both are full caller will get blocked.
+ final BlockingQueue<Runnable> delegate = new LinkedBlockingQueue<>(queueSize);
+ final BlockingQueue<Runnable> queue = new ForwardingBlockingQueue<Runnable>() {
+ @Override
+ protected BlockingQueue<Runnable> delegate() {
+ return delegate;
+ }
+
+ @Override
+ public boolean offer(final Runnable r) {
+ // ThreadPoolExecutor will spawn a new thread after core size is reached only
+ // if the queue.offer returns false.
+ return false;
+ }
+ };
+
+ final ThreadFactory factory = new ThreadFactoryBuilder()
+ .setDaemon(true)
+ .setNameFormat("md-sal-binding-notification-%d")
+ .build();
+
+ final ThreadPoolExecutor executor = new ThreadPoolExecutor(CORE_NOTIFICATION_THREADS, MAX_NOTIFICATION_THREADS,
+ NOTIFICATION_THREAD_LIFE, TimeUnit.SECONDS, queue, factory,
+ new RejectedExecutionHandler() {
+ // if the max threads are met, then it will raise a rejectedExecution. We then push to the queue.
+ @Override
+ public void rejectedExecution(final Runnable r, final ThreadPoolExecutor executor) {
+ try {
+ executor.getQueue().put(r);
+ } catch (final InterruptedException e) {
+ throw new RejectedExecutionException("Interrupted while waiting on the queue", e);
+ }
+ }
+ });
+
+ NOTIFICATION_EXECUTOR = MoreExecutors.listeningDecorator(executor);
+ }
+
+ return NOTIFICATION_EXECUTOR;
+ }
+
+ /**
+ * @deprecated This method is only used from configuration modules and thus callers of it
+ * should use service injection to make the executor configurable.
+ */
+ @Deprecated
+ public static synchronized ListeningExecutorService getDefaultCommitExecutor() {
+ if (COMMIT_EXECUTOR == null) {
+ final ThreadFactory factory = new ThreadFactoryBuilder().setDaemon(true).setNameFormat("md-sal-binding-commit-%d").build();
+ /*
+ * FIXME: this used to be newCacheThreadPool(), but MD-SAL does not have transaction
+ * ordering guarantees, which means that using a concurrent threadpool results
+ * in application data being committed in random order, potentially resulting
+ * in inconsistent data being present. Once proper primitives are introduced,
+ * concurrency can be reintroduced.
+ */
+ final ExecutorService executor = Executors.newSingleThreadExecutor(factory);
+ COMMIT_EXECUTOR = MoreExecutors.listeningDecorator(executor);
+ }
+
+ return COMMIT_EXECUTOR;
+ }
+
+ public static ExecutorService getDefaultChangeEventExecutor() {
+ if (CHANGE_EVENT_EXECUTOR == null) {
+ final ThreadFactory factory = new ThreadFactoryBuilder().setDaemon(true).setNameFormat("md-sal-binding-change-%d").build();
+ /*
+ * FIXME: this used to be newCacheThreadPool(), but MD-SAL does not have transaction
+ * ordering guarantees, which means that using a concurrent threadpool results
+ * in application data being committed in random order, potentially resulting
+ * in inconsistent data being present. Once proper primitives are introduced,
+ * concurrency can be reintroduced.
+ */
+ final ExecutorService executor = Executors.newSingleThreadExecutor(factory);
+ CHANGE_EVENT_EXECUTOR = MoreExecutors.listeningDecorator(executor);
+ }
+
+ return CHANGE_EVENT_EXECUTOR;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.impl;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.collect.ImmutableClassToInstanceMap;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.MountPointService;
+import org.opendaylight.controller.md.sal.binding.util.AbstractBindingSalProviderInstance;
+import org.opendaylight.controller.md.sal.binding.util.BindingContextUtils;
+import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.binding.api.BindingAwareConsumer;
+import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
+import org.opendaylight.controller.sal.binding.api.BindingAwareService;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.controller.sal.binding.api.NotificationService;
+import org.opendaylight.controller.sal.binding.api.RpcConsumerRegistry;
+import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
+import org.opendaylight.controller.sal.binding.api.mount.MountProviderService;
+import org.opendaylight.controller.sal.binding.api.mount.MountService;
+import org.opendaylight.controller.sal.binding.api.rpc.RpcContextIdentifier;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.Mutable;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.osgi.framework.BundleContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class RootBindingAwareBroker implements Mutable, Identifiable<String>, BindingAwareBroker, AutoCloseable,
+ RpcProviderRegistry {
+
+ private final static Logger LOG = LoggerFactory.getLogger(RootBindingAwareBroker.class);
+
+ RootSalInstance controllerRoot;
+
+ private final String identifier;
+
+ private RpcProviderRegistry rpcBroker;
+
+ private NotificationProviderService notificationBroker;
+
+ @SuppressWarnings("deprecation")
+ private DataProviderService legacyDataBroker;
+
+ private DataBroker dataBroker;
+
+ private MountProviderService legacyMount;
+
+ private ImmutableClassToInstanceMap<BindingAwareService> supportedConsumerServices;
+
+ private ImmutableClassToInstanceMap<BindingAwareService> supportedProviderServices;
+
+ private MountPointService mountService;
+
+ public void setLegacyMountManager(final MountProviderService legacyMount) {
+ this.legacyMount = legacyMount;
+ }
+
+ public RootBindingAwareBroker(final String instanceName) {
+ this.identifier = instanceName;
+ }
+
+ @Override
+ public String getIdentifier() {
+ return identifier;
+ }
+
+ public RootSalInstance getRoot() {
+ return controllerRoot;
+ }
+
+ public DataProviderService getDataBroker() {
+ return this.legacyDataBroker;
+ }
+
+ public NotificationProviderService getNotificationBroker() {
+ return this.notificationBroker;
+ }
+
+ public RpcProviderRegistry getRpcProviderRegistry() {
+ return this.rpcBroker;
+ }
+
+ public RpcProviderRegistry getRpcBroker() {
+ return rpcBroker;
+ }
+
+ public MountPointService getMountService() {
+ return mountService;
+ }
+
+ public MountProviderService getLegacyMount() {
+ return legacyMount;
+ }
+
+ public void setDataBroker(final DataBroker asyncDataBroker) {
+ dataBroker = asyncDataBroker;
+ }
+
+ public void setMountService(final MountPointService mount) {
+ this.mountService = mount;
+ }
+
+ public void setRpcBroker(final RpcProviderRegistry rpcBroker) {
+ this.rpcBroker = rpcBroker;
+ }
+
+ public void setNotificationBroker(final NotificationProviderService notificationBroker) {
+ this.notificationBroker = notificationBroker;
+ }
+
+ public void setLegacyDataBroker(final DataProviderService dataBroker) {
+ this.legacyDataBroker = dataBroker;
+ }
+
+ public void start() {
+ checkState(controllerRoot == null, "Binding Aware Broker was already started.");
+ LOG.info("Starting Binding Aware Broker: {}", identifier);
+
+ controllerRoot = new RootSalInstance(getRpcProviderRegistry(), getNotificationBroker(), getDataBroker());
+
+ final ImmutableClassToInstanceMap.Builder<BindingAwareService> consBuilder = ImmutableClassToInstanceMap
+ .builder();
+
+ consBuilder.put(NotificationService.class, getRoot());
+ consBuilder.put(DataBrokerService.class, getRoot());
+ consBuilder.put(RpcConsumerRegistry.class, getRoot());
+ if (dataBroker != null) {
+ consBuilder.put(DataBroker.class, dataBroker);
+ }
+ consBuilder.put(MountPointService.class, mountService);
+ consBuilder.put(MountService.class, legacyMount).build();
+ supportedConsumerServices = consBuilder.build();
+ supportedProviderServices = ImmutableClassToInstanceMap.<BindingAwareService> builder()
+ .putAll(supportedConsumerServices).put(NotificationProviderService.class, getRoot())
+ .put(DataProviderService.class, getRoot()).put(RpcProviderRegistry.class, getRoot())
+ .put(MountProviderService.class, legacyMount).build();
+ }
+
+ @Override
+ public ConsumerContext registerConsumer(final BindingAwareConsumer consumer, final BundleContext ctx) {
+ return registerConsumer(consumer);
+ }
+
+ @Override
+ public ProviderContext registerProvider(final BindingAwareProvider provider, final BundleContext ctx) {
+ return registerProvider(provider);
+ }
+
+ @Override
+ public ConsumerContext registerConsumer(final BindingAwareConsumer consumer) {
+ checkState(supportedConsumerServices != null, "Broker is not initialized.");
+ return BindingContextUtils.createConsumerContextAndInitialize(consumer, supportedConsumerServices);
+ }
+
+ @Override
+ public ProviderContext registerProvider(final BindingAwareProvider provider) {
+ checkState(supportedProviderServices != null, "Broker is not initialized.");
+ return BindingContextUtils.createProviderContextAndInitialize(provider, supportedProviderServices);
+ }
+
+ @Override
+ public void close() throws Exception {
+ // FIXME: Close all sessions
+ }
+
+ @Override
+ public <T extends RpcService> RoutedRpcRegistration<T> addRoutedRpcImplementation(final Class<T> type,
+ final T implementation) throws IllegalStateException {
+ return getRoot().addRoutedRpcImplementation(type, implementation);
+ }
+
+ @Override
+ public <T extends RpcService> RpcRegistration<T> addRpcImplementation(final Class<T> type, final T implementation)
+ throws IllegalStateException {
+ return getRoot().addRpcImplementation(type, implementation);
+ }
+
+ @Override
+ public <T extends RpcService> T getRpcService(final Class<T> module) {
+ return getRoot().getRpcService(module);
+ }
+
+ @Override
+ public <L extends RouteChangeListener<RpcContextIdentifier, InstanceIdentifier<?>>> ListenerRegistration<L> registerRouteChangeListener(
+ final L arg0) {
+ return getRoot().registerRouteChangeListener(arg0);
+ }
+
+ public class RootSalInstance extends
+ AbstractBindingSalProviderInstance<DataProviderService, NotificationProviderService, RpcProviderRegistry> {
+
+ public RootSalInstance(final RpcProviderRegistry rpcRegistry,
+ final NotificationProviderService notificationBroker, final DataProviderService dataBroker) {
+ super(rpcRegistry, notificationBroker, dataBroker);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+import org.junit.Test;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.bi.ba.rpcservice.rev140701.RockTheHouseInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.bi.ba.rpcservice.rev140701.RockTheHouseInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.rpc.routing.rev140701.EncapsulatedRoute;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.rpc.routing.rev140701.EncapsulatedRouteInGrouping;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.rpc.routing.rev140701.RoutedSimpleRouteInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.rpc.routing.rev140701.RoutedSimpleRouteInputBuilder;
+import org.opendaylight.yangtools.yang.binding.DataContainer;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public final class ContextExtractorTest {
+
+ public interface Transitive extends EncapsulatedRouteInGrouping {
+
+ }
+
+ private static final InstanceIdentifier<?> TEST_ROUTE = InstanceIdentifier.create(Top.class);
+ private static final Transitive TEST_GROUPING = new Transitive() {
+
+ @Override
+ public Class<? extends DataContainer> getImplementedInterface() {
+ return Transitive.class;
+ }
+
+ @Override
+ public EncapsulatedRoute getRoute() {
+ return new EncapsulatedRoute(TEST_ROUTE);
+ }
+ };
+
+ @Test
+ public void testNonRoutedExtraction() {
+ final ContextReferenceExtractor extractor = ContextReferenceExtractor.from(RockTheHouseInput.class);
+ final RockTheHouseInput input = new RockTheHouseInputBuilder().build();
+ final InstanceIdentifier<?> extractedValue = extractor.extract(input);
+ assertNull(extractedValue);
+ }
+
+ @Test
+ public void testRoutedSimpleExtraction() {
+ final ContextReferenceExtractor extractor = ContextReferenceExtractor.from(RoutedSimpleRouteInput.class);
+ final RoutedSimpleRouteInput input = new RoutedSimpleRouteInputBuilder().setRoute(TEST_ROUTE).build();
+ final InstanceIdentifier<?> extractedValue = extractor.extract(input);
+ assertSame(TEST_ROUTE,extractedValue);
+ }
+
+ @Test
+ public void testRoutedEncapsulatedExtraction() {
+ final ContextReferenceExtractor extractor = ContextReferenceExtractor.from(EncapsulatedRouteInGrouping.class);
+ final InstanceIdentifier<?> extractedValue = extractor.extract(TEST_GROUPING);
+ assertSame(TEST_ROUTE,extractedValue);
+
+ }
+
+ @Test
+ public void testRoutedEncapsulatedTransitiveExtraction() {
+ final ContextReferenceExtractor extractor = ContextReferenceExtractor.from(Transitive.class);
+ final InstanceIdentifier<?> extractedValue = extractor.extract(TEST_GROUPING);
+ assertSame(TEST_ROUTE,extractedValue);
+ }
+ }
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.concurrent.TimeUnit;
+import org.opendaylight.controller.md.sal.binding.compat.HeliumNotificationProviderServiceAdapter;
+
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
+import org.opendaylight.controller.md.sal.binding.api.NotificationService;
+import org.opendaylight.controller.md.sal.binding.test.AbstractNotificationBrokerTest;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.OpendaylightMdsalListTestListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.TwoLevelListChanged;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.TwoLevelListChangedBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.NotificationListener;
+
+public class BackwardsCompatibleNotificationBrokerTest extends AbstractNotificationBrokerTest {
+
+ private NotificationProviderService notificationProviderService;
+
+ @Before
+ public void initTest() {
+ final NotificationService notificationService = getNotificationService();
+ final NotificationPublishService notificationPublishService = getNotificationPublishService();
+ notificationProviderService = new HeliumNotificationProviderServiceAdapter(notificationPublishService, notificationService);
+ }
+
+ private TwoLevelListChanged createTestData() {
+ final TwoLevelListChangedBuilder tb = new TwoLevelListChangedBuilder();
+ tb.setTopLevelList(ImmutableList.of(new TopLevelListBuilder().setKey(new TopLevelListKey("test")).build()));
+ return tb.build();
+ }
+
+ @Test
+ public void testNotifSubscriptionForwarded() throws InterruptedException {
+ final CountDownLatch latch = new CountDownLatch(1);
+ final TwoLevelListChanged testData = createTestData();
+
+ final NotifTestListenerChild testNotifListener = new NotifTestListenerChild(latch);
+ final ListenerRegistration<NotificationListener> listenerRegistration =
+ notificationProviderService.registerNotificationListener(testNotifListener);
+ notificationProviderService.publish(testData);
+
+ latch.await(500L, TimeUnit.MILLISECONDS);
+ assertTrue(testNotifListener.getReceivedNotifications().size() == 1);
+ assertEquals(testData, testNotifListener.getReceivedNotifications().get(0));
+ listenerRegistration.close();
+ }
+
+ private static class NotifTestListenerChild extends NotifTestListener {
+
+ public NotifTestListenerChild(final CountDownLatch latch) {
+ super(latch);
+ }
+ }
+ private static class NotifTestListener implements OpendaylightMdsalListTestListener {
+ private List<TwoLevelListChanged> receivedNotifications = new ArrayList<>();
+ private CountDownLatch latch;
+
+ public NotifTestListener(CountDownLatch latch) {
+ this.latch = latch;
+ }
+
+ @Override
+ public void onTwoLevelListChanged(TwoLevelListChanged notification) {
+ receivedNotifications.add(notification);
+ latch.countDown();
+ }
+
+ public List<TwoLevelListChanged> getReceivedNotifications() {
+ return receivedNotifications;
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.binding.impl.test;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.md.sal.binding.api.ClusteredDataChangeListener;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMDataBrokerAdapter;
+import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.ClusteredDOMDataChangeListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry;
+import org.opendaylight.yangtools.sal.binding.generator.impl.GeneratedClassLoadingStrategy;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+
+public class BindingDOMDataBrokerAdapterTest {
+
+ @Mock
+ DOMDataBroker dataBroker;
+
+ @Mock
+ GeneratedClassLoadingStrategy classLoadingStrategy;
+ @Mock
+ BindingNormalizedNodeCodecRegistry codecRegistry;
+
+ @Mock
+ YangInstanceIdentifier yangInstanceIdentifier;
+
+
+ private static final InstanceIdentifier<Top> TOP_PATH = InstanceIdentifier
+ .create(Top.class);
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testClusteredDataChangeListernerRegisteration() {
+
+ BindingToNormalizedNodeCodec codec = new BindingToNormalizedNodeCodec(classLoadingStrategy, codecRegistry);
+
+ BindingDOMDataBrokerAdapter bindingDOMDataBrokerAdapter = new BindingDOMDataBrokerAdapter(dataBroker, codec);
+ Mockito.when(codecRegistry.toYangInstanceIdentifier(TOP_PATH)).thenReturn(yangInstanceIdentifier);
+
+ ArgumentCaptor<ClusteredDOMDataChangeListener> clusteredDOMListener = ArgumentCaptor.
+ forClass(ClusteredDOMDataChangeListener.class);
+ ArgumentCaptor<LogicalDatastoreType> logicalDatastoreType = ArgumentCaptor.forClass(LogicalDatastoreType.class);
+ ArgumentCaptor<AsyncDataBroker.DataChangeScope> dataChangeScope = ArgumentCaptor.
+ forClass(AsyncDataBroker.DataChangeScope.class);
+ ArgumentCaptor<YangInstanceIdentifier> yangInstanceIdentifier = ArgumentCaptor.
+ forClass(YangInstanceIdentifier.class);
+
+ TestListener listener = new TestListener();
+
+ bindingDOMDataBrokerAdapter.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL, TOP_PATH, listener,
+ AsyncDataBroker.DataChangeScope.BASE);
+ Mockito.verify(dataBroker).registerDataChangeListener(logicalDatastoreType.capture(), yangInstanceIdentifier.capture(),
+ clusteredDOMListener.capture(), dataChangeScope.capture());
+
+ }
+
+ private class TestListener implements ClusteredDataChangeListener {
+
+ @Override
+ public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.binding.impl.test;
+
+import static org.junit.Assert.assertTrue;
+
+import javassist.ClassPool;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec;
+import org.opendaylight.controller.md.sal.binding.test.AbstractSchemaAwareTest;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.TreeComplexUsesAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.TreeLeafOnlyAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
+import org.opendaylight.yangtools.binding.data.codec.gen.impl.DataObjectSerializerGenerator;
+import org.opendaylight.yangtools.binding.data.codec.gen.impl.StreamWriterGenerator;
+import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry;
+import org.opendaylight.yangtools.sal.binding.generator.impl.GeneratedClassLoadingStrategy;
+import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class BindingNormalizedCodecTest extends AbstractSchemaAwareTest {
+
+ private static final TopLevelListKey TOP_FOO_KEY = new TopLevelListKey("foo");
+ private static final InstanceIdentifier<TopLevelList> BA_TOP_LEVEL_LIST = InstanceIdentifier
+ .builder(Top.class).child(TopLevelList.class, TOP_FOO_KEY).toInstance();
+ private static final InstanceIdentifier<TreeLeafOnlyAugment> BA_TREE_LEAF_ONLY = BA_TOP_LEVEL_LIST.augmentation(TreeLeafOnlyAugment.class);
+ private static final InstanceIdentifier<TreeComplexUsesAugment> BA_TREE_COMPLEX_USES = BA_TOP_LEVEL_LIST.augmentation(TreeComplexUsesAugment.class);
+ private static final QName SIMPLE_VALUE_QNAME = QName.create(TreeComplexUsesAugment.QNAME, "simple-value");
+
+
+ private BindingToNormalizedNodeCodec codec;
+
+ @Override
+ protected void setupWithSchema(final SchemaContext context) {
+ final DataObjectSerializerGenerator streamWriter = StreamWriterGenerator.create(JavassistUtils.forClassPool(ClassPool.getDefault()));
+ final BindingNormalizedNodeCodecRegistry registry = new BindingNormalizedNodeCodecRegistry(streamWriter);
+ codec = new BindingToNormalizedNodeCodec(GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy(), registry);
+ codec.onGlobalContextUpdated(context);
+ };
+
+ @Test
+ public void testComplexAugmentationSerialization() {
+
+ final PathArgument lastArg = codec.toYangInstanceIdentifier(BA_TREE_COMPLEX_USES).getLastPathArgument();
+ assertTrue(lastArg instanceof AugmentationIdentifier);
+ }
+
+
+ @Test
+ public void testLeafOnlyAugmentationSerialization() {
+
+ final PathArgument leafOnlyLastArg = codec.toYangInstanceIdentifier(BA_TREE_LEAF_ONLY).getLastPathArgument();
+ assertTrue(leafOnlyLastArg instanceof AugmentationIdentifier);
+ assertTrue(((AugmentationIdentifier) leafOnlyLastArg).getPossibleChildNames().contains(SIMPLE_VALUE_QNAME));
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.binding.impl.test;
+
+import static org.opendaylight.controller.md.sal.binding.test.AssertCollections.assertContains;
+import static org.opendaylight.controller.md.sal.binding.test.AssertCollections.assertEmpty;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.TOP_FOO_KEY;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.path;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.topLevelList;
+
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.binding.test.AbstractDataChangeListenerTest;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.TreeComplexUsesAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.TreeComplexUsesAugmentBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.complex.from.grouping.ContainerWithUsesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.TopBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Regression test suite for Bug 1125 - Can't detect switch disconnection
+ * https://bugs.opendaylight.org/show_bug.cgi?id=1125
+ */
+public class Bug1125RegressionTest extends AbstractDataChangeListenerTest {
+
+ private static final InstanceIdentifier<Top> TOP_PATH = InstanceIdentifier
+ .create(Top.class);
+ private static final InstanceIdentifier<TopLevelList> TOP_FOO_PATH = TOP_PATH
+ .child(TopLevelList.class, TOP_FOO_KEY);
+
+ private static final InstanceIdentifier<TreeComplexUsesAugment> FOO_AUGMENT_PATH = TOP_FOO_PATH
+ .augmentation(TreeComplexUsesAugment.class);
+
+ private static final InstanceIdentifier<TreeComplexUsesAugment> WILDCARDED_AUGMENT_PATH = TOP_PATH
+ .child(TopLevelList.class).augmentation(
+ TreeComplexUsesAugment.class);
+
+ private void writeInitialState() {
+ WriteTransaction initialTx = getDataBroker().newWriteOnlyTransaction();
+ initialTx.put(LogicalDatastoreType.OPERATIONAL, TOP_PATH,
+ new TopBuilder().build());
+ TreeComplexUsesAugment fooAugment = new TreeComplexUsesAugmentBuilder()
+ .setContainerWithUses(
+ new ContainerWithUsesBuilder().setLeafFromGrouping(
+ "foo").build()).build();
+ initialTx.put(LogicalDatastoreType.OPERATIONAL, path(TOP_FOO_KEY),
+ topLevelList(TOP_FOO_KEY, fooAugment));
+ assertCommit(initialTx.submit());
+ }
+
+ private void delete(final InstanceIdentifier<?> path) {
+ WriteTransaction tx = getDataBroker().newWriteOnlyTransaction();
+ tx.delete(LogicalDatastoreType.OPERATIONAL, path);
+ assertCommit(tx.submit());
+ }
+
+ private void verifyRemoved(
+ final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> event) {
+ assertEmpty(event.getCreatedData());
+ assertEmpty(event.getUpdatedData());
+ assertContains(event.getRemovedPaths(), FOO_AUGMENT_PATH);
+ }
+
+ private void deleteAndListenAugment(final DataChangeScope scope,
+ final InstanceIdentifier<?> path) {
+ writeInitialState();
+ TestListener listener = createListener(
+ LogicalDatastoreType.OPERATIONAL, WILDCARDED_AUGMENT_PATH,
+ scope);
+ delete(path);
+ verifyRemoved(listener.event());
+ }
+
+ @Test
+ public void deleteAndListenAugment() {
+
+ deleteAndListenAugment(DataChangeScope.ONE, TOP_PATH);
+
+ deleteAndListenAugment(DataChangeScope.BASE, TOP_PATH);
+
+ deleteAndListenAugment(DataChangeScope.SUBTREE, TOP_PATH);
+
+ deleteAndListenAugment(DataChangeScope.BASE, TOP_FOO_PATH);
+
+ deleteAndListenAugment(DataChangeScope.ONE, TOP_FOO_PATH);
+
+ deleteAndListenAugment(DataChangeScope.SUBTREE, TOP_FOO_PATH);
+
+ deleteAndListenAugment(DataChangeScope.BASE, FOO_AUGMENT_PATH);
+
+ deleteAndListenAugment(DataChangeScope.ONE, FOO_AUGMENT_PATH);
+
+ deleteAndListenAugment(DataChangeScope.SUBTREE, FOO_AUGMENT_PATH);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl.test;
+
+import static org.opendaylight.controller.md.sal.binding.test.AssertCollections.assertContains;
+import static org.opendaylight.controller.md.sal.binding.test.AssertCollections.assertEmpty;
+import static org.opendaylight.controller.md.sal.binding.test.AssertCollections.assertNotContains;
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.TOP_FOO_KEY;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.USES_ONE_KEY;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.USES_TWO_KEY;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.complexUsesAugment;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.path;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.top;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.topLevelList;
+
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
+import org.opendaylight.controller.md.sal.binding.test.AbstractDataChangeListenerTest;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.TreeComplexUsesAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ *
+ * This testsuite tries to replicate bug 1333 and tests regresion of it
+ * using test-model with similar construction as one reported.
+ *
+ *
+ * See https://bugs.opendaylight.org/show_bug.cgi?id=1333 for Bug Description
+ *
+ */
+public class Bug1333DataChangeListenerTest extends AbstractDataChangeListenerTest{
+
+ private static final InstanceIdentifier<Top> TOP_PATH = InstanceIdentifier.create(Top.class);
+
+ private static final InstanceIdentifier<?> AUGMENT_WILDCARD = TOP_PATH.child(TopLevelList.class).augmentation(
+ TreeComplexUsesAugment.class);
+
+ public void writeTopWithListItem(final LogicalDatastoreType store) {
+ ReadWriteTransaction tx = getDataBroker().newReadWriteTransaction();
+ Top topItem = top(topLevelList(TOP_FOO_KEY, complexUsesAugment(USES_ONE_KEY, USES_TWO_KEY)));
+ tx.put(store, TOP_PATH, topItem);
+ assertCommit(tx.submit());
+ }
+
+ public void deleteItem(final LogicalDatastoreType store, final InstanceIdentifier<?> path) {
+ ReadWriteTransaction tx = getDataBroker().newReadWriteTransaction();
+ tx.delete(store, path);
+ assertCommit(tx.submit());
+ }
+
+ @Test
+ public void writeTopWithListItemAugmentedListenTopSubtree() {
+ TestListener listener = createListener(CONFIGURATION,TOP_PATH, DataChangeScope.SUBTREE);
+ listener.startCapture();
+
+ writeTopWithListItem(CONFIGURATION);
+
+ AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> event = listener.event();
+
+ assertContains(event.getCreatedData(), TOP_PATH);
+ assertContains(event.getCreatedData(), path(TOP_FOO_KEY));
+ assertContains(event.getCreatedData(), path(TOP_FOO_KEY, TreeComplexUsesAugment.class));
+ assertContains(event.getCreatedData(), path(TOP_FOO_KEY, USES_ONE_KEY));
+ assertContains(event.getCreatedData(), path(TOP_FOO_KEY, USES_TWO_KEY));
+
+ assertEmpty(event.getUpdatedData());
+ assertEmpty(event.getRemovedPaths());
+ }
+
+ @Test
+ public void writeTopWithListItemAugmentedListenAugmentSubtreeWildcarded() {
+ TestListener listener = createListener(CONFIGURATION,AUGMENT_WILDCARD, DataChangeScope.SUBTREE);
+ listener.startCapture();
+ writeTopWithListItem(CONFIGURATION);
+
+ AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> event = listener.event();
+
+ /*
+ * Event should not contain parent nodes
+ */
+ assertNotContains(event.getCreatedData(), TOP_PATH, path(TOP_FOO_KEY));
+
+ assertContains(event.getCreatedData(), path(TOP_FOO_KEY, TreeComplexUsesAugment.class));
+ assertContains(event.getCreatedData(), path(TOP_FOO_KEY, USES_ONE_KEY));
+ assertContains(event.getCreatedData(), path(TOP_FOO_KEY, USES_TWO_KEY));
+
+ assertEmpty(event.getUpdatedData());
+ assertEmpty(event.getRemovedPaths());
+ }
+
+ @Test
+ public void deleteAugmentChildListenTopSubtree() {
+ writeTopWithListItem(CONFIGURATION);
+ TestListener listener = createListener(CONFIGURATION, TOP_PATH, DataChangeScope.SUBTREE);
+ InstanceIdentifier<?> deletePath = path(TOP_FOO_KEY,USES_ONE_KEY);
+ deleteItem(CONFIGURATION,deletePath);
+
+ AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> event = listener.event();
+
+
+ assertEmpty(event.getCreatedData());
+
+ assertContains(event.getRemovedPaths(), deletePath);
+
+ assertContains(event.getUpdatedData(), TOP_PATH);
+ assertContains(event.getUpdatedData(), path(TOP_FOO_KEY));
+ assertContains(event.getUpdatedData(), path(TOP_FOO_KEY, TreeComplexUsesAugment.class));
+
+ assertNotContains(event.getCreatedData(), path(TOP_FOO_KEY, USES_TWO_KEY));
+ }
+
+ @Test
+ public void deleteAugmentChildListenAugmentSubtreeWildcarded() {
+ writeTopWithListItem(CONFIGURATION);
+
+ TestListener listener = createListener(CONFIGURATION, AUGMENT_WILDCARD, DataChangeScope.SUBTREE);
+ InstanceIdentifier<?> deletePath = path(TOP_FOO_KEY,USES_ONE_KEY);
+ deleteItem(CONFIGURATION,deletePath);
+ AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> event = listener.event();
+
+ assertEmpty(event.getCreatedData());
+
+ assertContains(event.getUpdatedData(), path(TOP_FOO_KEY, TreeComplexUsesAugment.class));
+
+ /*
+ * Event should not contain parent nodes
+ */
+ assertNotContains(event.getUpdatedData(), TOP_PATH, path(TOP_FOO_KEY));
+
+ assertContains(event.getRemovedPaths(), deletePath);
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.binding.impl.test;
+
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.binding.test.AbstractDataChangeListenerTest;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.TreeComplexUsesAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.TreeLeafOnlyUsesAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.complex.from.grouping.ListViaUses;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.complex.from.grouping.ListViaUsesKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.controller.md.sal.binding.test.AssertCollections.assertContains;
+import static org.opendaylight.controller.md.sal.binding.test.AssertCollections.assertEmpty;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.top;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.TOP_FOO_KEY;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.topLevelList;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.leafOnlyUsesAugment;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.complexUsesAugment;
+
+public class Bug1418AugmentationTest extends AbstractDataChangeListenerTest{
+ private static final InstanceIdentifier<Top> TOP = InstanceIdentifier.create(Top.class);
+ private static final InstanceIdentifier<TopLevelList> TOP_FOO = TOP.child(TopLevelList.class, TOP_FOO_KEY);
+ private static final InstanceIdentifier<TreeLeafOnlyUsesAugment> SIMPLE_AUGMENT =
+ TOP.child(TopLevelList.class, TOP_FOO_KEY).augmentation(TreeLeafOnlyUsesAugment.class);
+ private static final InstanceIdentifier<TreeComplexUsesAugment> COMPLEX_AUGMENT =
+ TOP.child(TopLevelList.class, TOP_FOO_KEY).augmentation(TreeComplexUsesAugment.class);
+ private static final ListViaUsesKey LIST_VIA_USES_KEY =
+ new ListViaUsesKey("list key");
+ private static final ListViaUsesKey LIST_VIA_USES_KEY_MOD =
+ new ListViaUsesKey("list key modified");
+
+ @Test
+ public void leafOnlyAugmentationCreatedTest() {
+ TestListener listener = createListener(LogicalDatastoreType.CONFIGURATION, SIMPLE_AUGMENT,
+ AsyncDataBroker.DataChangeScope.SUBTREE);
+ WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction();
+ writeTx.put(LogicalDatastoreType.CONFIGURATION, TOP, top());
+ writeTx.put(LogicalDatastoreType.CONFIGURATION, TOP_FOO, topLevelList(new TopLevelListKey(TOP_FOO_KEY)));
+ writeTx.put(LogicalDatastoreType.CONFIGURATION, SIMPLE_AUGMENT, leafOnlyUsesAugment("test leaf"));
+ assertCommit(writeTx.submit());
+ assertTrue(listener.hasEvent());
+ AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> event = listener.event();
+ assertContains(event.getCreatedData(), SIMPLE_AUGMENT);
+ assertEmpty(event.getUpdatedData());
+ assertEmpty(event.getOriginalData());
+ assertEmpty(event.getRemovedPaths());
+ }
+
+ @Test
+ public void leafOnlyAugmentationUpdatedTest() {
+ WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction();
+ writeTx.put(LogicalDatastoreType.CONFIGURATION, TOP, top());
+ writeTx.put(LogicalDatastoreType.CONFIGURATION, TOP_FOO, topLevelList(new TopLevelListKey(TOP_FOO_KEY)));
+ writeTx.put(LogicalDatastoreType.CONFIGURATION, SIMPLE_AUGMENT, leafOnlyUsesAugment("test leaf"));
+ assertCommit(writeTx.submit());
+ TestListener listener = createListener(LogicalDatastoreType.CONFIGURATION, SIMPLE_AUGMENT,
+ AsyncDataBroker.DataChangeScope.SUBTREE);
+ writeTx = getDataBroker().newWriteOnlyTransaction();
+ writeTx.put(LogicalDatastoreType.CONFIGURATION, SIMPLE_AUGMENT, leafOnlyUsesAugment("test leaf changed"));
+ assertCommit(writeTx.submit());
+ assertTrue(listener.hasEvent());
+ AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> event = listener.event();
+ assertContains(event.getUpdatedData(), SIMPLE_AUGMENT);
+ assertContains(event.getOriginalData(), SIMPLE_AUGMENT);
+ assertEmpty(event.getCreatedData());
+ assertEmpty(event.getRemovedPaths());
+ }
+
+ @Test
+ public void leafOnlyAugmentationDeletedTest() {
+ WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction();
+ writeTx.put(LogicalDatastoreType.CONFIGURATION, TOP, top());
+ writeTx.put(LogicalDatastoreType.CONFIGURATION, TOP_FOO, topLevelList(new TopLevelListKey(TOP_FOO_KEY)));
+ writeTx.put(LogicalDatastoreType.CONFIGURATION, SIMPLE_AUGMENT, leafOnlyUsesAugment("test leaf"));
+ assertCommit(writeTx.submit());
+ TestListener listener = createListener(LogicalDatastoreType.CONFIGURATION, SIMPLE_AUGMENT,
+ AsyncDataBroker.DataChangeScope.SUBTREE);
+ writeTx = getDataBroker().newWriteOnlyTransaction();
+ writeTx.delete(LogicalDatastoreType.CONFIGURATION, SIMPLE_AUGMENT);
+ assertCommit(writeTx.submit());
+ assertTrue(listener.hasEvent());
+ AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> event = listener.event();
+ assertContains(event.getRemovedPaths(), SIMPLE_AUGMENT);
+ assertContains(event.getOriginalData(), SIMPLE_AUGMENT);
+ assertEmpty(event.getCreatedData());
+ assertEmpty(event.getUpdatedData());
+ }
+
+ @Test
+ public void complexAugmentationCreatedTest() {
+ TestListener listener = createListener(LogicalDatastoreType.CONFIGURATION, COMPLEX_AUGMENT,
+ AsyncDataBroker.DataChangeScope.SUBTREE);
+ WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction();
+ writeTx.put(LogicalDatastoreType.CONFIGURATION, TOP, top());
+ writeTx.put(LogicalDatastoreType.CONFIGURATION, TOP_FOO, topLevelList(new TopLevelListKey(TOP_FOO_KEY)));
+ writeTx.put(LogicalDatastoreType.CONFIGURATION, COMPLEX_AUGMENT, complexUsesAugment(LIST_VIA_USES_KEY));
+ assertCommit(writeTx.submit());
+ assertTrue(listener.hasEvent());
+ AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> event = listener.event();
+ assertContains(event.getCreatedData(), COMPLEX_AUGMENT);
+ assertContains(event.getCreatedData(), COMPLEX_AUGMENT.child(ListViaUses.class, LIST_VIA_USES_KEY));
+ assertEmpty(event.getUpdatedData());
+ assertEmpty(event.getOriginalData());
+ assertEmpty(event.getRemovedPaths());
+ }
+
+ @Test
+ public void complexAugmentationUpdatedTest() {
+ WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction();
+ writeTx.put(LogicalDatastoreType.CONFIGURATION, TOP, top());
+ writeTx.put(LogicalDatastoreType.CONFIGURATION, TOP_FOO, topLevelList(new TopLevelListKey(TOP_FOO_KEY)));
+ writeTx.put(LogicalDatastoreType.CONFIGURATION, COMPLEX_AUGMENT, complexUsesAugment(LIST_VIA_USES_KEY));
+ assertCommit(writeTx.submit());
+ TestListener listener = createListener(LogicalDatastoreType.CONFIGURATION, COMPLEX_AUGMENT,
+ AsyncDataBroker.DataChangeScope.SUBTREE);
+ writeTx = getDataBroker().newWriteOnlyTransaction();
+ writeTx.put(LogicalDatastoreType.CONFIGURATION, COMPLEX_AUGMENT, complexUsesAugment(LIST_VIA_USES_KEY_MOD));
+ assertCommit(writeTx.submit());
+ assertTrue(listener.hasEvent());
+ AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> event = listener.event();
+ assertContains(event.getUpdatedData(), COMPLEX_AUGMENT);
+ assertContains(event.getCreatedData(), COMPLEX_AUGMENT.child(ListViaUses.class, LIST_VIA_USES_KEY_MOD));
+ assertContains(event.getRemovedPaths(), COMPLEX_AUGMENT.child(ListViaUses.class, LIST_VIA_USES_KEY));
+ assertContains(event.getOriginalData(), COMPLEX_AUGMENT);
+ assertContains(event.getOriginalData(), COMPLEX_AUGMENT.child(ListViaUses.class, LIST_VIA_USES_KEY));
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.binding.impl.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.TOP_BAR_KEY;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.TOP_FOO_KEY;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.USES_ONE_KEY;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.complexUsesAugment;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.path;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.top;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.topLevelList;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.util.concurrent.SettableFuture;
+import java.util.Collection;
+import java.util.concurrent.TimeUnit;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMDataBrokerAdapter;
+import org.opendaylight.controller.md.sal.binding.test.AbstractDataBrokerTest;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.TreeComplexUsesAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.TwoLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+
+public class DataTreeChangeListenerTest extends AbstractDataBrokerTest {
+
+ private static final InstanceIdentifier<Top> TOP_PATH = InstanceIdentifier.create(Top.class);
+ private static final PathArgument TOP_ARGUMENT= TOP_PATH.getPathArguments().iterator().next();
+ private static final InstanceIdentifier<TopLevelList> FOO_PATH = path(TOP_FOO_KEY);
+ private static final PathArgument FOO_ARGUMENT = Iterables.getLast(FOO_PATH.getPathArguments());
+ private static final TopLevelList FOO_DATA = topLevelList(TOP_FOO_KEY, complexUsesAugment(USES_ONE_KEY));
+ private static final InstanceIdentifier<TopLevelList> BAR_PATH = path(TOP_BAR_KEY);
+ private static final PathArgument BAR_ARGUMENT = Iterables.getLast(BAR_PATH.getPathArguments());
+ private static final TopLevelList BAR_DATA = topLevelList(TOP_BAR_KEY);
+private static final DataTreeIdentifier<Top> TOP_IDENTIFIER = new DataTreeIdentifier<Top>(LogicalDatastoreType.OPERATIONAL,
+ TOP_PATH);
+
+ private static final Top TOP_INITIAL_DATA = top(FOO_DATA);
+
+ private BindingDOMDataBrokerAdapter dataBrokerImpl;
+
+ private static final class EventCapturingListener<T extends DataObject> implements DataTreeChangeListener<T> {
+
+ private SettableFuture<Collection<DataTreeModification<T>>> changes = SettableFuture.create();
+
+ @Override
+ public void onDataTreeChanged(final Collection<DataTreeModification<T>> changes) {
+ this.changes.set(changes);
+
+ }
+
+ Collection<DataTreeModification<T>> nextEvent() throws Exception {
+ final Collection<DataTreeModification<T>> result = changes.get(200,TimeUnit.MILLISECONDS);
+ changes = SettableFuture.create();
+ return result;
+ }
+
+ }
+
+ @Override
+ protected Iterable<YangModuleInfo> getModuleInfos() throws Exception {
+ return ImmutableSet.of(
+ BindingReflections.getModuleInfo(TwoLevelList.class),
+ BindingReflections.getModuleInfo(TreeComplexUsesAugment.class)
+ );
+ }
+
+ @Override
+ protected void setupWithDataBroker(final DataBroker dataBroker) {
+ dataBrokerImpl = (BindingDOMDataBrokerAdapter) dataBroker;
+ }
+
+ @Test
+ public void testTopLevelListener() throws Exception {
+ final EventCapturingListener<Top> listener = new EventCapturingListener<>();
+ dataBrokerImpl.registerDataTreeChangeListener(TOP_IDENTIFIER, listener);
+
+ createAndVerifyTop(listener);
+
+ putTx(BAR_PATH, BAR_DATA).submit().checkedGet();
+ final DataObjectModification<Top> afterBarPutEvent = Iterables.getOnlyElement(listener.nextEvent()).getRootNode();
+ verifyModification(afterBarPutEvent, TOP_ARGUMENT, ModificationType.SUBTREE_MODIFIED);
+ final DataObjectModification<TopLevelList> barPutMod = afterBarPutEvent.getModifiedChildListItem(TopLevelList.class, TOP_BAR_KEY);
+ assertNotNull(barPutMod);
+ verifyModification(barPutMod, BAR_ARGUMENT, ModificationType.WRITE);
+
+ deleteTx(BAR_PATH).submit().checkedGet();
+ final DataObjectModification<Top> afterBarDeleteEvent = Iterables.getOnlyElement(listener.nextEvent()).getRootNode();
+ verifyModification(afterBarDeleteEvent, TOP_ARGUMENT, ModificationType.SUBTREE_MODIFIED);
+ final DataObjectModification<TopLevelList> barDeleteMod = afterBarDeleteEvent.getModifiedChildListItem(TopLevelList.class, TOP_BAR_KEY);
+ verifyModification(barDeleteMod, BAR_ARGUMENT, ModificationType.DELETE);
+ }
+
+ @Test
+ public void testWildcardedListListener() throws Exception {
+ final EventCapturingListener<TopLevelList> listener = new EventCapturingListener<>();
+ final DataTreeIdentifier<TopLevelList> wildcard = new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL, TOP_PATH.child(TopLevelList.class));
+ dataBrokerImpl.registerDataTreeChangeListener(wildcard, listener);
+
+ putTx(TOP_PATH, TOP_INITIAL_DATA).submit().checkedGet();
+
+ final DataTreeModification<TopLevelList> fooWriteEvent = Iterables.getOnlyElement(listener.nextEvent());
+ assertEquals(FOO_PATH, fooWriteEvent.getRootPath().getRootIdentifier());
+ verifyModification(fooWriteEvent.getRootNode(), FOO_ARGUMENT, ModificationType.WRITE);
+
+ putTx(BAR_PATH, BAR_DATA).submit().checkedGet();
+ final DataTreeModification<TopLevelList> barWriteEvent = Iterables.getOnlyElement(listener.nextEvent());
+ assertEquals(BAR_PATH, barWriteEvent.getRootPath().getRootIdentifier());
+ verifyModification(barWriteEvent.getRootNode(), BAR_ARGUMENT, ModificationType.WRITE);
+
+ deleteTx(BAR_PATH).submit().checkedGet();
+ final DataTreeModification<TopLevelList> barDeleteEvent = Iterables.getOnlyElement(listener.nextEvent());
+ assertEquals(BAR_PATH, barDeleteEvent.getRootPath().getRootIdentifier());
+ verifyModification(barDeleteEvent.getRootNode(), BAR_ARGUMENT, ModificationType.DELETE);
+ }
+
+
+
+ private void createAndVerifyTop(final EventCapturingListener<Top> listener) throws Exception {
+ putTx(TOP_PATH,TOP_INITIAL_DATA).submit().checkedGet();
+ final Collection<DataTreeModification<Top>> events = listener.nextEvent();
+
+ assertFalse("Non empty collection should be received.",events.isEmpty());
+ final DataTreeModification<Top> initialWrite = Iterables.getOnlyElement(events);
+ final DataObjectModification<? extends DataObject> initialNode = initialWrite.getRootNode();
+ verifyModification(initialNode,TOP_PATH.getPathArguments().iterator().next(),ModificationType.WRITE);
+ assertEquals(TOP_INITIAL_DATA, initialNode.getDataAfter());
+ }
+
+ private void verifyModification(final DataObjectModification<? extends DataObject> barWrite, final PathArgument pathArg,
+ final ModificationType eventType) {
+ assertEquals(pathArg.getType(), barWrite.getDataType());
+ assertEquals(eventType,barWrite.getModificationType());
+ assertEquals(pathArg, barWrite.getIdentifier());
+ }
+
+ private <T extends DataObject> WriteTransaction putTx(final InstanceIdentifier<T> path,final T data) {
+ final WriteTransaction tx = dataBrokerImpl.newWriteOnlyTransaction();
+ tx.put(LogicalDatastoreType.OPERATIONAL, path, data);
+ return tx;
+ }
+
+ private WriteTransaction deleteTx(final InstanceIdentifier<?> path) {
+ final WriteTransaction tx = dataBrokerImpl.newWriteOnlyTransaction();
+ tx.delete(LogicalDatastoreType.OPERATIONAL, path);
+ return tx;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.binding.impl.test;
+
+import static junit.framework.TestCase.assertNotNull;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.compat.HydrogenDataBrokerAdapter;
+import org.opendaylight.controller.md.sal.binding.test.AbstractDataBrokerTest;
+import org.opendaylight.controller.md.sal.binding.test.DataBrokerTestCustomizer;
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+
+@Deprecated
+public class ForwardedBackwardsCompatibleDataBrokerTest extends
+ AbstractDataBrokerTest {
+
+ private HydrogenDataBrokerAdapter dataBroker;
+ private static final InstanceIdentifier<Top> TOP_PATH = InstanceIdentifier.create(Top.class);
+ private static final TopLevelListKey TOP_LIST_KEY = new TopLevelListKey("foo");
+ private static final InstanceIdentifier<TopLevelList> NODE_PATH = TOP_PATH.child(TopLevelList.class, TOP_LIST_KEY);
+ private static final TopLevelList NODE = new TopLevelListBuilder().setKey(TOP_LIST_KEY).build();
+
+ @Override
+ protected Iterable<YangModuleInfo> getModuleInfos() throws Exception {
+ return ImmutableSet.of(BindingReflections.getModuleInfo(TopLevelList.class));
+ }
+
+ @Override
+ protected DataBrokerTestCustomizer createDataBrokerTestCustomizer() {
+ return new DataBrokerTestCustomizer();
+ }
+
+ @Override
+ protected void setupWithDataBroker(final DataBroker dataBroker) {
+ super.setupWithDataBroker(dataBroker);
+ this.dataBroker = new HydrogenDataBrokerAdapter(dataBroker);
+ }
+
+
+
+
+ /**
+ * The purpose of this test is to exercise the backwards compatible broker
+ * <p>
+ * This test tries to execute the code which ensures that the parents
+ * for a given node get automatically created.
+ *
+ * @see org.opendaylight.controller.md.sal.binding.impl.AbstractReadWriteTransaction#ensureParentsByMerge(org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType, org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier, org.opendaylight.yangtools.yang.binding.InstanceIdentifier)
+ */
+ @Test
+ public void testEnsureParentsByMerge() throws InterruptedException, ExecutionException {
+ final DataModificationTransaction writeTx =
+ dataBroker.beginTransaction();
+
+ writeTx.putOperationalData(NODE_PATH, NODE);
+
+ writeTx.commit().get();
+
+ // TOP_PATH should exist as it is the parent of NODE_PATH
+ final DataObject object = dataBroker.readOperationalData(TOP_PATH);
+
+ assertNotNull(object);
+
+ }
+
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import org.junit.Assert;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
+import org.opendaylight.controller.md.sal.binding.test.AbstractNotificationBrokerTest;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.OpendaylightMdsalListTestListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.TwoLevelListChanged;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.TwoLevelListChangedBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ForwardedNotificationAdapterTest extends AbstractNotificationBrokerTest {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ForwardedNotificationAdapterTest.class);
+
+ @Override
+ protected Iterable<YangModuleInfo> getModuleInfos() throws Exception {
+ return ImmutableSet.of(BindingReflections.getModuleInfo(TwoLevelListChanged.class));
+
+ }
+
+ private TwoLevelListChanged createTestData() {
+ final TwoLevelListChangedBuilder tb = new TwoLevelListChangedBuilder();
+ tb.setTopLevelList(ImmutableList.of(new TopLevelListBuilder().setKey(new TopLevelListKey("test")).build()));
+ return tb.build();
+ }
+
+ @Test
+ public void testNotifSubscription() throws InterruptedException {
+ final CountDownLatch latch = new CountDownLatch(1);
+ final TwoLevelListChanged testData = createTestData();
+
+ final TestNotifListener testNotifListener = new TestNotifListener(latch);
+ final ListenerRegistration<TestNotifListener> listenerRegistration = getNotificationService()
+ .registerNotificationListener(testNotifListener);
+ getNotificationPublishService().putNotification(testData);
+
+ latch.await();
+ assertTrue(testNotifListener.getReceivedNotifications().size() == 1);
+ assertEquals(testData, testNotifListener.getReceivedNotifications().get(0));
+
+ listenerRegistration.close();
+ }
+
+ @Test
+ public void testNotifSubscription2() throws InterruptedException {
+ final CountDownLatch latch = new CountDownLatch(1);
+ final TwoLevelListChanged testData = createTestData();
+
+ final TestNotifListener testNotifListener = new TestNotifListener(latch);
+ final ListenerRegistration<TestNotifListener> listenerRegistration = getNotificationService()
+ .registerNotificationListener(testNotifListener);
+ try {
+ getNotificationPublishService().offerNotification(testData).get(1, TimeUnit.SECONDS);
+ } catch (ExecutionException | TimeoutException e) {
+ LOG.error("Notification delivery failed", e);
+ Assert.fail("notification should be delivered");
+ }
+
+ latch.await();
+ assertTrue(testNotifListener.getReceivedNotifications().size() == 1);
+ assertEquals(testData, testNotifListener.getReceivedNotifications().get(0));
+
+ listenerRegistration.close();
+ }
+
+ @Test
+ public void testNotifSubscription3() throws InterruptedException {
+ final CountDownLatch latch = new CountDownLatch(1);
+ final TwoLevelListChanged testData = createTestData();
+
+ final TestNotifListener testNotifListener = new TestNotifListener(latch);
+ final ListenerRegistration<TestNotifListener> listenerRegistration = getNotificationService()
+ .registerNotificationListener(testNotifListener);
+ assertNotSame(NotificationPublishService.REJECTED,
+ getNotificationPublishService().offerNotification(testData, 5, TimeUnit.SECONDS));
+
+ latch.await();
+ assertTrue(testNotifListener.getReceivedNotifications().size() == 1);
+ assertEquals(testData, testNotifListener.getReceivedNotifications().get(0));
+
+ listenerRegistration.close();
+ }
+
+ private static class TestNotifListener implements OpendaylightMdsalListTestListener {
+ private List<TwoLevelListChanged> receivedNotifications = new ArrayList<>();
+ private CountDownLatch latch;
+
+ public TestNotifListener(CountDownLatch latch) {
+ this.latch = latch;
+ }
+
+ @Override
+ public void onTwoLevelListChanged(TwoLevelListChanged notification) {
+ receivedNotifications.add(notification);
+ latch.countDown();
+ }
+
+ public List<TwoLevelListChanged> getReceivedNotifications() {
+ return receivedNotifications;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl.test;
+import static org.junit.Assert.assertFalse;
+import static org.opendaylight.controller.md.sal.binding.test.AssertCollections.assertContains;
+import static org.opendaylight.controller.md.sal.binding.test.AssertCollections.assertEmpty;
+import static org.opendaylight.controller.md.sal.binding.test.AssertCollections.assertNotContains;
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.TOP_BAR_KEY;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.TOP_FOO_KEY;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.top;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.topLevelList;
+
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.binding.test.AbstractDataChangeListenerTest;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ *
+ * This testsuite tests explanation for data change scope and data modifications
+ * which were described in
+ * https://lists.opendaylight.org/pipermail/controller-dev/2014-July/005541.html
+ *
+ *
+ */
+public class ListInsertionDataChangeListenerTest extends AbstractDataChangeListenerTest{
+
+ private static final InstanceIdentifier<Top> TOP = InstanceIdentifier.create(Top.class);
+ private static final InstanceIdentifier<TopLevelList> WILDCARDED = TOP.child(TopLevelList.class);
+ private static final InstanceIdentifier<TopLevelList> TOP_FOO = TOP.child(TopLevelList.class, TOP_FOO_KEY);
+ private static final InstanceIdentifier<TopLevelList> TOP_BAR = TOP.child(TopLevelList.class, TOP_BAR_KEY);
+
+
+ @Override
+ protected void setupWithDataBroker(final DataBroker dataBroker) {
+ WriteTransaction initialTx = dataBroker.newWriteOnlyTransaction();
+ initialTx.put(CONFIGURATION, TOP, top(topLevelList(TOP_FOO_KEY)));
+ assertCommit(initialTx.submit());
+ }
+
+ @Test
+ public void replaceTopNodeSubtreeListeners() {
+ TestListener topListener = createListener(CONFIGURATION, TOP, DataChangeScope.SUBTREE);
+ TestListener allListener = createListener(CONFIGURATION, WILDCARDED, DataChangeScope.SUBTREE);
+ TestListener fooListener = createListener(CONFIGURATION, TOP_FOO, DataChangeScope.SUBTREE);
+ TestListener barListener = createListener(CONFIGURATION, TOP_BAR, DataChangeScope.SUBTREE);
+
+ ReadWriteTransaction writeTx = getDataBroker().newReadWriteTransaction();
+ writeTx.put(CONFIGURATION, TOP, top(topLevelList(TOP_BAR_KEY)));
+ assertCommit(writeTx.submit());
+ AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> top = topListener.event();
+ AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> all = allListener.event();
+ AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> foo = fooListener.event();
+ AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> bar = barListener.event();
+
+ // Listener for TOP element
+ assertContains(top.getOriginalData(), TOP,TOP_FOO);
+ assertContains(top.getCreatedData(), TOP_BAR);
+ assertContains(top.getUpdatedData(), TOP);
+ assertContains(top.getRemovedPaths(), TOP_FOO);
+
+ /*
+ * Listener for all list items
+ *
+ * Updated should be empty, since no list item was
+ * updated, items were only removed and added
+ */
+ assertContains(all.getOriginalData(), TOP_FOO);
+ assertContains(all.getCreatedData(), TOP_BAR);
+ assertEmpty(all.getUpdatedData());
+ assertContains(all.getRemovedPaths(), TOP_FOO);
+
+
+ /*
+ * Listener for all Foo item
+ *
+ * This one should see only Foo item removed
+ */
+ assertContains(foo.getOriginalData(), TOP_FOO);
+ assertEmpty(foo.getCreatedData());
+ assertEmpty(foo.getUpdatedData());
+ assertContains(foo.getRemovedPaths(), TOP_FOO);
+
+ /*
+ * Listener for bar list items
+ *
+ * Updated should be empty, since no list item was
+ * updated, items were only removed and added
+ */
+ assertEmpty(bar.getOriginalData());
+ assertContains(bar.getCreatedData(), TOP_BAR);
+ assertEmpty(bar.getUpdatedData());
+ assertEmpty(bar.getRemovedPaths());
+ }
+
+ @Test
+ public void mergeTopNodeSubtreeListeners() {
+ TestListener topListener = createListener(CONFIGURATION, TOP, DataChangeScope.SUBTREE);
+ TestListener allListener = createListener(CONFIGURATION, WILDCARDED, DataChangeScope.SUBTREE);
+ TestListener fooListener = createListener(CONFIGURATION, TOP_FOO, DataChangeScope.SUBTREE);
+ TestListener barListener = createListener(CONFIGURATION, TOP_BAR, DataChangeScope.SUBTREE);
+
+ ReadWriteTransaction writeTx = getDataBroker().newReadWriteTransaction();
+ writeTx.merge(CONFIGURATION, TOP, top(topLevelList(TOP_BAR_KEY)));
+ assertCommit(writeTx.submit());
+
+ verifyBarOnlyAdded(topListener,allListener,fooListener,barListener);
+ }
+
+ @Test
+ public void putTopBarNodeSubtreeListeners() {
+ TestListener topListener = createListener(CONFIGURATION, TOP, DataChangeScope.SUBTREE);
+ TestListener allListener = createListener(CONFIGURATION, WILDCARDED, DataChangeScope.SUBTREE);
+ TestListener fooListener = createListener(CONFIGURATION, TOP_FOO, DataChangeScope.SUBTREE);
+ TestListener barListener = createListener(CONFIGURATION, TOP_BAR, DataChangeScope.SUBTREE);
+
+ ReadWriteTransaction writeTx = getDataBroker().newReadWriteTransaction();
+ writeTx.put(CONFIGURATION, TOP_BAR, topLevelList(TOP_BAR_KEY));
+ assertCommit(writeTx.submit());
+
+ verifyBarOnlyAdded(topListener,allListener,fooListener,barListener);
+ }
+
+ @Test
+ public void mergeTopBarNodeSubtreeListeners() {
+ TestListener topListener = createListener(CONFIGURATION, TOP, DataChangeScope.SUBTREE);
+ TestListener allListener = createListener(CONFIGURATION, WILDCARDED, DataChangeScope.SUBTREE);
+ TestListener fooListener = createListener(CONFIGURATION, TOP_FOO, DataChangeScope.SUBTREE);
+ TestListener barListener = createListener(CONFIGURATION, TOP_BAR, DataChangeScope.SUBTREE);
+
+ ReadWriteTransaction writeTx = getDataBroker().newReadWriteTransaction();
+ writeTx.merge(CONFIGURATION, TOP_BAR, topLevelList(TOP_BAR_KEY));
+ assertCommit(writeTx.submit());
+
+ verifyBarOnlyAdded(topListener,allListener,fooListener,barListener);
+ }
+
+ private void verifyBarOnlyAdded(final TestListener top, final TestListener all, final TestListener foo,
+ final TestListener bar) {
+
+ assertFalse(foo.hasEvent());
+
+ // Listener for TOP element
+ assertContains(top.event().getOriginalData(), TOP);
+ assertNotContains(top.event().getOriginalData(),TOP_FOO);
+ assertContains(top.event().getCreatedData(), TOP_BAR);
+ assertContains(top.event().getUpdatedData(), TOP);
+ assertEmpty(top.event().getRemovedPaths());
+
+ /*
+ * Listener for all list items
+ *
+ * Updated should be empty, since no list item was
+ * updated, items were only removed and added
+ */
+ assertEmpty(all.event().getOriginalData());
+ assertContains(all.event().getCreatedData(), TOP_BAR);
+ assertEmpty(all.event().getUpdatedData());
+ assertEmpty(all.event().getRemovedPaths());
+
+ /*
+ * Listener for all Foo item
+ *
+ * Foo Listener should not have foo event
+ */
+ assertFalse(foo.hasEvent());
+
+ /*
+ * Listener for bar list items
+ *
+ * Updated should be empty, since no list item was
+ * updated, items were only removed and added
+ */
+ assertEmpty(bar.event().getOriginalData());
+ assertContains(bar.event().getCreatedData(), TOP_BAR);
+ assertEmpty(bar.event().getUpdatedData());
+ assertEmpty(bar.event().getRemovedPaths());
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.impl.test;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.concurrent.ExecutionException;
+
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.binding.test.AbstractDataBrokerTest;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.TopBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import com.google.common.base.Optional;
+
+
+public class WriteTransactionTest extends AbstractDataBrokerTest {
+
+ private static final InstanceIdentifier<Top> TOP_PATH = InstanceIdentifier.create(Top.class);
+ private static final TopLevelListKey TOP_LIST_KEY = new TopLevelListKey("foo");
+ private static final InstanceIdentifier<TopLevelList> NODE_PATH = TOP_PATH.child(TopLevelList.class, TOP_LIST_KEY);
+ private static final TopLevelList NODE = new TopLevelListBuilder().setKey(TOP_LIST_KEY).build();
+ @Test
+ public void test() throws InterruptedException, ExecutionException {
+ WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction();
+ writeTx.put(LogicalDatastoreType.OPERATIONAL, TOP_PATH, new TopBuilder().build());
+ writeTx.put(LogicalDatastoreType.OPERATIONAL, NODE_PATH, NODE);
+ writeTx.submit().get();
+ }
+
+ @Test
+ public void testPutCreateParentsSuccess() throws TransactionCommitFailedException, InterruptedException, ExecutionException {
+
+ WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction();
+ writeTx.put(LogicalDatastoreType.OPERATIONAL, NODE_PATH, NODE,true);
+ writeTx.submit().checkedGet();
+
+ ReadOnlyTransaction readTx = getDataBroker().newReadOnlyTransaction();
+ Optional<Top> topNode = readTx.read(LogicalDatastoreType.OPERATIONAL, TOP_PATH).get();
+ assertTrue("Top node must exists after commit",topNode.isPresent());
+ Optional<TopLevelList> listNode = readTx.read(LogicalDatastoreType.OPERATIONAL, NODE_PATH).get();
+ assertTrue("List node must exists after commit",listNode.isPresent());
+ }
+
+ @Test
+ public void testMergeCreateParentsSuccess() throws TransactionCommitFailedException, InterruptedException, ExecutionException {
+
+ WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction();
+ writeTx.merge(LogicalDatastoreType.OPERATIONAL, NODE_PATH, NODE,true);
+ writeTx.submit().checkedGet();
+
+ ReadOnlyTransaction readTx = getDataBroker().newReadOnlyTransaction();
+ Optional<Top> topNode = readTx.read(LogicalDatastoreType.OPERATIONAL, TOP_PATH).get();
+ assertTrue("Top node must exists after commit",topNode.isPresent());
+ Optional<TopLevelList> listNode = readTx.read(LogicalDatastoreType.OPERATIONAL, NODE_PATH).get();
+ assertTrue("List node must exists after commit",listNode.isPresent());
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.test;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+public class AbstractDataBrokerTest extends AbstractSchemaAwareTest {
+
+ private DataBrokerTestCustomizer testCustomizer;
+ private DataBroker dataBroker;
+ private DOMDataBroker domBroker;
+
+
+ @Override
+ protected void setupWithSchema(final SchemaContext context) {
+ testCustomizer = createDataBrokerTestCustomizer();
+ dataBroker = testCustomizer.createDataBroker();
+ domBroker = testCustomizer.createDOMDataBroker();
+ testCustomizer.updateSchema(context);
+ setupWithDataBroker(dataBroker);
+ }
+
+ protected void setupWithDataBroker(final DataBroker dataBroker) {
+ // Intentionally left No-op, subclasses may customize it
+ }
+
+ protected DataBrokerTestCustomizer createDataBrokerTestCustomizer() {
+ return new DataBrokerTestCustomizer();
+ }
+
+ public DataBroker getDataBroker() {
+ return dataBroker;
+ }
+
+ public DOMDataBroker getDomBroker() {
+ return domBroker;
+ }
+
+ protected static final void assertCommit(final ListenableFuture<Void> commit) {
+ try {
+ commit.get(500, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException | ExecutionException | TimeoutException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.test;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import com.google.common.util.concurrent.SettableFuture;
+
+public abstract class AbstractDataChangeListenerTest extends AbstractDataBrokerTest {
+
+ protected static final class TestListener implements DataChangeListener {
+
+ private final SettableFuture<AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject>> event;
+ private boolean capture = false;
+
+ private TestListener() {
+ event = SettableFuture.create();
+ }
+
+ @Override
+ public void onDataChanged(final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> arg) {
+ if (capture) {
+ event.set(arg);
+ }
+ }
+
+ public AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> event() {
+ try {
+ return event.get(500, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException | TimeoutException | ExecutionException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ public boolean hasEvent() {
+ return event.isDone();
+ }
+
+ public void startCapture() {
+ this.capture = true;
+ }
+ }
+
+ protected final TestListener createListener(final LogicalDatastoreType store, final InstanceIdentifier<?> path,
+ final DataChangeScope scope) {
+ TestListener listener = new TestListener();
+ getDataBroker().registerDataChangeListener(store, path, listener, scope);
+ listener.startCapture();
+ return listener;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.test;
+
+import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
+import org.opendaylight.controller.md.sal.binding.api.NotificationService;
+import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec;
+import org.opendaylight.controller.md.sal.dom.broker.impl.DOMNotificationRouter;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class AbstractNotificationBrokerTest extends AbstractSchemaAwareTest{
+ private BindingToNormalizedNodeCodec bindingToNormalizedNodeCodec;
+ private DOMNotificationRouter domNotificationRouter;
+ private NotificationService notificationService;
+ private NotificationPublishService notificationPublishService;
+
+
+ @Override
+ protected void setupWithSchema(final SchemaContext context) {
+ final DataBrokerTestCustomizer testCustomizer = createDataBrokerTestCustomizer();
+ domNotificationRouter = testCustomizer.getDomNotificationRouter();
+ notificationService = testCustomizer.createNotificationService();
+ notificationPublishService = testCustomizer.createNotificationPublishService();
+ bindingToNormalizedNodeCodec = testCustomizer.getBindingToNormalized();
+ testCustomizer.updateSchema(context);
+ }
+
+ protected DataBrokerTestCustomizer createDataBrokerTestCustomizer() {
+ return new DataBrokerTestCustomizer();
+ }
+
+ public NotificationService getNotificationService() {
+ return notificationService;
+ }
+
+ public NotificationPublishService getNotificationPublishService() {
+ return notificationPublishService;
+ }
+
+ public DOMNotificationRouter getDomNotificationRouter() {
+ return domNotificationRouter;
+ }
+
+ public BindingToNormalizedNodeCodec getBindingToNormalizedNodeCodec() {
+ return bindingToNormalizedNodeCodec;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.test;
+
+import org.junit.Before;
+import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext;
+import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public abstract class AbstractSchemaAwareTest {
+
+ private Iterable<YangModuleInfo> moduleInfos;
+ private SchemaContext schemaContext;
+
+
+ protected Iterable<YangModuleInfo> getModuleInfos() throws Exception {
+ return BindingReflections.loadModuleInfos();
+ }
+
+
+ @Before
+ public final void setup() throws Exception {
+ moduleInfos = getModuleInfos();
+ ModuleInfoBackedContext moduleContext = ModuleInfoBackedContext.create();
+ moduleContext.addModuleInfos(moduleInfos);
+ schemaContext = moduleContext.tryToCreateSchemaContext().get();
+ setupWithSchema(schemaContext);
+ }
+
+ /**
+ * Setups test with Schema context.
+ * This method is called before {@link #setupWithSchemaService(SchemaService)}
+ *
+ * @param context
+ */
+ protected abstract void setupWithSchema(SchemaContext context);
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.binding.test;
+
+import java.util.Collection;
+import java.util.Map;
+
+import org.junit.Assert;
+
+public class AssertCollections {
+
+ public static void assertEmpty(final Collection<?> set) {
+ Assert.assertTrue(set.isEmpty());
+ }
+
+ public static void assertEmpty(final Map<?,?> set) {
+ Assert.assertTrue(set.isEmpty());
+ }
+
+ public static void assertContains(final Collection<?> set, final Object... values) {
+ for (Object key : values) {
+ Assert.assertTrue(set.contains(key));
+ }
+
+ }
+
+ public static void assertNotContains(final Collection<?> set, final Object... values) {
+ for (Object key : values) {
+ Assert.assertFalse(set.contains(key));
+ }
+ }
+
+ public static void assertContains(final Map<?,?> map, final Object... values) {
+ for (Object key : values) {
+ Assert.assertTrue(map.containsKey(key));
+ }
+ }
+
+ public static void assertNotContains(final Map<?,?> map, final Object... values) {
+ for (Object key : values) {
+ Assert.assertFalse(map.containsKey(key));
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.test;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import javassist.ClassPool;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
+import org.opendaylight.controller.md.sal.binding.api.NotificationService;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMDataBrokerAdapter;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMNotificationPublishServiceAdapter;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMNotificationServiceAdapter;
+import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.broker.impl.DOMNotificationRouter;
+import org.opendaylight.controller.md.sal.dom.broker.impl.SerializedDOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
+import org.opendaylight.controller.sal.binding.test.util.MockSchemaService;
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.controller.sal.core.spi.data.DOMStore;
+import org.opendaylight.yangtools.binding.data.codec.gen.impl.DataObjectSerializerGenerator;
+import org.opendaylight.yangtools.binding.data.codec.gen.impl.StreamWriterGenerator;
+import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry;
+import org.opendaylight.yangtools.sal.binding.generator.impl.GeneratedClassLoadingStrategy;
+import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class DataBrokerTestCustomizer {
+
+ private DOMDataBroker domDataBroker;
+ private final DOMNotificationRouter domNotificationRouter;
+ private final MockSchemaService schemaService;
+ private ImmutableMap<LogicalDatastoreType, DOMStore> datastores;
+ private final BindingToNormalizedNodeCodec bindingToNormalized;
+
+ public ImmutableMap<LogicalDatastoreType, DOMStore> createDatastores() {
+ return ImmutableMap.<LogicalDatastoreType, DOMStore>builder()
+ .put(LogicalDatastoreType.OPERATIONAL, createOperationalDatastore())
+ .put(LogicalDatastoreType.CONFIGURATION,createConfigurationDatastore())
+ .build();
+ }
+
+ public DataBrokerTestCustomizer() {
+ schemaService = new MockSchemaService();
+ final ClassPool pool = ClassPool.getDefault();
+ final DataObjectSerializerGenerator generator = StreamWriterGenerator.create(JavassistUtils.forClassPool(pool));
+ final BindingNormalizedNodeCodecRegistry codecRegistry = new BindingNormalizedNodeCodecRegistry(generator);
+ final GeneratedClassLoadingStrategy loading = GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy();
+ bindingToNormalized = new BindingToNormalizedNodeCodec(loading, codecRegistry);
+ schemaService.registerSchemaContextListener(bindingToNormalized);
+ domNotificationRouter = DOMNotificationRouter.create(16);
+ }
+
+ public DOMStore createConfigurationDatastore() {
+ final InMemoryDOMDataStore store = new InMemoryDOMDataStore("CFG", MoreExecutors.sameThreadExecutor());
+ schemaService.registerSchemaContextListener(store);
+ return store;
+ }
+
+ public DOMStore createOperationalDatastore() {
+ final InMemoryDOMDataStore store = new InMemoryDOMDataStore("OPER", MoreExecutors.sameThreadExecutor());
+ schemaService.registerSchemaContextListener(store);
+ return store;
+ }
+
+ public DOMDataBroker createDOMDataBroker() {
+ return new SerializedDOMDataBroker(getDatastores(), getCommitCoordinatorExecutor());
+ }
+
+ public NotificationService createNotificationService() {
+ return new BindingDOMNotificationServiceAdapter(bindingToNormalized.getCodecRegistry(), domNotificationRouter);
+ }
+
+ public NotificationPublishService createNotificationPublishService() {
+ return new BindingDOMNotificationPublishServiceAdapter(bindingToNormalized, domNotificationRouter);
+ }
+
+
+ public ListeningExecutorService getCommitCoordinatorExecutor() {
+ return MoreExecutors.sameThreadExecutor();
+ }
+
+ public DataBroker createDataBroker() {
+ return new BindingDOMDataBrokerAdapter(getDOMDataBroker(), bindingToNormalized);
+ }
+
+ public BindingToNormalizedNodeCodec getBindingToNormalized() {
+ return bindingToNormalized;
+ }
+
+ public SchemaService getSchemaService() {
+ return schemaService;
+ }
+
+ private DOMDataBroker getDOMDataBroker() {
+ if(domDataBroker == null) {
+ domDataBroker = createDOMDataBroker();
+ }
+ return domDataBroker;
+ }
+
+ private synchronized ImmutableMap<LogicalDatastoreType, DOMStore> getDatastores() {
+ if (datastores == null) {
+ datastores = createDatastores();
+ }
+ return datastores;
+ }
+
+ public void updateSchema(final SchemaContext ctx) {
+ schemaService.changeSchema(ctx);
+ }
+
+ public DOMNotificationRouter getDomNotificationRouter() {
+ return domNotificationRouter;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.test;
+
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import org.junit.Before;
+import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
+import org.opendaylight.controller.sal.binding.test.util.BindingBrokerTestFactory;
+import org.opendaylight.controller.sal.binding.test.util.BindingTestContext;
+
+public abstract class AbstractDataServiceTest {
+
+ protected DataProviderService baDataService;
+
+ protected BindingTestContext testContext;
+
+ @Before
+ public void setUp() {
+ ListeningExecutorService executor = MoreExecutors.sameThreadExecutor();
+ BindingBrokerTestFactory factory = new BindingBrokerTestFactory();
+ factory.setExecutor(executor);
+ factory.setStartWithParsedSchema(getStartWithSchema());
+ testContext = factory.getTestContext();
+ testContext.start();
+
+ baDataService = testContext.getBindingDataBroker();
+ }
+
+ protected boolean getStartWithSchema() {
+ return true;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.test;
+
+import static org.junit.Assert.assertNotNull;
+import org.opendaylight.yangtools.yang.binding.Augmentable;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+
+public class AugmentationVerifier<T extends Augmentable<T>> {
+
+ private T object;
+
+ public AugmentationVerifier(T objectToVerify) {
+ this.object = objectToVerify;
+ }
+
+ public AugmentationVerifier<T> assertHasAugmentation(Class<? extends Augmentation<T>> augmentation) {
+ assertHasAugmentation(object, augmentation);
+ return (AugmentationVerifier<T>) this;
+ }
+
+ public static <T extends Augmentable<T>> void assertHasAugmentation(T object,
+ Class<? extends Augmentation<T>> augmentation) {
+ assertNotNull(object);
+ assertNotNull("Augmentation " + augmentation.getSimpleName() + " is not present.", object.getAugmentation(augmentation));
+ }
+
+ public static <T extends Augmentable<T>> AugmentationVerifier<T> from(T obj) {
+ return new AugmentationVerifier<T>(obj);
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.test;
+
+public class BindingTestUtilities {
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.test.mock;
+
+import org.opendaylight.yangtools.yang.binding.NotificationListener;
+
+public interface BarListener extends NotificationListener {
+
+ void onBarUpdate(BarUpdate notification);
+
+ void onFlowDelete(FlowDelete notification);
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.test.mock;
+
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+public interface BarUpdate extends Grouping,Notification {
+
+
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.test.mock;
+
+public interface CompositeListener extends FooListener,BarListener {
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.test.mock;
+
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+public interface FlowDelete extends Notification{
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.test.mock;
+
+import org.opendaylight.yangtools.yang.binding.NotificationListener;
+
+public interface FooListener extends NotificationListener {
+
+ void onFooUpdate(FooUpdate notification);
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.test.mock;
+
+import java.util.concurrent.Future;
+
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+public interface FooService extends RpcService {
+
+ Future<RpcResult<Void>> foo();
+
+ Future<RpcResult<Void>> simple(SimpleInput obj);
+
+ Future<RpcResult<Void>> inheritedContext(InheritedContextInput obj);
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.test.mock;
+
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+public interface FooUpdate extends Notification {
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.test.mock;
+
+import org.opendaylight.yangtools.yang.binding.BaseIdentity;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.annotations.RoutingContext;
+
+public interface Grouping {
+
+ @RoutingContext(BaseIdentity.class)
+ InstanceIdentifier<?> getInheritedIdentifier();
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.test.mock;
+
+public interface InheritedContextInput extends Grouping {
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.test.mock;
+
+import org.opendaylight.yangtools.yang.binding.ChildOf;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.DataRoot;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+
+public interface ReferencableObject extends DataObject,
+ Identifiable<ReferencableObjectKey>,ChildOf<DataRoot>{
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.test.mock;
+
+import org.opendaylight.yangtools.yang.binding.Identifier;
+
+public class ReferencableObjectKey implements Identifier<ReferencableObject> {
+ private static final long serialVersionUID = 1L;
+ final Integer value;
+
+ public ReferencableObjectKey(Integer _value) {
+ this.value = _value;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((value == null) ? 0 : value.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ ReferencableObjectKey other = (ReferencableObjectKey) obj;
+ if (value == null) {
+ if (other.value != null)
+ return false;
+ } else if (!value.equals(other.value))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "ReferencableObjectKey [value=" + value + "]";
+ }
+
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.test.mock;
+
+import org.opendaylight.yangtools.yang.binding.Augmentable;
+import org.opendaylight.yangtools.yang.binding.BaseIdentity;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.annotations.RoutingContext;
+
+public interface SimpleInput extends DataObject,Augmentable<SimpleInput> {
+
+ @RoutingContext(BaseIdentity.class)
+ InstanceIdentifier<?> getIdentifier();
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.test.util;
+
+import java.util.concurrent.ExecutorService;
+
+import javassist.ClassPool;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+
+@Beta
+public class BindingBrokerTestFactory {
+
+ private static final ClassPool CLASS_POOL = ClassPool.getDefault();
+ private boolean startWithParsedSchema = true;
+ private ExecutorService executor;
+ private ClassPool classPool;
+
+
+ public boolean isStartWithParsedSchema() {
+ return startWithParsedSchema;
+ }
+
+ public void setStartWithParsedSchema(final boolean startWithParsedSchema) {
+ this.startWithParsedSchema = startWithParsedSchema;
+ }
+
+ public ExecutorService getExecutor() {
+ return executor;
+ }
+
+ public void setExecutor(final ExecutorService executor) {
+ this.executor = executor;
+ }
+
+
+ public BindingTestContext getTestContext() {
+ Preconditions.checkState(executor != null, "Executor is not set.");
+ ListeningExecutorService listenableExecutor = MoreExecutors.listeningDecorator(executor);
+ return new BindingTestContext(listenableExecutor, getClassPool(),startWithParsedSchema);
+ }
+
+ public ClassPool getClassPool() {
+ if(classPool == null) {
+ return CLASS_POOL;
+ }
+
+ return classPool;
+ }
+
+ public void setClassPool(final ClassPool classPool) {
+ this.classPool = classPool;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.test.util;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.ImmutableClassToInstanceMap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.MutableClassToInstanceMap;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import javassist.ClassPool;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.MountPointService;
+import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
+import org.opendaylight.controller.md.sal.binding.api.NotificationService;
+import org.opendaylight.controller.md.sal.binding.compat.HeliumNotificationProviderServiceAdapter;
+import org.opendaylight.controller.md.sal.binding.compat.HeliumRpcProviderRegistry;
+import org.opendaylight.controller.md.sal.binding.compat.HydrogenDataBrokerAdapter;
+import org.opendaylight.controller.md.sal.binding.compat.HydrogenMountProvisionServiceAdapter;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMDataBrokerAdapter;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMMountPointServiceAdapter;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMNotificationPublishServiceAdapter;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMNotificationServiceAdapter;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMRpcProviderServiceAdapter;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMRpcServiceAdapter;
+import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationPublishService;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcProviderService;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
+import org.opendaylight.controller.md.sal.dom.broker.impl.DOMNotificationRouter;
+import org.opendaylight.controller.md.sal.dom.broker.impl.DOMRpcRouter;
+import org.opendaylight.controller.md.sal.dom.broker.impl.SerializedDOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.broker.impl.mount.DOMMountPointServiceImpl;
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
+import org.opendaylight.controller.sal.binding.api.RpcConsumerRegistry;
+import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
+import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
+import org.opendaylight.controller.sal.binding.api.mount.MountProviderService;
+import org.opendaylight.controller.sal.binding.impl.RootBindingAwareBroker;
+import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
+import org.opendaylight.controller.sal.core.api.BrokerService;
+import org.opendaylight.controller.sal.core.spi.data.DOMStore;
+import org.opendaylight.controller.sal.dom.broker.BrokerImpl;
+import org.opendaylight.yangtools.binding.data.codec.gen.impl.DataObjectSerializerGenerator;
+import org.opendaylight.yangtools.binding.data.codec.gen.impl.StreamWriterGenerator;
+import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry;
+import org.opendaylight.yangtools.sal.binding.generator.impl.GeneratedClassLoadingStrategy;
+import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext;
+import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils;
+import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+@Beta
+public class BindingTestContext implements AutoCloseable {
+
+
+ private BindingToNormalizedNodeCodec codec;
+
+ private RootBindingAwareBroker baBrokerImpl;
+
+ private HeliumNotificationProviderServiceAdapter baNotifyImpl;
+
+
+ private BrokerImpl biBrokerImpl;
+
+ private final ListeningExecutorService executor;
+ private final ClassPool classPool;
+
+ private final boolean startWithSchema;
+
+ private DOMMountPointService biMountImpl;
+
+ private ImmutableMap<LogicalDatastoreType, DOMStore> newDatastores;
+
+ @Deprecated
+ private DataProviderService baData;
+
+ private DOMDataBroker newDOMDataBroker;
+
+ private final MockSchemaService mockSchemaService = new MockSchemaService();
+
+ private DataBroker dataBroker;
+
+ private RpcConsumerRegistry baConsumerRpc;
+
+ private BindingDOMRpcProviderServiceAdapter baProviderRpc;
+ private DOMRpcRouter domRouter;
+
+ private NotificationPublishService publishService;
+
+ private NotificationService listenService;
+
+ private DOMNotificationPublishService domPublishService;
+
+ private DOMNotificationService domListenService;
+
+
+
+ public DOMDataBroker getDomAsyncDataBroker() {
+ return newDOMDataBroker;
+ }
+
+ public BindingToNormalizedNodeCodec getCodec() {
+ return codec;
+ }
+
+ protected BindingTestContext(final ListeningExecutorService executor, final ClassPool classPool, final boolean startWithSchema) {
+ this.executor = executor;
+ this.classPool = classPool;
+ this.startWithSchema = startWithSchema;
+ }
+
+ public void startDomDataBroker() {
+ }
+
+ public void startNewDataBroker() {
+ checkState(executor != null, "Executor needs to be set");
+ checkState(newDOMDataBroker != null, "DOM Data Broker must be set");
+ dataBroker = new BindingDOMDataBrokerAdapter(newDOMDataBroker, codec);
+ }
+
+ public void startNewDomDataBroker() {
+ checkState(executor != null, "Executor needs to be set");
+ final InMemoryDOMDataStore operStore = new InMemoryDOMDataStore("OPER", MoreExecutors.sameThreadExecutor());
+ final InMemoryDOMDataStore configStore = new InMemoryDOMDataStore("CFG", MoreExecutors.sameThreadExecutor());
+ newDatastores = ImmutableMap.<LogicalDatastoreType, DOMStore>builder()
+ .put(LogicalDatastoreType.OPERATIONAL, operStore)
+ .put(LogicalDatastoreType.CONFIGURATION, configStore)
+ .build();
+
+ newDOMDataBroker = new SerializedDOMDataBroker(newDatastores, executor);
+
+ mockSchemaService.registerSchemaContextListener(configStore);
+ mockSchemaService.registerSchemaContextListener(operStore);
+ }
+
+ public void startBindingDataBroker() {
+
+ }
+
+ public void startBindingBroker() {
+ checkState(executor != null, "Executor needs to be set");
+ checkState(baData != null, "Binding Data Broker must be started");
+ checkState(baNotifyImpl != null, "Notification Service must be started");
+
+ baConsumerRpc = new BindingDOMRpcServiceAdapter(getDomRpcInvoker(), codec);
+ baProviderRpc = new BindingDOMRpcProviderServiceAdapter(getDomRpcRegistry(), codec);
+
+ baBrokerImpl = new RootBindingAwareBroker("test");
+
+ final MountPointService mountService = new BindingDOMMountPointServiceAdapter(biMountImpl, codec);
+ baBrokerImpl.setMountService(mountService);
+ baBrokerImpl.setLegacyMountManager(new HydrogenMountProvisionServiceAdapter(mountService));
+ baBrokerImpl.setRpcBroker(new HeliumRpcProviderRegistry(baConsumerRpc, baProviderRpc));
+ baBrokerImpl.setLegacyDataBroker(baData);
+ baBrokerImpl.setNotificationBroker(baNotifyImpl);
+ baBrokerImpl.start();
+ }
+
+ public void startForwarding() {
+
+ }
+
+ private ProviderSession createMockContext() {
+
+ final ClassToInstanceMap<BrokerService> domBrokerServices = ImmutableClassToInstanceMap
+ .<BrokerService> builder()
+ //
+ .put(DOMRpcRouter.class, biBrokerImpl.getRouter()) //
+ .put(DOMMountPointService.class, biMountImpl)
+ .build();
+
+ return new ProviderSession() {
+
+ @Override
+ public <T extends BrokerService> T getService(final Class<T> service) {
+ return domBrokerServices.getInstance(service);
+ }
+
+ @Override
+ public boolean isClosed() {
+ return false;
+ }
+
+ @Override
+ public void close() {
+ }
+ };
+ }
+
+ public void startBindingToDomMappingService() {
+ checkState(classPool != null, "ClassPool needs to be present");
+
+ final DataObjectSerializerGenerator generator = StreamWriterGenerator.create(JavassistUtils.forClassPool(classPool));
+ final BindingNormalizedNodeCodecRegistry codecRegistry = new BindingNormalizedNodeCodecRegistry(generator);
+ final GeneratedClassLoadingStrategy loading = GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy();
+ codec = new BindingToNormalizedNodeCodec(loading, codecRegistry);
+ mockSchemaService.registerSchemaContextListener(codec);
+ }
+
+ private void updateYangSchema(final ImmutableSet<YangModuleInfo> moduleInfos) {
+ mockSchemaService.changeSchema(getContext(moduleInfos));
+ }
+
+ private SchemaContext getContext(final ImmutableSet<YangModuleInfo> moduleInfos) {
+ final ModuleInfoBackedContext ctx = ModuleInfoBackedContext.create();
+ ctx.addModuleInfos(moduleInfos);
+ return ctx.tryToCreateSchemaContext().get();
+ }
+
+ public void start() {
+ startNewDomDataBroker();
+
+ startDomBroker();
+ startDomMountPoint();
+ startBindingToDomMappingService();
+ startNewDataBroker();
+ startNewBindingDataBroker();
+ startBindingNotificationBroker();
+ startBindingBroker();
+
+ startForwarding();
+ if (startWithSchema) {
+ loadYangSchemaFromClasspath();
+ }
+ }
+
+ public void startNewBindingDataBroker() {
+ final HydrogenDataBrokerAdapter forwarded = new HydrogenDataBrokerAdapter(dataBroker);
+ baData = forwarded;
+ }
+
+ private void startDomMountPoint() {
+ biMountImpl = new DOMMountPointServiceImpl();
+ }
+
+ private void startDomBroker() {
+ checkState(executor != null);
+
+ domRouter = new DOMRpcRouter();
+ mockSchemaService.registerSchemaContextListener(domRouter);
+
+ final ClassToInstanceMap<BrokerService> services = MutableClassToInstanceMap.create();
+ services.put(DOMRpcService.class, domRouter);
+
+ biBrokerImpl = new BrokerImpl(domRouter,services);
+
+ }
+
+ public void startBindingNotificationBroker() {
+ checkState(executor != null);
+ final DOMNotificationRouter router = DOMNotificationRouter.create(16);
+ domPublishService = router;
+ domListenService = router;
+ publishService = new BindingDOMNotificationPublishServiceAdapter(codec, domPublishService);
+ listenService = new BindingDOMNotificationServiceAdapter(codec, domListenService);
+ baNotifyImpl = new HeliumNotificationProviderServiceAdapter(publishService,listenService);
+
+ }
+
+ public void loadYangSchemaFromClasspath() {
+ final ImmutableSet<YangModuleInfo> moduleInfos = BindingReflections.loadModuleInfos();
+ updateYangSchema(moduleInfos);
+ }
+
+ @Deprecated
+ public DataProviderService getBindingDataBroker() {
+ return baData;
+ }
+
+ public RpcProviderRegistry getBindingRpcRegistry() {
+ return baBrokerImpl.getRoot();
+ }
+
+ public DOMRpcProviderService getDomRpcRegistry() {
+ return domRouter;
+ }
+
+ public DOMRpcService getDomRpcInvoker() {
+ return domRouter;
+ }
+
+ @Override
+ public void close() throws Exception {
+
+ }
+
+ public MountProviderService getBindingMountProviderService() {
+ return baBrokerImpl.getLegacyMount();
+ }
+
+ public DOMMountPointService getDomMountProviderService() {
+ return biMountImpl;
+ }
+
+ public DataBroker getDataBroker() {
+ return dataBroker;
+ }
+
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.test.util;
+
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.util.ListenerRegistry;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
+
+public final class MockSchemaService implements SchemaService, SchemaContextProvider {
+
+ private SchemaContext schemaContext;
+
+ ListenerRegistry<SchemaContextListener> listeners = ListenerRegistry.create();
+
+ @Override
+ public void addModule(final Module module) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public synchronized SchemaContext getGlobalContext() {
+ return schemaContext;
+ }
+
+ @Override
+ public synchronized SchemaContext getSessionContext() {
+ return schemaContext;
+ }
+
+ @Override
+ public ListenerRegistration<SchemaContextListener> registerSchemaContextListener(
+ final SchemaContextListener listener) {
+ return listeners.register(listener);
+ }
+
+ @Override
+ public void removeModule(final Module module) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public synchronized SchemaContext getSchemaContext() {
+ return schemaContext;
+ }
+
+ public synchronized void changeSchema(final SchemaContext newContext) {
+ schemaContext = newContext;
+ for (ListenerRegistration<SchemaContextListener> listener : listeners) {
+ listener.getInstance().onGlobalContextUpdated(schemaContext);
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-parent</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>sal-binding-util</artifactId>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-api</artifactId>
+ </dependency>
+ </dependencies>
+ <scm>
+ <connection>scm:git:http://git.opendaylight.org/gerrit/controller.git</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+ <tag>HEAD</tag>
+ <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
+ </scm>
+</project>
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.util;
+
+import org.opendaylight.controller.sal.binding.api.NotificationListener;
+import org.opendaylight.controller.sal.binding.api.NotificationService;
+import org.opendaylight.controller.sal.binding.api.RpcConsumerRegistry;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.Notification;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+import com.google.common.base.Preconditions;
+
+public abstract class AbstractBindingSalConsumerInstance<D extends DataBrokerService, N extends NotificationService, R extends RpcConsumerRegistry> //
+ implements //
+ RpcConsumerRegistry, //
+ NotificationService, //
+ DataBrokerService {
+
+ private final R rpcRegistry;
+ private final N notificationBroker;
+ private final D dataBroker;
+
+ protected final R getRpcRegistry() {
+ return rpcRegistry;
+ }
+
+ protected final N getNotificationBroker() {
+ return notificationBroker;
+ }
+
+ protected final D getDataBroker() {
+ return dataBroker;
+ }
+
+ protected final R getRpcRegistryChecked() {
+ Preconditions.checkState(rpcRegistry != null,"Rpc Registry is not available.");
+ return rpcRegistry;
+ }
+
+ protected final N getNotificationBrokerChecked() {
+ Preconditions.checkState(notificationBroker != null,"Notification Broker is not available.");
+ return notificationBroker;
+ }
+
+ protected final D getDataBrokerChecked() {
+ Preconditions.checkState(dataBroker != null, "Data Broker is not available");
+ return dataBroker;
+ }
+
+
+ protected AbstractBindingSalConsumerInstance(R rpcRegistry, N notificationBroker, D dataBroker) {
+ this.rpcRegistry = rpcRegistry;
+ this.notificationBroker = notificationBroker;
+ this.dataBroker = dataBroker;
+ }
+
+ @Override
+ public <T extends RpcService> T getRpcService(Class<T> module) {
+ return getRpcRegistryChecked().getRpcService(module);
+ }
+
+ @Override
+ public <T extends Notification> ListenerRegistration<NotificationListener<T>> registerNotificationListener(
+ Class<T> notificationType, NotificationListener<T> listener) {
+ return getNotificationBrokerChecked().registerNotificationListener(notificationType, listener);
+ }
+
+ @Override
+ public ListenerRegistration<org.opendaylight.yangtools.yang.binding.NotificationListener> registerNotificationListener(
+ org.opendaylight.yangtools.yang.binding.NotificationListener listener) {
+ return getNotificationBrokerChecked().registerNotificationListener(listener);
+ }
+
+ @Override
+ public DataModificationTransaction beginTransaction() {
+ return getDataBrokerChecked().beginTransaction();
+ }
+
+ @Override
+ @Deprecated
+ public DataObject readConfigurationData(InstanceIdentifier<? extends DataObject> path) {
+ return getDataBrokerChecked().readConfigurationData(path);
+ }
+
+ @Override
+ public DataObject readOperationalData(InstanceIdentifier<? extends DataObject> path) {
+ return getDataBrokerChecked().readOperationalData(path);
+ }
+
+ @Override
+ @Deprecated
+ public ListenerRegistration<DataChangeListener> registerDataChangeListener(
+ InstanceIdentifier<? extends DataObject> path, DataChangeListener listener) {
+ return getDataBrokerChecked().registerDataChangeListener(path, listener);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.util;
+
+import java.util.concurrent.ExecutorService;
+
+import org.opendaylight.controller.md.sal.common.api.RegistrationListener;
+import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler;
+import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandlerRegistration;
+import org.opendaylight.controller.md.sal.common.api.data.DataReader;
+import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
+import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
+import org.opendaylight.controller.sal.binding.api.rpc.RpcContextIdentifier;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.Notification;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+public abstract class AbstractBindingSalProviderInstance<D extends DataProviderService, N extends NotificationProviderService, R extends RpcProviderRegistry> //
+ extends AbstractBindingSalConsumerInstance<D, N, R> //
+ implements //
+ DataProviderService, //
+ RpcProviderRegistry, //
+ NotificationProviderService {
+
+ public AbstractBindingSalProviderInstance(R rpcRegistry, N notificationBroker,
+ D dataBroker) {
+ super(rpcRegistry, notificationBroker, dataBroker);
+ }
+
+ @Override
+ public Registration registerDataReader(
+ InstanceIdentifier<? extends DataObject> path,
+ DataReader<InstanceIdentifier<? extends DataObject>, DataObject> reader) {
+ return getDataBrokerChecked().registerDataReader(path, reader);
+ }
+
+ @Override
+ public Registration registerCommitHandler(
+ InstanceIdentifier<? extends DataObject> path,
+ DataCommitHandler<InstanceIdentifier<? extends DataObject>, DataObject> commitHandler) {
+ return getDataBrokerChecked().registerCommitHandler(path, commitHandler);
+ }
+
+ @Override
+ public ListenerRegistration<RegistrationListener<DataCommitHandlerRegistration<InstanceIdentifier<? extends DataObject>, DataObject>>> registerCommitHandlerListener(
+ RegistrationListener<DataCommitHandlerRegistration<InstanceIdentifier<? extends DataObject>, DataObject>> commitHandlerListener) {
+ return getDataBrokerChecked().registerCommitHandlerListener(commitHandlerListener);
+ }
+
+ @Override
+ public <T extends RpcService> RpcRegistration<T> addRpcImplementation(Class<T> type, T implementation)
+ throws IllegalStateException {
+ return getRpcRegistryChecked().addRpcImplementation(type, implementation);
+ }
+
+ @Override
+ public <T extends RpcService> RoutedRpcRegistration<T> addRoutedRpcImplementation(Class<T> type, T implementation)
+ throws IllegalStateException {
+ return getRpcRegistryChecked().addRoutedRpcImplementation(type, implementation);
+ }
+
+ @Override
+ public void publish(Notification notification) {
+ getNotificationBrokerChecked().publish(notification);
+ }
+
+ @Override
+ public void publish(Notification notification, ExecutorService service) {
+ getNotificationBrokerChecked().publish(notification, service);
+ }
+
+ @Override
+ public <L extends RouteChangeListener<RpcContextIdentifier, InstanceIdentifier<?>>> ListenerRegistration<L> registerRouteChangeListener(
+ L listener) {
+ return getRpcRegistryChecked().registerRouteChangeListener(listener);
+ }
+
+ @Override
+ public ListenerRegistration<NotificationInterestListener> registerInterestListener(
+ NotificationInterestListener interestListener) {
+ return getNotificationBrokerChecked().registerInterestListener(interestListener);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.util;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerContext;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration;
+import org.opendaylight.controller.sal.binding.api.BindingAwareConsumer;
+import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
+import org.opendaylight.controller.sal.binding.api.BindingAwareService;
+import org.opendaylight.controller.sal.binding.api.RpcConsumerRegistry;
+import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
+import org.opendaylight.controller.sal.binding.api.rpc.RpcContextIdentifier;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.MutableClassToInstanceMap;
+
+public class BindingContextUtils {
+
+ public static ConsumerContext createConsumerContext(BindingAwareConsumer consumer,
+ ClassToInstanceMap<BindingAwareService> serviceProvider) {
+ checkNotNull(consumer,"Consumer should not be null");
+ checkNotNull(serviceProvider,"Service map should not be null");
+ return new SingleConsumerContextImpl(serviceProvider);
+ }
+
+ public static ProviderContext createProviderContext(BindingAwareProvider provider,
+ ClassToInstanceMap<BindingAwareService> serviceProvider) {
+ checkNotNull(provider,"Provider should not be null");
+ checkNotNull(serviceProvider,"Service map should not be null");
+ return new SingleProviderContextImpl(serviceProvider);
+ }
+
+ public static ConsumerContext createConsumerContextAndInitialize(BindingAwareConsumer consumer,
+ ClassToInstanceMap<BindingAwareService> serviceProvider) {
+ ConsumerContext context = createConsumerContext(consumer, serviceProvider);
+ consumer.onSessionInitialized(context);
+ return context;
+ }
+
+ public static ProviderContext createProviderContextAndInitialize(BindingAwareProvider provider,
+ ClassToInstanceMap<BindingAwareService> serviceProvider) {
+ ProviderContext context = createProviderContext(provider, serviceProvider);
+ provider.onSessionInitiated(context);
+ return context;
+ }
+
+ public static <T extends BindingAwareService> T createContextProxyOrReturnService(Class<T> service, T instance) {
+ // FIXME: Create Proxy
+ return instance;
+ }
+
+ private static class SingleConsumerContextImpl implements ConsumerContext, AutoCloseable {
+
+ private ClassToInstanceMap<BindingAwareService> alreadyRetrievedServices;
+ private ClassToInstanceMap<BindingAwareService> serviceProvider;
+
+ public SingleConsumerContextImpl(ClassToInstanceMap<BindingAwareService> serviceProvider) {
+ this.alreadyRetrievedServices = MutableClassToInstanceMap.create();
+ this.serviceProvider = serviceProvider;
+ }
+
+ @Override
+ public final <T extends RpcService> T getRpcService(Class<T> module) {
+ return getSALService(RpcConsumerRegistry.class).getRpcService(module);
+ }
+
+ @Override
+ public final <T extends BindingAwareService> T getSALService(Class<T> service) {
+ checkNotNull(service,"Service class should not be null.");
+ T potential = alreadyRetrievedServices.getInstance(service);
+ if(potential != null) {
+ return potential;
+ }
+ return tryToRetrieveSalService(service);
+ }
+
+ private synchronized <T extends BindingAwareService> T tryToRetrieveSalService(Class<T> service) {
+ final T potential = alreadyRetrievedServices.getInstance(service);
+ if(potential != null) {
+ return potential;
+ }
+ final T requested = serviceProvider.getInstance(service);
+ if(requested == null) {
+ throw new IllegalArgumentException("Requested service "+service.getName() +" is not available.");
+ }
+ final T retrieved = BindingContextUtils.createContextProxyOrReturnService(service,requested);
+ alreadyRetrievedServices.put(service, retrieved);
+ return retrieved;
+ }
+
+ @Override
+ public final void close() throws Exception {
+ alreadyRetrievedServices = null;
+ serviceProvider = null;
+ }
+ }
+
+ private static class SingleProviderContextImpl extends SingleConsumerContextImpl implements ProviderContext {
+
+ public SingleProviderContextImpl(ClassToInstanceMap<BindingAwareService> serviceProvider) {
+ super(serviceProvider);
+ }
+
+ @Override
+ public <L extends RouteChangeListener<RpcContextIdentifier, InstanceIdentifier<?>>> ListenerRegistration<L> registerRouteChangeListener(
+ L listener) {
+ return getSALService(RpcProviderRegistry.class).registerRouteChangeListener(listener);
+ }
+
+ @Override
+ public <T extends RpcService> RoutedRpcRegistration<T> addRoutedRpcImplementation(Class<T> type,
+ T implementation) throws IllegalStateException {
+ return getSALService(RpcProviderRegistry.class).addRoutedRpcImplementation(type, implementation);
+ }
+
+ @Override
+ public <T extends RpcService> RpcRegistration<T> addRpcImplementation(Class<T> type, T implementation)
+ throws IllegalStateException {
+ return getSALService(RpcProviderRegistry.class).addRpcImplementation(type, implementation);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.util;
+
+import org.opendaylight.controller.md.sal.common.api.data.DataReader;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ *
+ *
+ * @deprecated Use
+ * {@link org.opendaylight.controller.md.sal.binding.api.ReadTransaction#read(org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType, InstanceIdentifier)}
+ * instead.
+ */
+@Deprecated
+public final class TypeSafeDataReader {
+
+ private final DataReader<InstanceIdentifier<? extends DataObject>, DataObject> delegate;
+
+ public DataReader<InstanceIdentifier<?>, DataObject> getDelegate() {
+ return delegate;
+ }
+
+ public TypeSafeDataReader(
+ final DataReader<InstanceIdentifier<? extends DataObject>, DataObject> delegate) {
+ this.delegate = delegate;
+ }
+
+ @SuppressWarnings("unchecked")
+ public <D extends DataObject> D readConfigurationData(
+ final InstanceIdentifier<D> path) {
+ return (D) delegate.readConfigurationData(path);
+ }
+
+ @SuppressWarnings("unchecked")
+ public <D extends DataObject> D readOperationalData(
+ final InstanceIdentifier<D> path) {
+ return (D) delegate.readOperationalData(path);
+ }
+
+ public static TypeSafeDataReader forReader(
+ final DataReader<InstanceIdentifier<? extends DataObject>, DataObject> delegate) {
+ return new TypeSafeDataReader(delegate);
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-parent</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>sal-common-api</artifactId>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>concepts</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>util</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-common</artifactId>
+ </dependency>
+ </dependencies>
+
+ <scm>
+ <connection>scm:git:http://git.opendaylight.org/gerrit/controller.git</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+ <tag>HEAD</tag>
+ <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
+ </scm>
+</project>
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api;
+
+import java.util.EventListener;
+
+import org.opendaylight.yangtools.concepts.Registration;
+
+public interface RegistrationListener<T extends Registration> extends EventListener {
+
+ void onRegister(T registration);
+
+ void onUnregister(T registration);
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api;
+
+public enum TransactionStatus {
+ /**
+ * The transaction has been freshly allocated. The user is still accessing
+ * it and it has not been sealed.
+ */
+ NEW,
+ /**
+ * The transaction has been completed by the user and sealed. It is currently
+ * awaiting execution.
+ */
+ SUBMITED,
+ /**
+ * The transaction has been successfully committed to backing store.
+ */
+ COMMITED,
+ /**
+ * The transaction has failed to commit due to some underlying issue.
+ */
+ FAILED,
+ /**
+ * Currently unused.
+ */
+ CANCELED,
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ *
+ * Three phase Commit Cohort for subtree, which is
+ * uniquely associated with user submitted transcation.
+ *
+ * @param <P>
+ * Type of path (subtree identifier), which represents location in
+ * tree
+ * @param <D>
+ * Type of data (payload), which represents data payload
+ */
+public interface AsyncConfigurationCommitCohort<P extends Path<P>, D> {
+
+ /**
+ * Initiates a pre-commit of associated request
+ *
+ * Implementation MUST NOT do any blocking calls during this callback, all
+ * pre-commit preparation SHOULD happen asynchronously and MUST result in
+ * completing returned future object.
+ *
+ * @param rebasedTransaction
+ * Read-only view of transaction as if happened on top of actual
+ * data store
+ * @return Future which is completed once pre-commit phase for this request
+ * is finished.
+ */
+ ListenableFuture<Void> preCommit(AsyncReadTransaction<P, D> rebasedTransaction);
+
+ /**
+ *
+ * Initiates a commit phase of associated request
+ *
+ * Implementation MUST NOT do any blocking calls during this callback, all
+ * commit finalization SHOULD happen asynchronously and MUST result in
+ * completing returned future object.
+ *
+ * @return Future which is completed once commit phase for associated
+ * request is finished.
+ */
+ ListenableFuture<Void> commit();
+
+ /**
+ *
+ * Initiates abort phase of associated request
+ *
+ * Implementation MUST NOT do any blocking calls during this callback, all
+ * commit finalization SHOULD happen asynchronously and MUST result in
+ * completing returned future object.
+ *
+ * @return Future which is completed once commit phase for associated
+ * request is finished.
+ */
+ ListenableFuture<Void> abort();
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ * Three Phase Commit Coordinator with support of user-supplied commit cohorts
+ * which participates in three-phase commit protocols
+ *
+ * @param <P>
+ * Type of path (subtree identifier), which represents location in
+ * tree
+ * @param <D>
+ * Type of data (payload), which represents data payload
+ */
+public interface AsyncConfigurationCommitCoordinator<P extends Path<P>, D> {
+
+ /**
+ * Register configuration commit handler for particular subtree
+ *
+ * Configuration commit handler is invoked for all write transactions
+ * which modifies <code>subtree</code>
+ *
+ * @param subtree Subtree which configuration commit handler is interested it
+ * @param commitHandler Instance of user-provided commit handler
+ * @return Registration object representing this registration. Invoking {@link ObjectRegistration#close()}
+ * will unregister configuration commit handler.
+ */
+ <C extends AsyncConfigurationCommitCohort<P, D>> ObjectRegistration<C> registerConfigurationCommitHandler(
+ P subtree, C commitHandler);
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+import com.google.common.util.concurrent.CheckedFuture;
+
+/**
+ * User-supplied participant in three-phase commit of transaction for configuration data tree
+ *
+ * Client-supplied implementation of commit handler for subtree, which
+ * is responsible for processing CAN-COMMIT phase of three-phase commit protocol
+ * and return CommitCohort, which provides access to additional transitions
+ * such as PRE-COMMIT, COMMIT and ABORT.
+ *
+ * @param <P>
+ * Type of path (subtree identifier), which represents location in
+ * tree
+ * @param <D>
+ * Type of data (payload), which represents data payload
+ */
+public interface AsyncConfigurationCommitHandler<P extends Path<P>, D> {
+
+ /**
+ *
+ * Requests a can commit phase
+ *
+ * Implementations SHOULD NOT do any blocking operation during
+ * processing this callback.
+ *
+ * <b>Implementation Notes</b>
+ * <ul>
+ * <li>Implementation are REQUIRED to use <code>request</code> object for any data related access</li>
+ * <li>Implementations SHOULD NOT use any other state stored outside configuration subtree for validation</li>
+ * <li>Validation should happen asynchronously, outside callback call by updating returned {@link CheckedFuture}
+ * object.</li>
+ * <li>If validation (CAN_COMMIT) phase:
+ * <ul>
+ * <li><b>is successful</b> - invocation of {@link CheckedFuture#checkedGet()} on returned future MUST
+ * return {@link AsyncConfigurationCommitCohort} associated with request.</li>
+ * <li><b>is unsuccessful</b> - invocation of {@link CheckedFuture#checkedGet()} must throw instance of {@link DataValidationFailedException}
+ * with human readable explanaition of error condition.
+ * </li>
+ * </ul>
+ * </li>
+ * @param request
+ * Commit Request submitted by client, which contains
+ * information about modifications and read-only view as
+ * if transaction happened.
+ * @return CheckedFuture which contains client-supplied implementation of {@link AsyncConfigurationCommitCohort}
+ * associated with submitted request, if can commit phase is
+ * successful, if can commit was unsuccessful, future must fail with
+ * {@link TransactionCommitFailedException} exception.
+ */
+ CheckedFuture<AsyncConfigurationCommitCohort<P, D>, DataValidationFailedException> canCommit(
+ ConfigurationCommitRequest<P, D> request);
+
+ /**
+ *
+ * Commit Request as was submitted by client code
+ *
+ * Commit Request contains list view of created / updated / removed
+ * path and read-only view of proposed client transaction,
+ * which may be used to retrieve modified or referenced data.
+ *
+ *
+ * @param <P>
+ * Type of path (subtree identifier), which represents location
+ * in tree
+ * @param <D>
+ * Type of data (payload), which represents data payload
+ */
+ static interface ConfigurationCommitRequest<P extends Path<P>, D> {
+
+ /**
+ *
+ * Read-only transaction which provides access only to configuration
+ * data tree as if submitted transaction successfully happened and
+ * no other concurrent modifications happened between allocation
+ * of client transactions and write of client transactions.
+ *
+ * Implementations of Commit Handlers are REQUIRED to use this
+ * read-only view to access any data from configuration data tree,
+ * in order to capture them as preconditions for this transaction.
+ *
+ * @return Read-only transaction which provides access only to configuration
+ * data tree as if submitted transaction successfully happened
+ */
+ AsyncReadTransaction<P, D> getReadOnlyView();
+
+ /**
+ *
+ * Returns iteration of paths, to data which was introduced by this transaction.
+ *
+ * @return Iteration of paths, which was introduced by this transaction.
+ */
+ Iterable<P> getCreatedPaths();
+ /**
+ *
+ * Returns iteration of paths, to data which was updated by this transaction.
+ *
+ * @return Iteration of paths, which was updated by this transaction.
+ */
+ Iterable<P> getUpdatedPaths();
+
+ /**
+ *
+ * Returns iteration of paths, to data which was removed by this transaction.
+ *
+ * @return Iteration of paths, which was removed by this transaction.
+ */
+ Iterable<P> getRemovedPaths();
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ *
+ * Base interface that provides access to a conceptual data tree store and also provides the ability to
+ * subscribe for changes to data under a given branch of the tree.
+ *
+ * <p>
+ * All operations on the data tree are performed via one of the transactions:
+ * <ul>
+ * <li>Read-Only - allocated using {@link #newReadOnlyTransaction()}
+ * <li>Write-Only - allocated using {@link #newWriteOnlyTransaction()}
+ * <li>Read-Write - allocated using {@link #newReadWriteTransaction()}
+ * </ul>
+ *
+ * <p>
+ * These transactions provide a stable isolated view of data tree, which is
+ * guaranteed to be not affected by other concurrent transactions, until
+ * transaction is committed.
+ *
+ * <p>
+ * For a detailed explanation of how transaction are isolated and how transaction-local
+ * changes are committed to global data tree, see
+ * {@link AsyncReadTransaction}, {@link AsyncWriteTransaction},
+ * {@link AsyncReadWriteTransaction} and {@link AsyncWriteTransaction#commit()}.
+ *
+ *
+ * <p>
+ * It is strongly recommended to use the type of transaction, which
+ * provides only the minimal capabilities you need. This allows for
+ * optimizations at the data broker / data store level. For example,
+ * implementations may optimize the transaction for reading if they know ahead
+ * of time that you only need to read data - such as not keeping additional meta-data,
+ * which may be required for write transactions.
+ *
+ * <p>
+ * <b>Implementation Note:</b> This interface is not intended to be implemented
+ * by users of MD-SAL, but only to be consumed by them.
+ *
+ * @param <P>
+ * Type of path (subtree identifier), which represents location in
+ * tree
+ * @param <D>
+ * Type of data (payload), which represents data payload
+ */
+public interface AsyncDataBroker<P extends Path<P>, D, L extends AsyncDataChangeListener<P, D>> extends //
+ AsyncDataTransactionFactory<P, D> {
+
+ /**
+ *
+ * Scope of Data Change
+ *
+ * <p>
+ * Represents scope of data change (addition, replacement, deletion).
+ *
+ * The terminology for scope types is reused from LDAP.
+ *
+ * <h2>Examples</h2>
+ *
+ * Following is an example model with comments describing what notifications
+ * you would receive based on the scope you specify, when you are
+ * registering for changes on container a.
+ *
+ * <pre>
+ * container a // scope BASE, ONE, SUBTREE
+ * leaf "foo" // scope ONE, SUBTREE
+ * container // scope ONE, SUBTREE
+ * leaf "bar" // scope SUBTREE
+ * list list // scope ONE, SUBTREE
+ * list [a] // scope SUBTREE
+ * id "a" // scope SUBTREE
+ * list [b] // scope SUBTREE
+ * id "b" // scope SUBTREE
+ * </pre>
+ *
+ * Following is an example model with comments describing what notifications
+ * you would receive based on the scope you specify, when you are
+ * registering for changes on list list (without specifying concrete item in
+ * the list).
+ *
+ * <pre>
+ * list list // scope BASE, ONE, SUBTREE
+ * list [a] // scope ONE, SUBTREE
+ * id "a" // scope SUBTREE
+ * list [b] // scope ONE, SUBTREE
+ * id "b" // scope SUBTREE
+ * </pre>
+ *
+ *
+ * @see http://www.idevelopment.info/data/LDAP/LDAP_Resources/
+ * SEARCH_Setting_the_SCOPE_Parameter.shtml
+ */
+ public enum DataChangeScope {
+
+ /**
+ * Represents only a direct change of the node, such as replacement of a
+ * node, addition or deletion.
+ *
+ */
+ BASE,
+ /**
+ * Represent a change (addition,replacement,deletion) of the node or one
+ * of its direct children.
+ *
+ * This scope is superset of {@link #BASE}.
+ *
+ */
+ ONE,
+ /**
+ * Represents a change of the node or any of or any of its child nodes,
+ * direct and nested.
+ *
+ * This scope is superset of {@link #ONE} and {@link #BASE}.
+ *
+ */
+ SUBTREE
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public AsyncReadOnlyTransaction<P, D> newReadOnlyTransaction();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public AsyncReadWriteTransaction<P, D> newReadWriteTransaction();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public AsyncWriteTransaction<P, D> newWriteOnlyTransaction();
+
+ /**
+ * Registers a {@link AsyncDataChangeListener} to receive
+ * notifications when data changes under a given path in the conceptual data
+ * tree.
+ * <p>
+ * You are able to register for notifications for any node or subtree
+ * which can be reached via the supplied path.
+ * <p>
+ * If path type <code>P</code> allows it, you may specify paths up to the leaf nodes
+ * then it is possible to listen on leaf nodes.
+ * <p>
+ * You are able to register for data change notifications for a subtree even
+ * if it does not exist. You will receive notification once that node is
+ * created.
+ * <p>
+ * If there is any preexisting data in data tree on path for which you are
+ * registering, you will receive initial data change event, which will
+ * contain all preexisting data, marked as created.
+ *
+ * <p>
+ * You are also able to specify the scope of the changes you want to be
+ * notified.
+ * <p>
+ * Supported scopes are:
+ * <ul>
+ * <li>{@link DataChangeScope#BASE} - notification events will only be
+ * triggered when a node referenced by path is created, removed or replaced.
+ * <li>{@link DataChangeScope#ONE} - notifications events will only be
+ * triggered when a node referenced by path is created, removed or replaced,
+ * or any or any of its immediate children are created, updated or removed.
+ * <li>{@link DataChangeScope#SUBTREE} - notification events will be
+ * triggered when a node referenced by the path is created, removed
+ * or replaced or any of the children in its subtree are created, removed
+ * or replaced.
+ * </ul>
+ * See {@link DataChangeScope} for examples.
+ * <p>
+ * This method returns a {@link ListenerRegistration} object. To
+ * "unregister" your listener for changes call the "close" method on this
+ * returned object.
+ * <p>
+ * You MUST call close when you no longer need to receive notifications
+ * (such as during shutdown or for example if your bundle is shutting down).
+ *
+ * @param store
+ * Logical Data Store - Logical Datastore you want to listen for
+ * changes in. For example
+ * {@link LogicalDatastoreType#OPERATIONAL} or
+ * {@link LogicalDatastoreType#CONFIGURATION}
+ * @param path
+ * Path (subtree identifier) on which client listener will be
+ * invoked.
+ * @param listener
+ * Instance of listener which should be invoked on
+ * @param triggeringScope
+ * Scope of change which triggers callback.
+ * @return Listener registration object, which may be used to unregister
+ * your listener using {@link ListenerRegistration#close()} to stop
+ * delivery of change events.
+ */
+ ListenerRegistration<L> registerDataChangeListener(LogicalDatastoreType store, P path, L listener,
+ DataChangeScope triggeringScope);
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ *
+ * An event which contains a capture of changes in a data subtree
+ *
+ * <p>
+ * Represents a notification indicating that some data at or under a particular
+ * path has changed. The notification contains a capture of the changes in the data
+ * subtree. This event is triggered by successful application of modifications
+ * from a transaction on the global data tree. Use the
+ * {@link AsyncDataBroker#registerDataChangeListener(LogicalDatastoreType, Path, AsyncDataChangeListener, AsyncDataBroker.DataChangeScope)}
+ * method to register a listener for data change events.
+ *
+ * <p>
+ * A listener will only receive notifications for changes to data under the path
+ * they register for. See
+ * {@link AsyncDataBroker#registerDataChangeListener(LogicalDatastoreType, Path, AsyncDataChangeListener, AsyncDataBroker.DataChangeScope)}
+ * to learn more about registration scopes.
+ *
+ * <p>
+ * The entire subtree under the path will be provided via instance methods of Data
+ * Change Event even if just a leaf node changes.
+ *
+ * <p>
+ * <b>Implementation Note:</b> This interface is not intended to be implemented
+ * by users of MD-SAL, but only to be consumed by them.
+ *
+ * @param <P>
+ * Type of path (subtree identifier), which represents location in
+ * tree
+ * @param <D>
+ * Type of data (payload), which represents data payload
+ */
+public interface AsyncDataChangeEvent<P extends Path<P>, D> extends Immutable {
+ /**
+ * Returns a map of paths and newly created objects, which were introduced by
+ * this change into conceptual data tree, if no new objects were introduced
+ * this map will be empty.
+ *<p>
+ * This map contains all data tree nodes (and paths to them) which were created
+ * and are in the scope of listener registration. The data tree nodes
+ * contain their whole subtree with their current state.
+ *
+ * @return map of paths and newly created objects
+ */
+ Map<P, D> getCreatedData();
+
+ /**
+ * Returns a map of paths and objects which were updated by this change in the
+ * conceptual data tree if no existing objects were updated
+ * this map will be empty.
+ *<p>
+ * This map contains all data tree nodes (and paths to them) which were updated
+ * and are in the scope of listener registration. The data tree nodes
+ * contain their whole subtree with their current state.
+ *<p>
+ * A Node is considered updated if it contents were replaced or one of its
+ * children was created, removed or updated.
+ *<p>
+ * Original state of the updated data tree nodes is in
+ * {@link #getOriginalData()} stored with same path.
+ *
+ * @return map of paths and newly created objects
+ */
+ Map<P, D> getUpdatedData();
+
+ /**
+ * Returns an immutable set of removed paths.
+ *<p>
+ * This set contains the paths to the data tree nodes which are in the scope
+ * of the listener registration that have been removed.
+ *<p>
+ * Original state of the removed data tree nodes is in
+ * {@link #getOriginalData()} stored with same path.
+ *
+ * @return set of removed paths
+ */
+ Set<P> getRemovedPaths();
+
+ /**
+ * Returns an immutable map of updated or removed paths and their original
+ * states prior to this change.
+ *
+ *<p>
+ * This map contains the original version of the data tree nodes (and paths
+ * to them), which are in the scope of the listener registration.
+ *
+ * @return map of paths and original state of updated and removed objects.
+ */
+ Map<P, D> getOriginalData();
+
+ /**
+ * Returns an immutable stable view of data state, which captures the state of
+ * data store before the reported change.
+ *
+ *<p>
+ * The view is rooted at the point where the listener, to which the event is
+ * being delivered, was registered.
+ *<p>
+ * If listener used a wildcarded path (if supported by path type) during
+ * registration for change listeners this method returns null, and original
+ * state can be accessed only via {@link #getOriginalData()}
+ *
+ * @return Stable view of data before the change happened, rooted at the
+ * listener registration path.
+ *
+ */
+ D getOriginalSubtree();
+
+ /**
+ * Returns an immutable stable view of data, which captures the state of data
+ * store after the reported change.
+ *<p>
+ * The view is rooted at the point where the listener, to which the event is
+ * being delivered, was registered.
+ *<p>
+ * If listener used a wildcarded path (if supported by path type) during
+ * registration for change listeners this method returns null, and state
+ * can be accessed only via {@link #getCreatedData()},
+ * {@link #getUpdatedData()}, {@link #getRemovedPaths()}
+ *
+ * @return Stable view of data after the change happened, rooted at the
+ * listener registration path.
+ */
+ D getUpdatedSubtree();
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import java.util.EventListener;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ * Listener of data change events on particular subtree.
+ *
+ * <p>
+ * User-supplied implementations of this listener interface MUST register via
+ * {@link AsyncDataBroker#registerDataChangeListener(LogicalDatastoreType, Path, AsyncDataChangeListener, AsyncDataBroker.DataChangeScope)}
+ * in order to start receiving data change events, which capture state changes
+ * in a subtree.
+ *
+ * <p>
+ * <b>Implementation Note:</b> This interface is intended to be implemented
+ * by users of MD-SAL.
+ *
+ * @param <P>
+ * Type of path (subtree identifier), which represents location in
+ * tree
+ * @param <D>
+ * Type of data (payload), which represents data payload
+ */
+public interface AsyncDataChangeListener<P extends Path<P>, D> extends EventListener {
+ /**
+ *
+ * Invoked when there is data change for the particular path, which was used to
+ * register this listener.
+ * <p>
+ * This method may be also invoked during registration of the listener if
+ * there is any preexisting data in the conceptual data tree for supplied path.
+ * This initial event will contain all preexisting data as created.
+ *
+ * <p>
+ * <b>Note</b>: This method may be invoked from a shared thread pool.
+ * <li>Implementations <b>SHOULD NOT</b> perform CPU-intensive operations on the calling thread.
+ * <li>Implementations <b>MUST NOT block the calling thread</b> - to do so could lead to deadlock
+ * scenarios.
+ *
+ *<br>
+ *
+ * @param change
+ * Data Change Event being delivered.
+ */
+ void onDataChanged(AsyncDataChangeEvent<P, D> change);
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ * A factory which allocates new transactions to operate on the data
+ * tree.
+ *
+ * <p>
+ * <b>Note:</b> This interface is not intended to be used directly, but rather
+ * via subinterfaces which introduces additional semantics to allocated
+ * transactions.
+ * <ul>
+ * <li> {@link AsyncDataBroker}
+ * <li> {@link TransactionChain}
+ * </ul>
+ *
+ * <p>
+ * All operations on the data tree are performed via one of the transactions:
+ * <ul>
+ * <li>Read-Only - allocated using {@link #newReadOnlyTransaction()}
+ * <li>Write-Only - allocated using {@link #newWriteOnlyTransaction()}
+ * <li>Read-Write - allocated using {@link #newReadWriteTransaction()}
+ * </ul>
+ *
+ * <p>
+ * These transactions provides a stable isolated view of the data tree, which is
+ * guaranteed to be not affected by other concurrent transactions, until
+ * transaction is committed.
+ *
+ * <p>
+ * For a detailed explanation of how transaction are isolated and how transaction-local
+ * changes are committed to global data tree, see
+ * {@link AsyncReadTransaction}, {@link AsyncWriteTransaction},
+ * {@link AsyncReadWriteTransaction} and {@link AsyncWriteTransaction#commit()}.
+ *
+ * <p>
+ * It is strongly recommended to use the type of transaction, which
+ * provides only the minimal capabilities you need. This allows for
+ * optimizations at the data broker / data store level. For example,
+ * implementations may optimize the transaction for reading if they know ahead
+ * of time that you only need to read data - such as not keeping additional meta-data,
+ * which may be required for write transactions.
+ *<p>
+ * <b>Implementation Note:</b> This interface is not intended to be implemented
+ * by users of MD-SAL, but only to be consumed by them.
+ *
+ * @see AsyncDataBroker
+ * @see TransactionChain
+ *
+ * @param <P>
+ * Type of path (subtree identifier), which represents location in
+ * tree
+ * @param <D>
+ * Type of data (payload), which represents data payload
+ */
+public interface AsyncDataTransactionFactory<P extends Path<P>, D> {
+
+ /**
+ * Allocates a new read-only transaction which provides an immutable snapshot of
+ * the data tree.
+ *<p>
+ * The view of data tree is an immutable snapshot of current data tree state when
+ * transaction was allocated.
+ *
+ * @return new read-only transaction
+ */
+ AsyncReadOnlyTransaction<P, D> newReadOnlyTransaction();
+
+ /**
+ * Allocates new read-write transaction which provides a mutable view of the data
+ * tree.
+ *
+ * <p>
+ * Preconditions for mutation of data tree are captured from the snapshot of
+ * data tree state, when the transaction is allocated. If data was
+ * changed during transaction in an incompatible way then the commit of this transaction
+ * will fail. See {@link AsyncWriteTransaction#commit()} for more
+ * details about conflicting and not-conflicting changes and
+ * failure scenarios.
+ *
+ * @return new read-write transaction
+ */
+ AsyncReadWriteTransaction<P, D> newReadWriteTransaction();
+
+ /**
+ * Allocates new write-only transaction based on latest state of data
+ * tree.
+ *
+ * <p>
+ * Preconditions for mutation of data tree are captured from the snapshot of
+ * data tree state, when the transaction is allocated. If data was
+ * changed during transaction in an incompatible way then the commit of this transaction
+ * will fail. See {@link AsyncWriteTransaction#commit()} for more
+ * details about conflicting and not-conflicting changes and
+ * failure scenarios.
+ *
+ * <p>
+ * Since this transaction does not provide a view of the data it SHOULD BE
+ * used only by callers which are exclusive writers (exporters of data)
+ * to the subtree they modify. This prevents optimistic
+ * lock failures as described in {@link AsyncWriteTransaction#commit()}.
+ * <p>
+ * Exclusivity of writers to particular subtree SHOULD BE enforced by
+ * external locking mechanism.
+ *
+ * @return new write-only transaction
+ */
+ AsyncWriteTransaction<P, D> newWriteOnlyTransaction();
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ * Marker interface for a read-only view of the data tree.
+ *
+ * @see AsyncReadTransaction
+ *
+ * @param <P>
+ * Type of path (subtree identifier), which represents location in
+ * tree
+ * @param <D>
+ * Type of data (payload), which represents data payload
+ */
+public interface AsyncReadOnlyTransaction<P extends Path<P>, D> extends AsyncReadTransaction<P, D>, AutoCloseable {
+
+ /**
+ * Closes this transaction and releases all resources associated with it.
+ *
+ */
+ @Override
+ public void close();
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ *
+ * Marker interface for stateful read view of the data tree.
+ *
+ * <p>
+ * View of the data tree is a stable point-in-time snapshot of the current data tree state when
+ * the transaction was created. It's state and underlying data tree
+ * is not affected by other concurrently running transactions.
+ *
+ * <p>
+ * <b>Implementation Note:</b> This interface is not intended to be implemented
+ * by users of MD-SAL, but only to be consumed by them.
+ *
+ * <h2>Transaction isolation example</h2>
+ * Lets assume initial state of data tree for <code>PATH</code> is <code>A</code>.
+ *
+ * <pre>
+ * txRead = broker.newReadOnlyTransaction(); // read Transaction is snapshot of data
+ * txWrite = broker.newReadWriteTransactoin(); // concurrent write transaction
+ *
+ * txRead.read(OPERATIONAL,PATH).get(); // will return Optional containing A
+ * txWrite = broker.put(OPERATIONAL,PATH,B); // writes B to PATH
+ *
+ * txRead.read(OPERATIONAL,PATH).get(); // still returns Optional containing A
+ *
+ * txWrite.commit().get(); // data tree is updated, PATH contains B
+ * txRead.read(OPERATIONAL,PATH).get(); // still returns Optional containing A
+ *
+ * txAfterCommit = broker.newReadOnlyTransaction(); // read Transaction is snapshot of new state
+ * txAfterCommit.read(OPERATIONAL,PATH).get(); // returns Optional containing B;
+ * </pre>
+ *
+ * <p>
+ * <b>Note:</b> example contains blocking calls on future only to illustrate
+ * that action happened after other asynchronous action. Use of blocking call
+ * {@link com.google.common.util.concurrent.ListenableFuture#get()} is discouraged for most
+ * uses and you should use
+ * {@link com.google.common.util.concurrent.Futures#addCallback(com.google.common.util.concurrent.ListenableFuture, com.google.common.util.concurrent.FutureCallback)}
+ * or other functions from {@link com.google.common.util.concurrent.Futures} to
+ * register more specific listeners.
+ *
+ * @param <P>
+ * Type of path (subtree identifier), which represents location in
+ * tree
+ * @param <D>
+ * Type of data (payload), which represents data payload
+ *
+ * @see org.opendaylight.controller.md.sal.binding.api.ReadTransaction
+ * @see org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction
+ */
+public interface AsyncReadTransaction<P extends Path<P>, D> extends AsyncTransaction<P, D> {
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ * Transaction enabling a client to have a combined read/write capabilities.
+ *
+ * <p>
+ * The initial state of the write transaction is stable snapshot of current data tree
+ * state captured when transaction was created and it's state and underlying
+ * data tree are not affected by other concurrently running transactions.
+ *
+ * <p>
+ * Write transactions are isolated from other concurrent write transactions. All
+ * writes are local to the transaction and represents only a proposal of state
+ * change for data tree and it is not visible to any other concurrently running
+ * transactions.
+ *
+ * <p>
+ * Applications publish the changes proposed in the transaction by calling {@link #commit}
+ * on the transaction. This seals the transaction
+ * (preventing any further writes using this transaction) and submits it to be
+ * processed and applied to global conceptual data tree.
+ *
+ * <p>
+ * The transaction commit may fail due to a concurrent transaction modifying and committing data in
+ * an incompatible way. See {@link #commit()} for more concrete commit failure examples.
+ *
+ * <b>Implementation Note:</b> This interface is not intended to be implemented
+ * by users of MD-SAL, but only to be consumed by them.
+ *
+ * <h2>Examples</h2>
+ *
+ * <h3>Transaction local state</h3>
+ *
+ * Let assume initial state of data tree for <code>PATH</code> is <code>A</code>
+ * .
+ *
+ * <pre>
+ * txWrite = broker.newReadWriteTransaction(); // concurrent write transaction
+ *
+ * txWrite.read(OPERATIONAL,PATH).get() // will return Optional containing A
+ * txWrite.put(OPERATIONAL,PATH,B); // writes B to PATH
+ * txWrite.read(OPERATIONAL,PATH).get() // will return Optional Containing B
+ *
+ * txWrite.commit().get(); // data tree is updated, PATH contains B
+ *
+ * tx1afterCommit = broker.newReadOnlyTransaction(); // read Transaction is snapshot of new state
+ * tx1afterCommit.read(OPERATIONAL,PATH).get(); // returns Optional containing B
+ * </pre>
+ *
+ * As you could see read-write transaction provides capabilities as
+ * {@link AsyncWriteTransaction} but also allows for reading proposed changes as
+ * if they already happened.
+ *
+ * <h3>Transaction isolation (read transaction, read-write transaction)</h3> Let
+ * assume initial state of data tree for <code>PATH</code> is <code>A</code>.
+ *
+ * <pre>
+ * txRead = broker.newReadOnlyTransaction(); // read Transaction is snapshot of data
+ * txWrite = broker.newReadWriteTransaction(); // concurrent write transaction
+ *
+ * txRead.read(OPERATIONAL,PATH).get(); // will return Optional containing A
+ * txWrite.read(OPERATIONAL,PATH).get() // will return Optional containing A
+ *
+ * txWrite.put(OPERATIONAL,PATH,B); // writes B to PATH
+ * txWrite.read(OPERATIONAL,PATH).get() // will return Optional Containing B
+ *
+ * txRead.read(OPERATIONAL,PATH).get(); // concurrent read transaction still returns
+ * // Optional containing A
+ *
+ * txWrite.commit().get(); // data tree is updated, PATH contains B
+ * txRead.read(OPERATIONAL,PATH).get(); // still returns Optional containing A
+ *
+ * tx1afterCommit = broker.newReadOnlyTransaction(); // read Transaction is snapshot of new state
+ * tx1afterCommit.read(OPERATIONAL,PATH).get(); // returns Optional containing B
+ * </pre>
+ *
+ * <h3>Transaction isolation (2 concurrent read-write transactions)</h3> Let
+ * assume initial state of data tree for <code>PATH</code> is <code>A</code>.
+ *
+ * <pre>
+ * tx1 = broker.newReadWriteTransaction(); // read Transaction is snapshot of data
+ * tx2 = broker.newReadWriteTransaction(); // concurrent write transaction
+ *
+ * tx1.read(OPERATIONAL,PATH).get(); // will return Optional containing A
+ * tx2.read(OPERATIONAL,PATH).get() // will return Optional containing A
+ *
+ * tx2.put(OPERATIONAL,PATH,B); // writes B to PATH
+ * tx2.read(OPERATIONAL,PATH).get() // will return Optional Containing B
+ *
+ * tx1.read(OPERATIONAL,PATH).get(); // tx1 read-write transaction still sees Optional
+ * // containing A since is isolated from tx2
+ * tx1.put(OPERATIONAL,PATH,C); // writes C to PATH
+ * tx1.read(OPERATIONAL,PATH).get() // will return Optional Containing C
+ *
+ * tx2.read(OPERATIONAL,PATH).get() // tx2 read-write transaction still sees Optional
+ * // containing B since is isolated from tx1
+ *
+ * tx2.commit().get(); // data tree is updated, PATH contains B
+ * tx1.read(OPERATIONAL,PATH).get(); // still returns Optional containing C since is isolated from tx2
+ *
+ * tx1afterCommit = broker.newReadOnlyTransaction(); // read Transaction is snapshot of new state
+ * tx1afterCommit.read(OPERATIONAL,PATH).get(); // returns Optional containing B
+ *
+ * tx1.commit() // Will fail with OptimisticLockFailedException
+ * // which means concurrent transaction changed the same PATH
+ *
+ * </pre>
+ *
+ * <p>
+ * <b>Note:</b> examples contains blocking calls on future only to illustrate
+ * that action happened after other asynchronous action. Use of blocking call
+ * {@link com.google.common.util.concurrent.ListenableFuture#get()} is discouraged for most uses and you should
+ * use
+ * {@link com.google.common.util.concurrent.Futures#addCallback(com.google.common.util.concurrent.ListenableFuture, com.google.common.util.concurrent.FutureCallback)}
+ * or other functions from {@link com.google.common.util.concurrent.Futures} to
+ * register more specific listeners.
+ *
+ * @see AsyncReadTransaction
+ * @see AsyncWriteTransaction
+ *
+ * @param <P>
+ * Type of path (subtree identifier), which represents location in
+ * tree
+ * @param <D>
+ * Type of data (payload), which represents data payload
+ */
+public interface AsyncReadWriteTransaction<P extends Path<P>, D> extends AsyncReadTransaction<P, D>,
+ AsyncWriteTransaction<P, D> {
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.concepts.Path;
+
+
+/**
+ *
+ * A common parent for all transactions which operate on a conceptual data tree.
+ *
+ * See derived transaction types for more concrete behavior:
+ * <ul>
+ * <li>{@link AsyncReadTransaction} - Read capabilities, user is able to read data from data tree</li>
+ * <li>{@link AsyncWriteTransaction} - Write capabilities, user is able to propose changes to data tree</li>
+ * <li>{@link AsyncReadWriteTransaction} - Read and Write capabilities, user is able to read state and to propose changes of state.</li>
+ * </ul>
+ *
+ * <b>Implementation Note:</b> This interface is not intended to be implemented
+ * by users of MD-SAL.
+ *
+ * @param <P> Type of path (subtree identifier), which represents location in tree
+ * @param <D> Type of data (payload), which represents data payload
+ */
+public interface AsyncTransaction<P extends Path<P>,D> extends //
+ Identifiable<Object> {
+
+ @Override
+ public Object getIdentifier();
+
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.yangtools.concepts.Path;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Write transaction provides mutation capabilities for a data tree.
+ *
+ * <p>
+ * Initial state of write transaction is a stable snapshot of the current data tree.
+ * The state is captured when the transaction is created and its state and underlying
+ * data tree are not affected by other concurrently running transactions.
+ * <p>
+ * Write transactions are isolated from other concurrent write transactions. All
+ * writes are local to the transaction and represent only a proposal of state
+ * change for the data tree and it is not visible to any other concurrently running
+ * transaction.
+ * <p>
+ * Applications make changes to the local data tree in the transaction by via the
+ * <b>put</b>, <b>merge</b>, and <b>delete</b> operations.
+ *
+ * <h2>Put operation</h2>
+ * Stores a piece of data at a specified path. This acts as an add / replace
+ * operation, which is to say that whole subtree will be replaced by the
+ * specified data.
+ * <p>
+ * Performing the following put operations:
+ *
+ * <pre>
+ * 1) container { list [ a ] }
+ * 2) container { list [ b ] }
+ * </pre>
+ *
+ * will result in the following data being present:
+ *
+ * <pre>
+ * container { list [ b ] }
+ * </pre>
+ * <h2>Merge operation</h2>
+ * Merges a piece of data with the existing data at a specified path. Any pre-existing data
+ * which is not explicitly overwritten will be preserved. This means that if you store a container,
+ * its child lists will be merged.
+ * <p>
+ * Performing the following merge operations:
+ *
+ * <pre>
+ * 1) container { list [ a ] }
+ * 2) container { list [ b ] }
+ * </pre>
+ *
+ * will result in the following data being present:
+ *
+ * <pre>
+ * container { list [ a, b ] }
+ * </pre>
+ *
+ * This also means that storing the container will preserve any
+ * augmentations which have been attached to it.
+ *
+ * <h2>Delete operation</h2>
+ * Removes a piece of data from a specified path.
+ * <p>
+ * After applying changes to the local data tree, applications publish the changes proposed in the
+ * transaction by calling {@link #submit} on the transaction. This seals the transaction
+ * (preventing any further writes using this transaction) and submits it to be
+ * processed and applied to global conceptual data tree.
+ * <p>
+ * The transaction commit may fail due to a concurrent transaction modifying and committing data in
+ * an incompatible way. See {@link #submit} for more concrete commit failure examples.
+ * <p>
+ * <b>Implementation Note:</b> This interface is not intended to be implemented
+ * by users of MD-SAL, but only to be consumed by them.
+ *
+ * @param <P>
+ * Type of path (subtree identifier), which represents location in
+ * tree
+ * @param <D>
+ * Type of data (payload), which represents data payload
+ */
+public interface AsyncWriteTransaction<P extends Path<P>, D> extends AsyncTransaction<P, D> {
+ /**
+ * Cancels the transaction.
+ *
+ * Transactions can only be cancelled if it's status is
+ * {@link TransactionStatus#NEW} or {@link TransactionStatus#SUBMITED}
+ *
+ * Invoking cancel() on {@link TransactionStatus#FAILED} or
+ * {@link TransactionStatus#CANCELED} will have no effect, and transaction
+ * is considered cancelled.
+ *
+ * Invoking cancel() on finished transaction (future returned by {@link #submit()}
+ * already completed with {@link TransactionStatus#COMMITED}) will always
+ * fail (return false).
+ *
+ * @return <tt>false</tt> if the task could not be cancelled,
+ * typically because it has already completed normally;
+ * <tt>true</tt> otherwise
+ *
+ */
+ boolean cancel();
+
+ /**
+ * Removes a piece of data from specified path. This operation does not fail
+ * if the specified path does not exist.
+ *
+ * @param store
+ * Logical data store which should be modified
+ * @param path
+ * Data object path
+ * @throws IllegalStateException
+ * if the transaction is no longer {@link TransactionStatus#NEW}
+ */
+ void delete(LogicalDatastoreType store, P path);
+
+ /**
+ * Submits this transaction to be asynchronously applied to update the logical data tree.
+ * The returned CheckedFuture conveys the result of applying the data changes.
+ * <p>
+ * <b>Note:</b> It is strongly recommended to process the CheckedFuture result in an asynchronous
+ * manner rather than using the blocking get() method. See example usage below.
+ * <p>
+ * This call logically seals the transaction, which prevents the client from
+ * further changing data tree using this transaction. Any subsequent calls to
+ * {@link #put(LogicalDatastoreType, Path, Object)},
+ * {@link #merge(LogicalDatastoreType, Path, Object)} or
+ * {@link #delete(LogicalDatastoreType, Path)} will fail with
+ * {@link IllegalStateException}.
+ *
+ * The transaction is marked as {@link TransactionStatus#SUBMITED} and
+ * enqueued into the data store back-end for processing.
+ *
+ * <p>
+ * Whether or not the commit is successful is determined by versioning
+ * of the data tree and validation of registered commit participants
+ * ({@link AsyncConfigurationCommitHandler})
+ * if the transaction changes the data tree.
+ * <p>
+ * The effects of a successful commit of data depends on data change listeners
+ * ({@link AsyncDataChangeListener}) and commit participants
+ * ({@link AsyncConfigurationCommitHandler}) that are registered with the data broker.
+ * <p>
+ * <h3>Example usage:</h3>
+ * <pre>
+ * private void doWrite( final int tries ) {
+ * WriteTransaction writeTx = dataBroker.newWriteOnlyTransaction();
+ *
+ * MyDataObject data = ...;
+ * InstanceIdentifier<MyDataObject> path = ...;
+ * writeTx.put( LogicalDatastoreType.OPERATIONAL, path, data );
+ *
+ * Futures.addCallback( writeTx.submit(), new FutureCallback<Void>() {
+ * public void onSuccess( Void result ) {
+ * // succeeded
+ * }
+ *
+ * public void onFailure( Throwable t ) {
+ * if( t instanceof OptimisticLockFailedException ) {
+ * if( ( tries - 1 ) > 0 ) {
+ * // do retry
+ * doWrite( tries - 1 );
+ * } else {
+ * // out of retries
+ * }
+ * } else {
+ * // failed due to another type of TransactionCommitFailedException.
+ * }
+ * } );
+ * }
+ * ...
+ * doWrite( 2 );
+ * </pre>
+ * <h2>Failure scenarios</h2>
+ * <p>
+ * Transaction may fail because of multiple reasons, such as
+ * <ul>
+ * <li>Another transaction finished earlier and modified the same node in a
+ * non-compatible way (see below). In this case the returned future will fail with an
+ * {@link OptimisticLockFailedException}. It is the responsibility of the
+ * caller to create a new transaction and submit the same modification again in
+ * order to update data tree. <i><b>Warning</b>: In most cases, retrying after an
+ * OptimisticLockFailedException will result in a high probability of success.
+ * However, there are scenarios, albeit unusual, where any number of retries will
+ * not succeed. Therefore it is strongly recommended to limit the number of retries (2 or 3)
+ * to avoid an endless loop.</i>
+ * </li>
+ * <li>Data change introduced by this transaction did not pass validation by
+ * commit handlers or data was incorrectly structured. Returned future will
+ * fail with a {@link DataValidationFailedException}. User should not retry to
+ * create new transaction with same data, since it probably will fail again.
+ * </li>
+ * </ul>
+ *
+ * <h3>Change compatibility</h3>
+ *
+ * There are several sets of changes which could be considered incompatible
+ * between two transactions which are derived from same initial state.
+ * Rules for conflict detection applies recursively for each subtree
+ * level.
+ *
+ * <h4>Change compatibility of leafs, leaf-list items</h4>
+ *
+ * Following table shows state changes and failures between two concurrent transactions,
+ * which are based on same initial state, Tx 1 completes successfully
+ * before Tx 2 is submitted.
+ *
+ * <table>
+ * <tr><th>Initial state</th><th>Tx 1</th><th>Tx 2</th><th>Result</th></tr>
+ * <tr><td>Empty</td><td>put(A,1)</td><td>put(A,2)</td><td>Tx 2 will fail, state is A=1</td></tr>
+ * <tr><td>Empty</td><td>put(A,1)</td><td>merge(A,2)</td><td>A=2</td></tr>
+ *
+ * <tr><td>Empty</td><td>merge(A,1)</td><td>put(A,2)</td><td>Tx 2 will fail, state is A=1</td></tr>
+ * <tr><td>Empty</td><td>merge(A,1)</td><td>merge(A,2)</td><td>A=2</td></tr>
+ *
+ *
+ * <tr><td>A=0</td><td>put(A,1)</td><td>put(A,2)</td><td>Tx 2 will fail, A=1</td></tr>
+ * <tr><td>A=0</td><td>put(A,1)</td><td>merge(A,2)</td><td>A=2</td></tr>
+ * <tr><td>A=0</td><td>merge(A,1)</td><td>put(A,2)</td><td>Tx 2 will fail, A=1</td></tr>
+ * <tr><td>A=0</td><td>merge(A,1)</td><td>merge(A,2)</td><td>A=2</td></tr>
+ *
+ * <tr><td>A=0</td><td>delete(A)</td><td>put(A,2)</td><td>Tx 2 will fail, A does not exists</td></tr>
+ * <tr><td>A=0</td><td>delete(A)</td><td>merge(A,2)</td><td>A=2</td></tr>
+ * </table>
+ *
+ * <h4>Change compatibility of subtrees</h4>
+ *
+ * Following table shows state changes and failures between two concurrent transactions,
+ * which are based on same initial state, Tx 1 completes successfully
+ * before Tx 2 is submitted.
+ *
+ * <table>
+ * <tr><th>Initial state</th><th>Tx 1</th><th>Tx 2</th><th>Result</th></tr>
+ *
+ * <tr><td>Empty</td><td>put(TOP,[])</td><td>put(TOP,[])</td><td>Tx 2 will fail, state is TOP=[]</td></tr>
+ * <tr><td>Empty</td><td>put(TOP,[])</td><td>merge(TOP,[])</td><td>TOP=[]</td></tr>
+ *
+ * <tr><td>Empty</td><td>put(TOP,[FOO=1])</td><td>put(TOP,[BAR=1])</td><td>Tx 2 will fail, state is TOP=[FOO=1]</td></tr>
+ * <tr><td>Empty</td><td>put(TOP,[FOO=1])</td><td>merge(TOP,[BAR=1])</td><td>TOP=[FOO=1,BAR=1]</td></tr>
+ *
+ * <tr><td>Empty</td><td>merge(TOP,[FOO=1])</td><td>put(TOP,[BAR=1])</td><td>Tx 2 will fail, state is TOP=[FOO=1]</td></tr>
+ * <tr><td>Empty</td><td>merge(TOP,[FOO=1])</td><td>merge(TOP,[BAR=1])</td><td>TOP=[FOO=1,BAR=1]</td></tr>
+ *
+ * <tr><td>TOP=[]</td><td>put(TOP,[FOO=1])</td><td>put(TOP,[BAR=1])</td><td>Tx 2 will fail, state is TOP=[FOO=1]</td></tr>
+ * <tr><td>TOP=[]</td><td>put(TOP,[FOO=1])</td><td>merge(TOP,[BAR=1])</td><td>state is TOP=[FOO=1,BAR=1]</td></tr>
+ * <tr><td>TOP=[]</td><td>merge(TOP,[FOO=1])</td><td>put(TOP,[BAR=1])</td><td>Tx 2 will fail, state is TOP=[FOO=1]</td></tr>
+ * <tr><td>TOP=[]</td><td>merge(TOP,[FOO=1])</td><td>merge(TOP,[BAR=1])</td><td>state is TOP=[FOO=1,BAR=1]</td></tr>
+ * <tr><td>TOP=[]</td><td>delete(TOP)</td><td>put(TOP,[BAR=1])</td><td>Tx 2 will fail, state is empty store</td></tr>
+ * <tr><td>TOP=[]</td><td>delete(TOP)</td><td>merge(TOP,[BAR=1])</td><td>state is TOP=[BAR=1]</td></tr>
+ *
+ * <tr><td>TOP=[]</td><td>put(TOP/FOO,1)</td><td>put(TOP/BAR,1])</td><td>state is TOP=[FOO=1,BAR=1]</td></tr>
+ * <tr><td>TOP=[]</td><td>put(TOP/FOO,1)</td><td>merge(TOP/BAR,1)</td><td>state is TOP=[FOO=1,BAR=1]</td></tr>
+ * <tr><td>TOP=[]</td><td>merge(TOP/FOO,1)</td><td>put(TOP/BAR,1)</td><td>state is TOP=[FOO=1,BAR=1]</td></tr>
+ * <tr><td>TOP=[]</td><td>merge(TOP/FOO,1)</td><td>merge(TOP/BAR,1)</td><td>state is TOP=[FOO=1,BAR=1]</td></tr>
+ * <tr><td>TOP=[]</td><td>delete(TOP)</td><td>put(TOP/BAR,1)</td><td>Tx 2 will fail, state is empty store</td></tr>
+ * <tr><td>TOP=[]</td><td>delete(TOP)</td><td>merge(TOP/BAR,1]</td><td>Tx 2 will fail, state is empty store</td></tr>
+ *
+ * <tr><td>TOP=[FOO=1]</td><td>put(TOP/FOO,2)</td><td>put(TOP/BAR,1)</td><td>state is TOP=[FOO=2,BAR=1]</td></tr>
+ * <tr><td>TOP=[FOO=1]</td><td>put(TOP/FOO,2)</td><td>merge(TOP/BAR,1)</td><td>state is TOP=[FOO=2,BAR=1]</td></tr>
+ * <tr><td>TOP=[FOO=1]</td><td>merge(TOP/FOO,2)</td><td>put(TOP/BAR,1)</td><td>state is TOP=[FOO=2,BAR=1]</td></tr>
+ * <tr><td>TOP=[FOO=1]</td><td>merge(TOP/FOO,2)</td><td>merge(TOP/BAR,1)</td><td>state is TOP=[FOO=2,BAR=1]</td></tr>
+ * <tr><td>TOP=[FOO=1]</td><td>delete(TOP/FOO)</td><td>put(TOP/BAR,1)</td><td>state is TOP=[BAR=1]</td></tr>
+ * <tr><td>TOP=[FOO=1]</td><td>delete(TOP/FOO)</td><td>merge(TOP/BAR,1]</td><td>state is TOP=[BAR=1]</td></tr>
+ * </table>
+ *
+ *
+ * <h3>Examples of failure scenarios</h3>
+ *
+ * <h4>Conflict of two transactions</h4>
+ *
+ * This example illustrates two concurrent transactions, which derived from
+ * same initial state of data tree and proposes conflicting modifications.
+ *
+ * <pre>
+ * txA = broker.newWriteTransaction(); // allocates new transaction, data tree is empty
+ * txB = broker.newWriteTransaction(); // allocates new transaction, data tree is empty
+ *
+ * txA.put(CONFIGURATION, PATH, A); // writes to PATH value A
+ * txB.put(CONFIGURATION, PATH, B) // writes to PATH value B
+ *
+ * ListenableFuture futureA = txA.submit(); // transaction A is sealed and submitted
+ * ListenebleFuture futureB = txB.submit(); // transaction B is sealed and submitted
+ * </pre>
+ *
+ * Commit of transaction A will be processed asynchronously and data tree
+ * will be updated to contain value <code>A</code> for <code>PATH</code>.
+ * Returned {@link ListenableFuture} will successfully complete once
+ * state is applied to data tree.
+ *
+ * Commit of Transaction B will fail, because previous transaction also
+ * modified path in a concurrent way. The state introduced by transaction B
+ * will not be applied. Returned {@link ListenableFuture} object will fail
+ * with {@link OptimisticLockFailedException} exception, which indicates to
+ * client that concurrent transaction prevented the submitted transaction from being
+ * applied.
+ * <br>
+ * @return a CheckFuture containing the result of the commit. The Future blocks until the
+ * commit operation is complete. A successful commit returns nothing. On failure,
+ * the Future will fail with a {@link TransactionCommitFailedException} or an exception
+ * derived from TransactionCommitFailedException.
+ *
+ * @throws IllegalStateException
+ * if the transaction is not {@link TransactionStatus#NEW}
+ */
+ CheckedFuture<Void,TransactionCommitFailedException> submit();
+
+ /**
+ * @deprecated Use {@link #submit()} instead.
+ */
+ @Deprecated
+ ListenableFuture<RpcResult<TransactionStatus>> commit();
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+public interface DataChange<P extends Path<P>, D> {
+
+ /**
+ * Returns a map of paths and newly created objects
+ *
+ * @return map of paths and newly created objects
+ */
+ Map<P, D> getCreatedOperationalData();
+
+ /**
+ * Returns a map of paths and newly created objects
+ *
+ * @return map of paths and newly created objects
+ */
+ Map<P, D> getCreatedConfigurationData();
+
+ /**
+ * Returns a map of paths and respective updated objects after update.
+ *
+ * Original state of the object is in
+ * {@link #getOriginalOperationalData()}
+ *
+ * @return map of paths and newly created objects
+ */
+ Map<P, D> getUpdatedOperationalData();
+
+ /**
+ * Returns a map of paths and respective updated objects after update.
+ *
+ * Original state of the object is in
+ * {@link #getOriginalConfigurationData()}
+ *
+ * @return map of paths and newly created objects
+ */
+ Map<P, D> getUpdatedConfigurationData();
+
+
+
+ /**
+ * Returns a set of paths of removed objects.
+ *
+ * Original state of the object is in
+ * {@link #getOriginalConfigurationData()}
+ *
+ * @return map of paths and newly created objects
+ */
+ Set<P> getRemovedConfigurationData();
+
+ /**
+ * Returns a set of paths of removed objects.
+ *
+ * Original state of the object is in
+ * {@link #getOriginalOperationalData()}
+ *
+ * @return map of paths and newly created objects
+ */
+ Set<P> getRemovedOperationalData();
+
+ /**
+ * Return a map of paths and original state of updated and removed objectd.
+ *
+ * @return map of paths and original state of updated and removed objectd.
+ */
+ Map<P, D> getOriginalConfigurationData();
+
+ /**
+ * Return a map of paths and original state of updated and removed objectd.
+ *
+ * @return map of paths and original state of updated and removed objectd.
+ */
+ Map<P, D> getOriginalOperationalData();
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ *
+ *
+ *
+ * @param <P>
+ * @param <D>
+ * @deprecated Replaced by {@link AsyncDataChangeEvent}
+ */
+@Deprecated
+public interface DataChangeEvent<P extends Path<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 udpated subtree of data, which starts at the path
+ * where listener was registered.
+ *
+ */
+ D getUpdatedOperationalSubtree();
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import java.util.EventListener;
+
+import org.opendaylight.yangtools.concepts.Path;
+/**
+ *
+ *
+ * @deprecated Replaced by {@link AsyncDataChangeEvent}
+ */
+@Deprecated
+public interface DataChangeListener<P extends Path<P>, D> extends EventListener {
+ /**
+ * Note that this method may be invoked from a shared thread pool, so
+ * implementations SHOULD NOT perform CPU-intensive operations and they
+ * definitely MUST NOT invoke any potentially blocking operations.
+ *
+ * @param change Data Change Event being delivered.
+ **/
+ void onDataChanged(DataChangeEvent<P, D> change);
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ *
+ * @deprecated Replaced by {@link AsyncDataBroker}
+ */
+@Deprecated
+public interface DataChangePublisher<P extends Path<P>, D, L extends DataChangeListener<P,D>> {
+
+ ListenerRegistration<L> registerDataChangeListener(P path, L listener);
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+/**
+ * Two phase commit handler (cohort) of the two-phase commit protocol of data.
+ *
+ * <p>
+ * The provider should expose the implementation of DataCommitHandler if it's
+ * functionality depends on any subset of data stored in data repositories, in
+ * order to participate in {@link DataBrokerService#commit(DataStoreIdentifier)
+ * operation.
+ *
+ * <p>
+ * Operations of two-phase commit handlers should not change data in data store,
+ * this is responsibility of the coordinator (broker or some component of the
+ * broker).
+ *
+ * Commit handlers are responsible for changing the internal state of the
+ * provider to reflect the committed changes in data.
+ *
+ * <h3>Two-phase commit</h3>
+ *
+ * <h4>Commit Request Phase</h4>
+ *
+ * <ol>
+ * <li> <code>Consumer</code> edits data by invocation of
+ * <code>DataBrokerService.editCandidateData(DataStoreIdentifier, DataRoot)</code>
+ * <li> <code>Consumer</code> starts a commit by invoking
+ * <code>DataBrokerService.commit(DataStoreIdentifier)</code>
+ * <li> <code>Broker</code> retrieves a list of all registered
+ * <code>DataCommitHandlers</code>
+ * <li>For each <code>DataCommitHandler</code>
+ * <ol>
+ * <li><code>Broker</code> invokes a
+ * <code>DataCommitHandler.requestCommit(DataStoreIdentifier)</code> operation.
+ * <li><code>DataCommitHandler</code> returns a <code>RpcResult</code> with
+ * <code>CommitTransaction</code>
+ * <li>If the result was successful, broker adds <code>CommitTransaction</code>
+ * to the list of opened transactions. If not, brokers stops a commit request
+ * phase and starts a rollback phase.
+ * </ol>
+ * <li><code>Broker</code> starts a commit finish phase
+ * </ol>
+ *
+ * <h4>Commit Finish Phase</h4>
+ *
+ * <ol>
+ * <li>For each <code>CommitTransaction</code> from Commit Request phase
+ * <ol>
+ * <li><code>Broker</code> broker invokes a
+ * <code>CommitTransaction.finish()</code>
+ * <li>The provider finishes a commit (applies the change) and returns an
+ * <code>RpcResult</code>.
+ * </ol>
+ * <li>
+ * <ul>
+ * <li>If all returned results means successful, the brokers end two-phase
+ * commit by returning a success commit result to the Consumer.
+ * <li>If error occured, the broker starts a commit rollback phase.
+ * </ul>
+ * </ol>
+ *
+ * <h4>Commit Rollback Phase</h4>
+ * <li>For each <code>DataCommitTransaction</code> from Commit Request phase
+ * <ol>
+ * <li><code>Broker</code>
+ * broker invokes a {@link DataCommitTransaction#finish()}
+ * <li>The provider rollbacks a commit and returns an {@link RpcResult} of
+ * rollback. </ol>
+ * <li>Broker returns a error result to the consumer.
+ *
+ * @param <P> Class representing a path
+ * @param <D> Superclass from which all data objects are derived from.
+ * @deprecated Replaced by {@link AsyncConfigurationCommitHandler}
+ */
+@Deprecated
+public interface DataCommitHandler<P extends Path<P>, D> {
+
+
+ DataCommitTransaction<P, D> requestCommit(DataModification<P,D> modification);
+
+ public interface DataCommitTransaction<P extends Path<P>, D> {
+
+ DataModification<P,D> getModification();
+
+ /**
+ *
+ * Finishes a commit.
+ *
+ * This callback is invoked by commit coordinator to finish commit action.
+ *
+ * The implementation is required to finish transaction or return unsuccessful
+ * rpc result if something went wrong.
+ *
+ * The provider (commit handler) should apply all changes to its state
+ * which are a result of data change-
+ *
+ * @return
+ */
+ RpcResult<Void> finish() throws IllegalStateException;
+
+ /**
+ * Rollbacks a commit.
+ *
+ * This callback is invoked by commit coordinator to finish commit action.
+ *
+ * The provider (commit handler) should rollback all changes to its state
+ * which were a result of previous request commit.
+ *
+ * @return
+ * @throws IllegalStateException
+ * If the method is invoked after {@link #finish()}
+ */
+ RpcResult<Void> rollback() throws IllegalStateException;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+import org.opendaylight.yangtools.concepts.Registration;
+
+/**
+ *
+ *
+ * @deprecated THis was intended as Registration object, normal use of {@link org.opendaylight.yangtools.concepts.ObjectRegistration}
+ * is suffiecient, since {@link #getPath()} was implementation leak.
+ *
+ * @param <P>
+ * @param <D>
+ */
+@Deprecated
+public interface DataCommitHandlerRegistration<P extends Path<P>,D> extends Registration {
+
+ P getPath();
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import java.util.concurrent.Future;
+
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.yangtools.concepts.Path;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+/**
+ *
+ * @deprecated Replaced by {@link AsyncWriteTransaction}
+ */
+@Deprecated
+public interface DataModification<P extends Path<P>, D> extends DataChange<P, D>, DataReader<P, D> {
+ /**
+ * Returns transaction identifier
+ *
+ * @return Transaction identifier
+ */
+ Object getIdentifier();
+
+ TransactionStatus getStatus();
+
+ /**
+ * Store a piece of data at specified path. This acts as a merge operation,
+ * which is to say that any pre-existing data which is not explicitly
+ * overwritten will be preserved. This means that if you store a container,
+ * its child lists will be merged. Performing the following put operations:
+ *
+ * 1) container { list [ a ] }
+ * 2) container { list [ b ] }
+ *
+ * will result in the following data being present:
+ *
+ * container { list [ a, b ] }
+ *
+ * This also means that storing the container will preserve any augmentations
+ * which have been attached to it.
+ *
+ * If you require an explicit replace operation, perform
+ * {@link removeOperationalData} first.
+ */
+ void putOperationalData(P path, D data);
+
+ /**
+ * Store a piece of data at specified path. This acts as a merge operation,
+ * which is to say that any pre-existing data which is not explicitly
+ * overwritten will be preserved. This means that if you store a container,
+ * its child lists will be merged. Performing the following put operations:
+ *
+ * 1) container { list [ a ] }
+ * 2) container { list [ b ] }
+ *
+ * will result in the following data being present:
+ *
+ * container { list [ a, b ] }
+ *
+ * This also means that storing the container will preserve any augmentations
+ * which have been attached to it.
+ *
+ * If you require an explicit replace operation, perform
+ * {@link removeConfigurationData} first.
+ */
+ void putConfigurationData(P path, D data);
+
+ void removeOperationalData(P path);
+
+ void removeConfigurationData(P path);
+
+ /**
+ * Initiates a two-phase commit of modification.
+ *
+ * <p>
+ * The successful commit changes the state of the system and may affect
+ * several components.
+ *
+ * <p>
+ * The effects of successful commit of data are described in the
+ * specifications and YANG models describing the Provider components of
+ * controller. It is assumed that Consumer has an understanding of this
+ * changes.
+ *
+ *
+ * @see DataCommitHandler for further information how two-phase commit is
+ * processed.
+ * @param store
+ * Identifier of the store, where commit should occur.
+ * @return Result of the Commit, containing success information or list of
+ * encountered errors, if commit was not successful. The Future
+ * blocks until {@link TransactionStatus#COMMITED} or
+ * {@link TransactionStatus#FAILED} is reached.
+ */
+ Future<RpcResult<TransactionStatus>> commit();
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+/**
+ *
+ * @deprecated Replaced by {@link AsyncDataTransactionFactory}
+ */
+@Deprecated
+public interface DataModificationTransactionFactory<P extends Path<P> ,D> {
+
+ DataModification<P, D> beginTransaction();
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ *
+ *
+ * @deprecated Replaced by org.opendaylight.controller.sal.core.spi.data.DOMStore contract.
+ */
+@Deprecated
+public interface DataProvider<P extends Path<P>, D> extends DataReader<P, D> {
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.controller.md.sal.common.api.RegistrationListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.Path;
+import org.opendaylight.yangtools.concepts.Registration;
+
+/**
+ *
+ *
+ * @deprecated replaced by {@link AsyncDataBroker} and {@link AsyncConfigurationCommitCoordinator}
+ */
+@Deprecated
+public interface DataProvisionService<P extends Path<P> , D> {
+
+ public Registration registerCommitHandler(P path, DataCommitHandler<P, D> commitHandler);
+
+ public ListenerRegistration<RegistrationListener<DataCommitHandlerRegistration<P, D>>>
+ registerCommitHandlerListener(RegistrationListener<DataCommitHandlerRegistration<P, D>> commitHandlerListener);
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ * Reader for reading YANG subtrees based on their path.
+ *
+ * Reader is requested to return object at specified path and all it's subnodes
+ * known to the reader or null if node is not found in this reader.
+ *
+ * @param <P> Path Type
+ * @param <D> Data Type
+ * @deprecated Replaced by org.opendaylight.controller.sal.core.spi.data.DOMStore contract.
+ */
+@Deprecated
+public interface DataReader<P extends Path<P> ,D> {
+
+ /**
+ * Reads data from Operational data store located at provided path
+ *
+ * @param path Path to data
+ * @return
+ */
+ D readOperationalData(P path);
+
+ D readConfigurationData(P path);
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ *
+ * @deprecated Replaced by org.opendaylight.controller.sal.core.spi.data.DOMStore Contract.
+ */
+@Deprecated
+public interface DataStore<P extends Path<P>, D> extends //
+ DataReader<P, D>, //
+ DataModificationTransactionFactory<P, D> {
+
+ @Override
+ public DataModification<P, D> beginTransaction();
+
+ @Override
+ public D readConfigurationData(P path);
+
+ @Override
+ public D readOperationalData(P path);
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+/**
+ * This exception occurs if the datastore is temporarily unavailable.
+ * A retry of the transaction may succeed after a period of time
+ */
+
+public class DataStoreUnavailableException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public DataStoreUnavailableException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+
+import com.google.common.base.Preconditions;
+
+/**
+ *
+ * Failure of asynchronous transaction commit caused by invalid data.
+ *
+ * This exception is raised and returned when transaction commit
+ * failed, because other data submitted via transactions
+ *
+ * Clients usually are not able recover from this error condition by
+ * retrieving same transaction, since data introduced by this transaction
+ * are invalid.
+ *
+ */
+public class DataValidationFailedException extends TransactionCommitFailedException {
+
+ private static final long serialVersionUID = 1L;
+
+ private Path<?> path;
+
+ private Class<? extends Path<?>> pathType;
+
+ public <P extends Path<P>> DataValidationFailedException(final Class<P> pathType,final P path,
+ final String message, final Throwable cause) {
+ super(message, cause, RpcResultBuilder.newError(ErrorType.APPLICATION, "invalid-value", message, null,
+ path != null ? path.toString() : null, cause));
+ this.pathType = Preconditions.checkNotNull(pathType, "path type must not be null");
+ this.path = Preconditions.checkNotNull(path,"path must not be null.");
+ }
+
+ public <P extends Path<P>> DataValidationFailedException(final Class<P> pathType,final P path,
+ final String message) {
+ this(pathType, path, message, null);
+ }
+
+ public final Path<?> getPath() {
+ return path;
+ }
+
+ public final Class<? extends Path<?>> getPathType() {
+ return pathType;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+public enum LogicalDatastoreType {
+
+ /**
+ * Logical atastore representing operational state of the system
+ * and it's components
+ *
+ * This datastore is used to describe operational state of
+ * the system and it's operation related data.
+ *
+ */
+ OPERATIONAL,
+ /**
+ * Logical Datastore representing configuration state of the system
+ * and it's components.
+ *
+ * This datastore is used to describe intended state of
+ * the system and intended operation mode.
+ *
+ */
+ CONFIGURATION
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
+
+/**
+*
+* Failure of asynchronous transaction commit caused by failure
+* of optimistic locking.
+*
+* This exception is raised and returned when transaction commit
+* failed, because other transaction finished successfully
+* and modified same data as failed transaction.
+*
+* Clients may recover from this error condition by
+* retrieving current state and submitting new updated
+* transaction.
+*
+*/
+public class OptimisticLockFailedException extends TransactionCommitFailedException {
+
+ private static final long serialVersionUID = 1L;
+
+ public OptimisticLockFailedException(final String message, final Throwable cause) {
+ super(message, cause, RpcResultBuilder.newError(ErrorType.APPLICATION, "resource-denied",
+ message, null, null, cause));
+ }
+
+ public OptimisticLockFailedException(final String message) {
+ this(message, null);
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.util.concurrent.ExceptionMapper;
+import org.opendaylight.yangtools.yang.common.OperationFailedException;
+import org.opendaylight.yangtools.yang.common.RpcError;
+
+/**
+ * An exception for a failed read.
+ */
+public class ReadFailedException extends OperationFailedException {
+
+ private static final long serialVersionUID = 1L;
+
+ public static final ExceptionMapper<ReadFailedException> MAPPER =
+ new ExceptionMapper<ReadFailedException>("read", ReadFailedException.class) {
+ @Override
+ protected ReadFailedException newWithCause(String message, Throwable cause) {
+ return new ReadFailedException(message, cause);
+ }
+ };
+
+ public ReadFailedException(String message, RpcError... errors) {
+ super(message, errors);
+ }
+
+ public ReadFailedException(String message, Throwable cause, RpcError... errors) {
+ super(message, cause, errors);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ * A chain of transactions. Transactions in a chain need to be committed in
+ * sequence and each transaction should see the effects of previous committed transactions
+ * as they occurred. A chain makes no guarantees of atomicity across the chained transactions -
+ * the transactions are committed as soon as possible in the order that they were submitted.
+ *
+ * This behaviour is different from the default AsyncDataBroker, where a
+ * transaction is always created from the current global state, not taking into
+ * account any transactions previously committed by the calling thread. Due to
+ * the asynchronous nature of transaction submission this can lead to surprising
+ * results. If a thread executes the following sequence sufficiently quickly:
+ *
+ * AsyncWriteTransaction t1 = broker.newWriteOnlyTransaction();
+ * t1.put(id, data);
+ * t1.submit();
+ *
+ * AsyncReadTransaction t2 = broker.newReadOnlyTransaction();
+ * Optional<?> maybeData = t2.read(id).get();
+ *
+ * it may happen, that it sees maybeData.isPresent() == false, simply because
+ * t1 has not completed the processes of being applied and t2 is actually
+ * allocated from the previous state. This is obviously bad for users who create
+ * incremental state in the datastore and actually read what they write in
+ * subsequent transactions.
+ *
+ * Using a TransactionChain instead of a broker solves this particular problem,
+ * and leads to expected behavior: t2 will always see the data written in t1
+ * present.
+ */
+public interface TransactionChain<P extends Path<P>, D> extends AutoCloseable,
+ AsyncDataTransactionFactory<P, D> {
+
+ /**
+ * Create a new read only transaction which will continue the chain.
+ *
+ * <p>
+ * The previous write transaction has to be either SUBMITTED
+ * ({@link AsyncWriteTransaction#submit submit} was invoked) or CANCELLED
+ * ({@link #close close} was invoked).
+ * <p>
+ * The returned read-only transaction presents an isolated view of the data if the previous
+ * write transaction was successful - in other words, this read-only transaction will see the
+ * state changes made by the previous write transaction in the chain. However, state which
+ * was introduced by other transactions outside this transaction chain after creation of
+ * the previous transaction is not visible.
+ *
+ * @return New transaction in the chain.
+ * @throws IllegalStateException
+ * if the previous transaction was not SUBMITTED or CANCELLED.
+ * @throws TransactionChainClosedException
+ * if the chain has been closed.
+ */
+ @Override
+ public AsyncReadOnlyTransaction<P, D> newReadOnlyTransaction();
+
+ /**
+ * Create a new read-write transaction which will continue the chain.
+ *
+ * <p>
+ * The previous write transaction has to be either SUBMITTED
+ * ({@link AsyncWriteTransaction#submit submit} was invoked) or CANCELLED
+ * ({@link #close close} was invoked).
+ * <p>
+ * The returned read-write transaction presents an isolated view of the data if the previous
+ * write transaction was successful - in other words, this read-write transaction will see the
+ * state changes made by the previous write transaction in the chain. However, state which
+ * was introduced by other transactions outside this transaction chain after creation of
+ * the previous transaction is not visible.
+ * <p>
+ * Committing this read-write transaction using {@link AsyncWriteTransaction#submit submit}
+ * will submit the state changes in this transaction to be visible to any subsequent
+ * transaction in this chain and also to any transaction outside this chain.
+ *
+ * @return New transaction in the chain.
+ * @throws IllegalStateException
+ * if the previous transaction was not SUBMITTED or CANCELLED.
+ * @throws TransactionChainClosedException
+ * if the chain has been closed.
+ */
+ @Override
+ public AsyncReadWriteTransaction<P, D> newReadWriteTransaction();
+
+ /**
+ * Create a new write-only transaction which will continue the chain.
+ *
+ * <p>
+ * The previous write transaction has to be either SUBMITTED
+ * ({@link AsyncWriteTransaction#submit submit} was invoked) or CANCELLED
+ * ({@link #close close} was invoked).
+ * <p>
+ * The returned write-only transaction presents an isolated view of the data if the previous
+ * write transaction was successful - in other words, this write-only transaction will see the
+ * state changes made by the previous write transaction in the chain. However, state which
+ * was introduced by other transactions outside this transaction chain after creation of
+ * the previous transaction is not visible.
+ * <p>
+ * Committing this write-only transaction using {@link AsyncWriteTransaction#submit submit}
+ * will submit the state changes in this transaction to be visible to any subsequent
+ * transaction in this chain and also to any transaction outside this chain.
+ *
+ * @return New transaction in the chain.
+ * @throws IllegalStateException
+ * if the previous transaction was not SUBMITTED or CANCELLED.
+ * @throws TransactionChainClosedException
+ * if the chain has been closed.
+ */
+ @Override
+ public AsyncWriteTransaction<P, D> newWriteOnlyTransaction();
+
+ @Override
+ void close();
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+/**
+ * Exception thrown when an attempt is made to open a new transaction in a closed
+ * chain.
+ */
+public final class TransactionChainClosedException extends IllegalStateException {
+ private static final long serialVersionUID = 1L;
+
+ public TransactionChainClosedException(final String message) {
+ super(message);
+ }
+
+ public TransactionChainClosedException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ * Interface for creating transaction chains.
+ */
+public interface TransactionChainFactory<P extends Path<P>, D> {
+
+ /**
+ * Create a new transaction chain. The chain will be initialized to read
+ * from its backing datastore, with no outstanding transaction. Listener
+ * will be registered to handle chain-level events.
+ *
+ * @param listener Transaction chain event listener
+ * @return A new transaction chain.
+ */
+ TransactionChain<P, D> createTransactionChain(TransactionChainListener listener);
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import java.util.EventListener;
+
+/**
+ * Listener for transaction chain events.
+ */
+public interface TransactionChainListener extends EventListener {
+ /**
+ * Invoked if when a transaction in the chain fails. All other transactions are automatically cancelled by the time
+ * this notification is invoked. Implementations should invoke chain.close() to close the chain.
+ *
+ * @param chain Transaction chain which failed
+ * @param transaction Transaction which caused the chain to fail
+ * @param cause The cause of transaction failure
+ */
+ void onTransactionChainFailed(TransactionChain<?, ?> chain, AsyncTransaction<?, ?> transaction, Throwable cause);
+
+ /**
+ * Invoked when a transaction chain is completed. A transaction chain is considered completed when it has been
+ * closed and all its instructions have completed successfully.
+ *
+ * @param chain Transaction chain which completed
+ */
+ void onTransactionChainSuccessful(TransactionChain<?, ?> chain);
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import com.google.common.base.Supplier;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+
+/**
+ * A type of TransactionCommitFailedException that indicates a situation that would result in a
+ * threading deadlock. This can occur if a caller that submits a write transaction tries to perform
+ * a blocking call via one of the <code>get</code> methods on the returned ListenableFuture. Callers
+ * should process the commit result asynchronously (via Futures#addCallback) to ensure deadlock
+ * won't occur.
+ *
+ * @author Thomas Pantelis
+ */
+public class TransactionCommitDeadlockException extends TransactionCommitFailedException {
+ private static final long serialVersionUID = 1L;
+ private static final String DEADLOCK_MESSAGE =
+ "An attempt to block on a ListenableFuture via a get method from a write " +
+ "transaction submit was detected that would result in deadlock. The commit " +
+ "result must be obtained asynchronously, e.g. via Futures#addCallback, to avoid deadlock.";
+ private static final RpcError DEADLOCK_RPCERROR = RpcResultBuilder.newError(ErrorType.APPLICATION, "lock-denied", DEADLOCK_MESSAGE);
+
+ public static final Supplier<Exception> DEADLOCK_EXCEPTION_SUPPLIER = new Supplier<Exception>() {
+ @Override
+ public Exception get() {
+ return new TransactionCommitDeadlockException(DEADLOCK_MESSAGE, DEADLOCK_RPCERROR);
+ }
+ };
+
+ public TransactionCommitDeadlockException(final String message, final RpcError... errors) {
+ super(message, errors);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.yang.common.OperationFailedException;
+import org.opendaylight.yangtools.yang.common.RpcError;
+
+/**
+ *
+ * Failed commit of asynchronous transaction
+ *
+ * This exception is raised and returned when transaction commit
+ * failed.
+ *
+ */
+public class TransactionCommitFailedException extends OperationFailedException {
+
+ private static final long serialVersionUID = 1L;
+
+ public TransactionCommitFailedException(final String message, final RpcError... errors) {
+ this(message, null, errors);
+ }
+
+ public TransactionCommitFailedException(final String message, final Throwable cause,
+ final RpcError... errors) {
+ super(message, cause, errors);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.notify;
+
+import java.util.concurrent.ExecutorService;
+
+/**
+ * Interface for publishing YANG-modeled notifications.
+ * <p>
+ * Users of this interface can publish any YANG-modeled notification which will
+ * be delivered to all subscribed listeners.
+ * <p>
+ * Preferred way of publishing of notifications is done by invoking {@link #publish(Object)}.
+ *
+ * <p>You may consider using {@link #publish(Object, ExecutorService)} if and only if
+ * your use-case requires customized execution policy or run-to-completion
+ * inside process.
+ *
+ * <p>
+ * The metadata required to deliver a notification to the correct listeners is
+ * extracted from the published notification.
+ *
+ *
+ * FIXME: Consider clarification of execution/delivery policy, how it will be
+ * affected by Actor model and cluster-wide notifications.
+ *
+ * @param <N>
+ * the type of notifications
+ */
+public interface NotificationPublishService<N> {
+
+ /**
+ * Publishes a notification and notifies subscribed listeners. All listener
+ * notifications are done via a default executor.
+ * <p>
+ * <b>Note:</b> This call will block when the default executor is saturated
+ * and the notification queue for this executor is full.
+ *
+ * @param notification
+ * the notification to publish.
+ */
+ void publish(N notification);
+
+ /**
+ * Publishes a notification and notifies subscribed listeners. All listener
+ * notifications are done via the provided executor.
+ * <p>
+ * <b>Note:</b> Use only if necessary. Consider using
+ * {@link #publish(Object)} for most use-cases.
+ *
+ * <p>
+ * By using this method you could customize execution policy of listeners present
+ * inside process (e.g. using single-threaded executor or even same-thread executor
+ * delivery.
+ *
+ * <p>
+ * This executor is used only for inside-process notification deliveries.
+ *
+ * @param notification
+ * the notification to publish.
+ * @param executor
+ * the executor that will be used to deliver notifications to
+ * subscribed listeners.
+ */
+ void publish(N notification, ExecutorService executor);
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.notify;
+
+import java.util.EventListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+
+public interface NotificationSubscriptionService<T, N, L extends EventListener> {
+
+ ListenerRegistration<L> registerNotificationListener(T type, L listener);
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.routing;
+
+import org.opendaylight.yangtools.concepts.Mutable;
+import org.opendaylight.yangtools.concepts.Path;
+
+public interface MutableRoutingTable<C, P extends Path<P>, T> extends RoutingTable<C,P,T>, Mutable {
+
+ void setDefaultRoute(T target);
+ void updateRoute(P path,T target);
+ void removeRoute(P path);
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.routing;
+
+import org.opendaylight.yangtools.concepts.Immutable;
+
+public interface Route<C,P> extends Immutable {
+
+ C getType();
+
+ P getPath();
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.routing;
+
+import java.util.Map;
+import java.util.Set;
+/**
+ * Event representing change in RPC routing table.
+ *
+ *
+ * @param <C> Type, which is used to represent Routing context.
+ * @param <P> Type of data tree path, which is used to identify route.
+ */
+public interface RouteChange<C,P> {
+
+ /**
+ *
+ * Returns a map of removed routes in associated routing contexts.
+ * <p>
+ * This map represents routes, which were withdrawn from broker local
+ * routing table and broker may need to forward RPC to other broker
+ * in order to process RPC request.
+ *
+ * @return Map of contexts and removed routes
+ */
+ Map<C,Set<P>> getRemovals();
+ /**
+ *
+ * Returns a map of announced routes in associated routing contexts.
+ *
+ * This map represents routes, which were announced by broker
+ * and are present in broker's local routing table. This routes
+ * are processed by implementations which are registered
+ * to originating broker.
+ *
+ * @return Map of contexts and announced routes
+ */
+ Map<C,Set<P>> getAnnouncements();
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.routing;
+
+import java.util.EventListener;
+/**
+ *
+ * Listener which is interested in receiving RouteChangeEvents
+ * for its local broker.
+ * <p>
+ * Listener is registerd via {@link RouteChangePublisher#registerRouteChangeListener(RouteChangeListener)}
+ *
+ *
+ * @param <C> Type, which is used to represent Routing context.
+ * @param <P> Type of data tree path, which is used to identify route.
+ */
+public interface RouteChangeListener<C,P> extends EventListener {
+
+ /**
+ * Callback which is invoked if there is an rpc routing table change.
+ *
+ * @param change Event representing change in local RPC routing table.
+ */
+ void onRouteChange(RouteChange<C, P> change);
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.routing;
+
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+
+/**
+ * Publishes changes in local RPC routing table to registered listener.
+ *
+ * @param <C> Type, which is used to represent Routing context.
+ * @param <P> Type of data tree path, which is used to identify route.
+ */
+public interface RouteChangePublisher<C,P> {
+
+ <L extends RouteChangeListener<C,P>> ListenerRegistration<L> registerRouteChangeListener(L listener);
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.routing;
+
+import org.opendaylight.yangtools.concepts.Path;
+import org.opendaylight.yangtools.concepts.Registration;
+
+/**
+ * Base interface for a routed RPC RPC implementation registration.
+ *
+ * @param <C> the context type used for routing
+ * @param <P> the path identifier type
+ * @param <S> the RPC implementation type
+ */
+public interface RoutedRegistration<C, P extends Path<P>, S> extends Registration {
+
+ /**
+ * Registers the RPC implementation associated with this registration for the given path
+ * identifier and context.
+ *
+ * @param context the context used for routing RPCs to this implementation.
+ * @param path the path identifier for which to register.
+ */
+ void registerPath(C context, P path);
+
+ /**
+ * Unregisters the RPC implementation associated with this registration for the given path
+ * identifier and context.
+ *
+ * @param context the context used for routing RPCs to this implementation.
+ * @param path the path identifier for which to unregister.
+ */
+ void unregisterPath(C context, P path);
+
+ @Override
+ void close();
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.routing;
+
+import java.util.Map;
+import java.util.Set;
+
+public interface Router<C,P,D> extends //
+ RouteChangePublisher<C, P> {
+
+ Map<C, Set<P>> getAnnouncedPaths();
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.routing;
+
+import java.util.Map;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+public interface RoutingTable<C, P extends Path<P>, T> {
+
+ C getIdentifier();
+
+ T getDefaultRoute();
+
+ Map<P,T> getRoutes();
+
+ T getRoute(P path);
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-parent</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>sal-common-impl</artifactId>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-common-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-common-util</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-impl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-model-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-parser-impl</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Export-Package>org.opendaylight.controller.md.sal.common.impl,
+ org.opendaylight.controller.md.sal.common.impl.*</Export-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <scm>
+ <connection>scm:git:http://git.opendaylight.org/gerrit/controller.git</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+ <tag>HEAD</tag>
+ <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
+ </scm>
+
+</project>
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.impl;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Collections;
+import java.util.EventListener;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+
+public class ListenerRegistry<T extends EventListener> {
+
+ final Set<ListenerRegistration<T>> listeners;
+ final Set<ListenerRegistration<T>> unmodifiableView;
+
+ public ListenerRegistry() {
+ listeners = new HashSet<>();
+ unmodifiableView = Collections.unmodifiableSet(listeners);
+ }
+
+ public Iterable<ListenerRegistration<T>> getListeners() {
+ return unmodifiableView;
+ }
+
+
+ public ListenerRegistration<T> register(T listener) {
+ checkNotNull(listener, "Listener should not be null.");
+ ListenerRegistrationImpl<T> ret = new ListenerRegistrationImpl<T>(listener);
+ listeners.add(ret);
+ return ret;
+ }
+
+
+ @SuppressWarnings("rawtypes")
+ private void remove(ListenerRegistrationImpl registration) {
+ listeners.remove(registration);
+ }
+
+ private class ListenerRegistrationImpl<P extends EventListener> //
+ extends AbstractObjectRegistration<P> //
+ implements ListenerRegistration<P> {
+
+ public ListenerRegistrationImpl(P instance) {
+ super(instance);
+ }
+
+ @Override
+ protected void removeRegistration() {
+ ListenerRegistry.this.remove(this);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.impl.routing;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.opendaylight.controller.md.sal.common.api.routing.RouteChange;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+public class RoutingUtils {
+
+ public static <C,P> RouteChange<C,P> removalChange(C context,P path) {
+ final ImmutableMap<C, Set<P>> announcements = ImmutableMap.<C,Set<P>>of();
+ final ImmutableMap<C, Set<P>> removals = ImmutableMap.<C,Set<P>>of(context, ImmutableSet.of(path));
+ return new RouteChangeImpl<C,P>(announcements, removals);
+ }
+
+ public static <C,P> RouteChange<C,P> announcementChange(C context,P path) {
+ final ImmutableMap<C, Set<P>> announcements = ImmutableMap.<C,Set<P>>of(context, ImmutableSet.of(path));
+ final ImmutableMap<C, Set<P>> removals = ImmutableMap.<C,Set<P>>of();
+ return new RouteChangeImpl<C,P>(announcements, removals);
+ }
+
+
+ public static <C,P> RouteChange<C,P> change(Map<C, Set<P>> announcements,
+ Map<C, Set<P>> removals) {
+ final ImmutableMap<C, Set<P>> immutableAnnouncements = ImmutableMap.<C,Set<P>>copyOf(announcements);
+ final ImmutableMap<C, Set<P>> immutableRemovals = ImmutableMap.<C,Set<P>>copyOf(removals);
+ return new RouteChangeImpl<C,P>(immutableAnnouncements, immutableRemovals);
+ }
+
+
+ private static class RouteChangeImpl<C,P> implements RouteChange<C, P> {
+ private final Map<C, Set<P>> removal;
+ private final Map<C, Set<P>> announcement;
+
+ public RouteChangeImpl(ImmutableMap<C, Set<P>> announcement, ImmutableMap<C, Set<P>> removal) {
+ super();
+ this.removal = removal;
+ this.announcement = announcement;
+ }
+
+ @Override
+ public Map<C, Set<P>> getAnnouncements() {
+ return announcement;
+ }
+
+ @Override
+ public Map<C, Set<P>> getRemovals() {
+ return removal;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((announcement == null) ? 0 : announcement.hashCode());
+ result = prime * result + ((removal == null) ? 0 : removal.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ RouteChangeImpl<?, ?> other = (RouteChangeImpl<?, ?>) obj;
+ if (announcement == null) {
+ if (other.announcement != null) {
+ return false;
+ }
+ } else if (!announcement.equals(other.announcement)) {
+ return false;
+ }
+ if (removal == null) {
+ if (other.removal != null) {
+ return false;
+ }
+ } else if (!removal.equals(other.removal)) {
+ return false;
+ }
+ return true;
+ }
+ }
+
+
+
+}
--- /dev/null
+/**
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.impl.service;
+
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Deprecated
+public abstract class AbstractDataTransaction {
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractDataTransaction.class);
+ private static final ListenableFuture<RpcResult<TransactionStatus>> SUCCESS_FUTURE =
+ Futures.immediateFuture(RpcResultBuilder.success(TransactionStatus.COMMITED).build());
+
+ public static ListenableFuture<RpcResult<TransactionStatus>> convertToLegacyCommitFuture(final CheckedFuture<Void,TransactionCommitFailedException> from) {
+ return Futures.transform(from, new AsyncFunction<Void, RpcResult<TransactionStatus>>() {
+ @Override
+ public ListenableFuture<RpcResult<TransactionStatus>> apply(final Void input) {
+ return SUCCESS_FUTURE;
+ }
+ });
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.impl.util;
+
+import java.util.concurrent.locks.Lock;
+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;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.impl.util.compat;
+
+public class DataNormalizationException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public DataNormalizationException(String message) {
+ super(message);
+ }
+
+ public DataNormalizationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.impl.util.compat;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import java.util.Collections;
+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.ConcurrentHashMap;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
+import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.util.EffectiveAugmentationSchema;
+
+@Deprecated
+public abstract class DataNormalizationOperation<T extends PathArgument> implements Identifiable<T> {
+
+ private final T identifier;
+ private final Optional<DataSchemaNode> dataSchemaNode;
+
+ @Override
+ public T getIdentifier() {
+ return identifier;
+ };
+
+ protected DataNormalizationOperation(final T identifier, final SchemaNode schema) {
+ super();
+ this.identifier = identifier;
+ if(schema instanceof DataSchemaNode) {
+ this.dataSchemaNode = Optional.of((DataSchemaNode) schema);
+ } else {
+ this.dataSchemaNode = Optional.absent();
+ }
+ }
+
+ public boolean isMixin() {
+ return false;
+ }
+
+
+ public boolean isKeyedEntry() {
+ return false;
+ }
+
+ protected Set<QName> getQNameIdentifiers() {
+ return Collections.singleton(identifier.getNodeType());
+ }
+
+ public abstract DataNormalizationOperation<?> getChild(final PathArgument child) throws DataNormalizationException;
+
+ public abstract DataNormalizationOperation<?> getChild(QName child) throws DataNormalizationException;
+
+
+ public abstract boolean isLeaf();
+
+ public Optional<DataSchemaNode> getDataSchemaNode() {
+ // FIXME
+ return dataSchemaNode;
+ }
+
+ private static abstract class SimpleTypeNormalization<T extends PathArgument> extends DataNormalizationOperation<T> {
+
+ protected SimpleTypeNormalization(final T identifier, final DataSchemaNode potential) {
+ super(identifier,potential);
+ }
+
+ @Override
+ public DataNormalizationOperation<?> getChild(final PathArgument child) {
+ return null;
+ }
+
+ @Override
+ public DataNormalizationOperation<?> getChild(final QName child) {
+ return null;
+ }
+
+ @Override
+ public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
+ return null;
+ }
+
+ @Override
+ public boolean isLeaf() {
+ return true;
+ }
+
+ }
+
+ private static final class LeafNormalization extends SimpleTypeNormalization<NodeIdentifier> {
+
+ protected LeafNormalization(final LeafSchemaNode potential) {
+ super(new NodeIdentifier(potential.getQName()),potential);
+ }
+
+ }
+
+ private static final class LeafListEntryNormalization extends SimpleTypeNormalization<NodeWithValue> {
+
+ public LeafListEntryNormalization(final LeafListSchemaNode potential) {
+ super(new NodeWithValue(potential.getQName(), null),potential);
+ }
+
+ @Override
+ public boolean isKeyedEntry() {
+ return true;
+ }
+ }
+
+ private static abstract class CompositeNodeNormalizationOperation<T extends PathArgument> extends
+ DataNormalizationOperation<T> {
+
+ protected CompositeNodeNormalizationOperation(final T identifier, final DataSchemaNode schema) {
+ super(identifier,schema);
+ }
+
+ @Override
+ public boolean isLeaf() {
+ return false;
+ }
+
+
+ }
+
+ private static abstract class DataContainerNormalizationOperation<T extends PathArgument> extends
+ CompositeNodeNormalizationOperation<T> {
+
+ private final DataNodeContainer schema;
+ private final Map<QName, DataNormalizationOperation<?>> byQName;
+ private final Map<PathArgument, DataNormalizationOperation<?>> byArg;
+
+ protected DataContainerNormalizationOperation(final T identifier, final DataNodeContainer schema, final DataSchemaNode node) {
+ super(identifier,node);
+ this.schema = schema;
+ this.byArg = new ConcurrentHashMap<>();
+ this.byQName = new ConcurrentHashMap<>();
+ }
+
+ @Override
+ public DataNormalizationOperation<?> getChild(final PathArgument child) throws DataNormalizationException {
+ DataNormalizationOperation<?> potential = byArg.get(child);
+ if (potential != null) {
+ return potential;
+ }
+ potential = fromLocalSchema(child);
+ return register(potential);
+ }
+
+ private DataNormalizationOperation<?> fromLocalSchema(final PathArgument child) throws DataNormalizationException {
+ if (child instanceof AugmentationIdentifier) {
+ return fromSchemaAndQNameChecked(schema, ((AugmentationIdentifier) child).getPossibleChildNames()
+ .iterator().next());
+ }
+ return fromSchemaAndQNameChecked(schema, child.getNodeType());
+ }
+
+ @Override
+ public DataNormalizationOperation<?> getChild(final QName child) throws DataNormalizationException {
+ DataNormalizationOperation<?> potential = byQName.get(child);
+ if (potential != null) {
+ return potential;
+ }
+ potential = fromLocalSchemaAndQName(schema, child);
+ return register(potential);
+ }
+
+ protected DataNormalizationOperation<?> fromLocalSchemaAndQName(final DataNodeContainer schema2, final QName child) throws DataNormalizationException {
+ return fromSchemaAndQNameChecked(schema2, child);
+ }
+
+ private DataNormalizationOperation<?> register(final DataNormalizationOperation<?> potential) {
+ if (potential != null) {
+ byArg.put(potential.getIdentifier(), potential);
+ for (final QName qName : potential.getQNameIdentifiers()) {
+ byQName.put(qName, potential);
+ }
+ }
+ return potential;
+ }
+
+ }
+
+ private static final class ListItemNormalization extends
+ DataContainerNormalizationOperation<NodeIdentifierWithPredicates> {
+
+ private final List<QName> keyDefinition;
+
+ protected ListItemNormalization(final NodeIdentifierWithPredicates identifier, final ListSchemaNode schema) {
+ super(identifier, schema,schema);
+ keyDefinition = schema.getKeyDefinition();
+ }
+
+ @Override
+ public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
+ final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder = Builders
+ .mapEntryBuilder().withNodeIdentifier((NodeIdentifierWithPredicates) currentArg);
+ for (final Entry<QName, Object> keyValue : ((NodeIdentifierWithPredicates) currentArg).getKeyValues().entrySet()) {
+ builder.addChild(Builders.leafBuilder()
+ //
+ .withNodeIdentifier(new NodeIdentifier(keyValue.getKey())).withValue(keyValue.getValue())
+ .build());
+ }
+ return builder.build();
+ }
+
+
+ @Override
+ public boolean isKeyedEntry() {
+ return true;
+ }
+ }
+
+ private static final class UnkeyedListItemNormalization extends DataContainerNormalizationOperation<NodeIdentifier> {
+
+ protected UnkeyedListItemNormalization(final ListSchemaNode schema) {
+ super(new NodeIdentifier(schema.getQName()), schema,schema);
+ }
+
+ @Override
+ public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
+ return Builders.unkeyedListEntryBuilder().withNodeIdentifier((NodeIdentifier) currentArg).build();
+ }
+
+ }
+
+ private static final class ContainerNormalization extends DataContainerNormalizationOperation<NodeIdentifier> {
+
+ protected ContainerNormalization(final ContainerSchemaNode schema) {
+ super(new NodeIdentifier(schema.getQName()),schema, schema);
+ }
+
+ @Override
+ public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
+ return Builders.containerBuilder().withNodeIdentifier((NodeIdentifier) currentArg).build();
+ }
+
+ }
+
+ private static abstract class MixinNormalizationOp<T extends PathArgument> extends
+ CompositeNodeNormalizationOperation<T> {
+
+ protected MixinNormalizationOp(final T identifier, final DataSchemaNode schema) {
+ super(identifier,schema);
+ }
+
+ @Override
+ public final boolean isMixin() {
+ return true;
+ }
+
+ }
+
+
+ private static final class OrderedLeafListMixinNormalization extends UnorderedLeafListMixinNormalization {
+
+
+ public OrderedLeafListMixinNormalization(final LeafListSchemaNode potential) {
+ super(potential);
+ }
+
+ @Override
+ public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
+ return Builders.orderedLeafSetBuilder().withNodeIdentifier(getIdentifier()).build();
+ }
+ }
+
+ private static class UnorderedLeafListMixinNormalization extends MixinNormalizationOp<NodeIdentifier> {
+
+ private final DataNormalizationOperation<?> innerOp;
+
+ public UnorderedLeafListMixinNormalization(final LeafListSchemaNode potential) {
+ super(new NodeIdentifier(potential.getQName()),potential);
+ innerOp = new LeafListEntryNormalization(potential);
+ }
+ @Override
+ public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
+ return Builders.leafSetBuilder().withNodeIdentifier(getIdentifier()).build();
+ }
+
+ @Override
+ public DataNormalizationOperation<?> getChild(final PathArgument child) {
+ if (child instanceof NodeWithValue) {
+ return innerOp;
+ }
+ return null;
+ }
+
+ @Override
+ public DataNormalizationOperation<?> getChild(final QName child) {
+ if (getIdentifier().getNodeType().equals(child)) {
+ return innerOp;
+ }
+ return null;
+ }
+ }
+
+ private static final class AugmentationNormalization extends DataContainerNormalizationOperation<AugmentationIdentifier> {
+
+ public AugmentationNormalization(final AugmentationSchema augmentation, final DataNodeContainer schema) {
+ //super();
+ super(augmentationIdentifierFrom(augmentation), augmentationProxy(augmentation,schema),null);
+ }
+
+ @Override
+ public boolean isMixin() {
+ return true;
+ }
+
+
+
+ @Override
+ protected DataNormalizationOperation<?> fromLocalSchemaAndQName(final DataNodeContainer schema, final QName child)
+ throws DataNormalizationException {
+ final Optional<DataSchemaNode> potential = findChildSchemaNode(schema, child);
+ if (!potential.isPresent()) {
+ return null;
+ }
+
+ final DataSchemaNode result = potential.get();
+ // We try to look up if this node was added by augmentation
+ if ((schema instanceof DataSchemaNode) && result.isAugmenting()) {
+ return fromAugmentation(schema, (AugmentationTarget) schema, result);
+ }
+ return fromDataSchemaNode(result);
+ }
+
+ @Override
+ protected Set<QName> getQNameIdentifiers() {
+ return getIdentifier().getPossibleChildNames();
+ }
+
+ @Override
+ public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
+ return Builders.augmentationBuilder().withNodeIdentifier(getIdentifier()).build();
+ }
+
+ }
+
+ private static class UnorderedMapMixinNormalization extends MixinNormalizationOp<NodeIdentifier> {
+
+ private final ListItemNormalization innerNode;
+
+ public UnorderedMapMixinNormalization(final ListSchemaNode list) {
+ super(new NodeIdentifier(list.getQName()),list);
+ this.innerNode = new ListItemNormalization(new NodeIdentifierWithPredicates(list.getQName(),
+ Collections.<QName, Object> emptyMap()), list);
+ }
+
+ @Override
+ public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
+ return Builders.mapBuilder().withNodeIdentifier(getIdentifier()).build();
+ }
+
+ @Override
+ public DataNormalizationOperation<?> getChild(final PathArgument child) {
+ if (child.getNodeType().equals(getIdentifier().getNodeType())) {
+ return innerNode;
+ }
+ return null;
+ }
+
+ @Override
+ public DataNormalizationOperation<?> getChild(final QName child) {
+ if (getIdentifier().getNodeType().equals(child)) {
+ return innerNode;
+ }
+ return null;
+ }
+
+ }
+
+
+ private static class UnkeyedListMixinNormalization extends MixinNormalizationOp<NodeIdentifier> {
+
+ private final UnkeyedListItemNormalization innerNode;
+
+ public UnkeyedListMixinNormalization(final ListSchemaNode list) {
+ super(new NodeIdentifier(list.getQName()),list);
+ this.innerNode = new UnkeyedListItemNormalization(list);
+ }
+
+ @Override
+ public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
+ return Builders.unkeyedListBuilder().withNodeIdentifier(getIdentifier()).build();
+ }
+
+ @Override
+ public DataNormalizationOperation<?> getChild(final PathArgument child) {
+ if (child.getNodeType().equals(getIdentifier().getNodeType())) {
+ return innerNode;
+ }
+ return null;
+ }
+
+ @Override
+ public DataNormalizationOperation<?> getChild(final QName child) {
+ if (getIdentifier().getNodeType().equals(child)) {
+ return innerNode;
+ }
+ return null;
+ }
+
+ }
+
+ private static final class OrderedMapMixinNormalization extends UnorderedMapMixinNormalization {
+
+ public OrderedMapMixinNormalization(final ListSchemaNode list) {
+ super(list);
+ }
+
+ @Override
+ public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
+ return Builders.orderedMapBuilder().withNodeIdentifier(getIdentifier()).build();
+ }
+
+ }
+
+ private static class ChoiceNodeNormalization extends MixinNormalizationOp<NodeIdentifier> {
+
+ private final ImmutableMap<QName, DataNormalizationOperation<?>> byQName;
+ private final ImmutableMap<PathArgument, DataNormalizationOperation<?>> byArg;
+
+ protected ChoiceNodeNormalization(final ChoiceSchemaNode schema) {
+ super(new NodeIdentifier(schema.getQName()),schema);
+ final ImmutableMap.Builder<QName, DataNormalizationOperation<?>> byQNameBuilder = ImmutableMap.builder();
+ final ImmutableMap.Builder<PathArgument, DataNormalizationOperation<?>> byArgBuilder = ImmutableMap.builder();
+
+ for (final ChoiceCaseNode caze : schema.getCases()) {
+ for (final DataSchemaNode cazeChild : caze.getChildNodes()) {
+ final DataNormalizationOperation<?> childOp = fromDataSchemaNode(cazeChild);
+ byArgBuilder.put(childOp.getIdentifier(), childOp);
+ for (final QName qname : childOp.getQNameIdentifiers()) {
+ byQNameBuilder.put(qname, childOp);
+ }
+ }
+ }
+ byQName = byQNameBuilder.build();
+ byArg = byArgBuilder.build();
+ }
+
+ @Override
+ public DataNormalizationOperation<?> getChild(final PathArgument child) {
+ return byArg.get(child);
+ }
+
+ @Override
+ public DataNormalizationOperation<?> getChild(final QName child) {
+ return byQName.get(child);
+ }
+
+ @Override
+ public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
+ return Builders.choiceBuilder().withNodeIdentifier(getIdentifier()).build();
+ }
+ }
+
+ private static class AnyXmlNormalization extends DataNormalizationOperation<NodeIdentifier> {
+
+ protected AnyXmlNormalization( final AnyXmlSchemaNode schema) {
+ super( new NodeIdentifier(schema.getQName()), schema);
+ }
+
+ @Override
+ public DataNormalizationOperation<?> getChild( final PathArgument child ) throws DataNormalizationException {
+ return null;
+ }
+
+ @Override
+ public DataNormalizationOperation<?> getChild( final QName child ) throws DataNormalizationException {
+ return null;
+ }
+
+ @Override
+ public boolean isLeaf() {
+ return false;
+ }
+
+ @Override
+ public NormalizedNode<?, ?> createDefault( final PathArgument currentArg ) {
+ return null;
+ }
+ }
+
+ private static final Optional<DataSchemaNode> findChildSchemaNode(final DataNodeContainer parent,final QName child) {
+ DataSchemaNode potential = parent.getDataChildByName(child);
+ if (potential == null) {
+ final Iterable<ChoiceSchemaNode> choices = FluentIterable.from(parent.getChildNodes()).filter(ChoiceSchemaNode.class);
+ potential = findChoice(choices, child);
+ }
+ return Optional.fromNullable(potential);
+ }
+
+ private static DataNormalizationOperation<?> fromSchemaAndQNameChecked(final DataNodeContainer schema,
+ final QName child) throws DataNormalizationException {
+
+ final Optional<DataSchemaNode> potential = findChildSchemaNode(schema, child);
+ if (!potential.isPresent()) {
+ throw new DataNormalizationException(String.format("Supplied QName %s is not valid according to schema %s, potential children nodes: %s", child, schema,schema.getChildNodes()));
+ }
+
+ final DataSchemaNode result = potential.get();
+ // We try to look up if this node was added by augmentation
+ if ((schema instanceof DataSchemaNode) && result.isAugmenting()) {
+ return fromAugmentation(schema, (AugmentationTarget) schema, result);
+ }
+ return fromDataSchemaNode(result);
+ }
+
+ private static ChoiceSchemaNode findChoice(final Iterable<ChoiceSchemaNode> choices, final QName child) {
+ ChoiceSchemaNode foundChoice = null;
+ choiceLoop: for (final ChoiceSchemaNode choice : choices) {
+ for (final ChoiceCaseNode caze : choice.getCases()) {
+ if (findChildSchemaNode(caze, child).isPresent()) {
+ foundChoice = choice;
+ break choiceLoop;
+ }
+ }
+ }
+ return foundChoice;
+ }
+
+ public static AugmentationIdentifier augmentationIdentifierFrom(final AugmentationSchema augmentation) {
+ final ImmutableSet.Builder<QName> potentialChildren = ImmutableSet.builder();
+ for (final DataSchemaNode child : augmentation.getChildNodes()) {
+ potentialChildren.add(child.getQName());
+ }
+ return new AugmentationIdentifier(potentialChildren.build());
+ }
+
+ private static DataNodeContainer augmentationProxy(final AugmentationSchema augmentation, final DataNodeContainer schema) {
+ final Set<DataSchemaNode> children = new HashSet<>();
+ for (final DataSchemaNode augNode : augmentation.getChildNodes()) {
+ children.add(schema.getDataChildByName(augNode.getQName()));
+ }
+ return new EffectiveAugmentationSchema(augmentation, children);
+ }
+
+ /**
+ * Returns a DataNormalizationOperation for provided child node
+ *
+ * If supplied child is added by Augmentation this operation returns
+ * a DataNormalizationOperation for augmentation,
+ * otherwise returns a DataNormalizationOperation for child as
+ * call for {@link #fromDataSchemaNode(DataSchemaNode)}.
+ *
+ *
+ * @param parent
+ * @param parentAug
+ * @param child
+ * @return
+ */
+ private static DataNormalizationOperation<?> fromAugmentation(final DataNodeContainer parent,
+ final AugmentationTarget parentAug, final DataSchemaNode child) {
+ AugmentationSchema augmentation = null;
+ for (final AugmentationSchema aug : parentAug.getAvailableAugmentations()) {
+ final DataSchemaNode potential = aug.getDataChildByName(child.getQName());
+ if (potential != null) {
+ augmentation = aug;
+ break;
+ }
+
+ }
+ if (augmentation != null) {
+ return new AugmentationNormalization(augmentation, parent);
+ } else {
+ return fromDataSchemaNode(child);
+ }
+ }
+
+ public static DataNormalizationOperation<?> fromDataSchemaNode(final DataSchemaNode potential) {
+ if (potential instanceof ContainerSchemaNode) {
+ return new ContainerNormalization((ContainerSchemaNode) potential);
+ } else if (potential instanceof ListSchemaNode) {
+
+ return fromListSchemaNode((ListSchemaNode) potential);
+ } else if (potential instanceof LeafSchemaNode) {
+ return new LeafNormalization((LeafSchemaNode) potential);
+ } else if (potential instanceof ChoiceSchemaNode) {
+ return new ChoiceNodeNormalization((ChoiceSchemaNode) potential);
+ } else if (potential instanceof LeafListSchemaNode) {
+ return fromLeafListSchemaNode((LeafListSchemaNode) potential);
+ } else if (potential instanceof AnyXmlSchemaNode) {
+ return new AnyXmlNormalization( (AnyXmlSchemaNode) potential);
+ }
+ return null;
+ }
+
+ private static DataNormalizationOperation<?> fromListSchemaNode(final ListSchemaNode potential) {
+ final List<QName> keyDefinition = potential.getKeyDefinition();
+ if(keyDefinition == null || keyDefinition.isEmpty()) {
+ return new UnkeyedListMixinNormalization(potential);
+ }
+ if(potential.isUserOrdered()) {
+ return new OrderedMapMixinNormalization(potential);
+ }
+ return new UnorderedMapMixinNormalization(potential);
+ }
+
+ private static DataNormalizationOperation<?> fromLeafListSchemaNode(final LeafListSchemaNode potential) {
+ if(potential.isUserOrdered()) {
+ return new OrderedLeafListMixinNormalization(potential);
+ }
+ return new UnorderedLeafListMixinNormalization(potential);
+ }
+
+
+ public static DataNormalizationOperation<?> from(final SchemaContext ctx) {
+ return new ContainerNormalization(ctx);
+ }
+
+ public abstract NormalizedNode<?, ?> createDefault(PathArgument currentArg);
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.impl.util.compat;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Iterator;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * @deprecated This class provides compatibility between XML semantics
+ * and {@link org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree}
+ */
+@Deprecated
+public class DataNormalizer {
+
+ private final DataNormalizationOperation<?> operation;
+
+ public DataNormalizer(final SchemaContext ctx) {
+ operation = DataNormalizationOperation.from(ctx);
+ }
+
+ public YangInstanceIdentifier toNormalized(final YangInstanceIdentifier legacy) {
+ ImmutableList.Builder<PathArgument> normalizedArgs = ImmutableList.builder();
+
+ DataNormalizationOperation<?> currentOp = operation;
+ Iterator<PathArgument> arguments = legacy.getPathArguments().iterator();
+
+ try {
+ while (arguments.hasNext()) {
+ PathArgument legacyArg = arguments.next();
+ currentOp = currentOp.getChild(legacyArg);
+ checkArgument(currentOp != null,
+ "Legacy Instance Identifier %s is not correct. Normalized Instance Identifier so far %s",
+ legacy, normalizedArgs.build());
+ while (currentOp.isMixin()) {
+ normalizedArgs.add(currentOp.getIdentifier());
+ currentOp = currentOp.getChild(legacyArg.getNodeType());
+ }
+ normalizedArgs.add(legacyArg);
+ }
+ } catch (DataNormalizationException e) {
+ throw new IllegalArgumentException(String.format("Failed to normalize path %s", legacy), e);
+ }
+
+ return YangInstanceIdentifier.create(normalizedArgs.build());
+ }
+
+ public DataNormalizationOperation<?> getOperation(final YangInstanceIdentifier legacy) throws DataNormalizationException {
+ DataNormalizationOperation<?> currentOp = operation;
+ Iterator<PathArgument> arguments = legacy.getPathArguments().iterator();
+
+ while (arguments.hasNext()) {
+ currentOp = currentOp.getChild(arguments.next());
+ }
+ return currentOp;
+ }
+
+ public YangInstanceIdentifier toLegacy(final YangInstanceIdentifier normalized) throws DataNormalizationException {
+ ImmutableList.Builder<PathArgument> legacyArgs = ImmutableList.builder();
+ DataNormalizationOperation<?> currentOp = operation;
+ for (PathArgument normalizedArg : normalized.getPathArguments()) {
+ currentOp = currentOp.getChild(normalizedArg);
+ if (!currentOp.isMixin()) {
+ legacyArgs.add(normalizedArg);
+ }
+ }
+ return YangInstanceIdentifier.create(legacyArgs.build());
+ }
+
+ public DataNormalizationOperation<?> getRootOperation() {
+ return operation;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.impl.util.compat;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.UsesNode;
+
+class DataSchemaContainerProxy implements DataNodeContainer {
+
+ private final Set<DataSchemaNode> realChildSchemas;
+ private final Map<QName, DataSchemaNode> mappedChildSchemas;
+
+ public DataSchemaContainerProxy(final Set<DataSchemaNode> realChildSchema) {
+ realChildSchemas = realChildSchema;
+ mappedChildSchemas = new HashMap<QName, DataSchemaNode>();
+ for(DataSchemaNode schema : realChildSchemas) {
+ mappedChildSchemas.put(schema.getQName(),schema);
+ }
+ }
+
+ @Override
+ public DataSchemaNode getDataChildByName(final QName name) {
+ return mappedChildSchemas.get(name);
+ }
+
+ @Override
+ public Set<DataSchemaNode> getChildNodes() {
+ return realChildSchemas;
+ }
+
+ @Override
+ public DataSchemaNode getDataChildByName(final String arg0) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Set<GroupingDefinition> getGroupings() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Set<TypeDefinition<?>> getTypeDefinitions() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Set<UsesNode> getUses() {
+ return Collections.emptySet();
+ }
+
+}
--- /dev/null
+module normalization-test {
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:normalization:test";
+ prefix "norm-test";
+
+ revision "2014-03-13" {
+ description "Initial revision.";
+ }
+
+ grouping outer-grouping {
+ }
+
+ container test {
+ list outer-list {
+ key id;
+ leaf id {
+ type uint16;
+ }
+ choice outer-choice {
+ case one {
+ leaf one {
+ type string;
+ }
+ }
+ case two-three {
+ leaf two {
+ type string;
+ }
+ leaf three {
+ type string;
+ }
+ }
+ }
+ list inner-list {
+ key name;
+ ordered-by user;
+
+ leaf name {
+ type string;
+ }
+ leaf value {
+ type string;
+ }
+ }
+ }
+
+ list unkeyed-list {
+ leaf name {
+ type string;
+ }
+ }
+
+ leaf-list unordered-leaf-list {
+ type string;
+ }
+
+ leaf-list ordered-leaf-list {
+ ordered-by user;
+ type string;
+ }
+
+ container outer-container {
+ }
+
+ anyxml any-xml-data;
+ }
+
+ augment /norm-test:test/norm-test:outer-container {
+
+ leaf augmented-leaf {
+ type string;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-parent</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>sal-common-util</artifactId>
+
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-common-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>concepts</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-common</artifactId>
+ </dependency>
+
+ </dependencies>
+ <scm>
+ <connection>scm:git:http://git.opendaylight.org/gerrit/controller.git</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+ <tag>HEAD</tag>
+ <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
+ </scm>
+</project>
--- /dev/null
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.common.util.jmx;
+
+import java.lang.management.ManagementFactory;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.management.InstanceNotFoundException;
+import javax.management.MBeanRegistrationException;
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Abstract base for an MXBean implementation class.
+ * <p>
+ * This class is not intended for use outside of MD-SAL and its part of private
+ * implementation (still exported as public to be reused across MD-SAL implementation
+ * components) and may be removed in subsequent
+ * releases.
+ *
+ * @author Thomas Pantelis
+ */
+@Beta
+public abstract class AbstractMXBean {
+
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractMXBean.class);
+
+ public static String BASE_JMX_PREFIX = "org.opendaylight.controller:";
+
+ private final MBeanServer server = ManagementFactory.getPlatformMBeanServer();
+
+ private final String mBeanName;
+ private final String mBeanType;
+ private final String mBeanCategory;
+
+ /**
+ * Constructor.
+ *
+ * @param mBeanName Used as the <code>name</code> property in the bean's ObjectName.
+ * @param mBeanType Used as the <code>type</code> property in the bean's ObjectName.
+ * @param mBeanCategory Used as the <code>Category</code> property in the bean's ObjectName.
+ */
+ protected AbstractMXBean(@Nonnull String mBeanName, @Nonnull String mBeanType,
+ @Nullable String mBeanCategory) {
+ this.mBeanName = mBeanName;
+ this.mBeanType = mBeanType;
+ this.mBeanCategory = mBeanCategory;
+ }
+
+ private ObjectName getMBeanObjectName() throws MalformedObjectNameException {
+ StringBuilder builder = new StringBuilder(BASE_JMX_PREFIX)
+ .append("type=").append(getMBeanType());
+
+ if(getMBeanCategory() != null) {
+ builder.append(",Category=").append(getMBeanCategory());
+ }
+
+ builder.append(",name=").append(getMBeanName());
+ return new ObjectName(builder.toString());
+ }
+
+ /**
+ * Registers this bean with the platform MBean server with the domain defined by
+ * {@link #BASE_JMX_PREFIX}.
+ *
+ * @return true is successfully registered, false otherwise.
+ */
+ public boolean registerMBean() {
+ boolean registered = false;
+ try {
+ // Object to identify MBean
+ final ObjectName mbeanName = this.getMBeanObjectName();
+
+ LOG.debug("Register MBean {}", mbeanName);
+
+ // unregistered if already registered
+ if(server.isRegistered(mbeanName)) {
+
+ LOG.debug("MBean {} found to be already registered", mbeanName);
+
+ try {
+ unregisterMBean(mbeanName);
+ } catch(Exception e) {
+
+ LOG.warn("unregister mbean {} resulted in exception {} ", mbeanName, e);
+ }
+ }
+ server.registerMBean(this, mbeanName);
+ registered = true;
+
+ LOG.debug("MBean {} registered successfully", mbeanName.getCanonicalName());
+ } catch(Exception e) {
+
+ LOG.error("registration failed {}", e);
+
+ }
+ return registered;
+ }
+
+ /**
+ * Unregisters this bean with the platform MBean server.
+ *
+ * @return true is successfully unregistered, false otherwise.
+ */
+ public boolean unregisterMBean() {
+ boolean unregister = false;
+ try {
+ ObjectName mbeanName = this.getMBeanObjectName();
+ unregisterMBean(mbeanName);
+ unregister = true;
+ } catch(Exception e) {
+
+ LOG.error("Failed when unregistering MBean {}", e);
+ }
+
+ return unregister;
+ }
+
+ private void unregisterMBean(ObjectName mbeanName) throws MBeanRegistrationException,
+ InstanceNotFoundException {
+ server.unregisterMBean(mbeanName);
+ }
+
+ /**
+ * Returns the <code>name</code> property of the bean's ObjectName.
+ */
+ public String getMBeanName() {
+ return mBeanName;
+ }
+
+ /**
+ * Returns the <code>type</code> property of the bean's ObjectName.
+ */
+ public String getMBeanType() {
+ return mBeanType;
+ }
+
+ /**
+ * Returns the <code>Category</code> property of the bean's ObjectName.
+ */
+ public String getMBeanCategory() {
+ return mBeanCategory;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.common.util.jmx;
+
+import java.util.List;
+
+import org.opendaylight.yangtools.util.concurrent.ListenerNotificationQueueStats;
+
+/**
+ * MXBean interface for {@link QueuedNotificationManager} statistic metrics.
+ *
+ * @author Thomas Pantelis
+ */
+public interface QueuedNotificationManagerMXBean {
+
+ /**
+ * Returns a list of stat instances for each current listener notification task in progress.
+ */
+ List<ListenerNotificationQueueStats> getCurrentListenerQueueStats();
+
+ /**
+ * Returns the configured maximum listener queue size.
+ */
+ int getMaxListenerQueueSize();
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.common.util.jmx;
+
+import java.util.List;
+
+import org.opendaylight.yangtools.util.concurrent.ListenerNotificationQueueStats;
+import org.opendaylight.yangtools.util.concurrent.QueuedNotificationManager;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * Implementation of the QueuedNotificationManagerMXBean interface.
+ *
+ * <p>
+ * This class is not intended for use outside of MD-SAL and its part of private
+ * implementation (still exported as public to be reused across MD-SAL implementation
+ * components) and may be removed in subsequent
+ * releases.
+ *
+ * @author Thomas Pantelis
+ */
+public class QueuedNotificationManagerMXBeanImpl extends AbstractMXBean
+ implements QueuedNotificationManagerMXBean {
+
+ private final QueuedNotificationManager<?,?> manager;
+
+ public QueuedNotificationManagerMXBeanImpl( QueuedNotificationManager<?,?> manager,
+ String mBeanName, String mBeanType, String mBeanCategory ) {
+ super(mBeanName, mBeanType, mBeanCategory);
+ this.manager = Preconditions.checkNotNull( manager );
+ }
+
+ @Override
+ public List<ListenerNotificationQueueStats> getCurrentListenerQueueStats() {
+ return manager.getListenerNotificationQueueStats();
+ }
+
+ @Override
+ public int getMaxListenerQueueSize() {
+ return manager.getMaxQueueCapacity();
+ }
+
+ public QueuedNotificationManagerStats toQueuedNotificationManagerStats() {
+ return new QueuedNotificationManagerStats( getMaxListenerQueueSize(),
+ getCurrentListenerQueueStats() );
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.common.util.jmx;
+
+import java.beans.ConstructorProperties;
+import java.util.List;
+
+import org.opendaylight.yangtools.util.concurrent.ListenerNotificationQueueStats;
+
+/**
+ * A bean class that holds various QueuedNotificationManager statistic metrics. This class is
+ * suitable for mapping to the MXBean CompositeDataSupport type.
+ *
+ * <p>
+ * This class is not intended for use outside of MD-SAL and its part of private
+ * implementation (still exported as public to be reused across MD-SAL implementation
+ * components) and may be removed in subsequent
+ * releases.
+ * @author Thomas Pantelis
+ * @see QueuedNotificationManagerMXBeanImpl
+ */
+public class QueuedNotificationManagerStats {
+
+ private final int maxListenerQueueSize;
+ private final List<ListenerNotificationQueueStats> currentListenerQueueStats;
+
+ @ConstructorProperties({"maxListenerQueueSize","currentListenerQueueStats"})
+ public QueuedNotificationManagerStats( int maxListenerQueueSize,
+ List<ListenerNotificationQueueStats> currentListenerQueueStats ) {
+ super();
+ this.maxListenerQueueSize = maxListenerQueueSize;
+ this.currentListenerQueueStats = currentListenerQueueStats;
+ }
+
+ public List<ListenerNotificationQueueStats> getCurrentListenerQueueStats() {
+ return currentListenerQueueStats;
+ }
+
+ public int getMaxListenerQueueSize() {
+ return maxListenerQueueSize;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.common.util.jmx;
+
+import java.beans.ConstructorProperties;
+
+/**
+ * A bean class that holds various thread executor statistic metrics. This class is suitable for
+ * mapping to the MXBean CompositeDataSupport type;
+ *
+ * @author Thomas Pantelis
+ * @see ThreadExecutorStatsMXBeanImpl
+ */
+public class ThreadExecutorStats {
+
+ private final long activeThreadCount;
+ private final long completedTaskCount;
+ private final long currentQueueSize;
+ private final long maxThreadPoolSize;
+ private final long totalTaskCount;
+ private final long largestThreadPoolSize;
+ private final long maxQueueSize;
+ private final long currentThreadPoolSize;
+
+ // The following fields are defined as Long because they may be null if we can't a value
+ // from the underlying executor.
+ private final Long largestQueueSize;
+ private final Long rejectedTaskCount;
+
+ @ConstructorProperties({"activeThreadCount","currentThreadPoolSize","largestThreadPoolSize",
+ "maxThreadPoolSize","currentQueueSize","largestQueueSize","maxQueueSize",
+ "completedTaskCount","totalTaskCount","rejectedTaskCount"})
+ public ThreadExecutorStats(long activeThreadCount, long currentThreadPoolSize,
+ long largestThreadPoolSize, long maxThreadPoolSize, long currentQueueSize,
+ Long largestQueueSize, long maxQueueSize, long completedTaskCount,
+ long totalTaskCount, Long rejectedTaskCount) {
+ this.activeThreadCount = activeThreadCount;
+ this.currentThreadPoolSize = currentThreadPoolSize;
+ this.largestQueueSize = largestQueueSize;
+ this.largestThreadPoolSize = largestThreadPoolSize;
+ this.maxThreadPoolSize = maxThreadPoolSize;
+ this.currentQueueSize = currentQueueSize;
+ this.maxQueueSize = maxQueueSize;
+ this.completedTaskCount = completedTaskCount;
+ this.totalTaskCount = totalTaskCount;
+ this.rejectedTaskCount = rejectedTaskCount;
+ }
+
+ public long getActiveThreadCount() {
+ return activeThreadCount;
+ }
+
+ public long getCompletedTaskCount() {
+ return completedTaskCount;
+ }
+
+ public Long getRejectedTaskCount() {
+ return rejectedTaskCount;
+ }
+
+ public long getCurrentQueueSize() {
+ return currentQueueSize;
+ }
+
+ public Long getLargestQueueSize() {
+ return largestQueueSize;
+ }
+
+ public long getMaxThreadPoolSize() {
+ return maxThreadPoolSize;
+ }
+
+ public long getTotalTaskCount() {
+ return totalTaskCount;
+ }
+
+ public long getLargestThreadPoolSize() {
+ return largestThreadPoolSize;
+ }
+
+ public long getMaxQueueSize() {
+ return maxQueueSize;
+ }
+
+ public long getCurrentThreadPoolSize() {
+ return currentThreadPoolSize;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.common.util.jmx;
+
+/**
+ * MXBean interface for thread executor statistic metrics.
+ *
+ * @author Thomas Pantelis
+ */
+public interface ThreadExecutorStatsMXBean {
+
+ /**
+ * Returns the current thread pool size.
+ */
+ long getCurrentThreadPoolSize();
+
+ /**
+ * Returns the largest thread pool size.
+ */
+ long getLargestThreadPoolSize();
+
+ /**
+ * Returns the maximum thread pool size.
+ */
+ long getMaxThreadPoolSize();
+
+ /**
+ * Returns the current queue size.
+ */
+ long getCurrentQueueSize();
+
+ /**
+ * Returns the largest queue size, if available.
+ */
+ Long getLargestQueueSize();
+
+ /**
+ * Returns the maximum queue size.
+ */
+ long getMaxQueueSize();
+
+ /**
+ * Returns the active thread count.
+ */
+ long getActiveThreadCount();
+
+ /**
+ * Returns the completed task count.
+ */
+ long getCompletedTaskCount();
+
+ /**
+ * Returns the total task count.
+ */
+ long getTotalTaskCount();
+
+ /**
+ * Returns the rejected task count, if available.
+ */
+ Long getRejectedTaskCount();
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.common.util.jmx;
+
+import com.google.common.base.Preconditions;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.ThreadPoolExecutor;
+import javax.annotation.Nullable;
+import org.opendaylight.yangtools.util.concurrent.CountingRejectedExecutionHandler;
+import org.opendaylight.yangtools.util.concurrent.TrackingLinkedBlockingQueue;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * MXBean implementation of the ThreadExecutorStatsMXBean interface that retrieves statistics
+ * from a backing {@link java.util.concurrent.ExecutorService}.
+ *
+ * @author Thomas Pantelis
+ */
+public class ThreadExecutorStatsMXBeanImpl extends AbstractMXBean
+ implements ThreadExecutorStatsMXBean {
+ private static final Logger LOG = LoggerFactory.getLogger(ThreadExecutorStatsMXBeanImpl.class);
+ private final ThreadPoolExecutor executor;
+
+ /**
+ * Constructs an instance for the given {@link Executor}.
+ *
+ * @param executor the backing {@link Executor}
+ * @param mBeanName Used as the <code>name</code> property in the bean's ObjectName.
+ * @param mBeanType Used as the <code>type</code> property in the bean's ObjectName.
+ * @param mBeanCategory Used as the <code>Category</code> property in the bean's ObjectName.
+ */
+ public ThreadExecutorStatsMXBeanImpl(final ThreadPoolExecutor executor, final String mBeanName,
+ final String mBeanType, @Nullable final String mBeanCategory) {
+ super(mBeanName, mBeanType, mBeanCategory);
+ this.executor = Preconditions.checkNotNull(executor);
+ }
+
+ private static ThreadExecutorStatsMXBeanImpl createInternal(final Executor executor,
+ final String mBeanName, final String mBeanType, final String mBeanCategory) {
+ if (executor instanceof ThreadPoolExecutor) {
+ final ThreadExecutorStatsMXBeanImpl ret = new ThreadExecutorStatsMXBeanImpl(
+ (ThreadPoolExecutor) executor, mBeanName, mBeanType, mBeanCategory);
+ return ret;
+ }
+
+ LOG.info("Executor {} is not supported", executor);
+ return null;
+ }
+
+ /**
+ * Creates a new bean if the backing executor is a ThreadPoolExecutor and registers it.
+ *
+ * @param executor the backing {@link Executor}
+ * @param mBeanName Used as the <code>name</code> property in the bean's ObjectName.
+ * @param mBeanType Used as the <code>type</code> property in the bean's ObjectName.
+ * @param mBeanCategory Used as the <code>Category</code> property in the bean's ObjectName.
+ * @return a registered ThreadExecutorStatsMXBeanImpl instance if the backing executor
+ * is a ThreadPoolExecutor, otherwise null.
+ */
+ public static ThreadExecutorStatsMXBeanImpl create(final Executor executor, final String mBeanName,
+ final String mBeanType, @Nullable final String mBeanCategory) {
+ ThreadExecutorStatsMXBeanImpl ret = createInternal(executor, mBeanName, mBeanType, mBeanCategory);
+ if(ret != null) {
+ ret.registerMBean();
+ }
+
+ return ret;
+ }
+
+ /**
+ * Creates a new bean if the backing executor is a ThreadPoolExecutor.
+ *
+ * @param executor the backing {@link Executor}
+ * @return a ThreadExecutorStatsMXBeanImpl instance if the backing executor
+ * is a ThreadPoolExecutor, otherwise null.
+ */
+ public static ThreadExecutorStatsMXBeanImpl create(final Executor executor) {
+ return createInternal(executor, "", "", null);
+ }
+
+ @Override
+ public long getCurrentThreadPoolSize() {
+ return executor.getPoolSize();
+ }
+
+ @Override
+ public long getLargestThreadPoolSize() {
+ return executor.getLargestPoolSize();
+ }
+
+ @Override
+ public long getMaxThreadPoolSize() {
+ return executor.getMaximumPoolSize();
+ }
+
+ @Override
+ public long getCurrentQueueSize() {
+ return executor.getQueue().size();
+ }
+
+ @Override
+ public Long getLargestQueueSize() {
+ BlockingQueue<Runnable> queue = executor.getQueue();
+ if(queue instanceof TrackingLinkedBlockingQueue) {
+ return Long.valueOf(((TrackingLinkedBlockingQueue<?>)queue).getLargestQueueSize());
+ }
+
+ return null;
+ }
+
+ @Override
+ public long getMaxQueueSize() {
+ long queueSize = executor.getQueue().size();
+ return executor.getQueue().remainingCapacity() + queueSize;
+ }
+
+ @Override
+ public long getActiveThreadCount() {
+ return executor.getActiveCount();
+ }
+
+ @Override
+ public long getCompletedTaskCount() {
+ return executor.getCompletedTaskCount();
+ }
+
+ @Override
+ public long getTotalTaskCount() {
+ return executor.getTaskCount();
+ }
+
+ @Override
+ public Long getRejectedTaskCount() {
+ RejectedExecutionHandler rejectedHandler = executor.getRejectedExecutionHandler();
+ if(rejectedHandler instanceof CountingRejectedExecutionHandler) {
+ return Long.valueOf(((CountingRejectedExecutionHandler)rejectedHandler)
+ .getRejectedTaskCount());
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns a {@link ThreadExecutorStats} instance containing a snapshot of the statistic
+ * metrics.
+ */
+ public ThreadExecutorStats toThreadExecutorStats() {
+ return new ThreadExecutorStats(getActiveThreadCount(), getCurrentThreadPoolSize(),
+ getLargestThreadPoolSize(), getMaxThreadPoolSize(), getCurrentQueueSize(),
+ getLargestQueueSize(), getMaxQueueSize(), getCompletedTaskCount(),
+ getTotalTaskCount(), getRejectedTaskCount());
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.common.util;
+
+public final class Arguments {
+
+ private Arguments() {
+ throw new UnsupportedOperationException("Utility class");
+ }
+
+ /**
+ * Checks if value is instance of provided class
+ *
+ *
+ * @param value Value to check
+ * @param type Type to check
+ * @return Reference which was checked
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> T checkInstanceOf(Object value, Class<T> type) {
+ if(!type.isInstance(value)) {
+ throw new IllegalArgumentException(String.format("Value %s is not of type %s", value, type));
+ }
+ return (T) value;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.common.util;
+
+import org.opendaylight.controller.md.sal.common.api.data.DataModification;
+import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction;
+import org.opendaylight.yangtools.concepts.Path;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+
+/**
+ * @deprecated Utility class for {@link DataCommitTransaction}, will be removed at the same time.
+ */
+@Deprecated
+public class CommitHandlerTransactions {
+
+ private static class AllwaysSuccessfulTransaction<P extends Path<P>,D> implements DataCommitTransaction<P, D> {
+
+ private final DataModification<P, D> modification;
+
+ public AllwaysSuccessfulTransaction(DataModification<P, D> modification) {
+ this.modification = modification;
+ }
+ @Override
+ public RpcResult<Void> rollback() throws IllegalStateException {
+ return RpcResultBuilder.<Void>success().build();
+ }
+ @Override
+ public RpcResult<Void> finish() throws IllegalStateException {
+ return RpcResultBuilder.<Void>success().build();
+ }
+
+ @Override
+ public DataModification<P, D> getModification() {
+ return modification;
+ }
+ }
+
+ public static final <P extends Path<P>,D> AllwaysSuccessfulTransaction<P, D> allwaysSuccessfulTransaction(DataModification<P, D> modification) {
+ return new AllwaysSuccessfulTransaction<>(modification);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.common.util;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * @deprecated Use {@link com.google.common.util.concurrent.Futures} instead.
+ */
+@Deprecated
+public class Futures {
+
+ private Futures() {
+ }
+
+ public static <T> Future<T> immediateFuture(T result) {
+ return new ImmediateFuture<T>(result);
+ }
+
+ private static class ImmediateFuture<T> implements Future<T> {
+
+ private final T result;
+
+ public ImmediateFuture(T result) {
+ this.result = result;
+ }
+
+ @Override
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ return false;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return false;
+ }
+
+ @Override
+ public boolean isDone() {
+ return true;
+ }
+
+ @Override
+ public T get() throws InterruptedException, ExecutionException {
+ return result;
+ }
+
+ @Override
+ public T get(long timeout, TimeUnit unit) throws InterruptedException,
+ ExecutionException, TimeoutException {
+ return result;
+ }
+
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.common.util;
+
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity;
+import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
+
+/**
+ * @deprecated Use {@link org.opendaylight.yangtools.yang.common.RpcResultBuilder}
+ */
+@Deprecated
+public class RpcErrors {
+
+ /**
+ * @param applicationTag
+ * @param tag
+ * @param info
+ * @param severity
+ * @param message
+ * @param errorType
+ * @param cause
+ * @return {@link RpcError} implementation
+ */
+ public static RpcError getRpcError(String applicationTag, String tag, String info,
+ ErrorSeverity severity, String message, ErrorType errorType, Throwable cause) {
+ RpcErrorTO ret = new RpcErrorTO(applicationTag, tag, info, severity, message,
+ errorType, cause);
+ return ret;
+ }
+
+ private static class RpcErrorTO implements RpcError {
+
+ private final String applicationTag;
+ private final String tag;
+ private final String info;
+ private final ErrorSeverity severity;
+ private final String message;
+ private final ErrorType errorType;
+ private final Throwable cause;
+
+ /**
+ * @param applicationTag
+ * @param tag
+ * @param info
+ * @param severity
+ * @param message
+ * @param errorType
+ * @param cause
+ */
+ protected RpcErrorTO(String applicationTag, String tag, String info,
+ ErrorSeverity severity, String message, ErrorType errorType, Throwable cause) {
+ super();
+ this.applicationTag = applicationTag;
+ this.tag = tag;
+ this.info = info;
+ this.severity = severity;
+ this.message = message;
+ this.errorType = errorType;
+ this.cause = cause;
+ }
+
+ @Override
+ public String getApplicationTag() {
+ return applicationTag;
+ }
+
+ @Override
+ public String getInfo() {
+ return info;
+ }
+
+ @Override
+ public String getMessage() {
+ return message;
+ }
+
+ @Override
+ public ErrorSeverity getSeverity() {
+ return severity;
+ }
+
+ @Override
+ public String getTag() {
+ return tag;
+ }
+
+ @Override
+ public Throwable getCause() {
+ return cause;
+ }
+
+ @Override
+ public ErrorType getErrorType() {
+ return errorType;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.common.util;
+
+import java.io.Serializable;
+import java.util.Collection;
+
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * @deprecated Use {@link org.opendaylight.yangtools.yang.common.RpcResultBuilder}
+ */
+@Deprecated
+public class Rpcs {
+
+ public static <T> RpcResult<T> getRpcResult(boolean successful) {
+ RpcResult<T> ret = new RpcResultTO<T>(successful, null, ImmutableList.<RpcError>of());
+ return ret;
+ }
+
+ public static <T> RpcResult<T> getRpcResult(boolean successful, T result,
+ Collection<RpcError> errors) {
+ RpcResult<T> ret = new RpcResultTO<T>(successful, result, errors);
+ return ret;
+ }
+
+ public static <T> RpcResult<T> getRpcResult(boolean successful, Collection<RpcError> errors) {
+ return new RpcResultTO<T>(successful, null, errors);
+ }
+
+ private static class RpcResultTO<T> implements RpcResult<T>, Serializable, Immutable {
+ private static final long serialVersionUID = 1L;
+ private final Collection<RpcError> errors;
+ private final T result;
+ private final boolean successful;
+
+ public RpcResultTO(boolean successful, T result,
+ Collection<RpcError> errors) {
+ this.successful = successful;
+ this.result = result;
+ this.errors = ImmutableList.copyOf(errors);
+ }
+
+ @Override
+ public boolean isSuccessful() {
+ return successful;
+ }
+
+ @Override
+ public T getResult() {
+ return result;
+ }
+
+ @Override
+ public Collection<RpcError> getErrors() {
+ return errors;
+ }
+
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-parent</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>sal-core-api</artifactId>
+ <packaging>bundle</packaging>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-common-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-model-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-test-model</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-impl</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ </plugin>
+ </plugins>
+ </build>
+ <scm>
+ <connection>scm:git:http://git.opendaylight.org/gerrit/controller.git</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+ <tag>HEAD</tag>
+ <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
+ </scm>
+</project>
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.api;
+
+/**
+ * <p>ClusteredDOMDataChangeListener is a marker interface to enable data change notifications on all instances in a cluster,
+ * where this listener is registered.</p>
+ *
+ *
+ * <p>Applications should implement ClusteredDOMDataChangeListener instead of DOMDataChangeListener, if they want to listen
+ * to data change notifications on any node of clustered datastore. DOMDataChangeListener enables data change notifications
+ * only at leader of the datastore shard.</p>
+ *
+ */
+
+public interface ClusteredDOMDataChangeListener extends DOMDataChangeListener{
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainFactory;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
+import org.opendaylight.controller.sal.core.api.BrokerService;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * Data Broker which provides data transaction and data change listener functionality
+ * using {@link NormalizedNode} data format.
+ *
+ * This interface is type capture of generic interfaces and returns type captures
+ * of results for client-code convenience.
+ *
+ */
+public interface DOMDataBroker extends
+ AsyncDataBroker<YangInstanceIdentifier, NormalizedNode<?, ?>, DOMDataChangeListener>,
+ TransactionChainFactory<YangInstanceIdentifier, NormalizedNode<?, ?>>, BrokerService, DOMExtensibleService<DOMDataBroker, DOMDataBrokerExtension> {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ DOMDataReadOnlyTransaction newReadOnlyTransaction();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ DOMDataReadWriteTransaction newReadWriteTransaction();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ DOMDataWriteTransaction newWriteOnlyTransaction();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ DOMTransactionChain createTransactionChain(TransactionChainListener listener);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+/**
+ * Type capture of a {@link DOMServiceExtension} applicable to {@link DOMDataBroker}
+ * implementations.
+ */
+public interface DOMDataBrokerExtension extends DOMServiceExtension<DOMDataBroker, DOMDataBrokerExtension> {
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * DOMDataChangeListener enables data change notifications only at leader of the datastore shard.
+ */
+
+public interface DOMDataChangeListener extends AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>> {
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncReadOnlyTransaction;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface DOMDataReadOnlyTransaction extends DOMDataReadTransaction, AsyncReadOnlyTransaction<YangInstanceIdentifier, NormalizedNode<?, ?>> {
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncReadTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+
+/**
+ * A transaction that provides read access to a logical data store.
+ * <p>
+ * For more information on usage and examples, please see the documentation in {@link AsyncReadTransaction}.
+ */
+public interface DOMDataReadTransaction extends AsyncReadTransaction<YangInstanceIdentifier, NormalizedNode<?, ?>> {
+
+ /**
+ * Reads data from provided logical data store located at the provided path.
+ *<p>
+ * If the target is a subtree, then the whole subtree is read (and will be
+ * accessible from the returned data object).
+ *
+ * @param store
+ * Logical data store from which read should occur.
+ * @param path
+ * Path which uniquely identifies subtree which client want to
+ * read
+ * @return a CheckFuture containing the result of the read. The Future blocks until the
+ * commit operation is complete. Once complete:
+ * <ul>
+ * <li>If the data at the supplied path exists, the Future returns an Optional object
+ * containing the data.</li>
+ * <li>If the data at the supplied path does not exist, the Future returns
+ * Optional#absent().</li>
+ * <li>If the read of the data fails, the Future will fail with a
+ * {@link ReadFailedException} or an exception derived from ReadFailedException.</li>
+ * </ul>
+ */
+ CheckedFuture<Optional<NormalizedNode<?,?>>, ReadFailedException> read(
+ LogicalDatastoreType store, YangInstanceIdentifier path);
+
+ /**
+ /**
+ * Checks if data is available in the logical data store located at provided path.
+ * <p>
+ *
+ * Note: a successful result from this method makes no guarantee that a subsequent call to {@link #read}
+ * will succeed. It is possible that the data resides in a data store on a remote node and, if that
+ * node goes down or a network failure occurs, a subsequent read would fail. Another scenario is if
+ * the data is deleted in between the calls to <code>exists</code> and <code>read</code>
+ *
+ * @param store
+ * Logical data store from which read should occur.
+ * @param path
+ * Path which uniquely identifies subtree which client want to
+ * check existence of
+ * @return a CheckFuture containing the result of the check.
+ * <ul>
+ * <li>If the data at the supplied path exists, the Future returns a Boolean
+ * whose value is true, false otherwise</li>
+ * <li>If checking for the data fails, the Future will fail with a
+ * {@link ReadFailedException} or an exception derived from ReadFailedException.</li>
+ * </ul>
+ */
+ CheckedFuture<Boolean, ReadFailedException> exists(
+ LogicalDatastoreType store, YangInstanceIdentifier path);
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncReadWriteTransaction;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface DOMDataReadWriteTransaction extends DOMDataReadTransaction, DOMDataWriteTransaction, AsyncReadWriteTransaction<YangInstanceIdentifier, NormalizedNode<?, ?>> {
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import java.util.Collection;
+import java.util.EventListener;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+
+/**
+ * Interface implemented by classes interested in receiving notifications about
+ * data tree changes. This interface differs from {@link DOMDataChangeListener}
+ * in that it provides a cursor-based view of the change, which has potentially
+ * lower overhead.
+ */
+public interface DOMDataTreeChangeListener extends EventListener {
+ /**
+ * Invoked when there was data change for the supplied path, which was used
+ * to register this listener.
+ *
+ * <p>
+ * This method may be also invoked during registration of the listener if
+ * there is any pre-existing data in the conceptual data tree for supplied
+ * path. This initial event will contain all pre-existing data as created.
+ *
+ * <p>
+ * A data change event may be triggered spuriously, e.g. such that data before
+ * and after compare as equal. Implementations of this interface are expected
+ * to recover from such events. Event producers are expected to exert reasonable
+ * effort to suppress such events.
+ *
+ * In other words, it is completely acceptable to observe
+ * a {@link org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode},
+ * which reports a {@link org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType}
+ * other than UNMODIFIED, while the before- and after- data items compare as
+ * equal.
+ *
+ * @param changes Collection of change events, may not be null or empty.
+ */
+ void onDataTreeChanged(@Nonnull Collection<DataTreeCandidate> changes);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+
+/**
+ * A {@link DOMServiceExtension} which allows users to register for changes to a
+ * subtree.
+ */
+public interface DOMDataTreeChangeService extends DOMDataBrokerExtension {
+ /**
+ * Registers a {@link DOMDataTreeChangeListener} to receive
+ * notifications when data changes under a given path in the conceptual data
+ * tree.
+ * <p>
+ * You are able to register for notifications for any node or subtree
+ * which can be represented using {@link DOMDataTreeIdentifier}.
+ * <p>
+ *
+ * You are able to register for data change notifications for a subtree or leaf
+ * even if it does not exist. You will receive notification once that node is
+ * created.
+ * <p>
+ * If there is any pre-existing data in the data tree for the path for which you are
+ * registering, you will receive an initial data change event, which will
+ * contain all pre-existing data, marked as created.
+ *
+ * <p>
+ * This method returns a {@link ListenerRegistration} object. To
+ * "unregister" your listener for changes call the {@link ListenerRegistration#close()}
+ * method on the returned object.
+ * <p>
+ * You MUST explicitly unregister your listener when you no longer want to receive
+ * notifications. This is especially true in OSGi environments, where failure to
+ * do so during bundle shutdown can lead to stale listeners being still registered.
+ *
+ * @param treeId
+ * Data tree identifier of the subtree which should be watched for
+ * changes.
+ * @param listener
+ * Listener instance which is being registered
+ * @return Listener registration object, which may be used to unregister
+ * your listener using {@link ListenerRegistration#close()} to stop
+ * delivery of change events.
+ */
+ @Nonnull <L extends DOMDataTreeChangeListener> ListenerRegistration<L> registerDataTreeChangeListener(@Nonnull DOMDataTreeIdentifier treeId, @Nonnull L listener);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.api;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Preconditions;
+import java.io.Serializable;
+import java.util.Iterator;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.concepts.Path;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+/**
+ * A unique identifier for a particular subtree. It is composed of the logical
+ * data store type and the instance identifier of the root node.
+ */
+public final class DOMDataTreeIdentifier implements Immutable, Path<DOMDataTreeIdentifier>, Serializable, Comparable<DOMDataTreeIdentifier> {
+ private static final long serialVersionUID = 1L;
+ private final YangInstanceIdentifier rootIdentifier;
+ private final LogicalDatastoreType datastoreType;
+
+ public DOMDataTreeIdentifier(final LogicalDatastoreType datastoreType, final YangInstanceIdentifier rootIdentifier) {
+ this.datastoreType = Preconditions.checkNotNull(datastoreType);
+ this.rootIdentifier = Preconditions.checkNotNull(rootIdentifier);
+ }
+
+ /**
+ * Return the logical data store type.
+ *
+ * @return Logical data store type. Guaranteed to be non-null.
+ */
+ public @Nonnull LogicalDatastoreType getDatastoreType() {
+ return datastoreType;
+ }
+
+ /**
+ * Return the {@link YangInstanceIdentifier} of the root node.
+ *
+ * @return Instance identifier corresponding to the root node.
+ */
+ public @Nonnull YangInstanceIdentifier getRootIdentifier() {
+ return rootIdentifier;
+ }
+
+ @Override
+ public boolean contains(final DOMDataTreeIdentifier other) {
+ return datastoreType == other.datastoreType && rootIdentifier.contains(other.rootIdentifier);
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + datastoreType.hashCode();
+ result = prime * result + rootIdentifier.hashCode();
+ return result;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof DOMDataTreeIdentifier)) {
+ return false;
+ }
+ DOMDataTreeIdentifier other = (DOMDataTreeIdentifier) obj;
+ if (datastoreType != other.datastoreType) {
+ return false;
+ }
+ return rootIdentifier.equals(other.rootIdentifier);
+ }
+
+ @Override
+ public int compareTo(final DOMDataTreeIdentifier o) {
+ int i = datastoreType.compareTo(o.datastoreType);
+ if (i != 0) {
+ return i;
+ }
+
+ final Iterator<PathArgument> mi = rootIdentifier.getPathArguments().iterator();
+ final Iterator<PathArgument> oi = o.rootIdentifier.getPathArguments().iterator();
+
+ while (mi.hasNext()) {
+ if (!oi.hasNext()) {
+ return 1;
+ }
+
+ final PathArgument ma = mi.next();
+ final PathArgument oa = oi.next();
+ i = ma.compareTo(oa);
+ if (i != 0) {
+ return i;
+ }
+ }
+
+ return oi.hasNext() ? -1 : 0;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this).add("datastore", datastoreType).add("root", rootIdentifier).toString();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import com.google.common.base.Preconditions;
+
+
+/**
+ * Failure reported when a data tree is no longer accessible.
+ */
+public class DOMDataTreeInaccessibleException extends DOMDataTreeListeningException {
+ private static final long serialVersionUID = 1L;
+ private final DOMDataTreeIdentifier treeIdentifier;
+
+ public DOMDataTreeInaccessibleException(final DOMDataTreeIdentifier treeIdentifier, final String message) {
+ super(message);
+ this.treeIdentifier = Preconditions.checkNotNull(treeIdentifier);
+ }
+
+ public DOMDataTreeInaccessibleException(final DOMDataTreeIdentifier treeIdentifier, final String message, final Throwable cause) {
+ super(message);
+ this.treeIdentifier = Preconditions.checkNotNull(treeIdentifier);
+ }
+
+ public final DOMDataTreeIdentifier getTreeIdentifier() {
+ return treeIdentifier;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import java.util.Collection;
+import java.util.EventListener;
+import java.util.Map;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+
+/**
+ * Interface implemented by data consumers, e.g. processes wanting to act on data
+ * after it has been introduced to the conceptual data tree.
+ */
+public interface DOMDataTreeListener extends EventListener {
+ /**
+ * Invoked whenever one or more registered subtrees change. The logical changes are reported,
+ * as well as the roll up of new state for all subscribed subtrees.
+ *
+ * @param changes The set of changes being reported. Each subscribed subtree may be present
+ * at most once.
+ * @param subtrees Per-subtree state as visible after the reported changes have been applied.
+ * This includes all the subtrees this listener is subscribed to, even those
+ * which have not changed.
+ */
+ void onDataTreeChanged(@Nonnull Collection<DataTreeCandidate> changes, @Nonnull Map<DOMDataTreeIdentifier, NormalizedNode<?, ?>> subtrees);
+
+ /**
+ * Invoked when a subtree listening failure occurs. This can be triggered, for example, when
+ * a connection to external subtree source is broken. The listener will not receive any other
+ * callbacks, but its registration still needs to be closed to prevent resource leak.
+ *
+ * @param cause Collection of failure causes, may not be null or empty.
+ */
+ void onDataTreeFailed(@Nonnull Collection<DOMDataTreeListeningException> causes);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+/**
+ * Base exception for various causes why and {@link DOMDataTreeListener}
+ * may be terminated by the {@link DOMDataTreeService} implementation.
+ */
+public class DOMDataTreeListeningException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public DOMDataTreeListeningException(final String message) {
+ super(message);
+ }
+
+ public DOMDataTreeListeningException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Exception thrown when a loop is detected in the way {@link DOMDataTreeListener}
+ * and {@link DOMDataTreeProducer} instances would be connected.
+ */
+public class DOMDataTreeLoopException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public DOMDataTreeLoopException(final @Nonnull String message) {
+ super(message);
+ }
+
+ public DOMDataTreeLoopException(final @Nonnull String message, final @Nonnull Throwable cause) {
+ super(message, cause);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import java.util.Collection;
+import javax.annotation.Nonnull;
+
+/**
+ * A data producer context. It allows transactions to be submitted to the subtrees
+ * specified at instantiation time. At any given time there may be a single transaction
+ * open. It needs to be either submitted or cancelled before another one can be open.
+ * Once a transaction is submitted, it will proceed to be committed asynchronously.
+ *
+ * Each instance has an upper bound on the number of transactions which can be in-flight,
+ * once that capacity is exceeded, an attempt to create a new transaction will block
+ * until some transactions complete.
+ *
+ * Each {@link DOMDataTreeProducer} can be in two logical states, bound and unbound,
+ * which define the lifecycle rules for when is it legal to create and submit transactions
+ * in relationship with {@link DOMDataTreeListener} callbacks.
+ *
+ * When a producer is first created, it is unbound. In this state the producer can be
+ * accessed by any application thread to allocate or submit transactions, as long as
+ * the 'single open transaction' rule is maintained. The producer and any transaction
+ * object MUST NOT be accessed, directly or indirectly, from a {@link DOMDataTreeListener}
+ * callback.
+ *
+ * When a producer is referenced in a call to {@link DOMDataTreeService#registerListener(DOMDataTreeListener, java.util.Collection, boolean, java.util.Collection)},
+ * an attempt will be made to bind the producer to the specified {@link DOMDataTreeListener}.
+ * Such an attempt will fail the producer is already bound, or it has an open transaction.
+ * Once bound, the producer can only be accessed from within the {@link DOMDataTreeListener}
+ * callback on that particular instance. Any transaction which is not submitted by the
+ * time the callback returns will be implicitly cancelled. A producer becomes unbound
+ * when the listener it is bound to becomes unregistered.
+ */
+public interface DOMDataTreeProducer extends DOMDataTreeProducerFactory, AutoCloseable {
+ /**
+ * Allocate a new open transaction on this producer. Any and all transactions
+ * previously allocated must have been either submitted or cancelled by the
+ * time this method is invoked.
+ *
+ * @param barrier Indicates whether this transaction should be a barrier. A barrier
+ * transaction is processed separately from any preceding transactions.
+ * Non-barrier transactions may be merged and processed in a batch,
+ * such that any observers see the modifications contained in them as
+ * if the modifications were made in a single transaction.
+ * @return A new {@link DOMDataWriteTransaction}
+ * @throws {@link IllegalStateException} if a previous transaction was not closed.
+ * @throws {@link IllegalThreadStateException} if the calling thread context does not
+ * match the lifecycle rules enforced by the producer state (e.g. bound or unbound).
+ * This exception is thrown on a best effort basis and programs should not rely
+ * on it for correct operation.
+ */
+ @Nonnull DOMDataWriteTransaction createTransaction(boolean isolated);
+
+ /**
+ * {@inheritDoc}
+ *
+ * When invoked on a {@link DOMDataTreeProducer}, this method has additional restrictions.
+ * There may not be an open transaction from this producer. The method needs to be
+ * invoked in appropriate context, e.g. bound or unbound.
+ *
+ * Specified subtrees must be accessible by this producer. Accessible means they are a subset
+ * of the subtrees specified when the producer is instantiated. The set is further reduced as
+ * child producers are instantiated -- if you create a producer for /a and then a child for
+ * /a/b, /a/b is not accessible from the first producer.
+ *
+ * Once this method returns successfully, this (parent) producer loses the ability to
+ * access the specified paths until the resulting (child) producer is shut down.
+ *
+ * @throws {@link IllegalStateException} if there is an open transaction
+ * @throws {@link IllegalArgumentException} if subtrees contains a subtree which is not
+ * accessible by this producer
+ * @throws {@link IllegalThreadStateException} if the calling thread context does not
+ * match the lifecycle rules enforced by the producer state (e.g. bound or unbound).
+ * This exception is thrown on a best effort basis and programs should not rely
+ * on it for correct operation.
+ */
+ @Override
+ @Nonnull DOMDataTreeProducer createProducer(@Nonnull Collection<DOMDataTreeIdentifier> subtrees);
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws DOMDataTreeProducerBusyException when there is an open transaction.
+ */
+ @Override
+ void close() throws DOMDataTreeProducerException;
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+/**
+ * Exception indicating that the {@link DOMDataTreeProducer} has an open user
+ * transaction and cannot be closed.
+ */
+public class DOMDataTreeProducerBusyException extends DOMDataTreeProducerException {
+ private static final long serialVersionUID = 1L;
+
+ public DOMDataTreeProducerBusyException(final String message) {
+ super(message);
+ }
+
+ public DOMDataTreeProducerBusyException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+/**
+ * Base exception for all exceptions related to {@link DOMDataTreeProducer}s.
+ */
+public class DOMDataTreeProducerException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public DOMDataTreeProducerException(final String message) {
+ super(message);
+ }
+
+ public DOMDataTreeProducerException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import java.util.Collection;
+import javax.annotation.Nonnull;
+
+/**
+ * Base source of {@link DOMDataTreeProducer}s. This interface is usually not used directly,
+ * but rather through one of its sub-interfaces.
+ */
+public interface DOMDataTreeProducerFactory {
+ /**
+ * Create a producer, which is able to access to a set of trees.
+ *
+ * @param subtrees The collection of subtrees the resulting producer should have access to.
+ * @return A {@link DOMDataTreeProducer} instance.
+ * @throws {@link IllegalArgumentException} if subtrees is empty.
+ */
+ @Nonnull DOMDataTreeProducer createProducer(@Nonnull Collection<DOMDataTreeIdentifier> subtrees);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import java.util.Collection;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+
+/**
+ * A {@link DOMService} providing access to the conceptual data tree. Interactions
+ * with the data tree are split into data producers and consumers (listeners). Each
+ * of them operate on a set of subtrees, which need to be declared at instantiation time.
+ *
+ * Returned instances are not thread-safe and expected to be used by a single thread
+ * at a time. Furthermore, producers may not be accessed from consumer callbacks
+ * unless they were specified when the listener is registered.
+ *
+ * The service maintains a loop-free topology of producers and consumers. What this means
+ * is that a consumer is not allowed to access a producer, which affects any of the
+ * subtrees it is subscribed to. This restriction is in place to ensure the system does
+ * not go into a feedback loop, where it is impossible to block either a producer or
+ * a consumer without accumulating excess work in the backlog stemming from its previous
+ * activity.
+ */
+public interface DOMDataTreeService extends DOMDataTreeProducerFactory, DOMService {
+ /**
+ * Register a {@link DOMDataTreeListener} instance. Once registered, the listener
+ * will start receiving changes on the selected subtrees. If the listener cannot
+ * keep up with the rate of changes, and allowRxMerges is set to true, this service
+ * is free to merge the changes, so that a smaller number of them will be reported,
+ * possibly hiding some data transitions (like flaps).
+ *
+ * If the listener wants to write into any producer, that producer has to be mentioned
+ * in the call to this method. Those producers will be bound exclusively to the
+ * registration, so that accessing them outside of this listener's callback will trigger
+ * an error. Any producers mentioned must be idle, e.g. they may not have an open
+ * transaction at the time this method is invoked.
+ *
+ * Each listener instance can be registered at most once. Implementations of this
+ * interface have to guarantee that the listener's methods will not be invoked
+ * concurrently from multiple threads.
+ *
+ * @param listener {@link DOMDataTreeListener} that is being registered
+ * @param subtrees Conceptual subtree identifier of subtrees which should be monitored
+ * for changes. May not be null or empty.
+ * @param allowRxMerges True if the backend may perform ingress state compression.
+ * @param producers {@link DOMDataTreeProducer} instances to bind to the listener.
+ * @return A listener registration. Once closed, the listener will no longer be
+ * invoked and the producers will be unbound.
+ * @throws IllegalArgumentException if subtrees is empty or the listener is already bound
+ * @throws DOMDataTreeLoopException if the registration of the listener to the specified
+ * subtrees with specified producers would form a
+ * feedback loop
+ */
+ @Nonnull <T extends DOMDataTreeListener> ListenerRegistration<T> registerListener(@Nonnull T listener,
+ @Nonnull Collection<DOMDataTreeIdentifier> subtrees, boolean allowRxMerges, @Nonnull Collection<DOMDataTreeProducer> producers);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import java.util.EventListener;
+import javax.annotation.Nonnull;
+
+/**
+ * A single shard of the conceptual data tree. This interface defines the basic notifications
+ * a shard can receive. Each shard implementation is expected to also implement some of the
+ * datastore-level APIs. Which interfaces are required depends on the {@link DOMDataTreeShardingService}
+ * implementation.
+ */
+public interface DOMDataTreeShard extends EventListener {
+ /**
+ * Invoked whenever a child is getting attached as a more specific prefix under this shard.
+ *
+ * @param prefix Child's prefix
+ * @param child Child shard
+ */
+ void onChildAttached(@Nonnull DOMDataTreeIdentifier prefix, @Nonnull DOMDataTreeShard child);
+
+ /**
+ * Invoked whenever a child is getting detached as a more specific prefix under this shard.
+ *
+ * @param prefix Child's prefix
+ * @param child Child shard
+ */
+ void onChildDetached(@Nonnull DOMDataTreeIdentifier prefix, @Nonnull DOMDataTreeShard child);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Exception thrown when an attempt to attach a conflicting shard to the global
+ * table.
+ */
+public class DOMDataTreeShardingConflictException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public DOMDataTreeShardingConflictException(final @Nonnull String message) {
+ super(message);
+ }
+
+ public DOMDataTreeShardingConflictException(final @Nonnull String message, final @Nonnull Throwable cause) {
+ super(message, cause);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+
+/**
+ * A {@link DOMService} providing access to details on how the conceptual data tree
+ * is distributed among providers (also known as shards). Each shard is tied to a
+ * single {@link DOMDataTreeIdentifier}. Based on those data tree identifiers, the
+ * shards are organized in a tree, where there is a logical parent/child relationship.
+ *
+ * It is not allowed to attach two shards to the same data tree identifier, which means
+ * the mapping of each piece of information has an unambiguous home. When accessing
+ * the information, the shard with the longest matching data tree identifier is used,
+ * which is why this interface treats it is a prefix.
+ *
+ * Whenever a parent/child relationship is changed, the parent is notified, so it can
+ * understand that a logical child has been attached.
+ */
+public interface DOMDataTreeShardingService extends DOMService {
+ /**
+ * Register a shard as responsible for a particular subtree prefix.
+ *
+ * @param prefix Data tree identifier, may not be null.
+ * @param shard Responsible shard instance
+ * @return A registration. To remove the shard's binding, close the registration.
+ * @throws DOMDataTreeShardingConflictException if the prefix is already bound
+ */
+ @Nonnull <T extends DOMDataTreeShard> ListenerRegistration<T> registerDataTreeShard(@Nonnull DOMDataTreeIdentifier prefix, @Nonnull T shard) throws DOMDataTreeShardingConflictException;
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncWriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * A transaction that provides mutation capabilities on a data tree.
+ * <p>
+ * For more information on usage and examples, please see the documentation in {@link AsyncWriteTransaction}.
+ */
+public interface DOMDataWriteTransaction extends AsyncWriteTransaction<YangInstanceIdentifier, NormalizedNode<?, ?>> {
+
+ /**
+ * Stores a piece of data at the specified path. This acts as an add / replace
+ * operation, which is to say that whole subtree will be replaced by the specified data.
+ * <p>
+ * For more information on usage and examples, please see the documentation in {@link AsyncWriteTransaction}.
+ * <p>
+ * If you need to make sure that a parent object exists but you do not want modify
+ * its pre-existing state by using put, consider using {@link #merge} instead.
+ *
+ * @param store
+ * the logical data store which should be modified
+ * @param path
+ * the data object path
+ * @param data
+ * the data object to be written to the specified path
+ * @throws IllegalStateException
+ * if the transaction has already been submitted
+ */
+ void put(LogicalDatastoreType store, YangInstanceIdentifier path, NormalizedNode<?, ?> data);
+
+ /**
+ * Merges a piece of data with the existing data at a specified path. Any pre-existing data
+ * which is not explicitly overwritten will be preserved. This means that if you store a container,
+ * its child lists will be merged.
+ * <p>
+ * For more information on usage and examples, please see the documentation in {@link AsyncWriteTransaction}.
+ *<p>
+ * If you require an explicit replace operation, use {@link #put} instead.
+ *
+ * @param store
+ * the logical data store which should be modified
+ * @param path
+ * the data object path
+ * @param data
+ * the data object to be merged to the specified path
+ * @throws IllegalStateException
+ * if the transaction has already been submitted
+ */
+ void merge(LogicalDatastoreType store, YangInstanceIdentifier path, NormalizedNode<?, ?> data);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.api;
+
+import java.util.Date;
+
+/**
+ * Generic event interface
+ */
+public interface DOMEvent {
+
+ /**
+ * Get the time of the event occurrence
+ *
+ * @return the event time
+ */
+ Date getEventTime();
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import com.google.common.annotations.Beta;
+import java.util.Map;
+import javax.annotation.Nonnull;
+
+/**
+ * Marker interface for services which can support {@link DOMServiceExtension}.
+ * Aside for marking these, they also provide runtime query to detect whether
+ * a particular trait is in fact available.
+ *
+ * <T> Base {@link DOMService}
+ * <E> Extension type
+ */
+@Beta
+public interface DOMExtensibleService<T extends DOMExtensibleService<T, E>, E extends DOMServiceExtension<T, E>> extends DOMService {
+ /**
+ * Return a map of currently-supported extensions, along with accessor services
+ * which provide access to the specific functionality bound to this service.
+ *
+ * @return A map of supported functionality.
+ */
+ @Nonnull Map<Class<? extends E>, E> getSupportedExtensions();
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.api;
+
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+import com.google.common.base.Optional;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public interface DOMMountPoint extends Identifiable<YangInstanceIdentifier> {
+
+ <T extends DOMService> Optional<T> getService(Class<T> cls);
+
+ SchemaContext getSchemaContext();
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.api;
+
+import org.opendaylight.controller.sal.core.api.BrokerService;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+import com.google.common.base.Optional;
+
+
+public interface DOMMountPointService extends BrokerService {
+
+ Optional<DOMMountPoint> getMountPoint(YangInstanceIdentifier path);
+
+ DOMMountPointBuilder createMountPoint(YangInstanceIdentifier path);
+
+ ListenerRegistration<MountProvisionListener> registerProvisionListener(MountProvisionListener listener);
+
+ public interface DOMMountPointBuilder {
+
+ <T extends DOMService> DOMMountPointBuilder addService(Class<T> type,T impl);
+
+ DOMMountPointBuilder addInitialSchemaContext(SchemaContext ctx);
+
+ ObjectRegistration<DOMMountPoint> register();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+/**
+ * A single YANG notification.
+ */
+public interface DOMNotification {
+ /**
+ * Return the type of this notification.
+ *
+ * @return Notification type.
+ */
+ @Nonnull SchemaPath getType();
+
+ /**
+ * Return the body of this notification.
+ *
+ * @return Notification body.
+ */
+ @Nonnull ContainerNode getBody();
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import java.util.EventListener;
+import javax.annotation.Nonnull;
+
+/**
+ * Interface implemented by listeners interested in {@link DOMNotification}s.
+ */
+public interface DOMNotificationListener extends EventListener {
+ /**
+ * Invoked whenever a {@link DOMNotification} matching the subscription
+ * criteria is received.
+ *
+ * @param notification Received notification
+ */
+ void onNotification(@Nonnull DOMNotification notification);
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.concurrent.TimeUnit;
+import javax.annotation.Nonnegative;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.sal.core.api.BrokerService;
+
+/**
+ * A {@link DOMService} which allows its user to send {@link DOMNotification}s. It
+ * provides two styles of initiating the notification delivery, similar to
+ * {@link java.util.concurrent.BlockingQueue}:
+ * - a put-style method which waits until the implementation can accept the notification
+ * for delivery, and
+ * - an offer-style method, which attempts to enqueue the notification, but allows
+ * the caller to specify that it should never wait, or put an upper bound on how
+ * long it is going to wait.
+ */
+public interface DOMNotificationPublishService extends DOMService, BrokerService {
+ /**
+ * Well-known value indicating that the implementation is currently not
+ * able to accept a notification.
+ */
+ ListenableFuture<Object> REJECTED = Futures.immediateFailedFuture(new DOMNotificationRejectedException("Unacceptable blocking conditions encountered"));
+
+ /**
+ * Publish a notification. The result of this method is a {@link ListenableFuture}
+ * which will complete once the notification has been delivered to all immediate
+ * registrants. The type of the object resulting from the future is not defined
+ * and implementations may use it to convey additional information related to the
+ * publishing process.
+ *
+ * Abstract subclasses can refine the return type as returning a promise of a
+ * more specific type, e.g.:
+ *
+ * public interface DeliveryStatus { int getListenerCount(); }
+ * ListenableFuture<? extends DeliveryStatus> putNotification(DOMNotification notification);
+ *
+ * Once the Future succeeds, the resulting object can be queried for traits using
+ * instanceof, e.g:
+ *
+ * // Can block when (for example) the implemention's ThreadPool queue is full
+ * Object o = service.putNotification(notif).get();
+ * if (o instanceof DeliveryStatus) {
+ * DeliveryStatus ds = (DeliveryStatus)o;
+ * LOG.debug("Notification was received by {} listeners", ds.getListenerCount(););
+ * }
+ * }
+ *
+ * In case an implementation is running out of resources, it can block the calling
+ * thread until enough resources become available to accept the notification for
+ * processing, or it is interrupted.
+ *
+ * Caution: completion here means that the implementation has completed processing
+ * of the notification. This does not mean that all existing registrants
+ * have seen the notification. Most importantly, the delivery process at
+ * other cluster nodes may have not begun yet.
+ *
+ * @param notification Notification to be published.
+ * @return A listenable future which will report completion when the service
+ * has finished propagating the notification to its immediate registrants.
+ * @throws InterruptedException if interrupted while waiting
+ * @throws NullPointerException if notification is null.
+ */
+ @Nonnull ListenableFuture<? extends Object> putNotification(@Nonnull DOMNotification notification) throws InterruptedException;
+
+ /**
+ * Attempt to publish a notification. The result of this method is a {@link ListenableFuture}
+ * which will complete once the notification has been delivered to all immediate
+ * registrants. The type of the object resulting from the future is not defined
+ * and implementations may use it to convey additional information related to the
+ * publishing process. Unlike {@link #putNotification(DOMNotification)}, this method
+ * is guaranteed not to block if the underlying implementation encounters contention.
+ *
+ * @param notification Notification to be published.
+ * @return A listenable future which will report completion when the service
+ * has finished propagating the notification to its immediate registrants,
+ * or {@value #REJECTED} if resource constraints prevent
+ * the implementation from accepting the notification for delivery.
+ * @throws NullPointerException if notification is null.
+ */
+ @Nonnull ListenableFuture<? extends Object> offerNotification(@Nonnull DOMNotification notification);
+
+ /**
+ * Attempt to publish a notification. The result of this method is a {@link ListenableFuture}
+ * which will complete once the notification has been delivered to all immediate
+ * registrants. The type of the object resulting from the future is not defined
+ * and implementations may use it to convey additional information related to the
+ * publishing process. Unlike {@link #putNotification(DOMNotification)}, this method
+ * is guaranteed to block more than the specified timeout.
+ *
+ * @param notification Notification to be published.
+ * @param timeout how long to wait before giving up, in units of unit
+ * @param unit a TimeUnit determining how to interpret the timeout parameter
+ * @return A listenable future which will report completion when the service
+ * has finished propagating the notification to its immediate registrants,
+ * or {@value #REJECTED} if resource constraints prevent
+ * the implementation from accepting the notification for delivery.
+ * @throws InterruptedException if interrupted while waiting
+ * @throws NullPointerException if notification or unit is null.
+ * @throws IllegalArgumentException if timeout is negative.
+ */
+ @Nonnull ListenableFuture<? extends Object> offerNotification(@Nonnull DOMNotification notification,
+ @Nonnegative long timeout, @Nonnull TimeUnit unit) throws InterruptedException;
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+/**
+ * <p>
+ * This exception indicates that given notification can not be processed by corresponding mechanism.
+ * More info can be provided in message.
+ * <p/>
+ * <p>
+ * Expected use: {@link DOMNotificationPublishService}
+ * </p>
+ */
+public class DOMNotificationRejectedException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public DOMNotificationRejectedException(final String message) {
+ super(message);
+ }
+
+ public DOMNotificationRejectedException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import java.util.Collection;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.sal.core.api.BrokerService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+/**
+ * A {@link DOMService} which allows its users to subscribe to receive
+ * {@link DOMNotification}s.
+ */
+public interface DOMNotificationService extends DOMService, BrokerService {
+ /**
+ * Register a {@link DOMNotificationListener} to receive a set of notifications. As with
+ * other ListenerRegistration-based interfaces, registering an instance multiple times
+ * results in notifications being delivered for each registration.
+ *
+ * @param listener Notification instance to register
+ * @param types Notification types which should be delivered to the listener. Duplicate
+ * entries are processed only once, null entries are ignored.
+ * @return Registration handle. Invoking {@link DOMNotificationListenerRegistration#close()}
+ * will stop the delivery of notifications to the listener
+ * @throws IllegalArgumentException if types is empty or contains an invalid element, such as
+ * null or a SchemaPath which does not represent a valid {@link DOMNotification} type.
+ * @throws NullPointerException if either of the arguments is null
+ */
+ <T extends DOMNotificationListener> ListenerRegistration<T> registerNotificationListener(@Nonnull T listener, @Nonnull Collection<SchemaPath> types);
+
+ /**
+ * Register a {@link DOMNotificationListener} to receive a set of notifications. As with
+ * other ListenerRegistration-based interfaces, registering an instance multiple times
+ * results in notifications being delivered for each registration.
+ *
+ * @param listener Notification instance to register
+ * @param types Notification types which should be delivered to the listener. Duplicate
+ * entries are processed only once, null entries are ignored.
+ * @return Registration handle. Invoking {@link DOMNotificationListenerRegistration#close()}
+ * will stop the delivery of notifications to the listener
+ * @throws IllegalArgumentException if types is empty or contains an invalid element, such as
+ * null or a SchemaPath which does not represent a valid {@link DOMNotification} type.
+ * @throws NullPointerException if listener is null
+ */
+ // FIXME: Java 8: provide a default implementation of this method.
+ <T extends DOMNotificationListener> ListenerRegistration<T> registerNotificationListener(@Nonnull T listener, SchemaPath... types);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import java.util.Collection;
+import java.util.EventListener;
+import javax.annotation.Nonnull;
+
+/**
+ * An {@link EventListener} used to track RPC implementations becoming (un)available
+ * to a {@link DOMRpcService}.
+ */
+public interface DOMRpcAvailabilityListener extends EventListener {
+ /**
+ * Method invoked whenever an RPC type becomes available.
+ *
+ * @param rpcs RPC types newly available
+ */
+ void onRpcAvailable(@Nonnull Collection<DOMRpcIdentifier> rpcs);
+
+ /**
+ * Method invoked whenever an RPC type becomes unavailable.
+ *
+ * @param rpcs RPC types which became unavailable
+ */
+ void onRpcUnavailable(@Nonnull Collection<DOMRpcIdentifier> rpcs);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+/**
+ * Base class for failures that can occur during RPC invocation. This covers
+ * transport and protocol-level failures.
+ */
+public abstract class DOMRpcException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Construct an new instance with a message and an empty cause.
+ *
+ * @param message Exception message
+ */
+ protected DOMRpcException(final String message) {
+ super(message);
+ }
+
+ /**
+ * Construct an new instance with a message and a cause.
+ *
+ * @param message Exception message
+ * @param cause Chained cause
+ */
+ protected DOMRpcException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Preconditions;
+import java.util.Collections;
+import java.util.Objects;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+/**
+ * Identifier of a RPC context. This is an extension of the YANG RPC, which
+ * always has global context. It allows an RPC to have a instance identifier
+ * attached, so that there can be multiple implementations bound to different
+ * contexts concurrently.
+ */
+public abstract class DOMRpcIdentifier {
+
+ private static final YangInstanceIdentifier GLOBAL_CONTEXT = YangInstanceIdentifier.create(Collections.<PathArgument>emptySet());
+
+ private static final class Global extends DOMRpcIdentifier {
+ private Global(final @Nonnull SchemaPath type) {
+ super(type);
+ }
+
+ @Override
+ public YangInstanceIdentifier getContextReference() {
+ return GLOBAL_CONTEXT;
+ }
+ }
+
+ private static final class Local extends DOMRpcIdentifier {
+ private final YangInstanceIdentifier contextReference;
+
+ private Local(final @Nonnull SchemaPath type, final @Nonnull YangInstanceIdentifier contextReference) {
+ super(type);
+ this.contextReference = Preconditions.checkNotNull(contextReference);
+ }
+
+ @Override
+ public YangInstanceIdentifier getContextReference() {
+ return contextReference;
+ }
+ }
+
+ private final SchemaPath type;
+
+ private DOMRpcIdentifier(final SchemaPath type) {
+ this.type = Preconditions.checkNotNull(type);
+ }
+
+ /**
+ * Create a global RPC identifier.
+ *
+ * @param type RPC type, SchemaPath of its definition, may not be null
+ * @return A global RPC identifier, guaranteed to be non-null.
+ */
+ public static @Nonnull DOMRpcIdentifier create(final @Nonnull SchemaPath type) {
+ return new Global(type);
+ }
+
+ /**
+ * Create an RPC identifier with a particular context reference.
+ *
+ * @param type RPC type, SchemaPath of its definition, may not be null
+ * @param contextReference Context reference, null means a global RPC identifier.
+ * @return A global RPC identifier, guaranteed to be non-null.
+ */
+ public static @Nonnull DOMRpcIdentifier create(final @Nonnull SchemaPath type, final @Nullable YangInstanceIdentifier contextReference) {
+ if (contextReference == null || GLOBAL_CONTEXT.equals(contextReference)) {
+ return new Global(type);
+ } else {
+ return new Local(type, contextReference);
+ }
+ }
+
+ /**
+ * Return the RPC type.
+ *
+ * @return RPC type.
+ */
+ public final @Nonnull SchemaPath getType() {
+ return type;
+ }
+
+ /**
+ * Return the RPC context reference. Null value indicates global context.
+ *
+ * @return RPC context reference.
+ */
+ public abstract @Nonnull YangInstanceIdentifier getContextReference();
+
+ @Override
+ public final int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + type.hashCode();
+ result = prime * result + (getContextReference() == null ? 0 : getContextReference().hashCode());
+ return result;
+ }
+
+ @Override
+ public final boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof DOMRpcIdentifier)) {
+ return false;
+ }
+ DOMRpcIdentifier other = (DOMRpcIdentifier) obj;
+ if (!type.equals(other.type)) {
+ return false;
+ }
+ return Objects.equals(getContextReference(), other.getContextReference());
+ }
+
+ @Override
+ public final String toString() {
+ return MoreObjects.toStringHelper(this).omitNullValues().add("type", type).add("contextReference", getContextReference()).toString();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import com.google.common.util.concurrent.CheckedFuture;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * Interface implemented by an individual RPC implementation. This API allows for dispatch
+ * implementations, e.g. an individual object handling a multitude of RPCs.
+ */
+public interface DOMRpcImplementation {
+ /**
+ * Initiate invocation of the RPC. Implementations of this method are
+ * expected to not block on external resources.
+ *
+ * @param rpc RPC identifier which was invoked
+ * @param input Input arguments, null if the RPC does not take any.
+ * @return A {@link CheckedFuture} which will return either a result structure,
+ * or report a subclass of {@link DOMRpcException} reporting a transport
+ * error.
+ */
+ @Nonnull CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(@Nonnull DOMRpcIdentifier rpc, @Nullable NormalizedNode<?, ?> input);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import com.google.common.base.Preconditions;
+import javax.annotation.Nonnull;
+
+/**
+ * Exception indicating that no implementation of the requested RPC service is available.
+ */
+public class DOMRpcImplementationNotAvailableException extends DOMRpcException {
+ private static final long serialVersionUID = 1L;
+
+ public DOMRpcImplementationNotAvailableException(@Nonnull final String format, final Object... args) {
+ super(String.format(format, args));
+ }
+
+ public DOMRpcImplementationNotAvailableException(@Nonnull final Throwable cause, @Nonnull final String format, final Object... args) {
+ super(String.format(format, args), Preconditions.checkNotNull(cause));
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+
+/**
+ * A registration of a {@link DOMRpcImplementation}. Used to track and revoke a registration
+ * with a {@link DOMRpcProviderService}.
+ *
+ * @param <T> RPC implementation type
+ */
+public interface DOMRpcImplementationRegistration<T extends DOMRpcImplementation> extends ObjectRegistration<T> {
+ @Override
+ void close();
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import java.util.Set;
+import javax.annotation.Nonnull;
+
+/**
+ * A {@link DOMService} which allows registration of RPC implementations with a conceptual
+ * router. The client counterpart of this service is {@link DOMRpcService}.
+ */
+public interface DOMRpcProviderService extends DOMService {
+ /**
+ * Register an {@link DOMRpcImplementation} object with this service.
+ *
+ * @param implementation RPC implementation, must not be null
+ * @param rpcs Array of supported RPC identifiers. Must not be null, empty, or contain a null element.
+ * Each identifier is added exactly once, no matter how many times it occurs.
+ * @return A {@link DOMRpcImplementationRegistration} object, guaranteed to be non-null.
+ * @throws NullPointerException if implementation or types is null
+ * @throws IllegalArgumentException if types is empty or contains a null element.
+ */
+ @Nonnull <T extends DOMRpcImplementation> DOMRpcImplementationRegistration<T> registerRpcImplementation(@Nonnull T implementation, @Nonnull DOMRpcIdentifier... rpcs);
+
+ /**
+ * Register an {@link DOMRpcImplementation} object with this service.
+ *
+ * @param implementation RPC implementation, must not be null
+ * @param rpcs Set of supported RPC identifiers. Must not be null, empty, or contain a null element.
+ * @return A {@link DOMRpcImplementationRegistration} object, guaranteed to be non-null.
+ * @throws NullPointerException if implementation or types is null
+ * @throws IllegalArgumentException if types is empty or contains a null element.
+ */
+ @Nonnull <T extends DOMRpcImplementation> DOMRpcImplementationRegistration<T> registerRpcImplementation(@Nonnull T implementation, @Nonnull Set<DOMRpcIdentifier> rpcs);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import java.util.Collection;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * Interface defining a result of an RPC call.
+ */
+public interface DOMRpcResult {
+ /**
+ * Returns a set of errors and warnings which occurred during processing
+ * the call.
+ *
+ * @return a Collection of {@link RpcError}, guaranteed to be non-null. In case
+ * no errors are reported, an empty collection is returned.
+ */
+ @Nonnull Collection<RpcError> getErrors();
+
+ /**
+ * Returns the value result of the call or null if no result is available.
+ *
+ * @return Invocation result, null if the operation has not produced a result. This might
+ * be the case if the operation does not produce a result, or if it failed.
+ */
+ @Nullable NormalizedNode<?, ?> getResult();
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import com.google.common.util.concurrent.CheckedFuture;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+/**
+ * A {@link DOMService} which allows clients to invoke RPCs. The conceptual model of this
+ * service is that of a dynamic router, where the set of available RPC services can change
+ * dynamically. The service allows users to add a listener to track the process of
+ * RPCs becoming available.
+ */
+public interface DOMRpcService extends DOMService {
+ /**
+ * Initiate invocation of an RPC. This method is guaranteed to not block on any external
+ * resources.
+ *
+ * @param type SchemaPath of the RPC to be invoked
+ * @param input Input arguments, null if the RPC does not take any.
+ * @return A {@link CheckedFuture} which will return either a result structure,
+ * or report a subclass of {@link DOMRpcException} reporting a transport
+ * error.
+ */
+ @Nonnull CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(@Nonnull SchemaPath type, @Nullable NormalizedNode<?, ?> input);
+
+ /**
+ * Register a {@link DOMRpcAvailabilityListener} with this service to receive notifications
+ * about RPC implementations becoming (un)available. The listener will be invoked with the
+ * current implementations reported and will be kept uptodate as implementations come and go.
+ *
+ * Users should note that using a listener does not necessarily mean that {@link #invokeRpc(SchemaPath, NormalizedNode)}
+ * will not report a failure due to {@link DOMRpcImplementationNotAvailableException} and
+ * need to be ready to handle it. Implementations are encouraged to take reasonable precautions
+ * to prevent this scenario from occurring.
+ *
+ * @param listener {@link DOMRpcAvailabilityListener} instance to register
+ * @return A {@link DOMRpcAvailabilityListenerRegistration} representing this registration. Performing
+ * a {@link DOMRpcAvailabilityListenerRegistration#close()} will cancel it. Returned object
+ * is guaranteed to be non-null.
+ */
+ @Nonnull <T extends DOMRpcAvailabilityListener> ListenerRegistration<T> registerRpcListener(@Nonnull T listener);
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.api;
+
+import org.opendaylight.controller.sal.core.api.BrokerService;
+
+/**
+ * Marker interface for services which can be obtained from a {@link DOMMountPoint}
+ * instance. No further semantics are implied.
+ */
+public interface DOMService extends BrokerService {
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Marker interface for services which expose additional functionality on top
+ * of some base {@link DOMService}.
+ */
+@Beta
+public interface DOMServiceExtension<T extends DOMExtensibleService<T, E>, E extends DOMServiceExtension<T, E>> {
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * A chain of DOM Data transactions.
+ *
+ * Transactions in a chain need to be committed in sequence and each
+ * transaction should see the effects of previous transactions as if they happened. A chain
+ * makes no guarantees of atomicity, in fact transactions are committed as soon as possible.
+ *
+ * <p>
+ * This interface is type capture of {@link TransactionChain} for DOM Data Contracts.
+ */
+public interface DOMTransactionChain extends TransactionChain<YangInstanceIdentifier, NormalizedNode<?, ?>> {
+
+ @Override
+ DOMDataReadOnlyTransaction newReadOnlyTransaction();
+
+ @Override
+ DOMDataReadWriteTransaction newReadWriteTransaction();
+
+ @Override
+ DOMDataWriteTransaction newWriteOnlyTransaction();
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.api;
+
+import java.util.Collection;
+import java.util.Collections;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+public abstract class AbstractConsumer implements Consumer, BundleActivator,ServiceTrackerCustomizer<Broker, Broker> {
+
+ private BundleContext context;
+ private ServiceTracker<Broker, Broker> tracker;
+ private Broker broker;
+
+ @Override
+ public final void start(final BundleContext context) throws Exception {
+ this.context = context;
+ this.startImpl(context);
+ tracker = new ServiceTracker<>(context, Broker.class, this);
+ tracker.open();
+ }
+
+
+
+ @Override
+ public final void stop(final BundleContext context) throws Exception {
+ stopImpl(context);
+ broker = null;
+ tracker.close();
+ }
+
+ protected void startImpl(final BundleContext context) {
+ // NOOP
+ }
+ protected void stopImpl(final BundleContext context) {
+ // NOOP
+ }
+
+ @Override
+ public Collection<ConsumerFunctionality> getConsumerFunctionality() {
+ return Collections.emptySet();
+ }
+
+
+ @Override
+ public Broker addingService(final ServiceReference<Broker> reference) {
+ if(broker == null) {
+ broker = context.getService(reference);
+ broker.registerConsumer(this, context);
+ return broker;
+ }
+
+ return null;
+ }
+
+ @Override
+ public void modifiedService(final ServiceReference<Broker> reference, final Broker service) {
+ // NOOP
+ }
+
+ @Override
+ public void removedService(final ServiceReference<Broker> reference, final Broker service) {
+ stopImpl(context);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.api;
+
+import java.util.Collection;
+import java.util.Collections;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+public abstract class AbstractProvider implements BundleActivator, Provider,ServiceTrackerCustomizer<Broker, Broker> {
+
+ private Broker broker;
+ private BundleContext context;
+ private ServiceTracker<Broker, Broker> tracker;
+
+ @Override
+ public Collection<ProviderFunctionality> getProviderFunctionality() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public final void start(final BundleContext context) throws Exception {
+ this.context = context;
+ this.startImpl(context);
+ tracker = new ServiceTracker<>(context, Broker.class, this);
+ tracker.open();
+ }
+
+ protected void startImpl(final BundleContext context) {
+ // NOOP
+ }
+ protected void stopImpl(final BundleContext context) {
+ // NOOP
+ }
+
+ @Override
+ public final void stop(final BundleContext context) throws Exception {
+ broker = null;
+ tracker.close();
+ tracker = null;
+ stopImpl(context);
+ }
+
+ @Override
+ public Broker addingService(final ServiceReference<Broker> reference) {
+ if(broker == null) {
+ broker = context.getService(reference);
+ broker.registerProvider(this, context);
+ return broker;
+ }
+
+ return null;
+ }
+
+ @Override
+ public void modifiedService(final ServiceReference<Broker> reference, final Broker service) {
+ // NOOP
+ }
+
+ @Override
+ public void removedService(final ServiceReference<Broker> reference, final Broker service) {
+ stopImpl(context);
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.api;
+
+import org.osgi.framework.BundleContext;
+
+/**
+ * Core component of the SAL layer responsible for wiring the SAL consumers.
+ *
+ * The responsibility of the broker is to maintain registration of SAL
+ * functionality {@link Consumer}s and {@link Provider}s, store provider and
+ * consumer specific context and functionality registration via
+ * {@link ConsumerSession} and provide access to infrastructure services, which
+ * removes direct dependencies between providers and consumers.
+ *
+ *
+ * The services are exposed via session.
+ *
+ * <h3>Session-based access</h3>
+ *
+ * The providers and consumers needs to register in order to use the
+ * binding-independent SAL layer and to expose functionality via SAL layer.
+ *
+ * For more information about session-based access see {@link ConsumerSession}
+ * and {@link ProviderSession}
+ *
+ *
+ *
+ */
+public interface Broker {
+
+ /**
+ * Registers the {@link Consumer}, which will use the SAL layer.
+ *
+ * <p>
+ * During the registration, the broker obtains the initial functionality
+ * from consumer, using the {@link Consumer#getConsumerFunctionality()}, and
+ * register that functionality into system and concrete infrastructure
+ * services.
+ *
+ * <p>
+ * Note that consumer could register additional functionality at later point
+ * by using service and functionality specific APIs.
+ *
+ * <p>
+ * The consumer is required to use returned session for all communication
+ * with broker or one of the broker services. The session is announced to
+ * the consumer by invoking
+ * {@link Consumer#onSessionInitiated(ConsumerSession)}.
+ *
+ * @param cons
+ * Consumer to be registered.
+ * @return a session specific to consumer registration
+ * @throws IllegalArgumentException
+ * If the consumer is <code>null</code>.
+ * @throws IllegalStateException
+ * If the consumer is already registered.
+ */
+ ConsumerSession registerConsumer(Consumer cons);
+
+ /*
+ * @deprecated Use registerConsumer(Consumer cons) instead (BundleContext is no longer used)
+ */
+ @Deprecated
+ ConsumerSession registerConsumer(Consumer cons, BundleContext context);
+
+ /**
+ * Registers the {@link Provider}, which will use the SAL layer.
+ *
+ * <p>
+ * During the registration, the broker obtains the initial functionality
+ * from consumer, using the {@link Provider#getProviderFunctionality()}, and
+ * register that functionality into system and concrete infrastructure
+ * services.
+ * <p>
+ * The consumer is <b>required to use</b> returned session for all
+ * communication with broker or one of the broker services. The session is
+ * announced to the consumer by invoking
+ * {@link Provider#onSessionInitiated(ProviderSession)}.
+ *
+ *
+ * @param prov
+ * Provider to be registered.
+ * @return a session unique to the provider registration.
+ * @throws IllegalArgumentException
+ * If the provider is <code>null</code>.
+ * @throws IllegalStateException
+ * If the consumer is already registered.
+ */
+ ProviderSession registerProvider(Provider prov);
+
+ /*
+ * @deprecated Use registerProvider(Provider cons) instead (BundleContext is no longer used)
+ */
+ @Deprecated
+ ProviderSession registerProvider(Provider prov, BundleContext context);
+
+ /**
+ * {@link Consumer} specific access to the SAL functionality.
+ *
+ * <p>
+ * ConsumerSession is {@link Consumer}-specific access to the SAL
+ * functionality and infrastructure services.
+ *
+ * <p>
+ * The session serves to store SAL context (e.g. registration of
+ * functionality) for the consumer and provides access to the SAL
+ * infrastructure services and other functionality provided by
+ * {@link Provider}s.
+ *
+ *
+ *
+ */
+ public interface ConsumerSession {
+
+ boolean isClosed();
+
+ /**
+ * Returns a session specific instance (implementation) of requested
+ * service
+ *
+ * @param service
+ * Broker service
+ * @return Session specific implementation of service
+ */
+ <T extends BrokerService> T getService(Class<T> service);
+
+ /**
+ * Closes a session between consumer and broker.
+ *
+ * <p>
+ * The close operation unregisters a consumer and remove all registered
+ * functionality of the consumer from the system.
+ *
+ */
+ void close();
+ }
+
+ /**
+ * {@link Provider} specific access to the SAL functionality.
+ *
+ * <p>
+ * ProviderSession is {@link Provider}-specific access to the SAL
+ * functionality and infrastructure services, which also allows for exposing
+ * the provider's functionality to the other {@link Consumer}s.
+ *
+ * <p>
+ * The session serves to store SAL context (e.g. registration of
+ * functionality) for the providers and exposes access to the SAL
+ * infrastructure services, dynamic functionality registration and any other
+ * functionality provided by other {@link Provider}s.
+ *
+ */
+ public interface ProviderSession extends ConsumerSession {
+ /**
+ * Closes a session between provider and SAL.
+ *
+ * <p>
+ * The close operation unregisters a provider and remove all registered
+ * functionality of the provider from the system.
+ */
+ @Override
+ void close();
+
+ @Override
+ boolean isClosed();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.api;
+
+/**
+ *
+ * Session-specific instance of the broker functionality.
+ *
+ * <p>
+ * BrokerService is marker interface for infrastructure services provided by the
+ * SAL. These services are session-specific, each {@link Provider} and
+ * {@link Consumer} usually has own instance of the service with it's own
+ * context.
+ *
+ * <p>
+ * The consumer's (or provider's) instance of specific service could be obtained
+ * by invoking {@link org.opendaylight.controller.sal.core.api.Broker.ConsumerSession#getService(Class)} method on session
+ * assigned to the consumer.
+ *
+ * <p>
+ * {@link BrokerService} and {@link Provider} may seem similar, but provider
+ * provides YANG model-based functionality and {@link BrokerService} exposes the
+ * necessary supporting functionality to implement specific functionality of
+ * YANG and to reuse it in the development of {@link Consumer}s and
+ * {@link Provider}s.
+ *
+ *
+ */
+public interface BrokerService {
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.api;
+
+import java.util.Collection;
+
+import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;
+
+/**
+ *
+ * Defines the component of controller and supplies additional metadata. A
+ * component of the controller or application supplies a concrete implementation
+ * of this interface.
+ *
+ * A user-implemented component (application) which facilitates the SAL and SAL
+ * services to access infrastructure services or providers' functionality.
+ *
+ *
+ */
+public interface Consumer {
+
+ /**
+ * Callback signaling initialization of the consumer session to the SAL.
+ *
+ * The consumer MUST use the session for all communication with SAL or
+ * retrieving SAL infrastructure services.
+ *
+ * This method is invoked by {@link Broker#registerConsumer(Consumer)}
+ *
+ * @param session
+ * Unique session between consumer and SAL.
+ */
+ public void onSessionInitiated(ConsumerSession session);
+
+ /**
+ * @deprecated - no longer used or needed
+ * *
+ * Suggested implementation until removed:
+ * @code {
+ * public Collection<ConsumerFunctionality> getConsumerFunctionality() {
+ * return Collections.emptySet();
+ * }
+ * }
+ */
+ @Deprecated
+ public Collection<ConsumerFunctionality> getConsumerFunctionality();
+
+ /**
+ * @deprecated - no longer used or needed
+ */
+ @Deprecated
+ public interface ConsumerFunctionality {
+
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.api;
+
+import java.util.Collection;
+
+import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
+
+/**
+ *
+ * Defines the component of controller and supplies additional metadata. A
+ * component of the controller or application supplies a concrete implementation
+ * of this interface.
+ *
+ * <p>
+ * A user-implemented component (application) which facilitates the SAL and SAL
+ * services to access infrastructure services and to provide functionality to
+ * {@link Consumer}s and other providers.
+ *
+ *
+ */
+public interface Provider {
+
+ /**
+ * Callback signaling initialization of the provider session to the SAL.
+ *
+ * <p>
+ * The provider <b>MUST use the session</b> for all communication with SAL
+ * or retrieving SAL infrastructure services.
+ *
+ * <p>
+ * This method is invoked by {@link Broker#registerConsumer(Consumer)}
+ *
+ * @param session
+ * Unique session between provider and SAL.
+ */
+ public void onSessionInitiated(ProviderSession session);
+
+ /**
+ * @deprecated - No longer used or needed
+ *
+ * Suggested implementation until removed:
+ * @code {
+ * public Collection<ProviderFunctionality> getProviderFunctionality() {
+ * return Collections.emptySet();
+ * }
+ * }
+ */
+ @Deprecated
+ public Collection<ProviderFunctionality> getProviderFunctionality();
+
+ /**
+ * @deprecated - no longer used or needed
+ */
+ @Deprecated
+ public interface ProviderFunctionality {
+
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.api;
+
+/**
+ * Exception reported when no RPC implementation is found in the system.
+ *
+ * @deprecated Use {@link org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationNotAvailableException} instead.
+ */
+@Deprecated
+public class RpcImplementationUnavailableException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ public RpcImplementationUnavailableException(final String message) {
+ super(message);
+ }
+
+ public RpcImplementationUnavailableException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.api;
+
+import java.io.Serializable;
+
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.common.QName;
+
+public class RpcRoutingContext implements Immutable, Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -9079324728075883325L;
+
+ private final QName context;
+ private final QName rpc;
+
+
+ private RpcRoutingContext(QName context, QName rpc) {
+ super();
+ this.context = context;
+ this.rpc = rpc;
+ }
+
+ public static final RpcRoutingContext create(QName context, QName rpc) {
+ return new RpcRoutingContext(context, rpc);
+ }
+
+ public QName getContext() {
+ return context;
+ }
+
+ public QName getRpc() {
+ return rpc;
+ }
+
+ @Override
+ public String toString() {
+ return "RpcRoutingContext [context=" + context + ", rpc=" + rpc + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((context == null) ? 0 : context.hashCode());
+ result = prime * result + ((rpc == null) ? 0 : rpc.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ RpcRoutingContext other = (RpcRoutingContext) obj;
+ if (context == null) {
+ if (other.context != null)
+ return false;
+ } else if (!context.equals(other.context))
+ return false;
+ if (rpc == null) {
+ if (other.rpc != null)
+ return false;
+ } else if (!rpc.equals(other.rpc))
+ return false;
+ return true;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.api.model;
+
+import org.opendaylight.controller.sal.core.api.BrokerService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+
+public interface SchemaService extends BrokerService {
+
+ /**
+ * Registers a YANG module to session and global context
+ *
+ * @param module
+ */
+ void addModule(Module module);
+
+ /**
+ * Unregisters a YANG module from session context
+ *
+ * @param module
+ */
+ void removeModule(Module module);
+
+ /**
+ * Returns session specific YANG schema context
+ * @return
+ */
+ SchemaContext getSessionContext();
+
+ /**
+ * Returns global schema context
+ *
+ * @return
+ */
+ SchemaContext getGlobalContext();
+
+ /**
+ * Register a listener for changes in schema context.
+ *
+ * @param listener Listener which should be registered
+ * @return Listener registration handle
+ */
+ ListenerRegistration<SchemaContextListener> registerSchemaContextListener(SchemaContextListener listener);
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.sal.core.api.mount;
+
+import java.util.EventListener;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+public interface MountProvisionListener extends EventListener {
+
+ void onMountPointCreated(YangInstanceIdentifier path);
+
+ void onMountPointRemoved(YangInstanceIdentifier path);
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+/**
+ * Core binding-independent SAL contracts and components
+ */
+package org.opendaylight.controller.sal.core.api;
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import static org.junit.Assert.assertNotNull;
+import com.google.common.util.concurrent.CheckedFuture;
+import java.net.URI;
+import java.util.Collections;
+import javax.annotation.Nonnull;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+
+/**
+ * Abstract test suite demonstrating various access patterns on how a {@link DOMDataTreeService}
+ * can be used.
+ */
+public abstract class AbstractDOMDataTreeServiceTestSuite {
+ protected static final QNameModule TEST_MODULE = QNameModule.create(URI.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:test:store"), null);
+
+ protected static final YangInstanceIdentifier UNORDERED_CONTAINER_IID = YangInstanceIdentifier.create(
+ new NodeIdentifier(QName.create(TEST_MODULE, "lists")),
+ new NodeIdentifier(QName.create(TEST_MODULE, "unordered-container")));
+ protected static final DOMDataTreeIdentifier UNORDERED_CONTAINER_TREE = new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, UNORDERED_CONTAINER_IID);
+
+ /**
+ * Return a reference to the service used in this test. The instance
+ * needs to be reused within the same test and must be isolated between
+ * tests.
+ *
+ * @return {@link DOMDataTreeService} instance.
+ */
+ protected abstract @Nonnull DOMDataTreeService service();
+
+ /**
+ * A simple unbound producer. It write some basic things into the data store based on the
+ * test model.
+ * @throws DOMDataTreeProducerException
+ * @throws TransactionCommitFailedException
+ */
+ @Test
+ public final void testBasicProducer() throws DOMDataTreeProducerException, TransactionCommitFailedException {
+ // Create a producer. It is an AutoCloseable resource, hence the try-with pattern
+ try (final DOMDataTreeProducer prod = service().createProducer(Collections.singleton(UNORDERED_CONTAINER_TREE))) {
+ assertNotNull(prod);
+
+ final DOMDataWriteTransaction tx = prod.createTransaction(true);
+ assertNotNull(tx);
+
+ tx.put(LogicalDatastoreType.OPERATIONAL, UNORDERED_CONTAINER_IID, ImmutableContainerNodeBuilder.create().build());
+
+ final CheckedFuture<Void, TransactionCommitFailedException> f = tx.submit();
+ assertNotNull(f);
+
+ f.checkedGet();
+ }
+ }
+
+ // TODO: simple listener
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-parent</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>sal-broker-impl</artifactId>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.lmax</groupId>
+ <artifactId>disruptor</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-common-impl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-common-util</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-core-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-core-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-inmemory-datastore</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>util</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-impl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-parser-impl</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.yangtools.model</groupId>
+ <artifactId>ietf-yang-types</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>mockito-configuration</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name>
+ <Bundle-Activator>org.opendaylight.controller.sal.dom.broker.osgi.SchemaServiceActivator</Bundle-Activator>
+ <Export-Package>
+ <!-- Legacy code -->
+ org.opendaylight.controller.sal.dom.broker,
+ org.opendaylight.controller.sal.dom.broker.impl,
+ org.opendaylight.controller.sal.dom.broker.osgi,
+ org.opendaylight.controller.sal.dom.broker.util,
+ org.opendaylight.controller.sal.dom.broker.spi,
+ <!--sal.broker.impl is exported for sal-netconf-connector to use SchemaAwareRpcRegistry.-->
+ <!-- TODO Remove sal.broker.impl from export when SchemaAwareRpcRegistry is not used in connector anymore -->
+ org.opendaylight.controller.md.sal.dom.broker.impl,
+ org.opendaylight.controller.md.sal.dom.broker.impl.*,
+ </Export-Package>
+ <Import-Package>*</Import-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ <!-- TODO - unite yang-maven-plugin configuration in md-sal-->
+ <plugin>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>generate-sources</goal>
+ </goals>
+ <configuration>
+ <codeGenerators>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator</codeGeneratorClass>
+ <outputBaseDir>${jmxGeneratorPath}</outputBaseDir>
+ <additionalConfiguration>
+ <namespaceToPackage1>urn:opendaylight:params:xml:ns:yang:controller==org.opendaylight.controller.config.yang</namespaceToPackage1>
+ </additionalConfiguration>
+ </generator>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl</codeGeneratorClass>
+ <outputBaseDir>${salGeneratorPath}</outputBaseDir>
+ </generator>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.yangtools.yang.unified.doc.generator.maven.DocumentationGeneratorImpl</codeGeneratorClass>
+ <outputBaseDir>target/site/models</outputBaseDir>
+ </generator>
+ </codeGenerators>
+ <inspectDependencies>true</inspectDependencies>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ <scm>
+ <connection>scm:git:http://git.opendaylight.org/gerrit/controller.git</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+ <tag>HEAD</tag>
+ <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
+ </scm>
+</project>
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import static com.google.common.base.Preconditions.checkState;
+import com.google.common.collect.ImmutableMap;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.atomic.AtomicLong;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBrokerExtension;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
+import org.opendaylight.controller.sal.core.spi.data.DOMStore;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreTreeChangePublisher;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractDOMDataBroker extends AbstractDOMForwardedTransactionFactory<DOMStore> implements DOMDataBroker, AutoCloseable {
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractDOMDataBroker.class);
+
+ private final AtomicLong txNum = new AtomicLong();
+ private final AtomicLong chainNum = new AtomicLong();
+ private final Map<Class<? extends DOMDataBrokerExtension>, DOMDataBrokerExtension> extensions;
+ private volatile AutoCloseable closeable;
+
+ protected AbstractDOMDataBroker(final Map<LogicalDatastoreType, DOMStore> datastores) {
+ super(datastores);
+
+ boolean treeChange = true;
+ for (DOMStore ds : datastores.values()) {
+ if (!(ds instanceof DOMStoreTreeChangePublisher)) {
+ treeChange = false;
+ break;
+ }
+ }
+
+ if (treeChange) {
+ extensions = ImmutableMap.<Class<? extends DOMDataBrokerExtension>, DOMDataBrokerExtension>of(DOMDataTreeChangeService.class, new DOMDataTreeChangeService() {
+ @Override
+ public <L extends DOMDataTreeChangeListener> ListenerRegistration<L> registerDataTreeChangeListener(final DOMDataTreeIdentifier treeId, final L listener) {
+ DOMStore publisher = getTxFactories().get(treeId.getDatastoreType());
+ checkState(publisher != null, "Requested logical data store is not available.");
+
+ return ((DOMStoreTreeChangePublisher)publisher).registerTreeChangeListener(treeId.getRootIdentifier(), listener);
+ }
+ });
+ } else {
+ extensions = Collections.emptyMap();
+ }
+ }
+
+ public void setCloseable(final AutoCloseable closeable) {
+ this.closeable = closeable;
+ }
+
+ @Override
+ public void close() {
+ super.close();
+
+ if(closeable != null) {
+ try {
+ closeable.close();
+ } catch(Exception e) {
+ LOG.debug("Error closing instance", e);
+ }
+ }
+ }
+
+ @Override
+ protected Object newTransactionIdentifier() {
+ return "DOM-" + txNum.getAndIncrement();
+ }
+
+ @Override
+ public ListenerRegistration<DOMDataChangeListener> registerDataChangeListener(final LogicalDatastoreType store,
+ final YangInstanceIdentifier path, final DOMDataChangeListener listener, final DataChangeScope triggeringScope) {
+
+ DOMStore potentialStore = getTxFactories().get(store);
+ checkState(potentialStore != null, "Requested logical data store is not available.");
+ return potentialStore.registerChangeListener(path, listener, triggeringScope);
+ }
+
+ @Override
+ public Map<Class<? extends DOMDataBrokerExtension>, DOMDataBrokerExtension> getSupportedExtensions() {
+ return extensions;
+ }
+
+ @Override
+ public DOMTransactionChain createTransactionChain(final TransactionChainListener listener) {
+ checkNotClosed();
+
+ final Map<LogicalDatastoreType, DOMStoreTransactionChain> backingChains = new EnumMap<>(LogicalDatastoreType.class);
+ for (Entry<LogicalDatastoreType, DOMStore> entry : getTxFactories().entrySet()) {
+ backingChains.put(entry.getKey(), entry.getValue().createTransactionChain());
+ }
+
+ final long chainId = chainNum.getAndIncrement();
+ LOG.debug("Transactoin chain {} created with listener {}, backing store chains {}", chainId, listener,
+ backingChains);
+ return new DOMDataBrokerTransactionChainImpl(chainId, backingChains, this, listener);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Preconditions;
+import java.util.Collection;
+import java.util.Map;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransaction;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * Composite DOM Transaction backed by {@link DOMStoreTransaction}.
+ *
+ * Abstract base for composite transaction, which provides access only to common
+ * functionality as retrieval of subtransaction, close method and retrieval of
+ * identifier.
+ *
+ * @param <K>
+ * Subtransaction distinguisher
+ * @param <T>
+ * Subtransaction type
+ */
+abstract class AbstractDOMForwardedCompositeTransaction<K, T extends DOMStoreTransaction> implements
+ AsyncTransaction<YangInstanceIdentifier, NormalizedNode<?, ?>> {
+
+ private final Map<K, T> backingTxs;
+ private final Object identifier;
+
+ /**
+ *
+ * Creates new composite Transactions.
+ *
+ * @param identifier
+ * Identifier of transaction.
+ * @param backingTxs
+ * Key,value map of backing transactions.
+ */
+ protected AbstractDOMForwardedCompositeTransaction(final Object identifier, final Map<K, T> backingTxs) {
+ this.identifier = Preconditions.checkNotNull(identifier, "Identifier should not be null");
+ this.backingTxs = Preconditions.checkNotNull(backingTxs, "Backing transactions should not be null");
+ }
+
+ /**
+ * Returns subtransaction associated with supplied key.
+ *
+ * @param key
+ * @return
+ * @throws NullPointerException
+ * if key is null
+ * @throws IllegalArgumentException
+ * if no subtransaction is associated with key.
+ */
+ protected final T getSubtransaction(final K key) {
+ Preconditions.checkNotNull(key, "key must not be null.");
+
+ final T ret = backingTxs.get(key);
+ Preconditions.checkArgument(ret != null, "No subtransaction associated with %s", key);
+ return ret;
+ }
+
+ /**
+ * Returns immutable Iterable of all subtransactions.
+ *
+ */
+ protected Collection<T> getSubtransactions() {
+ return backingTxs.values();
+ }
+
+ @Override
+ public Object getIdentifier() {
+ return identifier;
+ }
+
+ protected void closeSubtransactions() {
+ /*
+ * We share one exception for all failures, which are added
+ * as supressedExceptions to it.
+ */
+ IllegalStateException failure = null;
+ for (T subtransaction : backingTxs.values()) {
+ try {
+ subtransaction.close();
+ } catch (Exception e) {
+ // If we did not allocated failure we allocate it
+ if (failure == null) {
+ failure = new IllegalStateException("Uncaught exception occured during closing transaction", e);
+ } else {
+ // We update it with additional exceptions, which occurred during error.
+ failure.addSuppressed(e);
+ }
+ }
+ }
+ // If we have failure, we throw it at after all attempts to close.
+ if (failure != null) {
+ throw failure;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.CheckedFuture;
+import java.util.Collection;
+import java.util.EnumMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionFactory;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+
+/**
+ *
+ * Abstract composite transaction factory.
+ *
+ * Provides an convenience common implementation for composite DOM Transactions,
+ * where subtransaction is identified by {@link LogicalDatastoreType} type and
+ * implementation of subtransaction is provided by
+ * {@link DOMStoreTransactionFactory}.
+ *
+ * <b>Note:</b>This class does not have thread-safe implementation of {@link #close()},
+ * implementation may allow accessing and allocating new transactions during closing
+ * this instance.
+ *
+ * @param <T>
+ * Type of {@link DOMStoreTransactionFactory} factory.
+ */
+abstract class AbstractDOMForwardedTransactionFactory<T extends DOMStoreTransactionFactory> implements AutoCloseable {
+ @SuppressWarnings("rawtypes")
+ private static final AtomicIntegerFieldUpdater<AbstractDOMForwardedTransactionFactory> UPDATER =
+ AtomicIntegerFieldUpdater.newUpdater(AbstractDOMForwardedTransactionFactory.class, "closed");
+ private final Map<LogicalDatastoreType, T> storeTxFactories;
+ private volatile int closed = 0;
+
+ protected AbstractDOMForwardedTransactionFactory(final Map<LogicalDatastoreType, ? extends T> txFactories) {
+ this.storeTxFactories = new EnumMap<>(txFactories);
+ }
+
+ /**
+ * Implementations must return unique identifier for each and every call of
+ * this method;
+ *
+ * @return new Unique transaction identifier.
+ */
+ protected abstract Object newTransactionIdentifier();
+
+ /**
+ * User-supplied implementation of {@link DOMDataWriteTransaction#submit()}
+ * for transaction.
+ *
+ * Callback invoked when {@link DOMDataWriteTransaction#submit()} is invoked
+ * on transaction created by this factory.
+ *
+ * @param transaction
+ * Transaction on which {@link DOMDataWriteTransaction#commit()}
+ * was invoked.
+ * @param cohorts
+ * Iteratable of cohorts for subtransactions associated with
+ * the transaction being committed.
+ * @return a CheckedFuture. if commit coordination on cohorts finished successfully,
+ * nothing is returned from the Future, On failure,
+ * the Future fails with a {@link TransactionCommitFailedException}.
+ */
+ protected abstract CheckedFuture<Void,TransactionCommitFailedException> submit(final DOMDataWriteTransaction transaction,
+ final Collection<DOMStoreThreePhaseCommitCohort> cohorts);
+
+ /**
+ * Creates a new composite read-only transaction
+ *
+ * Creates a new composite read-only transaction backed by one transaction
+ * per factory in {@link #getTxFactories()}.
+ *
+ * Subtransaction for reading is selected by supplied
+ * {@link LogicalDatastoreType} as parameter for
+ * {@link DOMDataReadOnlyTransaction#read(LogicalDatastoreType,org.opendaylight.yangtools.yang.data.api.InstanceIdentifier)}
+ * .
+ *
+ * Id of returned transaction is retrieved via
+ * {@link #newTransactionIdentifier()}.
+ *
+ * @return New composite read-only transaction.
+ */
+ public final DOMDataReadOnlyTransaction newReadOnlyTransaction() {
+ checkNotClosed();
+
+ final Map<LogicalDatastoreType, DOMStoreReadTransaction> txns = new EnumMap<>(LogicalDatastoreType.class);
+ for (Entry<LogicalDatastoreType, T> store : storeTxFactories.entrySet()) {
+ txns.put(store.getKey(), store.getValue().newReadOnlyTransaction());
+ }
+ return new DOMForwardedReadOnlyTransaction(newTransactionIdentifier(), txns);
+ }
+
+ /**
+ * Creates a new composite write-only transaction
+ *
+ * <p>
+ * Creates a new composite write-only transaction backed by one write-only
+ * transaction per factory in {@link #getTxFactories()}.
+ *
+ * <p>
+ * Implementation of composite Write-only transaction is following:
+ *
+ * <ul>
+ * <li>
+ * {@link DOMDataWriteTransaction#put(LogicalDatastoreType, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode)}
+ * - backing subtransaction is selected by {@link LogicalDatastoreType},
+ * {@link DOMStoreWriteTransaction#write(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode)}
+ * is invoked on selected subtransaction.
+ * <li>
+ * {@link DOMDataWriteTransaction#merge(LogicalDatastoreType, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode)}
+ * - backing subtransaction is selected by {@link LogicalDatastoreType},
+ * {@link DOMStoreWriteTransaction#merge(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode)}
+ * is invoked on selected subtransaction.
+ * <li>
+ * {@link DOMDataWriteTransaction#delete(LogicalDatastoreType, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier)
+ * - backing subtransaction is selected by {@link LogicalDatastoreType},
+ * {@link DOMStoreWriteTransaction#delete(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier)} is invoked on
+ * selected subtransaction.
+ * <li> {@link DOMDataWriteTransaction#commit()} - results in invoking
+ * {@link DOMStoreWriteTransaction#ready()}, gathering all resulting cohorts
+ * and then invoking finalized implementation callback
+ * {@link #submit(DOMDataWriteTransaction, Iterable)} with transaction which
+ * was commited and gathered results.
+ * </ul>
+ *
+ * Id of returned transaction is generated via
+ * {@link #newTransactionIdentifier()}.
+ *
+ * @return New composite write-only transaction associated with this
+ * factory.
+ */
+ public final DOMDataWriteTransaction newWriteOnlyTransaction() {
+ checkNotClosed();
+
+ final Map<LogicalDatastoreType, DOMStoreWriteTransaction> txns = new EnumMap<>(LogicalDatastoreType.class);
+ for (Entry<LogicalDatastoreType, T> store : storeTxFactories.entrySet()) {
+ txns.put(store.getKey(), store.getValue().newWriteOnlyTransaction());
+ }
+ return new DOMForwardedWriteTransaction<DOMStoreWriteTransaction>(newTransactionIdentifier(), txns, this);
+ }
+
+ /**
+ * Creates a new composite write-only transaction
+ *
+ * <p>
+ * Creates a new composite write-only transaction backed by one write-only
+ * transaction per factory in {@link #getTxFactories()}.
+ * <p>
+ * Implementation of composite Write-only transaction is following:
+ *
+ * <ul>
+ * <li>
+ * {@link DOMDataWriteTransaction#read(LogicalDatastoreType, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier)}
+ * - backing subtransaction is selected by {@link LogicalDatastoreType},
+ * {@link DOMStoreWriteTransaction#read(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier)} is invoked on
+ * selected subtransaction.
+ * <li>
+ * {@link DOMDataWriteTransaction#put(LogicalDatastoreType, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode)}
+ * - backing subtransaction is selected by {@link LogicalDatastoreType},
+ * {@link DOMStoreWriteTransaction#write(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode)}
+ * is invoked on selected subtransaction.
+ * <li>
+ * {@link DOMDataWriteTransaction#merge(LogicalDatastoreType, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode)}
+ * - backing subtransaction is selected by {@link LogicalDatastoreType},
+ * {@link DOMStoreWriteTransaction#merge(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode)}
+ * is invoked on selected subtransaction.
+ * <li>
+ * {@link DOMDataWriteTransaction#delete(LogicalDatastoreType, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier)
+ * - backing subtransaction is selected by {@link LogicalDatastoreType},
+ * {@link DOMStoreWriteTransaction#delete(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier)} is invoked on
+ * selected subtransaction.
+ * <li> {@link DOMDataWriteTransaction#commit()} - results in invoking
+ * {@link DOMStoreWriteTransaction#ready()}, gathering all resulting cohorts
+ * and then invoking finalized implementation callback
+ * {@link #submit(DOMDataWriteTransaction, Iterable)} with transaction which
+ * was commited and gathered results.
+ * <li>
+ * </ul>
+ *
+ * Id of returned transaction is generated via
+ * {@link #newTransactionIdentifier()}.
+ *
+ * @return New composite read-write transaction associated with this
+ * factory.
+ */
+ public final DOMDataReadWriteTransaction newReadWriteTransaction() {
+ checkNotClosed();
+
+ final Map<LogicalDatastoreType, DOMStoreReadWriteTransaction> txns = new EnumMap<>(LogicalDatastoreType.class);
+ for (Entry<LogicalDatastoreType, T> store : storeTxFactories.entrySet()) {
+ txns.put(store.getKey(), store.getValue().newReadWriteTransaction());
+ }
+ return new DOMForwardedReadWriteTransaction(newTransactionIdentifier(), txns, this);
+ }
+
+ /**
+ * Convenience accessor of backing factories intended to be used only by
+ * finalization of this class.
+ *
+ * <b>Note:</b>
+ * Finalization of this class may want to access other functionality of
+ * supplied Transaction factories.
+ *
+ * @return Map of backing transaction factories.
+ */
+ protected final Map<LogicalDatastoreType, T> getTxFactories() {
+ return storeTxFactories;
+ }
+
+ /**
+ * Checks if instance is not closed.
+ *
+ * @throws IllegalStateException If instance of this class was closed.
+ *
+ */
+ protected final void checkNotClosed() {
+ Preconditions.checkState(closed == 0, "Transaction factory was closed. No further operations allowed.");
+ }
+
+ @Override
+ public void close() {
+ final boolean success = UPDATER.compareAndSet(this, 0, 1);
+ Preconditions.checkState(success, "Transaction factory was already closed");
+ }
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+import com.google.common.util.concurrent.CheckedFuture;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementation;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+abstract class AbstractDOMRpcRoutingTableEntry {
+ private final Map<YangInstanceIdentifier, List<DOMRpcImplementation>> impls;
+ private final SchemaPath schemaPath;
+
+ protected AbstractDOMRpcRoutingTableEntry(final SchemaPath schemaPath, final Map<YangInstanceIdentifier, List<DOMRpcImplementation>> impls) {
+ this.schemaPath = Preconditions.checkNotNull(schemaPath);
+ this.impls = Preconditions.checkNotNull(impls);
+ }
+
+ protected final SchemaPath getSchemaPath() {
+ return schemaPath;
+ }
+
+ protected final List<DOMRpcImplementation> getImplementations(final YangInstanceIdentifier context) {
+ return impls.get(context);
+ }
+
+ final Map<YangInstanceIdentifier, List<DOMRpcImplementation>> getImplementations() {
+ return impls;
+ }
+
+ public boolean containsContext(final YangInstanceIdentifier contextReference) {
+ return impls.containsKey(contextReference);
+ }
+
+ final Set<YangInstanceIdentifier> registeredIdentifiers() {
+ return impls.keySet();
+ }
+
+ /**
+ *
+ * @param implementation
+ * @param newRpcs List of new RPCs, must be mutable
+ * @return
+ */
+ final AbstractDOMRpcRoutingTableEntry add(final DOMRpcImplementation implementation, final List<YangInstanceIdentifier> newRpcs) {
+ final Builder<YangInstanceIdentifier, List<DOMRpcImplementation>> vb = ImmutableMap.builder();
+ for (final Entry<YangInstanceIdentifier, List<DOMRpcImplementation>> ve : impls.entrySet()) {
+ if (newRpcs.remove(ve.getKey())) {
+ final ArrayList<DOMRpcImplementation> i = new ArrayList<>(ve.getValue().size() + 1);
+ i.addAll(ve.getValue());
+ i.add(implementation);
+ vb.put(ve.getKey(), i);
+ } else {
+ vb.put(ve);
+ }
+ }
+ for(final YangInstanceIdentifier ii : newRpcs) {
+ final ArrayList<DOMRpcImplementation> impl = new ArrayList<>(1);
+ impl.add(implementation);
+ vb.put(ii,impl);
+ }
+
+ return newInstance(vb.build());
+ }
+
+ final AbstractDOMRpcRoutingTableEntry remove(final DOMRpcImplementation implementation, final List<YangInstanceIdentifier> removed) {
+ final Builder<YangInstanceIdentifier, List<DOMRpcImplementation>> vb = ImmutableMap.builder();
+ for (final Entry<YangInstanceIdentifier, List<DOMRpcImplementation>> ve : impls.entrySet()) {
+ if (removed.remove(ve.getKey())) {
+ final ArrayList<DOMRpcImplementation> i = new ArrayList<>(ve.getValue());
+ i.remove(implementation);
+ // We could trimToSize(), but that may perform another copy just to get rid
+ // of a single element. That is probably not worth the trouble.
+ if (!i.isEmpty()) {
+ vb.put(ve.getKey(), i);
+ }
+ } else {
+ vb.put(ve);
+ }
+ }
+
+ final Map<YangInstanceIdentifier, List<DOMRpcImplementation>> v = vb.build();
+ return v.isEmpty() ? null : newInstance(v);
+ }
+
+ protected abstract CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(final NormalizedNode<?, ?> input);
+ protected abstract AbstractDOMRpcRoutingTableEntry newInstance(final Map<YangInstanceIdentifier, List<DOMRpcImplementation>> impls);
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Throwables;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.Collection;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.yangtools.util.DurationStatisticsTracker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Implementation of blocking three-phase commit-coordination tasks without
+ * support of cancellation.
+ */
+final class CommitCoordinationTask implements Callable<Void> {
+ private static enum Phase {
+ canCommit,
+ preCommit,
+ doCommit,
+ }
+
+ private static final Logger LOG = LoggerFactory.getLogger(CommitCoordinationTask.class);
+ private final Collection<DOMStoreThreePhaseCommitCohort> cohorts;
+ private final DurationStatisticsTracker commitStatTracker;
+ private final DOMDataWriteTransaction tx;
+
+ public CommitCoordinationTask(final DOMDataWriteTransaction transaction,
+ final Collection<DOMStoreThreePhaseCommitCohort> cohorts,
+ final DurationStatisticsTracker commitStatTracker) {
+ this.tx = Preconditions.checkNotNull(transaction, "transaction must not be null");
+ this.cohorts = Preconditions.checkNotNull(cohorts, "cohorts must not be null");
+ this.commitStatTracker = commitStatTracker;
+ }
+
+ @Override
+ public Void call() throws TransactionCommitFailedException {
+ final long startTime = commitStatTracker != null ? System.nanoTime() : 0;
+
+ Phase phase = Phase.canCommit;
+
+ try {
+ LOG.debug("Transaction {}: canCommit Started", tx.getIdentifier());
+ canCommitBlocking();
+
+ phase = Phase.preCommit;
+ LOG.debug("Transaction {}: preCommit Started", tx.getIdentifier());
+ preCommitBlocking();
+
+ phase = Phase.doCommit;
+ LOG.debug("Transaction {}: doCommit Started", tx.getIdentifier());
+ commitBlocking();
+
+ LOG.debug("Transaction {}: doCommit completed", tx.getIdentifier());
+ return null;
+ } catch (final TransactionCommitFailedException e) {
+ LOG.warn("Tx: {} Error during phase {}, starting Abort", tx.getIdentifier(), phase, e);
+ abortBlocking(e);
+ throw e;
+ } finally {
+ if (commitStatTracker != null) {
+ commitStatTracker.addDuration(System.nanoTime() - startTime);
+ }
+ }
+ }
+
+ /**
+ *
+ * Invokes canCommit on underlying cohorts and blocks till
+ * all results are returned.
+ *
+ * Valid state transition is from SUBMITTED to CAN_COMMIT,
+ * if currentPhase is not SUBMITTED throws IllegalStateException.
+ *
+ * @throws TransactionCommitFailedException
+ * If one of cohorts failed can Commit
+ *
+ */
+ private void canCommitBlocking() throws TransactionCommitFailedException {
+ for (final ListenableFuture<?> canCommit : canCommitAll()) {
+ try {
+ final Boolean result = (Boolean)canCommit.get();
+ if (result == null || !result) {
+ throw new TransactionCommitFailedException("Can Commit failed, no detailed cause available.");
+ }
+ } catch (InterruptedException | ExecutionException e) {
+ throw TransactionCommitFailedExceptionMapper.CAN_COMMIT_ERROR_MAPPER.apply(e);
+ }
+ }
+ }
+
+ /**
+ *
+ * Invokes canCommit on underlying cohorts and returns composite future
+ * which will contains {@link Boolean#TRUE} only and only if
+ * all cohorts returned true.
+ *
+ * Valid state transition is from SUBMITTED to CAN_COMMIT,
+ * if currentPhase is not SUBMITTED throws IllegalStateException.
+ *
+ * @return List of all cohorts futures from can commit phase.
+ *
+ */
+ private ListenableFuture<?>[] canCommitAll() {
+ final ListenableFuture<?>[] ops = new ListenableFuture<?>[cohorts.size()];
+ int i = 0;
+ for (final DOMStoreThreePhaseCommitCohort cohort : cohorts) {
+ ops[i++] = cohort.canCommit();
+ }
+ return ops;
+ }
+
+ /**
+ *
+ * Invokes preCommit on underlying cohorts and blocks till
+ * all results are returned.
+ *
+ * Valid state transition is from CAN_COMMIT to PRE_COMMIT, if current
+ * state is not CAN_COMMIT
+ * throws IllegalStateException.
+ *
+ * @throws TransactionCommitFailedException
+ * If one of cohorts failed preCommit
+ *
+ */
+ private void preCommitBlocking() throws TransactionCommitFailedException {
+ final ListenableFuture<?>[] preCommitFutures = preCommitAll();
+ try {
+ for(final ListenableFuture<?> future : preCommitFutures) {
+ future.get();
+ }
+ } catch (InterruptedException | ExecutionException e) {
+ throw TransactionCommitFailedExceptionMapper.PRE_COMMIT_MAPPER.apply(e);
+ }
+ }
+
+ /**
+ *
+ * Invokes preCommit on underlying cohorts and returns future
+ * which will complete once all preCommit on cohorts completed or
+ * failed.
+ *
+ *
+ * Valid state transition is from CAN_COMMIT to PRE_COMMIT, if current
+ * state is not CAN_COMMIT
+ * throws IllegalStateException.
+ *
+ * @return List of all cohorts futures from can commit phase.
+ *
+ */
+ private ListenableFuture<?>[] preCommitAll() {
+ final ListenableFuture<?>[] ops = new ListenableFuture<?>[cohorts.size()];
+ int i = 0;
+ for (final DOMStoreThreePhaseCommitCohort cohort : cohorts) {
+ ops[i++] = cohort.preCommit();
+ }
+ return ops;
+ }
+
+ /**
+ *
+ * Invokes commit on underlying cohorts and blocks till
+ * all results are returned.
+ *
+ * Valid state transition is from PRE_COMMIT to COMMIT, if not throws
+ * IllegalStateException.
+ *
+ * @throws TransactionCommitFailedException
+ * If one of cohorts failed preCommit
+ *
+ */
+ private void commitBlocking() throws TransactionCommitFailedException {
+ final ListenableFuture<?>[] commitFutures = commitAll();
+ try {
+ for(final ListenableFuture<?> future : commitFutures) {
+ future.get();
+ }
+ } catch (InterruptedException | ExecutionException e) {
+ throw TransactionCommitFailedExceptionMapper.COMMIT_ERROR_MAPPER.apply(e);
+ }
+ }
+
+ /**
+ *
+ * Invokes commit on underlying cohorts and returns future which
+ * completes
+ * once all commits on cohorts are completed.
+ *
+ * Valid state transition is from PRE_COMMIT to COMMIT, if not throws
+ * IllegalStateException
+ *
+ * @return List of all cohorts futures from can commit phase.
+ */
+ private ListenableFuture<?>[] commitAll() {
+ final ListenableFuture<?>[] ops = new ListenableFuture<?>[cohorts.size()];
+ int i = 0;
+ for (final DOMStoreThreePhaseCommitCohort cohort : cohorts) {
+ ops[i++] = cohort.commit();
+ }
+ return ops;
+ }
+
+ /**
+ * Aborts transaction.
+ *
+ * Invokes {@link DOMStoreThreePhaseCommitCohort#abort()} on all
+ * cohorts, blocks
+ * for all results. If any of the abort failed throws
+ * IllegalStateException,
+ * which will contains originalCause as suppressed Exception.
+ *
+ * If aborts we're successful throws supplied exception
+ *
+ * @param originalCause
+ * Exception which should be used to fail transaction for
+ * consumers of transaction
+ * future and listeners of transaction failure.
+ * @param phase phase in which the problem ensued
+ * @throws TransactionCommitFailedException
+ * on invocation of this method.
+ * originalCa
+ * @throws IllegalStateException
+ * if abort failed.
+ */
+ private void abortBlocking(final TransactionCommitFailedException originalCause) throws TransactionCommitFailedException {
+ Exception cause = originalCause;
+ try {
+ abortAsyncAll().get();
+ } catch (InterruptedException | ExecutionException e) {
+ LOG.error("Tx: {} Error during Abort.", tx.getIdentifier(), e);
+ cause = new IllegalStateException("Abort failed.", e);
+ cause.addSuppressed(e);
+ }
+ Throwables.propagateIfPossible(cause, TransactionCommitFailedException.class);
+ }
+
+ /**
+ * Invokes abort on underlying cohorts and returns future which
+ * completes once all abort on cohorts are completed.
+ *
+ * @return Future which will complete once all cohorts completed
+ * abort.
+ */
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ private ListenableFuture<Void> abortAsyncAll() {
+
+ final ListenableFuture<?>[] ops = new ListenableFuture<?>[cohorts.size()];
+ int i = 0;
+ for (final DOMStoreThreePhaseCommitCohort cohort : cohorts) {
+ ops[i++] = cohort.abort();
+ }
+
+ /*
+ * We are returning all futures as list, not only succeeded ones in
+ * order to fail composite future if any of them failed.
+ * See Futures.allAsList for this description.
+ */
+ return (ListenableFuture) Futures.allAsList(ops);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * NormalizedNode implementation of {@link org.opendaylight.controller.md.sal.common.api.data.TransactionChain} which is backed
+ * by several {@link DOMStoreTransactionChain} differentiated by provided
+ * {@link org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType} type.
+ *
+ */
+final class DOMDataBrokerTransactionChainImpl extends AbstractDOMForwardedTransactionFactory<DOMStoreTransactionChain>
+ implements DOMTransactionChain {
+ private static enum State {
+ RUNNING,
+ CLOSING,
+ CLOSED,
+ FAILED,
+ }
+
+ private static final AtomicIntegerFieldUpdater<DOMDataBrokerTransactionChainImpl> COUNTER_UPDATER =
+ AtomicIntegerFieldUpdater.newUpdater(DOMDataBrokerTransactionChainImpl.class, "counter");
+ private static final AtomicReferenceFieldUpdater<DOMDataBrokerTransactionChainImpl, State> STATE_UPDATER =
+ AtomicReferenceFieldUpdater.newUpdater(DOMDataBrokerTransactionChainImpl.class, State.class, "state");
+ private static final Logger LOG = LoggerFactory.getLogger(DOMDataBrokerTransactionChainImpl.class);
+ private final AtomicLong txNum = new AtomicLong();
+ private final AbstractDOMDataBroker broker;
+ private final TransactionChainListener listener;
+ private final long chainId;
+
+ private volatile State state = State.RUNNING;
+ private volatile int counter = 0;
+
+ /**
+ *
+ * @param chainId
+ * ID of transaction chain
+ * @param chains
+ * Backing {@link DOMStoreTransactionChain}s.
+ * @param coordinator
+ * Commit Coordinator which should be used to coordinate commits
+ * of transaction
+ * produced by this chain.
+ * @param listener
+ * Listener, which listens on transaction chain events.
+ * @throws NullPointerException
+ * If any of arguments is null.
+ */
+ public DOMDataBrokerTransactionChainImpl(final long chainId,
+ final Map<LogicalDatastoreType, DOMStoreTransactionChain> chains,
+ final AbstractDOMDataBroker broker, final TransactionChainListener listener) {
+ super(chains);
+ this.chainId = chainId;
+ this.broker = Preconditions.checkNotNull(broker);
+ this.listener = Preconditions.checkNotNull(listener);
+ }
+
+ private void checkNotFailed() {
+ Preconditions.checkState(state != State.FAILED, "Transaction chain has failed");
+ }
+
+ @Override
+ protected Object newTransactionIdentifier() {
+ return "DOM-CHAIN-" + chainId + "-" + txNum.getAndIncrement();
+ }
+
+ @Override
+ public CheckedFuture<Void, TransactionCommitFailedException> submit(
+ final DOMDataWriteTransaction transaction, final Collection<DOMStoreThreePhaseCommitCohort> cohorts) {
+ checkNotFailed();
+ checkNotClosed();
+
+ final CheckedFuture<Void, TransactionCommitFailedException> ret = broker.submit(transaction, cohorts);
+
+ COUNTER_UPDATER.incrementAndGet(this);
+ Futures.addCallback(ret, new FutureCallback<Void>() {
+ @Override
+ public void onSuccess(final Void result) {
+ transactionCompleted();
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ transactionFailed(transaction, t);
+ }
+ });
+
+ return ret;
+ }
+
+ @Override
+ public void close() {
+ final boolean success = STATE_UPDATER.compareAndSet(this, State.RUNNING, State.CLOSING);
+ if (!success) {
+ LOG.debug("Chain {} is no longer running", this);
+ return;
+ }
+
+ super.close();
+ for (DOMStoreTransactionChain subChain : getTxFactories().values()) {
+ subChain.close();
+ }
+
+ if (counter == 0) {
+ finishClose();
+ }
+ }
+
+ private void finishClose() {
+ state = State.CLOSED;
+ listener.onTransactionChainSuccessful(this);
+ }
+
+ private void transactionCompleted() {
+ if (COUNTER_UPDATER.decrementAndGet(this) == 0 && state == State.CLOSING) {
+ finishClose();
+ }
+ }
+
+ private void transactionFailed(final DOMDataWriteTransaction tx, final Throwable cause) {
+ state = State.FAILED;
+ LOG.debug("Transaction chain {}Â failed.", this, cause);
+ listener.onTransactionChainFailed(this, tx, cause);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import java.util.Map;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * Read Only Transaction, which is composed of several
+ * {@link DOMStoreReadTransaction} transactions. Subtransaction is selected by
+ * {@link LogicalDatastoreType} type parameter in
+ * {@link #read(LogicalDatastoreType, YangInstanceIdentifier)}.
+ */
+class DOMForwardedReadOnlyTransaction extends
+ AbstractDOMForwardedCompositeTransaction<LogicalDatastoreType, DOMStoreReadTransaction> implements
+ DOMDataReadOnlyTransaction {
+
+ protected DOMForwardedReadOnlyTransaction(final Object identifier,
+ final Map<LogicalDatastoreType, DOMStoreReadTransaction> backingTxs) {
+ super(identifier, backingTxs);
+ }
+
+ @Override
+ public CheckedFuture<Optional<NormalizedNode<?,?>>, ReadFailedException> read(
+ final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+ return getSubtransaction(store).read(path);
+ }
+
+ @Override
+ public CheckedFuture<Boolean, ReadFailedException> exists(
+ final LogicalDatastoreType store,
+ final YangInstanceIdentifier path) {
+ return getSubtransaction(store).exists(path);
+ }
+
+ @Override
+ public void close() {
+ closeSubtransactions();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import java.util.Map;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ *
+ * Read-Write Transaction, which is composed of several
+ * {@link DOMStoreReadWriteTransaction} transactions. Subtransaction is selected by
+ * {@link LogicalDatastoreType} type parameter in:
+ *
+ * <ul>
+ * <li>{@link #read(LogicalDatastoreType, YangInstanceIdentifier)}
+ * <li>{@link #put(LogicalDatastoreType, YangInstanceIdentifier, NormalizedNode)}
+ * <li>{@link #delete(LogicalDatastoreType, YangInstanceIdentifier)}
+ * <li>{@link #merge(LogicalDatastoreType, YangInstanceIdentifier, NormalizedNode)}
+ * </ul>
+ * {@link #commit()} will result in invocation of
+ * {@link DOMDataCommitImplementation#submit(org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction, Iterable)}
+ * invocation with all {@link org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort} for underlying
+ * transactions.
+ *
+ */
+final class DOMForwardedReadWriteTransaction extends DOMForwardedWriteTransaction<DOMStoreReadWriteTransaction> implements DOMDataReadWriteTransaction {
+ protected DOMForwardedReadWriteTransaction(final Object identifier,
+ final Map<LogicalDatastoreType, DOMStoreReadWriteTransaction> backingTxs,
+ final AbstractDOMForwardedTransactionFactory<?> commitImpl) {
+ super(identifier, backingTxs, commitImpl);
+ }
+
+ @Override
+ public CheckedFuture<Optional<NormalizedNode<?,?>>, ReadFailedException> read(
+ final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+ return getSubtransaction(store).read(path);
+ }
+
+ @Override
+ public CheckedFuture<Boolean, ReadFailedException> exists(
+ final LogicalDatastoreType store,
+ final YangInstanceIdentifier path) {
+ return getSubtransaction(store).exists(path);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.common.impl.service.AbstractDataTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Read-Write Transaction, which is composed of several
+ * {@link DOMStoreWriteTransaction} transactions. A sub-transaction is selected by
+ * {@link LogicalDatastoreType} type parameter in:
+ *
+ * <ul>
+ * <li>{@link #put(LogicalDatastoreType, YangInstanceIdentifier, NormalizedNode)}
+ * <li>{@link #delete(LogicalDatastoreType, YangInstanceIdentifier)}
+ * <li>{@link #merge(LogicalDatastoreType, YangInstanceIdentifier, NormalizedNode)}
+ * </ul>
+ * <p>
+ * {@link #commit()} will result in invocation of
+ * {@link DOMDataCommitImplementation#submit(org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction, Iterable)}
+ * invocation with all {@link org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort} for underlying
+ * transactions.
+ *
+ * @param <T> Subtype of {@link DOMStoreWriteTransaction} which is used as
+ * subtransaction.
+ */
+class DOMForwardedWriteTransaction<T extends DOMStoreWriteTransaction> extends
+ AbstractDOMForwardedCompositeTransaction<LogicalDatastoreType, T> implements DOMDataWriteTransaction {
+ @SuppressWarnings("rawtypes")
+ private static final AtomicReferenceFieldUpdater<DOMForwardedWriteTransaction, AbstractDOMForwardedTransactionFactory> IMPL_UPDATER =
+ AtomicReferenceFieldUpdater.newUpdater(DOMForwardedWriteTransaction.class, AbstractDOMForwardedTransactionFactory.class, "commitImpl");
+ @SuppressWarnings("rawtypes")
+ private static final AtomicReferenceFieldUpdater<DOMForwardedWriteTransaction, Future> FUTURE_UPDATER =
+ AtomicReferenceFieldUpdater.newUpdater(DOMForwardedWriteTransaction.class, Future.class, "commitFuture");
+ private static final Logger LOG = LoggerFactory.getLogger(DOMForwardedWriteTransaction.class);
+ private static final Future<?> CANCELLED_FUTURE = Futures.immediateCancelledFuture();
+
+ /**
+ * Implementation of real commit. It also acts as an indication that
+ * the transaction is running -- which we flip atomically using
+ * {@link #IMPL_UPDATER}.
+ */
+ private volatile AbstractDOMForwardedTransactionFactory<?> commitImpl;
+
+ /**
+ * Future task of transaction commit. It starts off as null, but is
+ * set appropriately on {@link #submit()} and {@link #cancel()} via
+ * {@link AtomicReferenceFieldUpdater#lazySet(Object, Object)}.
+ *
+ * Lazy set is safe for use because it is only referenced to in the
+ * {@link #cancel()} slow path, where we will busy-wait for it. The
+ * fast path gets the benefit of a store-store barrier instead of the
+ * usual store-load barrier.
+ */
+ private volatile Future<?> commitFuture;
+
+ protected DOMForwardedWriteTransaction(final Object identifier,
+ final Map<LogicalDatastoreType, T> backingTxs, final AbstractDOMForwardedTransactionFactory<?> commitImpl) {
+ super(identifier, backingTxs);
+ this.commitImpl = Preconditions.checkNotNull(commitImpl, "commitImpl must not be null.");
+ }
+
+ @Override
+ public void put(final LogicalDatastoreType store, final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
+ checkRunning(commitImpl);
+ getSubtransaction(store).write(path, data);
+ }
+
+ @Override
+ public void delete(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+ checkRunning(commitImpl);
+ getSubtransaction(store).delete(path);
+ }
+
+ @Override
+ public void merge(final LogicalDatastoreType store, final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
+ checkRunning(commitImpl);
+ getSubtransaction(store).merge(path, data);
+ }
+
+ @Override
+ public boolean cancel() {
+ final AbstractDOMForwardedTransactionFactory<?> impl = IMPL_UPDATER.getAndSet(this, null);
+ if (impl != null) {
+ LOG.trace("Transaction {} cancelled before submit", getIdentifier());
+ FUTURE_UPDATER.lazySet(this, CANCELLED_FUTURE);
+ closeSubtransactions();
+ return true;
+ }
+
+ // The transaction is in process of being submitted or cancelled. Busy-wait
+ // for the corresponding future.
+ Future<?> future;
+ do {
+ future = commitFuture;
+ } while (future == null);
+
+ return future.cancel(false);
+ }
+
+ @Deprecated
+ @Override
+ public ListenableFuture<RpcResult<TransactionStatus>> commit() {
+ return AbstractDataTransaction.convertToLegacyCommitFuture(submit());
+ }
+
+ @Override
+ public CheckedFuture<Void, TransactionCommitFailedException> submit() {
+ final AbstractDOMForwardedTransactionFactory<?> impl = IMPL_UPDATER.getAndSet(this, null);
+ checkRunning(impl);
+
+ final Collection<T> txns = getSubtransactions();
+ final Collection<DOMStoreThreePhaseCommitCohort> cohorts = new ArrayList<>(txns.size());
+
+ // FIXME: deal with errors thrown by backed (ready and submit can fail in theory)
+ for (DOMStoreWriteTransaction txn : txns) {
+ cohorts.add(txn.ready());
+ }
+
+ final CheckedFuture<Void, TransactionCommitFailedException> ret = impl.submit(this, cohorts);
+ FUTURE_UPDATER.lazySet(this, ret);
+ return ret;
+ }
+
+ private void checkRunning(final AbstractDOMForwardedTransactionFactory<?> impl) {
+ Preconditions.checkState(impl != null, "Transaction %s is no longer running", getIdentifier());
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableMultimap.Builder;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Multimaps;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.lmax.disruptor.EventHandler;
+import com.lmax.disruptor.InsufficientCapacityException;
+import com.lmax.disruptor.PhasedBackoffWaitStrategy;
+import com.lmax.disruptor.WaitStrategy;
+import com.lmax.disruptor.dsl.Disruptor;
+import com.lmax.disruptor.dsl.ProducerType;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationPublishService;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService;
+import org.opendaylight.controller.md.sal.dom.spi.DOMNotificationSubscriptionListener;
+import org.opendaylight.controller.md.sal.dom.spi.DOMNotificationSubscriptionListenerRegistry;
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.util.ListenerRegistry;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Joint implementation of {@link DOMNotificationPublishService} and {@link DOMNotificationService}. Provides
+ * routing of notifications from publishers to subscribers.
+ *
+ * Internal implementation works by allocating a two-handler Disruptor. The first handler delivers notifications
+ * to subscribed listeners and the second one notifies whoever may be listening on the returned future. Registration
+ * state tracking is performed by a simple immutable multimap -- when a registration or unregistration occurs we
+ * re-generate the entire map from scratch and set it atomically. While registrations/unregistrations synchronize
+ * on this instance, notifications do not take any locks here.
+ *
+ * The fully-blocking {@link #publish(long, DOMNotification, Collection)} and non-blocking {@link #offerNotification(DOMNotification)}
+ * are realized using the Disruptor's native operations. The bounded-blocking {@link #offerNotification(DOMNotification, long, TimeUnit)}
+ * is realized by arming a background wakeup interrupt.
+ */
+public final class DOMNotificationRouter implements AutoCloseable, DOMNotificationPublishService,
+ DOMNotificationService, DOMNotificationSubscriptionListenerRegistry {
+
+ private static final Logger LOG = LoggerFactory.getLogger(DOMNotificationRouter.class);
+ private static final ListenableFuture<Void> NO_LISTENERS = Futures.immediateFuture(null);
+ private static final WaitStrategy DEFAULT_STRATEGY = PhasedBackoffWaitStrategy.withLock(1L, 30L, TimeUnit.MILLISECONDS);
+ private static final EventHandler<DOMNotificationRouterEvent> DISPATCH_NOTIFICATIONS = new EventHandler<DOMNotificationRouterEvent>() {
+ @Override
+ public void onEvent(final DOMNotificationRouterEvent event, final long sequence, final boolean endOfBatch) throws Exception {
+ event.deliverNotification();
+
+ }
+ };
+ private static final EventHandler<DOMNotificationRouterEvent> NOTIFY_FUTURE = new EventHandler<DOMNotificationRouterEvent>() {
+ @Override
+ public void onEvent(final DOMNotificationRouterEvent event, final long sequence, final boolean endOfBatch) {
+ event.setFuture();
+ }
+ };
+
+ private final Disruptor<DOMNotificationRouterEvent> disruptor;
+ private final ExecutorService executor;
+ private volatile Multimap<SchemaPath, ListenerRegistration<? extends DOMNotificationListener>> listeners = ImmutableMultimap.of();
+ private final ListenerRegistry<DOMNotificationSubscriptionListener> subscriptionListeners = ListenerRegistry.create();
+
+ @SuppressWarnings("unchecked")
+ private DOMNotificationRouter(final ExecutorService executor, final int queueDepth, final WaitStrategy strategy) {
+ this.executor = Preconditions.checkNotNull(executor);
+
+ disruptor = new Disruptor<>(DOMNotificationRouterEvent.FACTORY, queueDepth, executor, ProducerType.MULTI, strategy);
+ disruptor.handleEventsWith(DISPATCH_NOTIFICATIONS);
+ disruptor.after(DISPATCH_NOTIFICATIONS).handleEventsWith(NOTIFY_FUTURE);
+ disruptor.start();
+ }
+
+ public static DOMNotificationRouter create(final int queueDepth) {
+ final ExecutorService executor = Executors.newCachedThreadPool();
+
+ return new DOMNotificationRouter(executor, queueDepth, DEFAULT_STRATEGY);
+ }
+
+ public static DOMNotificationRouter create(final int queueDepth, final long spinTime, final long parkTime, final TimeUnit unit) {
+ final ExecutorService executor = Executors.newCachedThreadPool();
+ final WaitStrategy strategy = PhasedBackoffWaitStrategy.withLock(spinTime, parkTime, unit);
+
+ return new DOMNotificationRouter(executor, queueDepth, strategy);
+ }
+
+ @Override
+ public synchronized <T extends DOMNotificationListener> ListenerRegistration<T> registerNotificationListener(final T listener, final Collection<SchemaPath> types) {
+ final ListenerRegistration<T> reg = new AbstractListenerRegistration<T>(listener) {
+ @Override
+ protected void removeRegistration() {
+ final ListenerRegistration<T> me = this;
+
+ synchronized (DOMNotificationRouter.this) {
+ replaceListeners(ImmutableMultimap.copyOf(Multimaps.filterValues(listeners, new Predicate<ListenerRegistration<? extends DOMNotificationListener>>() {
+ @Override
+ public boolean apply(final ListenerRegistration<? extends DOMNotificationListener> input) {
+ return input != me;
+ }
+ })));
+ }
+ }
+ };
+
+ if (!types.isEmpty()) {
+ final Builder<SchemaPath, ListenerRegistration<? extends DOMNotificationListener>> b = ImmutableMultimap.builder();
+ b.putAll(listeners);
+
+ for (final SchemaPath t : types) {
+ b.put(t, reg);
+ }
+
+ replaceListeners(b.build());
+ }
+
+ return reg;
+ }
+
+ @Override
+ public <T extends DOMNotificationListener> ListenerRegistration<T> registerNotificationListener(final T listener, final SchemaPath... types) {
+ return registerNotificationListener(listener, Arrays.asList(types));
+ }
+
+ /**
+ * Swaps registered listeners and triggers notification update
+ *
+ * @param newListeners
+ */
+ private void replaceListeners(
+ final Multimap<SchemaPath, ListenerRegistration<? extends DOMNotificationListener>> newListeners) {
+ listeners = newListeners;
+ notifyListenerTypesChanged(newListeners.keySet());
+ }
+
+ private void notifyListenerTypesChanged(final Set<SchemaPath> typesAfter) {
+ final List<ListenerRegistration<DOMNotificationSubscriptionListener>> listenersAfter =ImmutableList.copyOf(subscriptionListeners.getListeners());
+ executor.submit(new Runnable() {
+
+ @Override
+ public void run() {
+ for (final ListenerRegistration<DOMNotificationSubscriptionListener> subListener : listenersAfter) {
+ try {
+ subListener.getInstance().onSubscriptionChanged(typesAfter);
+ } catch (final Exception e) {
+ LOG.warn("Uncaught exception during invoking listener {}", subListener.getInstance(), e);
+ }
+ }
+ }
+ });
+ }
+
+ @Override
+ public <L extends DOMNotificationSubscriptionListener> ListenerRegistration<L> registerSubscriptionListener(
+ final L listener) {
+ final Set<SchemaPath> initialTypes = listeners.keySet();
+ executor.submit(new Runnable() {
+
+ @Override
+ public void run() {
+ listener.onSubscriptionChanged(initialTypes);
+ }
+ });
+ return subscriptionListeners.registerWithType(listener);
+ }
+
+ private ListenableFuture<Void> publish(final long seq, final DOMNotification notification, final Collection<ListenerRegistration<? extends DOMNotificationListener>> subscribers) {
+ final DOMNotificationRouterEvent event = disruptor.get(seq);
+ final ListenableFuture<Void> future = event.initialize(notification, subscribers);
+ disruptor.getRingBuffer().publish(seq);
+ return future;
+ }
+
+ @Override
+ public ListenableFuture<? extends Object> putNotification(final DOMNotification notification) throws InterruptedException {
+ final Collection<ListenerRegistration<? extends DOMNotificationListener>> subscribers = listeners.get(notification.getType());
+ if (subscribers.isEmpty()) {
+ return NO_LISTENERS;
+ }
+
+ final long seq = disruptor.getRingBuffer().next();
+ return publish(seq, notification, subscribers);
+ }
+
+ private ListenableFuture<? extends Object> tryPublish(final DOMNotification notification, final Collection<ListenerRegistration<? extends DOMNotificationListener>> subscribers) {
+ final long seq;
+ try {
+ seq = disruptor.getRingBuffer().tryNext();
+ } catch (final InsufficientCapacityException e) {
+ return DOMNotificationPublishService.REJECTED;
+ }
+
+ return publish(seq, notification, subscribers);
+ }
+
+ @Override
+ public ListenableFuture<? extends Object> offerNotification(final DOMNotification notification) {
+ final Collection<ListenerRegistration<? extends DOMNotificationListener>> subscribers = listeners.get(notification.getType());
+ if (subscribers.isEmpty()) {
+ return NO_LISTENERS;
+ }
+
+ return tryPublish(notification, subscribers);
+ }
+
+ @Override
+ public ListenableFuture<? extends Object> offerNotification(final DOMNotification notification, final long timeout,
+ final TimeUnit unit) throws InterruptedException {
+ final Collection<ListenerRegistration<? extends DOMNotificationListener>> subscribers = listeners.get(notification.getType());
+ if (subscribers.isEmpty()) {
+ return NO_LISTENERS;
+ }
+
+ // Attempt to perform a non-blocking publish first
+ final ListenableFuture<? extends Object> noBlock = tryPublish(notification, subscribers);
+ if (!DOMNotificationPublishService.REJECTED.equals(noBlock)) {
+ return noBlock;
+ }
+
+ /*
+ * FIXME: we need a background thread, which will watch out for blocking too long. Here
+ * we will arm a tasklet for it and synchronize delivery of interrupt properly.
+ */
+ throw new UnsupportedOperationException("Not implemented yet");
+ }
+
+ @Override
+ public void close() {
+ disruptor.shutdown();
+ executor.shutdown();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
+import com.lmax.disruptor.EventFactory;
+import java.util.Collection;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+
+/**
+ * A single notification event in the disruptor ringbuffer. These objects are reused,
+ * so they do have mutable state.
+ */
+final class DOMNotificationRouterEvent {
+ public static final EventFactory<DOMNotificationRouterEvent> FACTORY = new EventFactory<DOMNotificationRouterEvent>() {
+ @Override
+ public DOMNotificationRouterEvent newInstance() {
+ return new DOMNotificationRouterEvent();
+ }
+ };
+
+ private Collection<ListenerRegistration<? extends DOMNotificationListener>> subscribers;
+ private DOMNotification notification;
+ private SettableFuture<Void> future;
+
+ private DOMNotificationRouterEvent() {
+ // Hidden on purpose, initialized in initialize()
+ }
+
+ ListenableFuture<Void> initialize(final DOMNotification notification, final Collection<ListenerRegistration<? extends DOMNotificationListener>> subscribers) {
+ this.notification = Preconditions.checkNotNull(notification);
+ this.subscribers = Preconditions.checkNotNull(subscribers);
+ this.future = SettableFuture.create();
+ return this.future;
+ }
+
+ void deliverNotification() {
+ for (ListenerRegistration<? extends DOMNotificationListener> r : subscribers) {
+ final DOMNotificationListener l = r.getInstance();
+ if (l != null) {
+ l.onNotification(notification);
+ }
+ }
+ }
+
+ void setFuture() {
+ future.set(null);
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import javax.annotation.concurrent.GuardedBy;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcAvailabilityListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementation;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationRegistration;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcProviderService;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
+import org.opendaylight.controller.md.sal.dom.spi.AbstractDOMRpcImplementationRegistration;
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+public final class DOMRpcRouter implements AutoCloseable, DOMRpcService, DOMRpcProviderService, SchemaContextListener {
+ private static final ThreadFactory THREAD_FACTORY = new ThreadFactoryBuilder().setNameFormat("DOMRpcRouter-listener-%s").setDaemon(true).build();
+ private final ExecutorService listenerNotifier = Executors.newSingleThreadExecutor(THREAD_FACTORY);
+ @GuardedBy("this")
+ private Collection<ListenerRegistration<? extends DOMRpcAvailabilityListener>> listeners = Collections.emptyList();
+ private volatile DOMRpcRoutingTable routingTable = DOMRpcRoutingTable.EMPTY;
+
+ @Override
+ public <T extends DOMRpcImplementation> DOMRpcImplementationRegistration<T> registerRpcImplementation(final T implementation, final DOMRpcIdentifier... rpcs) {
+ return registerRpcImplementation(implementation, ImmutableSet.copyOf(rpcs));
+ }
+
+ private static Collection<DOMRpcIdentifier> notPresentRpcs(final DOMRpcRoutingTable table, final Collection<DOMRpcIdentifier> candidates) {
+ return ImmutableSet.copyOf(Collections2.filter(candidates, new Predicate<DOMRpcIdentifier>() {
+ @Override
+ public boolean apply(final DOMRpcIdentifier input) {
+ return !table.contains(input);
+ }
+ }));
+ }
+
+ private synchronized void removeRpcImplementation(final DOMRpcImplementation implementation, final Set<DOMRpcIdentifier> rpcs) {
+ final DOMRpcRoutingTable oldTable = routingTable;
+ final DOMRpcRoutingTable newTable = oldTable.remove(implementation, rpcs);
+
+ final Collection<DOMRpcIdentifier> removedRpcs = notPresentRpcs(newTable, rpcs);
+ routingTable = newTable;
+ if(!removedRpcs.isEmpty()) {
+ final Collection<ListenerRegistration<? extends DOMRpcAvailabilityListener>> capturedListeners = listeners;
+ listenerNotifier.execute(new Runnable() {
+ @Override
+ public void run() {
+ for (final ListenerRegistration<? extends DOMRpcAvailabilityListener> l : capturedListeners) {
+ // Need to ensure removed listeners do not get notified
+ synchronized (DOMRpcRouter.this) {
+ if (listeners.contains(l)) {
+ l.getInstance().onRpcUnavailable(removedRpcs);
+ }
+ }
+ }
+ }
+ });
+ }
+ }
+
+ @Override
+ public synchronized <T extends DOMRpcImplementation> DOMRpcImplementationRegistration<T> registerRpcImplementation(final T implementation, final Set<DOMRpcIdentifier> rpcs) {
+ final DOMRpcRoutingTable oldTable = routingTable;
+ final DOMRpcRoutingTable newTable = oldTable.add(implementation, rpcs);
+
+ final Collection<DOMRpcIdentifier> addedRpcs = notPresentRpcs(oldTable, rpcs);
+ routingTable = newTable;
+
+ if(!addedRpcs.isEmpty()) {
+ final Collection<ListenerRegistration<? extends DOMRpcAvailabilityListener>> capturedListeners = listeners;
+ listenerNotifier.execute(new Runnable() {
+ @Override
+ public void run() {
+ for (final ListenerRegistration<? extends DOMRpcAvailabilityListener> l : capturedListeners) {
+ // Need to ensure removed listeners do not get notified
+ synchronized (DOMRpcRouter.this) {
+ if (listeners.contains(l)) {
+ l.getInstance().onRpcAvailable(addedRpcs);
+ }
+ }
+ }
+ }
+ });
+ }
+
+ return new AbstractDOMRpcImplementationRegistration<T>(implementation) {
+ @Override
+ protected void removeRegistration() {
+ removeRpcImplementation(getInstance(), rpcs);
+ }
+ };
+ }
+
+ @Override
+ public CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(final SchemaPath type, final NormalizedNode<?, ?> input) {
+ return routingTable.invokeRpc(type, input);
+ }
+
+ private synchronized void removeListener(final ListenerRegistration<? extends DOMRpcAvailabilityListener> reg) {
+ listeners = ImmutableList.copyOf(Collections2.filter(listeners, new Predicate<Object>() {
+ @Override
+ public boolean apply(final Object input) {
+ return !reg.equals(input);
+ }
+ }));
+ }
+
+ @Override
+ public synchronized <T extends DOMRpcAvailabilityListener> ListenerRegistration<T> registerRpcListener(final T listener) {
+ final ListenerRegistration<T> ret = new AbstractListenerRegistration<T>(listener) {
+ @Override
+ protected void removeRegistration() {
+ removeListener(this);
+ }
+ };
+
+ final Builder<ListenerRegistration<? extends DOMRpcAvailabilityListener>> b = ImmutableList.builder();
+ b.addAll(listeners);
+ b.add(ret);
+ listeners = b.build();
+ final Map<SchemaPath, Set<YangInstanceIdentifier>> capturedRpcs = routingTable.getRpcs();
+
+ listenerNotifier.execute(new Runnable() {
+ @Override
+ public void run() {
+ for (final Entry<SchemaPath, Set<YangInstanceIdentifier>> e : capturedRpcs.entrySet()) {
+ listener.onRpcAvailable(Collections2.transform(e.getValue(), new Function<YangInstanceIdentifier, DOMRpcIdentifier>() {
+ @Override
+ public DOMRpcIdentifier apply(final YangInstanceIdentifier input) {
+ return DOMRpcIdentifier.create(e.getKey(), input);
+ }
+ }));
+ }
+ }
+ });
+
+ return ret;
+ }
+
+ @Override
+ public synchronized void onGlobalContextUpdated(final SchemaContext context) {
+ final DOMRpcRoutingTable oldTable = routingTable;
+ final DOMRpcRoutingTable newTable = oldTable.setSchemaContext(context);
+ routingTable = newTable;
+ }
+
+ @Override
+ public void close() {
+ listenerNotifier.shutdown();
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+import com.google.common.collect.LinkedListMultimap;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.Maps;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementation;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationNotAvailableException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+
+final class DOMRpcRoutingTable {
+ private static final QName CONTEXT_REFERENCE = QName.cachedReference(QName.create("urn:opendaylight:yang:extension:yang-ext", "2013-07-09", "context-reference"));
+
+ static final DOMRpcRoutingTable EMPTY = new DOMRpcRoutingTable();
+ private static final Function<AbstractDOMRpcRoutingTableEntry, Set<YangInstanceIdentifier>> EXTRACT_IDENTIFIERS =
+ new Function<AbstractDOMRpcRoutingTableEntry, Set<YangInstanceIdentifier>>() {
+ @Override
+ public Set<YangInstanceIdentifier> apply(final AbstractDOMRpcRoutingTableEntry input) {
+ return input.registeredIdentifiers();
+ }
+ };
+ private final Map<SchemaPath, AbstractDOMRpcRoutingTableEntry> rpcs;
+ private final SchemaContext schemaContext;
+
+ private DOMRpcRoutingTable() {
+ rpcs = Collections.emptyMap();
+ schemaContext = null;
+ }
+
+ private DOMRpcRoutingTable(final Map<SchemaPath, AbstractDOMRpcRoutingTableEntry> rpcs, final SchemaContext schemaContext) {
+ this.rpcs = Preconditions.checkNotNull(rpcs);
+ this.schemaContext = schemaContext;
+ }
+
+ private static ListMultimap<SchemaPath, YangInstanceIdentifier> decomposeIdentifiers(final Set<DOMRpcIdentifier> rpcs) {
+ final ListMultimap<SchemaPath, YangInstanceIdentifier> ret = LinkedListMultimap.create();
+ for (DOMRpcIdentifier i : rpcs) {
+ ret.put(i.getType(), i.getContextReference());
+ }
+ return ret;
+ }
+
+ DOMRpcRoutingTable add(final DOMRpcImplementation implementation, final Set<DOMRpcIdentifier> rpcs) {
+ if (rpcs.isEmpty()) {
+ return this;
+ }
+
+ // First decompose the identifiers to a multimap
+ final ListMultimap<SchemaPath, YangInstanceIdentifier> toAdd = decomposeIdentifiers(rpcs);
+
+ // Now iterate over existing entries, modifying them as appropriate...
+ final Builder<SchemaPath, AbstractDOMRpcRoutingTableEntry> mb = ImmutableMap.builder();
+ for (Entry<SchemaPath, AbstractDOMRpcRoutingTableEntry> re : this.rpcs.entrySet()) {
+ List<YangInstanceIdentifier> newRpcs = new ArrayList<>(toAdd.removeAll(re.getKey()));
+ if (!newRpcs.isEmpty()) {
+ final AbstractDOMRpcRoutingTableEntry ne = re.getValue().add(implementation, newRpcs);
+ mb.put(re.getKey(), ne);
+ } else {
+ mb.put(re);
+ }
+ }
+
+ // Finally add whatever is left in the decomposed multimap
+ for (Entry<SchemaPath, Collection<YangInstanceIdentifier>> e : toAdd.asMap().entrySet()) {
+ final Builder<YangInstanceIdentifier, List<DOMRpcImplementation>> vb = ImmutableMap.builder();
+ final List<DOMRpcImplementation> v = Collections.singletonList(implementation);
+ for (YangInstanceIdentifier i : e.getValue()) {
+ vb.put(i, v);
+ }
+
+ mb.put(e.getKey(), createRpcEntry(schemaContext, e.getKey(), vb.build()));
+ }
+
+ return new DOMRpcRoutingTable(mb.build(), schemaContext);
+ }
+
+ DOMRpcRoutingTable remove(final DOMRpcImplementation implementation, final Set<DOMRpcIdentifier> rpcs) {
+ if (rpcs.isEmpty()) {
+ return this;
+ }
+
+ // First decompose the identifiers to a multimap
+ final ListMultimap<SchemaPath, YangInstanceIdentifier> toRemove = decomposeIdentifiers(rpcs);
+
+ // Now iterate over existing entries, modifying them as appropriate...
+ final Builder<SchemaPath, AbstractDOMRpcRoutingTableEntry> b = ImmutableMap.builder();
+ for (Entry<SchemaPath, AbstractDOMRpcRoutingTableEntry> e : this.rpcs.entrySet()) {
+ final List<YangInstanceIdentifier> removed = new ArrayList<>(toRemove.removeAll(e.getKey()));
+ if (!removed.isEmpty()) {
+ final AbstractDOMRpcRoutingTableEntry ne = e.getValue().remove(implementation, removed);
+ if (ne != null) {
+ b.put(e.getKey(), ne);
+ }
+ } else {
+ b.put(e);
+ }
+ }
+
+ // All done, whatever is in toRemove, was not there in the first place
+ return new DOMRpcRoutingTable(b.build(), schemaContext);
+ }
+
+ boolean contains(final DOMRpcIdentifier input) {
+ final AbstractDOMRpcRoutingTableEntry contexts = rpcs.get(input.getType());
+ return contexts != null && contexts.containsContext(input.getContextReference());
+ }
+
+ Map<SchemaPath, Set<YangInstanceIdentifier>> getRpcs() {
+ return Maps.transformValues(rpcs, EXTRACT_IDENTIFIERS);
+ }
+
+ private static RpcDefinition findRpcDefinition(final SchemaContext context, final SchemaPath schemaPath) {
+ if (context != null) {
+ final QName qname = schemaPath.getPathFromRoot().iterator().next();
+ final Module module = context.findModuleByNamespaceAndRevision(qname.getNamespace(), qname.getRevision());
+ if (module != null && module.getRpcs() != null) {
+ for (RpcDefinition rpc : module.getRpcs()) {
+ if (qname.equals(rpc.getQName())) {
+ return rpc;
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private static AbstractDOMRpcRoutingTableEntry createRpcEntry(final SchemaContext context, final SchemaPath key, final Map<YangInstanceIdentifier, List<DOMRpcImplementation>> implementations) {
+ final RpcDefinition rpcDef = findRpcDefinition(context, key);
+ if (rpcDef != null) {
+ final ContainerSchemaNode input = rpcDef.getInput();
+ if (input != null) {
+ for (DataSchemaNode c : input.getChildNodes()) {
+ for (UnknownSchemaNode extension : c.getUnknownSchemaNodes()) {
+ if (CONTEXT_REFERENCE.equals(extension.getNodeType())) {
+ final YangInstanceIdentifier keyId = YangInstanceIdentifier.builder().node(c.getQName()).build();
+ return new RoutedDOMRpcRoutingTableEntry(rpcDef, keyId, implementations);
+ }
+ }
+ }
+ }
+
+ return new GlobalDOMRpcRoutingTableEntry(rpcDef, implementations);
+ } else {
+ return new UnknownDOMRpcRoutingTableEntry(key, implementations);
+ }
+ }
+
+ CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(final SchemaPath type, final NormalizedNode<?, ?> input) {
+ final AbstractDOMRpcRoutingTableEntry entry = rpcs.get(type);
+ if (entry == null) {
+ return Futures.<DOMRpcResult, DOMRpcException>immediateFailedCheckedFuture(new DOMRpcImplementationNotAvailableException("No implementation of RPC %s available", type));
+ }
+
+ return entry.invokeRpc(input);
+ }
+
+ DOMRpcRoutingTable setSchemaContext(final SchemaContext context) {
+ final Builder<SchemaPath, AbstractDOMRpcRoutingTableEntry> b = ImmutableMap.builder();
+
+ for (Entry<SchemaPath, AbstractDOMRpcRoutingTableEntry> e : rpcs.entrySet()) {
+ b.put(e.getKey(), createRpcEntry(context, e.getKey(), e.getValue().getImplementations()));
+ }
+
+ return new DOMRpcRoutingTable(b.build(), context);
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.CheckedFuture;
+import java.util.List;
+import java.util.Map;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementation;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+
+final class GlobalDOMRpcRoutingTableEntry extends AbstractDOMRpcRoutingTableEntry {
+ private static final YangInstanceIdentifier ROOT = YangInstanceIdentifier.builder().build();
+ private final DOMRpcIdentifier rpcId;
+
+ private GlobalDOMRpcRoutingTableEntry(final DOMRpcIdentifier rpcId, final Map<YangInstanceIdentifier, List<DOMRpcImplementation>> impls) {
+ super(rpcId.getType(), impls);
+ this.rpcId = Preconditions.checkNotNull(rpcId);
+ }
+
+ // We do not need the RpcDefinition, but this makes sure we do not
+ // forward something we don't know to be an RPC.
+ GlobalDOMRpcRoutingTableEntry(final RpcDefinition def, final Map<YangInstanceIdentifier, List<DOMRpcImplementation>> impls) {
+ super(def.getPath(), impls);
+ this.rpcId = DOMRpcIdentifier.create(def.getPath());
+ }
+
+ @Override
+ protected CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(final NormalizedNode<?, ?> input) {
+ return getImplementations(ROOT).get(0).invokeRpc(rpcId, input);
+ }
+
+ @Override
+ protected GlobalDOMRpcRoutingTableEntry newInstance(final Map<YangInstanceIdentifier, List<DOMRpcImplementation>> impls) {
+ return new GlobalDOMRpcRoutingTableEntry(rpcId, impls);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Preconditions;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.controller.md.sal.dom.spi.ForwardingDOMDataBroker;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+
+/**
+ * An implementation of a {@link DOMDataBroker}, which forwards most requests to a delegate.
+ *
+ * Its interpretation of the API contract is somewhat looser, specifically it does not
+ * guarantee transaction ordering between transactions allocated directly from the broker
+ * and its transaction chains.
+ */
+public final class PingPongDataBroker extends ForwardingDOMDataBroker implements AutoCloseable, DOMDataTreeChangeService {
+ private final DOMDataBroker delegate;
+
+ /**
+ * Instantiate a new broker, backed by the the specified delegate
+ * {@link DOMDataBroker}.
+ *
+ * @param delegate Backend broker, may not be null.
+ */
+ public PingPongDataBroker(@Nonnull final DOMDataBroker delegate) {
+ this.delegate = Preconditions.checkNotNull(delegate);
+ }
+
+ @Override
+ protected DOMDataBroker delegate() {
+ return delegate;
+ }
+
+ @Override
+ public PingPongTransactionChain createTransactionChain(final TransactionChainListener listener) {
+ return new PingPongTransactionChain(delegate, listener);
+ }
+
+ @Override
+ public void close() {
+ // TODO Auto-generated method stub
+ }
+
+ @Override
+ public <L extends DOMDataTreeChangeListener> ListenerRegistration<L> registerDataTreeChangeListener(final DOMDataTreeIdentifier treeId, final L listener) {
+ if (delegate instanceof DOMDataTreeChangeService) {
+ return ((DOMDataTreeChangeService)delegate).registerDataTreeChangeListener(treeId, listener);
+ }
+
+ throw new UnsupportedOperationException("Delegate " + delegate + " does not support required functionality");
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.util.concurrent.AbstractCheckedFuture;
+import com.google.common.util.concurrent.ListenableFuture;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+
+/**
+ * A {@link java.util.concurrent.Future} used to report the status of an future
+ * {@link java.util.concurrent.Future}.
+ */
+final class PingPongFuture extends AbstractCheckedFuture<Void, TransactionCommitFailedException> {
+ protected PingPongFuture(final ListenableFuture<Void> delegate) {
+ super(delegate);
+ }
+
+ @Override
+ protected TransactionCommitFailedException mapException(final Exception e) {
+ if (e.getCause() instanceof TransactionCommitFailedException){
+ return (TransactionCommitFailedException) e.getCause();
+ } else {
+ return new TransactionCommitFailedException(e.getMessage(), e.getCause());
+ }
+ }
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.common.impl.service.AbstractDataTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+/**
+ * Transaction context. Tracks the relationship with the backend transaction.
+ * We never leak this class to the user and have it implement the {@link FutureCallback}
+ * interface so we have a simple way of propagating the result.
+ */
+final class PingPongTransaction implements FutureCallback<Void> {
+ private final CheckedFuture<Void, TransactionCommitFailedException> submitFuture;
+ private final ListenableFuture<RpcResult<TransactionStatus>> commitFuture;
+ private final DOMDataReadWriteTransaction delegate;
+ private final SettableFuture<Void> future;
+ private DOMDataReadWriteTransaction frontendTransaction;
+
+ PingPongTransaction(final DOMDataReadWriteTransaction delegate) {
+ this.delegate = Preconditions.checkNotNull(delegate);
+ future = SettableFuture.create();
+ submitFuture = new PingPongFuture(future);
+ commitFuture = AbstractDataTransaction.convertToLegacyCommitFuture(submitFuture);
+ }
+
+ DOMDataReadWriteTransaction getTransaction() {
+ return delegate;
+ }
+
+ DOMDataReadWriteTransaction getFrontendTransaction() {
+ return frontendTransaction;
+ }
+
+ CheckedFuture<Void, TransactionCommitFailedException> getSubmitFuture() {
+ return submitFuture;
+ }
+
+ ListenableFuture<RpcResult<TransactionStatus>> getCommitFuture() {
+ return commitFuture;
+ }
+
+ @Override
+ public void onSuccess(final Void result) {
+ future.set(result);
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ future.setException(t);
+ }
+
+ void recordFrontendTransaction(final DOMDataReadWriteTransaction tx) {
+ if (frontendTransaction != null) {
+ frontendTransaction = tx;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
+ }
+
+ protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+ return toStringHelper.add("delegate", delegate);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import javax.annotation.Nonnull;
+import javax.annotation.concurrent.GuardedBy;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
+import org.opendaylight.controller.md.sal.dom.spi.ForwardingDOMDataReadWriteTransaction;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * An implementation of {@link DOMTransactionChain}, which has a very specific
+ * behavior, which some users may find surprising. If keeps the general
+ * intent of the contract, but it makes sure there are never more than two
+ * transactions allocated at any given time: one of them is being committed,
+ * and while that is happening, the other one acts as the scratch pad. Once
+ * the committing transaction completes successfully, the scratch transaction
+ * is enqueued as soon as it is ready.
+ *
+ * This mode of operation means that there is no inherent isolation between
+ * the front-end transactions and transactions cannot be reasonably cancelled.
+ *
+ * It furthermore means that the transactions returned by {@link #newReadOnlyTransaction()}
+ * counts as an outstanding transaction and the user may not allocate multiple
+ * read-only transactions at the same time.
+ */
+public final class PingPongTransactionChain implements DOMTransactionChain {
+ private static final Logger LOG = LoggerFactory.getLogger(PingPongTransactionChain.class);
+ private final DOMTransactionChain delegate;
+
+ @GuardedBy("this")
+ private boolean failed;
+
+ /**
+ * This updater is used to manipulate the "ready" transaction. We perform only atomic
+ * get-and-set on it.
+ */
+ private static final AtomicReferenceFieldUpdater<PingPongTransactionChain, PingPongTransaction> READY_UPDATER =
+ AtomicReferenceFieldUpdater.newUpdater(PingPongTransactionChain.class, PingPongTransaction.class, "readyTx");
+ private volatile PingPongTransaction readyTx;
+
+ /**
+ * This updater is used to manipulate the "locked" transaction. A locked transaction
+ * means we know that the user still holds a transaction and should at some point call
+ * us. We perform on compare-and-swap to ensure we properly detect when a user is
+ * attempting to allocated multiple transactions concurrently.
+ */
+ private static final AtomicReferenceFieldUpdater<PingPongTransactionChain, PingPongTransaction> LOCKED_UPDATER =
+ AtomicReferenceFieldUpdater.newUpdater(PingPongTransactionChain.class, PingPongTransaction.class, "lockedTx");
+ private volatile PingPongTransaction lockedTx;
+
+ /**
+ * This updater is used to manipulate the "inflight" transaction. There can be at most
+ * one of these at any given time. We perform only compare-and-swap on these.
+ */
+ private static final AtomicReferenceFieldUpdater<PingPongTransactionChain, PingPongTransaction> INFLIGHT_UPDATER =
+ AtomicReferenceFieldUpdater.newUpdater(PingPongTransactionChain.class, PingPongTransaction.class, "inflightTx");
+ private volatile PingPongTransaction inflightTx;
+
+ PingPongTransactionChain(final DOMDataBroker broker, final TransactionChainListener listener) {
+ this.delegate = broker.createTransactionChain(new TransactionChainListener() {
+ @Override
+ public void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction, final Throwable cause) {
+ LOG.debug("Delegate chain {} reported failure in {}", chain, transaction, cause);
+
+ final DOMDataReadWriteTransaction frontend;
+ final PingPongTransaction tx = inflightTx;
+ if (tx == null) {
+ LOG.warn("Transaction chain {} failed with no pending transactions", chain);
+ frontend = null;
+ } else {
+ frontend = tx.getFrontendTransaction();
+ }
+
+ listener.onTransactionChainFailed(PingPongTransactionChain.this, frontend, cause);
+ delegateFailed();
+ }
+
+ @Override
+ public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
+ listener.onTransactionChainSuccessful(PingPongTransactionChain.this);
+ }
+ });
+ }
+
+ private synchronized void delegateFailed() {
+ failed = true;
+
+ /*
+ * If we do not have a locked transaction, we need to ensure that
+ * the backend transaction is cancelled. Otherwise we can defer
+ * until the user calls us.
+ */
+ if (lockedTx == null) {
+ processIfReady();
+ }
+ }
+
+ private synchronized PingPongTransaction slowAllocateTransaction() {
+ final DOMDataReadWriteTransaction delegateTx = delegate.newReadWriteTransaction();
+ final PingPongTransaction newTx = new PingPongTransaction(delegateTx);
+
+ if (!LOCKED_UPDATER.compareAndSet(this, null, newTx)) {
+ delegateTx.cancel();
+ throw new IllegalStateException(String.format("New transaction %s raced with transacion %s", newTx, lockedTx));
+ }
+
+ return newTx;
+ }
+
+ private PingPongTransaction allocateTransaction() {
+ // Step 1: acquire current state
+ final PingPongTransaction oldTx = READY_UPDATER.getAndSet(this, null);
+
+ // Slow path: allocate a delegate transaction
+ if (oldTx == null) {
+ return slowAllocateTransaction();
+ }
+
+ // Fast path: reuse current transaction. We will check
+ // failures and similar on submit().
+ if (!LOCKED_UPDATER.compareAndSet(this, null, oldTx)) {
+ // Ouch. Delegate chain has not detected a duplicate
+ // transaction allocation. This is the best we can do.
+ oldTx.getTransaction().cancel();
+ throw new IllegalStateException(String.format("Reusable transaction %s raced with transaction %s", oldTx, lockedTx));
+ }
+
+ return oldTx;
+ }
+
+ /*
+ * This forces allocateTransaction() on a slow path, which has to happen after
+ * this method has completed executing. Also inflightTx may be updated outside
+ * the lock, hence we need to re-check.
+ */
+ @GuardedBy("this")
+ private void processIfReady() {
+ if (inflightTx == null) {
+ final PingPongTransaction tx = READY_UPDATER.getAndSet(this, null);
+ if (tx != null) {
+ processTransaction(tx);
+ }
+ }
+ }
+
+ /**
+ * Process a ready transaction. The caller needs to ensure that
+ * each transaction is seen only once by this method.
+ *
+ * @param tx Transaction which needs processing.
+ */
+ @GuardedBy("this")
+ private void processTransaction(@Nonnull final PingPongTransaction tx) {
+ if (failed) {
+ LOG.debug("Cancelling transaction {}", tx);
+ tx.getTransaction().cancel();
+ return;
+ }
+
+ LOG.debug("Submitting transaction {}", tx);
+ if (!INFLIGHT_UPDATER.compareAndSet(this, null, tx)) {
+ LOG.warn("Submitting transaction {} while {} is still running", tx, inflightTx);
+ }
+
+ Futures.addCallback(tx.getTransaction().submit(), new FutureCallback<Void>() {
+ @Override
+ public void onSuccess(final Void result) {
+ transactionSuccessful(tx, result);
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ transactionFailed(tx, t);
+ }
+ });
+ }
+
+ private void transactionSuccessful(final PingPongTransaction tx, final Void result) {
+ LOG.debug("Transaction {} completed successfully", tx);
+
+ final boolean success = INFLIGHT_UPDATER.compareAndSet(this, tx, null);
+ Preconditions.checkState(success, "Successful transaction %s while %s was submitted", tx, inflightTx);
+
+ synchronized (this) {
+ processIfReady();
+ }
+
+ // Can run unsynchronized
+ tx.onSuccess(result);
+ }
+
+ private void transactionFailed(final PingPongTransaction tx, final Throwable t) {
+ LOG.debug("Transaction {} failed", tx, t);
+
+ final boolean success = INFLIGHT_UPDATER.compareAndSet(this, tx, null);
+ Preconditions.checkState(success, "Failed transaction %s while %s was submitted", tx, inflightTx);
+
+ tx.onFailure(t);
+ }
+
+ private void readyTransaction(@Nonnull final PingPongTransaction tx) {
+ // First mark the transaction as not locked.
+ final boolean lockedMatch = LOCKED_UPDATER.compareAndSet(this, tx, null);
+ Preconditions.checkState(lockedMatch, "Attempted to submit transaction %s while we have %s", tx, lockedTx);
+ LOG.debug("Transaction {} unlocked", tx);
+
+ /*
+ * The transaction is ready. It will then be picked up by either next allocation,
+ * or a background transaction completion callback.
+ */
+ final boolean success = READY_UPDATER.compareAndSet(this, null, tx);
+ Preconditions.checkState(success, "Transaction %s collided on ready state", tx, readyTx);
+ LOG.debug("Transaction {} readied", tx);
+
+ /*
+ * We do not see a transaction being in-flight, so we need to take care of dispatching
+ * the transaction to the backend. We are in the ready case, we cannot short-cut
+ * the checking of readyTx, as an in-flight transaction may have completed between us
+ * setting the field above and us checking.
+ */
+ if (inflightTx == null) {
+ synchronized (this) {
+ processIfReady();
+ }
+ }
+ }
+
+ @Override
+ public synchronized void close() {
+ final PingPongTransaction notLocked = lockedTx;
+ Preconditions.checkState(notLocked == null, "Attempted to close chain with outstanding transaction %s", notLocked);
+
+ // Force allocations on slow path. We will complete the rest
+ final PingPongTransaction tx = READY_UPDATER.getAndSet(this, null);
+
+ // Make sure no transaction is outstanding. Otherwise sleep a bit and retry
+ while (inflightTx != null) {
+ LOG.debug("Busy-waiting for in-flight transaction {} to complete", inflightTx);
+ Thread.yield();
+ continue;
+ }
+
+ // If we have an outstanding transaction, send it down
+ if (tx != null) {
+ processTransaction(tx);
+ }
+
+ // All done, close the delegate. All new allocations should fail.
+ delegate.close();
+ }
+
+ @Override
+ public DOMDataReadOnlyTransaction newReadOnlyTransaction() {
+ final PingPongTransaction tx = allocateTransaction();
+
+ return new DOMDataReadOnlyTransaction() {
+ @Override
+ public CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read(final LogicalDatastoreType store,
+ final YangInstanceIdentifier path) {
+ return tx.getTransaction().read(store, path);
+ }
+
+ @Override
+ public CheckedFuture<Boolean, ReadFailedException> exists(final LogicalDatastoreType store,
+ final YangInstanceIdentifier path) {
+ return tx.getTransaction().exists(store, path);
+ }
+
+ @Override
+ public Object getIdentifier() {
+ return tx.getTransaction().getIdentifier();
+ }
+
+ @Override
+ public void close() {
+ readyTransaction(tx);
+ }
+ };
+ }
+
+ @Override
+ public DOMDataReadWriteTransaction newReadWriteTransaction() {
+ final PingPongTransaction tx = allocateTransaction();
+ final DOMDataReadWriteTransaction ret = new ForwardingDOMDataReadWriteTransaction() {
+ @Override
+ protected DOMDataReadWriteTransaction delegate() {
+ return tx.getTransaction();
+ }
+
+ @Override
+ public CheckedFuture<Void, TransactionCommitFailedException> submit() {
+ readyTransaction(tx);
+ return tx.getSubmitFuture();
+ }
+
+ @Override
+ public ListenableFuture<RpcResult<TransactionStatus>> commit() {
+ readyTransaction(tx);
+ return tx.getCommitFuture();
+ }
+
+ @Override
+ public boolean cancel() {
+ throw new UnsupportedOperationException("Transaction cancellation is not supported");
+ }
+ };
+
+ tx.recordFrontendTransaction(ret);
+ return ret;
+ }
+
+ @Override
+ public DOMDataWriteTransaction newWriteOnlyTransaction() {
+ return newReadWriteTransaction();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import java.util.List;
+import java.util.Map;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementation;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationNotAvailableException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class RoutedDOMRpcRoutingTableEntry extends AbstractDOMRpcRoutingTableEntry {
+ private static final Logger LOG = LoggerFactory.getLogger(RoutedDOMRpcRoutingTableEntry.class);
+ private final DOMRpcIdentifier globalRpcId;
+ private final YangInstanceIdentifier keyId;
+
+ private RoutedDOMRpcRoutingTableEntry(final DOMRpcIdentifier globalRpcId, final YangInstanceIdentifier keyId, final Map<YangInstanceIdentifier, List<DOMRpcImplementation>> impls) {
+ super(globalRpcId.getType(), impls);
+ this.keyId = Preconditions.checkNotNull(keyId);
+ this.globalRpcId = Preconditions.checkNotNull(globalRpcId);
+ }
+
+ RoutedDOMRpcRoutingTableEntry(final RpcDefinition def, final YangInstanceIdentifier keyId, final Map<YangInstanceIdentifier, List<DOMRpcImplementation>> impls) {
+ super(def.getPath(), impls);
+ this.keyId = Preconditions.checkNotNull(keyId);
+ this.globalRpcId = DOMRpcIdentifier.create(def.getPath());
+ }
+
+ @Override
+ protected CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(final NormalizedNode<?, ?> input) {
+ final Optional<NormalizedNode<?, ?>> maybeKey = NormalizedNodes.findNode(input, keyId);
+
+ // Routing key is present, attempt to deliver as a routed RPC
+ if (maybeKey.isPresent()) {
+ final NormalizedNode<?, ?> key = maybeKey.get();
+ final Object value = key.getValue();
+ if (value instanceof YangInstanceIdentifier) {
+ final YangInstanceIdentifier iid = (YangInstanceIdentifier) value;
+
+ // Find a DOMRpcImplementation for a specific iid
+ final List<DOMRpcImplementation> specificImpls = getImplementations(iid);
+ if (specificImpls != null) {
+ return specificImpls.get(0).invokeRpc(DOMRpcIdentifier.create(getSchemaPath(), iid), input);
+ }
+
+ LOG.debug("No implementation for context {} found will now look for wildcard id", iid);
+
+ // Find a DOMRpcImplementation for a wild card. Usually remote-rpc-connector would register an
+ // implementation this way
+ final List<DOMRpcImplementation> mayBeRemoteImpls = getImplementations(YangInstanceIdentifier.EMPTY);
+
+ if(mayBeRemoteImpls != null){
+ return mayBeRemoteImpls.get(0).invokeRpc(DOMRpcIdentifier.create(getSchemaPath(), iid), input);
+ }
+
+ } else {
+ LOG.warn("Ignoring wrong context value {}", value);
+ }
+ }
+
+ final List<DOMRpcImplementation> impls = getImplementations(null);
+ if (impls != null) {
+ return impls.get(0).invokeRpc(globalRpcId, input);
+ } else {
+ return Futures.<DOMRpcResult, DOMRpcException>immediateFailedCheckedFuture(new DOMRpcImplementationNotAvailableException("No implementation of RPC %s available", getSchemaPath()));
+ }
+ }
+
+ @Override
+ protected RoutedDOMRpcRoutingTableEntry newInstance(final Map<YangInstanceIdentifier, List<DOMRpcImplementation>> impls) {
+ return new RoutedDOMRpcRoutingTableEntry(globalRpcId, keyId, impls);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.RejectedExecutionException;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStore;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.yangtools.util.DurationStatisticsTracker;
+import org.opendaylight.yangtools.util.concurrent.MappingCheckedFuture;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Implementation of blocking three phase commit coordinator, which which
+ * supports coordination on multiple {@link DOMStoreThreePhaseCommitCohort}.
+ *
+ * This implementation does not support cancellation of commit,
+ *
+ * In order to advance to next phase of three phase commit all subtasks of
+ * previous step must be finish.
+ *
+ * This executor does not have an upper bound on subtask timeout.
+ */
+public class SerializedDOMDataBroker extends AbstractDOMDataBroker {
+ private static final Logger LOG = LoggerFactory.getLogger(SerializedDOMDataBroker.class);
+ private final DurationStatisticsTracker commitStatsTracker = DurationStatisticsTracker.createConcurrent();
+ private final ListeningExecutorService executor;
+
+ /**
+ *
+ * Construct DOMDataCommitCoordinator which uses supplied executor to
+ * process commit coordinations.
+ *
+ * @param executor
+ */
+ public SerializedDOMDataBroker(final Map<LogicalDatastoreType, DOMStore> datastores, final ListeningExecutorService executor) {
+ super(datastores);
+ this.executor = Preconditions.checkNotNull(executor, "executor must not be null.");
+ }
+
+ public DurationStatisticsTracker getCommitStatsTracker() {
+ return commitStatsTracker;
+ }
+
+ @Override
+ protected CheckedFuture<Void,TransactionCommitFailedException> submit(final DOMDataWriteTransaction transaction,
+ final Collection<DOMStoreThreePhaseCommitCohort> cohorts) {
+ Preconditions.checkArgument(transaction != null, "Transaction must not be null.");
+ Preconditions.checkArgument(cohorts != null, "Cohorts must not be null.");
+ LOG.debug("Tx: {} is submitted for execution.", transaction.getIdentifier());
+
+ ListenableFuture<Void> commitFuture = null;
+ try {
+ commitFuture = executor.submit(new CommitCoordinationTask(transaction, cohorts,
+ commitStatsTracker));
+ } catch(RejectedExecutionException e) {
+ LOG.error("The commit executor's queue is full - submit task was rejected. \n" +
+ executor, e);
+ return Futures.immediateFailedCheckedFuture(
+ new TransactionCommitFailedException(
+ "Could not submit the commit task - the commit queue capacity has been exceeded.", e));
+ }
+
+ return MappingCheckedFuture.create(commitFuture,
+ TransactionCommitFailedExceptionMapper.COMMIT_ERROR_MAPPER);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeShard;
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
+
+final class ShardRegistration<T extends DOMDataTreeShard> extends AbstractListenerRegistration<T> {
+ private final DOMDataTreeIdentifier prefix;
+ private final ShardedDOMDataTree tree;
+
+ protected ShardRegistration(final ShardedDOMDataTree tree, final DOMDataTreeIdentifier prefix, final T shard) {
+ super(shard);
+ this.tree = Preconditions.checkNotNull(tree);
+ this.prefix = Preconditions.checkNotNull(prefix);
+ }
+
+ DOMDataTreeIdentifier getPrefix() {
+ return prefix;
+ }
+
+ @Override
+ protected void removeRegistration() {
+ tree.removeShard(this);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Preconditions;
+import java.util.Collection;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.TreeMap;
+import javax.annotation.concurrent.GuardedBy;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeProducer;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeService;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeShard;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeShardingConflictException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeShardingService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class ShardedDOMDataTree implements DOMDataTreeService, DOMDataTreeShardingService {
+ private static final Logger LOG = LoggerFactory.getLogger(ShardedDOMDataTree.class);
+ private final Map<LogicalDatastoreType, ShardingTableEntry> shardingTables = new EnumMap<>(LogicalDatastoreType.class);
+ @GuardedBy("this")
+ private final Map<DOMDataTreeIdentifier, DOMDataTreeProducer> idToProducer = new TreeMap<>();
+
+ @GuardedBy("this")
+ private ShardingTableEntry lookupShard(final DOMDataTreeIdentifier prefix) {
+ final ShardingTableEntry t = shardingTables.get(prefix.getDatastoreType());
+ if (t == null) {
+ return null;
+ }
+
+ return t.lookup(prefix.getRootIdentifier());
+ }
+
+ @GuardedBy("this")
+ private void storeShard(final DOMDataTreeIdentifier prefix, final ShardRegistration<?> reg) {
+ ShardingTableEntry t = shardingTables.get(prefix.getDatastoreType());
+ if (t == null) {
+ t = new ShardingTableEntry();
+ shardingTables.put(prefix.getDatastoreType(), t);
+ }
+
+ t.store(prefix.getRootIdentifier(), reg);
+ }
+
+ void removeShard(final ShardRegistration<?> reg) {
+ final DOMDataTreeIdentifier prefix = reg.getPrefix();
+ final ShardRegistration<?> parentReg;
+
+ synchronized (this) {
+ final ShardingTableEntry t = shardingTables.get(prefix.getDatastoreType());
+ if (t == null) {
+ LOG.warn("Shard registration {} points to non-existent table", reg);
+ return;
+ }
+
+ t.remove(prefix.getRootIdentifier());
+ parentReg = lookupShard(prefix).getRegistration();
+
+ /*
+ * FIXME: adjust all producers. This is tricky, as we need different locking strategy,
+ * simply because we risk AB/BA deadlock with a producer being split off from
+ * a producer.
+ *
+ */
+ }
+
+ if (parentReg != null) {
+ parentReg.getInstance().onChildDetached(prefix, reg.getInstance());
+ }
+ }
+
+ @Override
+ public <T extends DOMDataTreeShard> ListenerRegistration<T> registerDataTreeShard(final DOMDataTreeIdentifier prefix, final T shard) throws DOMDataTreeShardingConflictException {
+ final ShardRegistration<T> reg;
+ final ShardRegistration<?> parentReg;
+
+ synchronized (this) {
+ /*
+ * Lookup the parent shard (e.g. the one which currently matches the prefix),
+ * and if it exists, check if its registration prefix does not collide with
+ * this registration.
+ */
+ final ShardingTableEntry parent = lookupShard(prefix);
+ parentReg = parent.getRegistration();
+ if (parentReg != null && prefix.equals(parentReg.getPrefix())) {
+ throw new DOMDataTreeShardingConflictException(String.format("Prefix %s is already occupied by shard %s", prefix, parentReg.getInstance()));
+ }
+
+ // FIXME: wrap the shard in a proper adaptor based on implemented interface
+
+ reg = new ShardRegistration<T>(this, prefix, shard);
+
+ storeShard(prefix, reg);
+
+ // FIXME: update any producers/registrations
+ }
+
+ // Notify the parent shard
+ if (parentReg != null) {
+ parentReg.getInstance().onChildAttached(prefix, shard);
+ }
+
+ return reg;
+ }
+
+ @GuardedBy("this")
+ private DOMDataTreeProducer findProducer(final DOMDataTreeIdentifier subtree) {
+ for (final Entry<DOMDataTreeIdentifier, DOMDataTreeProducer> e : idToProducer.entrySet()) {
+ if (e.getKey().contains(subtree)) {
+ return e.getValue();
+ }
+ }
+
+ return null;
+ }
+
+ synchronized void destroyProducer(final ShardedDOMDataTreeProducer producer) {
+ for (final DOMDataTreeIdentifier s : producer.getSubtrees()) {
+ final DOMDataTreeProducer r = idToProducer.remove(s);
+ if (!producer.equals(r)) {
+ LOG.error("Removed producer {} on subtree {} while removing {}", r, s, producer);
+ }
+ }
+ }
+
+ @GuardedBy("this")
+ private DOMDataTreeProducer createProducer(final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap) {
+ // Record the producer's attachment points
+ final DOMDataTreeProducer ret = ShardedDOMDataTreeProducer.create(this, shardMap);
+ for (DOMDataTreeIdentifier s : shardMap.keySet()) {
+ idToProducer.put(s, ret);
+ }
+
+ return ret;
+ }
+
+ @Override
+ public synchronized DOMDataTreeProducer createProducer(final Collection<DOMDataTreeIdentifier> subtrees) {
+ Preconditions.checkArgument(!subtrees.isEmpty(), "Subtrees may not be empty");
+
+ final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap = new HashMap<>();
+ for (final DOMDataTreeIdentifier s : subtrees) {
+ // Attempting to create a disconnected producer -- all subtrees have to be unclaimed
+ final DOMDataTreeProducer producer = findProducer(s);
+ Preconditions.checkArgument(producer == null, "Subtree %s is attached to producer %s", s, producer);
+
+ shardMap.put(s, lookupShard(s).getRegistration().getInstance());
+ }
+
+ return createProducer(shardMap);
+ }
+
+ synchronized DOMDataTreeProducer createProducer(final ShardedDOMDataTreeProducer parent, final Collection<DOMDataTreeIdentifier> subtrees) {
+ Preconditions.checkNotNull(parent);
+
+ final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap = new HashMap<>();
+ for (final DOMDataTreeIdentifier s : subtrees) {
+ shardMap.put(s, lookupShard(s).getRegistration().getInstance());
+ }
+
+ return createProducer(shardMap);
+ }
+
+ @Override
+ public synchronized <T extends DOMDataTreeListener> ListenerRegistration<T> registerListener(final T listener, final Collection<DOMDataTreeIdentifier> subtrees, final boolean allowRxMerges, final Collection<DOMDataTreeProducer> producers) {
+ // FIXME Implement this.
+ throw new UnsupportedOperationException("Not implemented yet.");
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableBiMap.Builder;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Queue;
+import java.util.Set;
+import javax.annotation.concurrent.GuardedBy;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeProducer;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeProducerBusyException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeProducerException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeShard;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStore;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class ShardedDOMDataTreeProducer implements DOMDataTreeProducer {
+ private static final Logger LOG = LoggerFactory.getLogger(ShardedDOMDataTreeProducer.class);
+ private final BiMap<DOMDataTreeShard, DOMStoreTransactionChain> shardToChain;
+ private final Map<DOMDataTreeIdentifier, DOMDataTreeShard> idToShard;
+ private final ShardedDOMDataTree dataTree;
+
+ @GuardedBy("this")
+ private Map<DOMDataTreeIdentifier, DOMDataTreeProducer> children = Collections.emptyMap();
+ @GuardedBy("this")
+ private DOMDataWriteTransaction openTx;
+ @GuardedBy("this")
+ private boolean closed;
+
+ ShardedDOMDataTreeProducer(final ShardedDOMDataTree dataTree, final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap, final Set<DOMDataTreeShard> shards) {
+ this.dataTree = Preconditions.checkNotNull(dataTree);
+
+ // Create shard -> chain map
+ final Builder<DOMDataTreeShard, DOMStoreTransactionChain> cb = ImmutableBiMap.builder();
+ final Queue<Exception> es = new LinkedList<>();
+
+ for (final DOMDataTreeShard s : shards) {
+ if (s instanceof DOMStore) {
+ try {
+ final DOMStoreTransactionChain c = ((DOMStore)s).createTransactionChain();
+ LOG.trace("Using DOMStore chain {} to access shard {}", c, s);
+ cb.put(s, c);
+ } catch (final Exception e) {
+ LOG.error("Failed to instantiate chain for shard {}", s, e);
+ es.add(e);
+ }
+ } else {
+ LOG.error("Unhandled shard instance type {}", s.getClass());
+ }
+ }
+ this.shardToChain = cb.build();
+
+ // An error was encountered, close chains and report the error
+ if (shardToChain.size() != shards.size()) {
+ for (final DOMStoreTransactionChain c : shardToChain.values()) {
+ try {
+ c.close();
+ } catch (final Exception e) {
+ LOG.warn("Exception raised while closing chain {}", c, e);
+ }
+ }
+
+ final IllegalStateException e = new IllegalStateException("Failed to completely allocate contexts", es.poll());
+ while (!es.isEmpty()) {
+ e.addSuppressed(es.poll());
+ }
+
+ throw e;
+ }
+
+ idToShard = ImmutableMap.copyOf(shardMap);
+ }
+
+ @Override
+ public synchronized DOMDataWriteTransaction createTransaction(final boolean isolated) {
+ Preconditions.checkState(!closed, "Producer is already closed");
+ Preconditions.checkState(openTx == null, "Transaction %s is still open", openTx);
+
+ // Allocate backing transactions
+ final Map<DOMDataTreeShard, DOMStoreWriteTransaction> shardToTx = new HashMap<>();
+ for (final Entry<DOMDataTreeShard, DOMStoreTransactionChain> e : shardToChain.entrySet()) {
+ shardToTx.put(e.getKey(), e.getValue().newWriteOnlyTransaction());
+ }
+
+ // Create the ID->transaction map
+ final ImmutableMap.Builder<DOMDataTreeIdentifier, DOMStoreWriteTransaction> b = ImmutableMap.builder();
+ for (final Entry<DOMDataTreeIdentifier, DOMDataTreeShard> e : idToShard.entrySet()) {
+ b.put(e.getKey(), shardToTx.get(e.getValue()));
+ }
+
+ final ShardedDOMDataWriteTransaction ret = new ShardedDOMDataWriteTransaction(this, b.build());
+ openTx = ret;
+ return ret;
+ }
+
+ @GuardedBy("this")
+ private boolean haveSubtree(final DOMDataTreeIdentifier subtree) {
+ for (final DOMDataTreeIdentifier i : idToShard.keySet()) {
+ if (i.contains(subtree)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @GuardedBy("this")
+ private DOMDataTreeProducer lookupChild(final DOMDataTreeIdentifier s) {
+ for (final Entry<DOMDataTreeIdentifier, DOMDataTreeProducer> e : children.entrySet()) {
+ if (e.getKey().contains(s)) {
+ return e.getValue();
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public synchronized DOMDataTreeProducer createProducer(final Collection<DOMDataTreeIdentifier> subtrees) {
+ Preconditions.checkState(!closed, "Producer is already closed");
+ Preconditions.checkState(openTx == null, "Transaction %s is still open", openTx);
+
+ for (final DOMDataTreeIdentifier s : subtrees) {
+ // Check if the subtree was visible at any time
+ if (!haveSubtree(s)) {
+ throw new IllegalArgumentException(String.format("Subtree %s was never available in producer %s", s, this));
+ }
+
+ // Check if the subtree has not been delegated to a child
+ final DOMDataTreeProducer child = lookupChild(s);
+ Preconditions.checkArgument(child == null, "Subtree %s is delegated to child producer %s", s, child);
+
+ // Check if part of the requested subtree is not delegated to a child.
+ for (final DOMDataTreeIdentifier c : children.keySet()) {
+ if (s.contains(c)) {
+ throw new IllegalArgumentException(String.format("Subtree %s cannot be delegated as it is superset of already-delegated %s", s, c));
+ }
+ }
+ }
+
+ final DOMDataTreeProducer ret = dataTree.createProducer(this, subtrees);
+ final ImmutableMap.Builder<DOMDataTreeIdentifier, DOMDataTreeProducer> cb = ImmutableMap.builder();
+ cb.putAll(children);
+ for (final DOMDataTreeIdentifier s : subtrees) {
+ cb.put(s, ret);
+ }
+
+ children = cb.build();
+ return ret;
+ }
+
+ @Override
+ public synchronized void close() throws DOMDataTreeProducerException {
+ if (!closed) {
+ if (openTx != null) {
+ throw new DOMDataTreeProducerBusyException(String.format("Transaction %s is still open", openTx));
+ }
+
+ closed = true;
+ dataTree.destroyProducer(this);
+ }
+ }
+
+ static DOMDataTreeProducer create(final ShardedDOMDataTree dataTree, final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap) {
+ /*
+ * FIXME: we do not allow multiple multiple shards in a producer because we do not implement the
+ * synchronization primitives yet
+ */
+ final Set<DOMDataTreeShard> shards = ImmutableSet.copyOf(shardMap.values());
+ if (shards.size() > 1) {
+ throw new UnsupportedOperationException("Cross-shard producers are not supported yet");
+ }
+
+ return new ShardedDOMDataTreeProducer(dataTree, shardMap, shards);
+ }
+
+ Set<DOMDataTreeIdentifier> getSubtrees() {
+ return idToShard.keySet();
+ }
+
+ synchronized void cancelTransaction(final ShardedDOMDataWriteTransaction transaction) {
+ if (!openTx.equals(transaction)) {
+ LOG.warn("Transaction {} is not open in producer {}", transaction, this);
+ return;
+ }
+
+ LOG.debug("Transaction {} cancelled", transaction);
+ openTx = null;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+import javax.annotation.concurrent.GuardedBy;
+import javax.annotation.concurrent.NotThreadSafe;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.common.impl.service.AbstractDataTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@NotThreadSafe
+final class ShardedDOMDataWriteTransaction implements DOMDataWriteTransaction {
+ private static final Logger LOG = LoggerFactory.getLogger(ShardedDOMDataWriteTransaction.class);
+ private static final AtomicLong COUNTER = new AtomicLong();
+ private final Map<DOMDataTreeIdentifier, DOMStoreWriteTransaction> idToTransaction;
+ private final ShardedDOMDataTreeProducer producer;
+ private final String identifier;
+ @GuardedBy("this")
+ private boolean closed = false;
+
+ ShardedDOMDataWriteTransaction(final ShardedDOMDataTreeProducer producer, final Map<DOMDataTreeIdentifier, DOMStoreWriteTransaction> idToTransaction) {
+ this.producer = Preconditions.checkNotNull(producer);
+ this.idToTransaction = Preconditions.checkNotNull(idToTransaction);
+ this.identifier = "SHARDED-DOM-" + COUNTER.getAndIncrement();
+ }
+
+ // FIXME: use atomic operations
+ @GuardedBy("this")
+ private DOMStoreWriteTransaction lookup(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+ final DOMDataTreeIdentifier id = new DOMDataTreeIdentifier(store, path);
+
+ for (Entry<DOMDataTreeIdentifier, DOMStoreWriteTransaction> e : idToTransaction.entrySet()) {
+ if (e.getKey().contains(id)) {
+ return e.getValue();
+ }
+ }
+
+ throw new IllegalArgumentException(String.format("Path %s is not acessible from transaction %s", id, this));
+ }
+
+ @Override
+ public String getIdentifier() {
+ return identifier;
+ }
+
+ @Override
+ public synchronized boolean cancel() {
+ if (closed) {
+ return false;
+ }
+
+ LOG.debug("Cancelling transaction {}", identifier);
+ for (DOMStoreWriteTransaction tx : ImmutableSet.copyOf(idToTransaction.values())) {
+ tx.close();
+ }
+
+ closed = true;
+ producer.cancelTransaction(this);
+ return true;
+ }
+
+ @Override
+ public synchronized CheckedFuture<Void, TransactionCommitFailedException> submit() {
+ Preconditions.checkState(!closed, "Transaction %s is already closed", identifier);
+
+ final Set<DOMStoreWriteTransaction> txns = ImmutableSet.copyOf(idToTransaction.values());
+ final List<DOMStoreThreePhaseCommitCohort> cohorts = new ArrayList<>(txns.size());
+ for (DOMStoreWriteTransaction tx : txns) {
+ cohorts.add(tx.ready());
+ }
+
+ try {
+ return Futures.immediateCheckedFuture(new CommitCoordinationTask(this, cohorts, null).call());
+ } catch (TransactionCommitFailedException e) {
+ return Futures.immediateFailedCheckedFuture(e);
+ }
+ }
+
+ @Override
+ @Deprecated
+ public ListenableFuture<RpcResult<TransactionStatus>> commit() {
+ return AbstractDataTransaction.convertToLegacyCommitFuture(submit());
+ }
+
+ @Override
+ public synchronized void delete(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+ lookup(store, path).delete(path);
+ }
+
+ @Override
+ public synchronized void put(final LogicalDatastoreType store, final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
+ lookup(store, path).write(path, data);
+ }
+
+ @Override
+ public synchronized void merge(final LogicalDatastoreType store, final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
+ lookup(store, path).merge(path, data);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.base.Preconditions;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class ShardingTableEntry implements Identifiable<PathArgument> {
+ private static final Logger LOG = LoggerFactory.getLogger(ShardingTableEntry.class);
+ private final Map<PathArgument, ShardingTableEntry> children = Collections.emptyMap();
+ private final PathArgument identifier;
+ private ShardRegistration<?> registration;
+
+ ShardingTableEntry() {
+ identifier = null;
+ }
+
+ ShardingTableEntry(final PathArgument identifier) {
+ this.identifier = Preconditions.checkNotNull(identifier);
+ }
+
+ @Override
+ public PathArgument getIdentifier() {
+ return identifier;
+ }
+
+ public ShardRegistration<?> getRegistration() {
+ return registration;
+ }
+
+ ShardingTableEntry lookup(final YangInstanceIdentifier id) {
+ final Iterator<PathArgument> it = id.getPathArguments().iterator();
+ ShardingTableEntry entry = this;
+
+ while (it.hasNext()) {
+ final PathArgument a = it.next();
+ final ShardingTableEntry child = entry.children.get(a);
+ if (child == null) {
+ LOG.debug("Lookup of {} stopped at {}", id, a);
+ break;
+ }
+
+ entry = child;
+ }
+
+ return entry;
+ }
+
+ void store(final YangInstanceIdentifier id, final ShardRegistration<?> reg) {
+ final Iterator<PathArgument> it = id.getPathArguments().iterator();
+ ShardingTableEntry entry = this;
+
+ while (it.hasNext()) {
+ final PathArgument a = it.next();
+ ShardingTableEntry child = entry.children.get(a);
+ if (child == null) {
+ child = new ShardingTableEntry(a);
+ entry.children.put(a, child);
+ }
+ }
+
+ Preconditions.checkState(entry.registration == null);
+ entry.registration = reg;
+ }
+
+ private boolean remove(final Iterator<PathArgument> it) {
+ if (it.hasNext()) {
+ final PathArgument arg = it.next();
+ final ShardingTableEntry child = children.get(arg);
+ if (child != null) {
+ if (child.remove(it)) {
+ children.remove(arg);
+ }
+ } else {
+ LOG.warn("Cannot remove non-existent child {}", arg);
+ }
+ }
+
+ return registration == null && children.isEmpty();
+ }
+
+ void remove(final YangInstanceIdentifier id) {
+ this.remove(id.getPathArguments().iterator());
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.yangtools.util.concurrent.ExceptionMapper;
+
+/**
+ * Utility exception mapper which translates Exception to {@link TransactionCommitFailedException}.
+ *
+ * @see ExceptionMapper
+ */
+public final class TransactionCommitFailedExceptionMapper
+ extends ExceptionMapper<TransactionCommitFailedException> {
+
+ public static final TransactionCommitFailedExceptionMapper PRE_COMMIT_MAPPER = create("preCommit");
+
+ public static final TransactionCommitFailedExceptionMapper CAN_COMMIT_ERROR_MAPPER = create("canCommit");
+
+ public static final TransactionCommitFailedExceptionMapper COMMIT_ERROR_MAPPER = create("commit");
+
+ private TransactionCommitFailedExceptionMapper(final String opName) {
+ super( opName, TransactionCommitFailedException.class );
+ }
+
+ public static TransactionCommitFailedExceptionMapper create(final String opName) {
+ return new TransactionCommitFailedExceptionMapper(opName);
+ }
+
+ @Override
+ protected TransactionCommitFailedException newWithCause( final String message, final Throwable cause ) {
+ return new TransactionCommitFailedException( message, cause );
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import java.util.List;
+import java.util.Map;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementation;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationNotAvailableException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+final class UnknownDOMRpcRoutingTableEntry extends AbstractDOMRpcRoutingTableEntry {
+ private final CheckedFuture<DOMRpcResult, DOMRpcException> unknownRpc;
+
+ UnknownDOMRpcRoutingTableEntry(final SchemaPath schemaPath, final Map<YangInstanceIdentifier, List<DOMRpcImplementation>> impls) {
+ super(schemaPath, impls);
+ unknownRpc = Futures.<DOMRpcResult, DOMRpcException>immediateFailedCheckedFuture(
+ new DOMRpcImplementationNotAvailableException("SchemaPath %s is not resolved to an RPC", schemaPath));
+ }
+
+ @Override
+ protected CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(final NormalizedNode<?, ?> input) {
+ return unknownRpc;
+ }
+
+ @Override
+ protected UnknownDOMRpcRoutingTableEntry newInstance(final Map<YangInstanceIdentifier, List<DOMRpcImplementation>> impls) {
+ return new UnknownDOMRpcRoutingTableEntry(getSchemaPath(), impls);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.broker.impl.jmx;
+
+/**
+ * MXBean interface for retrieving write Tx commit statistics.
+ *
+ * @author Thomas Pantelis
+ */
+public interface CommitStatsMXBean {
+
+ /**
+ * Returns the total number of commits that have occurred.
+ */
+ long getTotalCommits();
+
+ /**
+ * Returns a string representing the time duration of the longest commit, in the appropriate
+ * scaled units, along with the date/time that it occurred.
+ */
+ String getLongestCommitTime();
+
+ /**
+ * Returns a string representing the time duration of the shortest commit, in the appropriate
+ * scaled units, along with the date/time that it occurred.
+ */
+ String getShortestCommitTime();
+
+ /**
+ * Returns a string representing average commit time duration, in the appropriate
+ * scaled units.
+ */
+ String getAverageCommitTime();
+
+ /**
+ * Clears the current stats to their defaults.
+ */
+ void clearStats();
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.broker.impl.jmx;
+
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.common.util.jmx.AbstractMXBean;
+import org.opendaylight.yangtools.util.DurationStatisticsTracker;
+
+/**
+ * Implementation of the CommitStatsMXBean interface.
+ *
+ * @author Thomas Pantelis
+ */
+public class CommitStatsMXBeanImpl extends AbstractMXBean implements CommitStatsMXBean {
+
+ private final DurationStatisticsTracker commitStatsTracker;
+
+ /**
+ * Constructor.
+ *
+ * @param commitStatsTracker the DurationStatsTracker used to obtain the stats.
+ * @param mBeanType mBeanType Used as the <code>type</code> property in the bean's ObjectName.
+ */
+ public CommitStatsMXBeanImpl(@Nonnull DurationStatisticsTracker commitStatsTracker,
+ @Nonnull String mBeanType) {
+ super("CommitStats", mBeanType, null);
+ this.commitStatsTracker = commitStatsTracker;
+ }
+
+ @Override
+ public long getTotalCommits() {
+ return commitStatsTracker.getTotalDurations();
+ }
+
+ @Override
+ public String getLongestCommitTime() {
+ return commitStatsTracker.getDisplayableLongestDuration();
+ }
+
+ @Override
+ public String getShortestCommitTime() {
+ return commitStatsTracker.getDisplayableShortestDuration();
+ }
+
+ @Override
+ public String getAverageCommitTime() {
+ return commitStatsTracker.getDisplayableAverageDuration();
+ }
+
+ @Override
+ public void clearStats() {
+ commitStatsTracker.reset();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.broker.impl.mount;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.MutableClassToInstanceMap;
+import java.util.HashMap;
+import java.util.Map;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
+import org.opendaylight.controller.md.sal.dom.api.DOMService;
+import org.opendaylight.controller.md.sal.dom.broker.spi.mount.SimpleDOMMountPoint;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import org.opendaylight.yangtools.util.ListenerRegistry;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class DOMMountPointServiceImpl implements DOMMountPointService {
+
+ private final Map<YangInstanceIdentifier, DOMMountPoint> mountPoints = new HashMap<>();
+
+ private final ListenerRegistry<MountProvisionListener> listeners = ListenerRegistry.create();
+
+ @Override
+ public Optional<DOMMountPoint> getMountPoint(final YangInstanceIdentifier path) {
+ return Optional.fromNullable(mountPoints.get(path));
+ }
+
+ @Override
+ public DOMMountPointBuilder createMountPoint(final YangInstanceIdentifier path) {
+ Preconditions.checkState(!mountPoints.containsKey(path), "Mount point already exists");
+ return new DOMMountPointBuilderImpl(path);
+ }
+
+ public void notifyMountCreated(final YangInstanceIdentifier identifier) {
+ for (final ListenerRegistration<MountProvisionListener> listener : listeners
+ .getListeners()) {
+ listener.getInstance().onMountPointCreated(identifier);
+ }
+ }
+
+ public void notifyMountRemoved(final YangInstanceIdentifier identifier) {
+ for (final ListenerRegistration<MountProvisionListener> listener : listeners
+ .getListeners()) {
+ listener.getInstance().onMountPointRemoved(identifier);
+ }
+ }
+
+ @Override
+ public ListenerRegistration<MountProvisionListener> registerProvisionListener(
+ final MountProvisionListener listener) {
+ return listeners.register(listener);
+ }
+
+ public ObjectRegistration<DOMMountPoint> registerMountPoint(final DOMMountPoint mountPoint) {
+ synchronized (mountPoints) {
+ Preconditions.checkState(!mountPoints.containsKey(mountPoint.getIdentifier()), "Mount point already exists");
+ mountPoints.put(mountPoint.getIdentifier(), mountPoint);
+ }
+ notifyMountCreated(mountPoint.getIdentifier());
+
+ return new MountRegistration(mountPoint);
+ }
+
+ public void unregisterMountPoint(final YangInstanceIdentifier mountPointId) {
+ synchronized (mountPoints) {
+ Preconditions.checkState(mountPoints.containsKey(mountPointId), "Mount point does not exist");
+ mountPoints.remove(mountPointId);
+ }
+ notifyMountRemoved(mountPointId);
+ }
+
+ public class DOMMountPointBuilderImpl implements DOMMountPointBuilder {
+
+ ClassToInstanceMap<DOMService> services = MutableClassToInstanceMap.create();
+ private SimpleDOMMountPoint mountPoint;
+ private final YangInstanceIdentifier path;
+ private SchemaContext schemaContext;
+
+ public DOMMountPointBuilderImpl(final YangInstanceIdentifier path) {
+ this.path = path;
+ }
+
+ @Override
+ public <T extends DOMService> DOMMountPointBuilder addService(final Class<T> type, final T impl) {
+ services.putInstance(type, impl);
+ return this;
+ }
+
+ @Override
+ public DOMMountPointBuilder addInitialSchemaContext(final SchemaContext ctx) {
+ schemaContext = ctx;
+ return this;
+ }
+
+ @Override
+ public ObjectRegistration<DOMMountPoint> register() {
+ Preconditions.checkState(mountPoint == null, "Mount point is already built.");
+ mountPoint = SimpleDOMMountPoint.create(path, services,schemaContext);
+ return registerMountPoint(mountPoint);
+ }
+ }
+
+ private final class MountRegistration implements ObjectRegistration<DOMMountPoint> {
+ private final DOMMountPoint mountPoint;
+
+ public MountRegistration(final DOMMountPoint mountPoint) {
+ this.mountPoint = mountPoint;
+ }
+
+ @Override
+ public DOMMountPoint getInstance() {
+ return mountPoint;
+ }
+
+ @Override
+ public void close() throws Exception {
+ unregisterMountPoint(mountPoint.getIdentifier());
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.dom.broker;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.ImmutableClassToInstanceMap;
+import com.google.common.util.concurrent.CheckedFuture;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcAvailabilityListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementation;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationRegistration;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcProviderService;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
+import org.opendaylight.controller.md.sal.dom.broker.impl.DOMRpcRouter;
+import org.opendaylight.controller.sal.core.api.Broker;
+import org.opendaylight.controller.sal.core.api.BrokerService;
+import org.opendaylight.controller.sal.core.api.Consumer;
+import org.opendaylight.controller.sal.core.api.Provider;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.osgi.framework.BundleContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class BrokerImpl implements Broker, DOMRpcProviderService, DOMRpcService, AutoCloseable {
+ private final static Logger log = LoggerFactory.getLogger(BrokerImpl.class);
+
+ // Broker Generic Context
+ private final Set<ConsumerContextImpl> sessions = Collections
+ .synchronizedSet(new HashSet<ConsumerContextImpl>());
+ private final Set<ProviderContextImpl> providerSessions = Collections
+ .synchronizedSet(new HashSet<ProviderContextImpl>());
+
+ private AutoCloseable deactivator = null;
+
+ private DOMRpcRouter router = null;
+
+ private final ClassToInstanceMap<BrokerService> services;
+
+ public BrokerImpl(final DOMRpcRouter router,final ClassToInstanceMap<BrokerService> services) {
+ this.router = Preconditions.checkNotNull(router, "RPC Router must not be null");
+ this.services = ImmutableClassToInstanceMap.copyOf(services);
+ }
+
+
+ @Override
+ public ConsumerSession registerConsumer(final Consumer consumer,
+ final BundleContext ctx) {
+ return registerConsumer(consumer);
+ }
+
+ @Override
+ public ProviderSession registerProvider(final Provider provider,
+ final BundleContext ctx) {
+ return registerProvider(provider);
+ }
+
+ // Validation
+ private void checkPredicates(final Provider prov) {
+ Preconditions.checkNotNull(prov, "Provider should not be null.");
+ for (final ProviderContextImpl session : providerSessions) {
+ if (prov.equals(session.getProvider())) {
+ throw new IllegalStateException("Provider already registered");
+ }
+ }
+
+ }
+
+ private void checkPredicates(final Consumer cons) {
+ Preconditions.checkNotNull(cons, "Consumer should not be null.");
+ for (final ConsumerContextImpl session : sessions) {
+ if (cons.equals(session.getConsumer())) {
+ throw new IllegalStateException("Consumer already registered");
+ }
+ }
+ }
+
+ // Private Factory methods
+ private ConsumerContextImpl newSessionFor(final Consumer provider) {
+ final ConsumerContextImpl ret = new ConsumerContextImpl(provider, this);
+ return ret;
+ }
+
+ private ProviderContextImpl newSessionFor(final Provider provider) {
+ final ProviderContextImpl ret = new ProviderContextImpl(provider, this);
+ return ret;
+ }
+
+ protected void consumerSessionClosed(
+ final ConsumerContextImpl consumerContextImpl) {
+ sessions.remove(consumerContextImpl);
+ providerSessions.remove(consumerContextImpl);
+ }
+
+ @Override
+ public void close() throws Exception {
+ if (deactivator != null) {
+ deactivator.close();
+ deactivator = null;
+ }
+ }
+
+ /**
+ * @return the deactivator
+ */
+ public AutoCloseable getDeactivator() {
+ return deactivator;
+ }
+
+ /**
+ * @param deactivator
+ * the deactivator to set
+ */
+ public void setDeactivator(final AutoCloseable deactivator) {
+ this.deactivator = deactivator;
+ }
+
+ /**
+ * @return the router
+ */
+ public DOMRpcRouter getRouter() {
+ return router;
+ }
+
+ /**
+ * @param router
+ * the router to set
+ */
+ public void setRouter(final DOMRpcRouter router) {
+ this.router = router;
+ }
+
+ protected <T extends BrokerService> Optional<T> getGlobalService(final Class<T> service) {
+ return Optional.fromNullable(services.getInstance(service));
+ }
+
+
+ @Override
+ public ConsumerSession registerConsumer(final Consumer consumer) {
+ checkPredicates(consumer);
+ log.trace("Registering consumer {}", consumer);
+ final ConsumerContextImpl session = newSessionFor(consumer);
+ consumer.onSessionInitiated(session);
+ sessions.add(session);
+ return session;
+ }
+
+
+ @Override
+ public ProviderSession registerProvider(final Provider provider) {
+ checkPredicates(provider);
+ final ProviderContextImpl session = newSessionFor(provider);
+ provider.onSessionInitiated(session);
+ providerSessions.add(session);
+ return session;
+ }
+
+
+ @Nonnull
+ @Override
+ public <T extends DOMRpcImplementation> DOMRpcImplementationRegistration<T> registerRpcImplementation(@Nonnull final T implementation, @Nonnull final DOMRpcIdentifier... rpcs) {
+ return router.registerRpcImplementation(implementation, rpcs);
+ }
+
+ @Nonnull
+ @Override
+ public <T extends DOMRpcImplementation> DOMRpcImplementationRegistration<T> registerRpcImplementation(@Nonnull final T implementation, @Nonnull final Set<DOMRpcIdentifier> rpcs) {
+ return router.registerRpcImplementation(implementation, rpcs);
+ }
+
+ @Nonnull
+ @Override
+ public CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(@Nonnull final SchemaPath type, @Nullable final NormalizedNode<?, ?> input) {
+ return router.invokeRpc(type, input);
+ }
+
+ @Nonnull
+ @Override
+ public <T extends DOMRpcAvailabilityListener> ListenerRegistration<T> registerRpcListener(@Nonnull final T listener) {
+ return router.registerRpcListener(listener);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.dom.broker;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.MutableClassToInstanceMap;
+import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;
+import org.opendaylight.controller.sal.core.api.BrokerService;
+import org.opendaylight.controller.sal.core.api.Consumer;
+import org.opendaylight.controller.sal.dom.broker.osgi.AbstractBrokerServiceProxy;
+import org.opendaylight.controller.sal.dom.broker.osgi.ProxyFactory;
+
+import javax.annotation.concurrent.GuardedBy;
+import java.util.Collection;
+
+class ConsumerContextImpl implements ConsumerSession {
+
+ private final ClassToInstanceMap<BrokerService> instantiatedServices = MutableClassToInstanceMap
+ .create();
+ private final Consumer consumer;
+
+ private BrokerImpl broker = null;
+ @GuardedBy("this")
+ private boolean closed = false;
+
+ public ConsumerContextImpl(final Consumer provider, final BrokerImpl brokerImpl) {
+ broker = brokerImpl;
+ consumer = provider;
+ }
+
+ @Override
+ public <T extends BrokerService> T getService(final Class<T> service) {
+ checkNotClosed();
+ final T localProxy = instantiatedServices.getInstance(service);
+ if (localProxy != null) {
+ return localProxy;
+ }
+ final Optional<T> serviceImpl = broker.getGlobalService(service);
+ if(serviceImpl.isPresent()) {
+ final T ret = ProxyFactory.createProxy(null,serviceImpl.get());
+ instantiatedServices.putInstance(service, ret);
+ return ret;
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void close() {
+ synchronized (this) {
+ if (closed) {
+ return;
+ }
+ this.closed = true;
+ }
+
+ Collection<BrokerService> toStop = instantiatedServices.values();
+ for (BrokerService brokerService : toStop) {
+ if (brokerService instanceof AbstractBrokerServiceProxy<?>) {
+ ((AbstractBrokerServiceProxy<?>) brokerService).close();
+ }
+ }
+ broker.consumerSessionClosed(this);
+ broker = null;
+ }
+
+
+ @Override
+ public synchronized boolean isClosed() {
+ return closed;
+ }
+
+ /**
+ * @return the broker
+ */
+ protected final BrokerImpl getBrokerChecked() {
+ checkNotClosed();
+ return broker;
+ }
+
+ /**
+ * @return the _consumer
+ */
+ public Consumer getConsumer() {
+ return consumer;
+ }
+
+ protected final void checkNotClosed() {
+ Preconditions.checkState(!closed, "Session is closed.");
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.dom.broker;
+
+import static com.google.common.base.Preconditions.checkState;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.util.ListenerRegistry;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
+import org.opendaylight.yangtools.yang.parser.repo.URLSchemaContextResolver;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.BundleTracker;
+import org.osgi.util.tracker.BundleTrackerCustomizer;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvider, SchemaService, ServiceTrackerCustomizer<SchemaContextListener, SchemaContextListener>, AutoCloseable {
+ private static final Logger LOG = LoggerFactory.getLogger(GlobalBundleScanningSchemaServiceImpl.class);
+
+ private final ListenerRegistry<SchemaContextListener> listeners = new ListenerRegistry<>();
+ private final URLSchemaContextResolver contextResolver = URLSchemaContextResolver.create("global-bundle");
+ private final BundleScanner scanner = new BundleScanner();
+ private final BundleContext context;
+
+ private ServiceTracker<SchemaContextListener, SchemaContextListener> listenerTracker;
+ private BundleTracker<Iterable<Registration>> bundleTracker;
+ private boolean starting = true;
+ private static GlobalBundleScanningSchemaServiceImpl instance;
+
+ private GlobalBundleScanningSchemaServiceImpl(final BundleContext context) {
+ this.context = Preconditions.checkNotNull(context);
+ }
+
+ public synchronized static GlobalBundleScanningSchemaServiceImpl createInstance(final BundleContext ctx) {
+ Preconditions.checkState(instance == null);
+ instance = new GlobalBundleScanningSchemaServiceImpl(ctx);
+ instance.start();
+ return instance;
+ }
+
+ public synchronized static GlobalBundleScanningSchemaServiceImpl getInstance() {
+ Preconditions.checkState(instance != null, "Global Instance was not instantiated");
+ return instance;
+ }
+
+ @VisibleForTesting
+ public static synchronized void destroyInstance() {
+ try {
+ instance.close();
+ } finally {
+ instance = null;
+ }
+ }
+
+ public BundleContext getContext() {
+ return context;
+ }
+
+ public void start() {
+ checkState(context != null);
+ LOG.debug("start() starting");
+
+ listenerTracker = new ServiceTracker<>(context, SchemaContextListener.class, GlobalBundleScanningSchemaServiceImpl.this);
+ bundleTracker = new BundleTracker<>(context, Bundle.RESOLVED | Bundle.STARTING |
+ Bundle.STOPPING | Bundle.ACTIVE, scanner);
+ bundleTracker.open();
+
+ LOG.debug("BundleTracker.open() complete");
+
+ listenerTracker.open();
+ starting = false;
+ tryToUpdateSchemaContext();
+
+ LOG.debug("start() complete");
+ }
+
+ @Override
+ public SchemaContext getSchemaContext() {
+ return getGlobalContext();
+ }
+
+ @Override
+ public SchemaContext getGlobalContext() {
+ return contextResolver.getSchemaContext().orNull();
+ }
+
+ @Override
+ public void addModule(final Module module) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public SchemaContext getSessionContext() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void removeModule(final Module module) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public synchronized ListenerRegistration<SchemaContextListener> registerSchemaContextListener(final SchemaContextListener listener) {
+ Optional<SchemaContext> potentialCtx = contextResolver.getSchemaContext();
+ if(potentialCtx.isPresent()) {
+ listener.onGlobalContextUpdated(potentialCtx.get());
+ }
+ return listeners.register(listener);
+ }
+
+ @Override
+ public void close() {
+ if (bundleTracker != null) {
+ bundleTracker.close();
+ }
+ if (listenerTracker != null) {
+ listenerTracker.close();
+ }
+
+ for (ListenerRegistration<SchemaContextListener> l : listeners.getListeners()) {
+ l.close();
+ }
+ }
+
+ private synchronized void updateContext(final SchemaContext snapshot) {
+ Object[] services = listenerTracker.getServices();
+ for (ListenerRegistration<SchemaContextListener> listener : listeners) {
+ try {
+ listener.getInstance().onGlobalContextUpdated(snapshot);
+ } catch (Exception e) {
+ LOG.error("Exception occured during invoking listener", e);
+ }
+ }
+ if (services != null) {
+ for (Object rawListener : services) {
+ final SchemaContextListener listener = (SchemaContextListener) rawListener;
+ try {
+ listener.onGlobalContextUpdated(snapshot);
+ } catch (Exception e) {
+ LOG.error("Exception occured during invoking listener {}", listener, e);
+ }
+ }
+ }
+ }
+
+ private class BundleScanner implements BundleTrackerCustomizer<Iterable<Registration>> {
+ @Override
+ public Iterable<Registration> addingBundle(final Bundle bundle, final BundleEvent event) {
+
+ if (bundle.getBundleId() == 0) {
+ return Collections.emptyList();
+ }
+
+ final Enumeration<URL> enumeration = bundle.findEntries("META-INF/yang", "*.yang", false);
+ if (enumeration == null) {
+ return Collections.emptyList();
+ }
+
+ final List<Registration> urls = new ArrayList<>();
+ while (enumeration.hasMoreElements()) {
+ final URL u = enumeration.nextElement();
+ try {
+ urls.add(contextResolver.registerSource(u));
+ LOG.debug("Registered {}", u);
+ } catch (Exception e) {
+ LOG.warn("Failed to register {}, ignoring it", e);
+ }
+ }
+
+ if (!urls.isEmpty()) {
+ LOG.debug("Loaded {} new URLs from bundle {}, attempting to rebuild schema context",
+ urls.size(), bundle.getSymbolicName());
+ tryToUpdateSchemaContext();
+ }
+
+ return ImmutableList.copyOf(urls);
+ }
+
+ @Override
+ public void modifiedBundle(final Bundle bundle, final BundleEvent event, final Iterable<Registration> object) {
+ }
+
+ /**
+ * If removing YANG files makes yang store inconsistent, method
+ * {@link #getYangStoreSnapshot()} will throw exception. There is no
+ * rollback.
+ */
+
+ @Override
+ public synchronized void removedBundle(final Bundle bundle, final BundleEvent event, final Iterable<Registration> urls) {
+ for (Registration url : urls) {
+ try {
+ url.close();
+ } catch (Exception e) {
+ LOG.warn("Failed do unregister URL {}, proceeding", url, e);
+ }
+ }
+
+ int numUrls = Iterables.size(urls);
+ if(numUrls > 0 ) {
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("removedBundle: {}, state: {}, # urls: {}", bundle.getSymbolicName(), bundle.getState(), numUrls);
+ }
+
+ tryToUpdateSchemaContext();
+ }
+ }
+ }
+
+ @Override
+ public synchronized SchemaContextListener addingService(final ServiceReference<SchemaContextListener> reference) {
+
+ SchemaContextListener listener = context.getService(reference);
+ SchemaContext _ctxContext = getGlobalContext();
+ if (getContext() != null && _ctxContext != null) {
+ listener.onGlobalContextUpdated(_ctxContext);
+ }
+ return listener;
+ }
+
+ public synchronized void tryToUpdateSchemaContext() {
+ if (starting) {
+ return;
+ }
+ Optional<SchemaContext> schema = contextResolver.getSchemaContext();
+ if(schema.isPresent()) {
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("Got new SchemaContext: # of modules {}", schema.get().getAllModuleIdentifiers().size());
+ }
+
+ updateContext(schema.get());
+ }
+ }
+
+ @Override
+ public void modifiedService(final ServiceReference<SchemaContextListener> reference, final SchemaContextListener service) {
+ // NOOP
+ }
+
+ @Override
+ public void removedService(final ServiceReference<SchemaContextListener> reference, final SchemaContextListener service) {
+ context.ungetService(reference);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.dom.broker;
+
+import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
+import org.opendaylight.controller.sal.core.api.Provider;
+
+class ProviderContextImpl extends ConsumerContextImpl implements ProviderSession {
+ private final Provider provider;
+
+ public ProviderContextImpl(final Provider provider, final BrokerImpl broker) {
+ super(null, broker);
+ this.provider = provider;
+ }
+
+ @Override
+ public void close() {
+ }
+
+ /**
+ * @return the provider
+ */
+ public Provider getProvider() {
+ return provider;
+ }
+
+ /**
+ * @param provider
+ * the provider to set
+ */
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.dom.broker.impl;
+
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.yangtools.concepts.Delegator;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
+
+public class SchemaContextProviders {
+
+ private SchemaContextProviders() {
+ throw new UnsupportedOperationException("Utility class.");
+ }
+
+ public static final SchemaContextProvider fromSchemaService(final SchemaService schemaService) {
+ if (schemaService instanceof SchemaContextProvider) {
+ return (SchemaContextProvider) schemaService;
+ }
+ return new SchemaServiceAdapter(schemaService);
+ }
+
+ private static final class SchemaServiceAdapter implements SchemaContextProvider, Delegator<SchemaService> {
+
+ private final SchemaService service;
+
+ public SchemaServiceAdapter(final SchemaService service) {
+ super();
+ this.service = service;
+ }
+
+ @Override
+ public SchemaContext getSchemaContext() {
+ return service.getGlobalContext();
+ }
+
+ @Override
+ public SchemaService getDelegate() {
+ return service;
+ }
+
+ @Override
+ public String toString() {
+ return "SchemaServiceAdapter [service=" + service + "]";
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.dom.broker.osgi;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.annotation.Nullable;
+
+import org.opendaylight.controller.sal.core.api.BrokerService;
+import org.opendaylight.yangtools.concepts.Registration;
+import org.osgi.framework.ServiceReference;
+
+public abstract class AbstractBrokerServiceProxy<T extends BrokerService> implements AutoCloseable, BrokerService {
+
+ private T delegate;
+ private final ServiceReference<T> reference;
+
+ public AbstractBrokerServiceProxy(final @Nullable ServiceReference<T> ref, final T delegate) {
+ this.delegate = checkNotNull(delegate, "Delegate should not be null.");
+ this.reference = ref;
+ }
+
+ protected final T getDelegate() {
+ checkState(delegate != null, "Proxy was closed and unregistered.");
+ return delegate;
+ }
+
+ protected final ServiceReference<T> getReference() {
+ return reference;
+ }
+
+ private final Set<Registration> registrations = Collections.synchronizedSet(new HashSet<Registration>());
+
+ protected <R extends Registration> R addRegistration(final R registration) {
+ if (registration != null) {
+ registrations.add(registration);
+ }
+ return registration;
+ }
+
+ protected void closeBeforeUnregistrations() {
+ // NOOP
+ }
+
+ protected void closeAfterUnregistrations() {
+ // NOOP
+ }
+
+ @Override
+ public void close() {
+ if (delegate != null) {
+ delegate = null;
+ RuntimeException potentialException = new RuntimeException(
+ "Uncaught exceptions occured during unregistration");
+ boolean hasSuppressed = false;
+ for (Registration registration : registrations) {
+ try {
+ registration.close();
+ } catch (Exception e) {
+ potentialException.addSuppressed(e);
+ hasSuppressed = true;
+ }
+ }
+ if (hasSuppressed) {
+ throw potentialException;
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.sal.dom.broker.osgi;
+
+import java.util.Map;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBrokerExtension;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.osgi.framework.ServiceReference;
+
+public class DOMDataBrokerProxy extends AbstractBrokerServiceProxy<DOMDataBroker> implements DOMDataBroker {
+
+ public DOMDataBrokerProxy(final ServiceReference<DOMDataBroker> ref, final DOMDataBroker delegate) {
+ super(ref, delegate);
+ }
+
+ @Override
+ public DOMDataReadOnlyTransaction newReadOnlyTransaction() {
+ return getDelegate().newReadOnlyTransaction();
+ }
+
+ @Override
+ public DOMDataReadWriteTransaction newReadWriteTransaction() {
+ return getDelegate().newReadWriteTransaction();
+ }
+
+ @Override
+ public DOMDataWriteTransaction newWriteOnlyTransaction() {
+ return getDelegate().newWriteOnlyTransaction();
+ }
+
+ @Override
+ public ListenerRegistration<DOMDataChangeListener> registerDataChangeListener(final LogicalDatastoreType store,
+ final YangInstanceIdentifier path, final DOMDataChangeListener listener,
+ final org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope triggeringScope) {
+ return getDelegate().registerDataChangeListener(store, path, listener, triggeringScope);
+ }
+
+ @Override
+ public DOMTransactionChain createTransactionChain(final TransactionChainListener listener) {
+ return getDelegate().createTransactionChain(listener);
+ }
+
+ @Override
+ public Map<Class<? extends DOMDataBrokerExtension>, DOMDataBrokerExtension> getSupportedExtensions() {
+ return getDelegate().getSupportedExtensions();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.sal.dom.broker.osgi;
+
+import com.google.common.base.Optional;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.osgi.framework.ServiceReference;
+
+public class DOMMountPointServiceProxy extends AbstractBrokerServiceProxy<DOMMountPointService> implements DOMMountPointService{
+
+
+ public DOMMountPointServiceProxy(final ServiceReference<DOMMountPointService> ref, final DOMMountPointService delegate) {
+ super(ref, delegate);
+ }
+
+ @Override
+ public Optional<DOMMountPoint> getMountPoint(final YangInstanceIdentifier path) {
+ return getDelegate().getMountPoint(path);
+ }
+
+ @Override
+ public DOMMountPointBuilder createMountPoint(final YangInstanceIdentifier path) {
+ return getDelegate().createMountPoint(path);
+ }
+
+ public ListenerRegistration<MountProvisionListener> registerProvisionListener(final MountProvisionListener listener) {
+ return getDelegate().registerProvisionListener(listener);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.dom.broker.osgi;
+
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
+import org.opendaylight.controller.sal.core.api.BrokerService;
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.osgi.framework.ServiceReference;
+
+import java.util.Arrays;
+
+@SuppressWarnings("unchecked")
+public class ProxyFactory {
+
+ public static <T extends BrokerService> T createProxy(
+ final ServiceReference<T> serviceRef, final T service) {
+
+ Object _createProxyImpl = ProxyFactory.createProxyImpl(serviceRef,
+ service);
+ return ((T) _createProxyImpl);
+ }
+
+ private static Object createProxyImpl(final ServiceReference<?> ref,
+ final DOMMountPointService service) {
+
+ return new DOMMountPointServiceProxy(
+ ((ServiceReference<DOMMountPointService>) ref), service);
+ }
+
+ private static Object createProxyImpl(final ServiceReference<?> ref,
+ final SchemaService service) {
+
+ return new SchemaServiceProxy(((ServiceReference<SchemaService>) ref),
+ service);
+ }
+
+ private static DOMDataBrokerProxy createProxyImpl(
+ final ServiceReference<?> ref, final DOMDataBroker service) {
+
+ return new DOMDataBrokerProxy(((ServiceReference<DOMDataBroker>) ref),
+ service);
+ }
+
+ private static Object createProxyImplFallback(final ServiceReference<?> reference,
+ final BrokerService service) {
+
+ return service;
+ }
+
+ private static Object createProxyImpl(final ServiceReference<?> ref,
+ final BrokerService service) {
+
+ if (service instanceof DOMDataBroker) {
+ return createProxyImpl(ref, (DOMDataBroker) service);
+ } else if (service instanceof SchemaService) {
+ return createProxyImpl(ref, (SchemaService) service);
+ } else if (service instanceof DOMMountPointService) {
+ return createProxyImpl(ref, (DOMMountPointService) service);
+ } else if (service != null) {
+ return createProxyImplFallback(ref, service);
+ } else {
+ throw new IllegalArgumentException("Unhandled parameter types: "
+ + Arrays.<Object> asList(ref, service).toString());
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.dom.broker.osgi;
+
+import java.util.Hashtable;
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.controller.sal.dom.broker.GlobalBundleScanningSchemaServiceImpl;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+public class SchemaServiceActivator implements BundleActivator {
+
+
+ private ServiceRegistration<SchemaService> schemaServiceReg;
+ private GlobalBundleScanningSchemaServiceImpl schemaService;
+
+ @Override
+ public void start(final BundleContext context) {
+ schemaService = GlobalBundleScanningSchemaServiceImpl.createInstance(context);
+ schemaServiceReg = context.registerService(SchemaService.class, schemaService, new Hashtable<String,String>());
+ }
+
+ @Override
+ public void stop(final BundleContext context) throws Exception {
+ schemaServiceReg.unregister();
+ schemaService.close();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.dom.broker.osgi;
+
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+import org.osgi.framework.ServiceReference;
+
+public class SchemaServiceProxy extends AbstractBrokerServiceProxy<SchemaService> implements SchemaService {
+
+ public SchemaServiceProxy(ServiceReference<SchemaService> ref, SchemaService delegate) {
+ super(ref, delegate);
+ }
+
+ @Override
+ public void addModule(Module module) {
+ getDelegate().addModule(module);
+ }
+
+ @Override
+ public void removeModule(Module module) {
+ getDelegate().removeModule(module);
+ }
+
+ @Override
+ public SchemaContext getSessionContext() {
+ return null;
+ }
+
+ @Override
+ public SchemaContext getGlobalContext() {
+ return getDelegate().getGlobalContext();
+ }
+
+ @Override
+ public ListenerRegistration<SchemaContextListener> registerSchemaContextListener(SchemaContextListener listener) {
+ ListenerRegistration<SchemaContextListener> registration = getDelegate().registerSchemaContextListener(listener);
+ addRegistration(registration);
+ return registration;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.dom.broker;
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.sal.dom.broker.util;
+
+import java.net.URI;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
+import org.opendaylight.yangtools.yang.model.api.ConstraintDefinition;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.ModuleIdentifier;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.UsesNode;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+
+/**
+ * ProxySchema Context for SchemaContextProviders
+ */
+public class ProxySchemaContext implements SchemaContext {
+
+ private final SchemaContextProvider schemaProvider;
+
+ public ProxySchemaContext(final SchemaContextProvider schemaProvider) {
+ this.schemaProvider = schemaProvider;
+ }
+
+ private SchemaContext getCurrentSchema() {
+ Preconditions.checkState(schemaProvider.getSchemaContext() != null, "Schema context unavailable from %s", schemaProvider);
+ return schemaProvider.getSchemaContext();
+ }
+
+ @Override
+ public Set<DataSchemaNode> getDataDefinitions() {
+ return getCurrentSchema().getDataDefinitions();
+ }
+
+ @Override
+ public Set<Module> getModules() {
+ return getCurrentSchema().getModules();
+ }
+
+ @Override
+ public Set<NotificationDefinition> getNotifications() {
+ return getCurrentSchema().getNotifications();
+ }
+
+ @Override
+ public Set<RpcDefinition> getOperations() {
+ return getCurrentSchema().getOperations();
+ }
+
+ @Override
+ public Set<ExtensionDefinition> getExtensions() {
+ return getCurrentSchema().getExtensions();
+ }
+
+ @Override
+ public Module findModuleByName(final String s, final Date date) {
+ return getCurrentSchema().findModuleByName(s, date);
+ }
+
+ @Override
+ public Set<Module> findModuleByNamespace(final URI uri) {
+ return getCurrentSchema().findModuleByNamespace(uri);
+ }
+
+ @Override
+ public Module findModuleByNamespaceAndRevision(final URI uri, final Date date) {
+ return getCurrentSchema().findModuleByNamespaceAndRevision(uri, date);
+ }
+
+ @Override
+ public Optional<String> getModuleSource(final ModuleIdentifier moduleIdentifier) {
+ return getCurrentSchema().getModuleSource(moduleIdentifier);
+ }
+
+ @Override
+ public Set<ModuleIdentifier> getAllModuleIdentifiers() {
+ return getCurrentSchema().getAllModuleIdentifiers();
+ }
+
+ @Override
+ public boolean isPresenceContainer() {
+ return getCurrentSchema().isPresenceContainer();
+ }
+
+ @Override
+ public Set<TypeDefinition<?>> getTypeDefinitions() {
+ return getCurrentSchema().getTypeDefinitions();
+ }
+
+ @Override
+ public Collection<DataSchemaNode> getChildNodes() {
+ return getCurrentSchema().getChildNodes();
+ }
+
+ @Override
+ public Set<GroupingDefinition> getGroupings() {
+ return getCurrentSchema().getGroupings();
+ }
+
+ @Override
+ public DataSchemaNode getDataChildByName(final QName qName) {
+ return getCurrentSchema().getDataChildByName(qName);
+ }
+
+ @Override
+ public DataSchemaNode getDataChildByName(final String s) {
+ return getCurrentSchema().getDataChildByName(s);
+ }
+
+ @Override
+ public Set<UsesNode> getUses() {
+ return getCurrentSchema().getUses();
+ }
+
+ @Override
+ public Set<AugmentationSchema> getAvailableAugmentations() {
+ return getCurrentSchema().getAvailableAugmentations();
+ }
+
+ @Override
+ public boolean isAugmenting() {
+ return getCurrentSchema().isAugmenting();
+ }
+
+ @Override
+ public boolean isAddedByUses() {
+ return getCurrentSchema().isAddedByUses();
+ }
+
+ @Override
+ public boolean isConfiguration() {
+ return getCurrentSchema().isConfiguration();
+ }
+
+ @Override
+ public ConstraintDefinition getConstraints() {
+ return getCurrentSchema().getConstraints();
+ }
+
+ @Override
+ public QName getQName() {
+ return getCurrentSchema().getQName();
+ }
+
+ @Override
+ public SchemaPath getPath() {
+ return getCurrentSchema().getPath();
+ }
+
+ @Override
+ public List<UnknownSchemaNode> getUnknownSchemaNodes() {
+ return getCurrentSchema().getUnknownSchemaNodes();
+ }
+
+ @Override
+ public String getDescription() {
+ return getCurrentSchema().getDescription();
+ }
+
+ @Override
+ public String getReference() {
+ return getCurrentSchema().getReference();
+ }
+
+ @Override
+ public Status getStatus() {
+ return getCurrentSchema().getStatus();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.dom.broker.util;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.base.Function;
+import com.google.common.collect.FluentIterable;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ConstraintDefinition;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.UsesNode;
+
+public final class YangSchemaUtils {
+
+ private static final Function<PathArgument, QName> QNAME_FROM_PATH_ARGUMENT = new Function<PathArgument, QName>(){
+
+ @Override
+ public QName apply(final PathArgument input) {
+ if(input == null) {
+ return null;
+ }
+ return input.getNodeType();
+ }
+ };
+
+ private YangSchemaUtils() {
+ throw new UnsupportedOperationException("Utility class.");
+ }
+
+ public static DataSchemaNode getSchemaNode(final SchemaContext schema,final YangInstanceIdentifier path) {
+ checkArgument(schema != null,"YANG Schema must not be null.");
+ checkArgument(path != null,"Path must not be null.");
+ return getSchemaNode(schema, FluentIterable.from(path.getPathArguments()).transform(QNAME_FROM_PATH_ARGUMENT));
+ }
+
+ public static DataSchemaNode getSchemaNode(final SchemaContext schema,final Iterable<QName> path) {
+ checkArgument(schema != null,"YANG Schema must not be null.");
+ checkArgument(path != null,"Path must not be null.");
+ if(!path.iterator().hasNext()){
+ return toRootDataNode(schema);
+ }
+
+ QName firstNode = path.iterator().next();
+ DataNodeContainer previous = schema.findModuleByNamespaceAndRevision(firstNode.getNamespace(),
+ firstNode.getRevision());
+ Iterator<QName> iterator = path.iterator();
+
+ while (iterator.hasNext()) {
+ checkArgument(previous!= null, "Supplied path does not resolve into valid schema node.");
+ QName arg = iterator.next();
+ DataSchemaNode currentNode = previous.getDataChildByName(arg);
+ if (currentNode == null && previous instanceof DataNodeContainer) {
+ currentNode = searchInChoices(previous, arg);
+ }
+ if (currentNode instanceof DataNodeContainer) {
+ previous = (DataNodeContainer) currentNode;
+ } else if (currentNode instanceof LeafSchemaNode || currentNode instanceof LeafListSchemaNode) {
+ checkArgument(!iterator.hasNext(), "Path nests inside leaf node, which is not allowed.");
+ return currentNode;
+ }
+ checkState(currentNode != null, "Current node should not be null for %s",path);
+ }
+ checkState(previous instanceof DataSchemaNode, "Schema node for %s should be instance of DataSchemaNode. Found %s",path,previous);
+ return (DataSchemaNode) previous;
+ }
+
+ private static DataSchemaNode searchInChoices(final DataNodeContainer node, final QName arg) {
+ for (DataSchemaNode child : node.getChildNodes()) {
+ if (child instanceof ChoiceSchemaNode) {
+ ChoiceSchemaNode choiceNode = (ChoiceSchemaNode) child;
+ DataSchemaNode potential = searchInCases(choiceNode, arg);
+ if (potential != null) {
+ return potential;
+ }
+ }
+ }
+ return null;
+ }
+
+ private static DataSchemaNode searchInCases(final ChoiceSchemaNode choiceNode, final QName arg) {
+ Set<ChoiceCaseNode> cases = choiceNode.getCases();
+ for (ChoiceCaseNode caseNode : cases) {
+ DataSchemaNode node = caseNode.getDataChildByName(arg);
+ if (node != null) {
+ return node;
+ }
+ }
+ return null;
+ }
+
+ private static ContainerSchemaNode toRootDataNode(final SchemaContext schema) {
+ return new NetconfDataRootNode(schema);
+ }
+
+ private static final class NetconfDataRootNode implements ContainerSchemaNode {
+
+ public NetconfDataRootNode(final SchemaContext schema) {
+ // TODO Auto-generated constructor stub
+ }
+
+ @Override
+ public Set<TypeDefinition<?>> getTypeDefinitions() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Set<DataSchemaNode> getChildNodes() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Set<GroupingDefinition> getGroupings() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public DataSchemaNode getDataChildByName(final QName name) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public DataSchemaNode getDataChildByName(final String name) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Set<UsesNode> getUses() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Set<AugmentationSchema> getAvailableAugmentations() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public boolean isAugmenting() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean isAddedByUses() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean isConfiguration() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public ConstraintDefinition getConstraints() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public QName getQName() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public SchemaPath getPath() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String getDescription() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String getReference() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Status getStatus() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public List<UnknownSchemaNode> getUnknownSchemaNodes() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public boolean isPresenceContainer() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
+
+import com.google.common.util.concurrent.SettableFuture;
+
+/**
+ * Simple implementation of {@link TransactionChainListener} for testing.
+ *
+ * This transaction chain listener does not contain any logic, only update
+ * futures ({@link #getFailFuture()} and {@link #getSuccessFuture()} when
+ * transaction chain event is retrieved.
+ *
+ */
+class BlockingTransactionChainListener implements TransactionChainListener {
+
+ private final SettableFuture<Throwable> failFuture = SettableFuture.create();
+ private final SettableFuture<Void> successFuture = SettableFuture.create();
+
+ @Override
+ public void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction,
+ final Throwable cause) {
+ failFuture.set(cause);
+ }
+
+ @Override
+ public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
+ successFuture.set(null);
+ }
+
+ public SettableFuture<Throwable> getFailFuture() {
+ return failFuture;
+ }
+
+ public SettableFuture<Void> getSuccessFuture() {
+ return successFuture;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
+import org.opendaylight.controller.md.sal.dom.store.impl.TestModel;
+import org.opendaylight.controller.sal.core.spi.data.DOMStore;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DOMBrokerPerformanceTest {
+
+ private static final Logger log = LoggerFactory.getLogger(DOMBrokerPerformanceTest.class);
+
+ private static NormalizedNode<?, ?> outerList(final int i) {
+ return ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, i);
+ }
+
+ private static YangInstanceIdentifier outerListPath(final int i) {
+ return YangInstanceIdentifier.builder(TestModel.OUTER_LIST_PATH)//
+ .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, i) //
+ .build();
+ }
+
+ private SchemaContext schemaContext;
+ private AbstractDOMDataBroker domBroker;
+
+ private static <V> V measure(final String name, final Callable<V> callable) throws Exception {
+ // TODO Auto-generated method stub
+ log.debug("Measurement:{} Start", name);
+ long startNano = System.nanoTime();
+ try {
+ return callable.call();
+ } finally {
+ long endNano = System.nanoTime();
+ log.info("Measurement:\"{}\" Time:{} ms", name, (endNano - startNano) / 1000000.0d);
+ }
+ }
+
+ @Before
+ public void setupStore() {
+ InMemoryDOMDataStore operStore = new InMemoryDOMDataStore("OPER", MoreExecutors.newDirectExecutorService());
+ InMemoryDOMDataStore configStore = new InMemoryDOMDataStore("CFG", MoreExecutors.newDirectExecutorService());
+ schemaContext = TestModel.createTestContext();
+
+ operStore.onGlobalContextUpdated(schemaContext);
+ configStore.onGlobalContextUpdated(schemaContext);
+
+ ImmutableMap<LogicalDatastoreType, DOMStore> stores = ImmutableMap.<LogicalDatastoreType, DOMStore> builder() //
+ .put(CONFIGURATION, configStore) //
+ .put(OPERATIONAL, operStore) //
+ .build();
+ ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor());
+ domBroker = new SerializedDOMDataBroker(stores, executor);
+ }
+
+ @Test
+ public void testPerformance() throws Exception {
+ measure("Test Suite (all tests)", new Callable<Void>() {
+
+ @Override
+ public Void call() throws Exception {
+ smallTestSuite(10, 1000);
+ //smallTestSuite(10, 100);
+ smallTestSuite(100, 100);
+ //smallTestSuite(100, 100);
+ //smallTestSuite(1000, 10);
+ smallTestSuite(1000, 10);
+ //smallTestSuite(1000, 1000);
+ return null;
+ }
+ });
+ }
+
+ private void smallTestSuite(final int txNum, final int innerListWriteNum) throws Exception {
+ measure("TestSuite (Txs:" + txNum + " innerWrites:" + innerListWriteNum + ")", new Callable<Void>() {
+
+ @Override
+ public Void call() throws Exception {
+ measureOneTransactionTopContainer();
+ measureSeparateWritesOneLevel(txNum, innerListWriteNum);
+ return null;
+ }
+ });
+ }
+
+ private void measureSeparateWritesOneLevel(final int txNum, final int innerNum) throws Exception {
+ final List<DOMDataReadWriteTransaction> transactions = measure("Txs:"+ txNum + " Allocate",
+ new Callable<List<DOMDataReadWriteTransaction>>() {
+ @Override
+ public List<DOMDataReadWriteTransaction> call() throws Exception {
+ List<DOMDataReadWriteTransaction> builder = new ArrayList<>(txNum);
+ for (int i = 0; i < txNum; i++) {
+ DOMDataReadWriteTransaction writeTx = domBroker.newReadWriteTransaction();
+ builder.add(writeTx);
+ }
+ return builder;
+ }
+ });
+ assertEquals(txNum, transactions.size());
+ measure("Txs:"+ txNum + " Writes:1", new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ int i = 0;
+ for (DOMDataReadWriteTransaction writeTx :transactions) {
+ // Writes /test/outer-list/i in writeTx
+ writeTx.put(OPERATIONAL, outerListPath(i), outerList(i));
+ i++;
+ }
+ return null;
+ }
+ });
+
+ measure("Txs:"+ txNum + " Writes:" + innerNum, new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ int i = 0;
+ for (DOMDataReadWriteTransaction writeTx :transactions) {
+ // Writes /test/outer-list/i in writeTx
+ YangInstanceIdentifier path = YangInstanceIdentifier.builder(outerListPath(i))
+ .node(TestModel.INNER_LIST_QNAME).build();
+ writeTx.put(OPERATIONAL, path, ImmutableNodes.mapNodeBuilder(TestModel.INNER_LIST_QNAME).build());
+ for (int j = 0; j < innerNum; j++) {
+ YangInstanceIdentifier innerPath = YangInstanceIdentifier.builder(path)
+ .nodeWithKey(TestModel.INNER_LIST_QNAME, TestModel.NAME_QNAME, String.valueOf(j))
+ .build();
+ writeTx.put(
+ OPERATIONAL,
+ innerPath,
+ ImmutableNodes.mapEntry(TestModel.INNER_LIST_QNAME, TestModel.NAME_QNAME,
+ String.valueOf(j)));
+ }
+ i++;
+ }
+ return null;
+ }
+ });
+
+ measure("Txs:" + txNum + " Submit, Finish", new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ List<ListenableFuture<?>> allFutures = measure(txNum + " Submits",
+ new Callable<List<ListenableFuture<?>>>() {
+ @Override
+ public List<ListenableFuture<?>> call() throws Exception {
+ List<ListenableFuture<?>> builder = new ArrayList<>(txNum);
+ for (DOMDataReadWriteTransaction tx :transactions) {
+ builder.add(tx.submit());
+ }
+ return builder;
+ }
+ });
+ Futures.allAsList(allFutures).get();
+ return null;
+ }
+ });
+
+ final DOMDataReadTransaction readTx = measure("Txs:1 (ro), Allocate", new Callable<DOMDataReadTransaction>() {
+ @Override
+ public DOMDataReadTransaction call() throws Exception {
+ return domBroker.newReadOnlyTransaction();
+
+ }
+ });
+
+
+ measure("Txs:1 (ro) Reads:" + txNum + " (1-level)" , new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ for (int i = 0; i < txNum; i++) {
+ ListenableFuture<Optional<NormalizedNode<?, ?>>> potential = readTx.read(OPERATIONAL,
+ outerListPath(i));
+ assertTrue("outerList/" + i, potential.get().isPresent());
+ }
+ return null;
+ }
+ });
+
+ measure("Txs:1 (ro) Reads:" + txNum * innerNum + " (2-level)", new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ for (int i = 0; i < txNum; i++) {
+ for (int j = 0; j < innerNum; j++) {
+ YangInstanceIdentifier path = YangInstanceIdentifier
+ .builder(outerListPath(i))
+ //
+ .node(TestModel.INNER_LIST_QNAME)
+ .nodeWithKey(TestModel.INNER_LIST_QNAME, TestModel.NAME_QNAME, String.valueOf(j))
+ .build();
+ ListenableFuture<Optional<NormalizedNode<?, ?>>> potential = readTx.read(OPERATIONAL, path);
+ assertTrue("outer-list/" + i + "/inner-list/" + j, potential.get().isPresent());
+ }
+ }
+ return null;
+ }
+ });
+ }
+
+ private void measureOneTransactionTopContainer() throws Exception {
+
+ final DOMDataReadWriteTransaction writeTx = measure("Txs:1 Allocate", new Callable<DOMDataReadWriteTransaction>() {
+ @Override
+ public DOMDataReadWriteTransaction call() throws Exception {
+ return domBroker.newReadWriteTransaction();
+ }
+ });
+
+ measure("Txs:1 Write", new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ writeTx.put(OPERATIONAL, TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+ writeTx.put(OPERATIONAL, TestModel.OUTER_LIST_PATH,
+ ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME).build());
+ return null;
+ }
+ });
+
+ measure("Txs:1 Reads:1", new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ // Reads /test in writeTx
+ ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = writeTx.read(OPERATIONAL,
+ TestModel.TEST_PATH);
+ assertTrue(writeTxContainer.get().isPresent());
+ return null;
+ }
+ });
+
+ measure("Txs:1 Reads:1", new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ // Reads /test in writeTx
+ ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = writeTx.read(OPERATIONAL,
+ TestModel.TEST_PATH);
+ assertTrue(writeTxContainer.get().isPresent());
+ return null;
+ }
+ });
+
+ measure("Txs:1 Submit, Finish", new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ measure("Txs:1 Submit", new Callable<ListenableFuture<?>>() {
+ @Override
+ public ListenableFuture<?> call() throws Exception {
+ return writeTx.submit();
+ }
+ }).get();
+ return null;
+ }
+ });
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.ForwardingExecutorService;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.util.Collections;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitDeadlockException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
+import org.opendaylight.controller.md.sal.dom.store.impl.TestModel;
+import org.opendaylight.controller.sal.core.spi.data.DOMStore;
+import org.opendaylight.yangtools.util.concurrent.DeadlockDetectingListeningExecutorService;
+import org.opendaylight.yangtools.util.concurrent.SpecialExecutors;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class DOMBrokerTest {
+
+ private SchemaContext schemaContext;
+ private AbstractDOMDataBroker domBroker;
+ private ListeningExecutorService executor;
+ private ExecutorService futureExecutor;
+ private CommitExecutorService commitExecutor;
+
+ @Before
+ public void setupStore() {
+
+ InMemoryDOMDataStore operStore = new InMemoryDOMDataStore("OPER",
+ MoreExecutors.newDirectExecutorService());
+ InMemoryDOMDataStore configStore = new InMemoryDOMDataStore("CFG",
+ MoreExecutors.newDirectExecutorService());
+ schemaContext = TestModel.createTestContext();
+
+ operStore.onGlobalContextUpdated(schemaContext);
+ configStore.onGlobalContextUpdated(schemaContext);
+
+ ImmutableMap<LogicalDatastoreType, DOMStore> stores = ImmutableMap.<LogicalDatastoreType, DOMStore> builder() //
+ .put(CONFIGURATION, configStore) //
+ .put(OPERATIONAL, operStore) //
+ .build();
+
+ commitExecutor = new CommitExecutorService(Executors.newSingleThreadExecutor());
+ futureExecutor = SpecialExecutors.newBlockingBoundedCachedThreadPool(1, 5, "FCB");
+ executor = new DeadlockDetectingListeningExecutorService(commitExecutor,
+ TransactionCommitDeadlockException.DEADLOCK_EXCEPTION_SUPPLIER, futureExecutor);
+ domBroker = new SerializedDOMDataBroker(stores, executor);
+ }
+
+ @After
+ public void tearDown() {
+ if( executor != null ) {
+ executor.shutdownNow();
+ }
+
+ if(futureExecutor != null) {
+ futureExecutor.shutdownNow();
+ }
+ }
+
+ @Test(timeout=10000)
+ public void testTransactionIsolation() throws InterruptedException, ExecutionException {
+
+ assertNotNull(domBroker);
+
+ DOMDataReadTransaction readTx = domBroker.newReadOnlyTransaction();
+ assertNotNull(readTx);
+
+ DOMDataReadWriteTransaction writeTx = domBroker.newReadWriteTransaction();
+ assertNotNull(writeTx);
+ /**
+ *
+ * Writes /test in writeTx
+ *
+ */
+ writeTx.put(OPERATIONAL, TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+
+ /**
+ *
+ * Reads /test from writeTx Read should return container.
+ *
+ */
+ ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = writeTx.read(OPERATIONAL,
+ TestModel.TEST_PATH);
+ assertTrue(writeTxContainer.get().isPresent());
+
+ /**
+ *
+ * Reads /test from readTx Read should return Absent.
+ *
+ */
+ ListenableFuture<Optional<NormalizedNode<?, ?>>> readTxContainer = readTx
+ .read(OPERATIONAL, TestModel.TEST_PATH);
+ assertFalse(readTxContainer.get().isPresent());
+ }
+
+ @Test(timeout=10000)
+ public void testTransactionCommit() throws InterruptedException, ExecutionException {
+
+ DOMDataReadWriteTransaction writeTx = domBroker.newReadWriteTransaction();
+ assertNotNull(writeTx);
+ /**
+ *
+ * Writes /test in writeTx
+ *
+ */
+ writeTx.put(OPERATIONAL, TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+
+ /**
+ *
+ * Reads /test from writeTx Read should return container.
+ *
+ */
+ ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = writeTx.read(OPERATIONAL,
+ TestModel.TEST_PATH);
+ assertTrue(writeTxContainer.get().isPresent());
+
+ writeTx.submit().get();
+
+ Optional<NormalizedNode<?, ?>> afterCommitRead = domBroker.newReadOnlyTransaction()
+ .read(OPERATIONAL, TestModel.TEST_PATH).get();
+ assertTrue(afterCommitRead.isPresent());
+ }
+
+ @Test(expected=TransactionCommitFailedException.class)
+ public void testRejectedCommit() throws Exception {
+
+ commitExecutor.delegate = Mockito.mock( ExecutorService.class );
+ Mockito.doThrow( new RejectedExecutionException( "mock" ) )
+ .when( commitExecutor.delegate ).execute( Mockito.any( Runnable.class ) );
+ Mockito.doNothing().when( commitExecutor.delegate ).shutdown();
+ Mockito.doReturn( Collections.emptyList() ).when( commitExecutor.delegate ).shutdownNow();
+ Mockito.doReturn( "" ).when( commitExecutor.delegate ).toString();
+ Mockito.doReturn( true ).when( commitExecutor.delegate )
+ .awaitTermination( Mockito.anyLong(), Mockito.any( TimeUnit.class ) );
+
+ DOMDataReadWriteTransaction writeTx = domBroker.newReadWriteTransaction();
+ writeTx.put( OPERATIONAL, TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME) );
+
+ writeTx.submit().checkedGet( 5, TimeUnit.SECONDS );
+ }
+
+ /**
+ * Tests a simple DataChangeListener notification after a write.
+ */
+ @Test
+ public void testDataChangeListener() throws Throwable {
+
+ final NormalizedNode<?, ?> testNode = ImmutableNodes.containerNode( TestModel.TEST_QNAME );
+
+ TestDOMDataChangeListener dcListener = new TestDOMDataChangeListener();
+
+ domBroker.registerDataChangeListener( OPERATIONAL, TestModel.TEST_PATH,
+ dcListener, DataChangeScope.BASE );
+
+ final DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
+ assertNotNull( writeTx );
+
+ writeTx.put( OPERATIONAL, TestModel.TEST_PATH, testNode );
+
+ AtomicReference<Throwable> caughtEx = submitTxAsync( writeTx );
+
+ dcListener.waitForChange();
+
+ if( caughtEx.get() != null ) {
+ throw caughtEx.get();
+ }
+
+ NormalizedNode<?, ?> actualNode = dcListener.change.getCreatedData().get( TestModel.TEST_PATH );
+ assertEquals( "Created node", testNode, actualNode );
+ }
+
+ /**
+ * Tests a DataChangeListener that does an async submit of a write Tx in its onDataChanged method.
+ * This should succeed without deadlock.
+ */
+ @Test
+ public void testDataChangeListenerDoingAsyncWriteTxSubmit() throws Throwable {
+
+ final AtomicReference<Throwable> caughtCommitEx = new AtomicReference<>();
+ final CountDownLatch commitCompletedLatch = new CountDownLatch( 1 );
+
+ TestDOMDataChangeListener dcListener = new TestDOMDataChangeListener() {
+ @Override
+ public void onDataChanged( final AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change ) {
+
+ DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
+ writeTx.put( OPERATIONAL, TestModel.TEST2_PATH,
+ ImmutableNodes.containerNode( TestModel.TEST2_QNAME ) );
+ Futures.addCallback( writeTx.submit(), new FutureCallback<Void>() {
+ @Override
+ public void onSuccess( final Void result ) {
+ commitCompletedLatch.countDown();
+ }
+
+ @Override
+ public void onFailure( final Throwable t ) {
+ caughtCommitEx.set( t );
+ commitCompletedLatch.countDown();
+ }
+ } );
+
+ super.onDataChanged( change );
+ }
+ };
+
+ domBroker.registerDataChangeListener( OPERATIONAL, TestModel.TEST_PATH,
+ dcListener, DataChangeScope.BASE );
+
+ final DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
+ assertNotNull( writeTx );
+
+ writeTx.put( OPERATIONAL, TestModel.TEST_PATH, ImmutableNodes.containerNode( TestModel.TEST_QNAME ) );
+
+ AtomicReference<Throwable> caughtEx = submitTxAsync( writeTx );
+
+ dcListener.waitForChange();
+
+ if( caughtEx.get() != null ) {
+ throw caughtEx.get();
+ }
+
+ assertTrue( "Commit Future was not invoked", commitCompletedLatch.await( 5, TimeUnit.SECONDS ) );
+
+ if( caughtCommitEx.get() != null ) {
+ throw caughtCommitEx.get();
+ }
+ }
+
+ /**
+ * Tests a DataChangeListener that does a blocking submit of a write Tx in its onDataChanged method.
+ * This should throw an exception and not deadlock.
+ */
+ @Test(expected=TransactionCommitDeadlockException.class)
+ public void testDataChangeListenerDoingBlockingWriteTxSubmit() throws Throwable {
+
+ final AtomicReference<Throwable> caughtCommitEx = new AtomicReference<>();
+
+ TestDOMDataChangeListener dcListener = new TestDOMDataChangeListener() {
+ @Override
+ public void onDataChanged( final AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change ) {
+ DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
+ writeTx.put( OPERATIONAL, TestModel.TEST2_PATH,
+ ImmutableNodes.containerNode( TestModel.TEST2_QNAME ) );
+ try {
+ writeTx.submit().get();
+ } catch( ExecutionException e ) {
+ caughtCommitEx.set( e.getCause() );
+ } catch( Exception e ) {
+ caughtCommitEx.set( e );
+ }
+ finally {
+ super.onDataChanged( change );
+ }
+ }
+ };
+
+ domBroker.registerDataChangeListener( OPERATIONAL, TestModel.TEST_PATH,
+ dcListener, DataChangeScope.BASE );
+
+ final DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
+ assertNotNull( writeTx );
+
+ writeTx.put( OPERATIONAL, TestModel.TEST_PATH, ImmutableNodes.containerNode( TestModel.TEST_QNAME ) );
+
+ AtomicReference<Throwable> caughtEx = submitTxAsync( writeTx );
+
+ dcListener.waitForChange();
+
+ if( caughtEx.get() != null ) {
+ throw caughtEx.get();
+ }
+
+ if( caughtCommitEx.get() != null ) {
+ throw caughtCommitEx.get();
+ }
+ }
+
+ AtomicReference<Throwable> submitTxAsync( final DOMDataWriteTransaction writeTx ) {
+ final AtomicReference<Throwable> caughtEx = new AtomicReference<>();
+ new Thread() {
+ @Override
+ public void run() {
+
+ try {
+ writeTx.submit();
+ } catch( Throwable e ) {
+ caughtEx.set( e );
+ }
+ }
+
+ }.start();
+
+ return caughtEx;
+ }
+
+ static class TestDOMDataChangeListener implements DOMDataChangeListener {
+
+ volatile AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change;
+ private final CountDownLatch latch = new CountDownLatch( 1 );
+
+ @Override
+ public void onDataChanged( final AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change ) {
+ this.change = change;
+ latch.countDown();
+ }
+
+ void waitForChange() throws InterruptedException {
+ assertTrue( "onDataChanged was not called", latch.await( 5, TimeUnit.SECONDS ) );
+ }
+ }
+
+ static class CommitExecutorService extends ForwardingExecutorService {
+
+ ExecutorService delegate;
+
+ public CommitExecutorService( final ExecutorService delegate ) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ protected ExecutorService delegate() {
+ return delegate;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.ForwardingExecutorService;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import javax.annotation.Nonnull;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitDeadlockException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBrokerExtension;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
+import org.opendaylight.controller.md.sal.dom.store.impl.TestModel;
+import org.opendaylight.controller.sal.core.spi.data.DOMStore;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.util.concurrent.DeadlockDetectingListeningExecutorService;
+import org.opendaylight.yangtools.util.concurrent.SpecialExecutors;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class DOMDataTreeListenerTest {
+
+ private SchemaContext schemaContext;
+ private AbstractDOMDataBroker domBroker;
+ private ListeningExecutorService executor;
+ private ExecutorService futureExecutor;
+ private CommitExecutorService commitExecutor;
+
+ private static final DataContainerChild<?, ?> OUTER_LIST = ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
+ .withChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1))
+ .build();
+
+ private static final DataContainerChild<?, ?> OUTER_LIST_2 = ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
+ .withChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2))
+ .build();
+
+ private static final NormalizedNode<?, ?> TEST_CONTAINER = Builders.containerBuilder()
+ .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME))
+ .withChild(OUTER_LIST)
+ .build();
+
+ private static final NormalizedNode<?, ?> TEST_CONTAINER_2 = Builders.containerBuilder()
+ .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME))
+ .withChild(OUTER_LIST_2)
+ .build();
+
+ private static DOMDataTreeIdentifier ROOT_DATA_TREE_ID = new DOMDataTreeIdentifier(
+ LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH);
+
+ private static DOMDataTreeIdentifier OUTER_LIST_DATA_TREE_ID = new DOMDataTreeIdentifier(
+ LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH);
+
+ @Before
+ public void setupStore() {
+ InMemoryDOMDataStore operStore = new InMemoryDOMDataStore("OPER",
+ MoreExecutors.newDirectExecutorService());
+ InMemoryDOMDataStore configStore = new InMemoryDOMDataStore("CFG",
+ MoreExecutors.newDirectExecutorService());
+ schemaContext = TestModel.createTestContext();
+
+ operStore.onGlobalContextUpdated(schemaContext);
+ configStore.onGlobalContextUpdated(schemaContext);
+
+ ImmutableMap<LogicalDatastoreType, DOMStore> stores = ImmutableMap.<LogicalDatastoreType, DOMStore>builder() //
+ .put(CONFIGURATION, configStore) //
+ .put(OPERATIONAL, operStore) //
+ .build();
+
+ commitExecutor = new CommitExecutorService(Executors.newSingleThreadExecutor());
+ futureExecutor = SpecialExecutors.newBlockingBoundedCachedThreadPool(1, 5, "FCB");
+ executor = new DeadlockDetectingListeningExecutorService(commitExecutor,
+ TransactionCommitDeadlockException.DEADLOCK_EXCEPTION_SUPPLIER, futureExecutor);
+ domBroker = new SerializedDOMDataBroker(stores, executor);
+ }
+
+ @After
+ public void tearDown() {
+ if (executor != null) {
+ executor.shutdownNow();
+ }
+
+ if (futureExecutor != null) {
+ futureExecutor.shutdownNow();
+ }
+ }
+
+ @Test
+ public void writeContainerEmptyTreeTest() throws InterruptedException {
+ CountDownLatch latch = new CountDownLatch(1);
+
+ DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
+ assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
+ dataTreeChangeService);
+
+ final TestDataTreeListener listener = new TestDataTreeListener(latch);
+ final ListenerRegistration<TestDataTreeListener> listenerReg =
+ dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
+
+ final DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
+ writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
+ writeTx.submit();
+
+ latch.await(5, TimeUnit.SECONDS);
+
+ assertEquals(1, listener.getReceivedChanges().size());
+ final Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
+ assertEquals(1, changes.size());
+
+ DataTreeCandidate candidate = changes.iterator().next();
+ assertNotNull(candidate);
+ DataTreeCandidateNode candidateRoot = candidate.getRootNode();
+ checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
+ listenerReg.close();
+ }
+
+ @Test
+ public void replaceContainerContainerInTreeTest() throws InterruptedException, TransactionCommitFailedException {
+ CountDownLatch latch = new CountDownLatch(2);
+
+ DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
+ assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
+ dataTreeChangeService);
+
+ DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
+ writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
+ writeTx.submit().checkedGet();
+
+ final TestDataTreeListener listener = new TestDataTreeListener(latch);
+ final ListenerRegistration<TestDataTreeListener> listenerReg =
+ dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
+ writeTx = domBroker.newWriteOnlyTransaction();
+ writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER_2);
+ writeTx.submit();
+
+ latch.await(5, TimeUnit.SECONDS);
+
+ assertEquals(2, listener.getReceivedChanges().size());
+ Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
+ assertEquals(1, changes.size());
+
+ DataTreeCandidate candidate = changes.iterator().next();
+ assertNotNull(candidate);
+ DataTreeCandidateNode candidateRoot = candidate.getRootNode();
+ checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
+
+ changes = listener.getReceivedChanges().get(1);
+ assertEquals(1, changes.size());
+
+ candidate = changes.iterator().next();
+ assertNotNull(candidate);
+ candidateRoot = candidate.getRootNode();
+ checkChange(TEST_CONTAINER, TEST_CONTAINER_2, ModificationType.WRITE, candidateRoot);
+ listenerReg.close();
+ }
+
+ @Test
+ public void deleteContainerContainerInTreeTest() throws InterruptedException, TransactionCommitFailedException {
+ CountDownLatch latch = new CountDownLatch(2);
+
+ DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
+ assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
+ dataTreeChangeService);
+
+ DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
+ writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
+ writeTx.submit().checkedGet();
+
+ final TestDataTreeListener listener = new TestDataTreeListener(latch);
+ final ListenerRegistration<TestDataTreeListener> listenerReg =
+ dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
+
+ writeTx = domBroker.newWriteOnlyTransaction();
+ writeTx.delete(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH);
+ writeTx.submit();
+
+ latch.await(5, TimeUnit.SECONDS);
+
+ assertEquals(2, listener.getReceivedChanges().size());
+ Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
+ assertEquals(1, changes.size());
+
+ DataTreeCandidate candidate = changes.iterator().next();
+ assertNotNull(candidate);
+ DataTreeCandidateNode candidateRoot = candidate.getRootNode();
+ checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
+
+ changes = listener.getReceivedChanges().get(1);
+ assertEquals(1, changes.size());
+
+ candidate = changes.iterator().next();
+ assertNotNull(candidate);
+ candidateRoot = candidate.getRootNode();
+ checkChange(TEST_CONTAINER, null, ModificationType.DELETE, candidateRoot);
+ listenerReg.close();
+ }
+
+ @Test
+ public void replaceChildListContainerInTreeTest() throws InterruptedException, TransactionCommitFailedException {
+ CountDownLatch latch = new CountDownLatch(2);
+
+ DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
+ assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
+ dataTreeChangeService);
+
+ DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
+ writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
+ writeTx.submit().checkedGet();
+
+ final TestDataTreeListener listener = new TestDataTreeListener(latch);
+ final ListenerRegistration<TestDataTreeListener> listenerReg =
+ dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
+
+ writeTx = domBroker.newWriteOnlyTransaction();
+ writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH, OUTER_LIST_2);
+ writeTx.submit();
+
+ latch.await(5, TimeUnit.SECONDS);
+
+ assertEquals(2, listener.getReceivedChanges().size());
+ Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
+ assertEquals(1, changes.size());
+
+ DataTreeCandidate candidate = changes.iterator().next();
+ assertNotNull(candidate);
+ DataTreeCandidateNode candidateRoot = candidate.getRootNode();
+ checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
+
+ changes = listener.getReceivedChanges().get(1);
+ assertEquals(1, changes.size());
+
+ candidate = changes.iterator().next();
+ assertNotNull(candidate);
+ candidateRoot = candidate.getRootNode();
+ checkChange(TEST_CONTAINER, TEST_CONTAINER_2, ModificationType.SUBTREE_MODIFIED, candidateRoot);
+ final DataTreeCandidateNode modifiedChild = candidateRoot.getModifiedChild(
+ new YangInstanceIdentifier.NodeIdentifier(TestModel.OUTER_LIST_QNAME));
+ assertNotNull(modifiedChild);
+ checkChange(OUTER_LIST, OUTER_LIST_2, ModificationType.WRITE, modifiedChild);
+ listenerReg.close();
+ }
+
+ @Test
+ public void rootModificationChildListenerTest() throws InterruptedException, TransactionCommitFailedException {
+ CountDownLatch latch = new CountDownLatch(2);
+
+ DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
+ assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
+ dataTreeChangeService);
+
+ DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
+ writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
+ writeTx.submit().checkedGet();
+
+ final TestDataTreeListener listener = new TestDataTreeListener(latch);
+ final ListenerRegistration<TestDataTreeListener> listenerReg =
+ dataTreeChangeService.registerDataTreeChangeListener(OUTER_LIST_DATA_TREE_ID, listener);
+
+ writeTx = domBroker.newWriteOnlyTransaction();
+ writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER_2);
+ writeTx.submit().checkedGet();
+
+ latch.await(1, TimeUnit.SECONDS);
+
+ assertEquals(2, listener.getReceivedChanges().size());
+ Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
+ assertEquals(1, changes.size());
+
+ DataTreeCandidate candidate = changes.iterator().next();
+ assertNotNull(candidate);
+ DataTreeCandidateNode candidateRoot = candidate.getRootNode();
+ checkChange(null, OUTER_LIST, ModificationType.WRITE, candidateRoot);
+
+ changes = listener.getReceivedChanges().get(1);
+ assertEquals(1, changes.size());
+
+ candidate = changes.iterator().next();
+ assertNotNull(candidate);
+ candidateRoot = candidate.getRootNode();
+ checkChange(OUTER_LIST, OUTER_LIST_2, ModificationType.WRITE, candidateRoot);
+ listenerReg.close();
+ }
+
+ @Test
+ public void listEntryChangeNonRootRegistrationTest() throws InterruptedException, TransactionCommitFailedException {
+ CountDownLatch latch = new CountDownLatch(2);
+
+ DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
+ assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
+ dataTreeChangeService);
+
+ DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
+ writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
+ writeTx.submit().checkedGet();
+
+ final TestDataTreeListener listener = new TestDataTreeListener(latch);
+ final ListenerRegistration<TestDataTreeListener> listenerReg =
+ dataTreeChangeService.registerDataTreeChangeListener(OUTER_LIST_DATA_TREE_ID, listener);
+
+ final YangInstanceIdentifier.NodeIdentifierWithPredicates outerListEntryId1 =
+ new YangInstanceIdentifier.NodeIdentifierWithPredicates(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1);
+ final YangInstanceIdentifier.NodeIdentifierWithPredicates outerListEntryId2 =
+ new YangInstanceIdentifier.NodeIdentifierWithPredicates(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2);
+ final YangInstanceIdentifier.NodeIdentifierWithPredicates outerListEntryId3 =
+ new YangInstanceIdentifier.NodeIdentifierWithPredicates(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 3);
+
+ final MapEntryNode outerListEntry1 = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1);
+ final MapEntryNode outerListEntry2 = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2);
+ final MapEntryNode outerListEntry3 = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 3);
+
+ final MapNode listAfter = ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
+ .withChild(outerListEntry2)
+ .withChild(outerListEntry3)
+ .build();
+
+ writeTx = domBroker.newWriteOnlyTransaction();
+ writeTx.delete(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH.node(outerListEntryId1));
+ writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH.node(outerListEntryId2),
+ outerListEntry2);
+ writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH.node(outerListEntryId3),
+ outerListEntry3);
+ writeTx.submit();
+
+ latch.await(5, TimeUnit.SECONDS);
+
+ assertEquals(2, listener.getReceivedChanges().size());
+ Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
+ assertEquals(1, changes.size());
+
+ DataTreeCandidate candidate = changes.iterator().next();
+ assertNotNull(candidate);
+ DataTreeCandidateNode candidateRoot = candidate.getRootNode();
+ checkChange(null, OUTER_LIST, ModificationType.WRITE, candidateRoot);
+
+ changes = listener.getReceivedChanges().get(1);
+ assertEquals(1, changes.size());
+
+ candidate = changes.iterator().next();
+ assertNotNull(candidate);
+ candidateRoot = candidate.getRootNode();
+ checkChange(OUTER_LIST, listAfter, ModificationType.SUBTREE_MODIFIED, candidateRoot);
+ final DataTreeCandidateNode entry1Canditate = candidateRoot.getModifiedChild(outerListEntryId1);
+ checkChange(outerListEntry1, null, ModificationType.DELETE, entry1Canditate);
+ final DataTreeCandidateNode entry2Canditate = candidateRoot.getModifiedChild(outerListEntryId2);
+ checkChange(null, outerListEntry2, ModificationType.WRITE, entry2Canditate);
+ final DataTreeCandidateNode entry3Canditate = candidateRoot.getModifiedChild(outerListEntryId3);
+ checkChange(null, outerListEntry3, ModificationType.WRITE, entry3Canditate);
+ listenerReg.close();
+ }
+
+ private static void checkChange(final NormalizedNode<?, ?> expectedBefore,
+ final NormalizedNode<?, ?> expectedAfter,
+ final ModificationType expectedMod,
+ final DataTreeCandidateNode candidateNode) {
+ if (expectedBefore != null) {
+ assertTrue(candidateNode.getDataBefore().isPresent());
+ assertEquals(expectedBefore, candidateNode.getDataBefore().get());
+ } else {
+ assertFalse(candidateNode.getDataBefore().isPresent());
+ }
+
+ if (expectedAfter != null) {
+ assertTrue(candidateNode.getDataAfter().isPresent());
+ assertEquals(expectedAfter, candidateNode.getDataAfter().get());
+ } else {
+ assertFalse(candidateNode.getDataAfter().isPresent());
+ }
+
+ assertEquals(expectedMod, candidateNode.getModificationType());
+ }
+
+ private DOMDataTreeChangeService getDOMDataTreeChangeService() {
+ final DOMDataBrokerExtension extension = domBroker.getSupportedExtensions()
+ .get(DOMDataTreeChangeService.class);
+ if (extension == null) {
+ return null;
+ }
+ DOMDataTreeChangeService dataTreeChangeService = null;
+ if (extension instanceof DOMDataTreeChangeService) {
+ dataTreeChangeService = (DOMDataTreeChangeService) extension;
+ }
+ return dataTreeChangeService;
+ }
+
+
+ static class CommitExecutorService extends ForwardingExecutorService {
+
+ ExecutorService delegate;
+
+ public CommitExecutorService(final ExecutorService delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ protected ExecutorService delegate() {
+ return delegate;
+ }
+ }
+
+ static class TestDataTreeListener implements DOMDataTreeChangeListener {
+
+ private final List<Collection<DataTreeCandidate>> receivedChanges = new ArrayList<>();
+ private final CountDownLatch latch;
+
+ public TestDataTreeListener(final CountDownLatch latch) {
+ this.latch = latch;
+ }
+
+ @Override
+ public void onDataTreeChanged(@Nonnull final Collection<DataTreeCandidate> changes) {
+ receivedChanges.add(changes);
+ latch.countDown();
+ }
+
+ public List<Collection<DataTreeCandidate>> getReceivedChanges() {
+ return receivedChanges;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
+import org.opendaylight.controller.md.sal.dom.store.impl.TestModel;
+import org.opendaylight.controller.sal.core.spi.data.DOMStore;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class DOMTransactionChainTest {
+
+ private SchemaContext schemaContext;
+ private AbstractDOMDataBroker domBroker;
+
+ @Before
+ public void setupStore() {
+ InMemoryDOMDataStore operStore = new InMemoryDOMDataStore("OPER", MoreExecutors.newDirectExecutorService());
+ InMemoryDOMDataStore configStore = new InMemoryDOMDataStore("CFG", MoreExecutors.newDirectExecutorService());
+ schemaContext = TestModel.createTestContext();
+
+ operStore.onGlobalContextUpdated(schemaContext);
+ configStore.onGlobalContextUpdated(schemaContext);
+
+ ImmutableMap<LogicalDatastoreType, DOMStore> stores = ImmutableMap.<LogicalDatastoreType, DOMStore> builder() //
+ .put(CONFIGURATION, configStore) //
+ .put(OPERATIONAL, operStore) //
+ .build();
+
+ ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor());
+ domBroker = new SerializedDOMDataBroker(stores, executor);
+ }
+
+ @Test
+ public void testTransactionChainNoConflict() throws InterruptedException, ExecutionException, TimeoutException {
+ BlockingTransactionChainListener listener = new BlockingTransactionChainListener();
+ DOMTransactionChain txChain = domBroker.createTransactionChain(listener);
+ assertNotNull(txChain);
+
+ /**
+ * We alocate new read-write transaction and write /test
+ *
+ *
+ */
+ DOMDataReadWriteTransaction firstTx = allocateAndWrite(txChain);
+
+ /**
+ * First transaction is marked as ready, we are able to allocate chained
+ * transactions
+ */
+ ListenableFuture<Void> firstWriteTxFuture = firstTx.submit();
+
+ /**
+ * We alocate chained transaction - read transaction.
+ */
+ DOMDataReadTransaction secondReadTx = txChain.newReadOnlyTransaction();
+
+ /**
+ *
+ * We test if we are able to read data from tx, read should not fail
+ * since we are using chained transaction.
+ *
+ *
+ */
+ assertTestContainerExists(secondReadTx);
+
+ /**
+ *
+ * We alocate next transaction, which is still based on first one, but
+ * is read-write.
+ *
+ */
+ DOMDataReadWriteTransaction thirdDeleteTx = allocateAndDelete(txChain);
+
+ /**
+ * We commit first transaction
+ *
+ */
+ assertCommitSuccessful(firstWriteTxFuture);
+
+ /**
+ *
+ * Allocates transaction from data store.
+ *
+ */
+ DOMDataReadTransaction storeReadTx = domBroker.newReadOnlyTransaction();
+
+ /**
+ * We verify transaction is commited to store, container should exists
+ * in datastore.
+ */
+ assertTestContainerExists(storeReadTx);
+
+ /**
+ * third transaction is sealed and commited
+ */
+ ListenableFuture<Void> thirdDeleteTxFuture = thirdDeleteTx.submit();
+ assertCommitSuccessful(thirdDeleteTxFuture);
+
+ /**
+ * We close transaction chain.
+ */
+ txChain.close();
+
+ listener.getSuccessFuture().get(1000, TimeUnit.MILLISECONDS);
+ }
+
+ @Test
+ public void testTransactionChainNotSealed() throws InterruptedException, ExecutionException, TimeoutException {
+ BlockingTransactionChainListener listener = new BlockingTransactionChainListener();
+ DOMTransactionChain txChain = domBroker.createTransactionChain(listener);
+ assertNotNull(txChain);
+
+ /**
+ * We alocate new read-write transaction and write /test
+ *
+ *
+ */
+ allocateAndWrite(txChain);
+
+ /**
+ * We alocate chained transaction - read transaction, note first one is
+ * still not commited to datastore, so this allocation should fail with
+ * IllegalStateException.
+ */
+ try {
+ txChain.newReadOnlyTransaction();
+ fail("Allocation of secondReadTx should fail with IllegalStateException");
+ } catch (Exception e) {
+ assertTrue(e instanceof IllegalStateException);
+ }
+ }
+
+ private static DOMDataReadWriteTransaction allocateAndDelete(final DOMTransactionChain txChain)
+ throws InterruptedException, ExecutionException {
+ DOMDataReadWriteTransaction tx = txChain.newReadWriteTransaction();
+
+ /**
+ * We test existence of /test in third transaction container should
+ * still be visible from first one (which is still uncommmited).
+ *
+ */
+ assertTestContainerExists(tx);
+
+ /**
+ * We delete node in third transaction
+ */
+ tx.delete(LogicalDatastoreType.OPERATIONAL, TestModel.TEST_PATH);
+ return tx;
+ }
+
+ private static DOMDataReadWriteTransaction allocateAndWrite(final DOMTransactionChain txChain)
+ throws InterruptedException, ExecutionException {
+ DOMDataReadWriteTransaction tx = txChain.newReadWriteTransaction();
+ assertTestContainerWrite(tx);
+ return tx;
+ }
+
+ private static void assertCommitSuccessful(final ListenableFuture<Void> future)
+ throws InterruptedException, ExecutionException {
+ future.get();
+ }
+
+ private static void assertTestContainerExists(final DOMDataReadTransaction readTx) throws InterruptedException,
+ ExecutionException {
+ ListenableFuture<Optional<NormalizedNode<?, ?>>> readFuture = readTx.read(OPERATIONAL, TestModel.TEST_PATH);
+ Optional<NormalizedNode<?, ?>> readedData = readFuture.get();
+ assertTrue(readedData.isPresent());
+ }
+
+ private static void assertTestContainerWrite(final DOMDataReadWriteTransaction tx) throws InterruptedException,
+ ExecutionException {
+ tx.put(OPERATIONAL, TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+ assertTestContainerExists(tx);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.base.Optional;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService.DOMMountPointBuilder;
+import org.opendaylight.controller.md.sal.dom.broker.impl.mount.DOMMountPointServiceImpl;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+public class MountPointServiceTest {
+
+ private DOMMountPointService mountService;
+ private static final YangInstanceIdentifier PATH = YangInstanceIdentifier.of(QName.create("namespace", "12-12-2012", "top"));
+
+ @Before
+ public void setup() {
+ mountService = new DOMMountPointServiceImpl();
+ }
+
+ @Test
+ public void createSimpleMountPoint() {
+ Optional<DOMMountPoint> mountNotPresent = mountService.getMountPoint(PATH);
+ assertFalse(mountNotPresent.isPresent());
+ DOMMountPointBuilder mountBuilder = mountService.createMountPoint(PATH);
+ mountBuilder.register();
+
+ Optional<DOMMountPoint> mountPresent = mountService.getMountPoint(PATH);
+ assertTrue(mountPresent.isPresent());
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.broker.impl.jmx;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.util.DurationStatsTracker;
+
+/**
+ * Unit tests for CommitStatsMXBeanImpl.
+ *
+ * @author Thomas Pantelis
+ */
+public class CommitStatsMXBeanImplTest {
+
+ @Test
+ public void test() {
+
+ DurationStatsTracker commitStatsTracker = new DurationStatsTracker();
+ CommitStatsMXBeanImpl bean =
+ new CommitStatsMXBeanImpl(commitStatsTracker, "Test");
+
+ commitStatsTracker.addDuration(100);
+
+ assertEquals("getTotalCommits", 1L, bean.getTotalCommits());
+ assertNotNull(bean.getLongestCommitTime());
+ assertNotNull(bean.getShortestCommitTime());
+ assertNotNull(bean.getAverageCommitTime());
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+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 java.io.InputStream;
+import java.util.Collections;
+import java.util.Set;
+
+public class TestModel {
+
+ public static final QName TEST_QNAME = QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test", "2014-03-13",
+ "test");
+ public static final QName TEST2_QNAME = QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test", "2014-03-13",
+ "test2");
+ public static final QName OUTER_LIST_QNAME = QName.create(TEST_QNAME, "outer-list");
+ public static final QName INNER_LIST_QNAME = QName.create(TEST_QNAME, "inner-list");
+ public static final QName OUTER_CHOICE_QNAME = QName.create(TEST_QNAME, "outer-choice");
+ public static final QName ID_QNAME = QName.create(TEST_QNAME, "id");
+ public static final QName NAME_QNAME = QName.create(TEST_QNAME, "name");
+ public static final QName VALUE_QNAME = QName.create(TEST_QNAME, "value");
+ private static final String DATASTORE_TEST_YANG = "/odl-datastore-test.yang";
+
+ public static final YangInstanceIdentifier TEST_PATH = YangInstanceIdentifier.of(TEST_QNAME);
+ public static final YangInstanceIdentifier TEST2_PATH = YangInstanceIdentifier.of(TEST2_QNAME);
+ public static final YangInstanceIdentifier OUTER_LIST_PATH = YangInstanceIdentifier.builder(TEST_PATH).node(OUTER_LIST_QNAME).build();
+ public static final QName TWO_QNAME = QName.create(TEST_QNAME,"two");
+ public static final QName THREE_QNAME = QName.create(TEST_QNAME,"three");
+
+
+ public static final InputStream getDatastoreTestInputStream() {
+ return getInputStream(DATASTORE_TEST_YANG);
+ }
+
+ private static InputStream getInputStream(final String resourceName) {
+ return TestModel.class.getResourceAsStream(DATASTORE_TEST_YANG);
+ }
+
+ public static SchemaContext createTestContext() {
+ YangParserImpl parser = new YangParserImpl();
+ Set<Module> modules = parser.parseYangModelsFromStreams(Collections.singletonList(getDatastoreTestInputStream()));
+ return parser.resolveSchemaContext(modules);
+ }
+}
--- /dev/null
+module odl-datastore-test {
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test";
+ prefix "store-test";
+
+ revision "2014-03-13" {
+ description "Initial revision.";
+ }
+
+ container test {
+ list outer-list {
+ key id;
+ leaf id {
+ type uint16;
+ }
+ choice outer-choice {
+ case one {
+ leaf one {
+ type string;
+ }
+ }
+ case two-three {
+ leaf two {
+ type string;
+ }
+ leaf three {
+ type string;
+ }
+ }
+ }
+ list inner-list {
+ key name;
+ leaf name {
+ type string;
+ }
+ leaf value {
+ type string;
+ }
+ }
+ }
+ }
+
+ container test2 {
+ }
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-parent</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>sal-core-spi</artifactId>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-core-api</artifactId>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>config</id>
+ <goals>
+ <goal>generate-sources</goal>
+ </goals>
+ <configuration>
+ <codeGenerators>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator</codeGeneratorClass>
+ <outputBaseDir>${jmxGeneratorPath}</outputBaseDir>
+ <additionalConfiguration>
+ <namespaceToPackage1>urn:opendaylight:params:xml:ns:yang:controller==org.opendaylight.controller.config.yang</namespaceToPackage1>
+ </additionalConfiguration>
+ </generator>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl</codeGeneratorClass>
+ <outputBaseDir>${salGeneratorPath}</outputBaseDir>
+ </generator>
+ </codeGenerators>
+ <inspectDependencies>true</inspectDependencies>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+ <scm>
+ <connection>scm:git:http://git.opendaylight.org/gerrit/controller.git</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+ <tag>HEAD</tag>
+ <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
+ </scm>
+</project>
--- /dev/null
+/*
+ * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.broker.spi.mount;
+
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
+import org.opendaylight.controller.md.sal.dom.api.DOMService;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.ImmutableClassToInstanceMap;
+
+public class SimpleDOMMountPoint implements DOMMountPoint {
+
+ private final YangInstanceIdentifier identifier;
+ private final ClassToInstanceMap<DOMService> services;
+ private final SchemaContext schemaContext;
+
+ public static final SimpleDOMMountPoint create(final YangInstanceIdentifier identifier, final ClassToInstanceMap<DOMService> services, final SchemaContext ctx) {
+ return new SimpleDOMMountPoint(identifier, services, ctx);
+ }
+ private SimpleDOMMountPoint(final YangInstanceIdentifier identifier, final ClassToInstanceMap<DOMService> services, final SchemaContext ctx) {
+ this.identifier = identifier;
+ this.services = ImmutableClassToInstanceMap.copyOf(services);
+ this.schemaContext = ctx;
+ }
+
+ @Override
+ public YangInstanceIdentifier getIdentifier() {
+ return identifier;
+ }
+
+ @Override
+ public SchemaContext getSchemaContext() {
+ return schemaContext;
+ }
+
+ @Override
+ public <T extends DOMService> Optional<T> getService(final Class<T> cls) {
+ return Optional.fromNullable(services.getInstance(cls));
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.spi.rpc;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+
+public abstract class RpcRoutingStrategy implements Identifiable<QName> {
+
+ private static final QName CONTEXT_REFERENCE = QName.cachedReference(QName.create("urn:opendaylight:yang:extension:yang-ext",
+ "2013-07-09", "context-reference"));
+ private final QName identifier;
+
+ private RpcRoutingStrategy(final QName identifier) {
+ this.identifier = Preconditions.checkNotNull(identifier);
+ }
+
+ /**
+ * Returns leaf QName in which RPC Route is stored
+ *
+ *
+ * @return leaf QName in which RPC Route is stored
+ * @throws UnsupportedOperationException If RPC is not content routed.
+ * ({@link #isContextBasedRouted()} returned <code>false</code>)
+ */
+ public abstract QName getLeaf();
+
+ /**
+ * Returns identity QName which represents RPC Routing context
+ *
+ * @return identity QName which represents RPC Routing context
+ * @throws UnsupportedOperationException If RPC is not content routed.
+ * ({@link #isContextBasedRouted()} returned <code>false</code>)
+ */
+ public abstract QName getContext();
+
+ @Override
+ public final QName getIdentifier() {
+ return identifier;
+ }
+
+ /**
+ * Returns true if RPC is routed by context.
+ *
+ * @return true if RPC is routed by content.
+ */
+ public abstract boolean isContextBasedRouted();
+
+ public static RpcRoutingStrategy from(final RpcDefinition rpc) {
+ ContainerSchemaNode input = rpc.getInput();
+ if (input != null) {
+ for (DataSchemaNode schemaNode : input.getChildNodes()) {
+ Optional<QName> context = getRoutingContext(schemaNode);
+ if (context.isPresent()) {
+ return new RoutedRpcStrategy(rpc.getQName(), context.get(), schemaNode.getQName());
+ }
+ }
+ }
+ return new GlobalRpcStrategy(rpc.getQName());
+ }
+
+ public static Optional<QName> getRoutingContext(final DataSchemaNode schemaNode) {
+ for (UnknownSchemaNode extension : schemaNode.getUnknownSchemaNodes()) {
+ if (CONTEXT_REFERENCE.equals(extension.getNodeType())) {
+ return Optional.fromNullable(extension.getQName());
+ }
+ }
+ return Optional.absent();
+ }
+
+ private static final class RoutedRpcStrategy extends RpcRoutingStrategy {
+ private final QName context;
+ private final QName leaf;
+
+ private RoutedRpcStrategy(final QName identifier, final QName ctx, final QName leaf) {
+ super(identifier);
+ this.context = Preconditions.checkNotNull(ctx);
+ this.leaf = Preconditions.checkNotNull(leaf);
+ }
+
+ @Override
+ public QName getContext() {
+ return context;
+ }
+
+ @Override
+ public QName getLeaf() {
+ return leaf;
+ }
+
+ @Override
+ public boolean isContextBasedRouted() {
+ return true;
+ }
+ }
+
+ private static final class GlobalRpcStrategy extends RpcRoutingStrategy {
+
+ public GlobalRpcStrategy(final QName identifier) {
+ super(identifier);
+ }
+
+ @Override
+ public boolean isContextBasedRouted() {
+ return false;
+ }
+
+ @Override
+ public QName getContext() {
+ throw new UnsupportedOperationException("Non-routed strategy does not have a context");
+ }
+
+ @Override
+ public QName getLeaf() {
+ throw new UnsupportedOperationException("Non-routed strategy does not have a context");
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
+
+/**
+ * Abstract implementation of a ListenerRegistration constrained to subclasses
+ * of {@link DOMDataTreeChangeListener}.
+ *
+ * @param <T> type of listener
+ */
+public abstract class AbstractDOMDataTreeChangeListenerRegistration<T extends DOMDataTreeChangeListener> extends AbstractListenerRegistration<T> {
+ protected AbstractDOMDataTreeChangeListenerRegistration(final T listener) {
+ super(listener);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementation;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationRegistration;
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
+
+/**
+ * Abstract base class for {@link DOMRpcImplementationRegistration} implementations.
+ */
+public abstract class AbstractDOMRpcImplementationRegistration<T extends DOMRpcImplementation> extends AbstractObjectRegistration<T> implements DOMRpcImplementationRegistration<T> {
+ protected AbstractDOMRpcImplementationRegistration(final T instance) {
+ super(instance);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import com.google.common.collect.ImmutableSet;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementation;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationRegistration;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcProviderService;
+
+/**
+ * Convenience abstract base class for {@link DOMRpcProviderService} implementations.
+ */
+public abstract class AbstractDOMRpcProviderService implements DOMRpcProviderService {
+ @Override
+ public final <T extends DOMRpcImplementation> DOMRpcImplementationRegistration<T> registerRpcImplementation(final T implementation, final DOMRpcIdentifier... types) {
+ return registerRpcImplementation(implementation, ImmutableSet.copyOf(types));
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+
+/**
+ * An abstract tree of registrations. Allows a read-only snapshot to be taken.
+ *
+ * @param <T> Type of registered object
+ */
+public abstract class AbstractRegistrationTree<T> {
+ private final ReadWriteLock rwLock = new ReentrantReadWriteLock(true);
+ private final RegistrationTreeNode<T> rootNode = new RegistrationTreeNode<>(null, null);
+
+ protected AbstractRegistrationTree() {
+
+ }
+
+ /**
+ * Acquire the read-write lock. This should be done before invoking {@link #findNodeFor(Iterable)}.
+ */
+ protected final void takeLock() {
+ rwLock.writeLock().lock();
+ }
+
+ /**
+ * Release the read-write lock. This should be done after invocation of {@link #findNodeFor(Iterable)}
+ * and modification of the returned node. Note that callers should do so in a finally block.
+ */
+ protected final void releaseLock() {
+ rwLock.writeLock().unlock();
+ }
+
+ /**
+ * Find an existing, or allocate a fresh, node for a particular path. Must be called with the
+ * read-write lock held.
+ *
+ * @param path Path to find a node for
+ * @return A registration node for the specified path
+ */
+ @Nonnull protected final RegistrationTreeNode<T> findNodeFor(@Nonnull final Iterable<PathArgument> path) {
+ RegistrationTreeNode<T> walkNode = rootNode;
+ for (final PathArgument arg : path) {
+ walkNode = walkNode.ensureChild(arg);
+ }
+
+ return walkNode;
+ }
+
+ /**
+ * Add a registration to a particular node. The node must have been returned via {@link #findNodeFor(Iterable)}
+ * and the lock must still be held.
+ *
+ * @param node Tree node
+ * @param registration Registration instance
+ */
+ protected final void addRegistration(@Nonnull final RegistrationTreeNode<T> node, @Nonnull final T registration) {
+ node.addRegistration(registration);
+ }
+
+ /**
+ * Remove a registration from a particular node. This method must not be called while the read-write lock
+ * is held.
+ *
+ * @param node Tree node
+ * @param registration Registration instance
+ */
+ protected final void removeRegistration(@Nonnull final RegistrationTreeNode<T> node, @Nonnull final T registration) {
+ // Take the write lock
+ rwLock.writeLock().lock();
+ try {
+ node.removeRegistration(registration);
+ } finally {
+ // Always release the lock
+ rwLock.writeLock().unlock();
+ }
+ }
+
+ /**
+ * Obtain a tree snapshot. This snapshot ensures a consistent view of
+ * registrations. The snapshot should be closed as soon as it is not required,
+ * because each unclosed instance blocks modification of this tree.
+ *
+ * @return A snapshot instance.
+ */
+ @Nonnull public final RegistrationTreeSnapshot<T> takeSnapshot() {
+ final RegistrationTreeSnapshot<T> ret = new RegistrationTreeSnapshot<>(rwLock.readLock(), rootNode);
+ rwLock.readLock().lock();
+ return ret;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import com.google.common.annotations.Beta;
+import java.util.EventListener;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+/**
+ * Listener which is notified when subscriptions changes and
+ * provides set of notification types for which currently
+ * subscriptions are in place.
+ *
+ */
+@Beta
+public interface DOMNotificationSubscriptionListener extends EventListener {
+
+ /**
+ * Invoked when notification subscription changed
+ *
+ * @param currentTypes Set of notification types
+ * for which listeners are registered.
+ */
+ void onSubscriptionChanged(Set<SchemaPath> currentTypes);
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+
+/**
+ * Registry of {@link DOMNotificationSubscriptionListener}
+ * which listens for changes in notification types.
+ *
+ */
+@Beta
+public interface DOMNotificationSubscriptionListenerRegistry {
+
+ <L extends DOMNotificationSubscriptionListener> ListenerRegistration<L> registerSubscriptionListener(L listener);
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Objects;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * Utility class implementing {@link DefaultDOMRpcResult}.
+ */
+@Beta
+public final class DefaultDOMRpcResult implements DOMRpcResult, Immutable, Serializable {
+ private static final long serialVersionUID = 1L;
+ private final Collection<RpcError> errors;
+ private final NormalizedNode<?, ?> result;
+
+ private static Collection<RpcError> asCollection(final RpcError... errors) {
+ if (errors.length == 0) {
+ return Collections.emptyList();
+ } else {
+ return Arrays.asList(errors);
+ }
+ }
+
+ public DefaultDOMRpcResult(final NormalizedNode<?, ?> result, final RpcError... errors) {
+ this(result, asCollection(errors));
+ }
+
+ public DefaultDOMRpcResult(final RpcError... errors) {
+ this(null, asCollection(errors));
+ }
+
+ public DefaultDOMRpcResult(final NormalizedNode<?, ?> result) {
+ this(result, Collections.<RpcError>emptyList());
+ }
+
+ public DefaultDOMRpcResult(final NormalizedNode<?, ?> result, final @Nonnull Collection<RpcError> errors) {
+ this.result = result;
+ this.errors = Preconditions.checkNotNull(errors);
+ }
+
+ public DefaultDOMRpcResult(final @Nonnull Collection<RpcError> errors) {
+ this(null, errors);
+ }
+
+ @Override
+ public @Nonnull Collection<RpcError> getErrors() {
+ return errors;
+ }
+
+ @Override
+ public NormalizedNode<?, ?> getResult() {
+ return result;
+ }
+
+ @Override
+ public int hashCode() {
+ int ret = errors.hashCode();
+ if (result != null) {
+ ret = 31 * ret + result.hashCode();
+ }
+ return ret;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof DefaultDOMRpcResult)) {
+ return false;
+ }
+
+ final DefaultDOMRpcResult other = (DefaultDOMRpcResult) obj;
+ if (!errors.equals(other.errors)) {
+ return false;
+ }
+ return Objects.equals(result, other.result);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import com.google.common.collect.ForwardingObject;
+import java.util.Map;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBrokerExtension;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+/**
+ * Utility {@link DOMDataBroker} implementation which forwards all interface
+ * method invocation to a delegate instance.
+ */
+public abstract class ForwardingDOMDataBroker extends ForwardingObject implements DOMDataBroker {
+ @Override
+ protected abstract @Nonnull DOMDataBroker delegate();
+
+ @Override
+ public ListenerRegistration<DOMDataChangeListener> registerDataChangeListener(final LogicalDatastoreType store,
+ final YangInstanceIdentifier path, final DOMDataChangeListener listener,
+ final DataChangeScope triggeringScope) {
+ return delegate().registerDataChangeListener(store, path, listener, triggeringScope);
+ }
+
+ @Override
+ public DOMDataReadOnlyTransaction newReadOnlyTransaction() {
+ return delegate().newReadOnlyTransaction();
+ }
+
+ @Override
+ public DOMDataReadWriteTransaction newReadWriteTransaction() {
+ return delegate().newReadWriteTransaction();
+ }
+
+ @Override
+ public DOMDataWriteTransaction newWriteOnlyTransaction() {
+ return delegate().newWriteOnlyTransaction();
+ }
+
+ @Override
+ public DOMTransactionChain createTransactionChain(final TransactionChainListener listener) {
+ return delegate().createTransactionChain(listener);
+ }
+
+ @Override
+ public Map<Class<? extends DOMDataBrokerExtension>, DOMDataBrokerExtension> getSupportedExtensions() {
+ return delegate().getSupportedExtensions();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ForwardingObject;
+import com.google.common.util.concurrent.CheckedFuture;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * Utility {@link DOMDataReadOnlyTransaction} implementation which forwards all interface
+ * method invocation to a delegate instance.
+ */
+public abstract class ForwardingDOMDataReadOnlyTransaction extends ForwardingObject implements DOMDataReadOnlyTransaction {
+ @Override
+ protected abstract @Nonnull DOMDataReadOnlyTransaction delegate();
+
+ @Override
+ public CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+ return delegate().read(store, path);
+ }
+
+ @Override
+ public CheckedFuture<Boolean, ReadFailedException> exists(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+ return delegate().exists(store, path);
+ }
+
+ @Override
+ public Object getIdentifier() {
+ return delegate().getIdentifier();
+ }
+
+ @Override
+ public void close() {
+ delegate().close();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ForwardingObject;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.ListenableFuture;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * Utility {@link DOMDataReadWriteTransaction} implementation which forwards all interface
+ * method invocation to a delegate instance.
+ */
+public abstract class ForwardingDOMDataReadWriteTransaction extends ForwardingObject implements DOMDataReadWriteTransaction {
+ @Override
+ protected abstract @Nonnull DOMDataReadWriteTransaction delegate();
+
+ @Override
+ public CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+ return delegate().read(store, path);
+ }
+
+ @Override
+ public CheckedFuture<Boolean, ReadFailedException> exists(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+ return delegate().exists(store, path);
+ }
+
+ @Override
+ public Object getIdentifier() {
+ return delegate().getIdentifier();
+ }
+
+ @Override
+ public void put(final LogicalDatastoreType store, final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
+ delegate().put(store, path, data);
+ }
+
+ @Override
+ public void merge(final LogicalDatastoreType store, final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
+ delegate().merge(store, path, data);
+ }
+
+ @Override
+ public boolean cancel() {
+ return delegate().cancel();
+ }
+
+ @Override
+ public void delete(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+ delegate().delete(store, path);
+ }
+
+ @Override
+ public CheckedFuture<Void, TransactionCommitFailedException> submit() {
+ return delegate().submit();
+ }
+
+ @Override
+ @Deprecated
+ public ListenableFuture<RpcResult<TransactionStatus>> commit() {
+ return delegate().commit();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import com.google.common.collect.ForwardingObject;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.ListenableFuture;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * Utility {@link DOMDataWriteTransaction} implementation which forwards all interface
+ * method invocation to a delegate instance.
+ */
+public abstract class ForwardingDOMDataWriteTransaction extends ForwardingObject implements DOMDataWriteTransaction {
+ @Override
+ protected abstract @Nonnull DOMDataWriteTransaction delegate();
+
+ @Override
+ public Object getIdentifier() {
+ return delegate().getIdentifier();
+ }
+
+ @Override
+ public void put(final LogicalDatastoreType store, final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
+ delegate().put(store, path, data);
+ }
+
+ @Override
+ public void merge(final LogicalDatastoreType store, final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
+ delegate().merge(store, path, data);
+ }
+
+ @Override
+ public boolean cancel() {
+ return delegate().cancel();
+ }
+
+ @Override
+ public void delete(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+ delegate().delete(store, path);
+ }
+
+ @Override
+ public CheckedFuture<Void, TransactionCommitFailedException> submit() {
+ return delegate().submit();
+ }
+
+ @Override
+ @Deprecated
+ public ListenableFuture<RpcResult<TransactionStatus>> commit() {
+ return delegate().commit();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import com.google.common.collect.ForwardingObject;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.concurrent.TimeUnit;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationPublishService;
+
+/**
+ * Utility implementations of {@link DOMNotificationPublishService} which forwards
+ * all requests to a delegate instance.
+ */
+public abstract class ForwardingDOMNotificationPublishService extends ForwardingObject implements DOMNotificationPublishService {
+ @Override
+ protected abstract DOMNotificationPublishService delegate();
+
+ @Override
+ public ListenableFuture<? extends Object> putNotification(final DOMNotification notification) throws InterruptedException {
+ return delegate().putNotification(notification);
+ }
+
+ @Override
+ public ListenableFuture<? extends Object> offerNotification(final DOMNotification notification) {
+ return delegate().offerNotification(notification);
+ }
+
+ @Override
+ public ListenableFuture<? extends Object> offerNotification(final DOMNotification notification, final long timeout,
+ final TimeUnit unit) throws InterruptedException {
+ return delegate().offerNotification(notification, timeout, unit);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import com.google.common.collect.ForwardingObject;
+import java.util.Collection;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+/**
+ * Utility implementation of a {@link DOMNotificationService} which forwards all requests
+ * to a delegate instance.
+ */
+public abstract class ForwardingDOMNotificationService extends ForwardingObject implements DOMNotificationService {
+ @Override
+ protected abstract DOMNotificationService delegate();
+
+ @Override
+ public <T extends DOMNotificationListener> ListenerRegistration<T> registerNotificationListener(final T listener,
+ final Collection<SchemaPath> types) {
+ return delegate().registerNotificationListener(listener, types);
+ }
+
+ @Override
+ public <T extends DOMNotificationListener> ListenerRegistration<T> registerNotificationListener(final T listener,
+ final SchemaPath... types) {
+ return delegate().registerNotificationListener(listener, types);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import com.google.common.collect.ForwardingObject;
+import com.google.common.util.concurrent.CheckedFuture;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementation;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * Utility implementation which implements {@link DOMRpcImplementation} by forwarding it to
+ * a backing delegate.
+ */
+public abstract class ForwardingDOMRpcImplementation extends ForwardingObject implements DOMRpcImplementation {
+ @Override
+ protected abstract @Nonnull DOMRpcImplementation delegate();
+
+ @Override
+ public CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(final DOMRpcIdentifier type, final NormalizedNode<?, ?> input) {
+ return delegate().invokeRpc(type, input);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import com.google.common.collect.ForwardingObject;
+import java.util.Set;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementation;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationRegistration;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcProviderService;
+
+/**
+ * Utility class which implements {@link DOMRpcProviderService} by forwarding
+ * requests to a backing instance.
+ */
+public abstract class ForwardingDOMRpcProviderService extends ForwardingObject implements DOMRpcProviderService {
+ @Override
+ protected abstract @Nonnull DOMRpcProviderService delegate();
+
+ @Override
+ public <T extends DOMRpcImplementation> DOMRpcImplementationRegistration<T> registerRpcImplementation(final T implementation, final DOMRpcIdentifier... types) {
+ return delegate().registerRpcImplementation(implementation, types);
+ }
+
+ @Override
+ public <T extends DOMRpcImplementation> DOMRpcImplementationRegistration<T> registerRpcImplementation(final T implementation, final Set<DOMRpcIdentifier> types) {
+ return delegate().registerRpcImplementation(implementation, types);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import com.google.common.collect.ForwardingObject;
+import java.util.Collection;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * Utility class which implements {@link DOMRpcResult} by forwarding all methods
+ * to a backing instance.
+ */
+public abstract class ForwardingDOMRpcResult extends ForwardingObject implements DOMRpcResult {
+ @Override
+ protected abstract @Nonnull DOMRpcResult delegate();
+
+ @Override
+ public Collection<RpcError> getErrors() {
+ return delegate().getErrors();
+ }
+
+ @Override
+ public NormalizedNode<?, ?> getResult() {
+ return delegate().getResult();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import com.google.common.collect.ForwardingObject;
+import com.google.common.util.concurrent.CheckedFuture;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcAvailabilityListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+/**
+ * Utility {@link DOMRpcService} which forwards all requests to a backing delegate instance.
+ */
+public abstract class ForwardingDOMRpcService extends ForwardingObject implements DOMRpcService {
+ @Override
+ protected abstract @Nonnull DOMRpcService delegate();
+
+ @Override
+ public CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(final SchemaPath type, final NormalizedNode<?, ?> input) {
+ return delegate().invokeRpc(type, input);
+ }
+
+ @Override
+ public <T extends DOMRpcAvailabilityListener> ListenerRegistration<T> registerRpcListener(final T listener) {
+ return delegate().registerRpcListener(listener);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import com.google.common.collect.ForwardingObject;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
+
+/**
+ * Utility {@link DOMTransactionChain} implementation which forwards all interface
+ * method invocation to a delegate instance.
+ */
+public abstract class ForwardingDOMTransactionChain extends ForwardingObject implements DOMTransactionChain {
+ @Override
+ protected abstract @Nonnull DOMTransactionChain delegate();
+
+ @Override
+ public void close() {
+ delegate().close();
+ }
+
+ @Override
+ public DOMDataReadOnlyTransaction newReadOnlyTransaction() {
+ return delegate().newReadOnlyTransaction();
+ }
+
+ @Override
+ public DOMDataReadWriteTransaction newReadWriteTransaction() {
+ return delegate().newReadWriteTransaction();
+ }
+
+ @Override
+ public DOMDataWriteTransaction newWriteOnlyTransaction() {
+ return delegate().newWriteOnlyTransaction();
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Preconditions;
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This is a single node within the registration tree. Note that the data returned from
+ * and instance of this class is guaranteed to have any relevance or consistency
+ * only as long as the {@link RegistrationTreeSnapshot} instance through which it is reached
+ * remains unclosed.
+ *
+ * @param <T> registration type
+ * @author Robert Varga
+ */
+public final class RegistrationTreeNode<T> implements Identifiable<PathArgument> {
+ private static final Logger LOG = LoggerFactory.getLogger(RegistrationTreeNode.class);
+
+ private final Map<PathArgument, RegistrationTreeNode<T>> children = new HashMap<>();
+ private final Collection<T> registrations = new ArrayList<>(2);
+ private final Collection<T> publicRegistrations = Collections.unmodifiableCollection(registrations);
+ private final Reference<RegistrationTreeNode<T>> parent;
+ private final PathArgument identifier;
+
+ RegistrationTreeNode(final RegistrationTreeNode<T> parent, final PathArgument identifier) {
+ this.parent = new WeakReference<>(parent);
+ this.identifier = identifier;
+ }
+
+ @Override
+ public PathArgument getIdentifier() {
+ return identifier;
+ }
+
+ /**
+ * Return the child matching a {@link PathArgument} specification.
+ *
+ * @param arg Child identifier
+ * @return Child matching exactly, or null.
+ */
+ public RegistrationTreeNode<T> getExactChild(@Nonnull final PathArgument arg) {
+ return children.get(Preconditions.checkNotNull(arg));
+ }
+
+ /**
+ * Return a collection children which match a {@link PathArgument} specification inexactly.
+ * This explicitly excludes the child returned by {@link #getExactChild(PathArgument)}.
+ *
+ * @param arg Child identifier
+ * @return Collection of children, guaranteed to be non-null.
+ */
+ public @Nonnull Collection<RegistrationTreeNode<T>> getInexactChildren(@Nonnull final PathArgument arg) {
+ Preconditions.checkNotNull(arg);
+ if (arg instanceof NodeWithValue || arg instanceof NodeIdentifierWithPredicates) {
+ /*
+ * TODO: This just all-or-nothing wildcards, which we have historically supported. Given
+ * that the argument is supposed to have all the elements filled out, we could support
+ * partial wildcards by iterating over the registrations and matching the maps for
+ * partial matches.
+ */
+ final RegistrationTreeNode<T> child = children.get(new NodeIdentifier(arg.getNodeType()));
+ if (child == null) {
+ return Collections.emptyList();
+ } else {
+ return Collections.singletonList(child);
+ }
+ } else {
+ return Collections.emptyList();
+ }
+ }
+
+ public Collection<T> getRegistrations() {
+ return publicRegistrations;
+ }
+
+ RegistrationTreeNode<T> ensureChild(@Nonnull final PathArgument child) {
+ RegistrationTreeNode<T> potential = children.get(Preconditions.checkNotNull(child));
+ if (potential == null) {
+ potential = new RegistrationTreeNode<T>(this, child);
+ children.put(child, potential);
+ }
+ return potential;
+ }
+
+ void addRegistration(@Nonnull final T registration) {
+ registrations.add(Preconditions.checkNotNull(registration));
+ LOG.debug("Registration {} added", registration);
+ }
+
+ void removeRegistration(@Nonnull final T registration) {
+ registrations.remove(Preconditions.checkNotNull(registration));
+ LOG.debug("Registration {} removed", registration);
+
+ // We have been called with the write-lock held, so we can perform some cleanup.
+ removeThisIfUnused();
+ }
+
+ private void removeThisIfUnused() {
+ final RegistrationTreeNode<T> p = parent.get();
+ if (p != null && registrations.isEmpty() && children.isEmpty()) {
+ p.removeChild(identifier);
+ }
+ }
+
+ private void removeChild(final PathArgument arg) {
+ children.remove(arg);
+ removeThisIfUnused();
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("identifier", identifier)
+ .add("registrations", registrations.size())
+ .add("children", children.size()).toString();
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.spi;
+
+import com.google.common.base.Preconditions;
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+import java.util.concurrent.locks.Lock;
+
+/**
+ * A stable read-only snapshot of a {@link AbstractRegistrationTree}.
+ *
+ * @author Robert Varga
+ */
+public final class RegistrationTreeSnapshot<T> implements AutoCloseable {
+ @SuppressWarnings("rawtypes")
+ private static final AtomicIntegerFieldUpdater<RegistrationTreeSnapshot> CLOSED_UPDATER = AtomicIntegerFieldUpdater.newUpdater(RegistrationTreeSnapshot.class, "closed");
+ private final RegistrationTreeNode<T> node;
+ private final Lock lock;
+
+ // Used via CLOSED_UPDATER
+ @SuppressWarnings("unused")
+ private volatile int closed = 0;
+
+ RegistrationTreeSnapshot(final Lock lock, final RegistrationTreeNode<T> node) {
+ this.lock = Preconditions.checkNotNull(lock);
+ this.node = Preconditions.checkNotNull(node);
+ }
+
+ public RegistrationTreeNode<T> getRootNode() {
+ return node;
+ }
+
+ @Override
+ public void close() {
+ if (CLOSED_UPDATER.compareAndSet(this, 0, 1)) {
+ lock.unlock();
+ }
+ }
+}
--- /dev/null
+/*\r
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.\r
+ *\r
+ * This program and the accompanying materials are made available under the\r
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
+ * and is available at http://www.eclipse.org/legal/epl-v10.html\r
+ */\r
+package org.opendaylight.controller.sal.core.spi;\r
+\r
+import java.util.Set;\r
+\r
+import org.opendaylight.controller.sal.core.api.BrokerService;\r
+import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;\r
+import org.opendaylight.controller.sal.core.api.Consumer.ConsumerFunctionality;\r
+import org.opendaylight.controller.sal.core.api.Provider.ProviderFunctionality;\r
+\r
+public interface BrokerModule {\r
+\r
+ Set<Class<? extends BrokerService>> getProvidedServices();\r
+\r
+ Set<Class<? extends ConsumerFunctionality>> getSupportedConsumerFunctionality();\r
+\r
+ <T extends BrokerService> T getServiceForSession(Class<T> service,\r
+ ConsumerSession session);\r
+\r
+ Set<Class<? extends ProviderFunctionality>> getSupportedProviderFunctionality();\r
+}\r
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi;
+
+import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;
+import org.opendaylight.controller.sal.core.api.BrokerService;
+
+public abstract class ForwardingConsumerSession implements ConsumerSession {
+
+ protected abstract ConsumerSession delegate();
+
+ @Override
+ public void close() {
+ delegate().close();
+ }
+
+ @Override
+ public <T extends BrokerService> T getService(Class<T> arg0) {
+ return delegate().getService(arg0);
+ }
+
+ @Override
+ public boolean isClosed() {
+ return delegate().isClosed();
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.sal.core.spi;
+
+import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
+import org.opendaylight.controller.sal.core.api.BrokerService;
+
+public abstract class ForwardingProviderSession implements ProviderSession {
+
+ protected abstract ProviderSession delegate();
+
+ @Override
+ public void close() {
+ delegate().close();
+ }
+
+ @Override
+ public <T extends BrokerService> T getService(Class<T> arg0) {
+ return delegate().getService(arg0);
+ }
+
+ @Override
+ public boolean isClosed() {
+ return delegate().isClosed();
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.base.Preconditions;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+/**
+ * Abstract DOM Store Transaction.
+ *
+ * Convenience super implementation of DOM Store transaction which provides
+ * common implementation of {@link #toString()} and {@link #getIdentifier()}.
+ *
+ * It can optionally capture the context where it was allocated.
+ *
+ * <T> identifier type
+ */
+@Beta
+public abstract class AbstractDOMStoreTransaction<T> implements DOMStoreTransaction {
+ private final Throwable debugContext;
+ private final T identifier;
+
+ protected AbstractDOMStoreTransaction(@Nonnull final T identifier) {
+ this(identifier, false);
+ }
+
+ protected AbstractDOMStoreTransaction(@Nonnull final T identifier, final boolean debug) {
+ this.identifier = Preconditions.checkNotNull(identifier, "Identifier must not be null.");
+ this.debugContext = debug ? new Throwable().fillInStackTrace() : null;
+ }
+
+ @Override
+ public final T getIdentifier() {
+ return identifier;
+ }
+
+ /**
+ * Return the context in which this transaction was allocated.
+ *
+ * @return The context in which this transaction was allocated, or null
+ * if the context was not recorded.
+ */
+ @Nullable
+ public final Throwable getDebugContext() {
+ return debugContext;
+ }
+
+ @Override
+ public final String toString() {
+ return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
+ }
+
+ /**
+ * Add class-specific toString attributes.
+ *
+ * @param toStringHelper
+ * ToStringHelper instance
+ * @return ToStringHelper instance which was passed in
+ */
+ protected ToStringHelper addToStringAttributes(@Nonnull final ToStringHelper toStringHelper) {
+ return toStringHelper.add("id", identifier);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.List;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
+import org.opendaylight.controller.md.sal.dom.spi.AbstractDOMDataTreeChangeListenerRegistration;
+import org.opendaylight.controller.md.sal.dom.spi.AbstractRegistrationTree;
+import org.opendaylight.controller.md.sal.dom.spi.RegistrationTreeNode;
+import org.opendaylight.controller.md.sal.dom.spi.RegistrationTreeSnapshot;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Abstract base class for {@link DOMStoreTreeChangePublisher} implementations.
+ */
+public abstract class AbstractDOMStoreTreeChangePublisher extends AbstractRegistrationTree<AbstractDOMDataTreeChangeListenerRegistration<?>> implements DOMStoreTreeChangePublisher {
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractDOMStoreTreeChangePublisher.class);
+
+ /**
+ * Callback for subclass to notify specified registrations of a candidate at a specified path. This method is guaranteed
+ * to be only called from within {@link #processCandidateTree(DataTreeCandidate)}.
+ *
+ * @param registrations Registrations which are affected by the candidate node
+ * @param path Path of changed candidate node. Guaranteed to match the path specified by the registration
+ * @param node Candidate node
+ */
+ protected abstract void notifyListeners(@Nonnull Collection<AbstractDOMDataTreeChangeListenerRegistration<?>> registrations, @Nonnull YangInstanceIdentifier path, @Nonnull DataTreeCandidateNode node);
+
+ /**
+ * Callback notifying the subclass that the specified registration is being closed and it's user no longer
+ * wishes to receive notifications. This notification is invoked while the {@link org.opendaylight.yangtools.concepts.ListenerRegistration#close()}
+ * method is executing. Subclasses can use this callback to properly remove any delayed notifications pending
+ * towards the registration.
+ *
+ * @param registration Registration which is being closed
+ */
+ protected abstract void registrationRemoved(@Nonnull AbstractDOMDataTreeChangeListenerRegistration<?> registration);
+
+ /**
+ * Process a candidate tree with respect to registered listeners.
+ *
+ * @param candidate candidate three which needs to be processed
+ */
+ protected final void processCandidateTree(@Nonnull final DataTreeCandidate candidate) {
+ final DataTreeCandidateNode node = candidate.getRootNode();
+ if (node.getModificationType() == ModificationType.UNMODIFIED) {
+ LOG.debug("Skipping unmodified candidate {}", candidate);
+ return;
+ }
+
+ try (final RegistrationTreeSnapshot<AbstractDOMDataTreeChangeListenerRegistration<?>> snapshot = takeSnapshot()) {
+ final List<PathArgument> toLookup = ImmutableList.copyOf(candidate.getRootPath().getPathArguments());
+ lookupAndNotify(toLookup, 0, snapshot.getRootNode(), candidate);
+ }
+ }
+
+ @Override
+ public final <L extends DOMDataTreeChangeListener> AbstractDOMDataTreeChangeListenerRegistration<L> registerTreeChangeListener(final YangInstanceIdentifier treeId, final L listener) {
+ // Take the write lock
+ takeLock();
+ try {
+ final RegistrationTreeNode<AbstractDOMDataTreeChangeListenerRegistration<?>> node = findNodeFor(treeId.getPathArguments());
+ final AbstractDOMDataTreeChangeListenerRegistration<L> reg = new AbstractDOMDataTreeChangeListenerRegistration<L>(listener) {
+ @Override
+ protected void removeRegistration() {
+ AbstractDOMStoreTreeChangePublisher.this.removeRegistration(node, this);
+ registrationRemoved(this);
+ }
+ };
+
+ addRegistration(node, reg);
+ return reg;
+ } finally {
+ // Always release the lock
+ releaseLock();
+ }
+ }
+
+ private void lookupAndNotify(final List<PathArgument> args, final int offset, final RegistrationTreeNode<AbstractDOMDataTreeChangeListenerRegistration<?>> node, final DataTreeCandidate candidate) {
+ if (args.size() != offset) {
+ final PathArgument arg = args.get(offset);
+
+ final RegistrationTreeNode<AbstractDOMDataTreeChangeListenerRegistration<?>> exactChild = node.getExactChild(arg);
+ if (exactChild != null) {
+ lookupAndNotify(args, offset + 1, exactChild, candidate);
+ }
+
+ for (RegistrationTreeNode<AbstractDOMDataTreeChangeListenerRegistration<?>> c : node.getInexactChildren(arg)) {
+ lookupAndNotify(args, offset + 1, c, candidate);
+ }
+ } else {
+ notifyNode(candidate.getRootPath(), node, candidate.getRootNode());
+ }
+ }
+
+ private void notifyNode(final YangInstanceIdentifier path, final RegistrationTreeNode<AbstractDOMDataTreeChangeListenerRegistration<?>> regNode, final DataTreeCandidateNode candNode) {
+ if (candNode.getModificationType() == ModificationType.UNMODIFIED) {
+ LOG.debug("Skipping unmodified candidate {}", path);
+ return;
+ }
+
+ final Collection<AbstractDOMDataTreeChangeListenerRegistration<?>> regs = regNode.getRegistrations();
+ if (!regs.isEmpty()) {
+ notifyListeners(regs, path, candNode);
+ }
+
+ for (DataTreeCandidateNode candChild : candNode.getChildNodes()) {
+ if (candChild.getModificationType() != ModificationType.UNMODIFIED) {
+ final RegistrationTreeNode<AbstractDOMDataTreeChangeListenerRegistration<?>> regChild = regNode.getExactChild(candChild.getIdentifier());
+ if (regChild != null) {
+ notifyNode(path.node(candChild.getIdentifier()), regChild, candChild);
+ }
+
+ for (RegistrationTreeNode<AbstractDOMDataTreeChangeListenerRegistration<?>> rc : regNode.getInexactChildren(candChild.getIdentifier())) {
+ notifyNode(path.node(candChild.getIdentifier()), rc, candChild);
+ }
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.Map.Entry;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Abstract implementation of the {@link DOMStoreTransactionChain} interface relying on {@link DataTreeSnapshot} supplier
+ * and backend commit coordinator.
+ *
+ * @param <T> transaction identifier type
+ */
+@Beta
+public abstract class AbstractSnapshotBackedTransactionChain<T> extends TransactionReadyPrototype<T> implements DOMStoreTransactionChain {
+ private static abstract class State {
+ /**
+ * Allocate a new snapshot.
+ *
+ * @return A new snapshot
+ */
+ protected abstract DataTreeSnapshot getSnapshot();
+ }
+
+ private static final class Idle extends State {
+ private final AbstractSnapshotBackedTransactionChain<?> chain;
+
+ Idle(final AbstractSnapshotBackedTransactionChain<?> chain) {
+ this.chain = Preconditions.checkNotNull(chain);
+ }
+
+ @Override
+ protected DataTreeSnapshot getSnapshot() {
+ return chain.takeSnapshot();
+ }
+ }
+
+ /**
+ * We have a transaction out there.
+ */
+ private static final class Allocated extends State {
+ private static final AtomicReferenceFieldUpdater<Allocated, DataTreeSnapshot> SNAPSHOT_UPDATER =
+ AtomicReferenceFieldUpdater.newUpdater(Allocated.class, DataTreeSnapshot.class, "snapshot");
+ private final DOMStoreWriteTransaction transaction;
+ private volatile DataTreeSnapshot snapshot;
+
+ Allocated(final DOMStoreWriteTransaction transaction) {
+ this.transaction = Preconditions.checkNotNull(transaction);
+ }
+
+ public DOMStoreWriteTransaction getTransaction() {
+ return transaction;
+ }
+
+ @Override
+ protected DataTreeSnapshot getSnapshot() {
+ final DataTreeSnapshot ret = snapshot;
+ Preconditions.checkState(ret != null, "Previous transaction %s is not ready yet", transaction.getIdentifier());
+ return ret;
+ }
+
+ void setSnapshot(final DataTreeSnapshot snapshot) {
+ final boolean success = SNAPSHOT_UPDATER.compareAndSet(this, null, snapshot);
+ Preconditions.checkState(success, "Transaction %s has already been marked as ready", transaction.getIdentifier());
+ }
+ }
+
+ /**
+ * Chain is logically shut down, no further allocation allowed.
+ */
+ private static final class Shutdown extends State {
+ private final String message;
+
+ Shutdown(final String message) {
+ this.message = Preconditions.checkNotNull(message);
+ }
+
+ @Override
+ protected DataTreeSnapshot getSnapshot() {
+ throw new IllegalStateException(message);
+ }
+ }
+
+ @SuppressWarnings("rawtypes")
+ private static final AtomicReferenceFieldUpdater<AbstractSnapshotBackedTransactionChain, State> STATE_UPDATER =
+ AtomicReferenceFieldUpdater.newUpdater(AbstractSnapshotBackedTransactionChain.class, State.class, "state");
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractSnapshotBackedTransactionChain.class);
+ private static final Shutdown CLOSED = new Shutdown("Transaction chain is closed");
+ private static final Shutdown FAILED = new Shutdown("Transaction chain has failed");
+ private final Idle idleState;
+ private volatile State state;
+
+ protected AbstractSnapshotBackedTransactionChain() {
+ idleState = new Idle(this);
+ state = idleState;
+ }
+
+ private Entry<State, DataTreeSnapshot> getSnapshot() {
+ final State localState = state;
+ return new SimpleEntry<>(localState, localState.getSnapshot());
+ }
+
+ private boolean recordTransaction(final State expected, final DOMStoreWriteTransaction transaction) {
+ final State state = new Allocated(transaction);
+ return STATE_UPDATER.compareAndSet(this, expected, state);
+ }
+
+ @Override
+ public final DOMStoreReadTransaction newReadOnlyTransaction() {
+ return newReadOnlyTransaction(nextTransactionIdentifier());
+ }
+
+ protected DOMStoreReadTransaction newReadOnlyTransaction(T transactionId) {
+ final Entry<State, DataTreeSnapshot> entry = getSnapshot();
+ return SnapshotBackedTransactions.newReadTransaction(transactionId, getDebugTransactions(), entry.getValue());
+ }
+
+ @Override
+ public final DOMStoreReadWriteTransaction newReadWriteTransaction() {
+ return newReadWriteTransaction(nextTransactionIdentifier());
+ }
+
+ protected DOMStoreReadWriteTransaction newReadWriteTransaction(T transactionId) {
+ Entry<State, DataTreeSnapshot> entry;
+ DOMStoreReadWriteTransaction ret;
+
+ do {
+ entry = getSnapshot();
+ ret = new SnapshotBackedReadWriteTransaction<T>(transactionId, getDebugTransactions(), entry.getValue(), this);
+ } while (!recordTransaction(entry.getKey(), ret));
+
+ return ret;
+ }
+
+ @Override
+ public final DOMStoreWriteTransaction newWriteOnlyTransaction() {
+ return newWriteOnlyTransaction(nextTransactionIdentifier());
+ }
+
+ protected DOMStoreWriteTransaction newWriteOnlyTransaction(T transactionId) {
+ Entry<State, DataTreeSnapshot> entry;
+ DOMStoreWriteTransaction ret;
+
+ do {
+ entry = getSnapshot();
+ ret = new SnapshotBackedWriteTransaction<T>(transactionId, getDebugTransactions(), entry.getValue(), this);
+ } while (!recordTransaction(entry.getKey(), ret));
+
+ return ret;
+ }
+
+ @Override
+ protected final void transactionAborted(final SnapshotBackedWriteTransaction<T> tx) {
+ final State localState = state;
+ if (localState instanceof Allocated) {
+ final Allocated allocated = (Allocated)localState;
+ if (allocated.getTransaction().equals(tx)) {
+ final boolean success = STATE_UPDATER.compareAndSet(this, localState, idleState);
+ if (!success) {
+ LOG.warn("Transaction {} aborted, but chain {} state already transitioned from {} to {}, very strange",
+ tx, this, localState, state);
+ }
+ }
+ }
+ }
+
+ @Override
+ protected final DOMStoreThreePhaseCommitCohort transactionReady(final SnapshotBackedWriteTransaction<T> tx, final DataTreeModification tree) {
+ final State localState = state;
+
+ if (localState instanceof Allocated) {
+ final Allocated allocated = (Allocated)localState;
+ final DOMStoreWriteTransaction transaction = allocated.getTransaction();
+ Preconditions.checkState(tx.equals(transaction), "Mis-ordered ready transaction %s last allocated was %s", tx, transaction);
+ allocated.setSnapshot(tree);
+ } else {
+ LOG.debug("Ignoring transaction {} readiness due to state {}", tx, localState);
+ }
+
+ return createCohort(tx, tree);
+ }
+
+ @Override
+ public final void close() {
+ final State localState = state;
+
+ do {
+ Preconditions.checkState(!CLOSED.equals(localState), "Transaction chain {} has been closed", this);
+
+ if (FAILED.equals(localState)) {
+ LOG.debug("Ignoring user close in failed state");
+ return;
+ }
+ } while (!STATE_UPDATER.compareAndSet(this, localState, CLOSED));
+ }
+
+ /**
+ * Notify the base logic that a previously-submitted transaction has been committed successfully.
+ *
+ * @param transaction Transaction which completed successfully.
+ */
+ protected final void onTransactionCommited(final SnapshotBackedWriteTransaction<T> transaction) {
+ // If the committed transaction was the one we allocated last,
+ // we clear it and the ready snapshot, so the next transaction
+ // allocated refers to the data tree directly.
+ final State localState = state;
+
+ if (!(localState instanceof Allocated)) {
+ // This can legally happen if the chain is shut down before the transaction was committed
+ // by the backend.
+ LOG.debug("Ignoring successful transaction {} in state {}", transaction, localState);
+ return;
+ }
+
+ final Allocated allocated = (Allocated)localState;
+ final DOMStoreWriteTransaction tx = allocated.getTransaction();
+ if (!tx.equals(transaction)) {
+ LOG.debug("Ignoring non-latest successful transaction {} in state {}", transaction, allocated);
+ return;
+ }
+
+ if (!STATE_UPDATER.compareAndSet(this, localState, idleState)) {
+ LOG.debug("Transaction chain {} has already transitioned from {} to {}, not making it idle", this, localState, state);
+ }
+ }
+
+ /**
+ * Notify the base logic that a previously-submitted transaction has failed.
+ *
+ * @param transaction Transaction which failed.
+ * @param cause Failure cause
+ */
+ protected final void onTransactionFailed(final SnapshotBackedWriteTransaction<T> transaction, final Throwable cause) {
+ LOG.debug("Transaction chain {} failed on transaction {}", this, transaction, cause);
+ state = FAILED;
+ }
+
+ /**
+ * Return the next transaction identifier.
+ *
+ * @return transaction identifier.
+ */
+ protected abstract T nextTransactionIdentifier();
+
+ /**
+ * Inquire as to whether transactions should record their allocation context.
+ *
+ * @return True if allocation context should be recorded.
+ */
+ protected abstract boolean getDebugTransactions();
+
+ /**
+ * Take a fresh {@link DataTreeSnapshot} from the backend.
+ *
+ * @return A new snapshot.
+ */
+ protected abstract DataTreeSnapshot takeSnapshot();
+
+ /**
+ * Create a cohort for driving the transaction through the commit process.
+ *
+ * @param transaction Transaction handle
+ * @param modification {@link DataTreeModification} which needs to be applied to the backend
+ * @return A {@link DOMStoreThreePhaseCommitCohort} cohort.
+ */
+ protected abstract DOMStoreThreePhaseCommitCohort createCohort(final SnapshotBackedWriteTransaction<T> transaction, final DataTreeModification modification);
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * DOM Data Store
+ *
+ * <p>
+ * DOM Data Store provides transactional tree-like storage for YANG-modeled
+ * entities described by YANG schema and represented by {@link NormalizedNode}.
+ *
+ * Read and write access to stored data is provided only via transactions
+ * created using {@link #newReadOnlyTransaction()},
+ * {@link #newWriteOnlyTransaction()} and {@link #newReadWriteTransaction()}, or
+ * by creating {@link org.opendaylight.controller.md.sal.common.api.data.TransactionChain}.
+ *
+ */
+public interface DOMStore extends DOMStoreTransactionFactory {
+
+ /**
+ * Registers {@link org.opendaylight.controller.md.sal.common.api.data.DataChangeListener} for Data Change callbacks which will
+ * be triggered on the change of provided subpath. What constitutes a change
+ * depends on the @scope parameter.
+ *
+ * Listener upon registration receives an initial callback
+ * {@link AsyncDataChangeListener#onDataChanged(org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent)}
+ * which contains stable view of data tree at the time of registration.
+ *
+ * Â @param path Path (subtree identifier) on which client listener will be
+ * invoked.
+ *
+ * @param listener
+ * Instance of listener which should be invoked on
+ * @param scope
+ * Scope of change which triggers callback.
+ * @return Listener Registration object, which client may use to close
+ * registration / interest on receiving data changes.
+ *
+ */
+ <L extends AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>>> ListenerRegistration<L> registerChangeListener(
+ YangInstanceIdentifier path, L listener, DataChangeScope scope);
+
+ /**
+ *
+ * Creates new transaction chain.
+ *
+ * Transactions in a chain need to be committed in sequence and each
+ * transaction should see the effects of previous transactions as if they
+ * happened.
+ *
+ * See {@link DOMStoreTransactionChain} for more information.
+ *
+ * @return Newly created transaction chain.
+ */
+ DOMStoreTransactionChain createTransactionChain();
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+
+public interface DOMStoreReadTransaction extends DOMStoreTransaction {
+
+ /**
+ * Reads data from provided logical data store located at provided path
+ *
+ * @param path
+ * Path which uniquely identifies subtree which client want to
+ * read
+ * @return a CheckFuture containing the result of the read. The Future blocks until the
+ * commit operation is complete. Once complete:
+ * <ul>
+ * <li>If the data at the supplied path exists, the Future returns an Optional object
+ * containing the data.</li>
+ * <li>If the data at the supplied path does not exist, the Future returns
+ * Optional#absent().</li>
+ * <li>If the read of the data fails, the Future will fail with a
+ * {@link ReadFailedException} or an exception derived from ReadFailedException.</li>
+ * </ul>
+ */
+ CheckedFuture<Optional<NormalizedNode<?,?>>, ReadFailedException> read(YangInstanceIdentifier path);
+
+ /**
+ * Checks if data is available in the logical data store located at provided path.
+ * <p>
+ *
+ * Note: a successful result from this method makes no guarantee that a subsequent call to {@link #read}
+ * will succeed. It is possible that the data resides in a data store on a remote node and, if that
+ * node goes down or a network failure occurs, a subsequent read would fail. Another scenario is if
+ * the data is deleted in between the calls to <code>exists</code> and <code>read</code>
+ *
+ * @param path
+ * Path which uniquely identifies subtree which client want to
+ * check existence of
+ * @return a CheckFuture containing the result of the check.
+ * <ul>
+ * <li>If the data at the supplied path exists, the Future returns a Boolean
+ * whose value is true, false otherwise</li>
+ * <li>If checking for the data fails, the Future will fail with a
+ * {@link ReadFailedException} or an exception derived from ReadFailedException.</li>
+ * </ul>
+ */
+ CheckedFuture<Boolean, ReadFailedException> exists(YangInstanceIdentifier path);
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+/**
+ * Combination of a {@link DOMStoreReadTransaction} and {@link DOMStoreWriteTransaction}.
+ */
+public interface DOMStoreReadWriteTransaction extends DOMStoreReadTransaction, DOMStoreWriteTransaction {
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Interface implemented by the {@link DOMStore} and exposed for each {@link DOMStoreWriteTransaction}
+ * upon its transition to Ready state. The frontend (DOMStore user) uses this interface to drive the
+ * commit procedure across potentially multiple DOMStores using the Three-Phase-Commit (3PC) Protocol,
+ * as described in {@link https://en.wikipedia.org/wiki/Three-phase_commit}.
+ */
+public interface DOMStoreThreePhaseCommitCohort {
+
+ /**
+ * Sends transaction associated with this three phase commit instance to the
+ * participant, participant votes on the transaction, if the transaction
+ * should be committed or aborted.
+ *
+ * @return ListenableFuture with vote of the participant. Vote
+ * {@link ListenableFuture#get()} is following:
+ * <ul>
+ * <li>
+ * true if transaction is approved by data store.
+ * <li>false if the transaction is not approved by data store and
+ * should be aborted.
+ */
+ ListenableFuture<Boolean> canCommit();
+
+ /**
+ * Initiates a pre-commit phase of associated transaction on datastore.
+ *
+ * This message is valid only and only if and only if the participant responded
+ * on {@link #canCommit()} call with positive response.
+ *
+ * @return ListenableFuture representing acknowledgment for participant
+ * that pre-commit message was received and processed.
+ */
+ ListenableFuture<Void> preCommit();
+
+ /**
+ * Initiates a abort phase of associated transaction on data store.
+ *
+ * @return ListenableFuture representing acknowledgment for participant
+ * that abort message was received.
+ */
+ ListenableFuture<Void> abort();
+
+ /**
+ * Initiates a commit phase on of associated transaction on data store.
+ *
+ * @return ListenableFuture representing acknowledgment for participant
+ * that commit message was received and commit of transaction was
+ * processed.
+ */
+ ListenableFuture<Void> commit();
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+import org.opendaylight.yangtools.concepts.Identifiable;
+
+/**
+ * DOM Data Store transaction
+ *
+ * See {@link DOMStoreReadTransaction}, {@link DOMStoreWriteTransaction} and {@link DOMStoreReadWriteTransaction}
+ * for specific transaction types.
+ *
+ */
+public interface DOMStoreTransaction extends AutoCloseable, Identifiable<Object> {
+ /**
+ * Unique identifier of the transaction
+ */
+ @Override
+ public Object getIdentifier();
+
+ @Override
+ void close();
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+/**
+ * A chain of transactions. Transactions in a chain need to be committed in
+ * sequence and each transaction must see the effects of previous transactions
+ * as if they happened. A chain makes no guarantees of atomicity, in fact
+ * transactions are committed as soon as possible.
+ */
+public interface DOMStoreTransactionChain extends DOMStoreTransactionFactory, AutoCloseable {
+
+ /**
+ * Create a new read only transaction which will continue the chain. The
+ * previous write transaction has to be either READY or CANCELLED.
+ *
+ * If previous write transaction was already commited to data store, new
+ * read-only transaction is same as obtained via {@link DOMStore#newReadOnlyTransaction()}
+ * and contains merged result of previous one and current state of data store.
+ *
+ * Otherwise read-only transaction presents isolated view as if previous read-write
+ * transaction was successful. State which was introduced by other transactions
+ * outside this transaction chain after creation of previous transaction is not visible.
+ *
+ * @return New transaction in the chain.
+ * @throws IllegalStateException
+ * if the previous transaction was not READY or CANCELLED, or
+ * if the chain has been closed.
+ */
+ @Override
+ DOMStoreReadTransaction newReadOnlyTransaction();
+
+ /**
+ * Create a new read write transaction which will continue the chain. The
+ * previous read-write transaction has to be either COMMITED or CANCELLED.
+ *
+ * If previous write transaction was already commited to data store, new
+ * read-write transaction is same as obtained via {@link DOMStore#newReadWriteTransaction()}
+ * and contains merged result of previous one and current state of data store.
+ *
+ * Otherwise read-write transaction presents isolated view as if previous read-write
+ * transaction was successful. State which was introduced by other transactions
+ * outside this transaction chain after creation of previous transaction is not visible.
+ *
+ * @return New transaction in the chain.
+ * @throws IllegalStateException
+ * if the previous transaction was not READY or CANCELLED, or
+ * if the chain has been closed.
+ */
+ @Override
+ DOMStoreReadWriteTransaction newReadWriteTransaction();
+
+ /**
+ * Create a new write-only transaction which will continue the chain. The
+ * previous read-write transaction has to be either READY or CANCELLED.
+ *
+ *
+ * @return New transaction in the chain.
+ * @throws IllegalStateException
+ * if the previous transaction was not READY or CANCELLED, or
+ * if the chain has been closed.
+ */
+ @Override
+ DOMStoreWriteTransaction newWriteOnlyTransaction();
+
+ /**
+ * Closes Transaction Chain.
+ *
+ * Close method of transaction chain does not guarantee that
+ * last alocated transaction is ready or was submitted.
+ *
+ * @throws IllegalStateException If any of the outstanding created transactions was not canceled or ready.
+ */
+ @Override
+ void close();
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+/**
+ * Factory for DOM Store Transactions
+ *
+ * <p>
+ * Factory provides method to construct read-only, read-write and write-only
+ * transactions, which may be used to retrieve and modify stored information in
+ * Underlying {@link DOMStore} or {@link DOMStoreTransactionChain}.
+ *
+ * <p>
+ * See {@link DOMStore} or {@link DOMStoreTransactionChain} for concrete
+ * variations of this factory.
+ *
+ * <p>
+ * <b>Note:</b> This interface is used only to define common functionality
+ * between {@link DOMStore} and {@link DOMStoreTransactionChain}, which
+ * further specify behaviour of returned transactions.
+ *
+ */
+public interface DOMStoreTransactionFactory {
+
+ /**
+ *
+ * Creates a read only transaction
+ *
+ * <p>
+ * Creates a new read-only transaction, which provides read access to
+ * snapshot of current state.
+ *
+ * See {@link DOMStoreReadTransaction} for more information.
+ *
+ * @return new {@link DOMStoreReadTransaction}
+ * @throws IllegalStateException
+ * If state of factory prevents allocating new transaction.
+ *
+ */
+ DOMStoreReadTransaction newReadOnlyTransaction();
+
+ /**
+ * Creates write only transaction
+ *
+ * <p>
+ * See {@link DOMStoreWriteTransaction} for more information.
+ *
+ * @return new {@link DOMStoreWriteTransaction}
+ * @throws IllegalStateException If state of factory prevents allocating new transaction.
+ */
+ DOMStoreWriteTransaction newWriteOnlyTransaction();
+
+ /**
+ * Creates Read-Write transaction
+ *
+ * <p>
+ * See {@link DOMStoreReadWriteTransaction} for more information.
+ *
+ * @return new {@link DOMStoreWriteTransaction}
+ * @throws IllegalStateException If state of factory prevents allocating new transaction.
+ */
+ DOMStoreReadWriteTransaction newReadWriteTransaction();
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+/**
+ * Interface implemented by DOMStore implementations which allow registration
+ * of {@link DOMDataTreeChangeListener} instances.
+ */
+public interface DOMStoreTreeChangePublisher {
+ /**
+ * Registers a {@link DOMDataTreeChangeListener} to receive
+ * notifications when data changes under a given path in the conceptual data
+ * tree.
+ * <p>
+ * You are able to register for notifications for any node or subtree
+ * which can be represented using {@link YangInstanceIdentifier}.
+ * <p>
+ *
+ * You are able to register for data change notifications for a subtree or leaf
+ * even if it does not exist. You will receive notification once that node is
+ * created.
+ * <p>
+ * If there is any pre-existing data in data tree on path for which you are
+ * registering, you will receive initial data change event, which will
+ * contain all pre-existing data, marked as created.
+ *
+ * <p>
+ * This method returns a {@link ListenerRegistration} object. To
+ * "unregister" your listener for changes call the {@link ListenerRegistration#close()}
+ * method on this returned object.
+ * <p>
+ * You MUST explicitly unregister your listener when you no longer want to receive
+ * notifications. This is especially true in OSGi environments, where failure to
+ * do so during bundle shutdown can lead to stale listeners being still registered.
+ *
+ * @param treeId
+ * Data tree identifier of the subtree which should be watched for
+ * changes.
+ * @param listener
+ * Listener instance which is being registered
+ * @return Listener registration object, which may be used to unregister
+ * your listener using {@link ListenerRegistration#close()} to stop
+ * delivery of change events.
+ */
+ @Nonnull <L extends DOMDataTreeChangeListener> ListenerRegistration<L> registerTreeChangeListener(@Nonnull YangInstanceIdentifier treeId, @Nonnull L listener);
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface DOMStoreWriteTransaction extends DOMStoreTransaction {
+
+ /**
+ * Store a provided data at specified path. This acts as a add / replace
+ * operation, which is to say that whole subtree will be replaced by
+ * specified path.
+ *
+ * If you need add or merge of current object with specified use
+ * {@link #merge(org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType, org.opendaylight.yangtools.concepts.Path, Object)}
+ *
+ *
+ * @param path
+ * @param data
+ * Data object to be written
+ *
+ * @throws IllegalStateException
+ * if the client code already sealed transaction and invoked
+ * {@link #ready()}
+ */
+ void write(YangInstanceIdentifier path, NormalizedNode<?, ?> data);
+
+ /**
+ * Store a provided data at specified path. This acts as a add / replace
+ * operation, which is to say that whole subtree will be replaced by
+ * specified path.
+ *
+ * If you need add or merge of current object with specified use
+ * {@link #merge(org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType, org.opendaylight.yangtools.concepts.Path, Object)}
+ *
+ *
+ * @param path
+ * @param data
+ * Data object to be written
+ *
+ * @throws IllegalStateException
+ * if the client code already sealed transaction and invoked
+ * {@link #ready()}
+ */
+ void merge(YangInstanceIdentifier path, NormalizedNode<?, ?> data);
+
+ /**
+ *
+ * Deletes data and whole subtree located at provided path.
+ *
+ * @param path
+ * Path to delete
+ * @throws IllegalStateException
+ * if the client code already sealed transaction and invoked
+ * {@link #ready()}
+ */
+ void delete(YangInstanceIdentifier path);
+
+ /**
+ *
+ * Seals transaction, and returns three-phase commit cohort associated
+ * with this transaction and DOM Store to be coordinated by coordinator.
+ *
+ * @return Three Phase Commit Cohort instance for this transaction.
+ */
+ DOMStoreThreePhaseCommitCohort ready();
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ForwardingObject;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Abstract base class for {@link DOMStoreThreePhaseCommitCohort} implementations,
+ * which forward most of their functionality to a backend {@link #delegate()}.
+ */
+@Beta
+public abstract class ForwardingDOMStoreThreePhaseCommitCohort extends ForwardingObject implements DOMStoreThreePhaseCommitCohort {
+ @Override
+ protected abstract DOMStoreThreePhaseCommitCohort delegate();
+
+ @Override
+ public ListenableFuture<Boolean> canCommit() {
+ return delegate().canCommit();
+ }
+
+ @Override
+ public ListenableFuture<Void> preCommit() {
+ return delegate().preCommit();
+ }
+
+ @Override
+ public ListenableFuture<Void> abort() {
+ return delegate().abort();
+ }
+
+ @Override
+ public ListenableFuture<Void> commit() {
+ return delegate().commit();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import com.google.common.annotations.Beta;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ *
+ * Implementation of read-only transaction backed by {@link DataTreeSnapshot}
+ *
+ * Implementation of read-only transaction backed by {@link DataTreeSnapshot}
+ * which delegates most of its calls to similar methods provided by underlying snapshot.
+ *
+ * <T> identifier type
+ */
+@Beta
+public final class SnapshotBackedReadTransaction<T> extends AbstractDOMStoreTransaction<T> implements DOMStoreReadTransaction {
+ private static final Logger LOG = LoggerFactory.getLogger(SnapshotBackedReadTransaction.class);
+ private volatile DataTreeSnapshot stableSnapshot;
+
+ /**
+ * Creates a new read-only transaction.
+ *
+ * @param identifier Transaction Identifier
+ * @param debug Enable transaction debugging
+ * @param snapshot Snapshot which will be modified.
+ */
+ SnapshotBackedReadTransaction(final T identifier, final boolean debug, final DataTreeSnapshot snapshot) {
+ super(identifier, debug);
+ this.stableSnapshot = Preconditions.checkNotNull(snapshot);
+ LOG.debug("ReadOnly Tx: {} allocated with snapshot {}", identifier, snapshot);
+ }
+
+ @Override
+ public void close() {
+ LOG.debug("Store transaction: {} : Closed", getIdentifier());
+ stableSnapshot = null;
+ }
+
+ @Override
+ public CheckedFuture<Optional<NormalizedNode<?,?>>, ReadFailedException> read(final YangInstanceIdentifier path) {
+ LOG.debug("Tx: {} Read: {}", getIdentifier(), path);
+ checkNotNull(path, "Path must not be null.");
+
+ final DataTreeSnapshot snapshot = stableSnapshot;
+ if (snapshot == null) {
+ return Futures.immediateFailedCheckedFuture(new ReadFailedException("Transaction is closed"));
+ }
+
+ try {
+ return Futures.immediateCheckedFuture(snapshot.readNode(path));
+ } catch (Exception e) {
+ LOG.error("Tx: {} Failed Read of {}", getIdentifier(), path, e);
+ return Futures.immediateFailedCheckedFuture(new ReadFailedException("Read failed",e));
+ }
+ }
+
+ @Override
+ public CheckedFuture<Boolean, ReadFailedException> exists(final YangInstanceIdentifier path) {
+ LOG.debug("Tx: {} Exists: {}", getIdentifier(), path);
+ checkNotNull(path, "Path must not be null.");
+
+ try {
+ return Futures.immediateCheckedFuture(read(path).checkedGet().isPresent());
+ } catch (ReadFailedException e) {
+ return Futures.immediateFailedCheckedFuture(e);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import com.google.common.annotations.Beta;
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Implementation of Read-Write transaction which is backed by {@link DataTreeSnapshot}
+ * and executed according to {@link TransactionReadyPrototype}.
+ *
+ * @param <T> identifier type
+ */
+@Beta
+public final class SnapshotBackedReadWriteTransaction<T> extends SnapshotBackedWriteTransaction<T> implements DOMStoreReadWriteTransaction {
+ private static final Logger LOG = LoggerFactory.getLogger(SnapshotBackedReadWriteTransaction.class);
+
+ SnapshotBackedReadWriteTransaction(final T identifier, final boolean debug,
+ final DataTreeSnapshot snapshot, final TransactionReadyPrototype<T> readyImpl) {
+ super(identifier, debug, snapshot, readyImpl);
+ }
+
+ @Override
+ public CheckedFuture<Optional<NormalizedNode<?,?>>, ReadFailedException> read(final YangInstanceIdentifier path) {
+ LOG.debug("Tx: {} Read: {}", getIdentifier(), path);
+ checkNotNull(path, "Path must not be null.");
+
+ final Optional<NormalizedNode<?, ?>> result;
+ try {
+ result = readSnapshotNode(path);
+ } catch (Exception e) {
+ LOG.error("Tx: {} Failed Read of {}", getIdentifier(), path, e);
+ return Futures.immediateFailedCheckedFuture(new ReadFailedException("Read failed", e));
+ }
+
+ if (result == null) {
+ return Futures.immediateFailedCheckedFuture(new ReadFailedException("Transaction is closed"));
+ } else {
+ return Futures.immediateCheckedFuture(result);
+ }
+ }
+
+ @Override
+ public CheckedFuture<Boolean, ReadFailedException> exists(final YangInstanceIdentifier path) {
+ try {
+ return Futures.immediateCheckedFuture(
+ read(path).checkedGet().isPresent());
+ } catch (ReadFailedException e) {
+ return Futures.immediateFailedCheckedFuture(e);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
+
+/**
+ * Public utility class for instantiating snapshot-backed transactions.
+ */
+@Beta
+public final class SnapshotBackedTransactions {
+ private SnapshotBackedTransactions() {
+ throw new UnsupportedOperationException("Utility class");
+ }
+
+ /**
+ * Creates a new read-only transaction.
+ *
+ * @param identifier Transaction Identifier
+ * @param debug Enable transaction debugging
+ * @param snapshot Snapshot which will be modified.
+ */
+ public static <T> SnapshotBackedReadTransaction<T> newReadTransaction(final T identifier, final boolean debug, final DataTreeSnapshot snapshot) {
+ return new SnapshotBackedReadTransaction<T>(identifier, debug, snapshot);
+ }
+
+ /**
+ * Creates a new read-write transaction.
+ *
+ * @param identifier transaction Identifier
+ * @param debug Enable transaction debugging
+ * @param snapshot Snapshot which will be modified.
+ * @param readyImpl Implementation of ready method.
+ */
+ public static <T> SnapshotBackedReadWriteTransaction<T> newReadWriteTransaction(final T identifier, final boolean debug,
+ final DataTreeSnapshot snapshot, final TransactionReadyPrototype<T> readyImpl) {
+ return new SnapshotBackedReadWriteTransaction<T>(identifier, debug, snapshot, readyImpl);
+ }
+
+ /**
+ * Creates a new write-only transaction.
+ *
+ * @param identifier transaction Identifier
+ * @param debug Enable transaction debugging
+ * @param snapshot Snapshot which will be modified.
+ * @param readyImpl Implementation of ready method.
+ */
+ public static <T> SnapshotBackedWriteTransaction<T> newWriteTransaction(final T identifier, final boolean debug,
+ final DataTreeSnapshot snapshot, final TransactionReadyPrototype<T> readyImpl) {
+ return new SnapshotBackedWriteTransaction<T>(identifier, debug, snapshot, readyImpl);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.core.spi.data;
+
+import static com.google.common.base.Preconditions.checkState;
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Throwables;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Implementation of Write transaction which is backed by
+ * {@link DataTreeSnapshot} and executed according to
+ * {@link org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction.TransactionReadyPrototype}.
+ *
+ * @param <T> Identifier type
+ */
+@Beta
+public class SnapshotBackedWriteTransaction<T> extends AbstractDOMStoreTransaction<T> implements DOMStoreWriteTransaction {
+ private static final Logger LOG = LoggerFactory.getLogger(SnapshotBackedWriteTransaction.class);
+ @SuppressWarnings("rawtypes")
+ private static final AtomicReferenceFieldUpdater<SnapshotBackedWriteTransaction, TransactionReadyPrototype> READY_UPDATER =
+ AtomicReferenceFieldUpdater.newUpdater(SnapshotBackedWriteTransaction.class, TransactionReadyPrototype.class, "readyImpl");
+ @SuppressWarnings("rawtypes")
+ private static final AtomicReferenceFieldUpdater<SnapshotBackedWriteTransaction, DataTreeModification> TREE_UPDATER =
+ AtomicReferenceFieldUpdater.newUpdater(SnapshotBackedWriteTransaction.class, DataTreeModification.class, "mutableTree");
+
+ // non-null when not ready
+ private volatile TransactionReadyPrototype<T> readyImpl;
+ // non-null when not committed/closed
+ private volatile DataTreeModification mutableTree;
+
+ SnapshotBackedWriteTransaction(final T identifier, final boolean debug,
+ final DataTreeSnapshot snapshot, final TransactionReadyPrototype<T> readyImpl) {
+ super(identifier, debug);
+ this.readyImpl = Preconditions.checkNotNull(readyImpl, "readyImpl must not be null.");
+ mutableTree = snapshot.newModification();
+ LOG.debug("Write Tx: {} allocated with snapshot {}", identifier, snapshot);
+ }
+
+ @Override
+ public void write(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
+ checkNotReady();
+
+ final DataTreeModification tree = mutableTree;
+ LOG.debug("Tx: {} Write: {}:{}", getIdentifier(), path, data);
+
+ try {
+ tree.write(path, data);
+ // FIXME: Add checked exception
+ } catch (Exception e) {
+ LOG.error("Tx: {}, failed to write {}:{} in {}", getIdentifier(), path, data, tree, e);
+ // Rethrow original ones if they are subclasses of RuntimeException
+ // or Error
+ Throwables.propagateIfPossible(e);
+ // FIXME: Introduce proper checked exception
+ throw new IllegalArgumentException("Illegal input data.", e);
+ }
+ }
+
+ @Override
+ public void merge(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
+ checkNotReady();
+
+ final DataTreeModification tree = mutableTree;
+ LOG.debug("Tx: {} Merge: {}:{}", getIdentifier(), path, data);
+
+ try {
+ tree.merge(path, data);
+ // FIXME: Add checked exception
+ } catch (Exception e) {
+ LOG.error("Tx: {}, failed to write {}:{} in {}", getIdentifier(), path, data, tree, e);
+ // Rethrow original ones if they are subclasses of RuntimeException
+ // or Error
+ Throwables.propagateIfPossible(e);
+ // FIXME: Introduce proper checked exception
+ throw new IllegalArgumentException("Illegal input data.", e);
+ }
+ }
+
+ @Override
+ public void delete(final YangInstanceIdentifier path) {
+ checkNotReady();
+
+ final DataTreeModification tree = mutableTree;
+ LOG.debug("Tx: {} Delete: {}", getIdentifier(), path);
+
+ try {
+ tree.delete(path);
+ // FIXME: Add checked exception
+ } catch (Exception e) {
+ LOG.error("Tx: {}, failed to delete {} in {}", getIdentifier(), path, tree, e);
+ // Rethrow original ones if they are subclasses of RuntimeException
+ // or Error
+ Throwables.propagateIfPossible(e);
+ // FIXME: Introduce proper checked exception
+ throw new IllegalArgumentException("Illegal path to delete.", e);
+ }
+ }
+
+ /**
+ * Exposed for {@link SnapshotBackedReadWriteTransaction}'s sake only. The contract does
+ * not allow data access after the transaction has been closed or readied.
+ *
+ * @param path Path to read
+ * @return null if the the transaction has been closed;
+ */
+ final Optional<NormalizedNode<?, ?>> readSnapshotNode(final YangInstanceIdentifier path) {
+ return readyImpl == null ? null : mutableTree.readNode(path);
+ }
+
+ private final void checkNotReady() {
+ checkState(readyImpl != null, "Transaction %s is no longer open. No further modifications allowed.", getIdentifier());
+ }
+
+ @Override
+ public DOMStoreThreePhaseCommitCohort ready() {
+ @SuppressWarnings("unchecked")
+ final TransactionReadyPrototype<T> wasReady = READY_UPDATER.getAndSet(this, null);
+ checkState(wasReady != null, "Transaction %s is no longer open", getIdentifier());
+
+ LOG.debug("Store transaction: {} : Ready", getIdentifier());
+
+ final DataTreeModification tree = mutableTree;
+ TREE_UPDATER.lazySet(this, null);
+ tree.ready();
+ return wasReady.transactionReady(this, tree);
+ }
+
+ @Override
+ public void close() {
+ @SuppressWarnings("unchecked")
+ final TransactionReadyPrototype<T> wasReady = READY_UPDATER.getAndSet(this, null);
+ if (wasReady != null) {
+ LOG.debug("Store transaction: {} : Closed", getIdentifier());
+ TREE_UPDATER.lazySet(this, null);
+ wasReady.transactionAborted(this);
+ } else {
+ LOG.debug("Store transaction: {} : Closed after submit", getIdentifier());
+ }
+ }
+
+ @Override
+ protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+ return toStringHelper.add("ready", readyImpl == null);
+ }
+
+ /**
+ * Prototype implementation of
+ * {@link #ready(org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction)}
+ *
+ * This class is intended to be implemented by Transaction factories
+ * responsible for allocation of {@link org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction} and
+ * providing underlying logic for applying implementation.
+ *
+ * @param <T> identifier type
+ */
+ public abstract static class TransactionReadyPrototype<T> {
+ /**
+ * Called when a transaction is closed without being readied. This is not invoked for
+ * transactions which are ready.
+ *
+ * @param tx Transaction which got aborted.
+ */
+ protected abstract void transactionAborted(final SnapshotBackedWriteTransaction<T> tx);
+
+ /**
+ * Returns a commit coordinator associated with supplied transactions.
+ *
+ * This call must not fail.
+ *
+ * @param tx
+ * Transaction on which ready was invoked.
+ * @param tree
+ * Modified data tree which has been constructed.
+ * @return DOMStoreThreePhaseCommitCohort associated with transaction
+ */
+ protected abstract DOMStoreThreePhaseCommitCohort transactionReady(SnapshotBackedWriteTransaction<T> tx, DataTreeModification tree);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*\r
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.\r
+ *\r
+ * This program and the accompanying materials are made available under the\r
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
+ * and is available at http://www.eclipse.org/legal/epl-v10.html\r
+ */\r
+package org.opendaylight.controller.sal.core.spi.data;
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.sal.core.spi.data.statistics;
+
+import java.util.concurrent.ExecutorService;
+
+import javax.annotation.Nonnull;
+
+import org.opendaylight.yangtools.util.concurrent.QueuedNotificationManager;
+
+/**
+ * Interface for a class that tracks statistics for a data store.
+ *
+ * @author Thomas Pantelis
+ */
+public interface DOMStoreStatsTracker {
+
+ /**
+ * Sets the executor used for DataChangeListener notifications.
+ *
+ * @param dclExecutor the executor
+ */
+ void setDataChangeListenerExecutor( @Nonnull ExecutorService dclExecutor );
+
+ /**
+ * Sets the executor used internally by the data store.
+ *
+ * @param dsExecutor the executor
+ */
+ void setDataStoreExecutor( @Nonnull ExecutorService dsExecutor );
+
+ /**
+ * Sets the QueuedNotificationManager use for DataChangeListener notifications,
+ *
+ * @param manager the manager
+ */
+ void setNotificationManager( @Nonnull QueuedNotificationManager<?, ?> manager );
+}
--- /dev/null
+/*\r
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.\r
+ *\r
+ * This program and the accompanying materials are made available under the\r
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
+ * and is available at http://www.eclipse.org/legal/epl-v10.html\r
+ */\r
+package org.opendaylight.controller.sal.core.spi;
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-parent</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>sal-inmemory-datastore</artifactId>
+
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+
+ <!-- SAL Dependencies -->
+
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-api</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-api</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-common-api</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-common-util</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-core-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-core-spi</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>concepts</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>util</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-binding</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-common</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-impl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-parser-impl</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-dom-config</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+
+ <!-- Test Dependencies -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>binding-generator-impl</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>mockito-configuration</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-test-model</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name>
+
+ <Export-Package>org.opendaylight.controller.md.sal.dom.store.impl.*</Export-Package>
+
+ <Import-Package>*</Import-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.jacoco</groupId>
+ <artifactId>jacoco-maven-plugin</artifactId>
+ <configuration>
+ <includes>
+ <include>org.opendaylight.controller.*</include>
+ </includes>
+ <check>false</check>
+ </configuration>
+ <executions>
+ <execution>
+ <id>pre-test</id>
+ <goals>
+ <goal>prepare-agent</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>post-test</id>
+ <goals>
+ <goal>report</goal>
+ </goals>
+ <phase>test</phase>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>config</id>
+ <goals>
+ <goal>generate-sources</goal>
+ </goals>
+ <configuration>
+ <codeGenerators>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator</codeGeneratorClass>
+ <outputBaseDir>${jmxGeneratorPath}</outputBaseDir>
+ <additionalConfiguration>
+ <namespaceToPackage1>urn:opendaylight:params:xml:ns:yang:controller==org.opendaylight.controller.config.yang</namespaceToPackage1>
+ </additionalConfiguration>
+ </generator>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl</codeGeneratorClass>
+ <outputBaseDir>${salGeneratorPath}</outputBaseDir>
+ </generator>
+ </codeGenerators>
+ <inspectDependencies>true</inspectDependencies>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ <scm>
+ <connection>scm:git:http://git.opendaylight.org/gerrit/controller.git</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+ <tag>HEAD</tag>
+ <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL:Architecture:Clustering</url>
+ </scm>
+
+</project>
--- /dev/null
+package org.opendaylight.controller.config.yang.inmemory_datastore_provider;
+
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStoreConfigProperties;
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStoreFactory;
+import org.opendaylight.controller.md.sal.dom.store.impl.jmx.InMemoryDataStoreStats;
+
+public class InMemoryConfigDataStoreProviderModule extends org.opendaylight.controller.config.yang.inmemory_datastore_provider.AbstractInMemoryConfigDataStoreProviderModule {
+
+ public InMemoryConfigDataStoreProviderModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public InMemoryConfigDataStoreProviderModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, final org.opendaylight.controller.config.yang.inmemory_datastore_provider.InMemoryConfigDataStoreProviderModule oldModule, final java.lang.AutoCloseable oldInstance) {
+ super(identifier, dependencyResolver, oldModule, oldInstance);
+ }
+
+ @Override
+ public void customValidation() {
+ // add custom validation form module attributes here.
+ }
+
+ @Override
+ public java.lang.AutoCloseable createInstance() {
+ InMemoryDOMDataStore dataStore = InMemoryDOMDataStoreFactory.create("DOM-CFG", getSchemaServiceDependency(),
+ getDebugTransactions(),
+ InMemoryDOMDataStoreConfigProperties.create(getMaxDataChangeExecutorPoolSize(),
+ getMaxDataChangeExecutorQueueSize(), getMaxDataChangeListenerQueueSize(),
+ getMaxDataStoreExecutorQueueSize()));
+
+ InMemoryDataStoreStats statsBean = new InMemoryDataStoreStats("InMemoryConfigDataStore", dataStore);
+ dataStore.setCloseable(statsBean);
+
+ return dataStore;
+ }
+
+}
--- /dev/null
+/*
+* Generated file
+*
+* Generated from: yang module name: opendaylight-inmemory-datastore-provider yang module local name: inmemory-config-datastore-provider
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Thu Jun 19 17:10:42 PDT 2014
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.controller.config.yang.inmemory_datastore_provider;
+public class InMemoryConfigDataStoreProviderModuleFactory extends org.opendaylight.controller.config.yang.inmemory_datastore_provider.AbstractInMemoryConfigDataStoreProviderModuleFactory {
+
+}
--- /dev/null
+package org.opendaylight.controller.config.yang.inmemory_datastore_provider;
+
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStoreConfigProperties;
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStoreFactory;
+import org.opendaylight.controller.md.sal.dom.store.impl.jmx.InMemoryDataStoreStats;
+
+public class InMemoryOperationalDataStoreProviderModule extends org.opendaylight.controller.config.yang.inmemory_datastore_provider.AbstractInMemoryOperationalDataStoreProviderModule {
+
+ public InMemoryOperationalDataStoreProviderModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public InMemoryOperationalDataStoreProviderModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, final org.opendaylight.controller.config.yang.inmemory_datastore_provider.InMemoryOperationalDataStoreProviderModule oldModule, final java.lang.AutoCloseable oldInstance) {
+ super(identifier, dependencyResolver, oldModule, oldInstance);
+ }
+
+ @Override
+ public void customValidation() {
+ // add custom validation form module attributes here.
+ }
+
+ @Override
+ public java.lang.AutoCloseable createInstance() {
+ InMemoryDOMDataStore dataStore = InMemoryDOMDataStoreFactory.create("DOM-OPER", getSchemaServiceDependency(),
+ getDebugTransactions(), InMemoryDOMDataStoreConfigProperties.create(getMaxDataChangeExecutorPoolSize(),
+ getMaxDataChangeExecutorQueueSize(), getMaxDataChangeListenerQueueSize(),
+ getMaxDataStoreExecutorQueueSize()));
+
+
+ InMemoryDataStoreStats statsBean = new InMemoryDataStoreStats("InMemoryOperationalDataStore", dataStore);
+
+ dataStore.setCloseable(statsBean);
+
+ return dataStore;
+ }
+}
--- /dev/null
+/*
+* Generated file
+*
+* Generated from: yang module name: opendaylight-inmemory-datastore-provider yang module local name: inmemory-operational-datastore-provider
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Thu Jun 19 17:10:42 PDT 2014
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.controller.config.yang.inmemory_datastore_provider;
+public class InMemoryOperationalDataStoreProviderModuleFactory extends org.opendaylight.controller.config.yang.inmemory_datastore_provider.AbstractInMemoryOperationalDataStoreProviderModuleFactory {
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.ListenableFuture;
+import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+
+final class ChainedTransactionCommitImpl extends InMemoryDOMStoreThreePhaseCommitCohort {
+ private final DOMStoreTransactionChainImpl txChain;
+
+ ChainedTransactionCommitImpl(final InMemoryDOMDataStore store, final SnapshotBackedWriteTransaction<String> transaction,
+ final DataTreeModification modification, final DOMStoreTransactionChainImpl txChain) {
+ super(store, transaction, modification);
+ this.txChain = Preconditions.checkNotNull(txChain);
+ }
+
+ @Override
+ public ListenableFuture<Void> commit() {
+ ListenableFuture<Void> ret = super.commit();
+ txChain.transactionCommited(getTransaction());
+ return ret;
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import com.google.common.base.Preconditions;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public final class DOMImmutableDataChangeEvent implements
+ AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> {
+
+
+ private static final RemoveEventFactory REMOVE_EVENT_FACTORY = new RemoveEventFactory();
+ private static final CreateEventFactory CREATE_EVENT_FACTORY = new CreateEventFactory();
+
+ private final NormalizedNode<?, ?> original;
+ private final NormalizedNode<?, ?> updated;
+ private final Map<YangInstanceIdentifier, NormalizedNode<?, ?>> originalData;
+ private final Map<YangInstanceIdentifier, NormalizedNode<?, ?>> createdData;
+ private final Map<YangInstanceIdentifier, NormalizedNode<?, ?>> updatedData;
+ private final Set<YangInstanceIdentifier> removedPaths;
+ private final DataChangeScope scope;
+
+
+
+ private DOMImmutableDataChangeEvent(final Builder change) {
+ original = change.before;
+ updated = change.after;
+ originalData = Collections.unmodifiableMap(change.original);
+ createdData = Collections.unmodifiableMap(change.created);
+ updatedData = Collections.unmodifiableMap(change.updated);
+ removedPaths = Collections.unmodifiableSet(change.removed);
+ scope = change.scope;
+ }
+
+ public static final Builder builder(final DataChangeScope scope) {
+ return new Builder(scope);
+ }
+
+ protected DataChangeScope getScope() {
+ return scope;
+ }
+
+ @Override
+ public NormalizedNode<?, ?> getOriginalSubtree() {
+ return original;
+ }
+
+ @Override
+ public NormalizedNode<?, ?> getUpdatedSubtree() {
+ return updated;
+ }
+
+ @Override
+ public Map<YangInstanceIdentifier, NormalizedNode<?, ?>> getOriginalData() {
+ return originalData;
+ }
+
+ @Override
+ public Map<YangInstanceIdentifier, NormalizedNode<?, ?>> getCreatedData() {
+ return createdData;
+ }
+
+ @Override
+ public Map<YangInstanceIdentifier, NormalizedNode<?, ?>> getUpdatedData() {
+ return updatedData;
+ }
+
+ @Override
+ public Set<YangInstanceIdentifier> getRemovedPaths() {
+ return removedPaths;
+ }
+
+ @Override
+ public String toString() {
+ return "DOMImmutableDataChangeEvent [created=" + createdData.keySet() + ", updated=" + updatedData.keySet()
+ + ", removed=" + removedPaths + "]";
+ }
+
+ /**
+ * Simple event factory which creates event based on path and data
+ *
+ *
+ */
+ public interface SimpleEventFactory {
+ DOMImmutableDataChangeEvent create(YangInstanceIdentifier path, NormalizedNode<PathArgument,?> data);
+ }
+
+ /**
+ * Event factory which takes after state and creates event for it.
+ *
+ * Factory for events based on path and after state.
+ * After state is set as {@link #getUpdatedSubtree()} and is path,
+ * state mapping is also present in {@link #getUpdatedData()}.
+ *
+ * @return
+ */
+ public static final SimpleEventFactory getCreateEventFactory() {
+ return CREATE_EVENT_FACTORY;
+ }
+
+ /**
+ * Event factory which takes before state and creates event for it.
+ *
+ * Factory for events based on path and after state.
+ * After state is set as {@link #getOriginalSubtree()} and is path,
+ * state mapping is also present in {@link #getOriginalSubtree()}.
+ *
+ * Path is present in {@link #getRemovedPaths()}.
+ * @return
+ */
+ public static final SimpleEventFactory getRemoveEventFactory() {
+ return REMOVE_EVENT_FACTORY;
+ }
+ public static class Builder {
+
+ public DataChangeScope scope;
+ private NormalizedNode<?, ?> after;
+ private NormalizedNode<?, ?> before;
+
+ private final Map<YangInstanceIdentifier, NormalizedNode<?, ?>> original = new HashMap<>();
+ private final Map<YangInstanceIdentifier, NormalizedNode<?, ?>> created = new HashMap<>();
+ private final Map<YangInstanceIdentifier, NormalizedNode<?, ?>> updated = new HashMap<>();
+ private final Set<YangInstanceIdentifier> removed = new HashSet<>();
+
+ private Builder(final DataChangeScope scope) {
+ Preconditions.checkNotNull(scope, "Data change scope should not be null.");
+ this.scope = scope;
+ }
+
+ public Builder setAfter(final NormalizedNode<?, ?> node) {
+ after = node;
+ return this;
+ }
+
+ public DOMImmutableDataChangeEvent build() {
+
+ return new DOMImmutableDataChangeEvent(this);
+ }
+
+ public void merge(final DOMImmutableDataChangeEvent nestedChanges) {
+
+ original.putAll(nestedChanges.getOriginalData());
+ created.putAll(nestedChanges.getCreatedData());
+ updated.putAll(nestedChanges.getUpdatedData());
+ removed.addAll(nestedChanges.getRemovedPaths());
+
+ }
+
+ public Builder setBefore(final NormalizedNode<?, ?> node) {
+ this.before = node;
+ return this;
+ }
+
+ public Builder addCreated(final YangInstanceIdentifier path, final NormalizedNode<?, ?> node) {
+ created.put(path, node);
+ return this;
+ }
+
+ public Builder addRemoved(final YangInstanceIdentifier path, final NormalizedNode<?, ?> node) {
+ original.put(path, node);
+ removed.add(path);
+ return this;
+ }
+
+ public Builder addUpdated(final YangInstanceIdentifier path, final NormalizedNode<?, ?> before,
+ final NormalizedNode<?, ?> after) {
+ original.put(path, before);
+ updated.put(path, after);
+ return this;
+ }
+
+ public boolean isEmpty() {
+ return created.isEmpty() && removed.isEmpty() && updated.isEmpty();
+ }
+ }
+
+ private static final class RemoveEventFactory implements SimpleEventFactory {
+
+ @Override
+ public DOMImmutableDataChangeEvent create(final YangInstanceIdentifier path, final NormalizedNode<PathArgument, ?> data) {
+ return builder(DataChangeScope.BASE) //
+ .setBefore(data) //
+ .addRemoved(path, data) //
+ .build();
+ }
+
+ }
+
+ private static final class CreateEventFactory implements SimpleEventFactory {
+
+ @Override
+ public DOMImmutableDataChangeEvent create(final YangInstanceIdentifier path, final NormalizedNode<PathArgument, ?> data) {
+ return builder(DataChangeScope.BASE) //
+ .setAfter(data) //
+ .addCreated(path, data) //
+ .build();
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.sal.core.spi.data.AbstractSnapshotBackedTransactionChain;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
+
+final class DOMStoreTransactionChainImpl extends AbstractSnapshotBackedTransactionChain<String> {
+ private final InMemoryDOMDataStore store;
+
+ DOMStoreTransactionChainImpl(final InMemoryDOMDataStore store) {
+ this.store = Preconditions.checkNotNull(store);
+ }
+
+ @Override
+ protected DOMStoreThreePhaseCommitCohort createCohort(final SnapshotBackedWriteTransaction<String> tx, final DataTreeModification modification) {
+ return new ChainedTransactionCommitImpl(store, tx, modification, this);
+ }
+
+ @Override
+ protected DataTreeSnapshot takeSnapshot() {
+ return store.takeSnapshot();
+ }
+
+ @Override
+ protected String nextTransactionIdentifier() {
+ return store.nextIdentifier();
+ }
+
+ @Override
+ protected boolean getDebugTransactions() {
+ return store.getDebugTransactions();
+ }
+
+ void transactionCommited(final SnapshotBackedWriteTransaction<String> transaction) {
+ super.onTransactionCommited(transaction);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public interface DataChangeListenerRegistration<L extends AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>>> extends ListenerRegistration<L> {
+ @Override
+ L getInstance();
+
+ YangInstanceIdentifier getPath();
+
+ DataChangeScope getScope();
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerTree;
+import org.opendaylight.controller.sal.core.spi.data.DOMStore;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreTreeChangePublisher;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedTransactions;
+import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.util.ExecutorServiceUtil;
+import org.opendaylight.yangtools.util.concurrent.QueuedNotificationManager;
+import org.opendaylight.yangtools.util.concurrent.QueuedNotificationManager.Invoker;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.impl.schema.tree.InMemoryDataTreeFactory;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * In-memory DOM Data Store
+ *
+ * Implementation of {@link DOMStore} which uses {@link DataTree} and other
+ * classes such as {@link SnapshotBackedWriteTransaction}.
+ * {@link org.opendaylight.controller.sal.core.spi.data.SnapshotBackedReadTransaction} and {@link ResolveDataChangeEventsTask}
+ * to implement {@link DOMStore} contract.
+ *
+ */
+public class InMemoryDOMDataStore extends TransactionReadyPrototype<String> implements DOMStore, Identifiable<String>, SchemaContextListener, AutoCloseable, DOMStoreTreeChangePublisher {
+ private static final Logger LOG = LoggerFactory.getLogger(InMemoryDOMDataStore.class);
+
+ private static final Invoker<DataChangeListenerRegistration<?>, DOMImmutableDataChangeEvent> DCL_NOTIFICATION_MGR_INVOKER =
+ new Invoker<DataChangeListenerRegistration<?>, DOMImmutableDataChangeEvent>() {
+ @Override
+ public void invokeListener(final DataChangeListenerRegistration<?> listener,
+ final DOMImmutableDataChangeEvent notification ) {
+ final AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>> inst = listener.getInstance();
+ if (inst != null) {
+ inst.onDataChanged(notification);
+ }
+ }
+ };
+
+ private final DataTree dataTree = InMemoryDataTreeFactory.getInstance().create();
+ private final ListenerTree listenerTree = ListenerTree.create();
+ private final AtomicLong txCounter = new AtomicLong(0);
+
+ private final QueuedNotificationManager<DataChangeListenerRegistration<?>, DOMImmutableDataChangeEvent> dataChangeListenerNotificationManager;
+ private final InMemoryDOMStoreTreeChangePublisher changePublisher;
+ private final ExecutorService dataChangeListenerExecutor;
+ private final boolean debugTransactions;
+ private final String name;
+
+ private volatile AutoCloseable closeable;
+
+ public InMemoryDOMDataStore(final String name, final ExecutorService dataChangeListenerExecutor) {
+ this(name, dataChangeListenerExecutor, InMemoryDOMDataStoreConfigProperties.DEFAULT_MAX_DATA_CHANGE_LISTENER_QUEUE_SIZE, false);
+ }
+
+ public InMemoryDOMDataStore(final String name, final ExecutorService dataChangeListenerExecutor,
+ final int maxDataChangeListenerQueueSize, final boolean debugTransactions) {
+ this.name = Preconditions.checkNotNull(name);
+ this.dataChangeListenerExecutor = Preconditions.checkNotNull(dataChangeListenerExecutor);
+ this.debugTransactions = debugTransactions;
+
+ dataChangeListenerNotificationManager =
+ new QueuedNotificationManager<>(this.dataChangeListenerExecutor,
+ DCL_NOTIFICATION_MGR_INVOKER, maxDataChangeListenerQueueSize,
+ "DataChangeListenerQueueMgr");
+ changePublisher = new InMemoryDOMStoreTreeChangePublisher(this.dataChangeListenerExecutor, maxDataChangeListenerQueueSize);
+ }
+
+ public void setCloseable(final AutoCloseable closeable) {
+ this.closeable = closeable;
+ }
+
+ public QueuedNotificationManager<?, ?> getDataChangeListenerNotificationManager() {
+ return dataChangeListenerNotificationManager;
+ }
+
+ @Override
+ public final String getIdentifier() {
+ return name;
+ }
+
+ @Override
+ public DOMStoreReadTransaction newReadOnlyTransaction() {
+ return SnapshotBackedTransactions.newReadTransaction(nextIdentifier(), debugTransactions, dataTree.takeSnapshot());
+ }
+
+ @Override
+ public DOMStoreReadWriteTransaction newReadWriteTransaction() {
+ return SnapshotBackedTransactions.newReadWriteTransaction(nextIdentifier(), debugTransactions, dataTree.takeSnapshot(), this);
+ }
+
+ @Override
+ public DOMStoreWriteTransaction newWriteOnlyTransaction() {
+ return SnapshotBackedTransactions.newWriteTransaction(nextIdentifier(), debugTransactions, dataTree.takeSnapshot(), this);
+ }
+
+ @Override
+ public DOMStoreTransactionChain createTransactionChain() {
+ return new DOMStoreTransactionChainImpl(this);
+ }
+
+ @Override
+ public synchronized void onGlobalContextUpdated(final SchemaContext ctx) {
+ dataTree.setSchemaContext(ctx);
+ }
+
+ @Override
+ public void close() {
+ ExecutorServiceUtil.tryGracefulShutdown(dataChangeListenerExecutor, 30, TimeUnit.SECONDS);
+
+ if(closeable != null) {
+ try {
+ closeable.close();
+ } catch(Exception e) {
+ LOG.debug("Error closing instance", e);
+ }
+ }
+ }
+
+ public final boolean getDebugTransactions() {
+ return debugTransactions;
+ }
+
+ final DataTreeSnapshot takeSnapshot() {
+ return dataTree.takeSnapshot();
+ }
+
+ @Override
+ public <L extends AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>>> ListenerRegistration<L> registerChangeListener(
+ final YangInstanceIdentifier path, final L listener, final DataChangeScope scope) {
+
+ /*
+ * Make sure commit is not occurring right now. Listener has to be
+ * registered and its state capture enqueued at a consistent point.
+ *
+ * FIXME: improve this to read-write lock, such that multiple listener
+ * registrations can occur simultaneously
+ */
+ final DataChangeListenerRegistration<L> reg;
+ synchronized (this) {
+ LOG.debug("{}: Registering data change listener {} for {}", name, listener, path);
+
+ reg = listenerTree.registerDataChangeListener(path, listener, scope);
+
+ Optional<NormalizedNode<?, ?>> currentState = dataTree.takeSnapshot().readNode(path);
+ if (currentState.isPresent()) {
+ final NormalizedNode<?, ?> data = currentState.get();
+
+ final DOMImmutableDataChangeEvent event = DOMImmutableDataChangeEvent.builder(DataChangeScope.BASE) //
+ .setAfter(data) //
+ .addCreated(path, data) //
+ .build();
+
+ dataChangeListenerNotificationManager.submitNotification(reg, event);
+ }
+ }
+
+ return new AbstractListenerRegistration<L>(listener) {
+ @Override
+ protected void removeRegistration() {
+ synchronized (InMemoryDOMDataStore.this) {
+ reg.close();
+ }
+ }
+ };
+ }
+
+ @Override
+ public synchronized <L extends DOMDataTreeChangeListener> ListenerRegistration<L> registerTreeChangeListener(final YangInstanceIdentifier treeId, final L listener) {
+ /*
+ * Make sure commit is not occurring right now. Listener has to be
+ * registered and its state capture enqueued at a consistent point.
+ */
+ return changePublisher.registerTreeChangeListener(treeId, listener, dataTree.takeSnapshot());
+ }
+
+ @Override
+ protected void transactionAborted(final SnapshotBackedWriteTransaction<String> tx) {
+ LOG.debug("Tx: {} is closed.", tx.getIdentifier());
+ }
+
+ @Override
+ protected DOMStoreThreePhaseCommitCohort transactionReady(final SnapshotBackedWriteTransaction<String> tx, final DataTreeModification modification) {
+ LOG.debug("Tx: {} is submitted. Modifications: {}", tx.getIdentifier(), modification);
+ return new InMemoryDOMStoreThreePhaseCommitCohort(this, tx, modification);
+ }
+
+ String nextIdentifier() {
+ return name + "-" + txCounter.getAndIncrement();
+ }
+
+ void validate(final DataTreeModification modification) throws DataValidationFailedException {
+ dataTree.validate(modification);
+ }
+
+ DataTreeCandidate prepare(final DataTreeModification modification) {
+ return dataTree.prepare(modification);
+ }
+
+ synchronized void commit(final DataTreeCandidate candidate) {
+ dataTree.commit(candidate);
+ changePublisher.publishChange(candidate);
+ ResolveDataChangeEventsTask.create(candidate, listenerTree).resolve(dataChangeListenerNotificationManager);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+/**
+ * Holds configuration properties when creating an {@link InMemoryDOMDataStore} instance via the
+ * {@link InMemoryDOMDataStoreFactory}
+ *
+ * @author Thomas Pantelis
+ * @see InMemoryDOMDataStoreFactory
+ */
+public class InMemoryDOMDataStoreConfigProperties {
+
+ public static final int DEFAULT_MAX_DATA_CHANGE_EXECUTOR_QUEUE_SIZE = 1000;
+ public static final int DEFAULT_MAX_DATA_CHANGE_EXECUTOR_POOL_SIZE = 20;
+ public static final int DEFAULT_MAX_DATA_CHANGE_LISTENER_QUEUE_SIZE = 1000;
+ public static final int DEFAULT_MAX_DATA_STORE_EXECUTOR_QUEUE_SIZE = 5000;
+
+ private static final InMemoryDOMDataStoreConfigProperties DEFAULT =
+ create(DEFAULT_MAX_DATA_CHANGE_EXECUTOR_POOL_SIZE,
+ DEFAULT_MAX_DATA_CHANGE_EXECUTOR_QUEUE_SIZE,
+ DEFAULT_MAX_DATA_CHANGE_LISTENER_QUEUE_SIZE,
+ DEFAULT_MAX_DATA_STORE_EXECUTOR_QUEUE_SIZE);
+
+ private final int maxDataChangeExecutorQueueSize;
+ private final int maxDataChangeExecutorPoolSize;
+ private final int maxDataChangeListenerQueueSize;
+ private final int maxDataStoreExecutorQueueSize;
+
+ /**
+ * Constructs an instance with the given property values.
+ *
+ * @param maxDataChangeExecutorPoolSize
+ * maximum thread pool size for the data change notification executor.
+ * @param maxDataChangeExecutorQueueSize
+ * maximum queue size for the data change notification executor.
+ * @param maxDataChangeListenerQueueSize
+ * maximum queue size for the data change listeners.
+ * @param maxDataStoreExecutorQueueSize
+ * maximum queue size for the data store executor.
+ */
+ public static InMemoryDOMDataStoreConfigProperties create(int maxDataChangeExecutorPoolSize,
+ int maxDataChangeExecutorQueueSize, int maxDataChangeListenerQueueSize,
+ int maxDataStoreExecutorQueueSize) {
+ return new InMemoryDOMDataStoreConfigProperties(maxDataChangeExecutorPoolSize,
+ maxDataChangeExecutorQueueSize, maxDataChangeListenerQueueSize,
+ maxDataStoreExecutorQueueSize);
+ }
+
+ public static InMemoryDOMDataStoreConfigProperties create(int maxDataChangeExecutorPoolSize,
+ int maxDataChangeExecutorQueueSize, int maxDataChangeListenerQueueSize) {
+ return new InMemoryDOMDataStoreConfigProperties(maxDataChangeExecutorPoolSize,
+ maxDataChangeExecutorQueueSize, maxDataChangeListenerQueueSize,
+ DEFAULT_MAX_DATA_STORE_EXECUTOR_QUEUE_SIZE);
+ }
+
+ /**
+ * Returns the InMemoryDOMDataStoreConfigProperties instance with default values.
+ */
+ public static InMemoryDOMDataStoreConfigProperties getDefault() {
+ return DEFAULT;
+ }
+
+ private InMemoryDOMDataStoreConfigProperties(int maxDataChangeExecutorPoolSize,
+ int maxDataChangeExecutorQueueSize, int maxDataChangeListenerQueueSize,
+ int maxDataStoreExecutorQueueSize) {
+ this.maxDataChangeExecutorQueueSize = maxDataChangeExecutorQueueSize;
+ this.maxDataChangeExecutorPoolSize = maxDataChangeExecutorPoolSize;
+ this.maxDataChangeListenerQueueSize = maxDataChangeListenerQueueSize;
+ this.maxDataStoreExecutorQueueSize = maxDataStoreExecutorQueueSize;
+ }
+
+ /**
+ * Returns the maximum queue size for the data change notification executor.
+ */
+ public int getMaxDataChangeExecutorQueueSize() {
+ return maxDataChangeExecutorQueueSize;
+ }
+
+ /**
+ * Returns the maximum thread pool size for the data change notification executor.
+ */
+ public int getMaxDataChangeExecutorPoolSize() {
+ return maxDataChangeExecutorPoolSize;
+ }
+
+ /**
+ * Returns the maximum queue size for the data change listeners.
+ */
+ public int getMaxDataChangeListenerQueueSize() {
+ return maxDataChangeListenerQueueSize;
+ }
+
+ /**
+ * Returns the maximum queue size for the data store executor.
+ */
+ public int getMaxDataStoreExecutorQueueSize() {
+ return maxDataStoreExecutorQueueSize;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import java.util.concurrent.ExecutorService;
+import javax.annotation.Nullable;
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.yangtools.util.concurrent.SpecialExecutors;
+
+/**
+ * A factory for creating InMemoryDOMDataStore instances.
+ *
+ * @author Thomas Pantelis
+ */
+public final class InMemoryDOMDataStoreFactory {
+
+ private InMemoryDOMDataStoreFactory() {
+ }
+
+ public static InMemoryDOMDataStore create(final String name,
+ @Nullable final SchemaService schemaService) {
+ return create(name, schemaService, null);
+ }
+
+ /**
+ * Creates an InMemoryDOMDataStore instance.
+ *
+ * @param name the name of the data store
+ * @param schemaService the SchemaService to which to register the data store.
+ * @param properties configuration properties for the InMemoryDOMDataStore instance. If null,
+ * default property values are used.
+ * @return an InMemoryDOMDataStore instance
+ */
+ public static InMemoryDOMDataStore create(final String name,
+ @Nullable final SchemaService schemaService,
+ @Nullable final InMemoryDOMDataStoreConfigProperties properties) {
+ return create(name, schemaService, false, properties);
+ }
+
+ /**
+ * Creates an InMemoryDOMDataStore instance.
+ *
+ * @param name the name of the data store
+ * @param schemaService the SchemaService to which to register the data store.
+ * @param debugTransactions enable transaction debugging
+ * @param properties configuration properties for the InMemoryDOMDataStore instance. If null,
+ * default property values are used.
+ * @return an InMemoryDOMDataStore instance
+ */
+ public static InMemoryDOMDataStore create(final String name,
+ @Nullable final SchemaService schemaService, final boolean debugTransactions,
+ @Nullable final InMemoryDOMDataStoreConfigProperties properties) {
+
+ InMemoryDOMDataStoreConfigProperties actualProperties = properties;
+ if (actualProperties == null) {
+ actualProperties = InMemoryDOMDataStoreConfigProperties.getDefault();
+ }
+
+ // For DataChangeListener notifications we use an executor that provides the fastest
+ // task execution time to get higher throughput as DataChangeListeners typically provide
+ // much of the business logic for a data model. If the executor queue size limit is reached,
+ // subsequent submitted notifications will block the calling thread.
+ int dclExecutorMaxQueueSize = actualProperties.getMaxDataChangeExecutorQueueSize();
+ int dclExecutorMaxPoolSize = actualProperties.getMaxDataChangeExecutorPoolSize();
+
+ ExecutorService dataChangeListenerExecutor = SpecialExecutors.newBlockingBoundedFastThreadPool(
+ dclExecutorMaxPoolSize, dclExecutorMaxQueueSize, name + "-DCL" );
+
+ final InMemoryDOMDataStore dataStore = new InMemoryDOMDataStore(name, dataChangeListenerExecutor,
+ actualProperties.getMaxDataChangeListenerQueueSize(), debugTransactions);
+
+ if (schemaService != null) {
+ schemaService.registerSchemaContextListener(dataStore);
+ }
+
+ return dataStore;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import static com.google.common.base.Preconditions.checkState;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.sal.core.spi.data.AbstractDOMStoreTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ConflictingModificationAppliedException;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class InMemoryDOMStoreThreePhaseCommitCohort implements DOMStoreThreePhaseCommitCohort {
+ private static final Logger LOG = LoggerFactory.getLogger(InMemoryDOMStoreThreePhaseCommitCohort.class);
+ private static final ListenableFuture<Void> SUCCESSFUL_FUTURE = Futures.immediateFuture(null);
+ private static final ListenableFuture<Boolean> CAN_COMMIT_FUTURE = Futures.immediateFuture(Boolean.TRUE);
+ private final SnapshotBackedWriteTransaction<String> transaction;
+ private final DataTreeModification modification;
+ private final InMemoryDOMDataStore store;
+ private DataTreeCandidate candidate;
+
+ public InMemoryDOMStoreThreePhaseCommitCohort(final InMemoryDOMDataStore store, final SnapshotBackedWriteTransaction<String> writeTransaction, final DataTreeModification modification) {
+ this.transaction = Preconditions.checkNotNull(writeTransaction);
+ this.modification = Preconditions.checkNotNull(modification);
+ this.store = Preconditions.checkNotNull(store);
+ }
+
+ private static void warnDebugContext(final AbstractDOMStoreTransaction<?> transaction) {
+ final Throwable ctx = transaction.getDebugContext();
+ if (ctx != null) {
+ LOG.warn("Transaction {} has been allocated in the following context", transaction.getIdentifier(), ctx);
+ }
+ }
+
+ @Override
+ public final ListenableFuture<Boolean> canCommit() {
+ try {
+ store.validate(modification);
+ LOG.debug("Store Transaction: {} can be committed", getTransaction().getIdentifier());
+ return CAN_COMMIT_FUTURE;
+ } catch (ConflictingModificationAppliedException e) {
+ LOG.warn("Store Tx: {} Conflicting modification for {}.", getTransaction().getIdentifier(),
+ e.getPath());
+ warnDebugContext(getTransaction());
+ return Futures.immediateFailedFuture(new OptimisticLockFailedException("Optimistic lock failed.", e));
+ } catch (DataValidationFailedException e) {
+ LOG.warn("Store Tx: {} Data Precondition failed for {}.", getTransaction().getIdentifier(),
+ e.getPath(), e);
+ warnDebugContext(getTransaction());
+
+ // For debugging purposes, allow dumping of the modification. Coupled with the above
+ // precondition log, it should allow us to understand what went on.
+ LOG.trace("Store Tx: {} modifications: {} tree: {}", modification, store);
+
+ return Futures.immediateFailedFuture(new TransactionCommitFailedException("Data did not pass validation.", e));
+ } catch (Exception e) {
+ LOG.warn("Unexpected failure in validation phase", e);
+ return Futures.immediateFailedFuture(e);
+ }
+ }
+
+ @Override
+ public final ListenableFuture<Void> preCommit() {
+ try {
+ candidate = store.prepare(modification);
+ return SUCCESSFUL_FUTURE;
+ } catch (Exception e) {
+ LOG.warn("Unexpected failure in pre-commit phase", e);
+ return Futures.immediateFailedFuture(e);
+ }
+ }
+
+ @Override
+ public final ListenableFuture<Void> abort() {
+ candidate = null;
+ return SUCCESSFUL_FUTURE;
+ }
+
+ protected final SnapshotBackedWriteTransaction<String> getTransaction() {
+ return transaction;
+ }
+
+ @Override
+ public ListenableFuture<Void> commit() {
+ checkState(candidate != null, "Proposed subtree must be computed");
+
+ /*
+ * The commit has to occur atomically with regard to listener
+ * registrations.
+ */
+ store.commit(candidate);
+ return SUCCESSFUL_FUTURE;
+ }
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import com.google.common.base.Optional;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.concurrent.ExecutorService;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
+import org.opendaylight.controller.md.sal.dom.spi.AbstractDOMDataTreeChangeListenerRegistration;
+import org.opendaylight.controller.sal.core.spi.data.AbstractDOMStoreTreeChangePublisher;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.util.concurrent.QueuedNotificationManager;
+import org.opendaylight.yangtools.util.concurrent.QueuedNotificationManager.Invoker;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidates;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class InMemoryDOMStoreTreeChangePublisher extends AbstractDOMStoreTreeChangePublisher {
+ private static final Invoker<AbstractDOMDataTreeChangeListenerRegistration<?>, DataTreeCandidate> MANAGER_INVOKER =
+ new Invoker<AbstractDOMDataTreeChangeListenerRegistration<?>, DataTreeCandidate>() {
+ @Override
+ public void invokeListener(final AbstractDOMDataTreeChangeListenerRegistration<?> listener, final DataTreeCandidate notification) {
+ // FIXME: this is inefficient, as we could grab the entire queue for the listener and post it
+ final DOMDataTreeChangeListener inst = listener.getInstance();
+ if (inst != null) {
+ inst.onDataTreeChanged(Collections.singletonList(notification));
+ }
+ }
+ };
+ private static final Logger LOG = LoggerFactory.getLogger(InMemoryDOMStoreTreeChangePublisher.class);
+ private final QueuedNotificationManager<AbstractDOMDataTreeChangeListenerRegistration<?>, DataTreeCandidate> notificationManager;
+
+ InMemoryDOMStoreTreeChangePublisher(final ExecutorService listenerExecutor, final int maxQueueSize) {
+ notificationManager = new QueuedNotificationManager<>(listenerExecutor, MANAGER_INVOKER, maxQueueSize, "DataTreeChangeListenerQueueMgr");
+ }
+
+ @Override
+ protected void notifyListeners(final Collection<AbstractDOMDataTreeChangeListenerRegistration<?>> registrations, final YangInstanceIdentifier path, final DataTreeCandidateNode node) {
+ final DataTreeCandidate candidate = DataTreeCandidates.newDataTreeCandidate(path, node);
+
+ for (AbstractDOMDataTreeChangeListenerRegistration<?> reg : registrations) {
+ LOG.debug("Enqueueing candidate {} to registration {}", candidate, registrations);
+ notificationManager.submitNotification(reg, candidate);
+ }
+ }
+
+ @Override
+ protected synchronized void registrationRemoved(final AbstractDOMDataTreeChangeListenerRegistration<?> registration) {
+ LOG.debug("Closing registration {}", registration);
+
+ // FIXME: remove the queue for this registration and make sure we clear it
+ }
+
+ <L extends DOMDataTreeChangeListener> ListenerRegistration<L> registerTreeChangeListener(final YangInstanceIdentifier treeId, final L listener, final DataTreeSnapshot snapshot) {
+ final AbstractDOMDataTreeChangeListenerRegistration<L> reg = registerTreeChangeListener(treeId, listener);
+
+ final Optional<NormalizedNode<?, ?>> node = snapshot.readNode(treeId);
+ if (node.isPresent()) {
+ final DataTreeCandidate candidate = DataTreeCandidates.fromNormalizedNode(treeId, node.get());
+ notificationManager.submitNotification(reg, candidate);
+ }
+
+ return reg;
+ }
+
+ synchronized void publishChange(@Nonnull final DataTreeCandidate candidate) {
+ // Runs synchronized with registrationRemoved()
+ processCandidateTree(candidate);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Multimap;
+import java.util.Collection;
+import java.util.Map.Entry;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.dom.spi.RegistrationTreeSnapshot;
+import org.opendaylight.controller.md.sal.dom.store.impl.DOMImmutableDataChangeEvent.Builder;
+import org.opendaylight.controller.md.sal.dom.store.impl.DOMImmutableDataChangeEvent.SimpleEventFactory;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerTree;
+import org.opendaylight.yangtools.util.concurrent.NotificationManager;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Resolve Data Change Events based on modifications and listeners
+ *
+ * Computes data change events for all affected registered listeners in data
+ * tree.
+ */
+@Beta
+public final class ResolveDataChangeEventsTask {
+ private static final Logger LOG = LoggerFactory.getLogger(ResolveDataChangeEventsTask.class);
+
+ private final DataTreeCandidate candidate;
+ private final ListenerTree listenerRoot;
+
+ private Multimap<DataChangeListenerRegistration<?>, DOMImmutableDataChangeEvent> collectedEvents;
+
+ private ResolveDataChangeEventsTask(final DataTreeCandidate candidate, final ListenerTree listenerTree) {
+ this.candidate = Preconditions.checkNotNull(candidate);
+ this.listenerRoot = Preconditions.checkNotNull(listenerTree);
+ }
+
+ /**
+ * Resolves and submits notification tasks to the specified manager.
+ */
+ public synchronized void resolve(final NotificationManager<DataChangeListenerRegistration<?>, DOMImmutableDataChangeEvent> manager) {
+ try (final RegistrationTreeSnapshot<DataChangeListenerRegistration<?>> w = listenerRoot.takeSnapshot()) {
+ // Defensive: reset internal state
+ collectedEvents = ArrayListMultimap.create();
+
+ // Run through the tree
+ final ResolveDataChangeState s = ResolveDataChangeState.initial(candidate.getRootPath(), w.getRootNode());
+ resolveAnyChangeEvent(s, candidate.getRootNode());
+
+ /*
+ * Convert to tasks, but be mindful of multiple values -- those indicate multiple
+ * wildcard matches, which need to be merged.
+ */
+ for (Entry<DataChangeListenerRegistration<?>, Collection<DOMImmutableDataChangeEvent>> e : collectedEvents.asMap().entrySet()) {
+ final Collection<DOMImmutableDataChangeEvent> col = e.getValue();
+ final DOMImmutableDataChangeEvent event;
+
+ if (col.size() != 1) {
+ final Builder b = DOMImmutableDataChangeEvent.builder(DataChangeScope.BASE);
+ for (DOMImmutableDataChangeEvent i : col) {
+ b.merge(i);
+ }
+
+ event = b.build();
+ LOG.trace("Merged events {} into event {}", col, event);
+ } else {
+ event = col.iterator().next();
+ }
+
+ manager.submitNotification(e.getKey(), event);
+ }
+ }
+ }
+
+ /**
+ * Resolves data change event for supplied node
+ *
+ * @param path
+ * Path to current node in tree
+ * @param listeners
+ * Collection of Listener registration nodes interested in
+ * subtree
+ * @param modification
+ * Modification of current node
+ * @param before
+ * - Original (before) state of current node
+ * @param after
+ * - After state of current node
+ * @return True if the subtree changed, false otherwise
+ */
+ private boolean resolveAnyChangeEvent(final ResolveDataChangeState state, final DataTreeCandidateNode node) {
+ final Optional<NormalizedNode<?, ?>> maybeBefore = node.getDataBefore();
+ final Optional<NormalizedNode<?, ?>> maybeAfter = node.getDataAfter();
+ final ModificationType type = node.getModificationType();
+
+ if (type != ModificationType.UNMODIFIED && !maybeAfter.isPresent() && !maybeBefore.isPresent()) {
+ LOG.debug("Modification at {} has type {}, but no before- and after-data. Assuming unchanged.",
+ state.getPath(), type);
+ return false;
+ }
+
+ // no before and after state is present
+
+ switch (type) {
+ case SUBTREE_MODIFIED:
+ return resolveSubtreeChangeEvent(state, node);
+ case MERGE:
+ case WRITE:
+ Preconditions.checkArgument(maybeAfter.isPresent(),
+ "Modification at {} has type {} but no after-data", state.getPath(), type);
+ if (!maybeBefore.isPresent()) {
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ final NormalizedNode<PathArgument, ?> afterNode = (NormalizedNode)maybeAfter.get();
+ resolveSameEventRecursivelly(state, afterNode, DOMImmutableDataChangeEvent.getCreateEventFactory());
+ return true;
+ }
+
+ return resolveReplacedEvent(state, maybeBefore.get(), maybeAfter.get());
+ case DELETE:
+ Preconditions.checkArgument(maybeBefore.isPresent(),
+ "Modification at {} has type {} but no before-data", state.getPath(), type);
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ final NormalizedNode<PathArgument, ?> beforeNode = (NormalizedNode)maybeBefore.get();
+ resolveSameEventRecursivelly(state, beforeNode, DOMImmutableDataChangeEvent.getRemoveEventFactory());
+ return true;
+ case UNMODIFIED:
+ return false;
+ }
+
+ throw new IllegalStateException(String.format("Unhandled node state %s at %s", type, state.getPath()));
+ }
+
+ private boolean resolveReplacedEvent(final ResolveDataChangeState state,
+ final NormalizedNode<?, ?> beforeData, final NormalizedNode<?, ?> afterData) {
+
+ if (beforeData instanceof NormalizedNodeContainer<?, ?, ?>) {
+ /*
+ * Node is a container (contains a child) and we have interested
+ * listeners registered for it, that means we need to do
+ * resolution of changes on children level and can not
+ * shortcut resolution.
+ */
+ LOG.trace("Resolving subtree replace event for {} before {}, after {}", state.getPath(), beforeData, afterData);
+ @SuppressWarnings("unchecked")
+ NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>> beforeCont = (NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>>) beforeData;
+ @SuppressWarnings("unchecked")
+ NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>> afterCont = (NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>>) afterData;
+ return resolveNodeContainerReplaced(state, beforeCont, afterCont);
+ }
+
+ // Node is a Leaf type (does not contain child nodes)
+ // so normal equals method is sufficient for determining change.
+ if (beforeData.equals(afterData)) {
+ LOG.trace("Skipping equal leaf {}", state.getPath());
+ return false;
+ }
+
+ LOG.trace("Resolving leaf replace event for {} , before {}, after {}", state.getPath(), beforeData, afterData);
+ DOMImmutableDataChangeEvent event = DOMImmutableDataChangeEvent.builder(DataChangeScope.BASE).addUpdated(state.getPath(), beforeData, afterData).build();
+ state.addEvent(event);
+ state.collectEvents(beforeData, afterData, collectedEvents);
+ return true;
+ }
+
+ private boolean resolveNodeContainerReplaced(final ResolveDataChangeState state,
+ final NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>> beforeCont,
+ final NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>> afterCont) {
+ if (!state.needsProcessing()) {
+ LOG.trace("Not processing replaced container {}", state.getPath());
+ return true;
+ }
+
+ // We look at all children from before and compare it with after state.
+ boolean childChanged = false;
+ for (NormalizedNode<PathArgument, ?> beforeChild : beforeCont.getValue()) {
+ final PathArgument childId = beforeChild.getIdentifier();
+
+ if (resolveNodeContainerChildUpdated(state.child(childId), beforeChild, afterCont.getChild(childId))) {
+ childChanged = true;
+ }
+ }
+
+ for (NormalizedNode<PathArgument, ?> afterChild : afterCont.getValue()) {
+ final PathArgument childId = afterChild.getIdentifier();
+
+ /*
+ * We have already iterated of the before-children, so have already
+ * emitted modify/delete events. This means the child has been
+ * created.
+ */
+ if (!beforeCont.getChild(childId).isPresent()) {
+ resolveSameEventRecursivelly(state.child(childId), afterChild, DOMImmutableDataChangeEvent.getCreateEventFactory());
+ childChanged = true;
+ }
+ }
+
+ if (childChanged) {
+ DOMImmutableDataChangeEvent event = DOMImmutableDataChangeEvent.builder(DataChangeScope.BASE)
+ .addUpdated(state.getPath(), beforeCont, afterCont).build();
+ state.addEvent(event);
+ }
+
+ state.collectEvents(beforeCont, afterCont, collectedEvents);
+ return childChanged;
+ }
+
+ private boolean resolveNodeContainerChildUpdated(final ResolveDataChangeState state,
+ final NormalizedNode<PathArgument, ?> before, final Optional<NormalizedNode<PathArgument, ?>> after) {
+ if (after.isPresent()) {
+ // REPLACE or SUBTREE Modified
+ return resolveReplacedEvent(state, before, after.get());
+ }
+
+ // AFTER state is not present - child was deleted.
+ resolveSameEventRecursivelly(state, before, DOMImmutableDataChangeEvent.getRemoveEventFactory());
+ return true;
+ }
+
+ private void resolveSameEventRecursivelly(final ResolveDataChangeState state,
+ final NormalizedNode<PathArgument, ?> node, final SimpleEventFactory eventFactory) {
+ if (!state.needsProcessing()) {
+ LOG.trace("Skipping child {}", state.getPath());
+ return;
+ }
+
+ // We have listeners for this node or it's children, so we will try
+ // to do additional processing
+ if (node instanceof NormalizedNodeContainer<?, ?, ?>) {
+ LOG.trace("Resolving subtree recursive event for {}, type {}", state.getPath(), eventFactory);
+
+ // Node has children, so we will try to resolve it's children
+ // changes.
+ @SuppressWarnings("unchecked")
+ NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>> container = (NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>>) node;
+ for (NormalizedNode<PathArgument, ?> child : container.getValue()) {
+ final PathArgument childId = child.getIdentifier();
+
+ LOG.trace("Resolving event for child {}", childId);
+ resolveSameEventRecursivelly(state.child(childId), child, eventFactory);
+ }
+ }
+
+ final DOMImmutableDataChangeEvent event = eventFactory.create(state.getPath(), node);
+ LOG.trace("Adding event {} at path {}", event, state.getPath());
+ state.addEvent(event);
+ state.collectEvents(event.getOriginalSubtree(), event.getUpdatedSubtree(), collectedEvents);
+ }
+
+ private boolean resolveSubtreeChangeEvent(final ResolveDataChangeState state, final DataTreeCandidateNode modification) {
+ final Optional<NormalizedNode<?, ?>> maybeBefore = modification.getDataBefore();
+ final Optional<NormalizedNode<?, ?>> maybeAfter = modification.getDataAfter();
+
+ Preconditions.checkArgument(maybeBefore.isPresent(), "Subtree change with before-data not present at path %s", state.getPath());
+ Preconditions.checkArgument(maybeAfter.isPresent(), "Subtree change with after-data not present at path %s", state.getPath());
+
+ if (!state.needsProcessing()) {
+ LOG.trace("Not processing modified subtree {}", state.getPath());
+ return true;
+ }
+
+ DataChangeScope scope = null;
+ for (DataTreeCandidateNode childMod : modification.getChildNodes()) {
+ final ResolveDataChangeState childState = state.child(childMod.getIdentifier());
+
+ switch (childMod.getModificationType()) {
+ case WRITE:
+ case MERGE:
+ case DELETE:
+ if (resolveAnyChangeEvent(childState, childMod)) {
+ scope = DataChangeScope.ONE;
+ }
+ break;
+ case SUBTREE_MODIFIED:
+ if (resolveSubtreeChangeEvent(childState, childMod) && scope == null) {
+ scope = DataChangeScope.SUBTREE;
+ }
+ break;
+ case UNMODIFIED:
+ // no-op
+ break;
+ }
+ }
+
+ final NormalizedNode<?, ?> before = maybeBefore.get();
+ final NormalizedNode<?, ?> after = maybeAfter.get();
+
+ if (scope != null) {
+ DOMImmutableDataChangeEvent one = DOMImmutableDataChangeEvent.builder(scope).addUpdated(state.getPath(), before, after).build();
+ state.addEvent(one);
+ }
+
+ state.collectEvents(before, after, collectedEvents);
+ return scope != null;
+ }
+
+ public static ResolveDataChangeEventsTask create(final DataTreeCandidate candidate, final ListenerTree listenerTree) {
+ return new ResolveDataChangeEventsTask(candidate, listenerTree);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.dom.spi.RegistrationTreeNode;
+import org.opendaylight.controller.md.sal.dom.store.impl.DOMImmutableDataChangeEvent.Builder;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Recursion state used in {@link ResolveDataChangeEventsTask}. Instances of this
+ * method track which listeners are affected by a particular change node. It takes
+ * care of properly inheriting SUB/ONE listeners and also provides a means to
+ * understand when actual processing need not occur.
+ */
+final class ResolveDataChangeState {
+ private static final Logger LOG = LoggerFactory.getLogger(ResolveDataChangeState.class);
+ /**
+ * Inherited from all parents
+ */
+ private final Iterable<Builder> inheritedSub;
+ /**
+ * Inherited from immediate parent
+ */
+ private final Collection<Builder> inheritedOne;
+ private final YangInstanceIdentifier nodeId;
+ private final Collection<RegistrationTreeNode<DataChangeListenerRegistration<?>>> nodes;
+
+ private final Map<DataChangeListenerRegistration<?>, Builder> subBuilders;
+ private final Map<DataChangeListenerRegistration<?>, Builder> oneBuilders;
+ private final Map<DataChangeListenerRegistration<?>, Builder> baseBuilders;
+
+ private ResolveDataChangeState(final YangInstanceIdentifier nodeId,
+ final Iterable<Builder> inheritedSub, final Collection<Builder> inheritedOne,
+ final Collection<RegistrationTreeNode<DataChangeListenerRegistration<?>>> nodes) {
+ this.nodeId = Preconditions.checkNotNull(nodeId);
+ this.nodes = Preconditions.checkNotNull(nodes);
+ this.inheritedSub = Preconditions.checkNotNull(inheritedSub);
+ this.inheritedOne = Preconditions.checkNotNull(inheritedOne);
+
+ /*
+ * Collect the nodes which need to be propagated from us to the child.
+ */
+ final Map<DataChangeListenerRegistration<?>, Builder> sub = new HashMap<>();
+ final Map<DataChangeListenerRegistration<?>, Builder> one = new HashMap<>();
+ final Map<DataChangeListenerRegistration<?>, Builder> base = new HashMap<>();
+ for (RegistrationTreeNode<DataChangeListenerRegistration<?>> n : nodes) {
+ for (DataChangeListenerRegistration<?> l : n.getRegistrations()) {
+ final Builder b = DOMImmutableDataChangeEvent.builder(DataChangeScope.BASE);
+ switch (l.getScope()) {
+ case BASE:
+ base.put(l, b);
+ break;
+ case ONE:
+ one.put(l, b);
+ break;
+ case SUBTREE:
+ sub.put(l, b);
+ break;
+ }
+ }
+ }
+
+ baseBuilders = maybeEmpty(base);
+ oneBuilders = maybeEmpty(one);
+ subBuilders = maybeEmpty(sub);
+ }
+
+ private static <K, V> Map<K, V> maybeEmpty(final Map<K, V> map) {
+ if (map.isEmpty()) {
+ return Collections.emptyMap();
+ }
+ return map;
+ }
+
+ /**
+ * Create an initial state handle at a particular root node.
+ *
+ * @param rootId root instance identifier
+ * @param registrationTreeNode root node
+ * @return
+ */
+ public static ResolveDataChangeState initial(final YangInstanceIdentifier rootId, final RegistrationTreeNode<DataChangeListenerRegistration<?>> registrationTreeNode) {
+ return new ResolveDataChangeState(rootId, Collections.<Builder>emptyList(),
+ Collections.<Builder>emptyList(), Collections.singletonList(registrationTreeNode));
+ }
+
+ /**
+ * Create a state handle for iterating over a particular child.
+ *
+ * @param childId ID of the child
+ * @return State handle
+ */
+ public ResolveDataChangeState child(final PathArgument childId) {
+ /*
+ * We instantiate a concatenation only when needed:
+ *
+ * 1) If our collection is empty, we reuse the parent's. This is typically the case
+ * for intermediate node, which should be the vast majority.
+ * 2) If the parent's iterable is a Collection and it is empty, reuse our collection.
+ * This is the case for the first node which defines a subtree listener in a
+ * particular subtree.
+ * 3) Concatenate the two collections. This happens when we already have some
+ * subtree listeners and we encounter a node which adds a few more.
+ *
+ * This allows us to lower number of objects allocated and also
+ * speeds up Iterables.isEmpty() in needsProcessing().
+ *
+ * Note that the check for Collection in 2) relies on precisely this logic, which
+ * ensures that we simply cannot see an empty concatenation, but rather start off with
+ * an empty collection, then switch to a non-empty collection and finally switch to
+ * a concatenation. This saves us from instantiating iterators, which a trivial
+ * Iterables.isEmpty() would do as soon as we cross case 3).
+ */
+ final Iterable<Builder> sb;
+ if (!subBuilders.isEmpty()) {
+ if (inheritedSub instanceof Collection && ((Collection<?>) inheritedSub).isEmpty()) {
+ sb = subBuilders.values();
+ } else {
+ sb = Iterables.concat(inheritedSub, subBuilders.values());
+ }
+ } else {
+ sb = inheritedSub;
+ }
+
+ return new ResolveDataChangeState(nodeId.node(childId), sb,
+ oneBuilders.values(), getListenerChildrenWildcarded(nodes, childId));
+ }
+
+ /**
+ * Get the current path
+ *
+ * @return Current path.
+ */
+ public YangInstanceIdentifier getPath() {
+ return nodeId;
+ }
+
+ /**
+ * Check if this child needs processing.
+ *
+ * @return True if processing needs to occur, false otherwise.
+ */
+ public boolean needsProcessing() {
+ // May have underlying listeners, so we need to process
+ if (!nodes.isEmpty()) {
+ return true;
+ }
+ // Have ONE listeners
+ if (!inheritedOne.isEmpty()) {
+ return true;
+ }
+
+ /*
+ * Have SUBTREE listeners
+ *
+ * This is slightly magical replacement for !Iterables.isEmpty(inheritedSub).
+ * It relies on the logic in child(), which gives us the guarantee that when
+ * inheritedSub is not a Collection, it is guaranteed to be non-empty (which
+ * means we need to process). If it is a collection, we still need to check
+ * it for emptiness.
+ *
+ * Unlike Iterables.isEmpty() this code does not instantiate any temporary
+ * objects and is thus more efficient.
+ */
+ if (inheritedSub instanceof Collection) {
+ return !((Collection<?>) inheritedSub).isEmpty();
+ }
+
+ // Non-Collection => non-empty => have to process
+ return true;
+ }
+
+ /**
+ * Add an event to all current listeners.
+ *
+ * @param event
+ */
+ public void addEvent(final DOMImmutableDataChangeEvent event) {
+ // Subtree builders get always notified
+ for (Builder b : subBuilders.values()) {
+ b.merge(event);
+ }
+ for (Builder b : inheritedSub) {
+ b.merge(event);
+ }
+
+ if (event.getScope() == DataChangeScope.ONE || event.getScope() == DataChangeScope.BASE) {
+ for (Builder b : oneBuilders.values()) {
+ b.merge(event);
+ }
+ }
+
+ if (event.getScope() == DataChangeScope.BASE) {
+ for (Builder b : inheritedOne) {
+ b.merge(event);
+ }
+ for (Builder b : baseBuilders.values()) {
+ b.merge(event);
+ }
+ }
+ }
+
+ /**
+ * Gather all non-empty events into the provided map.
+ *
+ * @param before before-image
+ * @param after after-image
+ * @param map target map
+ */
+ public void collectEvents(final NormalizedNode<?, ?> before, final NormalizedNode<?, ?> after,
+ final Multimap<DataChangeListenerRegistration<?>, DOMImmutableDataChangeEvent> map) {
+ for (Entry<DataChangeListenerRegistration<?>, Builder> e : baseBuilders.entrySet()) {
+ final Builder b = e.getValue();
+ if (!b.isEmpty()) {
+ map.put(e.getKey(), b.setBefore(before).setAfter(after).build());
+ }
+ }
+ for (Entry<DataChangeListenerRegistration<?>, Builder> e : oneBuilders.entrySet()) {
+ final Builder b = e.getValue();
+ if (!b.isEmpty()) {
+ map.put(e.getKey(), b.setBefore(before).setAfter(after).build());
+ }
+ }
+ for (Entry<DataChangeListenerRegistration<?>, Builder> e : subBuilders.entrySet()) {
+ final Builder b = e.getValue();
+ if (!b.isEmpty()) {
+ map.put(e.getKey(), b.setBefore(before).setAfter(after).build());
+ }
+ }
+
+ LOG.trace("Collected events {}", map);
+ }
+
+ private static Collection<RegistrationTreeNode<DataChangeListenerRegistration<?>>> getListenerChildrenWildcarded(final Collection<RegistrationTreeNode<DataChangeListenerRegistration<?>>> parentNodes,
+ final PathArgument child) {
+ if (parentNodes.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ final List<RegistrationTreeNode<DataChangeListenerRegistration<?>>> result = new ArrayList<>();
+ if (child instanceof NodeWithValue || child instanceof NodeIdentifierWithPredicates) {
+ NodeIdentifier wildcardedIdentifier = new NodeIdentifier(child.getNodeType());
+ addChildNodes(result, parentNodes, wildcardedIdentifier);
+ }
+ addChildNodes(result, parentNodes, child);
+ return result;
+ }
+
+ private static void addChildNodes(final List<RegistrationTreeNode<DataChangeListenerRegistration<?>>> result, final Collection<RegistrationTreeNode<DataChangeListenerRegistration<?>>> parentNodes, final PathArgument childIdentifier) {
+ for (RegistrationTreeNode<DataChangeListenerRegistration<?>> node : parentNodes) {
+ RegistrationTreeNode<DataChangeListenerRegistration<?>> child = node.getExactChild(childIdentifier);
+ if (child != null) {
+ result.add(child);
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.store.impl.jmx;
+
+import org.opendaylight.controller.md.sal.common.util.jmx.AbstractMXBean;
+import org.opendaylight.controller.md.sal.common.util.jmx.QueuedNotificationManagerMXBeanImpl;
+import org.opendaylight.controller.md.sal.common.util.jmx.ThreadExecutorStatsMXBeanImpl;
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
+import org.opendaylight.yangtools.util.concurrent.QueuedNotificationManager;
+
+/**
+ * Wrapper class for data store MXbeans.
+ *
+ * @author Thomas Pantelis
+ */
+public class InMemoryDataStoreStats implements AutoCloseable {
+
+ private final AbstractMXBean notificationExecutorStatsBean;
+ private final QueuedNotificationManagerMXBeanImpl notificationManagerStatsBean;
+
+ public InMemoryDataStoreStats(final String mBeanType, final QueuedNotificationManager<?, ?> manager) {
+
+ notificationManagerStatsBean = new QueuedNotificationManagerMXBeanImpl(manager,
+ "notification-manager", mBeanType, null);
+ notificationManagerStatsBean.registerMBean();
+
+ notificationExecutorStatsBean = ThreadExecutorStatsMXBeanImpl.create(manager.getExecutor(),
+ "notification-executor", mBeanType, null);
+ if (notificationExecutorStatsBean != null) {
+ notificationExecutorStatsBean.registerMBean();
+ }
+ }
+
+ public InMemoryDataStoreStats(final String name, final InMemoryDOMDataStore dataStore) {
+ this(name, dataStore.getDataChangeListenerNotificationManager());
+ }
+
+ @Override
+ public void close() throws Exception {
+ if(notificationExecutorStatsBean != null) {
+ notificationExecutorStatsBean.unregisterMBean();
+ }
+
+ if(notificationManagerStatsBean != null) {
+ notificationManagerStatsBean.unregisterMBean();
+ }
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl.tree;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.controller.md.sal.dom.store.impl.DataChangeListenerRegistration;
+import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+abstract class DataChangeListenerRegistrationImpl<T extends AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>>> extends AbstractListenerRegistration<T> implements DataChangeListenerRegistration<T> {
+ public DataChangeListenerRegistrationImpl(final T listener) {
+ super(listener);
+ }
+}
\ No newline at end of file
--- /dev/null
+/**
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl.tree;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import java.util.Collection;
+import org.opendaylight.controller.md.sal.dom.spi.RegistrationTreeNode;
+import org.opendaylight.controller.md.sal.dom.store.impl.DataChangeListenerRegistration;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.StoreTreeNode;
+
+/**
+ * This is a single node within the listener tree. Note that the data returned from
+ * and instance of this class is guaranteed to have any relevance or consistency
+ * only as long as the {@link ListenerWalker} instance through which it is reached remains
+ * unclosed.
+ *
+ * @author Robert Varga
+ *
+ * @deprecated Use {@link RegistrationTreeNode} instead.
+ */
+@Deprecated
+public class ListenerNode implements StoreTreeNode<ListenerNode>, Identifiable<PathArgument> {
+ final RegistrationTreeNode<DataChangeListenerRegistration<?>> delegate;
+
+ ListenerNode(final RegistrationTreeNode<DataChangeListenerRegistration<?>> delegate) {
+ this.delegate = Preconditions.checkNotNull(delegate);
+ }
+
+ @Override
+ public PathArgument getIdentifier() {
+ return delegate.getIdentifier();
+ }
+
+ @Override
+ public Optional<ListenerNode> getChild(final PathArgument child) {
+ final RegistrationTreeNode<DataChangeListenerRegistration<?>> c = delegate.getExactChild(child);
+ if (c == null) {
+ return Optional.absent();
+ }
+
+ return Optional.of(new ListenerNode(c));
+ }
+
+ /**
+ * Return the list of current listeners. This collection is guaranteed
+ * to be immutable only while the walker, through which this node is
+ * reachable remains unclosed.
+ *
+ * @return the list of current listeners
+ */
+ public Collection<DataChangeListenerRegistration<?>> getListeners() {
+ return delegate.getRegistrations();
+ }
+
+ @Override
+ public int hashCode() {
+ return delegate.hashCode();
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ return delegate.equals(obj);
+ }
+
+ @Override
+ public String toString() {
+ return delegate.toString();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl.tree;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.controller.md.sal.dom.spi.AbstractRegistrationTree;
+import org.opendaylight.controller.md.sal.dom.spi.RegistrationTreeNode;
+import org.opendaylight.controller.md.sal.dom.store.impl.DataChangeListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * A set of listeners organized as a tree by node to which they listen. This class
+ * allows for efficient lookup of listeners when we walk the DataTreeCandidate.
+ *
+ * @author Robert Varga
+ */
+public final class ListenerTree extends AbstractRegistrationTree<DataChangeListenerRegistration<?>> {
+ private ListenerTree() {
+ // Private to disallow direct instantiation
+ }
+
+ /**
+ * Create a new empty instance of the listener tree.
+ *
+ * @return An empty instance.
+ */
+ public static ListenerTree create() {
+ return new ListenerTree();
+ }
+
+ /**
+ * Registers listener on this node.
+ *
+ * @param path Full path on which listener is registered.
+ * @param listener Listener
+ * @param scope Scope of triggering event.
+ * @return Listener registration
+ */
+ public <L extends AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>>> DataChangeListenerRegistration<L> registerDataChangeListener(final YangInstanceIdentifier path,
+ final L listener, final DataChangeScope scope) {
+
+ // Take the write lock
+ takeLock();
+ try {
+ final RegistrationTreeNode<DataChangeListenerRegistration<?>> node = findNodeFor(path.getPathArguments());
+ DataChangeListenerRegistration<L> reg = new DataChangeListenerRegistrationImpl<L>(listener) {
+ @Override
+ public DataChangeScope getScope() {
+ return scope;
+ }
+
+ @Override
+ public YangInstanceIdentifier getPath() {
+ return path;
+ }
+
+ @Override
+ protected void removeRegistration() {
+ /*
+ * TODO: Here's an interesting problem. The way the datastore works, it
+ * enqueues requests towards the listener, so the listener will be
+ * notified at some point in the future. Now if the registration is
+ * closed, we will prevent any new events from being delivered, but
+ * we have no way to purge that queue.
+ *
+ * While this does not directly violate the ListenerRegistration
+ * contract, it is probably not going to be liked by the users.
+ */
+ ListenerTree.this.removeRegistration(node, this);
+ }
+ };
+
+ addRegistration(node, reg);
+ return reg;
+ } finally {
+ // Always release the lock
+ releaseLock();
+ }
+ }
+
+ /**
+ * Obtain a tree walking context. This context ensures a consistent view of
+ * the listener registrations. The context should be closed as soon as it
+ * is not required, because each unclosed instance blocks modification of
+ * the listener tree.
+ *
+ * @return A walker instance.
+ *
+ * @deprecated Use {@link #takeSnapshot()} instead.
+ */
+ @Deprecated
+ public ListenerWalker getWalker() {
+ /*
+ * TODO: The only current user of this method is local to the datastore.
+ * Since this class represents a read-lock, losing a reference to
+ * it is a _major_ problem, as the registration process will get
+ * wedged, eventually grinding the system to a halt. Should an
+ * external user exist, make the Walker a phantom reference, which
+ * will cleanup the lock if not told to do so.
+ */
+ return new ListenerWalker(takeSnapshot());
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl.tree;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.md.sal.dom.spi.RegistrationTreeSnapshot;
+import org.opendaylight.controller.md.sal.dom.store.impl.DataChangeListenerRegistration;
+
+/**
+ * A walking context, pretty much equivalent to an iterator, but it
+ * exposes the underlying tree structure.
+ *
+ * @author Robert Varga
+ *
+ * @deprecated Superseded by {@link RegistrationTreeSnapshot}.
+ */
+@Deprecated
+public class ListenerWalker implements AutoCloseable {
+ private final RegistrationTreeSnapshot<DataChangeListenerRegistration<?>> delegate;
+
+ ListenerWalker(final RegistrationTreeSnapshot<DataChangeListenerRegistration<?>> delegate) {
+ this.delegate = Preconditions.checkNotNull(delegate);
+ }
+
+ public ListenerNode getRootNode() {
+ return new ListenerNode(delegate.getRootNode());
+ }
+
+ @Override
+ public void close() {
+ delegate.close();
+ }
+}
\ No newline at end of file
--- /dev/null
+module opendaylight-inmemory-datastore-provider {
+
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:inmemory-datastore-provider";
+ prefix "inmemory-datastore-provider";
+
+ import config { prefix config; revision-date 2013-04-05; }
+ import rpc-context { prefix rpcx; revision-date 2013-06-17; }
+ import opendaylight-config-dom-datastore {prefix config-dom-store-spi;}
+ import opendaylight-operational-dom-datastore {prefix operational-dom-store-spi;}
+ import opendaylight-md-sal-dom {prefix sal;}
+
+ description
+ "InMemory datastore provider implementation for config & operational datastore";
+
+ revision "2014-06-17" {
+ description
+ "Initial revision.";
+ }
+
+ // This is the definition of the service implementation as a module identity.
+ identity inmemory-config-datastore-provider {
+ base config:module-type;
+ config:provided-service config-dom-store-spi:config-dom-datastore;
+ config:java-name-prefix InMemoryConfigDataStoreProvider;
+ }
+
+ // This is the definition of the service implementation as a module identity.
+
+ identity inmemory-operational-datastore-provider {
+ base config:module-type;
+ config:provided-service operational-dom-store-spi:operational-dom-datastore;
+ config:java-name-prefix InMemoryOperationalDataStoreProvider;
+ }
+
+ grouping datastore-configuration {
+ leaf max-data-change-executor-queue-size {
+ default 1000;
+ type uint16;
+ description "The maximum queue size for the data change notification executor.";
+ }
+
+ leaf max-data-change-executor-pool-size {
+ default 20;
+ type uint16;
+ description "The maximum thread pool size for the data change notification executor.";
+ }
+
+ leaf max-data-change-listener-queue-size {
+ default 1000;
+ type uint16;
+ description "The maximum queue size for the data change listeners.";
+ }
+ leaf max-data-store-executor-queue-size {
+ default 5000;
+ type uint16;
+ description "The maximum queue size for the data store executor.";
+ }
+ leaf debug-transactions {
+ type boolean;
+ default false;
+ description "Enable transaction lifecycle debugging.";
+ }
+ }
+
+ // Augments the 'configuration' choice node under modules/module.
+ augment "/config:modules/config:module/config:configuration" {
+ case inmemory-config-datastore-provider {
+ when "/config:modules/config:module/config:type = 'inmemory-config-datastore-provider'";
+
+ container inmemory-config-datastore-provider {
+ container schema-service {
+ uses config:service-ref {
+ refine type {
+ mandatory false;
+ config:required-identity sal:schema-service;
+ }
+ }
+ }
+ uses datastore-configuration;
+ }
+ }
+ }
+
+ // Augments the 'configuration' choice node under modules/module.
+ augment "/config:modules/config:module/config:configuration" {
+ case inmemory-operational-datastore-provider {
+ when "/config:modules/config:module/config:type = 'inmemory-operational-datastore-provider'";
+
+ container inmemory-operational-datastore-provider {
+ container schema-service {
+ uses config:service-ref {
+ refine type {
+ mandatory false;
+ config:required-identity sal:schema-service;
+ }
+ }
+ }
+ uses datastore-configuration;
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import java.util.Collection;
+import java.util.Map;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.opendaylight.controller.md.sal.dom.store.impl.DatastoreTestTask.WriteTransactionCustomizer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.TwoLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.top.level.list.NestedList;
+import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext;
+import org.opendaylight.yangtools.util.concurrent.SpecialExecutors;
+import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public abstract class AbstractDataChangeListenerTest {
+
+ protected static final YangInstanceIdentifier TOP_LEVEL = YangInstanceIdentifier
+ .of(Top.QNAME);
+ private static final QName NAME_QNAME = QName.create(Top.QNAME, "name");
+ protected static final String FOO = "foo";
+ protected static final String BAR = "bar";
+ protected static final String BAZ = "baz";
+
+ private InMemoryDOMDataStore datastore;
+ private SchemaContext schemaContext;
+ private TestDCLExecutorService dclExecutorService;
+
+ @Before
+ public final void setup() throws Exception {
+ YangModuleInfo moduleInfo = BindingReflections
+ .getModuleInfo(TwoLevelList.class);
+ ModuleInfoBackedContext context = ModuleInfoBackedContext.create();
+ context.registerModuleInfo(moduleInfo);
+ schemaContext = context.tryToCreateSchemaContext().get();
+
+ dclExecutorService = new TestDCLExecutorService(
+ SpecialExecutors.newBlockingBoundedFastThreadPool(1, 10, "DCL" ));
+
+ datastore = new InMemoryDOMDataStore("TEST", dclExecutorService);
+ datastore.onGlobalContextUpdated(schemaContext);
+ }
+
+ @After
+ public void tearDown() {
+ if( dclExecutorService != null ) {
+ dclExecutorService.shutdownNow();
+ }
+ }
+
+ /**
+ * Create a new test task. The task will operate on the backed database,
+ * and will use the proper background executor service.
+ *
+ * @return Test task initialized to clean up {@value #TOP_LEVEL} and its
+ * children.
+ */
+ public final DatastoreTestTask newTestTask() {
+ return new DatastoreTestTask(datastore, dclExecutorService).cleanup(DatastoreTestTask
+ .simpleDelete(TOP_LEVEL));
+ }
+
+
+ public static final YangInstanceIdentifier path(final String topName,
+ final String nestedName) {
+ return path(topName).node(NestedList.QNAME).node(
+ new NodeIdentifierWithPredicates(NestedList.QNAME, NAME_QNAME,
+ nestedName));
+ }
+
+ public static final YangInstanceIdentifier path(final String topName) {
+ return TOP_LEVEL.node(TopLevelList.QNAME).node(
+ new NodeIdentifierWithPredicates(TopLevelList.QNAME,
+ NAME_QNAME, topName));
+ }
+
+ protected static DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> top() {
+ return Builders.containerBuilder().withNodeIdentifier(
+ new NodeIdentifier(Top.QNAME));
+ }
+
+
+
+ protected static void assertEmpty(final Collection<?> set) {
+ Assert.assertTrue(set.isEmpty());
+ }
+
+ protected static void assertEmpty(final Map<?,?> set) {
+ Assert.assertTrue(set.isEmpty());
+ }
+
+ protected static <K> void assertContains(final Collection<K> set, final K... values) {
+ for (K key : values) {
+ Assert.assertTrue(set.contains(key));
+ }
+
+ }
+
+ protected static <K> void assertNotContains(final Collection<K> set, final K... values) {
+ for (K key : values) {
+ Assert.assertFalse(set.contains(key));
+ }
+ }
+
+ protected static <K> void assertContains(final Map<K,?> map, final K... values) {
+ for (K key : values) {
+ Assert.assertTrue(map.containsKey(key));
+ }
+ }
+
+ protected static <K> void assertNotContains(final Map<K,?> map, final K... values) {
+ for (K key : values) {
+ Assert.assertFalse(map.containsKey(key));
+ }
+ }
+
+ protected static CollectionNodeBuilder<MapEntryNode, MapNode> topLevelMap() {
+ return ImmutableNodes.mapNodeBuilder(TopLevelList.QNAME);
+ }
+
+ protected static CollectionNodeBuilder<MapEntryNode, OrderedMapNode> nestedMap() {
+ return Builders.orderedMapBuilder().withNodeIdentifier(new NodeIdentifier(NestedList.QNAME));
+ }
+
+ public static DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> topLevelList(
+ final String key) {
+ return ImmutableNodes.mapEntryBuilder(TopLevelList.QNAME, NAME_QNAME,
+ key);
+ }
+
+ public static DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> nestedList(
+ final String key) {
+ return ImmutableNodes
+ .mapEntryBuilder(NestedList.QNAME, NAME_QNAME, key);
+ }
+
+ public static final WriteTransactionCustomizer writeOneTopMultipleNested(
+ final String topName, final String... nestedName) {
+ CollectionNodeBuilder<MapEntryNode, OrderedMapNode> nestedMapBuilder = nestedMap();
+ for (String nestedItem : nestedName) {
+ nestedMapBuilder.addChild(nestedList(nestedItem).build());
+ }
+
+ final ContainerNode data = top().addChild(
+ topLevelMap().addChild(
+ topLevelList(topName)
+ .addChild(nestedMapBuilder.build()).build())
+ .build()).build();
+
+ return DatastoreTestTask.simpleWrite(TOP_LEVEL, data);
+ }
+
+ public static final WriteTransactionCustomizer deleteNested(final String topName,
+ final String nestedName) {
+ return DatastoreTestTask.simpleDelete(path(topName, nestedName));
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.controller.sal.core.spi.data.DOMStore;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.SettableFuture;
+
+public class DatastoreTestTask {
+
+ private final DOMStore store;
+ private AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>> changeListener;
+
+ private WriteTransactionCustomizer setup;
+ private WriteTransactionCustomizer write;
+ private ReadTransactionVerifier read;
+ private WriteTransactionCustomizer cleanup;
+ private YangInstanceIdentifier changePath;
+ private DataChangeScope changeScope;
+ private volatile boolean postSetup = false;
+ private final ChangeEventListener internalListener;
+ private final TestDCLExecutorService dclExecutorService;
+
+ public DatastoreTestTask(final DOMStore datastore, final TestDCLExecutorService dclExecutorService) {
+ this.store = datastore;
+ this.dclExecutorService = dclExecutorService;
+ internalListener = new ChangeEventListener();
+ }
+
+ public DatastoreTestTask changeListener(final YangInstanceIdentifier path, final DataChangeScope scope,
+ final AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>> changeListener) {
+ this.changeListener = changeListener;
+ this.changePath = path;
+ this.changeScope = scope;
+ return this;
+ }
+
+ public DatastoreTestTask changeListener(final YangInstanceIdentifier path, final DataChangeScope scope) {
+ this.changePath = path;
+ this.changeScope = scope;
+ return this;
+ }
+
+ public DatastoreTestTask setup(final WriteTransactionCustomizer setup) {
+ this.setup = setup;
+ return this;
+ }
+
+ public DatastoreTestTask test(final WriteTransactionCustomizer write) {
+ this.write = write;
+ return this;
+ }
+
+ public DatastoreTestTask read(final ReadTransactionVerifier read) {
+ this.read = read;
+ return this;
+ }
+
+ public DatastoreTestTask cleanup(final WriteTransactionCustomizer cleanup) {
+ this.cleanup = cleanup;
+ return this;
+ }
+
+ public void run() throws InterruptedException, ExecutionException, TimeoutException {
+ if (setup != null) {
+ execute(setup);
+ }
+ ListenerRegistration<ChangeEventListener> registration = null;
+ if (changePath != null) {
+ registration = store.registerChangeListener(changePath, internalListener, changeScope);
+ }
+
+ Preconditions.checkState(write != null, "Write Transaction must be set.");
+
+ postSetup = true;
+ dclExecutorService.afterTestSetup();
+
+ execute(write);
+ if (registration != null) {
+ registration.close();
+ }
+
+ if (changeListener != null) {
+ changeListener.onDataChanged(getChangeEvent());
+ }
+ if (read != null) {
+ read.verify(store.newReadOnlyTransaction());
+ }
+ if (cleanup != null) {
+ execute(cleanup);
+ }
+ }
+
+ public AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> getChangeEvent() {
+ try {
+ return internalListener.receivedChange.get(10, TimeUnit.SECONDS);
+ } catch( Exception e ) {
+ fail( "Error getting the AsyncDataChangeEvent from the Future: " + e );
+ }
+
+ // won't get here
+ return null;
+ }
+
+ public void verifyNoChangeEvent() {
+ try {
+ Object unexpected = internalListener.receivedChange.get(500, TimeUnit.MILLISECONDS);
+ fail( "Got unexpected AsyncDataChangeEvent from the Future: " + unexpected );
+ } catch( TimeoutException e ) {
+ // Expected
+ } catch( Exception e ) {
+ fail( "Error getting the AsyncDataChangeEvent from the Future: " + e );
+ }
+ }
+
+ private void execute(final WriteTransactionCustomizer writeCustomizer) throws InterruptedException,
+ ExecutionException {
+ DOMStoreReadWriteTransaction tx = store.newReadWriteTransaction();
+ writeCustomizer.customize(tx);
+ DOMStoreThreePhaseCommitCohort cohort = tx.ready();
+ assertTrue(cohort.canCommit().get());
+ cohort.preCommit().get();
+ cohort.commit().get();
+ }
+
+ public interface WriteTransactionCustomizer {
+ public void customize(DOMStoreReadWriteTransaction tx);
+ }
+
+ public interface ReadTransactionVerifier {
+ public void verify(DOMStoreReadTransaction tx);
+ }
+
+ private final class ChangeEventListener implements
+ AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>> {
+
+ protected final SettableFuture<AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>>> receivedChange = SettableFuture
+ .create();
+
+ @Override
+ public void onDataChanged(final AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change) {
+ if (postSetup) {
+ receivedChange.set(change);
+ }
+ }
+ }
+
+ public static final WriteTransactionCustomizer simpleWrite(final YangInstanceIdentifier path,
+ final NormalizedNode<?, ?> data) {
+ return new WriteTransactionCustomizer() {
+
+ @Override
+ public void customize(final DOMStoreReadWriteTransaction tx) {
+ tx.write(path, data);
+ }
+ };
+ }
+
+ public static final WriteTransactionCustomizer simpleMerge(final YangInstanceIdentifier path,
+ final NormalizedNode<?, ?> data) {
+ return new WriteTransactionCustomizer() {
+
+ @Override
+ public void customize(final DOMStoreReadWriteTransaction tx) {
+ tx.merge(path, data);
+ }
+ };
+ }
+
+ public static final WriteTransactionCustomizer simpleDelete(final YangInstanceIdentifier path) {
+ return new WriteTransactionCustomizer() {
+ @Override
+ public void customize(final DOMStoreReadWriteTransaction tx) {
+ tx.delete(path);
+ }
+ };
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import java.util.concurrent.ExecutionException;
+
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.dom.store.impl.DatastoreTestTask.WriteTransactionCustomizer;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
+
+/**
+ * Base template for a test suite for testing DataChangeListener functionality.
+ */
+public abstract class DefaultDataChangeListenerTestSuite extends AbstractDataChangeListenerTest {
+
+ protected static final String FOO_SIBLING = "foo-sibling";
+
+ /**
+ * Callback invoked when the test suite can modify task parameters.
+ *
+ * @param task Update task configuration as needed
+ */
+ abstract protected void customizeTask(DatastoreTestTask task);
+
+ @Test
+ public final void putTopLevelOneNested() throws Exception {
+
+ DatastoreTestTask task = newTestTask().test(writeOneTopMultipleNested(FOO, BAR));
+ customizeTask(task);
+ task.run();
+ putTopLevelOneNested(task);
+ }
+
+ @Test
+ public final void existingTopWriteSibling() throws Exception {
+ DatastoreTestTask task = newTestTask().setup(writeOneTopMultipleNested(FOO)).test(
+ new WriteTransactionCustomizer() {
+ @Override
+ public void customize(final DOMStoreReadWriteTransaction tx) {
+ tx.write(path(FOO_SIBLING), topLevelList(FOO_SIBLING).build());
+ }
+ });
+ customizeTask(task);
+ task.run();
+ existingTopWriteSibling(task);
+ }
+
+ protected abstract void existingTopWriteSibling(DatastoreTestTask task) throws InterruptedException, ExecutionException;
+
+
+ @Test
+ public final void existingTopWriteTwoNested() throws Exception {
+ DatastoreTestTask task = newTestTask().setup(writeOneTopMultipleNested(FOO)).test(
+ new WriteTransactionCustomizer() {
+ @Override
+ public void customize(final DOMStoreReadWriteTransaction tx) {
+ tx.write(path(FOO,BAR), nestedList(BAR).build());
+ tx.write(path(FOO,BAZ), nestedList(BAZ).build());
+ }
+ });
+ customizeTask(task);
+ task.run();
+ existingTopWriteTwoNested(task);
+ }
+
+ protected abstract void existingTopWriteTwoNested(DatastoreTestTask task) throws InterruptedException, ExecutionException;
+
+
+ @Test
+ public final void existingOneNestedWriteAdditionalNested() throws Exception {
+ DatastoreTestTask task = newTestTask().setup(writeOneTopMultipleNested(FOO, BAR)).test(
+ new WriteTransactionCustomizer() {
+ @Override
+ public void customize(final DOMStoreReadWriteTransaction tx) {
+ tx.write(path(FOO,BAZ), nestedList(BAZ).build());
+ }
+ });
+ customizeTask(task);
+ task.run();
+ existingOneNestedWriteAdditionalNested(task);
+ }
+
+ protected abstract void existingOneNestedWriteAdditionalNested(DatastoreTestTask task) throws InterruptedException, ExecutionException;
+
+ protected abstract void putTopLevelOneNested(DatastoreTestTask task) throws Exception;
+
+ @Test
+ public final void replaceTopLevelNestedChanged() throws Exception {
+ DatastoreTestTask task = newTestTask().setup(writeOneTopMultipleNested(FOO, BAR)).test(
+ writeOneTopMultipleNested(FOO, BAZ));
+ customizeTask(task);
+ task.run();
+ replaceTopLevelNestedChanged(task);
+ }
+
+ protected abstract void replaceTopLevelNestedChanged(DatastoreTestTask task) throws InterruptedException,
+ ExecutionException;
+
+ @Test
+ public final void putTopLevelWithTwoNested() throws Exception {
+
+ DatastoreTestTask task = newTestTask().test(writeOneTopMultipleNested(FOO, BAR, BAZ));
+ customizeTask(task);
+ task.run();
+ putTopLevelWithTwoNested(task);
+ }
+
+ protected abstract void putTopLevelWithTwoNested(DatastoreTestTask task) throws InterruptedException,
+ ExecutionException;
+
+ @Test
+ public final void twoNestedExistsOneIsDeleted() throws Exception {
+
+ DatastoreTestTask task = newTestTask().setup(writeOneTopMultipleNested(FOO, BAR, BAZ)).test(
+ deleteNested(FOO, BAZ));
+ customizeTask(task);
+ task.run();
+ twoNestedExistsOneIsDeleted(task);
+ }
+
+ protected abstract void twoNestedExistsOneIsDeleted(DatastoreTestTask task) throws InterruptedException,
+ ExecutionException;
+
+ @Test
+ public final void nestedListExistsRootDeleted() throws Exception {
+
+ DatastoreTestTask task = newTestTask().cleanup(null).setup(writeOneTopMultipleNested(FOO, BAR, BAZ))
+ .test(DatastoreTestTask.simpleDelete(TOP_LEVEL));
+ customizeTask(task);
+ task.run();
+ nestedListExistsRootDeleted(task);
+ }
+
+ protected abstract void nestedListExistsRootDeleted(DatastoreTestTask task) throws InterruptedException,
+ ExecutionException;
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.util.concurrent.ExecutionException;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedTransactions;
+import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class InMemoryDataStoreTest {
+
+ private SchemaContext schemaContext;
+ private InMemoryDOMDataStore domStore;
+
+ @Before
+ public void setupStore() {
+ domStore = new InMemoryDOMDataStore("TEST", MoreExecutors.newDirectExecutorService());
+ schemaContext = TestModel.createTestContext();
+ domStore.onGlobalContextUpdated(schemaContext);
+ }
+
+ @Test
+ public void testTransactionIsolation() throws InterruptedException, ExecutionException {
+
+ assertNotNull(domStore);
+
+ DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
+ assertNotNull(readTx);
+
+ DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
+ assertNotNull(writeTx);
+
+ /**
+ * Writes /test in writeTx
+ */
+ NormalizedNode<?, ?> testNode = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
+ writeTx.write(TestModel.TEST_PATH, testNode);
+
+ /**
+ * Reads /test from writeTx Read should return container.
+ */
+ ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = writeTx.read(TestModel.TEST_PATH);
+ assertEquals("read: isPresent", true, writeTxContainer.get().isPresent());
+ assertEquals("read: data", testNode, writeTxContainer.get().get());
+
+ /**
+ * Reads /test from readTx Read should return Absent.
+ */
+ ListenableFuture<Optional<NormalizedNode<?, ?>>> readTxContainer = readTx.read(TestModel.TEST_PATH);
+ assertEquals("read: isPresent", false, readTxContainer.get().isPresent());
+ }
+
+ @Test
+ public void testTransactionCommit() throws InterruptedException, ExecutionException {
+
+ DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
+ assertNotNull(writeTx);
+
+ /**
+ * Writes /test in writeTx
+ */
+ NormalizedNode<?, ?> testNode = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
+ writeTx.write(TestModel.TEST_PATH, testNode);
+
+ /**
+ * Reads /test from writeTx Read should return container.
+ */
+ ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = writeTx.read(TestModel.TEST_PATH);
+ assertEquals("read: isPresent", true, writeTxContainer.get().isPresent());
+ assertEquals("read: data", testNode, writeTxContainer.get().get());
+
+ DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
+
+ assertThreePhaseCommit(cohort);
+
+ Optional<NormalizedNode<?, ?>> afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)
+ .get();
+ assertEquals("After commit read: isPresent", true, afterCommitRead.isPresent());
+ assertEquals("After commit read: data", testNode, afterCommitRead.get());
+ }
+
+ @Test
+ public void testDelete() throws Exception {
+
+ DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
+ assertNotNull( writeTx );
+
+ // Write /test and commit
+
+ writeTx.write( TestModel.TEST_PATH, ImmutableNodes.containerNode( TestModel.TEST_QNAME ) );
+
+ assertThreePhaseCommit( writeTx.ready() );
+
+ Optional<NormalizedNode<?, ?>> afterCommitRead = domStore.newReadOnlyTransaction().
+ read(TestModel.TEST_PATH ).get();
+ assertEquals( "After commit read: isPresent", true, afterCommitRead.isPresent() );
+
+ // Delete /test and verify
+
+ writeTx = domStore.newWriteOnlyTransaction();
+
+ writeTx.delete( TestModel.TEST_PATH );
+
+ assertThreePhaseCommit( writeTx.ready() );
+
+ afterCommitRead = domStore.newReadOnlyTransaction().
+ read(TestModel.TEST_PATH ).get();
+ assertEquals( "After commit read: isPresent", false, afterCommitRead.isPresent() );
+ }
+
+ @Test
+ public void testMerge() throws Exception {
+
+ DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
+ assertNotNull( writeTx );
+
+ ContainerNode containerNode = ImmutableContainerNodeBuilder.create()
+ .withNodeIdentifier( new NodeIdentifier( TestModel.TEST_QNAME ) )
+ .addChild( ImmutableNodes.mapNodeBuilder( TestModel.OUTER_LIST_QNAME )
+ .addChild( ImmutableNodes.mapEntry( TestModel.OUTER_LIST_QNAME,
+ TestModel.ID_QNAME, 1 ) ).build() ).build();
+
+ writeTx.merge( TestModel.TEST_PATH, containerNode );
+
+ assertThreePhaseCommit( writeTx.ready() );
+
+ Optional<NormalizedNode<?, ?>> afterCommitRead = domStore.newReadOnlyTransaction().
+ read(TestModel.TEST_PATH ).get();
+ assertEquals( "After commit read: isPresent", true, afterCommitRead.isPresent() );
+ assertEquals( "After commit read: data", containerNode, afterCommitRead.get() );
+
+ // Merge a new list entry node
+
+ writeTx = domStore.newWriteOnlyTransaction();
+ assertNotNull( writeTx );
+
+ containerNode = ImmutableContainerNodeBuilder.create()
+ .withNodeIdentifier( new NodeIdentifier( TestModel.TEST_QNAME ) )
+ .addChild( ImmutableNodes.mapNodeBuilder( TestModel.OUTER_LIST_QNAME )
+ .addChild( ImmutableNodes.mapEntry( TestModel.OUTER_LIST_QNAME,
+ TestModel.ID_QNAME, 1 ) )
+ .addChild( ImmutableNodes.mapEntry( TestModel.OUTER_LIST_QNAME,
+ TestModel.ID_QNAME, 2 ) ).build() ).build();
+
+ writeTx.merge( TestModel.TEST_PATH, containerNode );
+
+ assertThreePhaseCommit( writeTx.ready() );
+
+ afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH ).get();
+ assertEquals( "After commit read: isPresent", true, afterCommitRead.isPresent() );
+ assertEquals( "After commit read: data", containerNode, afterCommitRead.get() );
+ }
+
+
+ @Test
+ public void testExistsForExistingData() throws Exception {
+
+ DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
+ assertNotNull( writeTx );
+
+ ContainerNode containerNode = ImmutableContainerNodeBuilder.create()
+ .withNodeIdentifier( new NodeIdentifier( TestModel.TEST_QNAME ) )
+ .addChild( ImmutableNodes.mapNodeBuilder( TestModel.OUTER_LIST_QNAME )
+ .addChild( ImmutableNodes.mapEntry( TestModel.OUTER_LIST_QNAME,
+ TestModel.ID_QNAME, 1 ) ).build() ).build();
+
+ writeTx.merge( TestModel.TEST_PATH, containerNode );
+
+ CheckedFuture<Boolean, ReadFailedException> exists =
+ writeTx.exists(TestModel.TEST_PATH);
+
+ assertEquals(true, exists.checkedGet());
+
+ DOMStoreThreePhaseCommitCohort ready = writeTx.ready();
+
+ ready.preCommit().get();
+
+ ready.commit().get();
+
+ DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
+ assertNotNull( readTx );
+
+ exists =
+ readTx.exists(TestModel.TEST_PATH);
+
+ assertEquals(true, exists.checkedGet());
+ }
+
+ @Test
+ public void testExistsForNonExistingData() throws Exception {
+
+ DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
+ assertNotNull( writeTx );
+
+ CheckedFuture<Boolean, ReadFailedException> exists =
+ writeTx.exists(TestModel.TEST_PATH);
+
+ assertEquals(false, exists.checkedGet());
+
+ DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
+ assertNotNull( readTx );
+
+ exists =
+ readTx.exists(TestModel.TEST_PATH);
+
+ assertEquals(false, exists.checkedGet());
+ }
+
+ @Test(expected=ReadFailedException.class)
+ public void testExistsThrowsReadFailedException() throws Exception {
+
+ DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
+ assertNotNull( readTx );
+
+ readTx.close();
+
+ readTx.exists(TestModel.TEST_PATH).checkedGet();
+ }
+
+
+
+ @Test(expected=ReadFailedException.class)
+ public void testReadWithReadOnlyTransactionClosed() throws Throwable {
+
+ DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
+ assertNotNull( readTx );
+
+ readTx.close();
+
+ doReadAndThrowEx( readTx );
+ }
+
+ @Test(expected=ReadFailedException.class)
+ public void testReadWithReadOnlyTransactionFailure() throws Throwable {
+
+ DataTreeSnapshot mockSnapshot = Mockito.mock( DataTreeSnapshot.class );
+ Mockito.doThrow( new RuntimeException( "mock ex" ) ).when( mockSnapshot )
+ .readNode( Mockito.any( YangInstanceIdentifier.class ) );
+
+ DOMStoreReadTransaction readTx = SnapshotBackedTransactions.newReadTransaction("1", true, mockSnapshot);
+
+ doReadAndThrowEx( readTx );
+ }
+
+ @Test(expected=ReadFailedException.class)
+ public void testReadWithReadWriteTransactionClosed() throws Throwable {
+
+ DOMStoreReadTransaction readTx = domStore.newReadWriteTransaction();
+ assertNotNull( readTx );
+
+ readTx.close();
+
+ doReadAndThrowEx( readTx );
+ }
+
+ @Test(expected=ReadFailedException.class)
+ public void testReadWithReadWriteTransactionFailure() throws Throwable {
+
+ DataTreeSnapshot mockSnapshot = Mockito.mock( DataTreeSnapshot.class );
+ DataTreeModification mockModification = Mockito.mock( DataTreeModification.class );
+ Mockito.doThrow( new RuntimeException( "mock ex" ) ).when( mockModification )
+ .readNode( Mockito.any( YangInstanceIdentifier.class ) );
+ Mockito.doReturn( mockModification ).when( mockSnapshot ).newModification();
+ @SuppressWarnings("unchecked")
+ TransactionReadyPrototype<String> mockReady = Mockito.mock( TransactionReadyPrototype.class );
+ DOMStoreReadTransaction readTx = SnapshotBackedTransactions.newReadWriteTransaction("1", false, mockSnapshot, mockReady);
+
+ doReadAndThrowEx( readTx );
+ }
+
+ private static void doReadAndThrowEx( final DOMStoreReadTransaction readTx ) throws Throwable {
+ try {
+ readTx.read(TestModel.TEST_PATH).get();
+ } catch( ExecutionException e ) {
+ throw e.getCause();
+ }
+ }
+
+ @Test(expected=IllegalStateException.class)
+ public void testWriteWithTransactionReady() throws Exception {
+
+ DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
+
+ writeTx.ready();
+
+ // Should throw ex
+ writeTx.write( TestModel.TEST_PATH, ImmutableNodes.containerNode( TestModel.TEST_QNAME ) );
+ }
+
+ @Test(expected=IllegalStateException.class)
+ public void testReadyWithTransactionAlreadyReady() throws Exception {
+
+ DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
+
+ writeTx.ready();
+
+ // Should throw ex
+ writeTx.ready();
+ }
+
+ @Test
+ public void testTransactionAbort() throws InterruptedException, ExecutionException {
+
+ DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
+ assertNotNull(writeTx);
+
+ assertTestContainerWrite(writeTx);
+
+ DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
+
+ assertTrue(cohort.canCommit().get().booleanValue());
+ cohort.preCommit().get();
+ cohort.abort().get();
+
+ Optional<NormalizedNode<?, ?>> afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)
+ .get();
+ assertFalse(afterCommitRead.isPresent());
+ }
+
+ @Test
+ public void testTransactionChain() throws InterruptedException, ExecutionException {
+ DOMStoreTransactionChain txChain = domStore.createTransactionChain();
+ assertNotNull(txChain);
+
+ /**
+ * We alocate new read-write transaction and write /test
+ *
+ *
+ */
+ DOMStoreReadWriteTransaction firstTx = txChain.newReadWriteTransaction();
+ assertTestContainerWrite(firstTx);
+
+ /**
+ * First transaction is marked as ready, we are able to allocate chained
+ * transactions
+ */
+ DOMStoreThreePhaseCommitCohort firstWriteTxCohort = firstTx.ready();
+
+ /**
+ * We alocate chained transaction - read transaction, note first one is
+ * still not commited to datastore.
+ */
+ DOMStoreReadTransaction secondReadTx = txChain.newReadOnlyTransaction();
+
+ /**
+ *
+ * We test if we are able to read data from tx, read should not fail
+ * since we are using chained transaction.
+ *
+ *
+ */
+ assertTestContainerExists(secondReadTx);
+
+ /**
+ *
+ * We alocate next transaction, which is still based on first one, but
+ * is read-write.
+ *
+ */
+ DOMStoreReadWriteTransaction thirdDeleteTx = txChain.newReadWriteTransaction();
+
+ /**
+ * We test existence of /test in third transaction container should
+ * still be visible from first one (which is still uncommmited).
+ *
+ *
+ */
+ assertTestContainerExists(thirdDeleteTx);
+
+ /**
+ * We delete node in third transaction
+ */
+ thirdDeleteTx.delete(TestModel.TEST_PATH);
+
+ /**
+ * third transaction is sealed.
+ */
+ DOMStoreThreePhaseCommitCohort thirdDeleteTxCohort = thirdDeleteTx.ready();
+
+ /**
+ * We commit first transaction
+ *
+ */
+ assertThreePhaseCommit(firstWriteTxCohort);
+
+ // Alocates store transacion
+ DOMStoreReadTransaction storeReadTx = domStore.newReadOnlyTransaction();
+ /**
+ * We verify transaction is commited to store, container should exists
+ * in datastore.
+ */
+ assertTestContainerExists(storeReadTx);
+ /**
+ * We commit third transaction
+ *
+ */
+ assertThreePhaseCommit(thirdDeleteTxCohort);
+ }
+
+ @Test
+ @Ignore
+ public void testTransactionConflict() throws InterruptedException, ExecutionException {
+ DOMStoreReadWriteTransaction txOne = domStore.newReadWriteTransaction();
+ DOMStoreReadWriteTransaction txTwo = domStore.newReadWriteTransaction();
+ assertTestContainerWrite(txOne);
+ assertTestContainerWrite(txTwo);
+
+ /**
+ * Commits transaction
+ */
+ assertThreePhaseCommit(txOne.ready());
+
+ /**
+ * Asserts that txTwo could not be commited
+ */
+ assertFalse(txTwo.ready().canCommit().get());
+ }
+
+ private static void assertThreePhaseCommit(final DOMStoreThreePhaseCommitCohort cohort)
+ throws InterruptedException, ExecutionException {
+ assertTrue(cohort.canCommit().get().booleanValue());
+ cohort.preCommit().get();
+ cohort.commit().get();
+ }
+
+ private static Optional<NormalizedNode<?, ?>> assertTestContainerWrite(final DOMStoreReadWriteTransaction writeTx)
+ throws InterruptedException, ExecutionException {
+ /**
+ *
+ * Writes /test in writeTx
+ *
+ */
+ writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+
+ return assertTestContainerExists(writeTx);
+ }
+
+ /**
+ * Reads /test from readTx Read should return container.
+ */
+ private static Optional<NormalizedNode<?, ?>> assertTestContainerExists(final DOMStoreReadTransaction readTx)
+ throws InterruptedException, ExecutionException {
+
+ ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = readTx.read(TestModel.TEST_PATH);
+ assertTrue(writeTxContainer.get().isPresent());
+ return writeTxContainer.get();
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import java.util.concurrent.ExecutionException;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public class RootScopeSubtreeTest extends DefaultDataChangeListenerTestSuite {
+
+ @Override
+ protected void customizeTask(final DatastoreTestTask task) {
+ task.changeListener(TOP_LEVEL, DataChangeScope.SUBTREE);
+ }
+
+ @Override
+ public void putTopLevelOneNested(final DatastoreTestTask task) throws InterruptedException, ExecutionException {
+ AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+
+ assertContains(change.getCreatedData(), TOP_LEVEL, path(FOO), path(FOO, BAR));
+ assertEmpty(change.getUpdatedData());
+ assertEmpty(change.getRemovedPaths());
+ }
+
+ @Override
+ public void replaceTopLevelNestedChanged(final DatastoreTestTask task) throws InterruptedException,
+ ExecutionException {
+
+ AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+
+ assertContains(change.getCreatedData(), path(FOO, BAZ));
+ assertContains(change.getUpdatedData(), TOP_LEVEL, path(FOO));
+ assertContains(change.getRemovedPaths(), path(FOO, BAR));
+ }
+
+ @Override
+ protected void putTopLevelWithTwoNested(final DatastoreTestTask task) throws InterruptedException,
+ ExecutionException {
+
+ AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+
+ assertContains(change.getCreatedData(), TOP_LEVEL, path(FOO), path(FOO, BAR), path(FOO, BAZ));
+ assertEmpty(change.getUpdatedData());
+ assertEmpty(change.getRemovedPaths());
+ }
+
+ @Override
+ protected void twoNestedExistsOneIsDeleted(final DatastoreTestTask task) throws InterruptedException,
+ ExecutionException {
+
+ AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+
+ assertEmpty(change.getCreatedData());
+ assertContains(change.getUpdatedData(), TOP_LEVEL, path(FOO));
+ assertContains(change.getRemovedPaths(), path(FOO, BAZ));
+ }
+
+ @Override
+ protected void nestedListExistsRootDeleted(final DatastoreTestTask task) throws InterruptedException,
+ ExecutionException {
+
+ AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+
+ assertEmpty(change.getCreatedData());
+ assertEmpty(change.getUpdatedData());
+ assertContains(change.getRemovedPaths(), TOP_LEVEL, path(FOO), path(FOO, BAR), path(FOO, BAZ));
+ }
+
+ @Override
+ protected void existingOneNestedWriteAdditionalNested(final DatastoreTestTask task) throws InterruptedException, ExecutionException {
+ AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+
+ assertContains(change.getCreatedData(), path(FOO,BAZ));
+ assertNotContains(change.getCreatedData(), path(FOO,BAR));
+ assertContains(change.getUpdatedData(), TOP_LEVEL, path(FOO));
+ assertEmpty(change.getRemovedPaths());
+ }
+
+ @Override
+ protected void existingTopWriteTwoNested(final DatastoreTestTask task) throws InterruptedException, ExecutionException {
+ AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+
+ assertContains(change.getCreatedData(), path(FOO,BAR),path(FOO,BAZ));
+ assertContains(change.getUpdatedData(), TOP_LEVEL, path(FOO));
+ assertNotContains(change.getUpdatedData(), path(FOO,BAR));
+ assertEmpty(change.getRemovedPaths());
+ }
+
+ @Override
+ protected void existingTopWriteSibling(final DatastoreTestTask task) throws InterruptedException, ExecutionException {
+ AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+
+ assertContains(change.getCreatedData(), path(FOO_SIBLING));
+ assertContains(change.getUpdatedData(), TOP_LEVEL);
+ assertNotContains(change.getUpdatedData(), path(FOO));
+ assertEmpty(change.getRemovedPaths());
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import static org.junit.Assert.assertNotNull;
+import com.google.common.base.Throwables;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.util.concurrent.ExecutionException;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.bi.ba.rpcservice.rev140701.RockTheHouseInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext;
+import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class SchemaUpdateForTransactionTest {
+
+ private static final YangInstanceIdentifier TOP_PATH = YangInstanceIdentifier.of(Top.QNAME);
+ private SchemaContext schemaContext;
+ private InMemoryDOMDataStore domStore;
+
+ @Before
+ public void setupStore() {
+ domStore = new InMemoryDOMDataStore("TEST", MoreExecutors.newDirectExecutorService());
+ loadSchemas(RockTheHouseInput.class);
+ }
+
+ public void loadSchemas(final Class<?>... classes) {
+ YangModuleInfo moduleInfo;
+ try {
+ ModuleInfoBackedContext context = ModuleInfoBackedContext.create();
+ for (Class<?> clz : classes) {
+ moduleInfo = BindingReflections.getModuleInfo(clz);
+
+ context.registerModuleInfo(moduleInfo);
+ }
+ schemaContext = context.tryToCreateSchemaContext().get();
+ domStore.onGlobalContextUpdated(schemaContext);
+ } catch (Exception e) {
+ Throwables.propagateIfPossible(e);
+ }
+ }
+
+ /**
+ * Test suite tests allocating transaction when schema context
+ * does not contain module necessary for client write,
+ * then triggering update of global schema context
+ * and then performing write (according to new module).
+ *
+ * If transaction between allocation and schema context was
+ * unmodified, it is safe to change its schema context
+ * to new one (e.g. it will be same as if allocated after
+ * schema context update.)
+ *
+ * @throws InterruptedException
+ * @throws ExecutionException
+ */
+ @Test
+ public void testTransactionSchemaUpdate() throws InterruptedException, ExecutionException {
+
+ assertNotNull(domStore);
+
+ // We allocate transaction, initial schema context does not
+ // contain Lists model
+ DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
+ assertNotNull(writeTx);
+
+ // we trigger schema context update to contain Lists model
+ loadSchemas(RockTheHouseInput.class, Top.class);
+
+ /**
+ *
+ * Writes /test in writeTx, this write should not fail
+ * with IllegalArgumentException since /test is in
+ * schema context.
+ *
+ */
+ writeTx.write(TOP_PATH, ImmutableNodes.containerNode(Top.QNAME));
+
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import com.google.common.util.concurrent.ForwardingExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.util.concurrent.ExecutorService;
+
+/**
+ * A forwarding Executor used by unit tests for DataChangeListener notifications
+ *
+ * @author Thomas Pantelis
+ */
+public class TestDCLExecutorService extends ForwardingExecutorService {
+
+ // Start with a same thread executor to avoid timing issues during test setup.
+ private volatile ExecutorService currentExecutor = MoreExecutors.newDirectExecutorService();
+
+ // The real executor to use when test setup is complete.
+ private final ExecutorService postSetupExecutor;
+
+
+ public TestDCLExecutorService( final ExecutorService postSetupExecutor ) {
+ this.postSetupExecutor = postSetupExecutor;
+ }
+
+ @Override
+ protected ExecutorService delegate() {
+ return currentExecutor;
+ }
+
+ public void afterTestSetup() {
+ // Test setup complete - switch to the real executor.
+ currentExecutor = postSetupExecutor;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+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 java.io.InputStream;
+import java.util.Collections;
+import java.util.Set;
+
+public class TestModel {
+
+ public static final QName TEST_QNAME = QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test", "2014-03-13",
+ "test");
+ public static final QName OUTER_LIST_QNAME = QName.create(TEST_QNAME, "outer-list");
+ public static final QName INNER_LIST_QNAME = QName.create(TEST_QNAME, "inner-list");
+ public static final QName OUTER_CHOICE_QNAME = QName.create(TEST_QNAME, "outer-choice");
+ public static final QName ID_QNAME = QName.create(TEST_QNAME, "id");
+ public static final QName NAME_QNAME = QName.create(TEST_QNAME, "name");
+ public static final QName VALUE_QNAME = QName.create(TEST_QNAME, "value");
+ private static final String DATASTORE_TEST_YANG = "/odl-datastore-test.yang";
+
+ public static final YangInstanceIdentifier TEST_PATH = YangInstanceIdentifier.of(TEST_QNAME);
+ public static final YangInstanceIdentifier OUTER_LIST_PATH = YangInstanceIdentifier.builder(TEST_PATH).node(OUTER_LIST_QNAME).build();
+ public static final QName TWO_QNAME = QName.create(TEST_QNAME, "two");
+ public static final QName THREE_QNAME = QName.create(TEST_QNAME, "three");
+
+
+ public static final InputStream getDatastoreTestInputStream() {
+ return getInputStream(DATASTORE_TEST_YANG);
+ }
+
+ private static InputStream getInputStream(final String resourceName) {
+ return TestModel.class.getResourceAsStream(DATASTORE_TEST_YANG);
+ }
+
+ public static SchemaContext createTestContext() {
+ YangParserImpl parser = new YangParserImpl();
+ Set<Module> modules = parser.parseYangModelsFromStreams(Collections.singletonList(getDatastoreTestInputStream()));
+ return parser.resolveSchemaContext(modules);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.concurrent.ExecutionException;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public class WildcardedScopeBaseTest extends DefaultDataChangeListenerTestSuite {
+
+ private static final YangInstanceIdentifier TOP_LEVEL_LIST_ALL = TOP_LEVEL.node(TopLevelList.QNAME).node(
+ TopLevelList.QNAME);
+
+ @Override
+ protected void customizeTask(final DatastoreTestTask task) {
+ task.changeListener(TOP_LEVEL_LIST_ALL, DataChangeScope.BASE);
+ }
+
+ @Override
+ public void putTopLevelOneNested(final DatastoreTestTask task) throws InterruptedException, ExecutionException {
+
+ AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+
+ assertNotNull(change);
+
+ /*
+ * Created data must not contain nested-list item, since that is two-level deep.
+ */
+ assertNotContains(change.getCreatedData(), TOP_LEVEL,path(FOO, BAR));
+ assertContains(change.getCreatedData(), path(FOO) );
+
+ assertEmpty(change.getUpdatedData());
+ assertEmpty(change.getRemovedPaths());
+
+ }
+
+ @Override
+ public void replaceTopLevelNestedChanged(final DatastoreTestTask task) throws InterruptedException,
+ ExecutionException {
+
+ AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+ assertNotNull(change);
+ /*
+ * Created data must NOT contain nested-list item since scope is base, and change is two
+ * level deep.
+ */
+ assertNotContains(change.getCreatedData(), path(FOO, BAZ));
+ assertContains(change.getUpdatedData(), path(FOO));
+ assertNotContains(change.getUpdatedData(), TOP_LEVEL);
+ /*
+ * Removed data must NOT contain nested-list item since scope is base, and change is two
+ * level deep.
+ */
+ assertNotContains(change.getRemovedPaths(), path(FOO, BAR));
+
+ }
+
+ @Override
+ protected void putTopLevelWithTwoNested(final DatastoreTestTask task) throws InterruptedException,
+ ExecutionException {
+
+ AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+ assertNotNull(change);
+ assertFalse(change.getCreatedData().isEmpty());
+
+ // Base event should contain only changed item, no details about child.
+ assertContains(change.getCreatedData(), path(FOO));
+ assertNotContains(change.getCreatedData(), TOP_LEVEL,path(FOO, BAR), path(FOO, BAZ));
+ assertEmpty(change.getUpdatedData());
+ assertEmpty(change.getRemovedPaths());
+
+ }
+
+ @Override
+ protected void twoNestedExistsOneIsDeleted(final DatastoreTestTask task) throws InterruptedException,
+ ExecutionException {
+
+ /*
+ * Base listener should be notified only and only if actual node changed its state,
+ * since deletion of child, did not result in change of node we are listening
+ * for, we should not be getting data change event
+ * and this means settable future containing receivedDataChangeEvent is not done.
+ *
+ */
+ task.verifyNoChangeEvent();
+ }
+
+ @Override
+ public void nestedListExistsRootDeleted(final DatastoreTestTask task) throws InterruptedException,
+ ExecutionException {
+
+ AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+
+ assertEmpty(change.getCreatedData());
+ assertEmpty(change.getUpdatedData());
+
+ assertNotContains(change.getUpdatedData(), TOP_LEVEL);
+ /*
+ * Scope base listener event should contain top-level-list item and nested list path
+ * and should not contain baz, bar which are two-level deep
+ */
+ assertContains(change.getRemovedPaths(), path(FOO));
+ assertNotContains(change.getRemovedPaths(),path(FOO, BAZ),path(FOO,BAR));
+ }
+
+ @Override
+ protected void existingOneNestedWriteAdditionalNested(final DatastoreTestTask task) {
+ /*
+ * One listener should be notified only and only if actual node changed its state,
+ * since deletion of nested child (in this case /nested-list/nested-list[foo],
+ * did not result in change of node we are listening
+ * for, we should not be getting data change event
+ * and this means settable future containing receivedDataChangeEvent is not done.
+ *
+ */
+ task.verifyNoChangeEvent();
+ }
+
+ @Override
+ protected void existingTopWriteTwoNested(final DatastoreTestTask task) throws InterruptedException, ExecutionException {
+ /*
+ * One listener should be notified only and only if actual node changed its state,
+ * since deletion of nested child (in this case /nested-list/nested-list[foo],
+ * did not result in change of node we are listening
+ * for, we should not be getting data change event
+ * and this means settable future containing receivedDataChangeEvent is not done.
+ *
+ */
+ task.verifyNoChangeEvent();
+ }
+
+ @Override
+ protected void existingTopWriteSibling(final DatastoreTestTask task) throws InterruptedException, ExecutionException {
+ AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+
+ assertContains(change.getCreatedData(), path(FOO_SIBLING));
+ assertNotContains(change.getUpdatedData(), path(FOO), TOP_LEVEL);
+ assertEmpty(change.getRemovedPaths());
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.concurrent.ExecutionException;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.top.level.list.NestedList;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public class WildcardedScopeOneTest extends DefaultDataChangeListenerTestSuite {
+
+ private static final YangInstanceIdentifier TOP_LEVEL_LIST_ALL = TOP_LEVEL.node(TopLevelList.QNAME).node(
+ TopLevelList.QNAME);
+
+ @Override
+ protected void customizeTask(final DatastoreTestTask task) {
+ task.changeListener(TOP_LEVEL_LIST_ALL, DataChangeScope.ONE);
+ }
+
+ @Override
+ public void putTopLevelOneNested(final DatastoreTestTask task) throws InterruptedException, ExecutionException {
+
+ AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+
+ assertNotNull(change);
+
+ assertNotContains(change.getCreatedData(), TOP_LEVEL,path(FOO, BAR));
+ assertContains(change.getCreatedData(), path(FOO), path(FOO).node(NestedList.QNAME));
+
+ assertEmpty(change.getUpdatedData());
+ assertEmpty(change.getRemovedPaths());
+
+ }
+
+ @Override
+ public void replaceTopLevelNestedChanged(final DatastoreTestTask task) throws InterruptedException,
+ ExecutionException {
+
+ AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+ assertNotNull(change);
+ /*
+ * Created data must NOT contain nested-list item since scope is base, and change is two
+ * level deep.
+ */
+ assertNotContains(change.getCreatedData(), path(FOO, BAZ));
+ assertContains(change.getUpdatedData(), path(FOO),path(FOO).node(NestedList.QNAME));
+ assertNotContains(change.getUpdatedData(), TOP_LEVEL);
+ /*
+ * Removed data must NOT contain nested-list item since scope is base, and change is two
+ * level deep.
+ */
+ assertNotContains(change.getRemovedPaths(), path(FOO, BAR));
+
+ }
+
+ @Override
+ protected void putTopLevelWithTwoNested(final DatastoreTestTask task) throws InterruptedException,
+ ExecutionException {
+
+ AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+ assertNotNull(change);
+ assertFalse(change.getCreatedData().isEmpty());
+
+ // Base event should contain only changed item, and details about immediate child.
+ assertContains(change.getCreatedData(), path(FOO),path(FOO).node(NestedList.QNAME));
+ assertNotContains(change.getCreatedData(), TOP_LEVEL,path(FOO, BAR), path(FOO, BAZ));
+ assertEmpty(change.getUpdatedData());
+ assertEmpty(change.getRemovedPaths());
+
+ }
+
+ @Override
+ protected void twoNestedExistsOneIsDeleted(final DatastoreTestTask task) throws InterruptedException,
+ ExecutionException {
+
+ /*
+ * One listener should be notified only and only if actual node changed its state,
+ * since deletion of nested child (in this case /nested-list/nested-list[foo],
+ * did not result in change of node we are listening
+ * for, we should not be getting data change event
+ * and this means settable future containing receivedDataChangeEvent is not done.
+ *
+ */
+ task.verifyNoChangeEvent();
+ }
+
+ @Override
+ public void nestedListExistsRootDeleted(final DatastoreTestTask task) throws InterruptedException,
+ ExecutionException {
+
+ AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+
+ assertEmpty(change.getCreatedData());
+ assertEmpty(change.getUpdatedData());
+
+ assertNotContains(change.getUpdatedData(), TOP_LEVEL);
+ assertContains(change.getRemovedPaths(), path(FOO),path(FOO).node(NestedList.QNAME));
+ assertNotContains(change.getRemovedPaths(), path(FOO, BAZ),path(FOO,BAR));
+ }
+
+ @Override
+ protected void existingOneNestedWriteAdditionalNested(final DatastoreTestTask task) {
+ /*
+ * One listener should be notified only and only if actual node changed its state,
+ * since deletion of nested child (in this case /nested-list/nested-list[foo],
+ * did not result in change of node we are listening
+ * for, we should not be getting data change event
+ * and this means settable future containing receivedDataChangeEvent is not done.
+ *
+ */
+ task.verifyNoChangeEvent();
+ }
+
+ @Override
+ protected void existingTopWriteTwoNested(final DatastoreTestTask task) throws InterruptedException, ExecutionException {
+ /*
+ * One listener should be notified only and only if actual node changed its state,
+ * since deletion of nested child (in this case /nested-list/nested-list[foo],
+ * did not result in change of node we are listening
+ * for, we should not be getting data change event
+ * and this means settable future containing receivedDataChangeEvent is not done.
+ *
+ */
+ task.verifyNoChangeEvent();
+ }
+
+ @Override
+ protected void existingTopWriteSibling(final DatastoreTestTask task) throws InterruptedException, ExecutionException {
+ AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+
+ assertContains(change.getCreatedData(), path(FOO_SIBLING));
+ assertNotContains(change.getUpdatedData(),path(FOO), TOP_LEVEL);
+ assertEmpty(change.getRemovedPaths());
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.concurrent.ExecutionException;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public class WildcardedScopeSubtreeTest extends DefaultDataChangeListenerTestSuite {
+
+ private static final YangInstanceIdentifier TOP_LEVEL_LIST_ALL = TOP_LEVEL.node(TopLevelList.QNAME).node(
+ TopLevelList.QNAME);
+
+ @Override
+ protected void customizeTask(final DatastoreTestTask task) {
+ task.changeListener(TOP_LEVEL_LIST_ALL, DataChangeScope.SUBTREE);
+ }
+
+ @Override
+ public void putTopLevelOneNested(final DatastoreTestTask task) throws InterruptedException, ExecutionException {
+
+ AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+
+ assertNotContains(change.getCreatedData(), TOP_LEVEL);
+ assertContains(change.getCreatedData(), path(FOO), path(FOO, BAR));
+ assertEmpty(change.getUpdatedData());
+ assertEmpty(change.getRemovedPaths());
+
+ }
+
+ @Override
+ public void replaceTopLevelNestedChanged(final DatastoreTestTask task) throws InterruptedException,
+ ExecutionException {
+
+ AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+ assertNotNull(change);
+
+ assertContains(change.getCreatedData(), path(FOO, BAZ));
+ assertContains(change.getUpdatedData(), path(FOO));
+ assertNotContains(change.getUpdatedData(), TOP_LEVEL);
+ assertContains(change.getRemovedPaths(), path(FOO, BAR));
+
+ }
+
+ @Override
+ protected void putTopLevelWithTwoNested(final DatastoreTestTask task) throws InterruptedException,
+ ExecutionException {
+
+ AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+ assertNotNull(change);
+ assertFalse(change.getCreatedData().isEmpty());
+
+ assertContains(change.getCreatedData(), path(FOO), path(FOO, BAR), path(FOO, BAZ));
+ assertNotContains(change.getCreatedData(), TOP_LEVEL);
+ assertEmpty(change.getUpdatedData());
+ assertEmpty(change.getRemovedPaths());
+
+ }
+
+ @Override
+ protected void twoNestedExistsOneIsDeleted(final DatastoreTestTask task) throws InterruptedException,
+ ExecutionException {
+
+ AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+ assertNotNull(change);
+ assertTrue(change.getCreatedData().isEmpty());
+ assertContains(change.getUpdatedData(), path(FOO));
+ assertNotContains(change.getUpdatedData(), TOP_LEVEL);
+ assertContains(change.getRemovedPaths(),path(FOO, BAZ));
+ }
+
+ @Override
+ public void nestedListExistsRootDeleted(final DatastoreTestTask task) throws InterruptedException,
+ ExecutionException {
+
+ AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+
+ assertEmpty(change.getCreatedData());
+ assertEmpty(change.getUpdatedData());
+
+ assertNotContains(change.getUpdatedData(), TOP_LEVEL);
+ assertContains(change.getRemovedPaths(), path(FOO),path(FOO, BAZ),path(FOO,BAR));
+ }
+
+ @Override
+ protected void existingOneNestedWriteAdditionalNested(final DatastoreTestTask task) throws InterruptedException, ExecutionException {
+ AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+
+ assertContains(change.getCreatedData(), path(FOO,BAZ));
+ assertNotContains(change.getCreatedData(), path(FOO,BAR));
+ assertContains(change.getUpdatedData(), path(FOO));
+ assertNotContains(change.getUpdatedData(), TOP_LEVEL);
+ assertEmpty(change.getRemovedPaths());
+ }
+
+ @Override
+ protected void existingTopWriteTwoNested(final DatastoreTestTask task) throws InterruptedException, ExecutionException {
+ AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+
+ assertContains(change.getCreatedData(), path(FOO,BAR),path(FOO,BAZ));
+ assertContains(change.getUpdatedData(), path(FOO));
+ assertNotContains(change.getUpdatedData(), TOP_LEVEL, path(FOO,BAR));
+ assertEmpty(change.getRemovedPaths());
+ }
+
+ @Override
+ protected void existingTopWriteSibling(final DatastoreTestTask task) throws InterruptedException, ExecutionException {
+ AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = task.getChangeEvent();
+
+ assertContains(change.getCreatedData(), path(FOO_SIBLING));
+ assertNotContains(change.getUpdatedData(), path(FOO), TOP_LEVEL);
+ assertEmpty(change.getRemovedPaths());
+ }
+}
--- /dev/null
+module odl-datastore-test {
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test";
+ prefix "store-test";
+
+ revision "2014-03-13" {
+ description "Initial revision.";
+ }
+
+ container test {
+ list outer-list {
+ key id;
+ leaf id {
+ type uint16;
+ }
+ choice outer-choice {
+ case one {
+ leaf one {
+ type string;
+ }
+ }
+ case two-three {
+ leaf two {
+ type string;
+ }
+ leaf three {
+ type string;
+ }
+ }
+ }
+ list inner-list {
+ key name;
+ leaf name {
+ type string;
+ }
+ leaf value {
+ type string;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>sal-parent</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <version>1.3.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>sal-test-model</artifactId>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-binding</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools.model</groupId>
+ <artifactId>yang-ext</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.jacoco</groupId>
+ <artifactId>jacoco-maven-plugin</artifactId>
+ <configuration>
+ <includes>
+ <include>org.opendaylight.controller.*</include>
+ </includes>
+ </configuration>
+ <executions>
+ <execution>
+ <id>pre-test</id>
+ <goals>
+ <goal>prepare-agent</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>post-test</id>
+ <goals>
+ <goal>report</goal>
+ </goals>
+ <phase>test</phase>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>generate-sources</goal>
+ </goals>
+ <configuration>
+ <codeGenerators>
+ <generator>
+ <codeGeneratorClass>
+ org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl
+ </codeGeneratorClass>
+ <outputBaseDir>${salGeneratorPath}</outputBaseDir>
+ </generator>
+ </codeGenerators>
+ <inspectDependencies>true</inspectDependencies>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ <scm>
+ <connection>scm:git:http://git.opendaylight.org/gerrit/controller.git</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+ <tag>HEAD</tag>
+ <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
+ </scm>
+
+</project>
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.test.model.util;
+
+import com.google.common.collect.ImmutableList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.TreeComplexUsesAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.TreeComplexUsesAugmentBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.TreeLeafOnlyUsesAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.TreeLeafOnlyUsesAugmentBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.complex.from.grouping.ListViaUses;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.complex.from.grouping.ListViaUsesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.complex.from.grouping.ListViaUsesKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.TopBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.top.level.list.NestedList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.top.level.list.NestedListKey;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import java.util.Arrays;
+
+public class ListsBindingUtils {
+
+ private static final InstanceIdentifier<Top> TOP_PATH = InstanceIdentifier.create(Top.class);
+
+ private ListsBindingUtils() {
+ throw new UnsupportedOperationException();
+ }
+
+ public static final TopLevelListKey TOP_FOO_KEY = new TopLevelListKey("foo");
+ public static final TopLevelListKey TOP_BAR_KEY = new TopLevelListKey("bar");
+ public static final ListViaUsesKey USES_ONE_KEY = new ListViaUsesKey("one");
+ public static final ListViaUsesKey USES_TWO_KEY = new ListViaUsesKey("two");
+
+
+ public static InstanceIdentifier<TopLevelList> path(final TopLevelListKey key) {
+ return TOP_PATH.child(TopLevelList.class, key);
+ }
+
+ public static InstanceIdentifier<NestedList> path(final TopLevelListKey top,final NestedListKey nested) {
+ return path(top).child(NestedList.class, nested);
+ }
+
+ public static InstanceIdentifier<ListViaUses> path(final TopLevelListKey top,final ListViaUsesKey uses) {
+ return path(top).augmentation(TreeComplexUsesAugment.class).child(ListViaUses.class, uses);
+ }
+
+ public static <T extends DataObject & Augmentation<TopLevelList>> InstanceIdentifier<T> path(final TopLevelListKey key, final Class<T> augmentation) {
+ return path(key).augmentation(augmentation);
+ }
+
+ public static Top top(final TopLevelList... listItems) {
+ return new TopBuilder().setTopLevelList(Arrays.asList(listItems)).build();
+ }
+
+ public static TopLevelList topLevelList(final TopLevelListKey key) {
+ return new TopLevelListBuilder().setKey(key).build();
+ }
+
+ public static TopLevelList topLevelList(final TopLevelListKey key, final TreeComplexUsesAugment augment) {
+ TopLevelListBuilder builder = new TopLevelListBuilder().setKey(key);
+ builder.addAugmentation(TreeComplexUsesAugment.class, augment);
+ return builder.build();
+ }
+
+ public static TreeComplexUsesAugment complexUsesAugment(final ListViaUsesKey... keys) {
+ ImmutableList.Builder<ListViaUses> listViaUses = ImmutableList.<ListViaUses> builder();
+ for (ListViaUsesKey key : keys) {
+ listViaUses.add(new ListViaUsesBuilder().setKey(key).build());
+ }
+ return new TreeComplexUsesAugmentBuilder().setListViaUses(listViaUses.build()).build();
+ }
+
+ public static TreeLeafOnlyUsesAugment leafOnlyUsesAugment(String leafFromGroupingValue) {
+
+ return new TreeLeafOnlyUsesAugmentBuilder().setLeafFromGrouping(leafFromGroupingValue).build();
+ }
+
+}
--- /dev/null
+module opendaylight-mdsal-augment-test {
+
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:test:augment";
+ prefix aug-test;
+
+ import opendaylight-mdsal-list-test {
+ prefix test;
+ }
+ import yang-ext {
+ prefix ext;
+ }
+
+ description
+ "This module contains a collection of YANG augmentations used for
+ some test cases.";
+
+ revision 2014-07-09 {
+ description
+ "Test model for testing data broker with nested lists.";
+ }
+
+ grouping leaf-from-grouping {
+ leaf leaf-from-grouping {
+ type string;
+ }
+ }
+
+ grouping complex-from-grouping {
+ container container-with-uses {
+ uses leaf-from-grouping;
+ }
+ list list-via-uses {
+ key "name";
+ leaf name {
+ type string;
+ }
+ }
+
+ }
+
+ augment "/test:top/test:top-level-list" {
+ ext:augment-identifier tree-leaf-only-uses-augment;
+ uses leaf-from-grouping;
+ }
+
+ augment "/test:put-top/test:input/test:top-level-list" {
+ ext:augment-identifier rpc-leaf-only-uses-augment;
+ uses leaf-from-grouping;
+ }
+
+ augment "/test:top/test:top-level-list" {
+ ext:augment-identifier tree-complex-uses-augment;
+ uses complex-from-grouping;
+ }
+
+ augment "/test:put-top/test:input/test:top-level-list" {
+ ext:augment-identifier rpc-complex-uses-augment;
+ uses complex-from-grouping;
+ }
+
+ augment "/test:top/test:top-level-list" {
+ ext:augment-identifier tree-leaf-only-augment;
+
+ leaf simple-value {
+ type string;
+ }
+ }
+
+ augment "/test:top/test:top-level-list" {
+ ext:augment-identifier tree-second-leaf-only-augment;
+
+ leaf second-simple-value {
+ type string;
+ }
+ }
+
+ augment "/test:put-top/test:input/test:top-level-list" {
+ ext:augment-identifier rpc-leaf-only-augment;
+
+ leaf simple-value {
+ type string;
+ }
+ }
+
+ augment "/test:put-top/test:input/test:top-level-list" {
+ ext:augment-identifier rpc-second-leaf-only-augment;
+
+ leaf second-simple-value {
+ type string;
+ }
+ }
+
+}
\ No newline at end of file
--- /dev/null
+module opendaylight-mdsal-list-test {
+
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:test:list";
+ prefix list-test;
+
+
+ description
+ "This module contains a collection of YANG definitions used for
+ some test cases.";
+
+ revision 2014-07-01 {
+ description
+ "Test model for testing data broker with nested lists.";
+ }
+
+ grouping two-level-list {
+ list top-level-list {
+ description
+ "Top Level List";
+ key "name";
+ leaf name {
+ type string;
+ }
+ list nested-list {
+ key "name";
+ leaf name {
+ type string;
+ }
+ leaf type {
+ type string;
+ mandatory true;
+ description
+ "Mandatory type of list.";
+ }
+ ordered-by user;
+ description
+ "A list of service functions that compose the service chain";
+ }
+ }
+ }
+
+ container top {
+ uses two-level-list;
+ }
+
+ notification two-level-list-changed {
+ uses two-level-list;
+ }
+
+ rpc put-top {
+ input {
+ uses two-level-list;
+ }
+ }
+}
--- /dev/null
+module opendaylight-of-migration-test-model {
+
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:of-migration-test-model";
+ prefix of-migration-test;
+
+ import opendaylight-mdsal-list-test {prefix test;}
+ import yang-ext {prefix ext;}
+ import opendaylight-mdsal-augment-test {prefix aug;}
+ import opendaylight-test-routed-rpc {prefix routed;}
+
+ description
+ "This module contains a collection of YANG definitions used for
+ test cases that used to depend on flow model.";
+
+ revision 2015-02-10 {
+ }
+
+ typedef bit-flags {
+ type bits {
+ bit FLAG_ONE;
+ bit FLAG_TWO;
+ bit FLAG_THREE;
+ bit FLAG_FOUR;
+ bit FLAG_FIVE;
+ }
+ }
+
+ typedef custom-enum {
+ type enumeration {
+ enum type1;
+ enum type2;
+ enum type3;
+ }
+ }
+
+ grouping enum-grouping {
+ leaf attr-enum {
+ type custom-enum;
+ }
+ }
+
+ grouping aug-grouping {
+ container cont1 {
+ leaf attr-str {
+ type string;
+ }
+ }
+
+ container cont2 {
+ list contlist1 {
+ key "attr-str";
+
+ leaf attr-str {
+ type string;
+ }
+
+ uses enum-grouping;
+ }
+ }
+
+ leaf attr-str1 {
+ type string;
+ }
+
+ leaf attr-str2 {
+ type string;
+ }
+
+ leaf attr-str3 {
+ type string;
+ }
+
+ leaf attr-str4 {
+ type string;
+ }
+
+ list list1 {
+ key "attr-str";
+ leaf attr-str {
+ type string;
+ }
+
+ list list1-1 {
+ key "attr-int";
+ leaf attr-int {
+ type int32;
+ }
+
+ leaf attr-str {
+ type string;
+ }
+
+ leaf flags {
+ type bit-flags;
+ }
+ }
+
+ list list1-2 {
+ key "attr-int";
+ leaf attr-int {
+ type int32;
+ }
+
+ leaf attr-str {
+ type string;
+ }
+ }
+ }
+ }
+
+ augment "/test:top/test:top-level-list" {
+ ext:augment-identifier tll-complex-augment;
+ uses aug-grouping;
+ }
+
+ augment "/test:top/test:top-level-list/list1/list1-1" {
+ ext:augment-identifier list11-simple-augment;
+
+ leaf attr-str2 {
+ type string;
+ }
+
+ container cont {
+ leaf attr-int {
+ type int32;
+ }
+ }
+ }
+
+ augment "/test:top/test:top-level-list/test:nested-list/" {
+ ext:augment-identifier nested-list-simple-augment;
+
+ leaf type {
+ type string;
+ }
+ }
+
+ rpc knock-knock {
+ input {
+ leaf knocker-id {
+ ext:context-reference routed:test-context;
+ type instance-identifier;
+ }
+
+ leaf question {
+ type string;
+ }
+ }
+
+ output {
+ leaf answer {
+ type string;
+ }
+ }
+ }
+}
--- /dev/null
+module opendaylight-sal-test-store {
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:test:store";
+ prefix "binding-impl";
+
+ description
+ "Regression Test model for data store";
+
+ revision "2014-04-22" {
+ description
+ "Initial revision";
+ }
+
+ grouping name-value {
+ leaf name {
+ type string;
+ }
+ leaf value {
+ type string;
+ }
+ }
+
+ container lists {
+ config false;
+ container unordered-container {
+ list unordered-list {
+ key "name";
+ uses name-value;
+ }
+ }
+ container ordered-container {
+ list ordered-list {
+ ordered-by user;
+ key "name";
+ uses name-value;
+ }
+ }
+ container unkeyed-container {
+ /*
+ Following list nodes not specify key, which
+ makes impossible to uniquely identify list items
+ over time.
+ */
+ list unkeyed-list {
+ uses name-value;
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+module opendaylight-test-notification {
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:test:bi:ba:notification";
+ prefix "ntf";
+
+ description
+ "Test model for testing of registering notification listener and publishing of notification.";
+
+ revision "2015-02-05" {
+ description
+ "Initial revision";
+ }
+
+ notification out-of-pixie-dust-notification {
+ description "Just a testing notification that we can not fly for now.";
+
+ leaf reason {
+ type string;
+ }
+
+ leaf days-till-new-dust {
+ type uint16;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+module opendaylight-test-routed-rpc {
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:test:rpc:routing";
+ prefix "rpc";
+ import yang-ext { prefix ext; }
+
+ description
+ "Test model for testing of registering rpc service on binding independent mount point
+ and retrieving rpc service via binding aware mount point.";
+
+ revision "2014-07-01" {
+ description
+ "Initial revision";
+ }
+
+ identity test-context {
+ description "Test Context";
+ }
+
+ typedef encapsulated-route {
+ type instance-identifier;
+ }
+
+ grouping route-in-grouping {
+ leaf route {
+ type instance-identifier;
+ ext:context-reference test-context;
+ }
+ }
+
+ grouping encapsulated-route-in-grouping {
+ leaf route {
+ type encapsulated-route;
+ ext:context-reference test-context;
+ }
+ }
+
+ rpc routed-simple-route {
+ input {
+ leaf route {
+ type instance-identifier;
+ ext:context-reference test-context;
+ }
+ }
+ }
+}
--- /dev/null
+module opendaylight-test-rpc-service {
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:test:bi:ba:rpcservice";
+ prefix "rpc";
+
+ description
+ "Test model for testing of registering rpc service on binding independent mount point
+ and retrieving rpc service via binding aware mount point.";
+
+ revision "2014-07-01" {
+ description
+ "Initial revision";
+ }
+
+ rpc rock-the-house {
+ input {
+ leaf zip-code {
+ type string;
+ }
+ }
+ }
+}
\ No newline at end of file