void init(final String shardName, final YangInstanceIdentifier treeId) {
   Future<ActorRef> findFuture = actorContext.findLocalShardAsync(shardName);
   findFuture.onComplete(
       new OnComplete<ActorRef>() {
         @Override
         public void onComplete(final Throwable failure, final ActorRef shard) {
           if (failure instanceof LocalShardNotFoundException) {
             LOG.debug(
                 "No local shard found for {} - DataTreeChangeListener {} at path {} "
                     + "cannot be registered",
                 shardName,
                 getInstance(),
                 treeId);
           } else if (failure != null) {
             LOG.error(
                 "Failed to find local shard {} - DataTreeChangeListener {} at path {} "
                     + "cannot be registered: {}",
                 shardName,
                 getInstance(),
                 treeId,
                 failure);
           } else {
             doRegistration(shard, treeId);
           }
         }
       },
       actorContext.getClientDispatcher());
 }
  private void doRegistration(final ActorRef shard, final YangInstanceIdentifier path) {

    Future<Object> future =
        actorContext.executeOperationAsync(
            shard,
            new RegisterDataTreeChangeListener(path, dataChangeListenerActor),
            actorContext.getDatastoreContext().getShardInitializationTimeout());

    future.onComplete(
        new OnComplete<Object>() {
          @Override
          public void onComplete(final Throwable failure, final Object result) {
            if (failure != null) {
              LOG.error(
                  "Failed to register DataTreeChangeListener {} at path {}",
                  getInstance(),
                  path.toString(),
                  failure);
            } else {
              RegisterDataTreeChangeListenerReply reply =
                  (RegisterDataTreeChangeListenerReply) result;
              setListenerRegistrationActor(
                  actorContext.actorSelection(reply.getListenerRegistrationPath()));
            }
          }
        },
        actorContext.getClientDispatcher());
  }
 public DataTreeChangeListenerProxy(final ActorContext actorContext, final T listener) {
   super(listener);
   this.actorContext = Preconditions.checkNotNull(actorContext);
   this.dataChangeListenerActor =
       actorContext
           .getActorSystem()
           .actorOf(
               DataTreeChangeListenerActor.props(getInstance())
                   .withDispatcher(actorContext.getNotificationDispatcherPath()));
 }
  private Future<Iterable<Object>> invokeCohorts(Object message) {
    List<Future<Object>> futureList = Lists.newArrayListWithCapacity(cohorts.size());
    for (ActorSelection cohort : cohorts) {
      if (LOG.isDebugEnabled()) {
        LOG.debug("Tx {}: Sending {} to cohort {}", transactionId, message, cohort);
      }
      futureList.add(
          actorContext.executeOperationAsync(
              cohort, message, actorContext.getTransactionCommitOperationTimeout()));
    }

    return Futures.sequence(futureList, actorContext.getClientDispatcher());
  }
  @Override
  public ListenableFuture<Boolean> canCommit() {
    if (LOG.isDebugEnabled()) {
      LOG.debug("Tx {} canCommit", transactionId);
    }
    final SettableFuture<Boolean> returnFuture = SettableFuture.create();

    // The first phase of canCommit is to gather the list of cohort actor paths that will
    // participate in the commit. buildCohortPathsList combines the cohort path Futures into
    // one Future which we wait on asynchronously here. The cohort actor paths are
    // extracted from ReadyTransactionReply messages by the Futures that were obtained earlier
    // and passed to us from upstream processing. If any one fails then  we'll fail canCommit.

    buildCohortList()
        .onComplete(
            new OnComplete<Void>() {
              @Override
              public void onComplete(Throwable failure, Void notUsed) throws Throwable {
                if (failure != null) {
                  if (LOG.isDebugEnabled()) {
                    LOG.debug("Tx {}: a cohort Future failed: {}", transactionId, failure);
                  }
                  returnFuture.setException(failure);
                } else {
                  finishCanCommit(returnFuture);
                }
              }
            },
            actorContext.getClientDispatcher());

    return returnFuture;
  }
  final TransactionContextWrapper newTransactionContextWrapper(
      final TransactionProxy parent, final String shardName) {
    final TransactionContextWrapper transactionContextWrapper =
        new TransactionContextWrapper(parent.getIdentifier(), actorContext);

    Future<PrimaryShardInfo> findPrimaryFuture =
        findPrimaryShard(shardName, parent.getIdentifier());
    if (findPrimaryFuture.isCompleted()) {
      Try<PrimaryShardInfo> maybe = findPrimaryFuture.value().get();
      if (maybe.isSuccess()) {
        onFindPrimaryShardSuccess(maybe.get(), parent, shardName, transactionContextWrapper);
      } else {
        onFindPrimaryShardFailure(
            maybe.failed().get(), parent, shardName, transactionContextWrapper);
      }
    } else {
      findPrimaryFuture.onComplete(
          new OnComplete<PrimaryShardInfo>() {
            @Override
            public void onComplete(
                final Throwable failure, final PrimaryShardInfo primaryShardInfo) {
              if (failure == null) {
                onFindPrimaryShardSuccess(
                    primaryShardInfo, parent, shardName, transactionContextWrapper);
              } else {
                onFindPrimaryShardFailure(failure, parent, shardName, transactionContextWrapper);
              }
            }
          },
          actorContext.getClientDispatcher());
    }

    return transactionContextWrapper;
  }
  private ListenableFuture<Void> voidOperation(
      final String operationName,
      final Object message,
      final Class<?> expectedResponseClass,
      final boolean propagateException,
      final OperationCallback callback) {

    if (LOG.isDebugEnabled()) {
      LOG.debug("Tx {} {}", transactionId, operationName);
    }
    final SettableFuture<Void> returnFuture = SettableFuture.create();

    // The cohort actor list should already be built at this point by the canCommit phase but,
    // if not for some reason, we'll try to build it here.

    if (cohorts != null) {
      finishVoidOperation(
          operationName,
          message,
          expectedResponseClass,
          propagateException,
          returnFuture,
          callback);
    } else {
      buildCohortList()
          .onComplete(
              new OnComplete<Void>() {
                @Override
                public void onComplete(Throwable failure, Void notUsed) throws Throwable {
                  if (failure != null) {
                    if (LOG.isDebugEnabled()) {
                      LOG.debug(
                          "Tx {}: a {} cohort path Future failed: {}",
                          transactionId,
                          operationName,
                          failure);
                    }
                    if (propagateException) {
                      returnFuture.setException(failure);
                    } else {
                      returnFuture.set(null);
                    }
                  } else {
                    finishVoidOperation(
                        operationName,
                        message,
                        expectedResponseClass,
                        propagateException,
                        returnFuture,
                        callback);
                  }
                }
              },
              actorContext.getClientDispatcher());
    }

    return returnFuture;
  }
  private Future<Void> buildCohortList() {

    Future<Iterable<ActorSelection>> combinedFutures =
        Futures.sequence(cohortFutures, actorContext.getClientDispatcher());

    return combinedFutures.transform(
        new AbstractFunction1<Iterable<ActorSelection>, Void>() {
          @Override
          public Void apply(Iterable<ActorSelection> actorSelections) {
            cohorts = Lists.newArrayList(actorSelections);
            if (LOG.isDebugEnabled()) {
              LOG.debug("Tx {} successfully built cohort path list: {}", transactionId, cohorts);
            }
            return null;
          }
        },
        TransactionReadyReplyMapper.SAME_FAILURE_TRANSFORMER,
        actorContext.getClientDispatcher());
  }
  private void finishVoidOperation(
      final String operationName,
      final Object message,
      final Class<?> expectedResponseClass,
      final boolean propagateException,
      final SettableFuture<Void> returnFuture,
      final OperationCallback callback) {
    if (LOG.isDebugEnabled()) {
      LOG.debug("Tx {} finish {}", transactionId, operationName);
    }

    callback.run();

    Future<Iterable<Object>> combinedFuture = invokeCohorts(message);

    combinedFuture.onComplete(
        new OnComplete<Iterable<Object>>() {
          @Override
          public void onComplete(Throwable failure, Iterable<Object> responses) throws Throwable {

            Throwable exceptionToPropagate = failure;
            if (exceptionToPropagate == null) {
              for (Object response : responses) {
                if (!response.getClass().equals(expectedResponseClass)) {
                  exceptionToPropagate =
                      new IllegalArgumentException(
                          String.format("Unexpected response type %s", response.getClass()));
                  break;
                }
              }
            }

            if (exceptionToPropagate != null) {

              if (LOG.isDebugEnabled()) {
                LOG.debug(
                    "Tx {}: a {} cohort Future failed: {}",
                    transactionId,
                    operationName,
                    exceptionToPropagate);
              }
              if (propagateException) {
                // We don't log the exception here to avoid redundant logging since we're
                // propagating to the caller in MD-SAL core who will log it.
                returnFuture.setException(exceptionToPropagate);
              } else {
                // Since the caller doesn't want us to propagate the exception we'll also
                // not log it normally. But it's usually not good to totally silence
                // exceptions so we'll log it to debug level.
                if (LOG.isDebugEnabled()) {
                  LOG.debug(
                      String.format("%s failed", message.getClass().getSimpleName()),
                      exceptionToPropagate);
                }
                returnFuture.set(null);
              }

              callback.failure();
            } else {

              if (LOG.isDebugEnabled()) {
                LOG.debug("Tx {}: {} succeeded", transactionId, operationName);
              }
              returnFuture.set(null);

              callback.success();
            }
          }
        },
        actorContext.getClientDispatcher());
  }
  private void finishCanCommit(final SettableFuture<Boolean> returnFuture) {
    if (LOG.isDebugEnabled()) {
      LOG.debug("Tx {} finishCanCommit", transactionId);
    }

    // For empty transactions return immediately
    if (cohorts.size() == 0) {
      if (LOG.isDebugEnabled()) {
        LOG.debug("Tx {}: canCommit returning result: {}", transactionId, true);
      }
      returnFuture.set(Boolean.TRUE);
      return;
    }

    final Object message = new CanCommitTransaction(transactionId).toSerializable();

    final Iterator<ActorSelection> iterator = cohorts.iterator();

    final OnComplete<Object> onComplete =
        new OnComplete<Object>() {
          @Override
          public void onComplete(Throwable failure, Object response) throws Throwable {
            if (failure != null) {
              if (LOG.isDebugEnabled()) {
                LOG.debug("Tx {}: a canCommit cohort Future failed: {}", transactionId, failure);
              }
              returnFuture.setException(failure);
              return;
            }

            boolean result = true;
            if (response.getClass().equals(CanCommitTransactionReply.SERIALIZABLE_CLASS)) {
              CanCommitTransactionReply reply =
                  CanCommitTransactionReply.fromSerializable(response);
              if (!reply.getCanCommit()) {
                result = false;
              }
            } else {
              LOG.error("Unexpected response type {}", response.getClass());
              returnFuture.setException(
                  new IllegalArgumentException(
                      String.format("Unexpected response type %s", response.getClass())));
              return;
            }

            if (iterator.hasNext() && result) {
              Future<Object> future =
                  actorContext.executeOperationAsync(
                      iterator.next(),
                      message,
                      actorContext.getTransactionCommitOperationTimeout());
              future.onComplete(this, actorContext.getClientDispatcher());
            } else {
              if (LOG.isDebugEnabled()) {
                LOG.debug("Tx {}: canCommit returning result: {}", transactionId, result);
              }
              returnFuture.set(Boolean.valueOf(result));
            }
          }
        };

    Future<Object> future =
        actorContext.executeOperationAsync(
            iterator.next(), message, actorContext.getTransactionCommitOperationTimeout());
    future.onComplete(onComplete, actorContext.getClientDispatcher());
  }