- * When the ShardManager is constructed it reads the cluster configuration to find out which shard replicas
- * belong on this member. It finds out the name of the current cluster member from the Akka Clustering Service.
- *
- *
- *
Replica Elections
- *
- *
- * The Shard Manager uses multiple cues to initiate election.
- *
When a member of the cluster dies
- *
When a local shard replica dies
- *
When a local shard replica comes alive
- *
+ *
Monitor the cluster members and store their addresses
+ *
*/
public class ShardManager extends AbstractUntypedActor {
// Stores a mapping between a member name and the address of the member
+ // Member names look like "member-1", "member-2" etc and are as specified
+ // in configuration
private final Map memberNameToAddress = new HashMap<>();
+ // Stores a mapping between a shard name and it's corresponding information
+ // Shard names look like inventory, topology etc and are as specified in
+ // configuration
private final Map localShards = new HashMap<>();
-
+ // The type of a ShardManager reflects the type of the datastore itself
+ // A data store could be of type config/operational
private final String type;
private final ClusterWrapper cluster;
@@ -102,7 +97,8 @@ public class ShardManager extends AbstractUntypedActor {
if (message.getClass().equals(FindPrimary.SERIALIZABLE_CLASS)) {
findPrimary(
FindPrimary.fromSerializable(message));
-
+ } else if(message instanceof FindLocalShard){
+ findLocalShard((FindLocalShard) message);
} else if (message instanceof UpdateSchemaContext) {
updateSchemaContext(message);
} else if (message instanceof ClusterEvent.MemberUp){
@@ -117,6 +113,18 @@ public class ShardManager extends AbstractUntypedActor {
}
+ private void findLocalShard(FindLocalShard message) {
+ ShardInformation shardInformation =
+ localShards.get(message.getShardName());
+
+ if(shardInformation != null){
+ getSender().tell(new LocalShardFound(shardInformation.getActor()), getSelf());
+ return;
+ }
+
+ getSender().tell(new LocalShardNotFound(message.getShardName()), getSelf());
+ }
+
private void ignoreMessage(Object message){
LOG.debug("Unhandled message : " + message);
}
@@ -137,6 +145,11 @@ public class ShardManager extends AbstractUntypedActor {
}
}
+ /**
+ * Notifies all the local shards of a change in the schema context
+ *
+ * @param message
+ */
private void updateSchemaContext(Object message) {
for(ShardInformation info : localShards.values()){
info.getActor().tell(message,getSelf());
@@ -180,10 +193,7 @@ public class ShardManager extends AbstractUntypedActor {
getSender().tell(new PrimaryNotFound(shardName).toSerializable(), getSelf());
}
- private String
-
-
- getShardActorPath(String shardName, String memberName) {
+ private String getShardActorPath(String shardName, String memberName) {
Address address = memberNameToAddress.get(memberName);
if(address != null) {
return address.toString() + "/user/shardmanager-" + this.type + "/"
@@ -193,11 +203,23 @@ public class ShardManager extends AbstractUntypedActor {
return null;
}
+ /**
+ * Construct the name of the shard actor given the name of the member on
+ * which the shard resides and the name of the shard
+ *
+ * @param memberName
+ * @param shardName
+ * @return
+ */
private String getShardActorName(String memberName, String shardName){
return memberName + "-shard-" + shardName + "-" + this.type;
}
- // Create the shards that are local to this member
+ /**
+ * Create shards that are local to the member on which the ShardManager
+ * runs
+ *
+ */
private void createLocalShards() {
String memberName = this.cluster.getCurrentMemberName();
List memberShardNames =
@@ -214,6 +236,12 @@ public class ShardManager extends AbstractUntypedActor {
}
+ /**
+ * Given the name of the shard find the addresses of all it's peers
+ *
+ * @param shardName
+ * @return
+ */
private Map getPeerAddresses(String shardName){
Map peerAddresses = new HashMap<>();
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardReadTransaction.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardReadTransaction.java
new file mode 100644
index 0000000000..f78935b5e7
--- /dev/null
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardReadTransaction.java
@@ -0,0 +1,58 @@
+/*
+ *
+ * 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.cluster.datastore;
+
+import akka.actor.ActorRef;
+import akka.actor.PoisonPill;
+import akka.event.Logging;
+import akka.event.LoggingAdapter;
+import org.opendaylight.controller.cluster.datastore.messages.CloseTransaction;
+import org.opendaylight.controller.cluster.datastore.messages.CloseTransactionReply;
+import org.opendaylight.controller.cluster.datastore.messages.ReadData;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * @author: syedbahm
+ * Date: 8/6/14
+ */
+public class ShardReadTransaction extends ShardTransaction {
+ private final DOMStoreReadTransaction transaction;
+ private final LoggingAdapter log =
+ Logging.getLogger(getContext().system(), this);
+
+ public ShardReadTransaction(DOMStoreReadTransaction transaction, ActorRef shardActor, SchemaContext schemaContext) {
+ super(shardActor, schemaContext);
+ this.transaction = transaction;
+
+ }
+
+ public ShardReadTransaction(DOMStoreTransactionChain transactionChain, DOMStoreReadTransaction transaction, ActorRef shardActor, SchemaContext schemaContext) {
+ super(transactionChain, shardActor, schemaContext);
+ this.transaction = transaction;
+ }
+
+ @Override
+ public void handleReceive(Object message) throws Exception {
+ if (ReadData.SERIALIZABLE_CLASS.equals(message.getClass())) {
+ readData(transaction,ReadData.fromSerializable(message));
+ } else {
+ super.handleReceive(message);
+ }
+ }
+ protected void closeTransaction(CloseTransaction message) {
+ transaction.close();
+ getSender().tell(new CloseTransactionReply().toSerializable(), getSelf());
+ getSelf().tell(PoisonPill.getInstance(), getSelf());
+ }
+
+}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardReadWriteTransaction.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardReadWriteTransaction.java
new file mode 100644
index 0000000000..6733bcfb9f
--- /dev/null
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardReadWriteTransaction.java
@@ -0,0 +1,68 @@
+/*
+ *
+ * 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.cluster.datastore;
+
+import akka.actor.ActorRef;
+import akka.actor.PoisonPill;
+import akka.event.Logging;
+import akka.event.LoggingAdapter;
+import org.opendaylight.controller.cluster.datastore.messages.CloseTransaction;
+import org.opendaylight.controller.cluster.datastore.messages.CloseTransactionReply;
+import org.opendaylight.controller.cluster.datastore.messages.DeleteData;
+import org.opendaylight.controller.cluster.datastore.messages.MergeData;
+import org.opendaylight.controller.cluster.datastore.messages.ReadData;
+import org.opendaylight.controller.cluster.datastore.messages.ReadyTransaction;
+import org.opendaylight.controller.cluster.datastore.messages.WriteData;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * @author: syedbahm
+ * Date: 8/6/14
+ */
+public class ShardReadWriteTransaction extends ShardTransaction {
+ private final DOMStoreReadWriteTransaction transaction;
+ private final LoggingAdapter log =
+ Logging.getLogger(getContext().system(), this);
+ public ShardReadWriteTransaction(DOMStoreTransactionChain transactionChain, DOMStoreReadWriteTransaction transaction, ActorRef shardActor, SchemaContext schemaContext) {
+ super(transactionChain, shardActor, schemaContext);
+ this.transaction = transaction;
+ }
+
+ public ShardReadWriteTransaction(DOMStoreReadWriteTransaction transaction, ActorRef shardActor, SchemaContext schemaContext) {
+ super( shardActor, schemaContext);
+ this.transaction = transaction;
+ }
+
+ @Override
+ public void handleReceive(Object message) throws Exception {
+ if (ReadData.SERIALIZABLE_CLASS.equals(message.getClass())) {
+ readData(transaction,ReadData.fromSerializable(message));
+ }else if (WriteData.SERIALIZABLE_CLASS.equals(message.getClass())) {
+ writeData(transaction, WriteData.fromSerializable(message, schemaContext));
+ } else if (MergeData.SERIALIZABLE_CLASS.equals(message.getClass())) {
+ mergeData(transaction, MergeData.fromSerializable(message, schemaContext));
+ } else if (DeleteData.SERIALIZABLE_CLASS.equals(message.getClass())) {
+ deleteData(transaction,DeleteData.fromSerizalizable(message));
+ } else if (ReadyTransaction.SERIALIZABLE_CLASS.equals(message.getClass())) {
+ readyTransaction(transaction,new ReadyTransaction());
+ }else {
+ super.handleReceive(message);
+ }
+ }
+
+ protected void closeTransaction(CloseTransaction message) {
+ transaction.close();
+ getSender().tell(new CloseTransactionReply().toSerializable(), getSelf());
+ getSelf().tell(PoisonPill.getInstance(), getSelf());
+ }
+}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardTransaction.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardTransaction.java
index 737f57bf5d..3a916bda2c 100644
--- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardTransaction.java
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardTransaction.java
@@ -9,7 +9,6 @@
package org.opendaylight.controller.cluster.datastore;
import akka.actor.ActorRef;
-import akka.actor.PoisonPill;
import akka.actor.Props;
import akka.event.Logging;
import akka.event.LoggingAdapter;
@@ -17,7 +16,6 @@ import akka.japi.Creator;
import com.google.common.base.Optional;
import com.google.common.util.concurrent.ListenableFuture;
import org.opendaylight.controller.cluster.datastore.messages.CloseTransaction;
-import org.opendaylight.controller.cluster.datastore.messages.CloseTransactionReply;
import org.opendaylight.controller.cluster.datastore.messages.DeleteData;
import org.opendaylight.controller.cluster.datastore.messages.DeleteDataReply;
import org.opendaylight.controller.cluster.datastore.messages.MergeData;
@@ -34,9 +32,11 @@ import org.opendaylight.controller.cluster.datastore.modification.ImmutableCompo
import org.opendaylight.controller.cluster.datastore.modification.MergeModification;
import org.opendaylight.controller.cluster.datastore.modification.MutableCompositeModification;
import org.opendaylight.controller.cluster.datastore.modification.WriteModification;
+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.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
@@ -65,170 +65,200 @@ import java.util.concurrent.ExecutionException;
*
{@link org.opendaylight.controller.cluster.datastore.messages.CloseTransaction}
*
*/
-public class ShardTransaction extends AbstractUntypedActor {
-
- private final ActorRef shardActor;
- private final SchemaContext schemaContext;
-
- // FIXME : see below
- // If transactionChain is not null then this transaction is part of a
- // transactionChain. Not really clear as to what that buys us
- private final DOMStoreTransactionChain transactionChain;
-
- private final DOMStoreReadWriteTransaction transaction;
-
- private final MutableCompositeModification modification =
- new MutableCompositeModification();
-
- private final LoggingAdapter log =
- Logging.getLogger(getContext().system(), this);
-
- public ShardTransaction(DOMStoreReadWriteTransaction transaction,
- ActorRef shardActor, SchemaContext schemaContext) {
- this(null, transaction, shardActor, schemaContext);
- }
-
- public ShardTransaction(DOMStoreTransactionChain transactionChain, DOMStoreReadWriteTransaction transaction,
- ActorRef shardActor, SchemaContext schemaContext) {
- this.transactionChain = transactionChain;
- this.transaction = transaction;
- this.shardActor = shardActor;
- this.schemaContext = schemaContext;
- }
-
-
-
- public static Props props(final DOMStoreReadWriteTransaction transaction,
- final ActorRef shardActor, final SchemaContext schemaContext) {
- return Props.create(new Creator() {
-
- @Override
- public ShardTransaction create() throws Exception {
- return new ShardTransaction(transaction, shardActor, schemaContext);
- }
- });
- }
-
- public static Props props(final DOMStoreTransactionChain transactionChain, final DOMStoreReadWriteTransaction transaction,
- final ActorRef shardActor, final SchemaContext schemaContext) {
- return Props.create(new Creator() {
-
- @Override
- public ShardTransaction create() throws Exception {
- return new ShardTransaction(transactionChain, transaction, shardActor, schemaContext);
- }
- });
+public abstract class ShardTransaction extends AbstractUntypedActor {
+
+ private final ActorRef shardActor;
+ protected final SchemaContext schemaContext;
+
+ // FIXME : see below
+ // If transactionChain is not null then this transaction is part of a
+ // transactionChain. Not really clear as to what that buys us
+ private final DOMStoreTransactionChain transactionChain;
+
+
+ private final MutableCompositeModification modification =
+ new MutableCompositeModification();
+
+ private final LoggingAdapter log =
+ Logging.getLogger(getContext().system(), this);
+
+ protected ShardTransaction(
+ ActorRef shardActor, SchemaContext schemaContext) {
+ this(null, shardActor, schemaContext);
+ }
+
+ protected ShardTransaction(DOMStoreTransactionChain transactionChain,
+ ActorRef shardActor, SchemaContext schemaContext) {
+ this.transactionChain = transactionChain;
+ //this.transaction = transaction;
+ this.shardActor = shardActor;
+ this.schemaContext = schemaContext;
+ }
+
+
+
+ public static Props props(final DOMStoreReadTransaction transaction,
+ final ActorRef shardActor, final SchemaContext schemaContext) {
+ return Props.create(new Creator() {
+
+ @Override
+ public ShardTransaction create() throws Exception {
+ return new ShardReadTransaction(transaction, shardActor, schemaContext);
+ }
+ });
+ }
+
+ public static Props props(final DOMStoreTransactionChain transactionChain, final DOMStoreReadTransaction transaction,
+ final ActorRef shardActor, final SchemaContext schemaContext) {
+ return Props.create(new Creator() {
+
+ @Override
+ public ShardTransaction create() throws Exception {
+ return new ShardReadTransaction(transactionChain, transaction, shardActor, schemaContext);
+ }
+ });
+ }
+
+ public static Props props(final DOMStoreReadWriteTransaction transaction,
+ final ActorRef shardActor, final SchemaContext schemaContext) {
+ return Props.create(new Creator() {
+
+ @Override
+ public ShardTransaction create() throws Exception {
+ return new ShardReadWriteTransaction(transaction, shardActor, schemaContext);
+ }
+ });
+ }
+
+ public static Props props(final DOMStoreTransactionChain transactionChain, final DOMStoreReadWriteTransaction transaction,
+ final ActorRef shardActor, final SchemaContext schemaContext) {
+ return Props.create(new Creator() {
+
+ @Override
+ public ShardTransaction create() throws Exception {
+ return new ShardReadWriteTransaction(transactionChain, transaction, shardActor, schemaContext);
+ }
+ });
+ }
+
+
+ public static Props props(final DOMStoreWriteTransaction transaction,
+ final ActorRef shardActor, final SchemaContext schemaContext) {
+ return Props.create(new Creator() {
+
+ @Override
+ public ShardTransaction create() throws Exception {
+ return new ShardWriteTransaction(transaction, shardActor, schemaContext);
+ }
+ });
+ }
+
+ public static Props props(final DOMStoreTransactionChain transactionChain, final DOMStoreWriteTransaction transaction,
+ final ActorRef shardActor, final SchemaContext schemaContext) {
+ return Props.create(new Creator() {
+
+ @Override
+ public ShardTransaction create() throws Exception {
+ return new ShardWriteTransaction(transactionChain, transaction, shardActor, schemaContext);
+ }
+ });
+ }
+
+
+ @Override
+ public void handleReceive(Object message) throws Exception {
+ if (message.getClass().equals(CloseTransaction.SERIALIZABLE_CLASS)) {
+ closeTransaction(new CloseTransaction());
+ } else if (message instanceof GetCompositedModification) {
+ // This is here for testing only
+ getSender().tell(new GetCompositeModificationReply(
+ new ImmutableCompositeModification(modification)), getSelf());
+ }else{
+ throw new Exception ("ShardTransaction:handleRecieve received an unknown message"+message);
}
-
-
- @Override
- public void handleReceive(Object message) throws Exception {
- if (ReadData.SERIALIZABLE_CLASS.equals(message.getClass())) {
- readData(ReadData.fromSerializable(message));
- } else if (WriteData.SERIALIZABLE_CLASS.equals(message.getClass())) {
- writeData(WriteData.fromSerializable(message, schemaContext));
- } else if (MergeData.SERIALIZABLE_CLASS.equals(message.getClass())) {
- mergeData(MergeData.fromSerializable(message, schemaContext));
- } else if (DeleteData.SERIALIZABLE_CLASS.equals(message.getClass())) {
- deleteData(DeleteData.fromSerizalizable(message));
- } else if (ReadyTransaction.SERIALIZABLE_CLASS.equals(message.getClass())) {
- readyTransaction(new ReadyTransaction());
- } else if (message.getClass().equals(CloseTransaction.SERIALIZABLE_CLASS)) {
- closeTransaction(new CloseTransaction());
- } else if (message instanceof GetCompositedModification) {
- // This is here for testing only
- getSender().tell(new GetCompositeModificationReply(
- new ImmutableCompositeModification(modification)), getSelf());
- }else{
- throw new Exception ("Shard:handleRecieve received an unknown message"+message);
+ }
+
+ abstract protected void closeTransaction(CloseTransaction message);
+
+ protected void readData(DOMStoreReadTransaction transaction,ReadData message) {
+ final ActorRef sender = getSender();
+ final ActorRef self = getSelf();
+ final YangInstanceIdentifier path = message.getPath();
+ final ListenableFuture>> future =
+ transaction.read(path);
+
+ future.addListener(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ Optional> optional = future.get();
+ if (optional.isPresent()) {
+ sender.tell(new ReadDataReply(schemaContext,optional.get()).toSerializable(), self);
+ } else {
+ sender.tell(new ReadDataReply(schemaContext,null).toSerializable(), self);
+ }
+ } catch (InterruptedException | ExecutionException e) {
+ log.error(e,
+ "An exception happened when reading data from path : "
+ + path.toString());
}
- }
- private void readData(ReadData message) {
- final ActorRef sender = getSender();
- final ActorRef self = getSelf();
- final YangInstanceIdentifier path = message.getPath();
- final ListenableFuture>> future =
- transaction.read(path);
-
- future.addListener(new Runnable() {
- @Override
- public void run() {
- try {
- Optional> optional = future.get();
- if (optional.isPresent()) {
- sender.tell(new ReadDataReply(schemaContext,optional.get()).toSerializable(), self);
- } else {
- sender.tell(new ReadDataReply(schemaContext,null).toSerializable(), self);
- }
- } catch (InterruptedException | ExecutionException e) {
- log.error(e,
- "An exception happened when reading data from path : "
- + path.toString());
- }
-
- }
- }, getContext().dispatcher());
- }
+ }
+ }, getContext().dispatcher());
+ }
- private void writeData(WriteData message) {
- modification.addModification(
- new WriteModification(message.getPath(), message.getData(),schemaContext));
- LOG.debug("writeData at path : " + message.getPath().toString());
- transaction.write(message.getPath(), message.getData());
- getSender().tell(new WriteDataReply().toSerializable(), getSelf());
- }
+ protected void writeData(DOMStoreWriteTransaction transaction, WriteData message) {
+ modification.addModification(
+ new WriteModification(message.getPath(), message.getData(),schemaContext));
+ LOG.debug("writeData at path : " + message.getPath().toString());
+ transaction.write(message.getPath(), message.getData());
+ getSender().tell(new WriteDataReply().toSerializable(), getSelf());
+ }
- private void mergeData(MergeData message) {
- modification.addModification(
- new MergeModification(message.getPath(), message.getData(), schemaContext));
- LOG.debug("mergeData at path : " + message.getPath().toString());
- transaction.merge(message.getPath(), message.getData());
- getSender().tell(new MergeDataReply().toSerializable(), getSelf());
- }
+ protected void mergeData(DOMStoreWriteTransaction transaction, MergeData message) {
+ modification.addModification(
+ new MergeModification(message.getPath(), message.getData(), schemaContext));
+ LOG.debug("mergeData at path : " + message.getPath().toString());
+ transaction.merge(message.getPath(), message.getData());
+ getSender().tell(new MergeDataReply().toSerializable(), getSelf());
+ }
- private void deleteData(DeleteData message) {
- modification.addModification(new DeleteModification(message.getPath()));
- transaction.delete(message.getPath());
- getSender().tell(new DeleteDataReply().toSerializable(), getSelf());
- }
+ protected void deleteData(DOMStoreWriteTransaction transaction, DeleteData message) {
+ modification.addModification(new DeleteModification(message.getPath()));
+ transaction.delete(message.getPath());
+ getSender().tell(new DeleteDataReply().toSerializable(), getSelf());
+ }
- private void readyTransaction(ReadyTransaction message) {
- DOMStoreThreePhaseCommitCohort cohort = transaction.ready();
- ActorRef cohortActor = getContext().actorOf(
- ThreePhaseCommitCohort.props(cohort, shardActor, modification), "cohort");
- getSender()
- .tell(new ReadyTransactionReply(cohortActor.path()).toSerializable(), getSelf());
+ protected void readyTransaction(DOMStoreWriteTransaction transaction, ReadyTransaction message) {
+ DOMStoreThreePhaseCommitCohort cohort = transaction.ready();
+ ActorRef cohortActor = getContext().actorOf(
+ ThreePhaseCommitCohort.props(cohort, shardActor, modification), "cohort");
+ getSender()
+ .tell(new ReadyTransactionReply(cohortActor.path()).toSerializable(), getSelf());
- }
-
- private void closeTransaction(CloseTransaction message) {
- transaction.close();
- getSender().tell(new CloseTransactionReply().toSerializable(), getSelf());
- getSelf().tell(PoisonPill.getInstance(), getSelf());
- }
+ }
- // These classes are in here for test purposes only
+ // These classes are in here for test purposes only
- static class GetCompositedModification {
+ static class GetCompositedModification {
- }
+ }
- static class GetCompositeModificationReply {
- private final CompositeModification modification;
+ static class GetCompositeModificationReply {
+ private final CompositeModification modification;
- GetCompositeModificationReply(CompositeModification modification) {
- this.modification = modification;
- }
+ GetCompositeModificationReply(CompositeModification modification) {
+ this.modification = modification;
+ }
- public CompositeModification getModification() {
- return modification;
- }
+ public CompositeModification getModification() {
+ return modification;
}
+ }
}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardTransactionChain.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardTransactionChain.java
index 50042411b1..ce63f1107d 100644
--- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardTransactionChain.java
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardTransactionChain.java
@@ -15,7 +15,6 @@ import org.opendaylight.controller.cluster.datastore.messages.CloseTransactionCh
import org.opendaylight.controller.cluster.datastore.messages.CloseTransactionChainReply;
import org.opendaylight.controller.cluster.datastore.messages.CreateTransaction;
import org.opendaylight.controller.cluster.datastore.messages.CreateTransactionReply;
-import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
@@ -45,11 +44,27 @@ public class ShardTransactionChain extends AbstractUntypedActor {
}
}
+ private ActorRef createTypedTransactionActor(CreateTransaction createTransaction,String transactionId){
+ if(createTransaction.getTransactionType()== TransactionProxy.TransactionType.READ_ONLY.ordinal()){
+ return getContext().actorOf(
+ ShardTransaction.props( chain.newReadOnlyTransaction(), getSelf(), schemaContext), transactionId);
+
+ }else if (createTransaction.getTransactionType()== TransactionProxy.TransactionType.READ_WRITE.ordinal()){
+ return getContext().actorOf(
+ ShardTransaction.props( chain.newReadWriteTransaction(), getSelf(), schemaContext), transactionId);
+
+
+ }else if (createTransaction.getTransactionType()== TransactionProxy.TransactionType.WRITE_ONLY.ordinal()){
+ return getContext().actorOf(
+ ShardTransaction.props( chain.newWriteOnlyTransaction(), getSelf(), schemaContext), transactionId);
+ }else{
+ throw new IllegalArgumentException ("CreateTransaction message has unidentified transaction type="+createTransaction.getTransactionType()) ;
+ }
+ }
+
private void createTransaction(CreateTransaction createTransaction) {
- DOMStoreReadWriteTransaction transaction =
- chain.newReadWriteTransaction();
- ActorRef transactionActor = getContext().actorOf(ShardTransaction
- .props(chain, transaction, getContext().parent(), schemaContext), "shard-" + createTransaction.getTransactionId());
+
+ ActorRef transactionActor = createTypedTransactionActor(createTransaction, "shard-" + createTransaction.getTransactionId());
getSender()
.tell(new CreateTransactionReply(transactionActor.path().toString(),createTransaction.getTransactionId()).toSerializable(),
getSelf());
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardWriteTransaction.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardWriteTransaction.java
new file mode 100644
index 0000000000..2a5429ba81
--- /dev/null
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardWriteTransaction.java
@@ -0,0 +1,66 @@
+/*
+ *
+ * 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.cluster.datastore;
+
+import akka.actor.ActorRef;
+import akka.actor.PoisonPill;
+import akka.event.Logging;
+import akka.event.LoggingAdapter;
+import org.opendaylight.controller.cluster.datastore.messages.CloseTransaction;
+import org.opendaylight.controller.cluster.datastore.messages.CloseTransactionReply;
+import org.opendaylight.controller.cluster.datastore.messages.DeleteData;
+import org.opendaylight.controller.cluster.datastore.messages.MergeData;
+import org.opendaylight.controller.cluster.datastore.messages.ReadyTransaction;
+import org.opendaylight.controller.cluster.datastore.messages.WriteData;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * @author: syedbahm
+ * Date: 8/6/14
+ */
+public class ShardWriteTransaction extends ShardTransaction {
+ private final DOMStoreWriteTransaction transaction;
+ private final LoggingAdapter log =
+ Logging.getLogger(getContext().system(), this);
+ public ShardWriteTransaction(DOMStoreWriteTransaction transaction, ActorRef shardActor, SchemaContext schemaContext) {
+ super( shardActor, schemaContext);
+ this.transaction = transaction;
+
+ }
+
+ public ShardWriteTransaction(DOMStoreTransactionChain transactionChain, DOMStoreWriteTransaction transaction, ActorRef shardActor, SchemaContext schemaContext) {
+ super(transactionChain, shardActor, schemaContext);
+ this.transaction = transaction;
+ }
+
+ @Override
+ public void handleReceive(Object message) throws Exception {
+ if (WriteData.SERIALIZABLE_CLASS.equals(message.getClass())) {
+ writeData(transaction, WriteData.fromSerializable(message, schemaContext));
+ } else if (MergeData.SERIALIZABLE_CLASS.equals(message.getClass())) {
+ mergeData(transaction, MergeData.fromSerializable(message, schemaContext));
+ } else if (DeleteData.SERIALIZABLE_CLASS.equals(message.getClass())) {
+ deleteData(transaction,DeleteData.fromSerizalizable(message));
+ } else if (ReadyTransaction.SERIALIZABLE_CLASS.equals(message.getClass())) {
+ readyTransaction(transaction,new ReadyTransaction());
+ }else {
+ super.handleReceive(message);
+ }
+ }
+
+ protected void closeTransaction(CloseTransaction message) {
+ transaction.close();
+ getSender().tell(new CloseTransactionReply().toSerializable(), getSelf());
+ getSelf().tell(PoisonPill.getInstance(), getSelf());
+ }
+}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortProxy.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortProxy.java
index b56dc9432f..915b13dd8b 100644
--- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortProxy.java
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortProxy.java
@@ -10,8 +10,10 @@ package org.opendaylight.controller.cluster.datastore;
import akka.actor.ActorPath;
import akka.actor.ActorSelection;
+
import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.ListenableFutureTask;
+import com.google.common.util.concurrent.ListeningExecutorService;
+
import org.opendaylight.controller.cluster.datastore.exceptions.TimeoutException;
import org.opendaylight.controller.cluster.datastore.messages.AbortTransaction;
import org.opendaylight.controller.cluster.datastore.messages.AbortTransactionReply;
@@ -29,7 +31,6 @@ import org.slf4j.LoggerFactory;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutorService;
/**
* ThreePhaseCommitCohortProxy represents a set of remote cohort proxies
@@ -42,14 +43,14 @@ public class ThreePhaseCommitCohortProxy implements
private final ActorContext actorContext;
private final List cohortPaths;
- private final ExecutorService executor;
+ private final ListeningExecutorService executor;
private final String transactionId;
public ThreePhaseCommitCohortProxy(ActorContext actorContext,
List cohortPaths,
String transactionId,
- ExecutorService executor) {
+ ListeningExecutorService executor) {
this.actorContext = actorContext;
this.cohortPaths = cohortPaths;
@@ -58,42 +59,37 @@ public class ThreePhaseCommitCohortProxy implements
}
@Override public ListenableFuture canCommit() {
- Callable call = new Callable() {
-
- @Override public Boolean call() throws Exception {
- for(ActorPath actorPath : cohortPaths){
- ActorSelection cohort = actorContext.actorSelection(actorPath);
-
- try {
- Object response =
- actorContext.executeRemoteOperation(cohort,
- new CanCommitTransaction().toSerializable(),
- ActorContext.ASK_DURATION);
-
- if (response.getClass().equals(CanCommitTransactionReply.SERIALIZABLE_CLASS)) {
- CanCommitTransactionReply reply =
- CanCommitTransactionReply.fromSerializable(response);
- if (!reply.getCanCommit()) {
- return false;
+ Callable call = new Callable() {
+
+ @Override
+ public Boolean call() throws Exception {
+ for(ActorPath actorPath : cohortPaths){
+ ActorSelection cohort = actorContext.actorSelection(actorPath);
+
+ try {
+ Object response =
+ actorContext.executeRemoteOperation(cohort,
+ new CanCommitTransaction().toSerializable(),
+ ActorContext.ASK_DURATION);
+
+ if (response.getClass().equals(CanCommitTransactionReply.SERIALIZABLE_CLASS)) {
+ CanCommitTransactionReply reply =
+ CanCommitTransactionReply.fromSerializable(response);
+ if (!reply.getCanCommit()) {
+ return false;
+ }
}
+ } catch(RuntimeException e){
+ LOG.error("Unexpected Exception", e);
+ return false;
}
- } catch(RuntimeException e){
- LOG.error("Unexpected Exception", e);
- return false;
}
-
- }
- return true;
+ return true;
}
};
- ListenableFutureTask
- future = ListenableFutureTask.create(call);
-
- executor.submit(future);
-
- return future;
+ return executor.submit(call);
}
@Override public ListenableFuture preCommit() {
@@ -138,13 +134,7 @@ public class ThreePhaseCommitCohortProxy implements
}
};
- ListenableFutureTask
- future = ListenableFutureTask.create(call);
-
- executor.submit(future);
-
- return future;
-
+ return executor.submit(call);
}
public List getCohortPaths() {
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionChainProxy.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionChainProxy.java
index 2e8538d077..5e9defa5b5 100644
--- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionChainProxy.java
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionChainProxy.java
@@ -15,17 +15,18 @@ import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import java.util.concurrent.ExecutorService;
+import com.google.common.util.concurrent.ListeningExecutorService;
/**
* TransactionChainProxy acts as a proxy for a DOMStoreTransactionChain created on a remote shard
*/
public class TransactionChainProxy implements DOMStoreTransactionChain{
private final ActorContext actorContext;
- private final ExecutorService transactionExecutor;
+ private final ListeningExecutorService transactionExecutor;
private final SchemaContext schemaContext;
- public TransactionChainProxy(ActorContext actorContext, ExecutorService transactionExecutor, SchemaContext schemaContext) {
+ public TransactionChainProxy(ActorContext actorContext, ListeningExecutorService transactionExecutor,
+ SchemaContext schemaContext) {
this.actorContext = actorContext;
this.transactionExecutor = transactionExecutor;
this.schemaContext = schemaContext;
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionProxy.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionProxy.java
index c85d32012f..fa98905a66 100644
--- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionProxy.java
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionProxy.java
@@ -13,9 +13,10 @@ import akka.actor.ActorRef;
import akka.actor.ActorSelection;
import akka.actor.Props;
import com.google.common.base.Optional;
+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.ListenableFutureTask;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import org.opendaylight.controller.cluster.datastore.exceptions.PrimaryNotFoundException;
import org.opendaylight.controller.cluster.datastore.exceptions.TimeoutException;
import org.opendaylight.controller.cluster.datastore.messages.CloseTransaction;
import org.opendaylight.controller.cluster.datastore.messages.CreateTransaction;
@@ -29,8 +30,10 @@ import org.opendaylight.controller.cluster.datastore.messages.ReadyTransactionRe
import org.opendaylight.controller.cluster.datastore.messages.WriteData;
import org.opendaylight.controller.cluster.datastore.shardstrategy.ShardStrategyFactory;
import org.opendaylight.controller.cluster.datastore.utils.ActorContext;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.yangtools.util.concurrent.MappingCheckedFuture;
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;
@@ -42,7 +45,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicLong;
/**
@@ -74,13 +76,13 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction {
private final ActorContext actorContext;
private final Map remoteTransactionPaths = new HashMap<>();
private final String identifier;
- private final ExecutorService executor;
+ private final ListeningExecutorService executor;
private final SchemaContext schemaContext;
public TransactionProxy(
ActorContext actorContext,
TransactionType transactionType,
- ExecutorService executor,
+ ListeningExecutorService executor,
SchemaContext schemaContext
) {
@@ -94,7 +96,8 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction {
}
@Override
- public ListenableFuture>> read(final YangInstanceIdentifier path) {
+ public CheckedFuture>, ReadFailedException> read(
+ final YangInstanceIdentifier path) {
createTransactionIfMissing(actorContext, path);
@@ -177,7 +180,7 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction {
try {
Object response = actorContext.executeShardOperation(shardName,
- new CreateTransaction(identifier).toSerializable(),
+ new CreateTransaction(identifier,this.transactionType.ordinal() ).toSerializable(),
ActorContext.ASK_DURATION);
if (response.getClass()
.equals(CreateTransactionReply.SERIALIZABLE_CLASS)) {
@@ -196,8 +199,10 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction {
remoteTransactionPaths.put(shardName, transactionContext);
}
- } catch(TimeoutException e){
- remoteTransactionPaths.put(shardName, new NoOpTransactionContext(shardName));
+ } catch(TimeoutException | PrimaryNotFoundException e){
+ LOG.error("Creating NoOpTransaction because of : {}", e.getMessage());
+ remoteTransactionPaths.put(shardName,
+ new NoOpTransactionContext(shardName));
}
}
@@ -214,7 +219,8 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction {
void mergeData(YangInstanceIdentifier path, NormalizedNode, ?> data);
- ListenableFuture>> readData(final YangInstanceIdentifier path);
+ CheckedFuture>, ReadFailedException> readData(
+ final YangInstanceIdentifier path);
void writeData(YangInstanceIdentifier path, NormalizedNode, ?> data);
}
@@ -266,9 +272,10 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction {
getActor().tell(new MergeData(path, data, schemaContext).toSerializable(), null);
}
- @Override public ListenableFuture>> readData(final YangInstanceIdentifier path) {
+ @Override public CheckedFuture>, ReadFailedException> readData(
+ final YangInstanceIdentifier path) {
- Callable>> call = new Callable() {
+ Callable>> call = new Callable>>() {
@Override public Optional> call() throws Exception {
Object response = actorContext
@@ -279,20 +286,14 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction {
if(reply.getNormalizedNode() == null){
return Optional.absent();
}
- //FIXME : A cast should not be required here ???
- return (Optional>) Optional.of(reply.getNormalizedNode());
+ return Optional.>of(reply.getNormalizedNode());
}
return Optional.absent();
}
};
- ListenableFutureTask>>
- future = ListenableFutureTask.create(call);
-
- executor.submit(future);
-
- return future;
+ return MappingCheckedFuture.create(executor.submit(call), ReadFailedException.MAPPER);
}
@Override public void writeData(YangInstanceIdentifier path, NormalizedNode, ?> data) {
@@ -342,10 +343,10 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction {
}
@Override
- public ListenableFuture>> readData(
+ public CheckedFuture>, ReadFailedException> readData(
YangInstanceIdentifier path) {
LOG.error("readData called path = {}", path);
- return Futures.immediateFuture(
+ return Futures.immediateCheckedFuture(
Optional.>absent());
}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStats.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStats.java
index 2da6aae85f..4eb6a8cef9 100644
--- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStats.java
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStats.java
@@ -9,6 +9,8 @@ public class ShardStats extends AbstractBaseMBean implements ShardStatsMBean {
private Long committedTransactionsCount;
private Long journalMessagesCount;
final private String shardName;
+ private String leader;
+ private String raftState;
ShardStats(String shardName){
this.shardName = shardName;
@@ -33,6 +35,13 @@ public class ShardStats extends AbstractBaseMBean implements ShardStatsMBean {
return journalMessagesCount;
}
+ @Override public String getLeader() {
+ return leader;
+ }
+
+ @Override public String getRaftState() {
+ return raftState;
+ }
public Long incrementCommittedTransactionCount() {
return committedTransactionsCount++;
@@ -49,6 +58,13 @@ public class ShardStats extends AbstractBaseMBean implements ShardStatsMBean {
}
+ public void setLeader(String leader){
+ this.leader = leader;
+ }
+
+ public void setRaftState(String raftState){
+ this.raftState = raftState;
+ }
@Override
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStatsMBean.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStatsMBean.java
index c107e49e85..9ebcc7fa5a 100644
--- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStatsMBean.java
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStatsMBean.java
@@ -7,5 +7,6 @@ public interface ShardStatsMBean {
String getShardName();
Long getCommittedTransactionsCount();
Long getJournalMessagesCount();
-
+ String getLeader();
+ String getRaftState();
}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/CreateTransaction.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/CreateTransaction.java
index 795131fdbf..b27ad86be9 100644
--- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/CreateTransaction.java
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/CreateTransaction.java
@@ -15,23 +15,28 @@ import org.opendaylight.controller.protobuff.messages.transaction.ShardTransacti
public class CreateTransaction implements SerializableMessage {
public static Class SERIALIZABLE_CLASS = ShardTransactionMessages.CreateTransaction.class;
private final String transactionId;
+ private final int transactionType;
- public CreateTransaction(String transactionId){
+ public CreateTransaction(String transactionId, int transactionType){
this.transactionId = transactionId;
+ this.transactionType = transactionType;
}
public String getTransactionId() {
return transactionId;
}
+ public int getTransactionType() { return transactionType;}
+
@Override
public Object toSerializable() {
- return ShardTransactionMessages.CreateTransaction.newBuilder().setTransactionId(transactionId).build();
+ return ShardTransactionMessages.CreateTransaction.newBuilder().setTransactionId(transactionId).setTransactionType(transactionType).build();
}
public static CreateTransaction fromSerializable(Object message){
- return new CreateTransaction(((ShardTransactionMessages.CreateTransaction)message).getTransactionId());
+ ShardTransactionMessages.CreateTransaction createTransaction = (ShardTransactionMessages.CreateTransaction)message;
+ return new CreateTransaction(createTransaction.getTransactionId(),createTransaction.getTransactionType() );
}
}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/EnableNotification.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/EnableNotification.java
new file mode 100644
index 0000000000..67dab7e663
--- /dev/null
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/EnableNotification.java
@@ -0,0 +1,21 @@
+/*
+ * 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.cluster.datastore.messages;
+
+public class EnableNotification {
+ private final boolean enabled;
+
+ public EnableNotification(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/FindLocalShard.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/FindLocalShard.java
new file mode 100644
index 0000000000..c415db6efe
--- /dev/null
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/FindLocalShard.java
@@ -0,0 +1,25 @@
+/*
+ * 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.cluster.datastore.messages;
+
+/**
+ * FindLocalShard is a message that should be sent to the {@link org.opendaylight.controller.cluster.datastore.ShardManager}
+ * when we need to find a reference to a LocalShard
+ */
+public class FindLocalShard {
+ private final String shardName;
+
+ public FindLocalShard(String shardName) {
+ this.shardName = shardName;
+ }
+
+ public String getShardName() {
+ return shardName;
+ }
+}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/LocalShardFound.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/LocalShardFound.java
new file mode 100644
index 0000000000..feea38f3e3
--- /dev/null
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/LocalShardFound.java
@@ -0,0 +1,27 @@
+/*
+ * 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.cluster.datastore.messages;
+
+import akka.actor.ActorRef;
+
+/**
+ * LocalShardFound is a message that is sent by the {@link org.opendaylight.controller.cluster.datastore.ShardManager}
+ * when it finds a shard with the specified name in it's local shard registry
+ */
+public class LocalShardFound {
+ private final ActorRef path;
+
+ public LocalShardFound(ActorRef path) {
+ this.path = path;
+ }
+
+ public ActorRef getPath() {
+ return path;
+ }
+}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/LocalShardNotFound.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/LocalShardNotFound.java
new file mode 100644
index 0000000000..f6c6634e1a
--- /dev/null
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/LocalShardNotFound.java
@@ -0,0 +1,29 @@
+/*
+ * 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.cluster.datastore.messages;
+
+/**
+ * LocalShardNotFound is a message that is sent by the {@link org.opendaylight.controller.cluster.datastore.ShardManager}
+ * when it cannot locate a shard in it's local registry with the shardName specified
+ */
+public class LocalShardNotFound {
+ private final String shardName;
+
+ /**
+ *
+ * @param shardName the name of the shard that could not be found
+ */
+ public LocalShardNotFound(String shardName) {
+ this.shardName = shardName;
+ }
+
+ public String getShardName() {
+ return shardName;
+ }
+}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/shardstrategy/ModuleShardStrategy.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/shardstrategy/ModuleShardStrategy.java
index 6f4b65a6f3..fc7ebd94dd 100644
--- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/shardstrategy/ModuleShardStrategy.java
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/shardstrategy/ModuleShardStrategy.java
@@ -11,6 +11,8 @@ package org.opendaylight.controller.cluster.datastore.shardstrategy;
import org.opendaylight.controller.cluster.datastore.Configuration;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import java.util.List;
+
public class ModuleShardStrategy implements ShardStrategy {
public static final String NAME = "module";
@@ -25,6 +27,11 @@ public class ModuleShardStrategy implements ShardStrategy {
}
@Override public String findShard(YangInstanceIdentifier path) {
- return configuration.getShardNamesFromModuleName(moduleName).get(0);
+ List shardNames =
+ configuration.getShardNamesFromModuleName(moduleName);
+ if(shardNames.size() == 0){
+ return DefaultShardStrategy.DEFAULT_SHARD;
+ }
+ return shardNames.get(0);
}
}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/shardstrategy/ShardStrategy.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/shardstrategy/ShardStrategy.java
index 2df945edd5..9a05c381ea 100644
--- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/shardstrategy/ShardStrategy.java
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/shardstrategy/ShardStrategy.java
@@ -16,6 +16,9 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
public interface ShardStrategy {
/**
* Find the name of the shard in which the data pointed to by the specified path belongs in
+ *
+ * Should return the name of the default shard DefaultShardStrategy.DEFAULT_SHARD
+ * if no matching shard was found
*
* @param path The location of the data in the logical tree
* @return
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/ActorContext.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/ActorContext.java
index ac0893da5a..4706c66e25 100644
--- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/ActorContext.java
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/ActorContext.java
@@ -18,7 +18,9 @@ import org.opendaylight.controller.cluster.datastore.ClusterWrapper;
import org.opendaylight.controller.cluster.datastore.Configuration;
import org.opendaylight.controller.cluster.datastore.exceptions.PrimaryNotFoundException;
import org.opendaylight.controller.cluster.datastore.exceptions.TimeoutException;
+import org.opendaylight.controller.cluster.datastore.messages.FindLocalShard;
import org.opendaylight.controller.cluster.datastore.messages.FindPrimary;
+import org.opendaylight.controller.cluster.datastore.messages.LocalShardFound;
import org.opendaylight.controller.cluster.datastore.messages.PrimaryFound;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.slf4j.Logger;
@@ -91,6 +93,29 @@ public class ActorContext {
return actorSystem.actorSelection(path);
}
+ /**
+ * Finds a local shard given it's shard name and return it's ActorRef
+ *
+ * @param shardName the name of the local shard that needs to be found
+ * @return a reference to a local shard actor which represents the shard
+ * specified by the shardName
+ */
+ public ActorRef findLocalShard(String shardName) {
+ Object result = executeLocalOperation(shardManager,
+ new FindLocalShard(shardName), ASK_DURATION);
+
+ if (result instanceof LocalShardFound) {
+ LocalShardFound found = (LocalShardFound) result;
+
+ LOG.debug("Local shard found {}", found.getPath());
+
+ return found.getPath();
+ }
+
+ return null;
+ }
+
+
public String findPrimaryPath(String shardName) {
Object result = executeLocalOperation(shardManager,
new FindPrimary(shardName).toSerializable(), ASK_DURATION);
@@ -170,6 +195,34 @@ public class ActorContext {
return executeRemoteOperation(primary, message, duration);
}
+ /**
+ * Execute an operation on the the local shard only
+ *
+ * This method first finds the address of the local shard if any. It then
+ * executes the operation on it.
+ *
+ *
+ * @param shardName the name of the shard on which the operation needs to be executed
+ * @param message the message that needs to be sent to the shard
+ * @param duration the time duration in which this operation should complete
+ * @return the message that was returned by the local actor on which the
+ * the operation was executed. If a local shard was not found then
+ * null is returned
+ * @throws org.opendaylight.controller.cluster.datastore.exceptions.TimeoutException
+ * if the operation does not complete in a specified time duration
+ */
+ public Object executeLocalShardOperation(String shardName, Object message,
+ FiniteDuration duration) {
+ ActorRef local = findLocalShard(shardName);
+
+ if(local != null) {
+ return executeLocalOperation(local, message, duration);
+ }
+
+ return null;
+ }
+
+
public void shutdown() {
shardManager.tell(PoisonPill.getInstance(), null);
actorSystem.shutdown();
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/resources/application.conf b/opendaylight/md-sal/sal-distributed-datastore/src/main/resources/application.conf
index 76914c2c84..daac89c4c8 100644
--- a/opendaylight/md-sal/sal-distributed-datastore/src/main/resources/application.conf
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/resources/application.conf
@@ -1,15 +1,60 @@
-ODLCluster{
-actor {
- serializers {
- java = "akka.serialization.JavaSerializer"
- proto = "akka.remote.serialization.ProtobufSerializer"
- }
+odl-cluster-data {
+ akka {
+ cluster {
+ roles = [
+ "member-1"
+ ]
+ }
+ actor {
+ provider = "akka.cluster.ClusterActorRefProvider"
+ serializers {
+ java = "akka.serialization.JavaSerializer"
+ proto = "akka.remote.serialization.ProtobufSerializer"
+ }
+
+ serialization-bindings {
+ "com.google.protobuf.Message" = proto
+
+ }
+ }
+ remote {
+ log-remote-lifecycle-events = off
+ netty.tcp {
+ hostname = "127.0.0.1"
+ port = 2550
+ maximum-frame-size = 2097152
+ send-buffer-size = 52428800
+ receive-buffer-size = 52428800
+ }
+ }
- serialization-bindings {
- "com.google.protobuf.Message" = proto
+ cluster {
+ seed-nodes = ["akka.tcp://opendaylight-cluster-data@127.0.0.1:2550"]
- }
+ auto-down-unreachable-after = 10s
}
+ }
+}
+
+odl-cluster-rpc {
+ akka {
+ actor {
+ provider = "akka.cluster.ClusterActorRefProvider"
-}
\ No newline at end of file
+ }
+ remote {
+ log-remote-lifecycle-events = off
+ netty.tcp {
+ hostname = "127.0.0.1"
+ port = 2551
+ }
+ }
+
+ cluster {
+ seed-nodes = ["akka.tcp://opendaylight-cluster-rpc@127.0.0.1:2551"]
+
+ auto-down-unreachable-after = 10s
+ }
+ }
+}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/resources/modules.conf b/opendaylight/md-sal/sal-distributed-datastore/src/main/resources/modules.conf
index 05ef33f759..e820703eeb 100644
--- a/opendaylight/md-sal/sal-distributed-datastore/src/main/resources/modules.conf
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/resources/modules.conf
@@ -1,7 +1,7 @@
modules = [
{
name = "inventory"
- namespace = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test:people"
+ namespace = "urn:opendaylight:inventory"
shard-strategy = "module"
}
]
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/BasicIntegrationTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/BasicIntegrationTest.java
index 11ad559744..6599bd8eeb 100644
--- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/BasicIntegrationTest.java
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/BasicIntegrationTest.java
@@ -12,6 +12,7 @@ import akka.actor.ActorPath;
import akka.actor.ActorRef;
import akka.actor.ActorSelection;
import akka.actor.Props;
+import akka.event.Logging;
import akka.testkit.JavaTestKit;
import junit.framework.Assert;
import org.junit.Test;
@@ -35,6 +36,8 @@ import scala.concurrent.duration.FiniteDuration;
import java.util.Collections;
+import static junit.framework.Assert.assertEquals;
+
public class BasicIntegrationTest extends AbstractActorTest {
@Test
@@ -61,17 +64,24 @@ public class BasicIntegrationTest extends AbstractActorTest {
getRef());
- // Wait for Shard to become a Leader
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
+ // Wait for a specific log message to show up
+ final boolean result =
+ new JavaTestKit.EventFilter(Logging.Info.class
+ ) {
+ protected Boolean run() {
+ return true;
+ }
+ }.from(shard.path().toString())
+ .message("Switching from state Candidate to Leader")
+ .occurrences(1).exec();
+
+ assertEquals(true, result);
+
// 1. Create a TransactionChain
shard.tell(new CreateTransactionChain().toSerializable(), getRef());
final ActorSelection transactionChain =
- new ExpectMsg("CreateTransactionChainReply") {
+ new ExpectMsg(duration("1 seconds"), "CreateTransactionChainReply") {
protected ActorSelection match(Object in) {
if (in.getClass().equals(CreateTransactionChainReply.SERIALIZABLE_CLASS)) {
ActorPath transactionChainPath =
@@ -90,10 +100,10 @@ public class BasicIntegrationTest extends AbstractActorTest {
System.out.println("Successfully created transaction chain");
// 2. Create a Transaction on the TransactionChain
- transactionChain.tell(new CreateTransaction("txn-1").toSerializable(), getRef());
+ transactionChain.tell(new CreateTransaction("txn-1", TransactionProxy.TransactionType.WRITE_ONLY.ordinal() ).toSerializable(), getRef());
final ActorSelection transaction =
- new ExpectMsg("CreateTransactionReply") {
+ new ExpectMsg(duration("1 seconds"), "CreateTransactionReply") {
protected ActorSelection match(Object in) {
if (CreateTransactionReply.SERIALIZABLE_CLASS.equals(in.getClass())) {
CreateTransactionReply reply = CreateTransactionReply.fromSerializable(in);
@@ -115,7 +125,7 @@ public class BasicIntegrationTest extends AbstractActorTest {
ImmutableNodes.containerNode(TestModel.TEST_QNAME), TestModel.createTestContext()).toSerializable(),
getRef());
- Boolean writeDone = new ExpectMsg("WriteDataReply") {
+ Boolean writeDone = new ExpectMsg(duration("1 seconds"), "WriteDataReply") {
protected Boolean match(Object in) {
if (in.getClass().equals(WriteDataReply.SERIALIZABLE_CLASS)) {
return true;
@@ -134,7 +144,7 @@ public class BasicIntegrationTest extends AbstractActorTest {
transaction.tell(new ReadyTransaction().toSerializable(), getRef());
final ActorSelection cohort =
- new ExpectMsg("ReadyTransactionReply") {
+ new ExpectMsg(duration("1 seconds"), "ReadyTransactionReply") {
protected ActorSelection match(Object in) {
if (in.getClass().equals(ReadyTransactionReply.SERIALIZABLE_CLASS)) {
ActorPath cohortPath =
@@ -157,7 +167,7 @@ public class BasicIntegrationTest extends AbstractActorTest {
cohort.tell(new PreCommitTransaction().toSerializable(), getRef());
Boolean preCommitDone =
- new ExpectMsg("PreCommitTransactionReply") {
+ new ExpectMsg(duration("1 seconds"), "PreCommitTransactionReply") {
protected Boolean match(Object in) {
if (in.getClass().equals(PreCommitTransactionReply.SERIALIZABLE_CLASS)) {
return true;
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/CompositeModificationPayloadTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/CompositeModificationPayloadTest.java
new file mode 100644
index 0000000000..400eab1d8e
--- /dev/null
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/CompositeModificationPayloadTest.java
@@ -0,0 +1,84 @@
+package org.opendaylight.controller.cluster.datastore;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Test;
+import org.opendaylight.controller.cluster.datastore.modification.MutableCompositeModification;
+import org.opendaylight.controller.cluster.datastore.modification.WriteModification;
+import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry;
+import org.opendaylight.controller.cluster.raft.messages.AppendEntries;
+import org.opendaylight.controller.cluster.raft.protobuff.client.messages.Payload;
+import org.opendaylight.controller.cluster.raft.protobuff.messages.AppendEntriesMessages;
+import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class CompositeModificationPayloadTest {
+
+
+ private static final String SERIALIZE_OUT = "serialize.out";
+
+ @After
+ public void shutDown(){
+ File f = new File(SERIALIZE_OUT);
+ if(f.exists()){
+ f.delete();
+ }
+ }
+
+ @Test
+ public void testBasic() throws IOException {
+
+ List entries = new ArrayList<>();
+
+ entries.add(0, new ReplicatedLogEntry() {
+ @Override public Payload getData() {
+ WriteModification writeModification =
+ new WriteModification(TestModel.TEST_PATH, ImmutableNodes
+ .containerNode(TestModel.TEST_QNAME),
+ TestModel.createTestContext());
+
+ MutableCompositeModification compositeModification =
+ new MutableCompositeModification();
+
+ compositeModification.addModification(writeModification);
+
+ return new CompositeModificationPayload(compositeModification.toSerializable());
+ }
+
+ @Override public long getTerm() {
+ return 1;
+ }
+
+ @Override public long getIndex() {
+ return 1;
+ }
+ });
+
+ AppendEntries appendEntries =
+ new AppendEntries(1, "member-1", 0, 100, entries, 1);
+
+ AppendEntriesMessages.AppendEntries o = (AppendEntriesMessages.AppendEntries) appendEntries.toSerializable();
+
+ o.writeDelimitedTo(new FileOutputStream(SERIALIZE_OUT));
+
+ AppendEntriesMessages.AppendEntries appendEntries2 =
+ AppendEntriesMessages.AppendEntries
+ .parseDelimitedFrom(new FileInputStream(SERIALIZE_OUT));
+
+ AppendEntries appendEntries1 = AppendEntries.fromSerializable(appendEntries2);
+
+ Payload data = appendEntries1.getEntries().get(0).getData();
+
+
+ Assert.assertTrue(((CompositeModificationPayload) data).getModification().toString().contains(TestModel.TEST_QNAME.getNamespace().toString()));
+
+ }
+
+}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataChangeListenerProxyTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataChangeListenerProxyTest.java
index 8c1cbbbba0..b2ee4a49fe 100644
--- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataChangeListenerProxyTest.java
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataChangeListenerProxyTest.java
@@ -94,7 +94,7 @@ public class DataChangeListenerProxyTest extends AbstractActorTest {
Assert.assertEquals(1, listMessages.size());
- Assert.assertTrue(listMessages.get(0).getClass().equals(DataChanged.SERIALIZABLE_CLASS));
+ Assert.assertTrue(listMessages.get(0).getClass().equals(DataChanged.class));
}
}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataChangeListenerRegistrationTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataChangeListenerRegistrationTest.java
index 8413bac3a7..920248521a 100644
--- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataChangeListenerRegistrationTest.java
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataChangeListenerRegistrationTest.java
@@ -41,7 +41,7 @@ public class DataChangeListenerRegistrationTest extends AbstractActorTest {
subject.tell(new CloseDataChangeListenerRegistration().toSerializable(), getRef());
- final String out = new ExpectMsg("match hint") {
+ final String out = new ExpectMsg(duration("1 seconds"), "match hint") {
// do not put code outside this method, will run afterwards
protected String match(Object in) {
if (in.getClass().equals(CloseDataChangeListenerRegistrationReply.SERIALIZABLE_CLASS)) {
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataChangeListenerTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataChangeListenerTest.java
index fd61032220..26ec583b3e 100644
--- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataChangeListenerTest.java
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataChangeListenerTest.java
@@ -6,6 +6,7 @@ import akka.testkit.JavaTestKit;
import org.junit.Test;
import org.opendaylight.controller.cluster.datastore.messages.DataChanged;
import org.opendaylight.controller.cluster.datastore.messages.DataChangedReply;
+import org.opendaylight.controller.cluster.datastore.messages.EnableNotification;
import org.opendaylight.controller.md.cluster.datastore.model.CompositeModel;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
@@ -86,24 +87,28 @@ public class DataChangeListenerTest extends AbstractActorTest {
}
@Test
- public void testDataChanged(){
+ public void testDataChangedWhenNotificationsAreEnabled(){
new JavaTestKit(getSystem()) {{
final MockDataChangeListener listener = new MockDataChangeListener();
final Props props = DataChangeListener.props(CompositeModel.createTestContext(),listener,CompositeModel.FAMILY_PATH );
final ActorRef subject =
- getSystem().actorOf(props, "testDataChanged");
+ getSystem().actorOf(props, "testDataChangedNotificationsEnabled");
new Within(duration("1 seconds")) {
protected void run() {
+ // Let the DataChangeListener know that notifications should
+ // be enabled
+ subject.tell(new EnableNotification(true), getRef());
+
subject.tell(
- new DataChanged(CompositeModel.createTestContext(),new MockDataChangedEvent()).toSerializable(),
+ new DataChanged(CompositeModel.createTestContext(),new MockDataChangedEvent()),
getRef());
- final Boolean out = new ExpectMsg("dataChanged") {
+ final Boolean out = new ExpectMsg(duration("800 millis"), "dataChanged") {
// do not put code outside this method, will run afterwards
protected Boolean match(Object in) {
- if (in.getClass().equals(DataChangedReply.SERIALIZABLE_CLASS)) {
+ if (in != null && in.getClass().equals(DataChangedReply.class)) {
return true;
} else {
@@ -115,7 +120,30 @@ public class DataChangeListenerTest extends AbstractActorTest {
assertTrue(out);
assertTrue(listener.gotIt());
assertNotNull(listener.getChange().getCreatedData());
- // Will wait for the rest of the 3 seconds
+
+ expectNoMsg();
+ }
+
+
+ };
+ }};
+ }
+
+ @Test
+ public void testDataChangedWhenNotificationsAreDisabled(){
+ new JavaTestKit(getSystem()) {{
+ final MockDataChangeListener listener = new MockDataChangeListener();
+ final Props props = DataChangeListener.props(CompositeModel.createTestContext(),listener,CompositeModel.FAMILY_PATH );
+ final ActorRef subject =
+ getSystem().actorOf(props, "testDataChangedNotificationsDisabled");
+
+ new Within(duration("1 seconds")) {
+ protected void run() {
+
+ subject.tell(
+ new DataChanged(CompositeModel.createTestContext(),new MockDataChangedEvent()),
+ getRef());
+
expectNoMsg();
}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreIntegrationTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreIntegrationTest.java
index b5e3d24ef6..fc527b6bff 100644
--- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreIntegrationTest.java
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreIntegrationTest.java
@@ -1,9 +1,12 @@
package org.opendaylight.controller.cluster.datastore;
import akka.actor.ActorSystem;
+import akka.event.Logging;
import akka.testkit.JavaTestKit;
import com.google.common.base.Optional;
import com.google.common.util.concurrent.ListenableFuture;
+import junit.framework.Assert;
+import org.apache.commons.io.FileUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -18,17 +21,29 @@ import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCoh
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import java.io.File;
+import java.io.IOException;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
-public class DistributedDataStoreIntegrationTest{
+public class DistributedDataStoreIntegrationTest {
private static ActorSystem system;
@Before
- public void setUp() {
+ public void setUp() throws IOException {
+ File journal = new File("journal");
+
+ if(journal.exists()) {
+ FileUtils.deleteDirectory(journal);
+ }
+
+
System.setProperty("shard.persistent", "false");
system = ActorSystem.create("test");
}
@@ -45,79 +60,152 @@ public class DistributedDataStoreIntegrationTest{
@Test
public void integrationTest() throws Exception {
- Configuration configuration = new ConfigurationImpl("module-shards.conf", "modules.conf");
+ final Configuration configuration = new ConfigurationImpl("module-shards.conf", "modules.conf");
ShardStrategyFactory.setConfiguration(configuration);
- DistributedDataStore distributedDataStore =
- new DistributedDataStore(getSystem(), "config", new MockClusterWrapper(), configuration);
- distributedDataStore.onGlobalContextUpdated(TestModel.createTestContext());
- Thread.sleep(1000);
- DOMStoreReadWriteTransaction transaction =
- distributedDataStore.newReadWriteTransaction();
+ new JavaTestKit(getSystem()) {
+ {
+
+ new Within(duration("10 seconds")) {
+ protected void run() {
+ try {
+ final DistributedDataStore distributedDataStore =
+ new DistributedDataStore(getSystem(), "config", new MockClusterWrapper(), configuration);
+
+ distributedDataStore.onGlobalContextUpdated(TestModel.createTestContext());
+
+ // Wait for a specific log message to show up
+ final boolean result =
+ new JavaTestKit.EventFilter(Logging.Info.class
+ ) {
+ protected Boolean run() {
+ return true;
+ }
+ }.from("akka://test/user/shardmanager-config/member-1-shard-test-1-config")
+ .message("Switching from state Candidate to Leader")
+ .occurrences(1).exec();
+
+ assertEquals(true, result);
+
+ DOMStoreReadWriteTransaction transaction =
+ distributedDataStore.newReadWriteTransaction();
- transaction.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+ transaction
+ .write(TestModel.TEST_PATH, ImmutableNodes
+ .containerNode(TestModel.TEST_QNAME));
- ListenableFuture>> future =
- transaction.read(TestModel.TEST_PATH);
+ ListenableFuture>>
+ future =
+ transaction.read(TestModel.TEST_PATH);
- Optional> optional = future.get();
+ Optional> optional =
+ future.get();
- NormalizedNode, ?> normalizedNode = optional.get();
+ Assert.assertTrue("Node not found", optional.isPresent());
- assertEquals(TestModel.TEST_QNAME, normalizedNode.getNodeType());
+ NormalizedNode, ?> normalizedNode =
+ optional.get();
- DOMStoreThreePhaseCommitCohort ready = transaction.ready();
+ assertEquals(TestModel.TEST_QNAME,
+ normalizedNode.getNodeType());
- ListenableFuture canCommit = ready.canCommit();
+ DOMStoreThreePhaseCommitCohort ready =
+ transaction.ready();
- assertTrue(canCommit.get());
+ ListenableFuture canCommit =
+ ready.canCommit();
- ListenableFuture preCommit = ready.preCommit();
+ assertTrue(canCommit.get(5, TimeUnit.SECONDS));
- preCommit.get();
+ ListenableFuture preCommit =
+ ready.preCommit();
- ListenableFuture commit = ready.commit();
+ preCommit.get(5, TimeUnit.SECONDS);
- commit.get();
+ ListenableFuture commit = ready.commit();
+
+ commit.get(5, TimeUnit.SECONDS);
+ } catch (ExecutionException | TimeoutException | InterruptedException e){
+ fail(e.getMessage());
+ }
+ }
+ };
+ }
+ };
}
- @Test
+ //FIXME : Disabling test because it's flaky
+ //@Test
public void integrationTestWithMultiShardConfiguration()
- throws ExecutionException, InterruptedException {
- Configuration configuration = new ConfigurationImpl("module-shards.conf", "modules.conf");
+ throws ExecutionException, InterruptedException, TimeoutException {
+ final Configuration configuration = new ConfigurationImpl("module-shards.conf", "modules.conf");
ShardStrategyFactory.setConfiguration(configuration);
- DistributedDataStore distributedDataStore =
- new DistributedDataStore(getSystem(), "config", new MockClusterWrapper(), configuration);
+
+ new JavaTestKit(getSystem()) {
+ {
+
+ new Within(duration("10 seconds")) {
+ protected void run() {
+ try {
+ final DistributedDataStore distributedDataStore =
+ new DistributedDataStore(getSystem(), "config",
+ new MockClusterWrapper(), configuration);
+
+ distributedDataStore.onGlobalContextUpdated(
+ SchemaContextHelper.full());
+
+ // Wait for a specific log message to show up
+ final boolean result =
+ new JavaTestKit.EventFilter(
+ Logging.Info.class
+ ) {
+ protected Boolean run() {
+ return true;
+ }
+ }.from(
+ "akka://test/user/shardmanager-config/member-1-shard-cars-1-config")
+ .message(
+ "Switching from state Candidate to Leader")
+ .occurrences(1)
+ .exec();
+
+ Thread.sleep(1000);
- distributedDataStore.onGlobalContextUpdated(SchemaContextHelper.full());
+ DOMStoreReadWriteTransaction transaction =
+ distributedDataStore.newReadWriteTransaction();
- Thread.sleep(1000);
+ transaction.write(CarsModel.BASE_PATH, CarsModel.emptyContainer());
+ transaction.write(PeopleModel.BASE_PATH, PeopleModel.emptyContainer());
- DOMStoreReadWriteTransaction transaction =
- distributedDataStore.newReadWriteTransaction();
+ DOMStoreThreePhaseCommitCohort ready = transaction.ready();
- transaction.write(CarsModel.BASE_PATH, CarsModel.emptyContainer());
- transaction.write(PeopleModel.BASE_PATH, PeopleModel.emptyContainer());
+ ListenableFuture canCommit = ready.canCommit();
- DOMStoreThreePhaseCommitCohort ready = transaction.ready();
+ assertTrue(canCommit.get(5, TimeUnit.SECONDS));
- ListenableFuture canCommit = ready.canCommit();
+ ListenableFuture preCommit = ready.preCommit();
- assertTrue(canCommit.get());
+ preCommit.get(5, TimeUnit.SECONDS);
- ListenableFuture preCommit = ready.preCommit();
+ ListenableFuture commit = ready.commit();
- preCommit.get();
+ commit.get(5, TimeUnit.SECONDS);
- ListenableFuture commit = ready.commit();
+ assertEquals(true, result);
+ } catch(ExecutionException | TimeoutException | InterruptedException e){
+ fail(e.getMessage());
+ }
+ }
+ };
+ }
+ };
- commit.get();
}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreTest.java
index 23a1ed4931..d1beab9049 100644
--- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreTest.java
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreTest.java
@@ -54,8 +54,8 @@ public class DistributedDataStoreTest extends AbstractActorTest{
}
@org.junit.Test
- public void testRegisterChangeListener() throws Exception {
- mockActorContext.setExecuteShardOperationResponse(new RegisterChangeListenerReply(doNothingActorRef.path()).toSerializable());
+ public void testRegisterChangeListenerWhenShardIsNotLocal() throws Exception {
+
ListenerRegistration registration =
distributedDataStore.registerChangeListener(TestModel.TEST_PATH, new AsyncDataChangeListener>() {
@Override
@@ -64,9 +64,31 @@ public class DistributedDataStoreTest extends AbstractActorTest{
}
}, AsyncDataBroker.DataChangeScope.BASE);
+ // Since we do not expect the shard to be local registration will return a NoOpRegistration
+ Assert.assertTrue(registration instanceof NoOpDataChangeListenerRegistration);
+
+ Assert.assertNotNull(registration);
+ }
+
+ @org.junit.Test
+ public void testRegisterChangeListenerWhenShardIsLocal() throws Exception {
+
+ mockActorContext.setExecuteLocalShardOperationResponse(new RegisterChangeListenerReply(doNothingActorRef.path()));
+
+ ListenerRegistration registration =
+ distributedDataStore.registerChangeListener(TestModel.TEST_PATH, new AsyncDataChangeListener>() {
+ @Override
+ public void onDataChanged(AsyncDataChangeEvent> change) {
+ throw new UnsupportedOperationException("onDataChanged");
+ }
+ }, AsyncDataBroker.DataChangeScope.BASE);
+
+ Assert.assertTrue(registration instanceof DataChangeListenerRegistrationProxy);
+
Assert.assertNotNull(registration);
}
+
@org.junit.Test
public void testCreateTransactionChain() throws Exception {
final DOMStoreTransactionChain transactionChain = distributedDataStore.createTransactionChain();
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardManagerTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardManagerTest.java
index 87d257a3f2..e9ad450ed8 100644
--- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardManagerTest.java
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardManagerTest.java
@@ -1,5 +1,6 @@
package org.opendaylight.controller.cluster.datastore;
+import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.testkit.JavaTestKit;
@@ -8,13 +9,19 @@ import junit.framework.Assert;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
+import org.opendaylight.controller.cluster.datastore.messages.FindLocalShard;
import org.opendaylight.controller.cluster.datastore.messages.FindPrimary;
+import org.opendaylight.controller.cluster.datastore.messages.LocalShardFound;
+import org.opendaylight.controller.cluster.datastore.messages.LocalShardNotFound;
import org.opendaylight.controller.cluster.datastore.messages.PrimaryFound;
import org.opendaylight.controller.cluster.datastore.messages.PrimaryNotFound;
import org.opendaylight.controller.cluster.datastore.utils.MockClusterWrapper;
import org.opendaylight.controller.cluster.datastore.utils.MockConfiguration;
import scala.concurrent.duration.Duration;
+import static junit.framework.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
public class ShardManagerTest {
private static ActorSystem system;
@@ -47,7 +54,6 @@ public class ShardManagerTest {
expectMsgEquals(Duration.Zero(),
new PrimaryNotFound("inventory").toSerializable());
- // Will wait for the rest of the 3 seconds
expectNoMsg();
}
};
@@ -64,13 +70,81 @@ public class ShardManagerTest {
final TestActorRef subject =
TestActorRef.create(system, props);
- // the run() method needs to finish within 3 seconds
new Within(duration("1 seconds")) {
protected void run() {
subject.tell(new FindPrimary(Shard.DEFAULT_NAME).toSerializable(), getRef());
- expectMsgClass(PrimaryFound.SERIALIZABLE_CLASS);
+ expectMsgClass(duration("1 seconds"), PrimaryFound.SERIALIZABLE_CLASS);
+
+ expectNoMsg();
+ }
+ };
+ }};
+ }
+
+ @Test
+ public void testOnReceiveFindLocalShardForNonExistentShard() throws Exception {
+
+ new JavaTestKit(system) {{
+ final Props props = ShardManager
+ .props("config", new MockClusterWrapper(),
+ new MockConfiguration());
+ final TestActorRef subject =
+ TestActorRef.create(system, props);
+
+ new Within(duration("1 seconds")) {
+ protected void run() {
+
+ subject.tell(new FindLocalShard("inventory"), getRef());
+
+ final String out = new ExpectMsg(duration("1 seconds"), "find local") {
+ protected String match(Object in) {
+ if (in instanceof LocalShardNotFound) {
+ return ((LocalShardNotFound) in).getShardName();
+ } else {
+ throw noMatch();
+ }
+ }
+ }.get(); // this extracts the received message
+
+ assertEquals("inventory", out);
+
+ expectNoMsg();
+ }
+ };
+ }};
+ }
+
+ @Test
+ public void testOnReceiveFindLocalShardForExistentShard() throws Exception {
+
+ final MockClusterWrapper mockClusterWrapper = new MockClusterWrapper();
+
+ new JavaTestKit(system) {{
+ final Props props = ShardManager
+ .props("config", mockClusterWrapper,
+ new MockConfiguration());
+ final TestActorRef subject =
+ TestActorRef.create(system, props);
+
+ new Within(duration("1 seconds")) {
+ protected void run() {
+
+ subject.tell(new FindLocalShard(Shard.DEFAULT_NAME), getRef());
+
+ final ActorRef out = new ExpectMsg(duration("1 seconds"), "find local") {
+ protected ActorRef match(Object in) {
+ if (in instanceof LocalShardFound) {
+ return ((LocalShardFound) in).getPath();
+ } else {
+ throw noMatch();
+ }
+ }
+ }.get(); // this extracts the received message
+
+ assertTrue(out.path().toString(), out.path().toString().contains("member-1-shard-default-config"));
+
expectNoMsg();
}
@@ -96,7 +170,7 @@ public class ShardManagerTest {
subject.tell(new FindPrimary("astronauts").toSerializable(), getRef());
- final String out = new ExpectMsg("primary found") {
+ final String out = new ExpectMsg(duration("1 seconds"), "primary found") {
// do not put code outside this method, will run afterwards
protected String match(Object in) {
if (in.getClass().equals(PrimaryFound.SERIALIZABLE_CLASS)) {
@@ -134,13 +208,13 @@ public class ShardManagerTest {
subject.tell(new FindPrimary("astronauts").toSerializable(), getRef());
- expectMsgClass(PrimaryFound.SERIALIZABLE_CLASS);
+ expectMsgClass(duration("1 seconds"), PrimaryFound.SERIALIZABLE_CLASS);
MockClusterWrapper.sendMemberRemoved(subject, "member-2", getRef().path().toString());
subject.tell(new FindPrimary("astronauts").toSerializable(), getRef());
- expectMsgClass(PrimaryNotFound.SERIALIZABLE_CLASS);
+ expectMsgClass(duration("1 seconds"), PrimaryNotFound.SERIALIZABLE_CLASS);
expectNoMsg();
}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTest.java
index 7d57ea8284..431a266b14 100644
--- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTest.java
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTest.java
@@ -2,11 +2,14 @@ package org.opendaylight.controller.cluster.datastore;
import akka.actor.ActorRef;
import akka.actor.Props;
+import akka.event.Logging;
import akka.testkit.JavaTestKit;
+import junit.framework.Assert;
import org.junit.Test;
import org.opendaylight.controller.cluster.datastore.messages.CreateTransaction;
import org.opendaylight.controller.cluster.datastore.messages.CreateTransactionChain;
import org.opendaylight.controller.cluster.datastore.messages.CreateTransactionChainReply;
+import org.opendaylight.controller.cluster.datastore.messages.EnableNotification;
import org.opendaylight.controller.cluster.datastore.messages.PeerAddressResolved;
import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListener;
import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListenerReply;
@@ -24,6 +27,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
+import static junit.framework.Assert.assertFalse;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -36,19 +40,25 @@ public class ShardTest extends AbstractActorTest {
getSystem().actorOf(props, "testCreateTransactionChain");
- // Wait for Shard to become a Leader
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
+ // Wait for a specific log message to show up
+ final boolean result =
+ new JavaTestKit.EventFilter(Logging.Info.class
+ ) {
+ protected Boolean run() {
+ return true;
+ }
+ }.from(subject.path().toString())
+ .message("Switching from state Candidate to Leader")
+ .occurrences(1).exec();
+
+ Assert.assertEquals(true, result);
new Within(duration("1 seconds")) {
protected void run() {
subject.tell(new CreateTransactionChain().toSerializable(), getRef());
- final String out = new ExpectMsg("match hint") {
+ final String out = new ExpectMsg(duration("1 seconds"), "match hint") {
// do not put code outside this method, will run afterwards
protected String match(Object in) {
if (in.getClass().equals(CreateTransactionChainReply.SERIALIZABLE_CLASS)){
@@ -89,15 +99,28 @@ public class ShardTest extends AbstractActorTest {
getRef());
subject.tell(new RegisterChangeListener(TestModel.TEST_PATH,
- getRef().path(), AsyncDataBroker.DataChangeScope.BASE).toSerializable(),
+ getRef().path(), AsyncDataBroker.DataChangeScope.BASE),
getRef());
- final String out = new ExpectMsg("match hint") {
+ final Boolean notificationEnabled = new ExpectMsg("enable notification") {
+ // do not put code outside this method, will run afterwards
+ protected Boolean match(Object in) {
+ if(in instanceof EnableNotification){
+ return ((EnableNotification) in).isEnabled();
+ } else {
+ throw noMatch();
+ }
+ }
+ }.get(); // this extracts the received message
+
+ assertFalse(notificationEnabled);
+
+ final String out = new ExpectMsg(duration("1 seconds"), "match hint") {
// do not put code outside this method, will run afterwards
protected String match(Object in) {
- if (in.getClass().equals(RegisterChangeListenerReply.SERIALIZABLE_CLASS)) {
+ if (in.getClass().equals(RegisterChangeListenerReply.class)) {
RegisterChangeListenerReply reply =
- RegisterChangeListenerReply.fromSerializable(getSystem(),in);
+ (RegisterChangeListenerReply) in;
return reply.getListenerRegistrationPath()
.toString();
} else {
@@ -108,8 +131,6 @@ public class ShardTest extends AbstractActorTest {
assertTrue(out.matches(
"akka:\\/\\/test\\/user\\/testRegisterChangeListener\\/\\$.*"));
- // Will wait for the rest of the 3 seconds
- expectNoMsg();
}
@@ -125,13 +146,18 @@ public class ShardTest extends AbstractActorTest {
getSystem().actorOf(props, "testCreateTransaction");
- // Wait for Shard to become a Leader
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
+ // Wait for a specific log message to show up
+ final boolean result =
+ new JavaTestKit.EventFilter(Logging.Info.class
+ ) {
+ protected Boolean run() {
+ return true;
+ }
+ }.from(subject.path().toString())
+ .message("Switching from state Candidate to Leader")
+ .occurrences(1).exec();
+ Assert.assertEquals(true, result);
new Within(duration("1 seconds")) {
protected void run() {
@@ -140,10 +166,10 @@ public class ShardTest extends AbstractActorTest {
new UpdateSchemaContext(TestModel.createTestContext()),
getRef());
- subject.tell(new CreateTransaction("txn-1").toSerializable(),
+ subject.tell(new CreateTransaction("txn-1", TransactionProxy.TransactionType.READ_ONLY.ordinal() ).toSerializable(),
getRef());
- final String out = new ExpectMsg("match hint") {
+ final String out = new ExpectMsg(duration("1 seconds"), "match hint") {
// do not put code outside this method, will run afterwards
protected String match(Object in) {
if (in instanceof CreateTransactionReply) {
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTransactionChainTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTransactionChainTest.java
index 6330ad8acc..b35880a6a5 100644
--- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTransactionChainTest.java
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTransactionChainTest.java
@@ -33,9 +33,9 @@ public class ShardTransactionChainTest extends AbstractActorTest {
new Within(duration("1 seconds")) {
protected void run() {
- subject.tell(new CreateTransaction("txn-1").toSerializable(), getRef());
+ subject.tell(new CreateTransaction("txn-1", TransactionProxy.TransactionType.READ_ONLY.ordinal() ).toSerializable(), getRef());
- final String out = new ExpectMsg("match hint") {
+ final String out = new ExpectMsg(duration("1 seconds"), "match hint") {
// do not put code outside this method, will run afterwards
protected String match(Object in) {
if (in.getClass().equals(CreateTransactionReply.SERIALIZABLE_CLASS)) {
@@ -70,7 +70,7 @@ public class ShardTransactionChainTest extends AbstractActorTest {
subject.tell(new CloseTransactionChain().toSerializable(), getRef());
- final String out = new ExpectMsg("match hint") {
+ final String out = new ExpectMsg(duration("1 seconds"), "match hint") {
// do not put code outside this method, will run afterwards
protected String match(Object in) {
if (in.getClass().equals(CloseTransactionChainReply.SERIALIZABLE_CLASS)) {
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTransactionTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTransactionTest.java
index 7884eeccda..632ecc29cd 100644
--- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTransactionTest.java
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTransactionTest.java
@@ -4,8 +4,10 @@ import akka.actor.ActorRef;
import akka.actor.Props;
import akka.actor.Terminated;
import akka.testkit.JavaTestKit;
+import akka.testkit.TestActorRef;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
+import org.junit.Assert;
import org.junit.Test;
import org.opendaylight.controller.cluster.datastore.messages.CloseTransaction;
import org.opendaylight.controller.cluster.datastore.messages.CloseTransactionReply;
@@ -53,7 +55,7 @@ public class ShardTransactionTest extends AbstractActorTest {
new JavaTestKit(getSystem()) {{
final ActorRef shard = getSystem().actorOf(Shard.props("config", Collections.EMPTY_MAP));
final Props props =
- ShardTransaction.props(store.newReadWriteTransaction(), shard, testSchemaContext);
+ ShardTransaction.props(store.newReadOnlyTransaction(), shard, testSchemaContext);
final ActorRef subject = getSystem().actorOf(props, "testReadData");
new Within(duration("1 seconds")) {
@@ -63,7 +65,7 @@ public class ShardTransactionTest extends AbstractActorTest {
new ReadData(YangInstanceIdentifier.builder().build()).toSerializable(),
getRef());
- final String out = new ExpectMsg("match hint") {
+ final String out = new ExpectMsg(duration("1 seconds"), "match hint") {
// do not put code outside this method, will run afterwards
protected String match(Object in) {
if (in.getClass().equals(ReadDataReply.SERIALIZABLE_CLASS)) {
@@ -93,7 +95,7 @@ public class ShardTransactionTest extends AbstractActorTest {
new JavaTestKit(getSystem()) {{
final ActorRef shard = getSystem().actorOf(Shard.props("config", Collections.EMPTY_MAP));
final Props props =
- ShardTransaction.props(store.newReadWriteTransaction(), shard, testSchemaContext);
+ ShardTransaction.props( store.newReadOnlyTransaction(), shard, testSchemaContext);
final ActorRef subject = getSystem().actorOf(props, "testReadDataWhenDataNotFound");
new Within(duration("1 seconds")) {
@@ -103,7 +105,7 @@ public class ShardTransactionTest extends AbstractActorTest {
new ReadData(TestModel.TEST_PATH).toSerializable(),
getRef());
- final String out = new ExpectMsg("match hint") {
+ final String out = new ExpectMsg(duration("1 seconds"), "match hint") {
// do not put code outside this method, will run afterwards
protected String match(Object in) {
if (in.getClass().equals(ReadDataReply.SERIALIZABLE_CLASS)) {
@@ -139,7 +141,7 @@ public class ShardTransactionTest extends AbstractActorTest {
getRef());
final CompositeModification compositeModification =
- new ExpectMsg("match hint") {
+ new ExpectMsg(duration("1 seconds"), "match hint") {
// do not put code outside this method, will run afterwards
protected CompositeModification match(Object in) {
if (in instanceof ShardTransaction.GetCompositeModificationReply) {
@@ -167,7 +169,7 @@ public class ShardTransactionTest extends AbstractActorTest {
new JavaTestKit(getSystem()) {{
final ActorRef shard = getSystem().actorOf(Shard.props("config", Collections.EMPTY_MAP));
final Props props =
- ShardTransaction.props(store.newReadWriteTransaction(), shard, TestModel.createTestContext());
+ ShardTransaction.props(store.newWriteOnlyTransaction(), shard, TestModel.createTestContext());
final ActorRef subject =
getSystem().actorOf(props, "testWriteData");
@@ -178,7 +180,7 @@ public class ShardTransactionTest extends AbstractActorTest {
ImmutableNodes.containerNode(TestModel.TEST_QNAME), TestModel.createTestContext()).toSerializable(),
getRef());
- final String out = new ExpectMsg("match hint") {
+ final String out = new ExpectMsg(duration("1 seconds"), "match hint") {
// do not put code outside this method, will run afterwards
protected String match(Object in) {
if (in.getClass().equals(WriteDataReply.SERIALIZABLE_CLASS)) {
@@ -244,7 +246,7 @@ public class ShardTransactionTest extends AbstractActorTest {
new JavaTestKit(getSystem()) {{
final ActorRef shard = getSystem().actorOf(Shard.props("config", Collections.EMPTY_MAP));
final Props props =
- ShardTransaction.props(store.newReadWriteTransaction(), shard, TestModel.createTestContext());
+ ShardTransaction.props( store.newWriteOnlyTransaction(), shard, TestModel.createTestContext());
final ActorRef subject =
getSystem().actorOf(props, "testDeleteData");
@@ -253,7 +255,7 @@ public class ShardTransactionTest extends AbstractActorTest {
subject.tell(new DeleteData(TestModel.TEST_PATH).toSerializable(), getRef());
- final String out = new ExpectMsg("match hint") {
+ final String out = new ExpectMsg(duration("1 seconds"), "match hint") {
// do not put code outside this method, will run afterwards
protected String match(Object in) {
if (in.getClass().equals(DeleteDataReply.SERIALIZABLE_CLASS)) {
@@ -281,7 +283,7 @@ public class ShardTransactionTest extends AbstractActorTest {
new JavaTestKit(getSystem()) {{
final ActorRef shard = getSystem().actorOf(Shard.props("config", Collections.EMPTY_MAP));
final Props props =
- ShardTransaction.props(store.newReadWriteTransaction(), shard, TestModel.createTestContext());
+ ShardTransaction.props( store.newReadWriteTransaction(), shard, TestModel.createTestContext());
final ActorRef subject =
getSystem().actorOf(props, "testReadyTransaction");
@@ -290,7 +292,7 @@ public class ShardTransactionTest extends AbstractActorTest {
subject.tell(new ReadyTransaction().toSerializable(), getRef());
- final String out = new ExpectMsg("match hint") {
+ final String out = new ExpectMsg(duration("1 seconds"), "match hint") {
// do not put code outside this method, will run afterwards
protected String match(Object in) {
if (in.getClass().equals(ReadyTransactionReply.SERIALIZABLE_CLASS)) {
@@ -328,7 +330,7 @@ public class ShardTransactionTest extends AbstractActorTest {
subject.tell(new CloseTransaction().toSerializable(), getRef());
- final String out = new ExpectMsg("match hint") {
+ final String out = new ExpectMsg(duration("1 seconds"), "match hint") {
// do not put code outside this method, will run afterwards
protected String match(Object in) {
if (in.getClass().equals(CloseTransactionReply.SERIALIZABLE_CLASS)) {
@@ -341,7 +343,7 @@ public class ShardTransactionTest extends AbstractActorTest {
assertEquals("match", out);
- final String termination = new ExpectMsg("match hint") {
+ final String termination = new ExpectMsg(duration("1 seconds"), "match hint") {
// do not put code outside this method, will run afterwards
protected String match(Object in) {
if (in instanceof Terminated) {
@@ -361,4 +363,24 @@ public class ShardTransactionTest extends AbstractActorTest {
}};
}
+
+
+ @Test
+ public void testNegativePerformingWriteOperationOnReadTransaction() throws Exception {
+ try {
+
+ final ActorRef shard = getSystem().actorOf(Shard.props("config", Collections.EMPTY_MAP));
+ final Props props =
+ ShardTransaction.props(store.newReadOnlyTransaction(), shard, TestModel.createTestContext());
+ final TestActorRef subject = TestActorRef.apply(props,getSystem());
+
+ subject.receive(new DeleteData(TestModel.TEST_PATH).toSerializable(), ActorRef.noSender());
+ Assert.assertFalse(true);
+
+
+ } catch (Exception cs) {
+ assertEquals(cs.getClass().getSimpleName(), Exception.class.getSimpleName());
+ assertTrue(cs.getMessage().startsWith("ShardTransaction:handleRecieve received an unknown message"));
+ }
+ }
}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortProxyTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortProxyTest.java
index 992518e100..4eca5671f6 100644
--- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortProxyTest.java
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortProxyTest.java
@@ -2,8 +2,14 @@ package org.opendaylight.controller.cluster.datastore;
import akka.actor.ActorRef;
import akka.actor.Props;
+
import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+
import junit.framework.Assert;
+
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.opendaylight.controller.cluster.datastore.messages.AbortTransactionReply;
@@ -14,7 +20,6 @@ import org.opendaylight.controller.cluster.datastore.utils.MessageCollectorActor
import org.opendaylight.controller.cluster.datastore.utils.MockActorContext;
import java.util.Arrays;
-import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import static org.junit.Assert.assertNotNull;
@@ -25,7 +30,8 @@ public class ThreePhaseCommitCohortProxyTest extends AbstractActorTest {
private Props props;
private ActorRef actorRef;
private MockActorContext actorContext;
- private ExecutorService executor = Executors.newSingleThreadExecutor();
+ private final ListeningExecutorService executor = MoreExecutors.listeningDecorator(
+ Executors.newSingleThreadExecutor());
@Before
public void setUp(){
@@ -39,6 +45,11 @@ public class ThreePhaseCommitCohortProxyTest extends AbstractActorTest {
}
+ @After
+ public void tearDown() {
+ executor.shutdownNow();
+ }
+
@Test
public void testCanCommit() throws Exception {
actorContext.setExecuteRemoteOperationResponse(new CanCommitTransactionReply(true).toSerializable());
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/TransactionProxyTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/TransactionProxyTest.java
index f654e3aced..0cd029c2ff 100644
--- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/TransactionProxyTest.java
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/TransactionProxyTest.java
@@ -2,11 +2,19 @@ package org.opendaylight.controller.cluster.datastore;
import akka.actor.ActorRef;
import akka.actor.Props;
+
import com.google.common.base.Optional;
import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+
import junit.framework.Assert;
+
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+import org.opendaylight.controller.cluster.datastore.exceptions.PrimaryNotFoundException;
+import org.opendaylight.controller.cluster.datastore.exceptions.TimeoutException;
import org.opendaylight.controller.cluster.datastore.messages.CloseTransaction;
import org.opendaylight.controller.cluster.datastore.messages.DeleteData;
import org.opendaylight.controller.cluster.datastore.messages.MergeData;
@@ -26,11 +34,17 @@ import org.opendaylight.controller.protobuff.messages.transaction.ShardTransacti
import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import scala.concurrent.duration.FiniteDuration;
import java.util.List;
-import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import static junit.framework.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
public class TransactionProxyTest extends AbstractActorTest {
private final Configuration configuration = new MockConfiguration();
@@ -38,14 +52,19 @@ public class TransactionProxyTest extends AbstractActorTest {
private final ActorContext testContext =
new ActorContext(getSystem(), getSystem().actorOf(Props.create(DoNothingActor.class)), new MockClusterWrapper(), configuration );
- private ExecutorService transactionExecutor =
- Executors.newSingleThreadExecutor();
+ private final ListeningExecutorService transactionExecutor =
+ MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor());
@Before
public void setUp(){
ShardStrategyFactory.setConfiguration(configuration);
}
+ @After
+ public void tearDown() {
+ transactionExecutor.shutdownNow();
+ }
+
@Test
public void testRead() throws Exception {
final Props props = Props.create(DoNothingActor.class);
@@ -111,6 +130,68 @@ public class TransactionProxyTest extends AbstractActorTest {
Assert.assertFalse(normalizedNodeOptional.isPresent());
}
+ @Test
+ public void testReadWhenAPrimaryNotFoundExceptionIsThrown() throws Exception {
+ final ActorContext actorContext = mock(ActorContext.class);
+
+ when(actorContext.executeShardOperation(anyString(), any(), any(
+ FiniteDuration.class))).thenThrow(new PrimaryNotFoundException("test"));
+
+ TransactionProxy transactionProxy =
+ new TransactionProxy(actorContext,
+ TransactionProxy.TransactionType.READ_ONLY, transactionExecutor, TestModel.createTestContext());
+
+
+ ListenableFuture>> read =
+ transactionProxy.read(TestModel.TEST_PATH);
+
+ Assert.assertFalse(read.get().isPresent());
+
+ }
+
+
+ @Test
+ public void testReadWhenATimeoutExceptionIsThrown() throws Exception {
+ final ActorContext actorContext = mock(ActorContext.class);
+
+ when(actorContext.executeShardOperation(anyString(), any(), any(
+ FiniteDuration.class))).thenThrow(new TimeoutException("test", new Exception("reason")));
+
+ TransactionProxy transactionProxy =
+ new TransactionProxy(actorContext,
+ TransactionProxy.TransactionType.READ_ONLY, transactionExecutor, TestModel.createTestContext());
+
+
+ ListenableFuture>> read =
+ transactionProxy.read(TestModel.TEST_PATH);
+
+ Assert.assertFalse(read.get().isPresent());
+
+ }
+
+ @Test
+ public void testReadWhenAAnyOtherExceptionIsThrown() throws Exception {
+ final ActorContext actorContext = mock(ActorContext.class);
+
+ when(actorContext.executeShardOperation(anyString(), any(), any(
+ FiniteDuration.class))).thenThrow(new NullPointerException());
+
+ TransactionProxy transactionProxy =
+ new TransactionProxy(actorContext,
+ TransactionProxy.TransactionType.READ_ONLY, transactionExecutor, TestModel.createTestContext());
+
+
+ try {
+ ListenableFuture>> read =
+ transactionProxy.read(TestModel.TEST_PATH);
+ fail("A null pointer exception was expected");
+ } catch(NullPointerException e){
+
+ }
+ }
+
+
+
@Test
public void testWrite() throws Exception {
final Props props = Props.create(MessageCollectorActor.class);
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/shardstrategy/ModuleShardStrategyTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/shardstrategy/ModuleShardStrategyTest.java
index 88753e4b0a..3394cdc959 100644
--- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/shardstrategy/ModuleShardStrategyTest.java
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/shardstrategy/ModuleShardStrategyTest.java
@@ -1,6 +1,5 @@
package org.opendaylight.controller.cluster.datastore.shardstrategy;
-import junit.framework.Assert;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
@@ -8,6 +7,10 @@ import org.junit.rules.ExpectedException;
import org.opendaylight.controller.cluster.datastore.Configuration;
import org.opendaylight.controller.cluster.datastore.ConfigurationImpl;
import org.opendaylight.controller.md.cluster.datastore.model.CarsModel;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+import static junit.framework.Assert.assertEquals;
public class ModuleShardStrategyTest {
@Rule
@@ -28,6 +31,23 @@ public class ModuleShardStrategyTest {
String shard = moduleShardStrategy.findShard(CarsModel.BASE_PATH);
- Assert.assertEquals("cars-1", shard);
+ assertEquals("cars-1", shard);
+ }
+
+ @Test
+ public void testFindShardWhenModuleConfigurationPresentInModulesButMissingInModuleShards() {
+
+ final QName BASE_QNAME = QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test:missing", "2014-03-13",
+ "missing");
+
+ final YangInstanceIdentifier BASE_PATH = YangInstanceIdentifier.of(BASE_QNAME);
+
+ ModuleShardStrategy moduleShardStrategy =
+ new ModuleShardStrategy("missing", configuration);
+
+ String shard = moduleShardStrategy.findShard(BASE_PATH);
+
+ assertEquals(DefaultShardStrategy.DEFAULT_SHARD, shard);
+
}
}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/ActorContextTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/ActorContextTest.java
index 3dd0214e9b..5874eccda4 100644
--- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/ActorContextTest.java
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/ActorContextTest.java
@@ -2,12 +2,20 @@ package org.opendaylight.controller.cluster.datastore.utils;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
+import akka.actor.Props;
+import akka.actor.UntypedActor;
+import akka.japi.Creator;
+import akka.testkit.JavaTestKit;
import org.junit.Test;
import org.opendaylight.controller.cluster.datastore.AbstractActorTest;
import org.opendaylight.controller.cluster.datastore.ClusterWrapper;
import org.opendaylight.controller.cluster.datastore.Configuration;
+import org.opendaylight.controller.cluster.datastore.messages.FindLocalShard;
+import org.opendaylight.controller.cluster.datastore.messages.LocalShardFound;
+import org.opendaylight.controller.cluster.datastore.messages.LocalShardNotFound;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.mock;
public class ActorContextTest extends AbstractActorTest{
@@ -44,4 +52,145 @@ public class ActorContextTest extends AbstractActorTest{
System.out.println(actorContext
.actorFor("akka://system/user/shardmanager/shard/transaction"));
}
+
+
+ private static class MockShardManager extends UntypedActor {
+
+ private final boolean found;
+ private final ActorRef actorRef;
+
+ private MockShardManager(boolean found, ActorRef actorRef){
+
+ this.found = found;
+ this.actorRef = actorRef;
+ }
+
+ @Override public void onReceive(Object message) throws Exception {
+ if(found){
+ getSender().tell(new LocalShardFound(actorRef), getSelf());
+ } else {
+ getSender().tell(new LocalShardNotFound(((FindLocalShard) message).getShardName()), getSelf());
+ }
+ }
+
+ private static Props props(final boolean found, final ActorRef actorRef){
+ return Props.create(new Creator() {
+
+ @Override public MockShardManager create()
+ throws Exception {
+ return new MockShardManager(found,
+ actorRef);
+ }
+ });
+ }
+ }
+
+ @Test
+ public void testExecuteLocalShardOperationWithShardFound(){
+ new JavaTestKit(getSystem()) {{
+
+ new Within(duration("1 seconds")) {
+ protected void run() {
+
+ ActorRef shardActorRef = getSystem().actorOf(Props.create(EchoActor.class));
+
+ ActorRef shardManagerActorRef = getSystem()
+ .actorOf(MockShardManager.props(true, shardActorRef));
+
+ ActorContext actorContext =
+ new ActorContext(getSystem(), shardManagerActorRef , mock(ClusterWrapper.class),
+ mock(Configuration.class));
+
+ Object out = actorContext.executeLocalShardOperation("default", "hello", duration("1 seconds"));
+
+ assertEquals("hello", out);
+
+
+ expectNoMsg();
+ }
+ };
+ }};
+
+ }
+
+ @Test
+ public void testExecuteLocalShardOperationWithShardNotFound(){
+ new JavaTestKit(getSystem()) {{
+
+ new Within(duration("1 seconds")) {
+ protected void run() {
+
+ ActorRef shardManagerActorRef = getSystem()
+ .actorOf(MockShardManager.props(false, null));
+
+ ActorContext actorContext =
+ new ActorContext(getSystem(), shardManagerActorRef , mock(ClusterWrapper.class),
+ mock(Configuration.class));
+
+ Object out = actorContext.executeLocalShardOperation("default", "hello", duration("1 seconds"));
+
+ assertNull(out);
+
+
+ expectNoMsg();
+ }
+ };
+ }};
+
+ }
+
+
+ @Test
+ public void testFindLocalShardWithShardFound(){
+ new JavaTestKit(getSystem()) {{
+
+ new Within(duration("1 seconds")) {
+ protected void run() {
+
+ ActorRef shardActorRef = getSystem().actorOf(Props.create(EchoActor.class));
+
+ ActorRef shardManagerActorRef = getSystem()
+ .actorOf(MockShardManager.props(true, shardActorRef));
+
+ ActorContext actorContext =
+ new ActorContext(getSystem(), shardManagerActorRef , mock(ClusterWrapper.class),
+ mock(Configuration.class));
+
+ Object out = actorContext.findLocalShard("default");
+
+ assertEquals(shardActorRef, out);
+
+
+ expectNoMsg();
+ }
+ };
+ }};
+
+ }
+
+ @Test
+ public void testFindLocalShardWithShardNotFound(){
+ new JavaTestKit(getSystem()) {{
+
+ new Within(duration("1 seconds")) {
+ protected void run() {
+
+ ActorRef shardManagerActorRef = getSystem()
+ .actorOf(MockShardManager.props(false, null));
+
+ ActorContext actorContext =
+ new ActorContext(getSystem(), shardManagerActorRef , mock(ClusterWrapper.class),
+ mock(Configuration.class));
+
+ Object out = actorContext.findLocalShard("default");
+
+ assertNull(out);
+
+
+ expectNoMsg();
+ }
+ };
+ }};
+
+ }
}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/EchoActor.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/EchoActor.java
new file mode 100644
index 0000000000..fe88afe1db
--- /dev/null
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/EchoActor.java
@@ -0,0 +1,21 @@
+/*
+ * 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.cluster.datastore.utils;
+
+import akka.actor.UntypedActor;
+
+/**
+ * The EchoActor simply responds back with the same message that it receives
+ */
+public class EchoActor extends UntypedActor{
+
+ @Override public void onReceive(Object message) throws Exception {
+ getSender().tell(message, getSelf());
+ }
+}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/MockActorContext.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/MockActorContext.java
index 1d1e661488..5d3853f311 100644
--- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/MockActorContext.java
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/MockActorContext.java
@@ -19,6 +19,7 @@ public class MockActorContext extends ActorContext {
private Object executeShardOperationResponse;
private Object executeRemoteOperationResponse;
private Object executeLocalOperationResponse;
+ private Object executeLocalShardOperationResponse;
public MockActorContext(ActorSystem actorSystem) {
super(actorSystem, null, new MockClusterWrapper(), new MockConfiguration());
@@ -56,8 +57,18 @@ public class MockActorContext extends ActorContext {
this.executeLocalOperationResponse = executeLocalOperationResponse;
}
+ public void setExecuteLocalShardOperationResponse(
+ Object executeLocalShardOperationResponse) {
+ this.executeLocalShardOperationResponse = executeLocalShardOperationResponse;
+ }
+
@Override public Object executeLocalOperation(ActorRef actor,
Object message, FiniteDuration duration) {
return this.executeLocalOperationResponse;
}
+
+ @Override public Object executeLocalShardOperation(String shardName,
+ Object message, FiniteDuration duration) {
+ return this.executeLocalShardOperationResponse;
+ }
}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/CarsModel.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/CarsModel.java
index 57df20172d..6860872b75 100644
--- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/CarsModel.java
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/CarsModel.java
@@ -8,6 +8,7 @@
package org.opendaylight.controller.md.cluster.datastore.model;
+import java.math.BigInteger;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
@@ -42,14 +43,14 @@ public class CarsModel {
MapEntryNode altima =
ImmutableNodes.mapEntryBuilder(CAR_QNAME, CAR_NAME_QNAME, "altima")
.withChild(ImmutableNodes.leafNode(CAR_NAME_QNAME, "altima"))
- .withChild(ImmutableNodes.leafNode(CAR_PRICE_QNAME, 1000))
+ .withChild(ImmutableNodes.leafNode(CAR_PRICE_QNAME, new BigInteger("1000")))
.build();
// Create an entry for the car accord
MapEntryNode honda =
ImmutableNodes.mapEntryBuilder(CAR_QNAME, CAR_NAME_QNAME, "accord")
.withChild(ImmutableNodes.leafNode(CAR_NAME_QNAME, "accord"))
- .withChild(ImmutableNodes.leafNode(CAR_PRICE_QNAME, 2000))
+ .withChild(ImmutableNodes.leafNode(CAR_PRICE_QNAME, new BigInteger("2000")))
.build();
cars.withChild(altima);
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/PeopleModel.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/PeopleModel.java
index 1b4020af43..e637920e78 100644
--- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/PeopleModel.java
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/PeopleModel.java
@@ -42,14 +42,14 @@ public class PeopleModel {
MapEntryNode jack =
ImmutableNodes.mapEntryBuilder(PERSON_QNAME, PERSON_NAME_QNAME, "jack")
.withChild(ImmutableNodes.leafNode(PERSON_NAME_QNAME, "jack"))
- .withChild(ImmutableNodes.leafNode(PERSON_AGE_QNAME, 100))
+ .withChild(ImmutableNodes.leafNode(PERSON_AGE_QNAME, 100L))
.build();
// Create an entry for the person jill
MapEntryNode jill =
ImmutableNodes.mapEntryBuilder(PERSON_QNAME, PERSON_NAME_QNAME, "jill")
.withChild(ImmutableNodes.leafNode(PERSON_NAME_QNAME, "jill"))
- .withChild(ImmutableNodes.leafNode(PERSON_AGE_QNAME, 200))
+ .withChild(ImmutableNodes.leafNode(PERSON_AGE_QNAME, 200L))
.build();
cars.withChild(jack);
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/SampleModelsTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/SampleModelsTest.java
index d8fefcd986..be8713c702 100644
--- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/SampleModelsTest.java
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/SampleModelsTest.java
@@ -18,45 +18,45 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
public class SampleModelsTest {
@Test
public void testPeopleModel(){
- NormalizedNode, ?> expected = PeopleModel.create();
+ final NormalizedNode, ?> expected = PeopleModel.create();
- NormalizedNodeMessages.Container node =
+ final NormalizedNodeMessages.Container node =
new NormalizedNodeToNodeCodec(SchemaContextHelper.full())
.encode(YangInstanceIdentifier.of(PeopleModel.BASE_QNAME),
expected);
- NormalizedNodeMessages.Node normalizedNode =
+ final NormalizedNodeMessages.Node normalizedNode =
node.getNormalizedNode();
- NormalizedNode,?> actual = new NormalizedNodeToNodeCodec(SchemaContextHelper.full()).decode(YangInstanceIdentifier.of(PeopleModel.BASE_QNAME),
+ final NormalizedNode,?> actual = new NormalizedNodeToNodeCodec(SchemaContextHelper.full()).decode(YangInstanceIdentifier.of(PeopleModel.BASE_QNAME),
normalizedNode);
- Assert.assertEquals(expected.toString(), actual.toString());
+ Assert.assertEquals(expected, actual);
}
@Test
public void testCarsModel(){
- NormalizedNode, ?> expected = CarsModel.create();
+ final NormalizedNode, ?> expected = CarsModel.create();
- NormalizedNodeMessages.Container node =
+ final NormalizedNodeMessages.Container node =
new NormalizedNodeToNodeCodec(SchemaContextHelper.full())
.encode(YangInstanceIdentifier.of(CarsModel.BASE_QNAME),
expected);
- NormalizedNodeMessages.Node normalizedNode =
+ final NormalizedNodeMessages.Node normalizedNode =
node.getNormalizedNode();
- NormalizedNode,?> actual = new NormalizedNodeToNodeCodec(SchemaContextHelper.full()).decode(
+ final NormalizedNode,?> actual = new NormalizedNodeToNodeCodec(SchemaContextHelper.full()).decode(
YangInstanceIdentifier.of(CarsModel.BASE_QNAME),
normalizedNode);
- Assert.assertEquals(expected.toString(), actual.toString());
+ Assert.assertEquals(expected, actual);
}
}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/programs/appendentries/Client.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/programs/appendentries/Client.java
new file mode 100644
index 0000000000..2671be80bb
--- /dev/null
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/programs/appendentries/Client.java
@@ -0,0 +1,120 @@
+/*
+ * 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.programs.appendentries;
+
+import akka.actor.ActorSelection;
+import akka.actor.ActorSystem;
+import akka.actor.Props;
+import akka.actor.UntypedActor;
+import com.typesafe.config.ConfigFactory;
+import org.opendaylight.controller.cluster.datastore.CompositeModificationPayload;
+import org.opendaylight.controller.cluster.datastore.modification.MutableCompositeModification;
+import org.opendaylight.controller.cluster.datastore.modification.WriteModification;
+import org.opendaylight.controller.cluster.example.messages.KeyValue;
+import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry;
+import org.opendaylight.controller.cluster.raft.messages.AppendEntries;
+import org.opendaylight.controller.cluster.raft.protobuff.client.messages.Payload;
+import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Client {
+
+ private static ActorSystem actorSystem;
+
+ public static class ClientActor extends UntypedActor {
+
+ @Override public void onReceive(Object message) throws Exception {
+
+ }
+ }
+
+ public static void main(String[] args){
+ actorSystem = ActorSystem.create("appendentries", ConfigFactory
+ .load().getConfig("ODLCluster"));
+
+ ActorSelection actorSelection = actorSystem.actorSelection(
+ "akka.tcp://appendentries@127.0.0.1:2550/user/server");
+
+ AppendEntries appendEntries = modificationAppendEntries();
+
+ Payload data = appendEntries.getEntries().get(0).getData();
+ if(data instanceof CompositeModificationPayload) {
+ System.out.println(
+ "Sending : " + ((CompositeModificationPayload) data)
+ .getModification());
+ } else {
+ System.out.println(
+ "Sending : " + ((KeyValue) data)
+ .getKey());
+
+ }
+
+ actorSelection.tell(appendEntries.toSerializable(), null);
+
+
+
+
+ actorSystem.actorOf(Props.create(ClientActor.class), "client");
+ }
+
+ public static AppendEntries modificationAppendEntries() {
+ List modification = new ArrayList<>();
+
+ modification.add(0, new ReplicatedLogEntry() {
+ @Override public Payload getData() {
+ WriteModification writeModification =
+ new WriteModification(TestModel.TEST_PATH, ImmutableNodes
+ .containerNode(TestModel.TEST_QNAME),
+ TestModel.createTestContext()
+ );
+
+ MutableCompositeModification compositeModification =
+ new MutableCompositeModification();
+
+ compositeModification.addModification(writeModification);
+
+ return new CompositeModificationPayload(
+ compositeModification.toSerializable());
+ }
+
+ @Override public long getTerm() {
+ return 1;
+ }
+
+ @Override public long getIndex() {
+ return 1;
+ }
+ });
+
+ return new AppendEntries(1, "member-1", 0, 100, modification, 1);
+ }
+
+ public static AppendEntries keyValueAppendEntries() {
+ List modification = new ArrayList<>();
+
+ modification.add(0, new ReplicatedLogEntry() {
+ @Override public Payload getData() {
+ return new KeyValue("moiz", "test");
+ }
+
+ @Override public long getTerm() {
+ return 1;
+ }
+
+ @Override public long getIndex() {
+ return 1;
+ }
+ });
+
+ return new AppendEntries(1, "member-1", 0, 100, modification, 1);
+ }
+}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/programs/appendentries/Server.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/programs/appendentries/Server.java
new file mode 100644
index 0000000000..0e6d535301
--- /dev/null
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/programs/appendentries/Server.java
@@ -0,0 +1,53 @@
+/*
+ * 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.programs.appendentries;
+
+import akka.actor.ActorSystem;
+import akka.actor.Props;
+import akka.actor.UntypedActor;
+import com.typesafe.config.ConfigFactory;
+import org.opendaylight.controller.cluster.datastore.CompositeModificationPayload;
+import org.opendaylight.controller.cluster.example.messages.KeyValue;
+import org.opendaylight.controller.cluster.raft.messages.AppendEntries;
+import org.opendaylight.controller.cluster.raft.protobuff.client.messages.Payload;
+
+public class Server {
+
+ private static ActorSystem actorSystem;
+
+ public static class ServerActor extends UntypedActor {
+
+ @Override public void onReceive(Object message) throws Exception {
+ if(AppendEntries.SERIALIZABLE_CLASS.equals(message.getClass())){
+ AppendEntries appendEntries =
+ AppendEntries.fromSerializable(message);
+
+ Payload data = appendEntries.getEntries()
+ .get(0).getData();
+ if(data instanceof KeyValue){
+ System.out.println("Received : " + ((KeyValue) appendEntries.getEntries().get(0).getData()).getKey());
+ } else {
+ System.out.println("Received :" +
+ ((CompositeModificationPayload) appendEntries
+ .getEntries()
+ .get(0).getData()).getModification().toString());
+ }
+ } else if(message instanceof String){
+ System.out.println(message);
+ }
+ }
+ }
+
+ public static void main(String[] args){
+ actorSystem = ActorSystem.create("appendentries", ConfigFactory
+ .load().getConfig("ODLCluster"));
+
+ actorSystem.actorOf(Props.create(ServerActor.class), "server");
+ }
+}
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/application.conf b/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/application.conf
index aebff27c7d..eda1c304e4 100644
--- a/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/application.conf
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/application.conf
@@ -1,4 +1,5 @@
akka {
+ loggers = [akka.testkit.TestEventListener]
actor {
serializers {
java = "akka.serialization.JavaSerializer"
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/modules.conf b/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/modules.conf
index 22854cb11a..f4919e7895 100644
--- a/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/modules.conf
+++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/resources/modules.conf
@@ -15,4 +15,10 @@ modules = [
shard-strategy = "module"
}
+ {
+ name = "missing"
+ namespace = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:missing"
+ shard-strategy = "module"
+ }
+
]
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataReadTransaction.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataReadTransaction.java
index afa2286d53..fc251c8445 100644
--- a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataReadTransaction.java
+++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataReadTransaction.java
@@ -9,11 +9,12 @@ 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.ListenableFuture;
+import com.google.common.util.concurrent.CheckedFuture;
/**
* A transaction that provides read access to a logical data store.
@@ -33,14 +34,17 @@ public interface DOMDataReadTransaction extends AsyncReadTransaction
- *
*/
- ListenableFuture>> read(LogicalDatastoreType store,YangInstanceIdentifier path);
+ CheckedFuture>, ReadFailedException> read(
+ LogicalDatastoreType store, YangInstanceIdentifier path);
}
diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/model/SchemaService.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/model/SchemaService.java
index 34e5b1b803..c3e979c536 100644
--- a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/model/SchemaService.java
+++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/model/SchemaService.java
@@ -11,7 +11,7 @@ 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.SchemaServiceListener;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
public interface SchemaService extends BrokerService {
@@ -42,5 +42,11 @@ public interface SchemaService extends BrokerService {
*/
SchemaContext getGlobalContext();
- ListenerRegistration registerSchemaServiceListener(SchemaServiceListener listener);
+ /**
+ * Register a listener for changes in schema context.
+ *
+ * @param listener Listener which should be registered
+ * @return Listener registration handle
+ */
+ ListenerRegistration registerSchemaContextListener(SchemaContextListener listener);
}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomBrokerImplModule.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomBrokerImplModule.java
index 17b78f4ebd..f1f16cd635 100644
--- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomBrokerImplModule.java
+++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomBrokerImplModule.java
@@ -91,7 +91,7 @@ public final class DomBrokerImplModule extends org.opendaylight.controller.confi
wrappedStore.changeDelegate(legacyStore);
wrappedStore.setValidationEnabled(false);
- schemaService.registerSchemaServiceListener(wrappedStore);
+ schemaService.registerSchemaContextListener(wrappedStore);
dataService.registerConfigurationReader(rootPath, wrappedStore);
dataService.registerCommitHandler(rootPath, wrappedStore);
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomInmemoryDataBrokerModule.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomInmemoryDataBrokerModule.java
index 69b17ee3c4..22dad6af23 100644
--- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomInmemoryDataBrokerModule.java
+++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomInmemoryDataBrokerModule.java
@@ -10,9 +10,11 @@ package org.opendaylight.controller.config.yang.md.sal.dom.impl;
import java.util.concurrent.Executors;
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.dom.broker.impl.DOMDataBrokerImpl;
import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
import org.opendaylight.controller.sal.core.spi.data.DOMStore;
+import org.opendaylight.yangtools.util.concurrent.DeadlockDetectingListeningExecutorService;
import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.ListeningExecutorService;
@@ -50,7 +52,7 @@ public final class DomInmemoryDataBrokerModule extends
//we will default to InMemoryDOMDataStore creation
operStore = new InMemoryDOMDataStore("DOM-OPER", storeExecutor);
//here we will register the SchemaContext listener
- getSchemaServiceDependency().registerSchemaServiceListener((InMemoryDOMDataStore)operStore);
+ getSchemaServiceDependency().registerSchemaContextListener((InMemoryDOMDataStore)operStore);
}
DOMStore configStore = getConfigDataStoreDependency();
@@ -58,13 +60,15 @@ public final class DomInmemoryDataBrokerModule extends
//we will default to InMemoryDOMDataStore creation
configStore = new InMemoryDOMDataStore("DOM-CFG", storeExecutor);
//here we will register the SchemaContext listener
- getSchemaServiceDependency().registerSchemaServiceListener((InMemoryDOMDataStore)configStore);
+ getSchemaServiceDependency().registerSchemaContextListener((InMemoryDOMDataStore)configStore);
}
ImmutableMap datastores = ImmutableMap
. builder().put(LogicalDatastoreType.OPERATIONAL, operStore)
.put(LogicalDatastoreType.CONFIGURATION, configStore).build();
- DOMDataBrokerImpl newDataBroker = new DOMDataBrokerImpl(datastores, MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()));
+ DOMDataBrokerImpl newDataBroker = new DOMDataBrokerImpl(datastores,
+ new DeadlockDetectingListeningExecutorService(Executors.newSingleThreadExecutor(),
+ TransactionCommitDeadlockException.DEADLOCK_EXECUTOR_FUNCTION));
return newDataBroker;
}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/SchemaServiceImplSingletonModule.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/SchemaServiceImplSingletonModule.java
index fbc418dc2a..62b026430a 100644
--- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/SchemaServiceImplSingletonModule.java
+++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/SchemaServiceImplSingletonModule.java
@@ -13,7 +13,7 @@ import org.opendaylight.yangtools.concepts.Delegator;
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.SchemaServiceListener;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -90,8 +90,8 @@ org.opendaylight.controller.config.yang.md.sal.dom.impl.AbstractSchemaServiceImp
}
@Override
- public ListenerRegistration registerSchemaServiceListener(final SchemaServiceListener arg0) {
- return delegate.registerSchemaServiceListener(arg0);
+ public ListenerRegistration registerSchemaContextListener(final SchemaContextListener arg0) {
+ return delegate.registerSchemaContextListener(arg0);
}
@Override
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataCommitCoordinatorImpl.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataCommitCoordinatorImpl.java
index 8b9eb445fd..9a6d12fb18 100644
--- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataCommitCoordinatorImpl.java
+++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMDataCommitCoordinatorImpl.java
@@ -15,6 +15,7 @@ import javax.annotation.concurrent.GuardedBy;
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.concurrent.MappingCheckedFuture;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -91,7 +92,8 @@ public class DOMDataCommitCoordinatorImpl implements DOMDataCommitExecutor {
Futures.addCallback(commitFuture, new DOMDataCommitErrorInvoker(transaction, listener.get()));
}
- return Futures.makeChecked(commitFuture, TransactionCommitFailedExceptionMapper.COMMIT_ERROR_MAPPER);
+ return MappingCheckedFuture.create(commitFuture,
+ TransactionCommitFailedExceptionMapper.COMMIT_ERROR_MAPPER);
}
/**
@@ -285,7 +287,8 @@ public class DOMDataCommitCoordinatorImpl implements DOMDataCommitExecutor {
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
ListenableFuture compositeResult = (ListenableFuture) Futures.allAsList(ops.build());
- return Futures.makeChecked(compositeResult, TransactionCommitFailedExceptionMapper.PRE_COMMIT_MAPPER);
+ return MappingCheckedFuture.create(compositeResult,
+ TransactionCommitFailedExceptionMapper.PRE_COMMIT_MAPPER);
}
/**
@@ -316,7 +319,8 @@ public class DOMDataCommitCoordinatorImpl implements DOMDataCommitExecutor {
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
ListenableFuture compositeResult = (ListenableFuture) Futures.allAsList(ops.build());
- return Futures.makeChecked(compositeResult, TransactionCommitFailedExceptionMapper.COMMIT_ERROR_MAPPER);
+ return MappingCheckedFuture.create(compositeResult,
+ TransactionCommitFailedExceptionMapper.COMMIT_ERROR_MAPPER);
}
/**
@@ -342,8 +346,8 @@ public class DOMDataCommitCoordinatorImpl implements DOMDataCommitExecutor {
}
ListenableFuture> allCanCommits = Futures.allAsList(canCommitOperations.build());
ListenableFuture allSuccessFuture = Futures.transform(allCanCommits, AND_FUNCTION);
- return Futures
- .makeChecked(allSuccessFuture, TransactionCommitFailedExceptionMapper.CAN_COMMIT_ERROR_MAPPER);
+ return MappingCheckedFuture.create(allSuccessFuture,
+ TransactionCommitFailedExceptionMapper.CAN_COMMIT_ERROR_MAPPER);
}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMForwardedReadOnlyTransaction.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMForwardedReadOnlyTransaction.java
index c8edcbc6e2..b4562cf2ec 100644
--- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMForwardedReadOnlyTransaction.java
+++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMForwardedReadOnlyTransaction.java
@@ -8,6 +8,7 @@
package org.opendaylight.controller.md.sal.dom.broker.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.dom.api.DOMDataReadOnlyTransaction;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
@@ -15,7 +16,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
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.CheckedFuture;
/**
*
@@ -34,8 +35,8 @@ class DOMForwardedReadOnlyTransaction extends
}
@Override
- public ListenableFuture>> read(final LogicalDatastoreType store,
- final YangInstanceIdentifier path) {
+ public CheckedFuture>, ReadFailedException> read(
+ final LogicalDatastoreType store, final YangInstanceIdentifier path) {
return getSubtransaction(store).read(path);
}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMForwardedReadWriteTransaction.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMForwardedReadWriteTransaction.java
index e6521b2377..74a4c52e36 100644
--- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMForwardedReadWriteTransaction.java
+++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMForwardedReadWriteTransaction.java
@@ -7,6 +7,7 @@
*/package org.opendaylight.controller.md.sal.dom.broker.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.dom.api.DOMDataReadWriteTransaction;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
@@ -14,7 +15,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
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.CheckedFuture;
/**
*
@@ -45,8 +46,8 @@ class DOMForwardedReadWriteTransaction extends DOMForwardedWriteTransaction>> read(final LogicalDatastoreType store,
- final YangInstanceIdentifier path) {
+ public CheckedFuture>, ReadFailedException> read(
+ final LogicalDatastoreType store, final YangInstanceIdentifier path) {
return getSubtransaction(store).read(path);
}
}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/TransactionCommitFailedExceptionMapper.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/TransactionCommitFailedExceptionMapper.java
index 258b068929..799a8a09ed 100644
--- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/TransactionCommitFailedExceptionMapper.java
+++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/TransactionCommitFailedExceptionMapper.java
@@ -7,29 +7,16 @@
*/
package org.opendaylight.controller.md.sal.dom.broker.impl;
-import java.util.concurrent.ExecutionException;
-
import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
-
-import com.google.common.base.Function;
-import com.google.common.base.Preconditions;
+import org.opendaylight.yangtools.util.concurrent.ExceptionMapper;
/**
+ * Utility exception mapper which translates Exception to {@link TransactionCommitFailedException}.
*
- * Utility exception mapper which translates {@link Exception}
- * to {@link TransactionCommitFailedException}.
- *
- * This mapper is intended to be used with {@link com.google.common.util.concurrent.Futures#makeChecked(com.google.common.util.concurrent.ListenableFuture, Function)}
- *
- *
if exception is {@link TransactionCommitFailedException} or one of its subclasses returns original exception.
- *
if exception is {@link ExecutionException} and cause is {@link TransactionCommitFailedException} return cause
- *
otherwise returns {@link TransactionCommitFailedException} with original exception as a cause.
- *
- *
+ * @see ExceptionMapper
*/
-
-final class TransactionCommitFailedExceptionMapper implements
- Function {
+final class TransactionCommitFailedExceptionMapper
+ extends ExceptionMapper {
static final TransactionCommitFailedExceptionMapper PRE_COMMIT_MAPPER = create("canCommit");
@@ -37,10 +24,8 @@ final class TransactionCommitFailedExceptionMapper implements
static final TransactionCommitFailedExceptionMapper COMMIT_ERROR_MAPPER = create("commit");
- private final String opName;
-
private TransactionCommitFailedExceptionMapper(final String opName) {
- this.opName = Preconditions.checkNotNull(opName);
+ super( opName, TransactionCommitFailedException.class );
}
public static final TransactionCommitFailedExceptionMapper create(final String opName) {
@@ -48,22 +33,7 @@ final class TransactionCommitFailedExceptionMapper implements
}
@Override
- public TransactionCommitFailedException apply(final Exception e) {
- // If excetion is TransactionCommitFailedException
- // we reuse it directly.
- if (e instanceof TransactionCommitFailedException) {
- return (TransactionCommitFailedException) e;
- }
- // If error is ExecutionException which was caused by cause of
- // TransactionCommitFailedException
- // we reuse original cause
- if (e instanceof ExecutionException && e.getCause() instanceof TransactionCommitFailedException) {
- return (TransactionCommitFailedException) e.getCause();
- }
- if (e instanceof InterruptedException) {
- return new TransactionCommitFailedException(opName + " failed - DOMStore was interupted.", e);
- }
- // Otherwise we are using new exception, with original cause
- return new TransactionCommitFailedException(opName + " failed", e);
+ protected TransactionCommitFailedException newWithCause( String message, Throwable cause ) {
+ return new TransactionCommitFailedException( message, cause );
}
}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/compat/BackwardsCompatibleDataBroker.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/compat/BackwardsCompatibleDataBroker.java
index e8f8da53c9..dc122cfdc2 100644
--- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/compat/BackwardsCompatibleDataBroker.java
+++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/compat/BackwardsCompatibleDataBroker.java
@@ -1,3 +1,10 @@
+/*
+ * 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.compat;
import javax.annotation.concurrent.ThreadSafe;
@@ -21,18 +28,17 @@ import org.opendaylight.yangtools.yang.data.api.CompositeNode;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
-import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
@ThreadSafe
public class BackwardsCompatibleDataBroker implements DataProviderService {
private final DOMDataBroker backingBroker;
private volatile DataNormalizer normalizer;
- private final ListenerRegistration schemaReg;
+ private final ListenerRegistration schemaReg;
public BackwardsCompatibleDataBroker(final DOMDataBroker newBiDataImpl, final SchemaService schemaService) {
backingBroker = newBiDataImpl;
- schemaReg = schemaService.registerSchemaServiceListener(new SchemaListener());
+ schemaReg = schemaService.registerSchemaContextListener(new SchemaListener());
}
@Override
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/BackwardsCompatibleMountPoint.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/BackwardsCompatibleMountPoint.java
index d837d75ddc..5bd8a7bc02 100644
--- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/BackwardsCompatibleMountPoint.java
+++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/BackwardsCompatibleMountPoint.java
@@ -15,16 +15,20 @@ import com.google.common.util.concurrent.CheckedFuture;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.JdkFutureAdapters;
import com.google.common.util.concurrent.ListenableFuture;
+
import java.util.List;
import java.util.Map;
import java.util.Set;
+
import javax.annotation.Nullable;
+
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.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.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
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.common.api.routing.RouteChangeListener;
@@ -72,7 +76,7 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgum
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
public class BackwardsCompatibleMountPoint implements MountProvisionInstance, SchemaContextProvider, SchemaService {
@@ -83,7 +87,7 @@ public class BackwardsCompatibleMountPoint implements MountProvisionInstance, Sc
private final NotificationPublishService notificationPublishService;
private final RpcProvisionRegistry rpcs;
- private final ListenerRegistry schemaListenerRegistry = new ListenerRegistry<>();
+ private final ListenerRegistry schemaListenerRegistry = new ListenerRegistry<>();
private SchemaContext schemaContext;
@@ -154,7 +158,7 @@ public class BackwardsCompatibleMountPoint implements MountProvisionInstance, Sc
}
@Override
- public ListenerRegistration registerSchemaServiceListener(final SchemaServiceListener listener) {
+ public ListenerRegistration registerSchemaContextListener(final SchemaContextListener listener) {
return schemaListenerRegistry.register(listener);
}
@@ -275,7 +279,7 @@ public class BackwardsCompatibleMountPoint implements MountProvisionInstance, Sc
@Override
public void setSchemaContext(final SchemaContext schemaContext) {
this.schemaContext = schemaContext;
- for (ListenerRegistration schemaServiceListenerListenerRegistration : schemaListenerRegistry.getListeners()) {
+ for (ListenerRegistration schemaServiceListenerListenerRegistration : schemaListenerRegistry.getListeners()) {
schemaServiceListenerListenerRegistration.getInstance().onGlobalContextUpdated(schemaContext);
}
}
@@ -380,7 +384,8 @@ public class BackwardsCompatibleMountPoint implements MountProvisionInstance, Sc
}
@Override
- public ListenableFuture>> read(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+ public CheckedFuture>, ReadFailedException> read(
+ final LogicalDatastoreType store, final YangInstanceIdentifier path) {
CompositeNode rawData = null;
@@ -398,7 +403,7 @@ public class BackwardsCompatibleMountPoint implements MountProvisionInstance, Sc
final Map.Entry> normalized = normalizer.toNormalized(path, rawData);
final Optional> normalizedNodeOptional = Optional.>fromNullable(normalized.getValue());
- return com.google.common.util.concurrent.Futures.immediateFuture(normalizedNodeOptional);
+ return Futures.immediateCheckedFuture(normalizedNodeOptional);
}
}
@@ -508,7 +513,8 @@ public class BackwardsCompatibleMountPoint implements MountProvisionInstance, Sc
}
@Override
- public ListenableFuture>> read(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+ public CheckedFuture>, ReadFailedException> read(
+ final LogicalDatastoreType store, final YangInstanceIdentifier path) {
return new BackwardsCompatibleReadTransaction(dataReader, dataNormalizer).read(store, path);
}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/GlobalBundleScanningSchemaServiceImpl.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/GlobalBundleScanningSchemaServiceImpl.java
index d8174c312a..82637327f6 100644
--- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/GlobalBundleScanningSchemaServiceImpl.java
+++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/GlobalBundleScanningSchemaServiceImpl.java
@@ -23,7 +23,7 @@ import org.opendaylight.yangtools.concepts.Registration;
import org.opendaylight.yangtools.concepts.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.SchemaServiceListener;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
import org.opendaylight.yangtools.yang.parser.impl.util.URLSchemaContextResolver;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
@@ -40,15 +40,15 @@ import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
-public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvider, SchemaService, ServiceTrackerCustomizer, AutoCloseable {
+public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvider, SchemaService, ServiceTrackerCustomizer, AutoCloseable {
private static final Logger LOG = LoggerFactory.getLogger(GlobalBundleScanningSchemaServiceImpl.class);
- private final ListenerRegistry listeners = new ListenerRegistry<>();
+ private final ListenerRegistry listeners = new ListenerRegistry<>();
private final URLSchemaContextResolver contextResolver = new URLSchemaContextResolver();
private final BundleScanner scanner = new BundleScanner();
private final BundleContext context;
- private ServiceTracker listenerTracker;
+ private ServiceTracker listenerTracker;
private BundleTracker> bundleTracker;
private boolean starting = true;
private static GlobalBundleScanningSchemaServiceImpl instance;
@@ -81,7 +81,7 @@ public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvi
public void start() {
checkState(context != null);
- listenerTracker = new ServiceTracker<>(context, SchemaServiceListener.class, GlobalBundleScanningSchemaServiceImpl.this);
+ listenerTracker = new ServiceTracker<>(context, SchemaContextListener.class, GlobalBundleScanningSchemaServiceImpl.this);
bundleTracker = new BundleTracker<>(context, BundleEvent.RESOLVED | BundleEvent.UNRESOLVED, scanner);
bundleTracker.open();
listenerTracker.open();
@@ -115,7 +115,7 @@ public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvi
}
@Override
- public synchronized ListenerRegistration registerSchemaServiceListener(final SchemaServiceListener listener) {
+ public synchronized ListenerRegistration registerSchemaContextListener(final SchemaContextListener listener) {
Optional potentialCtx = contextResolver.getSchemaContext();
if(potentialCtx.isPresent()) {
listener.onGlobalContextUpdated(potentialCtx.get());
@@ -137,7 +137,7 @@ public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvi
private synchronized void updateContext(final SchemaContext snapshot) {
Object[] services = listenerTracker.getServices();
- for (ListenerRegistration listener : listeners) {
+ for (ListenerRegistration listener : listeners) {
try {
listener.getInstance().onGlobalContextUpdated(snapshot);
} catch (Exception e) {
@@ -146,7 +146,7 @@ public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvi
}
if (services != null) {
for (Object rawListener : services) {
- SchemaServiceListener listener = (SchemaServiceListener) rawListener;
+ final SchemaContextListener listener = (SchemaContextListener) rawListener;
try {
listener.onGlobalContextUpdated(snapshot);
} catch (Exception e) {
@@ -213,9 +213,9 @@ public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvi
}
@Override
- public synchronized SchemaServiceListener addingService(final ServiceReference reference) {
+ public synchronized SchemaContextListener addingService(final ServiceReference reference) {
- SchemaServiceListener listener = context.getService(reference);
+ SchemaContextListener listener = context.getService(reference);
SchemaContext _ctxContext = getGlobalContext();
if (getContext() != null && _ctxContext != null) {
listener.onGlobalContextUpdated(_ctxContext);
@@ -234,12 +234,12 @@ public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvi
}
@Override
- public void modifiedService(final ServiceReference reference, final SchemaServiceListener service) {
+ public void modifiedService(final ServiceReference reference, final SchemaContextListener service) {
// NOOP
}
@Override
- public void removedService(final ServiceReference reference, final SchemaServiceListener service) {
+ public void removedService(final ServiceReference reference, final SchemaContextListener service) {
context.ungetService(reference);
}
}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/SchemaServiceProxy.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/SchemaServiceProxy.java
index 1d864eec5b..d8d2346a8c 100644
--- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/SchemaServiceProxy.java
+++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/SchemaServiceProxy.java
@@ -8,10 +8,10 @@
package org.opendaylight.controller.sal.dom.broker.osgi;
import org.opendaylight.controller.sal.core.api.model.SchemaService;
-import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
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 implements SchemaService {
@@ -41,12 +41,9 @@ public class SchemaServiceProxy extends AbstractBrokerServiceProxy registerSchemaServiceListener(SchemaServiceListener listener) {
- ListenerRegistration registration = getDelegate().registerSchemaServiceListener(listener);
+ public ListenerRegistration registerSchemaContextListener(SchemaContextListener listener) {
+ ListenerRegistration registration = getDelegate().registerSchemaContextListener(listener);
addRegistration(registration);
return registration;
}
-
-
-
}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMBrokerTest.java b/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMBrokerTest.java
index b006ca94e5..0bb16a39b9 100644
--- a/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMBrokerTest.java
+++ b/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMBrokerTest.java
@@ -3,26 +3,40 @@ package org.opendaylight.controller.md.sal.dom.broker.impl;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
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 java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
+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.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.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.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 com.google.common.base.Optional;
import com.google.common.collect.ImmutableMap;
+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;
@@ -31,6 +45,7 @@ public class DOMBrokerTest {
private SchemaContext schemaContext;
private DOMDataBrokerImpl domBroker;
+ private ListeningExecutorService executor;
@Before
public void setupStore() {
@@ -46,11 +61,19 @@ public class DOMBrokerTest {
.put(OPERATIONAL, operStore) //
.build();
- ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor());
+ executor = new DeadlockDetectingListeningExecutorService(Executors.newSingleThreadExecutor(),
+ TransactionCommitDeadlockException.DEADLOCK_EXECUTOR_FUNCTION);
domBroker = new DOMDataBrokerImpl(stores, executor);
}
- @Test
+ @After
+ public void tearDown() {
+ if( executor != null ) {
+ executor.shutdownNow();
+ }
+ }
+
+ @Test(timeout=10000)
public void testTransactionIsolation() throws InterruptedException, ExecutionException {
assertNotNull(domBroker);
@@ -86,7 +109,7 @@ public class DOMBrokerTest {
assertFalse(readTxContainer.get().isPresent());
}
- @Test
+ @Test(timeout=10000)
public void testTransactionCommit() throws InterruptedException, ExecutionException {
DOMDataReadWriteTransaction writeTx = domBroker.newReadWriteTransaction();
@@ -114,6 +137,173 @@ public class DOMBrokerTest {
assertTrue(afterCommitRead.isPresent());
}
+ /**
+ * 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 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 caughtCommitEx = new AtomicReference<>();
+ final CountDownLatch commitCompletedLatch = new CountDownLatch( 1 );
+
+ TestDOMDataChangeListener dcListener = new TestDOMDataChangeListener() {
+ @Override
+ public void onDataChanged( AsyncDataChangeEvent> change ) {
+
+ DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
+ writeTx.put( OPERATIONAL, TestModel.TEST2_PATH,
+ ImmutableNodes.containerNode( TestModel.TEST2_QNAME ) );
+ Futures.addCallback( writeTx.submit(), new FutureCallback() {
+ @Override
+ public void onSuccess( Void result ) {
+ commitCompletedLatch.countDown();
+ }
+
+ @Override
+ public void onFailure( 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 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 caughtCommitEx = new AtomicReference<>();
+
+ TestDOMDataChangeListener dcListener = new TestDOMDataChangeListener() {
+ @Override
+ public void onDataChanged( AsyncDataChangeEvent> 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 caughtEx = submitTxAsync( writeTx );
+ dcListener.waitForChange();
+ if( caughtEx.get() != null ) {
+ throw caughtEx.get();
+ }
+
+ if( caughtCommitEx.get() != null ) {
+ throw caughtCommitEx.get();
+ }
+ }
+
+ AtomicReference submitTxAsync( final DOMDataWriteTransaction writeTx ) {
+ final AtomicReference 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> change;
+ private final CountDownLatch latch = new CountDownLatch( 1 );
+
+ @Override
+ public void onDataChanged( AsyncDataChangeEvent> change ) {
+ this.change = change;
+ latch.countDown();
+ }
+
+ void waitForChange() throws InterruptedException {
+ assertTrue( "onDataChanged was not called", latch.await( 5, TimeUnit.SECONDS ) );
+ }
+ }
}
diff --git a/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/TestModel.java b/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/TestModel.java
index d5ba2a2b9a..09835ec5e3 100644
--- a/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/TestModel.java
+++ b/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/TestModel.java
@@ -21,6 +21,8 @@ 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");
@@ -30,6 +32,7 @@ public class TestModel {
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");
diff --git a/opendaylight/md-sal/sal-dom-broker/src/test/resources/odl-datastore-test.yang b/opendaylight/md-sal/sal-dom-broker/src/test/resources/odl-datastore-test.yang
index 17541fecab..5fbf470f09 100644
--- a/opendaylight/md-sal/sal-dom-broker/src/test/resources/odl-datastore-test.yang
+++ b/opendaylight/md-sal/sal-dom-broker/src/test/resources/odl-datastore-test.yang
@@ -39,4 +39,7 @@ module odl-datastore-test {
}
}
}
+
+ container test2 {
+ }
}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreReadTransaction.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreReadTransaction.java
index ae1b3ee2aa..84d09c7cb0 100644
--- a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreReadTransaction.java
+++ b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreReadTransaction.java
@@ -7,29 +7,31 @@
*/
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.ListenableFuture;
+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 Listenable Future which contains read result
+ * @return a CheckFuture containing the result of the read. The Future blocks until the
+ * commit operation is complete. Once complete:
*
- *
If data at supplied path exists the {@link java.util.concurrent.Future#get()}
- * returns Optional object containing data
- *
If data at supplied path does not exists the
- * {@link java.util.concurrent.Future#get()} returns {@link Optional#absent()}.
+ *
If the data at the supplied path exists, the Future returns an Optional object
+ * containing the data.
+ *
If the data at the supplied path does not exist, the Future returns
+ * Optional#absent().
+ *
If the read of the data fails, the Future will fail with a
+ * {@link ReadFailedException} or an exception derived from ReadFailedException.
*
*/
- ListenableFuture>> read(YangInstanceIdentifier path);
+ CheckedFuture>, ReadFailedException> read(YangInstanceIdentifier path);
}
diff --git a/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/yang/gen/v1/http/netconfcentral/org/ns/xsql/rev140626/XSQLModule.java b/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/yang/gen/v1/http/netconfcentral/org/ns/xsql/rev140626/XSQLModule.java
index 399f49bd6b..59cdc76e7a 100644
--- a/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/yang/gen/v1/http/netconfcentral/org/ns/xsql/rev140626/XSQLModule.java
+++ b/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/yang/gen/v1/http/netconfcentral/org/ns/xsql/rev140626/XSQLModule.java
@@ -20,7 +20,7 @@ public class XSQLModule extends org.opendaylight.yang.gen.v1.http.netconfcentral
@Override
public java.lang.AutoCloseable createInstance() {
XSQLAdapter xsqlAdapter = XSQLAdapter.getInstance();
- getSchemaServiceDependency().registerSchemaServiceListener(xsqlAdapter);
+ getSchemaServiceDependency().registerSchemaContextListener(xsqlAdapter);
xsqlAdapter.setDataBroker(getAsyncDataBrokerDependency());
XSQLProvider p = new XSQLProvider();
p.buildXSQL(getDataBrokerDependency());
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/pom.xml b/opendaylight/md-sal/sal-inmemory-datastore/pom.xml
index 906847426c..725b24cd9e 100644
--- a/opendaylight/md-sal/sal-inmemory-datastore/pom.xml
+++ b/opendaylight/md-sal/sal-inmemory-datastore/pom.xml
@@ -112,12 +112,16 @@
binding-generator-impltest
+
+ org.opendaylight.yangtools
+ mockito-configuration
+ test
+ org.opendaylight.controllersal-test-modeltest
- ${project.version}
-
+
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryConfigDataStoreProviderModule.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryConfigDataStoreProviderModule.java
index 01a5989dcd..805608d479 100644
--- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryConfigDataStoreProviderModule.java
+++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryConfigDataStoreProviderModule.java
@@ -23,7 +23,7 @@ public class InMemoryConfigDataStoreProviderModule extends org.opendaylight.cont
@Override
public java.lang.AutoCloseable createInstance() {
InMemoryDOMDataStore ids = new InMemoryDOMDataStore("DOM-CFG", MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()));
- getSchemaServiceDependency().registerSchemaServiceListener(ids);
+ getSchemaServiceDependency().registerSchemaContextListener(ids);
return ids;
}
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryOperationalDataStoreProviderModule.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryOperationalDataStoreProviderModule.java
index b39c9bbbd8..f4795588ab 100644
--- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryOperationalDataStoreProviderModule.java
+++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryOperationalDataStoreProviderModule.java
@@ -23,7 +23,7 @@ public class InMemoryOperationalDataStoreProviderModule extends org.opendaylight
@Override
public java.lang.AutoCloseable createInstance() {
InMemoryDOMDataStore ids = new InMemoryDOMDataStore("DOM-OPER", MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()));
- getOperationalSchemaServiceDependency().registerSchemaServiceListener(ids);
+ getOperationalSchemaServiceDependency().registerSchemaContextListener(ids);
return ids;
}
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ResolveDataChangeEventsTask.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ResolveDataChangeEventsTask.java
index ff64cd64c4..3ddf0b60fa 100644
--- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ResolveDataChangeEventsTask.java
+++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ResolveDataChangeEventsTask.java
@@ -9,9 +9,15 @@ package org.opendaylight.controller.md.sal.dom.store.impl;
import static org.opendaylight.controller.md.sal.dom.store.impl.DOMImmutableDataChangeEvent.builder;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
+
import java.util.Collection;
import java.util.Collections;
-import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
@@ -37,13 +43,6 @@ import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Multimap;
-
/**
* Resolve Data Change Events based on modifications and listeners
*
@@ -278,6 +277,11 @@ final class ResolveDataChangeEventsTask implements Callable listeners, final NormalizedNode, ?> beforeData,
final NormalizedNode, ?> afterData) {
+ // FIXME: BUG-1493: check the listeners to prune unneeded changes:
+ // for subtrees, we have to do all
+ // for one, we need to expand children
+ // for base, we just report replacement
+
if (beforeData instanceof NormalizedNodeContainer, ?, ?>) {
// Node is container (contains child) and we have interested
// listeners registered for it, that means we need to do
@@ -306,14 +310,12 @@ final class ResolveDataChangeEventsTask implements Callable listeners,
final NormalizedNodeContainer, PathArgument, NormalizedNode> beforeCont,
final NormalizedNodeContainer, PathArgument, NormalizedNode> afterCont) {
- final Set alreadyProcessed = new HashSet<>();
final List childChanges = new LinkedList<>();
- DataChangeScope potentialScope = DataChangeScope.BASE;
// We look at all children from before and compare it with after state.
for (NormalizedNode beforeChild : beforeCont.getValue()) {
- PathArgument childId = beforeChild.getIdentifier();
- alreadyProcessed.add(childId);
+ final PathArgument childId = beforeChild.getIdentifier();
+
YangInstanceIdentifier childPath = path.node(childId);
Collection childListeners = getListenerChildrenWildcarded(listeners, childId);
Optional> afterChild = afterCont.getChild(childId);
@@ -323,15 +325,17 @@ final class ResolveDataChangeEventsTask implements Callable afterChild : afterCont.getValue()) {
- PathArgument childId = afterChild.getIdentifier();
- if (!alreadyProcessed.contains(childId)) {
- // We did not processed that child already
- // and it was not present in previous loop, that means it is
- // created.
+ 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()) {
Collection childListeners = getListenerChildrenWildcarded(listeners, childId);
YangInstanceIdentifier childPath = path.node(childId);
childChanges.add(resolveSameEventRecursivelly(childPath , childListeners, afterChild,
@@ -342,7 +346,7 @@ final class ResolveDataChangeEventsTask implements Callable>> read(final YangInstanceIdentifier path) {
+ public CheckedFuture>, ReadFailedException> read(final YangInstanceIdentifier path) {
+ LOG.debug("Tx: {} Read: {}", getIdentifier(), path);
checkNotNull(path, "Path must not be null.");
- checkState(stableSnapshot != null, "Transaction is closed");
- return Futures.immediateFuture(stableSnapshot.readNode(path));
+
+ if(stableSnapshot == null) {
+ return Futures.immediateFailedCheckedFuture(new ReadFailedException("Transaction is closed"));
+ }
+
+ try {
+ return Futures.immediateCheckedFuture(stableSnapshot.readNode(path));
+ } catch (Exception e) {
+ LOG.error("Tx: {} Failed Read of {}", getIdentifier(), path, e);
+ return Futures.immediateFailedCheckedFuture(new ReadFailedException("Read failed",e));
+ }
}
}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedReadWriteTransaction.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedReadWriteTransaction.java
index ec17d7a3f7..5c5e9c6b6d 100644
--- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedReadWriteTransaction.java
+++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SnapshotBackedReadWriteTransaction.java
@@ -7,7 +7,11 @@
*/
package org.opendaylight.controller.md.sal.dom.store.impl;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
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;
@@ -15,16 +19,16 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
/**
* Implementation of Read-Write transaction which is backed by {@link DataTreeSnapshot}
* and executed according to {@link TransactionReadyPrototype}.
*
*/
-class SnapshotBackedReadWriteTransaction extends SnapshotBackedWriteTransaction implements
-DOMStoreReadWriteTransaction {
+class SnapshotBackedReadWriteTransaction extends SnapshotBackedWriteTransaction
+ implements DOMStoreReadWriteTransaction {
private static final Logger LOG = LoggerFactory.getLogger(SnapshotBackedReadWriteTransaction.class);
@@ -41,13 +45,20 @@ DOMStoreReadWriteTransaction {
}
@Override
- public ListenableFuture>> read(final YangInstanceIdentifier path) {
+ public CheckedFuture>, ReadFailedException> read(final YangInstanceIdentifier path) {
LOG.debug("Tx: {} Read: {}", getIdentifier(), path);
+ checkNotNull(path, "Path must not be null.");
+
+ DataTreeModification dataView = getMutatedView();
+ if(dataView == null) {
+ return Futures.immediateFailedCheckedFuture(new ReadFailedException("Transaction is closed"));
+ }
+
try {
- return Futures.immediateFuture(getMutatedView().readNode(path));
+ return Futures.immediateCheckedFuture(dataView.readNode(path));
} catch (Exception e) {
LOG.error("Tx: {} Failed Read of {}", getIdentifier(), path, e);
- throw e;
+ return Futures.immediateFailedCheckedFuture(new ReadFailedException("Read failed",e));
}
}
}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDataStoreTest.java b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDataStoreTest.java
index 96369dea5f..9b105aa306 100644
--- a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDataStoreTest.java
+++ b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDataStoreTest.java
@@ -7,6 +7,7 @@
*/
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;
@@ -16,12 +17,22 @@ 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.md.sal.dom.store.impl.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
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.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;
import com.google.common.base.Optional;
@@ -31,226 +42,373 @@ import com.google.common.util.concurrent.MoreExecutors;
public class InMemoryDataStoreTest {
- private SchemaContext schemaContext;
- private InMemoryDOMDataStore domStore;
+ private SchemaContext schemaContext;
+ private InMemoryDOMDataStore domStore;
- @Before
- public void setupStore() {
- domStore = new InMemoryDOMDataStore("TEST", MoreExecutors.sameThreadExecutor());
- schemaContext = TestModel.createTestContext();
- domStore.onGlobalContextUpdated(schemaContext);
+ @Before
+ public void setupStore() {
+ domStore = new InMemoryDOMDataStore("TEST", MoreExecutors.sameThreadExecutor());
+ schemaContext = TestModel.createTestContext();
+ domStore.onGlobalContextUpdated(schemaContext);
+ }
- }
+ @Test
+ public void testTransactionIsolation() throws InterruptedException, ExecutionException {
- @Test
- public void testTransactionIsolation() throws InterruptedException, ExecutionException {
+ assertNotNull(domStore);
- assertNotNull(domStore);
+ DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
+ assertNotNull(readTx);
- DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
- assertNotNull(readTx);
+ DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
+ assertNotNull(writeTx);
- DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
- assertNotNull(writeTx);
- /**
- *
- * Writes /test in writeTx
- *
- */
- writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+ /**
+ * 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>> writeTxContainer = writeTx.read(TestModel.TEST_PATH);
- assertTrue(writeTxContainer.get().isPresent());
+ /**
+ * Reads /test from writeTx Read should return container.
+ */
+ ListenableFuture>> 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>> readTxContainer = readTx.read(TestModel.TEST_PATH);
- assertFalse(readTxContainer.get().isPresent());
- }
+ /**
+ * Reads /test from readTx Read should return Absent.
+ */
+ ListenableFuture>> readTxContainer = readTx.read(TestModel.TEST_PATH);
+ assertEquals("read: isPresent", false, readTxContainer.get().isPresent());
+ }
- @Test
- public void testTransactionCommit() throws InterruptedException, ExecutionException {
+ @Test
+ public void testTransactionCommit() throws InterruptedException, ExecutionException {
- DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
- assertNotNull(writeTx);
- /**
- *
- * Writes /test in writeTx
- *
- */
- writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+ DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
+ assertNotNull(writeTx);
- /**
- *
- * Reads /test from writeTx Read should return container.
- *
- */
- ListenableFuture>> writeTxContainer = writeTx.read(TestModel.TEST_PATH);
- assertTrue(writeTxContainer.get().isPresent());
+ /**
+ * Writes /test in writeTx
+ */
+ NormalizedNode, ?> testNode = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
+ writeTx.write(TestModel.TEST_PATH, testNode);
- DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
+ /**
+ * Reads /test from writeTx Read should return container.
+ */
+ ListenableFuture>> writeTxContainer = writeTx.read(TestModel.TEST_PATH);
+ assertEquals("read: isPresent", true, writeTxContainer.get().isPresent());
+ assertEquals("read: data", testNode, writeTxContainer.get().get());
- assertThreePhaseCommit(cohort);
+ DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
- Optional> afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)
- .get();
- assertTrue(afterCommitRead.isPresent());
- }
+ assertThreePhaseCommit(cohort);
- @Test
- public void testTransactionAbort() throws InterruptedException, ExecutionException {
+ Optional> afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)
+ .get();
+ assertEquals("After commit read: isPresent", true, afterCommitRead.isPresent());
+ assertEquals("After commit read: data", testNode, afterCommitRead.get());
+ }
- DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
- assertNotNull(writeTx);
+ @Test
+ public void testDelete() throws Exception {
- assertTestContainerWrite(writeTx);
+ DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
+ assertNotNull( writeTx );
- DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
+ // Write /test and commit
- assertTrue(cohort.canCommit().get().booleanValue());
- cohort.preCommit().get();
- cohort.abort().get();
+ writeTx.write( TestModel.TEST_PATH, ImmutableNodes.containerNode( TestModel.TEST_QNAME ) );
- Optional> afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)
- .get();
- assertFalse(afterCommitRead.isPresent());
- }
+ assertThreePhaseCommit( writeTx.ready() );
- @Test
- public void testTransactionChain() throws InterruptedException, ExecutionException {
- DOMStoreTransactionChain txChain = domStore.createTransactionChain();
- assertNotNull(txChain);
+ Optional> afterCommitRead = domStore.newReadOnlyTransaction().
+ read(TestModel.TEST_PATH ).get();
+ assertEquals( "After commit read: isPresent", true, afterCommitRead.isPresent() );
- /**
- * We alocate new read-write transaction and write /test
- *
- *
- */
- DOMStoreReadWriteTransaction firstTx = txChain.newReadWriteTransaction();
- assertTestContainerWrite(firstTx);
+ // Delete /test and verify
- /**
- * First transaction is marked as ready, we are able to allocate chained
- * transactions
- */
- DOMStoreThreePhaseCommitCohort firstWriteTxCohort = firstTx.ready();
+ writeTx = domStore.newWriteOnlyTransaction();
- /**
- * We alocate chained transaction - read transaction, note first one is
- * still not commited to datastore.
- */
- DOMStoreReadTransaction secondReadTx = txChain.newReadOnlyTransaction();
+ writeTx.delete( TestModel.TEST_PATH );
- /**
- *
- * We test if we are able to read data from tx, read should not fail
- * since we are using chained transaction.
- *
- *
- */
- assertTestContainerExists(secondReadTx);
+ assertThreePhaseCommit( writeTx.ready() );
- /**
- *
- * We alocate next transaction, which is still based on first one, but
- * is read-write.
- *
- */
- DOMStoreReadWriteTransaction thirdDeleteTx = txChain.newReadWriteTransaction();
+ afterCommitRead = domStore.newReadOnlyTransaction().
+ read(TestModel.TEST_PATH ).get();
+ assertEquals( "After commit read: isPresent", false, afterCommitRead.isPresent() );
+ }
- /**
- * We test existence of /test in third transaction container should
- * still be visible from first one (which is still uncommmited).
- *
- *
- */
- assertTestContainerExists(thirdDeleteTx);
+ @Test
+ public void testMerge() throws Exception {
- /**
- * We delete node in third transaction
- */
- thirdDeleteTx.delete(TestModel.TEST_PATH);
+ DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
+ assertNotNull( writeTx );
- /**
- * third transaction is sealed.
- */
- DOMStoreThreePhaseCommitCohort thirdDeleteTxCohort = thirdDeleteTx.ready();
+ 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();
- /**
- * We commit first transaction
- *
- */
- assertThreePhaseCommit(firstWriteTxCohort);
+ writeTx.merge( TestModel.TEST_PATH, containerNode );
- // 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);
- }
+ assertThreePhaseCommit( writeTx.ready() );
- @Test
- @Ignore
- public void testTransactionConflict() throws InterruptedException, ExecutionException {
- DOMStoreReadWriteTransaction txOne = domStore.newReadWriteTransaction();
- DOMStoreReadWriteTransaction txTwo = domStore.newReadWriteTransaction();
- assertTestContainerWrite(txOne);
- assertTestContainerWrite(txTwo);
+ Optional> afterCommitRead = domStore.newReadOnlyTransaction().
+ read(TestModel.TEST_PATH ).get();
+ assertEquals( "After commit read: isPresent", true, afterCommitRead.isPresent() );
+ assertEquals( "After commit read: data", containerNode, afterCommitRead.get() );
- /**
- * Commits transaction
- */
- assertThreePhaseCommit(txOne.ready());
+ // Merge a new list entry node
- /**
- * 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> assertTestContainerWrite(final DOMStoreReadWriteTransaction writeTx)
- throws InterruptedException, ExecutionException {
- /**
- *
- * Writes /test in writeTx
- *
- */
- writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+ 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(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 = new SnapshotBackedReadTransaction( "1", mockSnapshot );
+
+ doReadAndThrowEx( readTx );
+ }
- return assertTestContainerExists(writeTx);
- }
+ @Test(expected=ReadFailedException.class)
+ public void testReadWithReadWriteTransactionClosed() throws Throwable {
- /**
- * Reads /test from readTx Read should return container.
- */
- private static Optional> assertTestContainerExists(final DOMStoreReadTransaction readTx)
- throws InterruptedException, ExecutionException {
+ 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();
+ TransactionReadyPrototype mockReady = Mockito.mock( TransactionReadyPrototype.class );
+ DOMStoreReadTransaction readTx = new SnapshotBackedReadWriteTransaction( "1", mockSnapshot, mockReady );
+
+ doReadAndThrowEx( readTx );
+ }
+
+ private void doReadAndThrowEx( 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> 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> 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> assertTestContainerExists(final DOMStoreReadTransaction readTx)
+ throws InterruptedException, ExecutionException {
- ListenableFuture>> writeTxContainer = readTx.read(TestModel.TEST_PATH);
- assertTrue(writeTxContainer.get().isPresent());
- return writeTxContainer.get();
- }
+ ListenableFuture>> writeTxContainer = readTx.read(TestModel.TEST_PATH);
+ assertTrue(writeTxContainer.get().isPresent());
+ return writeTxContainer.get();
+ }
}
diff --git a/opendaylight/md-sal/sal-netconf-connector/pom.xml b/opendaylight/md-sal/sal-netconf-connector/pom.xml
index 10fe4a587a..049f8c2e3c 100644
--- a/opendaylight/md-sal/sal-netconf-connector/pom.xml
+++ b/opendaylight/md-sal/sal-netconf-connector/pom.xml
@@ -153,11 +153,6 @@
logback-configtest
-
- org.opendaylight.controller
- sal-binding-broker-impl
- test
- org.opendaylight.controllersal-binding-broker-impl
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java
index 037bfb4a82..b75df80f4e 100644
--- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java
+++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java
@@ -13,8 +13,10 @@ import static org.opendaylight.controller.config.api.JmxAttributeValidationExcep
import java.io.File;
import java.io.InputStream;
import java.net.InetSocketAddress;
+import java.util.List;
import java.util.concurrent.ExecutorService;
+import org.opendaylight.controller.config.api.JmxAttributeValidationException;
import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguration;
import org.opendaylight.controller.netconf.client.conf.NetconfReconnectingClientConfiguration;
@@ -24,6 +26,7 @@ import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
import org.opendaylight.controller.sal.connect.api.RemoteDeviceHandler;
import org.opendaylight.controller.sal.connect.netconf.NetconfDevice;
import org.opendaylight.controller.sal.connect.netconf.listener.NetconfDeviceCommunicator;
+import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionCapabilities;
import org.opendaylight.controller.sal.connect.netconf.sal.NetconfDeviceSalFacade;
import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
import org.opendaylight.controller.sal.core.api.Broker;
@@ -40,6 +43,8 @@ import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.base.Optional;
+
/**
*
*/
@@ -49,6 +54,7 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co
private static AbstractCachingSchemaSourceProvider GLOBAL_NETCONF_SOURCE_PROVIDER = null;
private BundleContext bundleContext;
+ private Optional userCapabilities;
public NetconfConnectorModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
super(identifier, dependencyResolver);
@@ -82,9 +88,11 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co
checkNotNull(getPassword(), passwordJmxAttribute);
}
+ userCapabilities = getUserCapabilities();
+
}
- private boolean isHostAddressPresent(Host address) {
+ private boolean isHostAddressPresent(final Host address) {
return address.getDomainName() != null ||
address.getIpAddress() != null && (address.getIpAddress().getIpv4Address() != null || address.getIpAddress().getIpv6Address() != null);
}
@@ -98,10 +106,14 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co
final Broker domBroker = getDomRegistryDependency();
final BindingAwareBroker bindingBroker = getBindingRegistryDependency();
- final RemoteDeviceHandler salFacade = new NetconfDeviceSalFacade(id, domBroker, bindingBroker, bundleContext, globalProcessingExecutor);
+ final RemoteDeviceHandler salFacade
+ = new NetconfDeviceSalFacade(id, domBroker, bindingBroker, bundleContext, globalProcessingExecutor);
final NetconfDevice device =
NetconfDevice.createNetconfDevice(id, getGlobalNetconfSchemaProvider(), globalProcessingExecutor, salFacade);
- final NetconfDeviceCommunicator listener = new NetconfDeviceCommunicator(id, device);
+
+ final NetconfDeviceCommunicator listener = userCapabilities.isPresent() ?
+ new NetconfDeviceCommunicator(id, device, userCapabilities.get()) : new NetconfDeviceCommunicator(id, device);
+
final NetconfReconnectingClientConfiguration clientConfig = getClientConfig(listener);
final NetconfClientDispatcher dispatcher = getClientDispatcherDependency();
@@ -116,6 +128,26 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co
};
}
+ private Optional getUserCapabilities() {
+ if(getYangModuleCapabilities() == null) {
+ return Optional.absent();
+ }
+
+ final List capabilities = getYangModuleCapabilities().getCapability();
+ if(capabilities == null || capabilities.isEmpty()) {
+ return Optional.absent();
+ }
+
+ final NetconfSessionCapabilities parsedOverrideCapabilities = NetconfSessionCapabilities.fromStrings(capabilities);
+ JmxAttributeValidationException.checkCondition(
+ parsedOverrideCapabilities.getNonModuleCaps().isEmpty(),
+ "Capabilities to override can only contain module based capabilities, non-module capabilities will be retrieved from the device," +
+ " configured non-module capabilities: " + parsedOverrideCapabilities.getNonModuleCaps(),
+ yangModuleCapabilitiesJmxAttribute);
+
+ return Optional.of(parsedOverrideCapabilities);
+ }
+
private synchronized AbstractCachingSchemaSourceProvider getGlobalNetconfSchemaProvider() {
if(GLOBAL_NETCONF_SOURCE_PROVIDER == null) {
final String storageFile = "cache/schema";
@@ -175,8 +207,8 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co
if(getAddress().getDomainName() != null) {
return new InetSocketAddress(getAddress().getDomainName().getValue(), getPort().getValue());
} else {
- IpAddress ipAddress = getAddress().getIpAddress();
- String ip = ipAddress.getIpv4Address() != null ? ipAddress.getIpv4Address().getValue() : ipAddress.getIpv6Address().getValue();
+ final IpAddress ipAddress = getAddress().getIpAddress();
+ final String ip = ipAddress.getIpv4Address() != null ? ipAddress.getIpv4Address().getValue() : ipAddress.getIpv6Address().getValue();
return new InetSocketAddress(ip, getPort().getValue());
}
}
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.java
index de4ac7ac18..07d3c08774 100644
--- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.java
+++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.java
@@ -118,6 +118,7 @@ public final class NetconfDevice implements RemoteDevice {
private static final Logger logger = LoggerFactory.getLogger(NetconfDeviceCommunicator.class);
private final RemoteDevice remoteDevice;
+ private final Optional overrideNetconfCapabilities;
private final RemoteDeviceId id;
private final Lock sessionLock = new ReentrantLock();
+ private final Queue requests = new ArrayDeque<>();
+ private NetconfClientSession session;
+
+ public NetconfDeviceCommunicator(final RemoteDeviceId id, final RemoteDevice remoteDevice,
+ final NetconfSessionCapabilities netconfSessionCapabilities) {
+ this(id, remoteDevice, Optional.of(netconfSessionCapabilities));
+ }
+
public NetconfDeviceCommunicator(final RemoteDeviceId id,
- final RemoteDevice remoteDevice) {
+ final RemoteDevice remoteDevice) {
+ this(id, remoteDevice, Optional.absent());
+ }
+
+ private NetconfDeviceCommunicator(final RemoteDeviceId id, final RemoteDevice remoteDevice,
+ final Optional overrideNetconfCapabilities) {
this.id = id;
this.remoteDevice = remoteDevice;
+ this.overrideNetconfCapabilities = overrideNetconfCapabilities;
}
- private final Queue requests = new ArrayDeque<>();
- private NetconfClientSession session;
-
@Override
public void onSessionUp(final NetconfClientSession session) {
sessionLock.lock();
@@ -68,10 +78,15 @@ public class NetconfDeviceCommunicator implements NetconfClientSessionListener,
logger.debug("{}: Session established", id);
this.session = session;
- final NetconfSessionCapabilities netconfSessionCapabilities =
+ NetconfSessionCapabilities netconfSessionCapabilities =
NetconfSessionCapabilities.fromNetconfSession(session);
logger.trace("{}: Session advertised capabilities: {}", id, netconfSessionCapabilities);
+ if(overrideNetconfCapabilities.isPresent()) {
+ netconfSessionCapabilities = netconfSessionCapabilities.replaceModuleCaps(overrideNetconfCapabilities.get());
+ logger.debug("{}: Session capabilities overridden, capabilities that will be used: {}", id, netconfSessionCapabilities);
+ }
+
remoteDevice.onRemoteSessionUp(netconfSessionCapabilities, this);
}
finally {
@@ -223,7 +238,7 @@ public class NetconfDeviceCommunicator implements NetconfClientSessionListener,
return;
}
- request.future.set( RpcResultBuilder.success( message ).build() );
+ request.future.set( RpcResultBuilder.success( message ).build() );
}
}
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfSessionCapabilities.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfSessionCapabilities.java
index 8964a80228..1a7d90e9c0 100644
--- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfSessionCapabilities.java
+++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfSessionCapabilities.java
@@ -8,6 +8,7 @@ import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
@@ -19,6 +20,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class NetconfSessionCapabilities {
+
private static final class ParameterMatcher {
private final Predicate predicate;
private final int skipLength;
@@ -57,10 +59,10 @@ public final class NetconfSessionCapabilities {
};
private final Set moduleBasedCaps;
- private final Set capabilities;
+ private final Set nonModuleCaps;
- private NetconfSessionCapabilities(final Set capabilities, final Set moduleBasedCaps) {
- this.capabilities = Preconditions.checkNotNull(capabilities);
+ private NetconfSessionCapabilities(final Set nonModuleCaps, final Set moduleBasedCaps) {
+ this.nonModuleCaps = Preconditions.checkNotNull(nonModuleCaps);
this.moduleBasedCaps = Preconditions.checkNotNull(moduleBasedCaps);
}
@@ -68,30 +70,49 @@ public final class NetconfSessionCapabilities {
return moduleBasedCaps;
}
- public boolean containsCapability(final String capability) {
- return capabilities.contains(capability);
+ public Set getNonModuleCaps() {
+ return nonModuleCaps;
+ }
+
+ public boolean containsNonModuleCapability(final String capability) {
+ return nonModuleCaps.contains(capability);
}
- public boolean containsCapability(final QName capability) {
+ public boolean containsModuleCapability(final QName capability) {
return moduleBasedCaps.contains(capability);
}
@Override
public String toString() {
return Objects.toStringHelper(this)
- .add("capabilities", capabilities)
+ .add("capabilities", nonModuleCaps)
+ .add("moduleBasedCapabilities", moduleBasedCaps)
.add("rollback", isRollbackSupported())
.add("monitoring", isMonitoringSupported())
.toString();
}
public boolean isRollbackSupported() {
- return containsCapability(NetconfMessageTransformUtil.NETCONF_ROLLBACK_ON_ERROR_URI.toString());
+ return containsNonModuleCapability(NetconfMessageTransformUtil.NETCONF_ROLLBACK_ON_ERROR_URI.toString());
+ }
+
+ public boolean isCandidateSupported() {
+ return containsNonModuleCapability(NetconfMessageTransformUtil.NETCONF_CANDIDATE_URI.toString());
}
public boolean isMonitoringSupported() {
- return containsCapability(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING)
- || containsCapability(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING.getNamespace().toString());
+ return containsModuleCapability(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING)
+ || containsNonModuleCapability(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING.getNamespace().toString());
+ }
+
+ public NetconfSessionCapabilities replaceModuleCaps(final NetconfSessionCapabilities netconfSessionModuleCapabilities) {
+ final Set moduleBasedCaps = Sets.newHashSet(netconfSessionModuleCapabilities.getModuleBasedCaps());
+
+ // Preserve monitoring module, since it indicates support for ietf-netconf-monitoring
+ if(containsModuleCapability(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING)) {
+ moduleBasedCaps.add(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING);
+ }
+ return new NetconfSessionCapabilities(getNonModuleCaps(), moduleBasedCaps);
}
public static NetconfSessionCapabilities fromNetconfSession(final NetconfClientSession session) {
@@ -100,6 +121,7 @@ public final class NetconfSessionCapabilities {
public static NetconfSessionCapabilities fromStrings(final Collection capabilities) {
final Set moduleBasedCaps = new HashSet<>();
+ final Set nonModuleCaps = Sets.newHashSet(capabilities);
for (final String capability : capabilities) {
final int qmark = capability.indexOf('?');
@@ -117,6 +139,7 @@ public final class NetconfSessionCapabilities {
String revision = REVISION_PARAM.from(queryParams);
if (revision != null) {
moduleBasedCaps.add(QName.create(namespace, revision, moduleName));
+ nonModuleCaps.remove(capability);
continue;
}
@@ -136,8 +159,9 @@ public final class NetconfSessionCapabilities {
// FIXME: do we really want to continue here?
moduleBasedCaps.add(QName.create(namespace, revision, moduleName));
+ nonModuleCaps.remove(capability);
}
- return new NetconfSessionCapabilities(ImmutableSet.copyOf(capabilities), ImmutableSet.copyOf(moduleBasedCaps));
+ return new NetconfSessionCapabilities(ImmutableSet.copyOf(nonModuleCaps), ImmutableSet.copyOf(moduleBasedCaps));
}
}
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceDataBroker.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceDataBroker.java
index ee0c8b7217..f3a9acd630 100644
--- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceDataBroker.java
+++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceDataBroker.java
@@ -42,7 +42,7 @@ final class NetconfDeviceDataBroker implements DOMDataBroker {
@Override
public DOMDataReadOnlyTransaction newReadOnlyTransaction() {
- return new NetconfDeviceReadOnlyTx(rpc, normalizer);
+ return new NetconfDeviceReadOnlyTx(rpc, normalizer, id);
}
@Override
@@ -52,8 +52,7 @@ final class NetconfDeviceDataBroker implements DOMDataBroker {
@Override
public DOMDataWriteTransaction newWriteOnlyTransaction() {
- // FIXME detect if candidate is supported, true is hardcoded
- return new NetconfDeviceWriteOnlyTx(id, rpc, normalizer, true, netconfSessionPreferences.isRollbackSupported());
+ return new NetconfDeviceWriteOnlyTx(id, rpc, normalizer, netconfSessionPreferences.isCandidateSupported(), netconfSessionPreferences.isRollbackSupported());
}
@Override
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceReadOnlyTx.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceReadOnlyTx.java
index 3248453baf..533df9cce7 100644
--- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceReadOnlyTx.java
+++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceReadOnlyTx.java
@@ -15,42 +15,53 @@ import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessag
import com.google.common.base.Function;
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 com.google.common.util.concurrent.ListenableFuture;
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.DataNormalizer;
import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
+import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
import org.opendaylight.controller.sal.core.api.RpcImplementation;
+import org.opendaylight.yangtools.util.concurrent.MappingCheckedFuture;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.Node;
import org.opendaylight.yangtools.yang.data.api.SimpleNode;
+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;
+
public final class NetconfDeviceReadOnlyTx implements DOMDataReadOnlyTransaction {
private static final Logger LOG = LoggerFactory.getLogger(NetconfDeviceReadOnlyTx.class);
private final RpcImplementation rpc;
private final DataNormalizer normalizer;
+ private final RemoteDeviceId id;
- public NetconfDeviceReadOnlyTx(final RpcImplementation rpc, final DataNormalizer normalizer) {
+ public NetconfDeviceReadOnlyTx(final RpcImplementation rpc, final DataNormalizer normalizer, final RemoteDeviceId id) {
this.rpc = rpc;
this.normalizer = normalizer;
+ this.id = id;
}
- public ListenableFuture>> readConfigurationData(final YangInstanceIdentifier path) {
+ private CheckedFuture>, ReadFailedException> readConfigurationData(
+ final YangInstanceIdentifier path) {
final ListenableFuture> future = rpc.invokeRpc(NETCONF_GET_CONFIG_QNAME,
NetconfMessageTransformUtil.wrap(NETCONF_GET_CONFIG_QNAME, CONFIG_SOURCE_RUNNING, toFilterStructure(path)));
- return Futures.transform(future, new Function, Optional>>() {
+ ListenableFuture>> transformedFuture = Futures.transform(future, new Function, Optional>>() {
@Override
public Optional> apply(final RpcResult result) {
+ checkReadSuccess(result, path);
+
final CompositeNode data = result.getResult().getFirstCompositeByName(NETCONF_DATA_QNAME);
final CompositeNode node = (CompositeNode) findNode(data, path);
@@ -59,6 +70,17 @@ public final class NetconfDeviceReadOnlyTx implements DOMDataReadOnlyTransaction
transform(path, node);
}
});
+
+ return MappingCheckedFuture.create(transformedFuture, ReadFailedException.MAPPER);
+ }
+
+ private void checkReadSuccess(final RpcResult result, final YangInstanceIdentifier path) {
+ try {
+ Preconditions.checkArgument(result.isSuccessful(), "%s: Unable to read data: %s, errors: %s", id, path, result.getErrors());
+ } catch (IllegalArgumentException e) {
+ LOG.warn("{}: Unable to read data: {}, errors: {}", id, path, result.getErrors());
+ throw e;
+ }
}
private Optional> transform(final YangInstanceIdentifier path, final CompositeNode node) {
@@ -68,17 +90,20 @@ public final class NetconfDeviceReadOnlyTx implements DOMDataReadOnlyTransaction
try {
return Optional.>of(normalizer.toNormalized(path, node).getValue());
} catch (final Exception e) {
- LOG.error("Unable to normalize data for {}, data: {}", path, node, e);
+ LOG.error("{}: Unable to normalize data for {}, data: {}", id, path, node, e);
throw e;
}
}
- public ListenableFuture>> readOperationalData(final YangInstanceIdentifier path) {
+ private CheckedFuture>, ReadFailedException> readOperationalData(
+ final YangInstanceIdentifier path) {
final ListenableFuture> future = rpc.invokeRpc(NETCONF_GET_QNAME, NetconfMessageTransformUtil.wrap(NETCONF_GET_QNAME, toFilterStructure(path)));
- return Futures.transform(future, new Function, Optional>>() {
+ ListenableFuture>> transformedFuture = Futures.transform(future, new Function, Optional>>() {
@Override
public Optional> apply(final RpcResult result) {
+ checkReadSuccess(result, path);
+
final CompositeNode data = result.getResult().getFirstCompositeByName(NETCONF_DATA_QNAME);
final CompositeNode node = (CompositeNode) findNode(data, path);
@@ -87,6 +112,8 @@ public final class NetconfDeviceReadOnlyTx implements DOMDataReadOnlyTransaction
transform(path, node);
}
});
+
+ return MappingCheckedFuture.create(transformedFuture, ReadFailedException.MAPPER);
}
private static Node> findNode(final CompositeNode node, final YangInstanceIdentifier identifier) {
@@ -122,8 +149,9 @@ public final class NetconfDeviceReadOnlyTx implements DOMDataReadOnlyTransaction
}
@Override
- public ListenableFuture>> read(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
- final YangInstanceIdentifier legacyPath = toLegacyPath(normalizer, path);
+ public CheckedFuture>, ReadFailedException> read(
+ final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+ final YangInstanceIdentifier legacyPath = toLegacyPath(normalizer, path, id);
switch (store) {
case CONFIGURATION : {
@@ -134,14 +162,14 @@ public final class NetconfDeviceReadOnlyTx implements DOMDataReadOnlyTransaction
}
}
- throw new IllegalArgumentException(String.format("Cannot read data %s for %s datastore, unknown datastore type", path, store));
+ throw new IllegalArgumentException(String.format("%s, Cannot read data %s for %s datastore, unknown datastore type", id, path, store));
}
- static YangInstanceIdentifier toLegacyPath(final DataNormalizer normalizer, final YangInstanceIdentifier path) {
+ static YangInstanceIdentifier toLegacyPath(final DataNormalizer normalizer, final YangInstanceIdentifier path, final RemoteDeviceId id) {
try {
return normalizer.toLegacy(path);
} catch (final DataNormalizationException e) {
- throw new IllegalArgumentException("Cannot normalize path " + path, e);
+ throw new IllegalArgumentException(id + ": Cannot normalize path " + path, e);
}
}
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceReadWriteTx.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceReadWriteTx.java
index 4054cf9403..3d2c3b9d44 100644
--- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceReadWriteTx.java
+++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceReadWriteTx.java
@@ -11,8 +11,10 @@ package org.opendaylight.controller.sal.connect.netconf.sal.tx;
import com.google.common.base.Optional;
import com.google.common.util.concurrent.CheckedFuture;
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.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.DOMDataReadTransaction;
import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
@@ -62,7 +64,8 @@ public class NetconfDeviceReadWriteTx implements DOMDataReadWriteTransaction {
}
@Override
- public ListenableFuture>> read(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+ public CheckedFuture>, ReadFailedException> read(
+ final LogicalDatastoreType store, final YangInstanceIdentifier path) {
return delegateReadTx.read(store, path);
}
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceWriteOnlyTx.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceWriteOnlyTx.java
index c8d9028210..87f5477d35 100644
--- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceWriteOnlyTx.java
+++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceWriteOnlyTx.java
@@ -8,10 +8,11 @@
package org.opendaylight.controller.sal.connect.netconf.sal.tx;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.DISCARD_CHANGES_RPC_CONTENT;
import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_CANDIDATE_QNAME;
-import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME;
import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_CONFIG_QNAME;
import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_DEFAULT_OPERATION_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_DISCARD_CHANGES_QNAME;
import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME;
import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_ERROR_OPTION_QNAME;
import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_OPERATION_QNAME;
@@ -26,13 +27,14 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
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.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
-import javax.annotation.Nullable;
+import java.util.concurrent.atomic.AtomicBoolean;
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;
@@ -57,97 +59,114 @@ import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class NetconfDeviceWriteOnlyTx implements DOMDataWriteTransaction {
+public class NetconfDeviceWriteOnlyTx implements DOMDataWriteTransaction, FutureCallback> {
private static final Logger LOG = LoggerFactory.getLogger(NetconfDeviceWriteOnlyTx.class);
private final RemoteDeviceId id;
private final RpcImplementation rpc;
private final DataNormalizer normalizer;
+
private final boolean rollbackSupported;
+ private final boolean candidateSupported;
private final CompositeNode targetNode;
+ // Allow commit to be called only once
+ private final AtomicBoolean finished = new AtomicBoolean(false);
+
public NetconfDeviceWriteOnlyTx(final RemoteDeviceId id, final RpcImplementation rpc, final DataNormalizer normalizer, final boolean candidateSupported, final boolean rollbackOnErrorSupported) {
this.id = id;
this.rpc = rpc;
this.normalizer = normalizer;
- this.targetNode = getTargetNode(candidateSupported);
+
+ this.candidateSupported = candidateSupported;
+ this.targetNode = getTargetNode(this.candidateSupported);
this.rollbackSupported = rollbackOnErrorSupported;
}
- // FIXME add logging
-
@Override
public boolean cancel() {
- if(isCommitted()) {
+ if(isFinished()) {
return false;
}
return discardChanges();
}
- private boolean isCommitted() {
- // TODO 732
- return true;
+ private boolean isFinished() {
+ return finished.get();
}
private boolean discardChanges() {
- // TODO 732
+ finished.set(true);
+
+ if(candidateSupported) {
+ sendDiscardChanges();
+ }
return true;
}
// TODO should the edit operations be blocking ?
+ // TODO should the discard-changes operations be blocking ?
@Override
public void put(final LogicalDatastoreType store, final YangInstanceIdentifier path, final NormalizedNode, ?> data) {
+ checkNotFinished();
Preconditions.checkArgument(store == LogicalDatastoreType.CONFIGURATION, "Can merge only configuration, not %s", store);
try {
- final YangInstanceIdentifier legacyPath = NetconfDeviceReadOnlyTx.toLegacyPath(normalizer, path);
+ final YangInstanceIdentifier legacyPath = NetconfDeviceReadOnlyTx.toLegacyPath(normalizer, path, id);
final CompositeNode legacyData = normalizer.toLegacy(path, data);
- sendEditRpc(createEditConfigStructure(legacyPath, Optional.of(ModifyAction.REPLACE), Optional.fromNullable(legacyData)), Optional.of(ModifyAction.NONE));
+ sendEditRpc(
+ createEditConfigStructure(legacyPath, Optional.of(ModifyAction.REPLACE), Optional.fromNullable(legacyData)), Optional.of(ModifyAction.NONE));
} catch (final ExecutionException e) {
- LOG.warn("Error putting data to {}, data: {}, discarding changes", path, data, e);
+ LOG.warn("{}: Error putting data to {}, data: {}, discarding changes", id, path, data, e);
discardChanges();
- throw new RuntimeException("Error while replacing " + path, e);
+ throw new RuntimeException(id + ": Error while replacing " + path, e);
}
}
+ private void checkNotFinished() {
+ Preconditions.checkState(isFinished() == false, "%s: Transaction %s already finished", id, getIdentifier());
+ }
+
@Override
public void merge(final LogicalDatastoreType store, final YangInstanceIdentifier path, final NormalizedNode, ?> data) {
- Preconditions.checkArgument(store == LogicalDatastoreType.CONFIGURATION, "Can merge only configuration, not %s", store);
+ checkNotFinished();
+ Preconditions.checkArgument(store == LogicalDatastoreType.CONFIGURATION, "%s: Can merge only configuration, not %s", id, store);
try {
- final YangInstanceIdentifier legacyPath = NetconfDeviceReadOnlyTx.toLegacyPath(normalizer, path);
+ final YangInstanceIdentifier legacyPath = NetconfDeviceReadOnlyTx.toLegacyPath(normalizer, path, id);
final CompositeNode legacyData = normalizer.toLegacy(path, data);
sendEditRpc(
createEditConfigStructure(legacyPath, Optional. absent(), Optional.fromNullable(legacyData)), Optional. absent());
} catch (final ExecutionException e) {
- LOG.warn("Error merging data to {}, data: {}, discarding changes", path, data, e);
+ LOG.warn("{}: Error merging data to {}, data: {}, discarding changes", id, path, data, e);
discardChanges();
- throw new RuntimeException("Error while merging " + path, e);
+ throw new RuntimeException(id + ": Error while merging " + path, e);
}
}
@Override
public void delete(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
- Preconditions.checkArgument(store == LogicalDatastoreType.CONFIGURATION, "Can merge only configuration, not %s", store);
+ checkNotFinished();
+ Preconditions.checkArgument(store == LogicalDatastoreType.CONFIGURATION, "%s: Can merge only configuration, not %s", id, store);
try {
- sendEditRpc(createEditConfigStructure(NetconfDeviceReadOnlyTx.toLegacyPath(normalizer, path), Optional.of(ModifyAction.DELETE), Optional.absent()), Optional.of(ModifyAction.NONE));
+ sendEditRpc(
+ createEditConfigStructure(NetconfDeviceReadOnlyTx.toLegacyPath(normalizer, path, id), Optional.of(ModifyAction.DELETE), Optional.absent()), Optional.of(ModifyAction.NONE));
} catch (final ExecutionException e) {
- LOG.warn("Error deleting data {}, discarding changes", path, e);
+ LOG.warn("{}: Error deleting data {}, discarding changes", id, path, e);
discardChanges();
- throw new RuntimeException("Error while deleting " + path, e);
+ throw new RuntimeException(id + ": Error while deleting " + path, e);
}
}
@Override
public CheckedFuture submit() {
final ListenableFuture commmitFutureAsVoid = Futures.transform(commit(), new Function, Void>() {
- @Nullable
@Override
- public Void apply(@Nullable final RpcResult input) {
+ public Void apply(final RpcResult input) {
return null;
}
});
@@ -162,25 +181,46 @@ public class NetconfDeviceWriteOnlyTx implements DOMDataWriteTransaction {
@Override
public ListenableFuture> commit() {
- // FIXME do not allow commit if closed or failed
+ checkNotFinished();
+ finished.set(true);
- final ListenableFuture> rpcResult = rpc.invokeRpc(NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME, getCommitRequest());
- return Futures.transform(rpcResult, new Function, RpcResult>() {
- @Override
- public RpcResult apply(@Nullable final RpcResult input) {
- if(input.isSuccessful()) {
- return RpcResultBuilder.success(TransactionStatus.COMMITED).build();
- } else {
- final RpcResultBuilder failed = RpcResultBuilder.failed();
- for (final RpcError rpcError : input.getErrors()) {
- failed.withError(rpcError.getErrorType(), rpcError.getTag(), rpcError.getMessage(), rpcError.getApplicationTag(), rpcError.getInfo(), rpcError.getCause());
+ if(candidateSupported == false) {
+ return Futures.immediateFuture(RpcResultBuilder.success(TransactionStatus.COMMITED).build());
+ }
+
+ final ListenableFuture> rpcResult = rpc.invokeRpc(
+ NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME, NetconfMessageTransformUtil.COMMIT_RPC_CONTENT);
+
+ final ListenableFuture> transformed = Futures.transform(rpcResult,
+ new Function, RpcResult>() {
+ @Override
+ public RpcResult apply(final RpcResult input) {
+ if (input.isSuccessful()) {
+ return RpcResultBuilder.success(TransactionStatus.COMMITED).build();
+ } else {
+ final RpcResultBuilder failed = RpcResultBuilder.failed();
+ for (final RpcError rpcError : input.getErrors()) {
+ failed.withError(rpcError.getErrorType(), rpcError.getTag(), rpcError.getMessage(),
+ rpcError.getApplicationTag(), rpcError.getInfo(), rpcError.getCause());
+ }
+ return failed.build();
+ }
}
- return failed.build();
- }
- }
- });
+ });
- // FIXME 732 detect commit failure
+ Futures.addCallback(transformed, this);
+ return transformed;
+ }
+
+ @Override
+ public void onSuccess(final RpcResult result) {
+ LOG.debug("{}: Write successful, transaction: {}", id, getIdentifier());
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ LOG.warn("{}: Write failed, transaction {}, discarding changes", id, getIdentifier(), t);
+ discardChanges();
}
private void sendEditRpc(final CompositeNode editStructure, final Optional defaultOperation) throws ExecutionException {
@@ -200,6 +240,22 @@ public class NetconfDeviceWriteOnlyTx implements DOMDataWriteTransaction {
}
}
+ private void sendDiscardChanges() {
+ final ListenableFuture> discardFuture = rpc.invokeRpc(NETCONF_DISCARD_CHANGES_QNAME, DISCARD_CHANGES_RPC_CONTENT);
+ Futures.addCallback(discardFuture, new FutureCallback>() {
+ @Override
+ public void onSuccess(final RpcResult result) {
+ LOG.debug("{}: Discarding transaction: {}", id, getIdentifier());
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ LOG.error("{}: Discarding changes failed, transaction: {}. Device configuration might be corrupted", id, getIdentifier(), t);
+ throw new RuntimeException(id + ": Discarding changes failed, transaction " + getIdentifier(), t);
+ }
+ });
+ }
+
private CompositeNode createEditConfigStructure(final YangInstanceIdentifier dataPath, final Optional operation,
final Optional lastChildOverride) {
Preconditions.checkArgument(Iterables.isEmpty(dataPath.getPathArguments()) == false, "Instance identifier with empty path %s", dataPath);
@@ -298,13 +354,6 @@ public class NetconfDeviceWriteOnlyTx implements DOMDataWriteTransaction {
}
}
- private ImmutableCompositeNode getCommitRequest() {
- final CompositeNodeBuilder commitInput = ImmutableCompositeNode.builder();
- commitInput.setQName(NETCONF_COMMIT_QNAME);
- return commitInput.toInstance();
- }
-
-
@Override
public Object getIdentifier() {
return this;
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/mapping/NetconfMessageTransformer.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/mapping/NetconfMessageTransformer.java
index 80d0f67ac4..47ef9039d1 100644
--- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/mapping/NetconfMessageTransformer.java
+++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/mapping/NetconfMessageTransformer.java
@@ -70,10 +70,15 @@ public class NetconfMessageTransformer implements MessageTransformer toRpcResult(final NetconfMessage message, final QName rpc, final SchemaContext context) {
final CompositeNode compositeNode;
-
if (NetconfMessageTransformUtil.isDataRetrievalOperation(rpc)) {
-
final Element xmlData = NetconfMessageTransformUtil.getDataSubtree(message.getDocument());
-
final List> dataNodes = XmlDocumentUtils.toDomNodes(xmlData,
Optional.of(context.getDataDefinitions()), context);
final CompositeNodeBuilder it = ImmutableCompositeNode.builder();
it.setQName(NetconfMessageTransformUtil.NETCONF_RPC_REPLY_QNAME);
it.add(ImmutableCompositeNode.create(NetconfMessageTransformUtil.NETCONF_DATA_QNAME, dataNodes));
-
compositeNode = it.toInstance();
} else {
- // TODO map rpc with schema
- compositeNode = (CompositeNode) XmlDocumentUtils.toDomNode(message.getDocument());
+ final CompositeNode rpcReply = XmlDocumentUtils.rpcReplyToDomNodes(message.getDocument(), rpc, context);
+ if (rpcReply != null) {
+ compositeNode = rpcReply;
+ } else {
+ compositeNode = (CompositeNode) XmlDocumentUtils.toDomNode(message.getDocument());
+ }
}
-
return RpcResultBuilder.success( compositeNode ).build();
}
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfMessageTransformUtil.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfMessageTransformUtil.java
index a6924d9d37..4f792a0a71 100644
--- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfMessageTransformUtil.java
+++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfMessageTransformUtil.java
@@ -7,6 +7,7 @@
*/
package org.opendaylight.controller.sal.connect.netconf.util;
+import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
@@ -35,6 +36,7 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.Node;
import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
+import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl;
import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
@@ -45,8 +47,7 @@ import org.w3c.dom.Element;
public class NetconfMessageTransformUtil {
- private NetconfMessageTransformUtil() {
- }
+ private NetconfMessageTransformUtil() {}
public static final QName IETF_NETCONF_MONITORING = QName.create("urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring", "2010-10-04", "ietf-netconf-monitoring");
public static URI NETCONF_URI = URI.create("urn:ietf:params:xml:ns:netconf:base:1.0");
@@ -66,14 +67,27 @@ public class NetconfMessageTransformUtil {
public static QName NETCONF_DEFAULT_OPERATION_QNAME = QName.create(NETCONF_OPERATION_QNAME, "default-operation");
public static QName NETCONF_EDIT_CONFIG_QNAME = QName.create(NETCONF_QNAME, "edit-config");
public static QName NETCONF_GET_CONFIG_QNAME = QName.create(NETCONF_QNAME, "get-config");
+ public static QName NETCONF_DISCARD_CHANGES_QNAME = QName.create(NETCONF_QNAME, "discard-changes");
public static QName NETCONF_TYPE_QNAME = QName.create(NETCONF_QNAME, "type");
public static QName NETCONF_FILTER_QNAME = QName.create(NETCONF_QNAME, "filter");
public static QName NETCONF_GET_QNAME = QName.create(NETCONF_QNAME, "get");
public static QName NETCONF_RPC_QNAME = QName.create(NETCONF_QNAME, "rpc");
+
public static URI NETCONF_ROLLBACK_ON_ERROR_URI = URI
.create("urn:ietf:params:netconf:capability:rollback-on-error:1.0");
public static String ROLLBACK_ON_ERROR_OPTION = "rollback-on-error";
+ public static URI NETCONF_CANDIDATE_URI = URI
+ .create("urn:ietf:params:netconf:capability:candidate:1.0");
+
+ // Discard changes message
+ public static final CompositeNode DISCARD_CHANGES_RPC_CONTENT =
+ NodeFactory.createImmutableCompositeNode(NETCONF_DISCARD_CHANGES_QNAME, null, Collections.>emptyList());
+
+ // Commit changes message
+ public static final CompositeNode COMMIT_RPC_CONTENT =
+ NodeFactory.createImmutableCompositeNode(NETCONF_COMMIT_QNAME, null, Collections.>emptyList());
+
public static Node> toFilterStructure(final YangInstanceIdentifier identifier) {
Node> previous = null;
if (Iterables.isEmpty(identifier.getPathArguments())) {
@@ -213,6 +227,14 @@ public class NetconfMessageTransformUtil {
NETCONF_GET_QNAME.getLocalName()));
}
+ public static boolean isGetOperation(final QName rpc) {
+ return NETCONF_URI.equals(rpc.getNamespace()) && rpc.getLocalName().equals(NETCONF_GET_QNAME.getLocalName());
+ }
+
+ public static boolean isGetConfigOperation(final QName rpc) {
+ return NETCONF_URI.equals(rpc.getNamespace()) && rpc.getLocalName().equals(NETCONF_GET_CONFIG_QNAME.getLocalName());
+ }
+
public static boolean isDataEditOperation(final QName rpc) {
return NETCONF_URI.equals(rpc.getNamespace())
&& rpc.getLocalName().equals(NETCONF_EDIT_CONFIG_QNAME.getLocalName());
@@ -243,6 +265,80 @@ public class NetconfMessageTransformUtil {
return new NodeContainerProxy(NETCONF_RPC_QNAME, Sets.newHashSet(editConfigProxy));
}
+ /**
+ * Creates artificial schema node for edit-config rpc. This artificial schema looks like:
+ *
+ * {@code
+ * rpc
+ * get
+ * filter
+ * // All schema nodes from remote schema
+ * filter
+ * get
+ * rpc
+ * }
+ *
+ *
+ * This makes the translation of rpc get request(especially the config node)
+ * to xml use schema which is crucial for some types of nodes e.g. identity-ref.
+ */
+ public static DataNodeContainer createSchemaForGet(final SchemaContext schemaContext) {
+ final QName filter = QName.create(NETCONF_GET_QNAME, "filter");
+ final QName get = QName.create(NETCONF_GET_QNAME, "get");
+ final NodeContainerProxy configProxy = new NodeContainerProxy(filter, schemaContext.getChildNodes());
+ final NodeContainerProxy editConfigProxy = new NodeContainerProxy(get, Sets.newHashSet(configProxy));
+ return new NodeContainerProxy(NETCONF_RPC_QNAME, Sets.newHashSet(editConfigProxy));
+ }
+
+ /**
+ * Creates artificial schema node for get rpc. This artificial schema looks like:
+ *
+ *
+ * This makes the translation of rpc get-config request(especially the config node)
+ * to xml use schema which is crucial for some types of nodes e.g. identity-ref.
+ */
+ public static DataNodeContainer createSchemaForGetConfig(final SchemaContext schemaContext) {
+ final QName filter = QName.create(NETCONF_GET_CONFIG_QNAME, "filter");
+ final QName getConfig = QName.create(NETCONF_GET_CONFIG_QNAME, "get-config");
+ final NodeContainerProxy configProxy = new NodeContainerProxy(filter, schemaContext.getChildNodes());
+ final NodeContainerProxy editConfigProxy = new NodeContainerProxy(getConfig, Sets.newHashSet(configProxy));
+ return new NodeContainerProxy(NETCONF_RPC_QNAME, Sets.newHashSet(editConfigProxy));
+ }
+
+ /**
+ * Creates artificial schema node for schema defined rpc. This artificial schema looks like:
+ *