/**
     * @param nodeId Node ID.
     * @return Class loader ID for node ID.
     */
    GridTuple2<GridUuid, Long> getClassLoaderId(UUID nodeId) {
      assert nodeId != null;

      assert Thread.holdsLock(mux);

      return loader().registeredClassLoaderId(nodeId);
    }
  private void addLineDef(@Nonnull final PlotLineDef toBeAdded) {
    if (!Thread.holdsLock(this)) throw new AssertionError();

    List<PlotLineDef> registeredLineDefs = registeredLineDefsMap.get(toBeAdded);
    if (registeredLineDefs == null) {
      registeredLineDefs = new ArrayList<PlotLineDef>();
      registeredLineDefsMap.put(toBeAdded, registeredLineDefs);
    }

    try {
      Iterables.find(
          registeredLineDefs,
          new Predicate<PlotLineDef>() {
            @Override
            public boolean apply(@Nullable PlotLineDef lineDef) {
              return lineDef == toBeAdded;
            }
          });

      // already added

    } catch (NoSuchElementException e) {
      registeredLineDefs.add(toBeAdded);
    }
  }
  /** {@inheritDoc} */
  protected void getNewMonitors(Map<String, Monitor> map) throws MonitorException {
    assert Thread.holdsLock(this);

    int used = prologue.getUsed();
    long modificationTime = prologue.getModificationTimeStamp();

    if ((used > lastUsed) || (lastModificationTime > modificationTime)) {

      lastUsed = used;
      lastModificationTime = modificationTime;

      Monitor monitor = getNextMonitorEntry();
      while (monitor != null) {
        String name = monitor.getName();

        // guard against duplicate entries
        if (!map.containsKey(name)) {
          map.put(name, monitor);

          /*
           * insertedMonitors is null when called from pollFor()
           * via buildMonitorMap(). Since we update insertedMonitors
           * at the end of buildMonitorMap(), it's ok to skip the
           * add here.
           */
          if (insertedMonitors != null) {
            insertedMonitors.add(monitor);
          }
        }
        monitor = getNextMonitorEntry();
      }
    }
  }
    /**
     * Checks if node is participating in deployment.
     *
     * @param nodeId Node ID to check.
     * @param ldrId Class loader ID.
     * @return {@code True} if node is participating in deployment.
     */
    boolean hasParticipant(UUID nodeId, GridUuid ldrId) {
      assert nodeId != null;
      assert ldrId != null;

      assert Thread.holdsLock(mux);

      return loader().hasRegisteredNode(nodeId, ldrId);
    }
  /**
   * Adds new participant to deployment.
   *
   * @param dep Shared deployment.
   * @param meta Request metadata.
   * @return {@code True} if participant was added.
   */
  private boolean addParticipant(SharedDeployment dep, GridDeploymentMetadata meta) {
    assert dep != null;
    assert meta != null;

    assert Thread.holdsLock(mux);

    if (!checkModeMatch(dep, meta)) return false;

    if (meta.participants() != null) {
      for (Map.Entry<UUID, GridTuple2<GridUuid, Long>> e : meta.participants().entrySet()) {
        dep.addParticipant(e.getKey(), e.getValue().get1(), e.getValue().get2());

        if (log.isDebugEnabled())
          log.debug(
              "Added new participant [nodeId="
                  + e.getKey()
                  + ", clsLdrId="
                  + e.getValue().get1()
                  + ", seqNum="
                  + e.getValue().get2()
                  + ']');
      }
    }

    if (dep.deployMode() == CONTINUOUS || meta.participants() == null) {
      if (!dep.addParticipant(meta.senderNodeId(), meta.classLoaderId(), meta.sequenceNumber())) {
        U.warn(
            log,
            "Failed to create shared mode deployment "
                + "(requested class loader was already undeployed, did sender node leave grid?) "
                + "[clsLdrId="
                + meta.classLoaderId()
                + ", senderNodeId="
                + meta.senderNodeId()
                + ']');

        return false;
      }

      if (log.isDebugEnabled())
        log.debug(
            "Added new participant [nodeId="
                + meta.senderNodeId()
                + ", clsLdrId="
                + meta.classLoaderId()
                + ", seqNum="
                + meta.sequenceNumber()
                + ']');
    }

    return true;
  }
  /**
   * @param meta Request metadata.
   * @return {@code True} if class loader is obsolete.
   */
  private boolean isDeadClassLoader(GridDeploymentMetadata meta) {
    assert Thread.holdsLock(mux);

    synchronized (mux) {
      if (deadClsLdrs.contains(meta.classLoaderId())) {
        if (log.isDebugEnabled()) log.debug("Ignoring request for obsolete class loader: " + meta);

        return true;
      }

      return false;
    }
  }
    /** Sets property removed. */
    void onRemoved() {
      assert Thread.holdsLock(mux);

      removed = true;

      Collection<GridUuid> deadIds = loader().registeredClassLoaderIds();

      if (log.isDebugEnabled()) log.debug("Registering dead class loader IDs: " + deadIds);

      synchronized (mux) {
        deadClsLdrs.addAll(deadIds);
      }
    }
    /** @param nodeId Node ID to remove. */
    void removeParticipant(UUID nodeId) {
      assert nodeId != null;

      assert Thread.holdsLock(mux);

      GridUuid ldrId = loader().unregister(nodeId);

      if (log.isDebugEnabled()) log.debug("Registering dead class loader ID: " + ldrId);

      synchronized (mux) {
        deadClsLdrs.add(ldrId);
      }
    }
  /** {@inheritDoc} */
  protected MonitorStatus getMonitorStatus(Map<String, Monitor> map) throws MonitorException {
    assert Thread.holdsLock(this);
    assert insertedMonitors != null;

    // load any new monitors
    getNewMonitors(map);

    // current implementation doesn't support deletion or reuse of entries
    ArrayList removed = EMPTY_LIST;
    ArrayList inserted = insertedMonitors;

    insertedMonitors = new ArrayList<Monitor>();
    return new MonitorStatus(inserted, removed);
  }
    /**
     * @param nodeId Grid node ID.
     * @param ldrId Class loader ID.
     * @param seqNum Sequence number for the class loader.
     * @return Whether actually added or not.
     */
    boolean addParticipant(UUID nodeId, GridUuid ldrId, long seqNum) {
      assert nodeId != null;
      assert ldrId != null;

      assert Thread.holdsLock(mux);

      synchronized (mux) {
        if (!deadClsLdrs.contains(ldrId)) {
          loader().register(nodeId, ldrId, seqNum);

          return true;
        }

        return false;
      }
    }
  /**
   * @param alias Class alias.
   * @return Deployed class.
   */
  @Nullable
  private GridDeployment getDeployment(String alias) {
    assert Thread.holdsLock(mux);

    LinkedList<GridDeployment> deps = cache.get(alias);

    if (deps != null) {
      assert !deps.isEmpty();

      GridDeployment dep = deps.getFirst();

      if (!dep.isUndeployed()) {
        return dep;
      }
    }

    return null;
  }
  // package-private
  int poll(int events, long timeout) throws IOException {
    assert Thread.holdsLock(blockingLock()) && !isBlocking();

    synchronized (readLock) {
      int n = 0;
      try {
        begin();
        synchronized (stateLock) {
          if (!isOpen()) return 0;
          readerThread = NativeThread.current();
        }
        n = Net.poll(fd, events, timeout);
      } finally {
        readerThread = 0;
        end(n > 0);
      }
      return n;
    }
  }
  /** {@inheritDoc} */
  protected void buildMonitorMap(Map<String, Monitor> map) throws MonitorException {
    assert Thread.holdsLock(this);

    // start at the beginning of the buffer
    buffer.rewind();

    // create pseudo monitors
    buildPseudoMonitors(map);

    // position buffer to start of the data section
    buffer.position(prologue.getSize());
    nextEntry = buffer.position();
    perfDataItem = 0;

    int used = prologue.getUsed();
    long modificationTime = prologue.getModificationTimeStamp();

    Monitor m = getNextMonitorEntry();
    while (m != null) {
      map.put(m.getName(), m);
      m = getNextMonitorEntry();
    }

    /*
     * set the last modification data. These are set to the values
     * recorded before parsing the data structure. This allows the
     * the data structure to be modified while the Map is being built.
     * The Map may contain more entries than indicated based on the
     * time stamp, but this is handled by ignoring duplicate entries
     * when the Map is updated in getNewMonitors().
     */
    lastUsed = used;
    lastModificationTime = modificationTime;

    // synchronize with the target jvm
    synchWithTarget(map);

    // work around 1.4.2 counter inititization bugs
    kludge(map);

    insertedMonitors = new ArrayList<Monitor>(map.values());
  }
  private <T> T doIndexTask(IndexTask<T> task, T defaultValue) {
    assert Thread.holdsLock(this);

    if (!isBroken) {
      try {
        return task.doTask();
      } catch (Exception e1) {
        MavenLog.LOG.warn(e1);

        cleanupBrokenData();
        try {
          open();
        } catch (MavenIndexException e2) {
          MavenLog.LOG.warn(e2);
        }
      }
    }
    markAsBroken();
    return defaultValue;
  }
  private void removeLineDef(@Nonnull final PlotLineDef toBeRemoved) {
    if (!Thread.holdsLock(this)) throw new AssertionError();

    List<PlotLineDef> registeredLineDefs = registeredLineDefsMap.get(toBeRemoved);

    if (registeredLineDefs != null) {
      Iterables.removeIf(
          registeredLineDefs,
          new Predicate<PlotLineDef>() {
            @Override
            public boolean apply(@Nullable PlotLineDef lineDef) {
              return lineDef == toBeRemoved;
            }
          });

      if (registeredLineDefs.isEmpty()) {
        registeredLineDefsMap.remove(toBeRemoved);
      }

    } else {
      registeredLineDefsMap.remove(toBeRemoved);
    }
  }
    /** {@inheritDoc} */
    @Override
    public void onDeployed(Class<?> cls) {
      assert !Thread.holdsLock(mux);

      boolean isTask = isTask(cls);

      String msg =
          (isTask ? "Task" : "Class") + " was deployed in SHARED or CONTINUOUS mode: " + cls;

      int type = isTask ? EVT_TASK_DEPLOYED : EVT_CLASS_DEPLOYED;

      if (ctx.event().isRecordable(type)) {
        GridDeploymentEvent evt = new GridDeploymentEvent();

        evt.nodeId(ctx.localNodeId());
        evt.message(msg);
        evt.type(type);
        evt.alias(cls.getName());

        ctx.event().record(evt);
      }

      if (log.isInfoEnabled()) log.info(msg);
    }
    /**
     * Called to record all undeployed classes..
     *
     * @param leftNodeId Left node ID.
     */
    void recordUndeployed(@Nullable UUID leftNodeId) {
      assert !Thread.holdsLock(mux);

      for (Map.Entry<String, Class<?>> depCls : deployedClassMap().entrySet()) {
        boolean isTask = isTask(depCls.getValue());

        String msg =
            (isTask ? "Task" : "Class")
                + " was undeployed in SHARED or CONTINUOUS mode: "
                + depCls.getValue();

        int type = isTask ? EVT_TASK_UNDEPLOYED : EVT_CLASS_UNDEPLOYED;

        if (ctx.event().isRecordable(type)) {
          GridDeploymentEvent evt = new GridDeploymentEvent();

          evt.nodeId(ctx.localNodeId());
          evt.message(msg);
          evt.type(type);
          evt.alias(depCls.getKey());

          ctx.event().record(evt);
        }

        if (log.isInfoEnabled()) log.info(msg);
      }

      if (isObsolete()) {
        // Resource cleanup.
        ctx.resource().onUndeployed(this);

        ctx.cache().onUndeployed(leftNodeId, loader());

        clearSerializationCaches();
      }
    }
  @Override
  public void startRunInjectors(@NotNull final Document hostDocument, final boolean synchronously) {
    if (myProject.isDisposed()) return;
    if (!synchronously && ApplicationManager.getApplication().isWriteAccessAllowed()) return;
    // use cached to avoid recreate PSI in alien project
    final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject);
    final PsiFile hostPsiFile = documentManager.getCachedPsiFile(hostDocument);
    if (hostPsiFile == null) return;

    final ConcurrentList<DocumentWindow> injected =
        InjectedLanguageUtil.getCachedInjectedDocuments(hostPsiFile);
    if (injected.isEmpty()) return;

    if (myProgress.isCanceled()) {
      myProgress = new DaemonProgressIndicator();
    }
    final Set<DocumentWindow> newDocuments = Collections.synchronizedSet(new THashSet<>());
    final Processor<DocumentWindow> commitProcessor =
        documentWindow -> {
          if (myProject.isDisposed()) return false;
          ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
          if (indicator != null && indicator.isCanceled()) return false;
          if (documentManager.isUncommited(hostDocument) || !hostPsiFile.isValid())
            return false; // will be committed later

          // it is here where the reparse happens and old file contents replaced
          InjectedLanguageUtil.enumerate(
              documentWindow,
              hostPsiFile,
              (injectedPsi, places) -> {
                DocumentWindow newDocument =
                    (DocumentWindow) injectedPsi.getViewProvider().getDocument();
                if (newDocument != null) {
                  PsiDocumentManagerBase.checkConsistency(injectedPsi, newDocument);
                  newDocuments.add(newDocument);
                }
              });
          return true;
        };
    final Runnable commitInjectionsRunnable =
        () -> {
          if (myProgress.isCanceled()) return;
          JobLauncher.getInstance()
              .invokeConcurrentlyUnderProgress(
                  new ArrayList<>(injected), myProgress, true, commitProcessor);

          synchronized (ourInjectionPsiLock) {
            injected.clear();
            injected.addAll(newDocuments);
          }
        };

    if (synchronously) {
      if (Thread.holdsLock(PsiLock.LOCK)) {
        // hack for the case when docCommit was called from within PSI modification, e.g. in
        // formatter.
        // we can't spawn threads to do injections there, otherwise a deadlock is imminent
        ContainerUtil.process(new ArrayList<>(injected), commitProcessor);
      } else {
        commitInjectionsRunnable.run();
      }
    } else {
      JobLauncher.getInstance()
          .submitToJobThread(
              () ->
                  ApplicationManagerEx.getApplicationEx()
                      .tryRunReadAction(commitInjectionsRunnable),
              null);
    }
  }
    /** @return Set of participating nodes. */
    Collection<UUID> getParticipantNodeIds() {
      assert Thread.holdsLock(mux);

      return loader().registeredNodeIds();
    }
  public void startRunInjectors(@NotNull final Document hostDocument, final boolean synchronously) {
    if (myProject.isDisposed()) return;
    if (!synchronously && ApplicationManager.getApplication().isWriteAccessAllowed()) return;
    // use cached to avoid recreate PSI in alien project
    final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject);
    final PsiFile hostPsiFile = documentManager.getCachedPsiFile(hostDocument);
    if (hostPsiFile == null) return;

    final CopyOnWriteArrayList<DocumentWindow> injected =
        (CopyOnWriteArrayList<DocumentWindow>)
            InjectedLanguageUtil.getCachedInjectedDocuments(hostPsiFile);
    if (injected.isEmpty()) return;

    if (myProgress.isCanceled()) {
      myProgress = new DaemonProgressIndicator();
    }

    final Processor<DocumentWindow> commitProcessor =
        new Processor<DocumentWindow>() {
          @Override
          public boolean process(DocumentWindow documentWindow) {
            if (myProject.isDisposed()) return false;
            ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
            if (indicator != null && indicator.isCanceled()) return false;
            if (documentManager.isUncommited(hostDocument) || !hostPsiFile.isValid())
              return false; // will be committed later

            Segment[] ranges = documentWindow.getHostRanges();
            Segment rangeMarker = ranges.length > 0 ? ranges[0] : null;
            PsiElement element =
                rangeMarker == null
                    ? null
                    : hostPsiFile.findElementAt(rangeMarker.getStartOffset());
            if (element == null) {
              synchronized (PsiLock.LOCK) {
                injected.remove(documentWindow);
              }
              return true;
            }
            final DocumentWindow[] stillInjectedDocument = {null};
            // it is here where the reparse happens and old file contents replaced
            InjectedLanguageUtil.enumerate(
                element,
                hostPsiFile,
                true,
                new PsiLanguageInjectionHost.InjectedPsiVisitor() {
                  @Override
                  public void visit(
                      @NotNull PsiFile injectedPsi,
                      @NotNull List<PsiLanguageInjectionHost.Shred> places) {
                    stillInjectedDocument[0] =
                        (DocumentWindow) injectedPsi.getViewProvider().getDocument();
                    PsiDocumentManagerImpl.checkConsistency(injectedPsi, stillInjectedDocument[0]);
                  }
                });
            synchronized (PsiLock.LOCK) {
              if (stillInjectedDocument[0] == null) {
                injected.remove(documentWindow);
              } else if (stillInjectedDocument[0] != documentWindow) {
                injected.remove(documentWindow);
                injected.addIfAbsent(stillInjectedDocument[0]);
              }
            }

            return true;
          }
        };
    final Runnable commitInjectionsRunnable =
        new Runnable() {
          @Override
          public void run() {
            if (myProgress.isCanceled()) return;
            JobLauncher.getInstance()
                .invokeConcurrentlyUnderProgress(
                    new ArrayList<DocumentWindow>(injected),
                    myProgress,
                    !synchronously,
                    commitProcessor);
          }
        };

    if (synchronously) {
      if (Thread.holdsLock(PsiLock.LOCK)) {
        // hack for the case when docCommit was called from within PSI modification, e.g. in
        // formatter.
        // we can't spawn threads to do injections there, otherwise a deadlock is imminent
        ContainerUtil.process(new ArrayList<DocumentWindow>(injected), commitProcessor);
      } else {
        commitInjectionsRunnable.run();
      }
    } else {
      JobLauncher.getInstance()
          .submitToJobThread(
              Job.DEFAULT_PRIORITY,
              new Runnable() {
                @Override
                public void run() {
                  ApplicationManagerEx.getApplicationEx()
                      .tryRunReadAction(commitInjectionsRunnable);
                }
              });
    }
  }
    /** @return Registered class loader IDs. */
    Collection<GridUuid> getClassLoaderIds() {
      assert Thread.holdsLock(mux);

      return loader().registeredClassLoaderIds();
    }
    /** @return {@code True} if deployment has any node participants. */
    boolean hasParticipants() {
      assert Thread.holdsLock(mux);

      return loader().hasRegisteredNodes();
    }
    /**
     * Gets property removed.
     *
     * @return Property removed.
     */
    boolean isRemoved() {
      assert Thread.holdsLock(mux);

      return removed;
    }
  /**
   * @param depMode Deployment mode.
   * @param ldr Class loader to deploy.
   * @param cls Class.
   * @param alias Class alias.
   * @return Deployment.
   */
  @SuppressWarnings({"ConstantConditions"})
  private GridDeployment deploy(
      GridDeploymentMode depMode, ClassLoader ldr, Class<?> cls, String alias) {
    assert Thread.holdsLock(mux);

    LinkedList<GridDeployment> cachedDeps = null;

    GridDeployment dep = null;

    // Find existing class loader info.
    for (LinkedList<GridDeployment> deps : cache.values()) {
      for (GridDeployment d : deps) {
        if (d.classLoader() == ldr) {
          // Cache class and alias.
          d.addDeployedClass(cls, alias);

          cachedDeps = deps;

          dep = d;

          break;
        }
      }

      if (cachedDeps != null) {
        break;
      }
    }

    if (cachedDeps != null) {
      assert dep != null;

      cache.put(alias, cachedDeps);

      if (!cls.getName().equals(alias)) {
        // Cache by class name as well.
        cache.put(cls.getName(), cachedDeps);
      }

      return dep;
    }

    GridUuid ldrId = GridUuid.randomUuid();

    long seqNum = seq.incrementAndGet();

    String userVer = getUserVersion(ldr);

    dep = new GridDeployment(depMode, ldr, ldrId, seqNum, userVer, cls.getName(), true);

    dep.addDeployedClass(cls, alias);

    LinkedList<GridDeployment> deps =
        F.addIfAbsent(cache, alias, F.<GridDeployment>newLinkedList());

    if (!deps.isEmpty()) {
      for (GridDeployment d : deps) {
        if (!d.isUndeployed()) {
          U.error(
              log,
              "Found more than one active deployment for the same resource "
                  + "[cls="
                  + cls
                  + ", depMode="
                  + depMode
                  + ", dep="
                  + d
                  + ']');

          return null;
        }
      }
    }

    // Add at the beginning of the list for future fast access.
    deps.addFirst(dep);

    if (!cls.getName().equals(alias)) {
      // Cache by class name as well.
      cache.put(cls.getName(), deps);
    }

    if (log.isDebugEnabled()) {
      log.debug("Created new deployment: " + dep);
    }

    return dep;
  }
  /**
   * Creates and caches new deployment.
   *
   * @param meta Deployment metadata.
   * @param isCache Whether or not to cache.
   * @return New deployment.
   */
  private SharedDeployment createNewDeployment(GridDeploymentMetadata meta, boolean isCache) {
    assert Thread.holdsLock(mux);

    assert meta.parentLoader() == null;

    GridUuid ldrId = GridUuid.randomUuid();

    GridDeploymentClassLoader clsLdr;

    if (meta.deploymentMode() == CONTINUOUS || meta.participants() == null) {
      // Create peer class loader.
      // Note that we are passing empty list for local P2P exclude, as it really
      // does not make sense with shared deployment.
      clsLdr =
          new GridDeploymentClassLoader(
              ldrId,
              meta.userVersion(),
              meta.deploymentMode(),
              false,
              ctx,
              ctxLdr,
              meta.classLoaderId(),
              meta.senderNodeId(),
              meta.sequenceNumber(),
              comm,
              ctx.config().getNetworkTimeout(),
              log,
              ctx.config().getPeerClassLoadingClassPathExclude(),
              ctx.config().getPeerClassLoadingMissedResourcesCacheSize(),
              meta.deploymentMode() == CONTINUOUS /* enable class byte cache in CONTINUOUS mode */);

      if (meta.participants() != null)
        for (Map.Entry<UUID, GridTuple2<GridUuid, Long>> e : meta.participants().entrySet())
          clsLdr.register(e.getKey(), e.getValue().get1(), e.getValue().get2());

      if (log.isDebugEnabled())
        log.debug(
            "Created class loader in CONTINUOUS mode or without participants "
                + "[ldr="
                + clsLdr
                + ", meta="
                + meta
                + ']');
    } else {
      assert meta.deploymentMode() == SHARED;

      // Create peer class loader.
      // Note that we are passing empty list for local P2P exclude, as it really
      // does not make sense with shared deployment.
      clsLdr =
          new GridDeploymentClassLoader(
              ldrId,
              meta.userVersion(),
              meta.deploymentMode(),
              false,
              ctx,
              ctxLdr,
              meta.participants(),
              comm,
              ctx.config().getNetworkTimeout(),
              log,
              ctx.config().getPeerClassLoadingClassPathExclude(),
              ctx.config().getPeerClassLoadingMissedResourcesCacheSize(),
              false);

      if (log.isDebugEnabled())
        log.debug(
            "Created classloader in SHARED mode with participants "
                + "[ldr="
                + clsLdr
                + ", meta="
                + meta
                + ']');
    }

    // Give this deployment a unique class loader to emphasize that this
    // ID is unique to this shared deployment and is not ID of loader on
    // sender node.
    SharedDeployment dep =
        new SharedDeployment(
            meta.deploymentMode(), clsLdr, ldrId, -1, meta.userVersion(), meta.alias());

    if (log.isDebugEnabled()) log.debug("Created new deployment: " + dep);

    if (isCache) {
      List<SharedDeployment> deps =
          F.addIfAbsent(cache, meta.userVersion(), new LinkedList<SharedDeployment>());

      assert deps != null;

      deps.add(dep);

      if (log.isDebugEnabled()) log.debug("Added deployment to cache: " + cache);
    }

    return dep;
  }
  /**
   * Removes obsolete deployments in case of redeploy.
   *
   * @param meta Request metadata.
   * @return List of shares deployment.
   */
  private GridTuple2<Boolean, SharedDeployment> checkRedeploy(GridDeploymentMetadata meta) {
    assert Thread.holdsLock(mux);

    SharedDeployment newDep = null;

    for (List<SharedDeployment> deps : cache.values()) {
      for (SharedDeployment dep : deps) {
        if (!dep.isUndeployed() && !dep.isPendingUndeploy()) {
          long undeployTimeout = ctx.config().getNetworkTimeout();

          SharedDeployment doomed = null;

          // Only check deployments with no participants.
          if (!dep.hasParticipants()) {
            // In case of SHARED deployment it is possible to get hear if
            // unmarshalling happens during undeploy. In this case, we
            // simply don't do anything.
            if (dep.deployMode() == CONTINUOUS) {
              if (dep.existingDeployedClass(meta.className()) != null) {
                // Change from shared deploy to shared undeploy or user version change.
                // Simply remove all deployments with no participating nodes.
                if (meta.deploymentMode() == SHARED
                    || !meta.userVersion().equals(dep.userVersion())) doomed = dep;
              }
            }
          }
          // If there are participants, we undeploy if class loader ID on some node changed.
          else if (dep.existingDeployedClass(meta.className()) != null) {
            GridTuple2<GridUuid, Long> ldr = dep.getClassLoaderId(meta.senderNodeId());

            if (ldr != null) {
              if (!ldr.get1().equals(meta.classLoaderId())) {
                // If deployed sequence number is less, then schedule for undeployment.
                if (ldr.get2() < meta.sequenceNumber()) {
                  if (log.isDebugEnabled())
                    log.debug(
                        "Received request for a class with newer sequence number "
                            + "(will schedule current class for undeployment) [newSeq="
                            + meta.sequenceNumber()
                            + ", oldSeq="
                            + ldr.get2()
                            + ", senderNodeId="
                            + meta.senderNodeId()
                            + ", newClsLdrId="
                            + meta.classLoaderId()
                            + ", oldClsLdrId="
                            + ldr.get1()
                            + ']');

                  doomed = dep;
                } else if (ldr.get2() > meta.sequenceNumber()) {
                  long time = System.currentTimeMillis() - dep.timestamp();

                  if (newDep == null && time < ctx.config().getNetworkTimeout()) {
                    // Set undeployTimeout, so the class will be scheduled
                    // for undeployment.
                    undeployTimeout = ctx.config().getNetworkTimeout() - time;

                    if (log.isDebugEnabled())
                      log.debug(
                          "Received execution request for a stale class (will deploy and "
                              + "schedule undeployment in "
                              + undeployTimeout
                              + "ms) "
                              + "[curSeq="
                              + ldr.get2()
                              + ", staleSeq="
                              + meta.sequenceNumber()
                              + ", cls="
                              + meta.className()
                              + ", senderNodeId="
                              + meta.senderNodeId()
                              + ", curLdrId="
                              + ldr.get1()
                              + ", staleLdrId="
                              + meta.classLoaderId()
                              + ']');

                    // We got the redeployed class before the old one.
                    // Simply create a temporary deployment for the sender node,
                    // and schedule undeploy for it.
                    newDep = createNewDeployment(meta, false);

                    doomed = newDep;
                  } else {
                    U.warn(
                        log,
                        "Received execution request for a class that has been redeployed "
                            + "(will ignore): "
                            + meta.alias());

                    if (log.isDebugEnabled())
                      log.debug(
                          "Received execution request for a class that has been redeployed "
                              + "(will ignore) [alias="
                              + meta.alias()
                              + ", dep="
                              + dep
                              + ']');

                    return F.t(false, null);
                  }
                } else {
                  U.error(
                      log,
                      "Sequence number does not correspond to class loader ID [seqNum="
                          + meta.sequenceNumber()
                          + ", dep="
                          + dep
                          + ']');

                  return F.t(false, null);
                }
              }
            }
          }

          if (doomed != null) {
            doomed.onUndeployScheduled();

            if (log.isDebugEnabled()) log.debug("Deployment was scheduled for undeploy: " + doomed);

            // Lifespan time.
            final long endTime = System.currentTimeMillis() + undeployTimeout;

            // Deployment to undeploy.
            final SharedDeployment undep = doomed;

            ctx.timeout()
                .addTimeoutObject(
                    new GridTimeoutObject() {
                      @Override
                      public GridUuid timeoutId() {
                        return undep.classLoaderId();
                      }

                      @Override
                      public long endTime() {
                        return endTime < 0 ? Long.MAX_VALUE : endTime;
                      }

                      @Override
                      public void onTimeout() {
                        boolean removed = false;

                        // Hot redeployment.
                        synchronized (mux) {
                          assert undep.isPendingUndeploy();

                          if (!undep.isUndeployed()) {
                            undep.undeploy();

                            undep.onRemoved();

                            removed = true;

                            Collection<SharedDeployment> deps = cache.get(undep.userVersion());

                            if (deps != null) {
                              for (Iterator<SharedDeployment> i = deps.iterator(); i.hasNext(); )
                                if (i.next() == undep) i.remove();

                              if (deps.isEmpty()) cache.remove(undep.userVersion());
                            }

                            if (log.isInfoEnabled())
                              log.info(
                                  "Undeployed class loader due to deployment mode change, "
                                      + "user version change, or hot redeployment: "
                                      + undep);
                          }
                        }

                        // Outside synchronization.
                        if (removed) undep.recordUndeployed(null);
                      }
                    });
          }
        }
      }
    }

    if (newDep != null) {
      List<SharedDeployment> list =
          F.addIfAbsent(cache, meta.userVersion(), F.<SharedDeployment>newList());

      assert list != null;

      list.add(newDep);
    }

    return F.t(true, newDep);
  }