private <KIn, VIn, KOut, VOut> Map<KOut, List<VOut>> combineForLocalReduction(
      MapCombineCommand<KIn, VIn, KOut, VOut> mcc, CollectableCollector<KOut, VOut> collector) {

    String taskId = mcc.getTaskId();
    Reducer<KOut, VOut> combiner = mcc.getCombiner();
    Map<KOut, List<VOut>> result = null;

    if (combiner != null) {
      result = new HashMap<KOut, List<VOut>>();
      log.tracef("For m/r task %s invoking combiner %s at %s", taskId, mcc, cdl.getAddress());
      MapReduceTaskLifecycleService taskLifecycleService =
          MapReduceTaskLifecycleService.getInstance();
      long start = log.isTraceEnabled() ? timeService.time() : 0;
      try {
        Cache<?, ?> cache = cacheManager.getCache(mcc.getCacheName());
        taskLifecycleService.onPreExecute(combiner, cache);
        Map<KOut, List<VOut>> collectedValues = collector.collectedValues();
        for (Entry<KOut, List<VOut>> e : collectedValues.entrySet()) {
          VOut combined;
          List<VOut> list = e.getValue();
          List<VOut> l = new LinkedList<VOut>();
          if (list.size() > 1) {
            combined = combiner.reduce(e.getKey(), list.iterator());
          } else {
            combined = list.get(0);
          }
          l.add(combined);
          result.put(e.getKey(), l);
          log.tracef(
              "For m/r task %s combined %s to %s at %s",
              taskId, e.getKey(), combined, cdl.getAddress());
        }
      } finally {
        if (log.isTraceEnabled()) {
          log.tracef(
              "Combine for task %s took %s milliseconds",
              mcc.getTaskId(), timeService.timeDuration(start, TimeUnit.MILLISECONDS));
        }
        taskLifecycleService.onPostExecute(combiner);
      }
    } else {
      // Combiner not specified
      result = collector.collectedValues();
    }
    return result;
  }
  /** @param isRemote true if the command is deserialized and is executed remote. */
  @Override
  public void initializeReplicableCommand(ReplicableCommand c, boolean isRemote) {
    if (c == null) return;
    switch (c.getCommandId()) {
      case PutKeyValueCommand.COMMAND_ID:
        ((PutKeyValueCommand) c).init(notifier, configuration);
        break;
      case ReplaceCommand.COMMAND_ID:
        ((ReplaceCommand) c).init(notifier, configuration);
        break;
      case PutMapCommand.COMMAND_ID:
        ((PutMapCommand) c).init(notifier);
        break;
      case RemoveCommand.COMMAND_ID:
        ((RemoveCommand) c).init(notifier, configuration);
        break;
      case MultipleRpcCommand.COMMAND_ID:
        MultipleRpcCommand rc = (MultipleRpcCommand) c;
        rc.init(interceptorChain, icf);
        if (rc.getCommands() != null)
          for (ReplicableCommand nested : rc.getCommands()) {
            initializeReplicableCommand(nested, false);
          }
        break;
      case SingleRpcCommand.COMMAND_ID:
        SingleRpcCommand src = (SingleRpcCommand) c;
        src.init(interceptorChain, icf);
        if (src.getCommand() != null) initializeReplicableCommand(src.getCommand(), false);

        break;
      case InvalidateCommand.COMMAND_ID:
        InvalidateCommand ic = (InvalidateCommand) c;
        ic.init(notifier, configuration);
        break;
      case InvalidateL1Command.COMMAND_ID:
        InvalidateL1Command ilc = (InvalidateL1Command) c;
        ilc.init(configuration, distributionManager, notifier, dataContainer);
        break;
      case PrepareCommand.COMMAND_ID:
      case VersionedPrepareCommand.COMMAND_ID:
      case TotalOrderNonVersionedPrepareCommand.COMMAND_ID:
      case TotalOrderVersionedPrepareCommand.COMMAND_ID:
        PrepareCommand pc = (PrepareCommand) c;
        pc.init(interceptorChain, icf, txTable);
        pc.initialize(notifier, recoveryManager);
        if (pc.getModifications() != null)
          for (ReplicableCommand nested : pc.getModifications()) {
            initializeReplicableCommand(nested, false);
          }
        pc.markTransactionAsRemote(isRemote);
        if (configuration.deadlockDetection().enabled() && isRemote) {
          DldGlobalTransaction transaction = (DldGlobalTransaction) pc.getGlobalTransaction();
          transaction.setLocksHeldAtOrigin(pc.getAffectedKeys());
        }
        break;
      case CommitCommand.COMMAND_ID:
      case VersionedCommitCommand.COMMAND_ID:
      case TotalOrderCommitCommand.COMMAND_ID:
      case TotalOrderVersionedCommitCommand.COMMAND_ID:
        CommitCommand commitCommand = (CommitCommand) c;
        commitCommand.init(interceptorChain, icf, txTable);
        commitCommand.markTransactionAsRemote(isRemote);
        break;
      case RollbackCommand.COMMAND_ID:
      case TotalOrderRollbackCommand.COMMAND_ID:
        RollbackCommand rollbackCommand = (RollbackCommand) c;
        rollbackCommand.init(interceptorChain, icf, txTable);
        rollbackCommand.markTransactionAsRemote(isRemote);
        break;
      case ClearCommand.COMMAND_ID:
        ClearCommand cc = (ClearCommand) c;
        cc.init(notifier, dataContainer);
        break;
      case ClusteredGetCommand.COMMAND_ID:
        ClusteredGetCommand clusteredGetCommand = (ClusteredGetCommand) c;
        clusteredGetCommand.initialize(
            icf,
            this,
            entryFactory,
            interceptorChain,
            distributionManager,
            txTable,
            configuration.dataContainer().keyEquivalence());
        break;
      case LockControlCommand.COMMAND_ID:
        LockControlCommand lcc = (LockControlCommand) c;
        lcc.init(interceptorChain, icf, txTable);
        lcc.markTransactionAsRemote(isRemote);
        if (configuration.deadlockDetection().enabled() && isRemote) {
          DldGlobalTransaction gtx = (DldGlobalTransaction) lcc.getGlobalTransaction();
          RemoteTransaction transaction = txTable.getRemoteTransaction(gtx);
          if (transaction != null) {
            if (!configuration.clustering().cacheMode().isDistributed()) {
              Set<Object> keys = txTable.getLockedKeysForRemoteTransaction(gtx);
              GlobalTransaction gtx2 = transaction.getGlobalTransaction();
              ((DldGlobalTransaction) gtx2).setLocksHeldAtOrigin(keys);
              gtx.setLocksHeldAtOrigin(keys);
            } else {
              GlobalTransaction gtx2 = transaction.getGlobalTransaction();
              ((DldGlobalTransaction) gtx2).setLocksHeldAtOrigin(gtx.getLocksHeldAtOrigin());
            }
          }
        }
        break;
      case StateRequestCommand.COMMAND_ID:
        ((StateRequestCommand) c).init(stateProvider);
        break;
      case StateResponseCommand.COMMAND_ID:
        ((StateResponseCommand) c).init(stateConsumer);
        break;
      case GetInDoubtTransactionsCommand.COMMAND_ID:
        GetInDoubtTransactionsCommand gptx = (GetInDoubtTransactionsCommand) c;
        gptx.init(recoveryManager);
        break;
      case TxCompletionNotificationCommand.COMMAND_ID:
        TxCompletionNotificationCommand ftx = (TxCompletionNotificationCommand) c;
        ftx.init(txTable, lockManager, recoveryManager, stateTransferManager);
        break;
      case MapCombineCommand.COMMAND_ID:
        MapCombineCommand mrc = (MapCombineCommand) c;
        mrc.init(mapReduceManager);
        break;
      case ReduceCommand.COMMAND_ID:
        ReduceCommand reduceCommand = (ReduceCommand) c;
        reduceCommand.init(mapReduceManager);
        break;
      case DistributedExecuteCommand.COMMAND_ID:
        DistributedExecuteCommand dec = (DistributedExecuteCommand) c;
        dec.init(cache);
        break;
      case GetInDoubtTxInfoCommand.COMMAND_ID:
        GetInDoubtTxInfoCommand gidTxInfoCommand = (GetInDoubtTxInfoCommand) c;
        gidTxInfoCommand.init(recoveryManager);
        break;
      case CompleteTransactionCommand.COMMAND_ID:
        CompleteTransactionCommand ccc = (CompleteTransactionCommand) c;
        ccc.init(recoveryManager);
        break;
      case ApplyDeltaCommand.COMMAND_ID:
        break;
      case CreateCacheCommand.COMMAND_ID:
        CreateCacheCommand createCacheCommand = (CreateCacheCommand) c;
        createCacheCommand.init(cache.getCacheManager());
        break;
      case XSiteAdminCommand.COMMAND_ID:
        XSiteAdminCommand xSiteAdminCommand = (XSiteAdminCommand) c;
        xSiteAdminCommand.init(backupSender);
        break;
      case CancelCommand.COMMAND_ID:
        CancelCommand cancelCommand = (CancelCommand) c;
        cancelCommand.init(cancellationService);
        break;
      case XSiteStateTransferControlCommand.COMMAND_ID:
        XSiteStateTransferControlCommand xSiteStateTransferControlCommand =
            (XSiteStateTransferControlCommand) c;
        xSiteStateTransferControlCommand.initialize(
            xSiteStateProvider, xSiteStateConsumer, xSiteStateTransferManager);
        break;
      case XSiteStatePushCommand.COMMAND_ID:
        XSiteStatePushCommand xSiteStatePushCommand = (XSiteStatePushCommand) c;
        xSiteStatePushCommand.initialize(xSiteStateConsumer);
        break;
      case EntryRequestCommand.COMMAND_ID:
        EntryRequestCommand entryRequestCommand = (EntryRequestCommand) c;
        entryRequestCommand.init(entryRetriever);
        break;
      case EntryResponseCommand.COMMAND_ID:
        EntryResponseCommand entryResponseCommand = (EntryResponseCommand) c;
        entryResponseCommand.init(entryRetriever);
        break;
      case GetKeysInGroupCommand.COMMAND_ID:
        GetKeysInGroupCommand getKeysInGroupCommand = (GetKeysInGroupCommand) c;
        getKeysInGroupCommand.setGroupManager(groupManager);
        break;
      case ClusteredGetAllCommand.COMMAND_ID:
        ClusteredGetAllCommand clusteredGetAllCommand = (ClusteredGetAllCommand) c;
        clusteredGetAllCommand.init(
            icf,
            this,
            entryFactory,
            interceptorChain,
            txTable,
            configuration.dataContainer().keyEquivalence());
        break;
      case StreamRequestCommand.COMMAND_ID:
        StreamRequestCommand streamRequestCommand = (StreamRequestCommand) c;
        streamRequestCommand.inject(localStreamManager);
        break;
      case StreamResponseCommand.COMMAND_ID:
        StreamResponseCommand streamResponseCommand = (StreamResponseCommand) c;
        streamResponseCommand.inject(clusterStreamManager);
        break;
      case StreamSegmentResponseCommand.COMMAND_ID:
        StreamSegmentResponseCommand streamSegmentResponseCommand =
            (StreamSegmentResponseCommand) c;
        streamSegmentResponseCommand.inject(clusterStreamManager);
        break;
      case RemoveExpiredCommand.COMMAND_ID:
        RemoveExpiredCommand removeExpiredCommand = (RemoveExpiredCommand) c;
        removeExpiredCommand.init(notifier, configuration);
        break;
      default:
        ModuleCommandInitializer mci = moduleCommandInitializers.get(c.getCommandId());
        if (mci != null) {
          mci.initializeReplicableCommand(c, isRemote);
        } else {
          if (trace) log.tracef("Nothing to initialize for command: %s", c);
        }
    }
  }
  protected <KIn, VIn, KOut, VOut> Set<KOut> combine(
      MapCombineCommand<KIn, VIn, KOut, VOut> mcc, CollectableCollector<KOut, VOut> collector)
      throws Exception {

    String taskId = mcc.getTaskId();
    boolean emitCompositeIntermediateKeys = mcc.isEmitCompositeIntermediateKeys();
    Reducer<KOut, VOut> combiner = mcc.getCombiner();
    Set<KOut> mapPhaseKeys = new HashSet<KOut>();
    Cache<Object, DeltaAwareList<VOut>> tmpCache = null;
    if (emitCompositeIntermediateKeys) {
      tmpCache = cacheManager.getCache(DEFAULT_TMP_CACHE_CONFIGURATION_NAME);
    } else {
      tmpCache = cacheManager.getCache(taskId);
    }
    if (tmpCache == null) {
      throw new IllegalStateException(
          "Temporary cache for MapReduceTask " + taskId + " not found on " + cdl.getAddress());
    }
    DistributionManager dm = tmpCache.getAdvancedCache().getDistributionManager();

    if (combiner != null) {
      Cache<?, ?> cache = cacheManager.getCache(mcc.getCacheName());
      log.tracef("For m/r task %s invoking combiner %s at %s", taskId, mcc, cdl.getAddress());
      MapReduceTaskLifecycleService taskLifecycleService =
          MapReduceTaskLifecycleService.getInstance();
      Map<KOut, VOut> combinedMap = new ConcurrentHashMap<KOut, VOut>();
      long start = log.isTraceEnabled() ? timeService.time() : 0;
      try {
        taskLifecycleService.onPreExecute(combiner, cache);
        Map<KOut, List<VOut>> collectedValues = collector.collectedValues();
        for (Entry<KOut, List<VOut>> e : collectedValues.entrySet()) {
          List<VOut> list = e.getValue();
          VOut combined;
          if (list.size() > 1) {
            combined = combiner.reduce(e.getKey(), list.iterator());
            combinedMap.put(e.getKey(), combined);
          } else {
            combined = list.get(0);
            combinedMap.put(e.getKey(), combined);
          }
          log.tracef(
              "For m/r task %s combined %s to %s at %s",
              taskId, e.getKey(), combined, cdl.getAddress());
        }
      } finally {
        if (log.isTraceEnabled()) {
          log.tracef(
              "Combine for task %s took %s milliseconds",
              mcc.getTaskId(), timeService.timeDuration(start, TimeUnit.MILLISECONDS));
        }
        taskLifecycleService.onPostExecute(combiner);
      }
      Map<Address, List<KOut>> keysToNodes =
          mapKeysToNodes(dm, taskId, combinedMap.keySet(), emitCompositeIntermediateKeys);

      start = log.isTraceEnabled() ? timeService.time() : 0;
      try {
        for (Entry<Address, List<KOut>> entry : keysToNodes.entrySet()) {
          List<KOut> keysHashedToAddress = entry.getValue();
          try {
            log.tracef(
                "For m/r task %s migrating intermediate keys %s to %s",
                taskId, keysHashedToAddress, entry.getKey());
            for (KOut key : keysHashedToAddress) {
              VOut value = combinedMap.get(key);
              DeltaAwareList<VOut> delta = new DeltaAwareList<VOut>(value);
              if (emitCompositeIntermediateKeys) {
                tmpCache.put(new IntermediateCompositeKey<KOut>(taskId, key), delta);
              } else {
                tmpCache.put(key, delta);
              }
              mapPhaseKeys.add(key);
            }
          } catch (Exception e) {
            throw new CacheException(
                "Could not move intermediate keys/values for M/R task " + taskId, e);
          }
        }
      } finally {
        if (log.isTraceEnabled()) {
          log.tracef(
              "Migrating keys for task %s took %s milliseconds (Migrated %s keys)",
              mcc.getTaskId(),
              timeService.timeDuration(start, TimeUnit.MILLISECONDS),
              mapPhaseKeys.size());
        }
      }
    } else {
      // Combiner not specified so lets insert each key/uncombined-List pair into tmp cache

      Map<KOut, List<VOut>> collectedValues = collector.collectedValues();
      Map<Address, List<KOut>> keysToNodes =
          mapKeysToNodes(dm, taskId, collectedValues.keySet(), emitCompositeIntermediateKeys);

      long start = log.isTraceEnabled() ? timeService.time() : 0;
      try {
        for (Entry<Address, List<KOut>> entry : keysToNodes.entrySet()) {
          List<KOut> keysHashedToAddress = entry.getValue();
          try {
            log.tracef(
                "For m/r task %s migrating intermediate keys %s to %s",
                taskId, keysHashedToAddress, entry.getKey());
            for (KOut key : keysHashedToAddress) {
              List<VOut> value = collectedValues.get(key);
              DeltaAwareList<VOut> delta = new DeltaAwareList<VOut>(value);
              if (emitCompositeIntermediateKeys) {
                tmpCache.put(new IntermediateCompositeKey<KOut>(taskId, key), delta);
              } else {
                tmpCache.put(key, delta);
              }
              mapPhaseKeys.add(key);
            }
          } catch (Exception e) {
            throw new CacheException(
                "Could not move intermediate keys/values for M/R task " + taskId, e);
          }
        }
      } finally {
        if (log.isTraceEnabled()) {
          log.tracef(
              "Migrating keys for task %s took %s milliseconds (Migrated %s keys)",
              mcc.getTaskId(),
              timeService.timeDuration(start, TimeUnit.MILLISECONDS),
              mapPhaseKeys.size());
        }
      }
    }
    return mapPhaseKeys;
  }
  protected <KIn, VIn, KOut, VOut> CollectableCollector<KOut, VOut> map(
      MapCombineCommand<KIn, VIn, KOut, VOut> mcc) throws InterruptedException {
    Cache<KIn, VIn> cache = cacheManager.getCache(mcc.getCacheName());
    Set<KIn> keys = mcc.getKeys();
    Set<KIn> inputKeysCopy = null;
    Mapper<KIn, VIn, KOut, VOut> mapper = mcc.getMapper();
    DistributionManager dm = cache.getAdvancedCache().getDistributionManager();
    boolean inputKeysSpecified = keys != null && !keys.isEmpty();
    Set<KIn> inputKeys = keys;
    if (!inputKeysSpecified) {
      inputKeys = filterLocalPrimaryOwner(cache.keySet(), dm);
    } else {
      inputKeysCopy = new HashSet<KIn>(keys);
    }
    // hook map function into lifecycle and execute it
    MapReduceTaskLifecycleService taskLifecycleService =
        MapReduceTaskLifecycleService.getInstance();
    DefaultCollector<KOut, VOut> collector = new DefaultCollector<KOut, VOut>();
    log.tracef("For m/r task %s invoking %s with input keys %s", mcc.getTaskId(), mcc, inputKeys);
    int interruptCount = 0;
    long start = log.isTraceEnabled() ? timeService.time() : 0;
    try {
      taskLifecycleService.onPreExecute(mapper, cache);
      for (KIn key : inputKeys) {
        if (checkInterrupt(interruptCount++) && Thread.currentThread().isInterrupted())
          throw new InterruptedException();

        VIn value = cache.get(key);
        mapper.map(key, value, collector);
        if (inputKeysSpecified) {
          inputKeysCopy.remove(key);
        }
      }
      Set<KIn> keysFromCacheLoader = null;
      if (inputKeysSpecified) {
        // load only specified remaining input keys - iff in CL and pinned to this primary owner
        keysFromCacheLoader = filterLocalPrimaryOwner(inputKeysCopy, dm);
      } else {
        // load everything from CL pinned to this primary owner
        keysFromCacheLoader =
            filterLocalPrimaryOwner(loadAllKeysFromCacheLoaderUsingFilter(inputKeys), dm);
      }
      log.tracef(
          "For m/r task %s cache loader input keys %s", mcc.getTaskId(), keysFromCacheLoader);
      interruptCount = 0;
      for (KIn key : keysFromCacheLoader) {
        if (checkInterrupt(interruptCount++) && Thread.currentThread().isInterrupted())
          throw new InterruptedException();

        VIn value = loadValueFromCacheLoader(key);
        if (value != null) {
          mapper.map(key, value, collector);
        }
      }
    } finally {
      if (log.isTraceEnabled()) {
        log.tracef(
            "Map phase for task %s took %s milliseconds",
            mcc.getTaskId(), timeService.timeDuration(start, TimeUnit.MILLISECONDS));
      }
      taskLifecycleService.onPostExecute(mapper);
    }
    return collector;
  }