*/
package org.opendaylight.controller.md.sal.binding.impl;
+import java.util.ArrayList;
import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import com.google.common.base.Objects;
import com.google.common.base.Optional;
+import com.google.common.collect.Iterables;
public abstract class AbstractForwardedDataBroker implements Delegator<DOMDataBroker>, DomForwardedBroker,
SchemaContextListener, AutoCloseable {
protected Map<InstanceIdentifier<?>, DataObject> toBinding(
final Map<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, ? extends NormalizedNode<?, ?>> normalized) {
Map<InstanceIdentifier<?>, DataObject> newMap = new HashMap<>();
- for (Map.Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, ? extends NormalizedNode<?, ?>> entry : normalized
- .entrySet()) {
+
+ for (Map.Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, ? extends NormalizedNode<?, ?>> entry : sortedEntries(normalized)) {
try {
Optional<Entry<InstanceIdentifier<? extends DataObject>, DataObject>> potential = getCodec().toBinding(
entry);
return newMap;
}
+ private static <T> Iterable<Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier,T>> sortedEntries(final Map<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, T> map) {
+ ArrayList<Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, T>> entries = new ArrayList<>(map.entrySet());
+ Collections.sort(entries, new Comparator<Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, T>>() {
+
+ @Override
+ public int compare(final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, T> left,
+ final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, T> right) {
+ int leftSize = Iterables.size(left.getKey().getPathArguments());
+ int rightSize = Iterables.size(right.getKey().getPathArguments());
+ return Integer.compare(leftSize, rightSize);
+ }
+ });
+ return entries;
+ }
+
protected Set<InstanceIdentifier<?>> toBinding(
final Set<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier> normalized) {
Set<InstanceIdentifier<?>> hashSet = new HashSet<>();
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore;
+
+import akka.actor.Props;
+import akka.actor.UntypedActor;
+import akka.japi.Creator;
+
+public class DataChangeListener extends UntypedActor {
+ @Override public void onReceive(Object message) throws Exception {
+ throw new UnsupportedOperationException("onReceive");
+ }
+
+ public static Props props() {
+ return Props.create(new Creator<DataChangeListener>() {
+ @Override
+ public DataChangeListener create() throws Exception {
+ return new DataChangeListener();
+ }
+
+ });
+
+ }
+}
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
+import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListener;
+import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListenerReply;
+import org.opendaylight.controller.cluster.datastore.messages.UpdateSchemaContext;
+import org.opendaylight.controller.cluster.datastore.utils.ActorContext;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
import org.opendaylight.controller.sal.core.spi.data.DOMStore;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
*
*/
-public class DistributedDataStore implements DOMStore {
- private final ActorRef shardManager;
-
- public DistributedDataStore(ActorSystem actorSystem, String type) {
- shardManager = actorSystem.actorOf(ShardManager.props(type));
- }
-
- @Override
- public <L extends AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>> ListenerRegistration<L> registerChangeListener(InstanceIdentifier path, L listener, AsyncDataBroker.DataChangeScope scope) {
- return new ListenerRegistrationProxy();
- }
-
- @Override
- public DOMStoreTransactionChain createTransactionChain() {
- return new TransactionChainProxy();
- }
-
- @Override
- public DOMStoreReadTransaction newReadOnlyTransaction() {
- return new TransactionProxy();
- }
-
- @Override
- public DOMStoreWriteTransaction newWriteOnlyTransaction() {
- return new TransactionProxy();
- }
-
- @Override
- public DOMStoreReadWriteTransaction newReadWriteTransaction() {
- return new TransactionProxy();
- }
+public class DistributedDataStore implements DOMStore, SchemaContextListener {
+
+ private static final Logger
+ LOG = LoggerFactory.getLogger(DistributedDataStore.class);
+
+
+ private final String type;
+ private final ActorContext actorContext;
+
+ public DistributedDataStore(ActorSystem actorSystem, String type) {
+ this(new ActorContext(actorSystem, actorSystem.actorOf(ShardManager.props(type))), type);
+ }
+
+ public DistributedDataStore(ActorContext actorContext, String type) {
+ this.type = type;
+ this.actorContext = actorContext;
+ }
+
+
+ @Override
+ public <L extends AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>> ListenerRegistration<L> registerChangeListener(
+ InstanceIdentifier path, L listener,
+ AsyncDataBroker.DataChangeScope scope) {
+
+ ActorRef dataChangeListenerActor = actorContext.getActorSystem().actorOf(DataChangeListener.props());
+
+ Object result = actorContext.executeShardOperation(Shard.DEFAULT_NAME,
+ new RegisterChangeListener(path, dataChangeListenerActor.path(),
+ AsyncDataBroker.DataChangeScope.BASE),
+ ActorContext.ASK_DURATION);
+
+ RegisterChangeListenerReply reply = (RegisterChangeListenerReply) result;
+ return new ListenerRegistrationProxy(reply.getListenerRegistrationPath());
+ }
+
+
+
+ @Override
+ public DOMStoreTransactionChain createTransactionChain() {
+ return new TransactionChainProxy(actorContext);
+ }
+
+ @Override
+ public DOMStoreReadTransaction newReadOnlyTransaction() {
+ return new TransactionProxy(actorContext, TransactionProxy.TransactionType.READ_ONLY);
+ }
+
+ @Override
+ public DOMStoreWriteTransaction newWriteOnlyTransaction() {
+ return new TransactionProxy(actorContext, TransactionProxy.TransactionType.WRITE_ONLY);
+ }
+
+ @Override
+ public DOMStoreReadWriteTransaction newReadWriteTransaction() {
+ return new TransactionProxy(actorContext, TransactionProxy.TransactionType.READ_WRITE);
+ }
+
+ @Override public void onGlobalContextUpdated(SchemaContext schemaContext) {
+ actorContext.getShardManager().tell(new UpdateSchemaContext(schemaContext), null);
+ }
}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore;
+
+import akka.actor.ActorSelection;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public class ListenerProxy implements AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>{
+ private final ActorSelection listenerRegistrationActor;
+
+ public ListenerProxy(ActorSelection listenerRegistrationActor) {
+ this.listenerRegistrationActor = listenerRegistrationActor;
+ }
+
+ @Override public void onDataChanged(
+ AsyncDataChangeEvent<InstanceIdentifier, NormalizedNode<?, ?>> change) {
+ throw new UnsupportedOperationException("onDataChanged");
+ }
+}
package org.opendaylight.controller.cluster.datastore;
+import akka.actor.ActorPath;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
/**
* The ListenerRegistrationProxy talks to a remote ListenerRegistration actor.
*/
public class ListenerRegistrationProxy implements ListenerRegistration {
+ private final ActorPath listenerRegistrationPath;
+
+ public ListenerRegistrationProxy(ActorPath listenerRegistrationPath) {
+
+ this.listenerRegistrationPath = listenerRegistrationPath;
+ }
+
@Override
public Object getInstance() {
throw new UnsupportedOperationException("getInstance");
package org.opendaylight.controller.cluster.datastore;
import akka.actor.ActorRef;
+import akka.actor.ActorSelection;
import akka.actor.Props;
import akka.event.Logging;
import akka.event.LoggingAdapter;
*/
public class Shard extends UntypedProcessor {
- public static final String DEFAULT_NAME = "default";
+ public static final String DEFAULT_NAME = "default";
- private final ListeningExecutorService storeExecutor = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(2));
+ private final ListeningExecutorService storeExecutor =
+ MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(2));
- private final InMemoryDOMDataStore store;
+ private final InMemoryDOMDataStore store;
- private final Map<Modification, DOMStoreThreePhaseCommitCohort> modificationToCohort = new HashMap<>();
+ private final Map<Modification, DOMStoreThreePhaseCommitCohort>
+ modificationToCohort = new HashMap<>();
- private final LoggingAdapter log = Logging.getLogger(getContext().system(), this);
+ private final LoggingAdapter log =
+ Logging.getLogger(getContext().system(), this);
- private Shard(String name){
- store = new InMemoryDOMDataStore(name, storeExecutor);
- }
-
- public static Props props(final String name) {
- return Props.create(new Creator<Shard>() {
+ private Shard(String name) {
+ store = new InMemoryDOMDataStore(name, storeExecutor);
+ }
- @Override
- public Shard create() throws Exception {
- return new Shard(name);
- }
+ public static Props props(final String name) {
+ return Props.create(new Creator<Shard>() {
- });
- }
+ @Override
+ public Shard create() throws Exception {
+ return new Shard(name);
+ }
- @Override
- public void onReceive(Object message) throws Exception {
- if (message instanceof CreateTransactionChain) {
- createTransactionChain();
- } else if (message instanceof RegisterChangeListener) {
- registerChangeListener((RegisterChangeListener) message);
- } else if (message instanceof UpdateSchemaContext) {
- updateSchemaContext((UpdateSchemaContext) message);
- } else if (message instanceof ForwardedCommitTransaction ) {
- handleForwardedCommit((ForwardedCommitTransaction) message);
- } else if (message instanceof Persistent){
- commit((Persistent) message);
+ });
}
- }
-
- private void commit(Persistent message) {
- Modification modification = (Modification) message.payload();
- DOMStoreThreePhaseCommitCohort cohort = modificationToCohort.remove(modification);
- if(cohort == null){
- log.error("Could not find cohort for modification : " + modification);
- return;
+
+ @Override
+ public void onReceive(Object message) throws Exception {
+ if (message instanceof CreateTransactionChain) {
+ createTransactionChain();
+ } else if (message instanceof RegisterChangeListener) {
+ registerChangeListener((RegisterChangeListener) message);
+ } else if (message instanceof UpdateSchemaContext) {
+ updateSchemaContext((UpdateSchemaContext) message);
+ } else if (message instanceof ForwardedCommitTransaction) {
+ handleForwardedCommit((ForwardedCommitTransaction) message);
+ } else if (message instanceof Persistent) {
+ commit((Persistent) message);
+ }
}
- final ListenableFuture<Void> future = cohort.commit();
- final ActorRef sender = getSender();
- final ActorRef self = getSelf();
- future.addListener(new Runnable() {
- @Override
- public void run() {
- try {
- future.get();
- sender.tell(new CommitTransactionReply(), self);
- } catch (InterruptedException | ExecutionException e) {
- log.error(e, "An exception happened when committing");
+
+ private void commit(Persistent message) {
+ Modification modification = (Modification) message.payload();
+ DOMStoreThreePhaseCommitCohort cohort =
+ modificationToCohort.remove(modification);
+ if (cohort == null) {
+ log.error(
+ "Could not find cohort for modification : " + modification);
+ return;
}
- }
- }, getContext().dispatcher());
- }
-
- private void handleForwardedCommit(ForwardedCommitTransaction message) {
- log.info("received forwarded transaction");
- modificationToCohort.put(message.getModification(), message.getCohort());
- getSelf().forward(Persistent.create(message.getModification()), getContext());
- }
-
- private void updateSchemaContext(UpdateSchemaContext message) {
- store.onGlobalContextUpdated(message.getSchemaContext());
- }
-
- private void registerChangeListener(RegisterChangeListener registerChangeListener) {
- org.opendaylight.yangtools.concepts.ListenerRegistration<AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>> registration =
- store.registerChangeListener(registerChangeListener.getPath(), registerChangeListener.getListener(), registerChangeListener.getScope());
- ActorRef listenerRegistration = getContext().actorOf(ListenerRegistration.props(registration));
- getSender().tell(new RegisterChangeListenerReply(listenerRegistration.path()), getSelf());
- }
-
- private void createTransactionChain() {
- DOMStoreTransactionChain chain = store.createTransactionChain();
- ActorRef transactionChain = getContext().actorOf(ShardTransactionChain.props(chain));
- getSender().tell(new CreateTransactionChainReply(transactionChain.path()), getSelf());
- }
+ final ListenableFuture<Void> future = cohort.commit();
+ final ActorRef sender = getSender();
+ final ActorRef self = getSelf();
+ future.addListener(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ future.get();
+ sender.tell(new CommitTransactionReply(), self);
+ } catch (InterruptedException | ExecutionException e) {
+ log.error(e, "An exception happened when committing");
+ }
+ }
+ }, getContext().dispatcher());
+ }
+
+ private void handleForwardedCommit(ForwardedCommitTransaction message) {
+ log.info("received forwarded transaction");
+ modificationToCohort
+ .put(message.getModification(), message.getCohort());
+ getSelf().forward(Persistent.create(message.getModification()),
+ getContext());
+ }
+
+ private void updateSchemaContext(UpdateSchemaContext message) {
+ store.onGlobalContextUpdated(message.getSchemaContext());
+ }
+
+ private void registerChangeListener(
+ RegisterChangeListener registerChangeListener) {
+
+ ActorSelection listenerRegistrationActor = getContext()
+ .system().actorSelection(registerChangeListener.getDataChangeListenerPath());
+
+ AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>
+ listener = new ListenerProxy(listenerRegistrationActor);
+
+ org.opendaylight.yangtools.concepts.ListenerRegistration<AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>>
+ registration =
+ store.registerChangeListener(registerChangeListener.getPath(),
+ listener, registerChangeListener.getScope());
+ ActorRef listenerRegistration =
+ getContext().actorOf(ListenerRegistration.props(registration));
+ getSender()
+ .tell(new RegisterChangeListenerReply(listenerRegistration.path()),
+ getSelf());
+ }
+
+ private void createTransactionChain() {
+ DOMStoreTransactionChain chain = store.createTransactionChain();
+ ActorRef transactionChain =
+ getContext().actorOf(ShardTransactionChain.props(chain));
+ getSender()
+ .tell(new CreateTransactionChainReply(transactionChain.path()),
+ getSelf());
+ }
}
import org.opendaylight.controller.cluster.datastore.messages.FindPrimary;
import org.opendaylight.controller.cluster.datastore.messages.PrimaryFound;
import org.opendaylight.controller.cluster.datastore.messages.PrimaryNotFound;
+import org.opendaylight.controller.cluster.datastore.messages.UpdateSchemaContext;
import java.util.HashMap;
import java.util.List;
} else {
getSender().tell(new PrimaryNotFound(shardName), getSelf());
}
+ } else if(message instanceof UpdateSchemaContext){
+ // FIXME : Notify all local shards of a context change
+ getContext().system().actorSelection(defaultShardPath).forward(message, getContext());
}
}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore;
+
+import akka.actor.ActorPath;
+import com.google.common.util.concurrent.ListenableFuture;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * ThreePhaseCommitCohortProxy represents a set of remote cohort proxies
+ */
+public class ThreePhaseCommitCohortProxy implements
+ DOMStoreThreePhaseCommitCohort{
+
+ private final List<ActorPath> cohortPaths;
+
+ public ThreePhaseCommitCohortProxy(List<ActorPath> cohortPaths) {
+
+ this.cohortPaths = cohortPaths;
+ }
+
+ @Override public ListenableFuture<Boolean> canCommit() {
+ throw new UnsupportedOperationException("canCommit");
+ }
+
+ @Override public ListenableFuture<Void> preCommit() {
+ throw new UnsupportedOperationException("preCommit");
+ }
+
+ @Override public ListenableFuture<Void> abort() {
+ throw new UnsupportedOperationException("abort");
+ }
+
+ @Override public ListenableFuture<Void> commit() {
+ throw new UnsupportedOperationException("commit");
+ }
+
+ public List<ActorPath> getCohortPaths() {
+ return Collections.unmodifiableList(this.cohortPaths);
+ }
+}
package org.opendaylight.controller.cluster.datastore;
+import org.opendaylight.controller.cluster.datastore.utils.ActorContext;
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.DOMStoreTransactionChain;
* TransactionChainProxy acts as a proxy for a DOMStoreTransactionChain created on a remote shard
*/
public class TransactionChainProxy implements DOMStoreTransactionChain{
+ private final ActorContext actorContext;
+
+ public TransactionChainProxy(ActorContext actorContext) {
+ this.actorContext = actorContext;
+ }
+
@Override
public DOMStoreReadTransaction newReadOnlyTransaction() {
- throw new UnsupportedOperationException("newReadOnlyTransaction");
+ return new TransactionProxy(actorContext,
+ TransactionProxy.TransactionType.READ_ONLY);
}
@Override
public DOMStoreReadWriteTransaction newReadWriteTransaction() {
- throw new UnsupportedOperationException("newReadWriteTransaction");
+ return new TransactionProxy(actorContext,
+ TransactionProxy.TransactionType.WRITE_ONLY);
}
@Override
public DOMStoreWriteTransaction newWriteOnlyTransaction() {
- throw new UnsupportedOperationException("newWriteOnlyTransaction");
+ return new TransactionProxy(actorContext,
+ TransactionProxy.TransactionType.READ_WRITE);
}
@Override
public void close() {
- throw new UnsupportedOperationException("close");
+ throw new UnsupportedOperationException("close - not sure what to do here?");
}
}
package org.opendaylight.controller.cluster.datastore;
+import akka.actor.ActorPath;
+import akka.actor.ActorSelection;
import com.google.common.base.Optional;
import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListenableFutureTask;
+import org.opendaylight.controller.cluster.datastore.messages.CloseTransaction;
+import org.opendaylight.controller.cluster.datastore.messages.CreateTransaction;
+import org.opendaylight.controller.cluster.datastore.messages.CreateTransactionReply;
+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.ReadDataReply;
+import org.opendaylight.controller.cluster.datastore.messages.ReadyTransaction;
+import org.opendaylight.controller.cluster.datastore.messages.ReadyTransactionReply;
+import org.opendaylight.controller.cluster.datastore.messages.WriteData;
+import org.opendaylight.controller.cluster.datastore.utils.ActorContext;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicLong;
+
/**
* TransactionProxy acts as a proxy for one or more transactions that were created on a remote shard
- *
+ * <p>
* Creating a transaction on the consumer side will create one instance of a transaction proxy. If during
* the transaction reads and writes are done on data that belongs to different shards then a separate transaction will
* be created on each of those shards by the TransactionProxy
- *
+ *</p>
+ * <p>
* The TransactionProxy does not make any guarantees about atomicity or order in which the transactions on the various
* shards will be executed.
- *
+ * </p>
*/
public class TransactionProxy implements DOMStoreReadWriteTransaction {
+
+ public enum TransactionType {
+ READ_ONLY,
+ WRITE_ONLY,
+ READ_WRITE
+ }
+
+ private static final AtomicLong counter = new AtomicLong();
+
+ private final TransactionType readOnly;
+ private final ActorContext actorContext;
+ private final Map<String, ActorSelection> remoteTransactionPaths = new HashMap<>();
+ private final String identifier;
+
+ public TransactionProxy(
+ ActorContext actorContext,
+ TransactionType readOnly) {
+
+ this.identifier = "transaction-" + counter.getAndIncrement();
+ this.readOnly = readOnly;
+ this.actorContext = actorContext;
+
+ Object response = actorContext.executeShardOperation(Shard.DEFAULT_NAME, new CreateTransaction(), ActorContext.ASK_DURATION);
+ if(response instanceof CreateTransactionReply){
+ CreateTransactionReply reply = (CreateTransactionReply) response;
+ remoteTransactionPaths.put(Shard.DEFAULT_NAME, actorContext.actorSelection(reply.getTransactionPath()));
+ }
+ }
+
@Override
- public ListenableFuture<Optional<NormalizedNode<?, ?>>> read(InstanceIdentifier path) {
- throw new UnsupportedOperationException("read");
+ public ListenableFuture<Optional<NormalizedNode<?, ?>>> read(final InstanceIdentifier path) {
+ final ActorSelection remoteTransaction = remoteTransactionFromIdentifier(path);
+
+ Callable<Optional<NormalizedNode<?,?>>> call = new Callable() {
+
+ @Override public Optional<NormalizedNode<?,?>> call() throws Exception {
+ Object response = actorContext
+ .executeRemoteOperation(remoteTransaction, new ReadData(path),
+ ActorContext.ASK_DURATION);
+ if(response instanceof ReadDataReply){
+ ReadDataReply reply = (ReadDataReply) response;
+ //FIXME : A cast should not be required here ???
+ return (Optional<NormalizedNode<?, ?>>) Optional.of(reply.getNormalizedNode());
+ }
+
+ return Optional.absent();
+ }
+ };
+
+ ListenableFutureTask<Optional<NormalizedNode<?, ?>>>
+ future = ListenableFutureTask.create(call);
+
+ //FIXME : Use a thread pool here
+ Executors.newSingleThreadExecutor().submit(future);
+
+ return future;
}
@Override
public void write(InstanceIdentifier path, NormalizedNode<?, ?> data) {
- throw new UnsupportedOperationException("write");
+ final ActorSelection remoteTransaction = remoteTransactionFromIdentifier(path);
+ remoteTransaction.tell(new WriteData(path, data), null);
}
@Override
public void merge(InstanceIdentifier path, NormalizedNode<?, ?> data) {
- throw new UnsupportedOperationException("merge");
+ final ActorSelection remoteTransaction = remoteTransactionFromIdentifier(path);
+ remoteTransaction.tell(new MergeData(path, data), null);
}
@Override
public void delete(InstanceIdentifier path) {
- throw new UnsupportedOperationException("delete");
+ final ActorSelection remoteTransaction = remoteTransactionFromIdentifier(path);
+ remoteTransaction.tell(new DeleteData(path), null);
}
@Override
public DOMStoreThreePhaseCommitCohort ready() {
- throw new UnsupportedOperationException("ready");
+ List<ActorPath> cohortPaths = new ArrayList<>();
+
+ for(ActorSelection remoteTransaction : remoteTransactionPaths.values()) {
+ Object result = actorContext.executeRemoteOperation(remoteTransaction,
+ new ReadyTransaction(),
+ ActorContext.ASK_DURATION
+ );
+
+ if(result instanceof ReadyTransactionReply){
+ ReadyTransactionReply reply = (ReadyTransactionReply) result;
+ cohortPaths.add(reply.getCohortPath());
+ }
+ }
+
+ return new ThreePhaseCommitCohortProxy(cohortPaths);
}
@Override
public Object getIdentifier() {
- throw new UnsupportedOperationException("getIdentifier");
+ return this.identifier;
}
@Override
public void close() {
- throw new UnsupportedOperationException("close");
+ for(ActorSelection remoteTransaction : remoteTransactionPaths.values()) {
+ remoteTransaction.tell(new CloseTransaction(), null);
+ }
+ }
+
+ private ActorSelection remoteTransactionFromIdentifier(InstanceIdentifier path){
+ String shardName = shardNameFromIdentifier(path);
+ return remoteTransactionPaths.get(shardName);
+ }
+
+ private String shardNameFromIdentifier(InstanceIdentifier path){
+ return Shard.DEFAULT_NAME;
}
}
package org.opendaylight.controller.cluster.datastore.messages;
+import akka.actor.ActorPath;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
-import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
public class RegisterChangeListener {
- private final InstanceIdentifier path;
- private final AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>> listener;
- private final AsyncDataBroker.DataChangeScope scope;
+ private final InstanceIdentifier path;
+ private final ActorPath dataChangeListenerPath;
+ private final AsyncDataBroker.DataChangeScope scope;
- public RegisterChangeListener(InstanceIdentifier path, AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>> listener, AsyncDataBroker.DataChangeScope scope) {
- this.path = path;
- this.listener = listener;
- this.scope = scope;
- }
+ public RegisterChangeListener(InstanceIdentifier path,
+ ActorPath dataChangeListenerPath,
+ AsyncDataBroker.DataChangeScope scope) {
+ this.path = path;
+ this.dataChangeListenerPath = dataChangeListenerPath;
+ this.scope = scope;
+ }
- public InstanceIdentifier getPath() {
- return path;
- }
+ public InstanceIdentifier getPath() {
+ return path;
+ }
- public AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>> getListener() {
- return listener;
- }
- public AsyncDataBroker.DataChangeScope getScope() {
- return scope;
- }
+ public AsyncDataBroker.DataChangeScope getScope() {
+ return scope;
+ }
+
+ public ActorPath getDataChangeListenerPath() {
+ return dataChangeListenerPath;
+ }
}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.utils;
+
+import akka.actor.ActorPath;
+import akka.actor.ActorRef;
+import akka.actor.ActorSelection;
+import akka.actor.ActorSystem;
+import akka.util.Timeout;
+import org.opendaylight.controller.cluster.datastore.messages.FindPrimary;
+import org.opendaylight.controller.cluster.datastore.messages.PrimaryFound;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import scala.concurrent.Await;
+import scala.concurrent.Future;
+import scala.concurrent.duration.Duration;
+import scala.concurrent.duration.FiniteDuration;
+
+import java.util.concurrent.TimeUnit;
+
+import static akka.pattern.Patterns.ask;
+
+/**
+ * The ActorContext class contains utility methods which could be used by
+ * non-actors (like DistributedDataStore) to work with actors a little more
+ * easily. An ActorContext can be freely passed around to local object instances
+ * but should not be passed to actors especially remote actors
+ */
+public class ActorContext {
+ private static final Logger
+ LOG = LoggerFactory.getLogger(ActorContext.class);
+
+ public static final FiniteDuration ASK_DURATION = Duration.create(5, TimeUnit.SECONDS);
+ public static final Duration AWAIT_DURATION = Duration.create(5, TimeUnit.SECONDS);
+
+ private final ActorSystem actorSystem;
+ private final ActorRef shardManager;
+
+ public ActorContext(ActorSystem actorSystem, ActorRef shardManager){
+ this.actorSystem = actorSystem;
+ this.shardManager = shardManager;
+ }
+
+ public ActorSystem getActorSystem() {
+ return actorSystem;
+ }
+
+ public ActorRef getShardManager() {
+ return shardManager;
+ }
+
+ public ActorSelection actorSelection(String actorPath){
+ return actorSystem.actorSelection(actorPath);
+ }
+
+ public ActorSelection actorSelection(ActorPath actorPath){
+ return actorSystem.actorSelection(actorPath);
+ }
+
+
+ /**
+ * Finds the primary for a given shard
+ *
+ * @param shardName
+ * @return
+ */
+ public ActorSelection findPrimary(String shardName) {
+ Object result = executeLocalOperation(shardManager,
+ new FindPrimary(shardName), ASK_DURATION);
+
+ if(result instanceof PrimaryFound){
+ PrimaryFound found = (PrimaryFound) result;
+
+ LOG.error("Primary found {}", found.getPrimaryPath());
+
+ return actorSystem.actorSelection(found.getPrimaryPath());
+ }
+ throw new RuntimeException("primary was not found");
+ }
+
+ /**
+ * Executes an operation on a local actor and wait for it's response
+ * @param actor
+ * @param message
+ * @param duration
+ * @return The response of the operation
+ */
+ public Object executeLocalOperation(ActorRef actor, Object message,
+ FiniteDuration duration){
+ Future<Object> future =
+ ask(actor, message, new Timeout(duration));
+
+ try {
+ return Await.result(future, AWAIT_DURATION);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Execute an operation on a remote actor and wait for it's response
+ * @param actor
+ * @param message
+ * @param duration
+ * @return
+ */
+ public Object executeRemoteOperation(ActorSelection actor, Object message,
+ FiniteDuration duration){
+ Future<Object> future =
+ ask(actor, message, new Timeout(duration));
+
+ try {
+ return Await.result(future, AWAIT_DURATION);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Execute an operation on the primary for a given shard
+ * <p>
+ * This method first finds the primary for a given shard ,then sends
+ * the message to the remote shard and waits for a response
+ * </p>
+ * @param shardName
+ * @param message
+ * @param duration
+ * @throws java.lang.RuntimeException when a primary is not found or if the message to the remote shard fails or times out
+ *
+ * @return
+ */
+ public Object executeShardOperation(String shardName, Object message, FiniteDuration duration){
+ ActorSelection primary = findPrimary(shardName);
+
+ return executeRemoteOperation(primary, message, duration);
+ }
+
+}
package org.opendaylight.controller.cluster.datastore;
+import akka.actor.ActorRef;
+import akka.actor.Props;
import junit.framework.Assert;
+import org.opendaylight.controller.cluster.datastore.messages.CreateTransactionReply;
+import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListenerReply;
+import org.opendaylight.controller.cluster.datastore.utils.DoNothingActor;
+import org.opendaylight.controller.cluster.datastore.utils.MockActorContext;
+import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
public class DistributedDataStoreTest extends AbstractActorTest{
private DistributedDataStore distributedDataStore;
+ private MockActorContext mockActorContext;
+ private ActorRef doNothingActorRef;
@org.junit.Before
public void setUp() throws Exception {
- distributedDataStore = new DistributedDataStore(getSystem(), "config");
+ final Props props = Props.create(DoNothingActor.class);
+
+ doNothingActorRef = getSystem().actorOf(props);
+
+ mockActorContext = new MockActorContext(getSystem(), doNothingActorRef);
+ distributedDataStore = new DistributedDataStore(mockActorContext, "config");
+ distributedDataStore.onGlobalContextUpdated(
+ TestModel.createTestContext());
+
+ // Make CreateTransactionReply as the default response. Will need to be
+ // tuned if a specific test requires some other response
+ mockActorContext.setExecuteShardOperationResponse(
+ new CreateTransactionReply(doNothingActorRef.path()));
}
@org.junit.After
@org.junit.Test
public void testRegisterChangeListener() throws Exception {
+ mockActorContext.setExecuteShardOperationResponse(new RegisterChangeListenerReply(doNothingActorRef.path()));
ListenerRegistration registration =
- distributedDataStore.registerChangeListener(InstanceIdentifier.builder().build(), new AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>() {
+ distributedDataStore.registerChangeListener(TestModel.TEST_PATH, new AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>() {
@Override
public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier, NormalizedNode<?, ?>> change) {
throw new UnsupportedOperationException("onDataChanged");
subject.tell(new UpdateSchemaContext(TestModel.createTestContext()), getRef());
- subject.tell(new RegisterChangeListener(InstanceIdentifier.builder().build(), noOpDataChangeListener() , AsyncDataBroker.DataChangeScope.BASE), getRef());
+ subject.tell(new RegisterChangeListener(TestModel.TEST_PATH, getRef().path() , AsyncDataBroker.DataChangeScope.BASE), getRef());
final String out = new ExpectMsg<String>("match hint") {
// do not put code outside this method, will run afterwards
}
};
}
-}
\ No newline at end of file
+}
--- /dev/null
+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 junit.framework.Assert;
+import org.junit.Test;
+import org.opendaylight.controller.cluster.datastore.messages.CloseTransaction;
+import org.opendaylight.controller.cluster.datastore.messages.CreateTransactionReply;
+import org.opendaylight.controller.cluster.datastore.messages.DeleteData;
+import org.opendaylight.controller.cluster.datastore.messages.MergeData;
+import org.opendaylight.controller.cluster.datastore.messages.ReadDataReply;
+import org.opendaylight.controller.cluster.datastore.messages.ReadyTransactionReply;
+import org.opendaylight.controller.cluster.datastore.messages.WriteData;
+import org.opendaylight.controller.cluster.datastore.utils.ActorContext;
+import org.opendaylight.controller.cluster.datastore.utils.DoNothingActor;
+import org.opendaylight.controller.cluster.datastore.utils.MessageCollectorActor;
+import org.opendaylight.controller.cluster.datastore.utils.MockActorContext;
+import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
+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 java.util.List;
+
+public class TransactionProxyTest extends AbstractActorTest {
+
+ @Test
+ public void testRead() throws Exception {
+ final Props props = Props.create(DoNothingActor.class);
+ final ActorRef actorRef = getSystem().actorOf(props);
+
+ final MockActorContext actorContext = new MockActorContext(this.getSystem());
+ actorContext.setExecuteShardOperationResponse(new CreateTransactionReply(actorRef.path()));
+ actorContext.setExecuteRemoteOperationResponse("message");
+
+ TransactionProxy transactionProxy =
+ new TransactionProxy(actorContext,
+ TransactionProxy.TransactionType.READ_ONLY);
+
+
+ ListenableFuture<Optional<NormalizedNode<?, ?>>> read =
+ transactionProxy.read(TestModel.TEST_PATH);
+
+ Optional<NormalizedNode<?, ?>> normalizedNodeOptional = read.get();
+
+ Assert.assertFalse(normalizedNodeOptional.isPresent());
+
+ actorContext.setExecuteRemoteOperationResponse(new ReadDataReply(
+ ImmutableNodes.containerNode(TestModel.TEST_QNAME)));
+
+ read = transactionProxy.read(TestModel.TEST_PATH);
+
+ normalizedNodeOptional = read.get();
+
+ Assert.assertTrue(normalizedNodeOptional.isPresent());
+ }
+
+ @Test
+ public void testWrite() throws Exception {
+ final Props props = Props.create(MessageCollectorActor.class);
+ final ActorRef actorRef = getSystem().actorOf(props);
+
+ final MockActorContext actorContext = new MockActorContext(this.getSystem());
+ actorContext.setExecuteShardOperationResponse(new CreateTransactionReply(actorRef.path()));
+ actorContext.setExecuteRemoteOperationResponse("message");
+
+ TransactionProxy transactionProxy =
+ new TransactionProxy(actorContext,
+ TransactionProxy.TransactionType.READ_ONLY);
+
+ transactionProxy.write(TestModel.TEST_PATH,
+ ImmutableNodes.containerNode(TestModel.NAME_QNAME));
+
+ ActorContext testContext = new ActorContext(getSystem(), getSystem().actorOf(Props.create(DoNothingActor.class)));
+ Object messages = testContext
+ .executeLocalOperation(actorRef, "messages",
+ ActorContext.ASK_DURATION);
+
+ Assert.assertNotNull(messages);
+
+ Assert.assertTrue(messages instanceof List);
+
+ List<Object> listMessages = (List<Object>) messages;
+
+ Assert.assertEquals(1, listMessages.size());
+
+ Assert.assertTrue(listMessages.get(0) instanceof WriteData);
+ }
+
+ @Test
+ public void testMerge() throws Exception {
+ final Props props = Props.create(MessageCollectorActor.class);
+ final ActorRef actorRef = getSystem().actorOf(props);
+
+ final MockActorContext actorContext = new MockActorContext(this.getSystem());
+ actorContext.setExecuteShardOperationResponse(new CreateTransactionReply(actorRef.path()));
+ actorContext.setExecuteRemoteOperationResponse("message");
+
+ TransactionProxy transactionProxy =
+ new TransactionProxy(actorContext,
+ TransactionProxy.TransactionType.READ_ONLY);
+
+ transactionProxy.merge(TestModel.TEST_PATH,
+ ImmutableNodes.containerNode(TestModel.NAME_QNAME));
+
+ ActorContext testContext = new ActorContext(getSystem(), getSystem().actorOf(Props.create(DoNothingActor.class)));
+ Object messages = testContext
+ .executeLocalOperation(actorRef, "messages",
+ ActorContext.ASK_DURATION);
+
+ Assert.assertNotNull(messages);
+
+ Assert.assertTrue(messages instanceof List);
+
+ List<Object> listMessages = (List<Object>) messages;
+
+ Assert.assertEquals(1, listMessages.size());
+
+ Assert.assertTrue(listMessages.get(0) instanceof MergeData);
+ }
+
+ @Test
+ public void testDelete() throws Exception {
+ final Props props = Props.create(MessageCollectorActor.class);
+ final ActorRef actorRef = getSystem().actorOf(props);
+
+ final MockActorContext actorContext = new MockActorContext(this.getSystem());
+ actorContext.setExecuteShardOperationResponse(new CreateTransactionReply(actorRef.path()));
+ actorContext.setExecuteRemoteOperationResponse("message");
+
+ TransactionProxy transactionProxy =
+ new TransactionProxy(actorContext,
+ TransactionProxy.TransactionType.READ_ONLY);
+
+ transactionProxy.delete(TestModel.TEST_PATH);
+
+ ActorContext testContext = new ActorContext(getSystem(), getSystem().actorOf(Props.create(DoNothingActor.class)));
+ Object messages = testContext
+ .executeLocalOperation(actorRef, "messages",
+ ActorContext.ASK_DURATION);
+
+ Assert.assertNotNull(messages);
+
+ Assert.assertTrue(messages instanceof List);
+
+ List<Object> listMessages = (List<Object>) messages;
+
+ Assert.assertEquals(1, listMessages.size());
+
+ Assert.assertTrue(listMessages.get(0) instanceof DeleteData);
+ }
+
+ @Test
+ public void testReady() throws Exception {
+ final Props props = Props.create(DoNothingActor.class);
+ final ActorRef doNothingActorRef = getSystem().actorOf(props);
+
+ final MockActorContext actorContext = new MockActorContext(this.getSystem());
+ actorContext.setExecuteShardOperationResponse(new CreateTransactionReply(doNothingActorRef.path()));
+ actorContext.setExecuteRemoteOperationResponse(new ReadyTransactionReply(doNothingActorRef.path()));
+
+ TransactionProxy transactionProxy =
+ new TransactionProxy(actorContext,
+ TransactionProxy.TransactionType.READ_ONLY);
+
+
+ DOMStoreThreePhaseCommitCohort ready = transactionProxy.ready();
+
+ Assert.assertTrue(ready instanceof ThreePhaseCommitCohortProxy);
+
+ ThreePhaseCommitCohortProxy proxy = (ThreePhaseCommitCohortProxy) ready;
+
+ Assert.assertTrue("No cohort paths returned", proxy.getCohortPaths().size() > 0);
+
+ }
+
+ @Test
+ public void testGetIdentifier(){
+ final Props props = Props.create(DoNothingActor.class);
+ final ActorRef doNothingActorRef = getSystem().actorOf(props);
+
+ final MockActorContext actorContext = new MockActorContext(this.getSystem());
+ actorContext.setExecuteShardOperationResponse(
+ new CreateTransactionReply(doNothingActorRef.path()));
+
+ TransactionProxy transactionProxy =
+ new TransactionProxy(actorContext,
+ TransactionProxy.TransactionType.READ_ONLY);
+
+ Assert.assertNotNull(transactionProxy.getIdentifier());
+ }
+
+ @Test
+ public void testClose(){
+ final Props props = Props.create(MessageCollectorActor.class);
+ final ActorRef actorRef = getSystem().actorOf(props);
+
+ final MockActorContext actorContext = new MockActorContext(this.getSystem());
+ actorContext.setExecuteShardOperationResponse(new CreateTransactionReply(actorRef.path()));
+ actorContext.setExecuteRemoteOperationResponse("message");
+
+ TransactionProxy transactionProxy =
+ new TransactionProxy(actorContext,
+ TransactionProxy.TransactionType.READ_ONLY);
+
+ transactionProxy.close();
+
+ ActorContext testContext = new ActorContext(getSystem(), getSystem().actorOf(Props.create(DoNothingActor.class)));
+ Object messages = testContext
+ .executeLocalOperation(actorRef, "messages",
+ ActorContext.ASK_DURATION);
+
+ Assert.assertNotNull(messages);
+
+ Assert.assertTrue(messages instanceof List);
+
+ List<Object> listMessages = (List<Object>) messages;
+
+ Assert.assertEquals(1, listMessages.size());
+
+ Assert.assertTrue(listMessages.get(0) instanceof CloseTransaction);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.utils;
+
+import akka.actor.UntypedActor;
+
+public class DoNothingActor extends UntypedActor {
+
+ @Override public void onReceive(Object message) throws Exception {
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.utils;
+
+import akka.actor.UntypedActor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * MessageCollectorActor collects messages as it receives them. It can send
+ * those collected messages to any sender which sends it the "messages" message
+ * <p>
+ * This class would be useful as a mock to test whether messages were sent
+ * to a remote actor or not.
+ * </p>
+ */
+public class MessageCollectorActor extends UntypedActor {
+ private List<Object> messages = new ArrayList<>();
+
+ @Override public void onReceive(Object message) throws Exception {
+ if(message instanceof String){
+ if("messages".equals(message)){
+ getSender().tell(messages, getSelf());
+ }
+ } else {
+ messages.add(message);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.utils;
+
+
+import akka.actor.ActorRef;
+import akka.actor.ActorSelection;
+import akka.actor.ActorSystem;
+import scala.concurrent.duration.FiniteDuration;
+
+public class MockActorContext extends ActorContext {
+
+ private Object executeShardOperationResponse;
+ private Object executeRemoteOperationResponse;
+ private Object executeLocalOperationResponse;
+
+ public MockActorContext(ActorSystem actorSystem) {
+ super(actorSystem, null);
+ }
+
+ public MockActorContext(ActorSystem actorSystem, ActorRef shardManager) {
+ super(actorSystem, shardManager);
+ }
+
+
+ @Override public Object executeShardOperation(String shardName,
+ Object message, FiniteDuration duration) {
+ return executeShardOperationResponse;
+ }
+
+ @Override public Object executeRemoteOperation(ActorSelection actor,
+ Object message, FiniteDuration duration) {
+ return executeRemoteOperationResponse;
+ }
+
+ @Override public ActorSelection findPrimary(String shardName) {
+ return null;
+ }
+
+ public void setExecuteShardOperationResponse(Object response){
+ executeShardOperationResponse = response;
+ }
+
+ public void setExecuteRemoteOperationResponse(Object response){
+ executeRemoteOperationResponse = response;
+ }
+
+ public void setExecuteLocalOperationResponse(
+ Object executeLocalOperationResponse) {
+ this.executeLocalOperationResponse = executeLocalOperationResponse;
+ }
+
+
+}