/** {@inheritDoc} */
  @Override
  public void explicitUndeploy(UUID nodeId, String rsrcName) {
    Collection<SharedDeployment> undeployed = new LinkedList<SharedDeployment>();

    synchronized (mux) {
      for (Iterator<List<SharedDeployment>> i1 = cache.values().iterator(); i1.hasNext(); ) {
        List<SharedDeployment> deps = i1.next();

        for (Iterator<SharedDeployment> i2 = deps.iterator(); i2.hasNext(); ) {
          SharedDeployment dep = i2.next();

          if (dep.hasName(rsrcName)) {
            if (!dep.isUndeployed()) {
              dep.undeploy();

              dep.onRemoved();

              // Undeploy.
              i2.remove();

              undeployed.add(dep);

              if (log.isInfoEnabled()) log.info("Undeployed per-version class loader: " + dep);
            }

            break;
          }
        }

        if (deps.isEmpty()) i1.remove();
      }
    }

    recordUndeployed(null, undeployed);
  }
  /** {@inheritDoc} */
  @Override
  public Collection<GridDeployment> getDeployments() {
    Collection<GridDeployment> deps = new LinkedList<GridDeployment>();

    synchronized (mux) {
      for (List<SharedDeployment> list : cache.values())
        for (SharedDeployment d : list) deps.add(d);
    }

    return deps;
  }
  /** {@inheritDoc} */
  @Override
  public Collection<GridDeployment> getDeployments() {
    Collection<GridDeployment> deps = new ArrayList<GridDeployment>();

    synchronized (mux) {
      for (List<GridDeployment> depList : cache.values()) {
        for (GridDeployment d : depList) {
          if (!deps.contains(d)) {
            deps.add(d);
          }
        }
      }

      return deps;
    }
  }
  /** {@inheritDoc} */
  @Override
  public void stop() {
    Collection<SharedDeployment> copy = new HashSet<SharedDeployment>();

    synchronized (mux) {
      for (List<SharedDeployment> deps : cache.values())
        for (SharedDeployment dep : deps) {
          // Mark undeployed.
          dep.undeploy();

          copy.add(dep);
        }

      cache.clear();
    }

    for (SharedDeployment dep : copy) dep.recordUndeployed(null);

    if (log.isDebugEnabled()) log.debug(stopInfo());
  }
  /**
   * @param ldr Class loader to undeploy.
   * @param recEvt Whether or not to record the event.
   */
  private void undeploy(ClassLoader ldr, boolean recEvt) {
    Collection<GridDeployment> doomed = new HashSet<GridDeployment>();

    synchronized (mux) {
      for (Iterator<LinkedList<GridDeployment>> i1 = cache.values().iterator(); i1.hasNext(); ) {
        LinkedList<GridDeployment> deps = i1.next();

        for (Iterator<GridDeployment> i2 = deps.iterator(); i2.hasNext(); ) {
          GridDeployment dep = i2.next();

          if (dep.classLoader() == ldr) {
            dep.undeploy();

            i2.remove();

            doomed.add(dep);

            if (log.isInfoEnabled()) {
              log.info("Removed undeployed class: " + dep);
            }
          }
        }

        if (deps.isEmpty()) {
          i1.remove();
        }
      }
    }

    for (GridDeployment dep : doomed) {
      if (dep.isObsolete()) {
        // Resource cleanup.
        ctx.resource().onUndeployed(dep);
      }

      if (recEvt) {
        recordUndeploy(dep, 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;
    }
  }
  /** {@inheritDoc} */
  @Override
  public void onKernalStart() throws GridException {
    discoLsnr =
        new GridLocalEventListener() {
          @Override
          public void onEvent(GridEvent evt) {
            assert evt instanceof GridDiscoveryEvent;

            assert evt.type() == EVT_NODE_LEFT || evt.type() == EVT_NODE_FAILED;

            GridDiscoveryEvent discoEvt = (GridDiscoveryEvent) evt;

            Collection<SharedDeployment> undeployed = new LinkedList<SharedDeployment>();

            if (log.isDebugEnabled()) log.debug("Processing node departure event: " + evt);

            synchronized (mux) {
              for (Iterator<List<SharedDeployment>> i1 = cache.values().iterator();
                  i1.hasNext(); ) {
                List<SharedDeployment> deps = i1.next();

                for (Iterator<SharedDeployment> i2 = deps.iterator(); i2.hasNext(); ) {
                  SharedDeployment dep = i2.next();

                  dep.removeParticipant(discoEvt.eventNodeId());

                  if (!dep.hasParticipants()) {
                    if (dep.deployMode() == SHARED) {
                      if (!dep.isUndeployed()) {
                        dep.undeploy();

                        // Undeploy.
                        i2.remove();

                        assert !dep.isRemoved();

                        dep.onRemoved();

                        undeployed.add(dep);

                        if (log.isDebugEnabled())
                          log.debug(
                              "Undeployed class loader as there are no participating "
                                  + "nodes: "
                                  + dep);
                      }
                    } else if (log.isDebugEnabled())
                      log.debug("Preserving deployment without node participants: " + dep);
                  } else if (log.isDebugEnabled())
                    log.debug("Keeping deployment as it still has participants: " + dep);
                }

                if (deps.isEmpty()) i1.remove();
              }
            }

            recordUndeployed(discoEvt.eventNodeId(), undeployed);
          }
        };

    ctx.event().addLocalEventListener(discoLsnr, EVT_NODE_FAILED, EVT_NODE_LEFT);

    Collection<SharedDeployment> undeployed = new LinkedList<SharedDeployment>();

    synchronized (mux) {
      for (Iterator<List<SharedDeployment>> i1 = cache.values().iterator(); i1.hasNext(); ) {
        List<SharedDeployment> deps = i1.next();

        for (Iterator<SharedDeployment> i2 = deps.iterator(); i2.hasNext(); ) {
          SharedDeployment dep = i2.next();

          for (UUID nodeId : dep.getParticipantNodeIds())
            if (ctx.discovery().node(nodeId) == null) dep.removeParticipant(nodeId);

          if (!dep.hasParticipants()) {
            if (dep.deployMode() == SHARED) {
              if (!dep.isUndeployed()) {
                dep.undeploy();

                // Undeploy.
                i2.remove();

                dep.onRemoved();

                undeployed.add(dep);

                if (log.isDebugEnabled())
                  log.debug("Undeployed class loader as there are no participating nodes: " + dep);
              }
            } else if (log.isDebugEnabled())
              log.debug("Preserving deployment without node participants: " + dep);
          } else if (log.isDebugEnabled())
            log.debug("Keeping deployment as it still has participants: " + dep);
        }

        if (deps.isEmpty()) i1.remove();
      }
    }

    recordUndeployed(null, undeployed);

    if (log.isDebugEnabled()) log.debug("Registered deployment discovery listener: " + discoLsnr);
  }