/** {@inheritDoc} */
  @Override
  public final Map<UUID, GridNodeMetrics> metrics(Collection<UUID> nodeIds)
      throws GridSpiException {
    assert !F.isEmpty(nodeIds);

    long now = U.currentTimeMillis();

    Collection<UUID> expired = new LinkedList<>();

    for (UUID id : nodeIds) {
      GridNodeMetrics nodeMetrics = metricsMap.get(id);

      Long ts = tsMap.get(id);

      if (nodeMetrics == null || ts == null || ts < now - metricsExpireTime) expired.add(id);
    }

    if (!expired.isEmpty()) {
      Map<UUID, GridNodeMetrics> refreshed = metrics0(expired);

      for (UUID id : refreshed.keySet()) tsMap.put(id, now);

      metricsMap.putAll(refreshed);
    }

    return F.view(metricsMap, F.contains(nodeIds));
  }
  /** @throws Exception If failed. */
  public void testAffinityPut() throws Exception {
    Thread.sleep(2 * TOP_REFRESH_FREQ);

    assertEquals(NODES_CNT, client.compute().refreshTopology(false, false).size());

    Map<UUID, Grid> gridsByLocNode = new HashMap<>(NODES_CNT);

    GridClientData partitioned = client.data(PARTITIONED_CACHE_NAME);

    GridClientCompute compute = client.compute();

    for (int i = 0; i < NODES_CNT; i++) gridsByLocNode.put(grid(i).localNode().id(), grid(i));

    for (int i = 0; i < 100; i++) {
      String key = "key" + i;

      UUID primaryNodeId = grid(0).mapKeyToNode(PARTITIONED_CACHE_NAME, key).id();

      assertEquals("Affinity mismatch for key: " + key, primaryNodeId, partitioned.affinity(key));

      assertEquals(primaryNodeId, partitioned.affinity(key));

      // Must go to primary node only. Since backup count is 0, value must present on
      // primary node only.
      partitioned.put(key, "val" + key);

      for (Map.Entry<UUID, Grid> entry : gridsByLocNode.entrySet()) {
        Object val = entry.getValue().cache(PARTITIONED_CACHE_NAME).peek(key);

        if (primaryNodeId.equals(entry.getKey())) assertEquals("val" + key, val);
        else assertNull(val);
      }
    }

    // Now check that we will see value in near cache in pinned mode.
    for (int i = 100; i < 200; i++) {
      String pinnedKey = "key" + i;

      UUID primaryNodeId = grid(0).mapKeyToNode(PARTITIONED_CACHE_NAME, pinnedKey).id();

      UUID pinnedNodeId = F.first(F.view(gridsByLocNode.keySet(), F.notEqualTo(primaryNodeId)));

      GridClientNode node = compute.node(pinnedNodeId);

      partitioned.pinNodes(node).put(pinnedKey, "val" + pinnedKey);

      for (Map.Entry<UUID, Grid> entry : gridsByLocNode.entrySet()) {
        Object val = entry.getValue().cache(PARTITIONED_CACHE_NAME).peek(pinnedKey);

        if (primaryNodeId.equals(entry.getKey()) || pinnedNodeId.equals(entry.getKey()))
          assertEquals("val" + pinnedKey, val);
        else assertNull(val);
      }
    }
  }
  /** @throws Exception If failed. */
  public void testTopologyListener() throws Exception {
    final Collection<UUID> added = new ArrayList<>(1);
    final Collection<UUID> rmvd = new ArrayList<>(1);

    final CountDownLatch addedLatch = new CountDownLatch(1);
    final CountDownLatch rmvLatch = new CountDownLatch(1);

    assertEquals(NODES_CNT, client.compute().refreshTopology(false, false).size());

    GridClientTopologyListener lsnr =
        new GridClientTopologyListener() {
          @Override
          public void onNodeAdded(GridClientNode node) {
            added.add(node.nodeId());

            addedLatch.countDown();
          }

          @Override
          public void onNodeRemoved(GridClientNode node) {
            rmvd.add(node.nodeId());

            rmvLatch.countDown();
          }
        };

    client.addTopologyListener(lsnr);

    try {
      Grid g = startGrid(NODES_CNT + 1);

      UUID id = g.localNode().id();

      assertTrue(addedLatch.await(2 * TOP_REFRESH_FREQ, MILLISECONDS));

      assertEquals(1, added.size());
      assertEquals(id, F.first(added));

      stopGrid(NODES_CNT + 1);

      assertTrue(rmvLatch.await(2 * TOP_REFRESH_FREQ, MILLISECONDS));

      assertEquals(1, rmvd.size());
      assertEquals(id, F.first(rmvd));
    } finally {
      client.removeTopologyListener(lsnr);

      stopGrid(NODES_CNT + 1);
    }
  }
  /** {@inheritDoc} */
  @Override
  public <T extends GridEvent> Collection<T> localEvents(GridPredicate<T> p) {
    A.notNull(p, "p");

    cleanupQueue();

    return F.retain((Collection<T>) evts, true, p);
  }
  /** {@inheritDoc} */
  @Override
  public final void removeMetrics(Collection<UUID> nodeIds) throws GridSpiException {
    assert !F.isEmpty(nodeIds);

    for (UUID id : nodeIds) {
      metricsMap.remove(id);

      tsMap.remove(id);
    }

    removeMetrics0(nodeIds);
  }
  /**
   * Checks {@link GridSystemProperties#GG_JETTY_PORT} system property and overrides default
   * connector port if it present. Then initializes {@code port} with the found value.
   *
   * @param con Jetty connector.
   */
  private void override(AbstractNetworkConnector con) {
    String host = System.getProperty(GG_JETTY_HOST);

    if (!F.isEmpty(host)) con.setHost(host);

    int currPort = con.getPort();

    Integer overridePort = Integer.getInteger(GG_JETTY_PORT);

    if (overridePort != null && overridePort != 0) currPort = overridePort;

    con.setPort(currPort);
    port = currPort;
  }
  /** {@inheritDoc} */
  @Override
  public void prepareMarshal(GridCacheContext<K, V> ctx) throws GridException {
    super.prepareMarshal(ctx);

    if (err != null) errBytes = ctx.marshaller().marshal(err);

    metaDataBytes = marshalCollection(metadata, ctx);
    dataBytes = fields ? marshalFieldsCollection(data, ctx) : marshalCollection(data, ctx);

    if (ctx.deploymentEnabled() && !F.isEmpty(data)) {
      for (Object o : data) {
        if (o instanceof Map.Entry) {
          Map.Entry e = (Map.Entry) o;

          prepareObject(e.getKey(), ctx);
          prepareObject(e.getValue(), ctx);
        }
      }
    }
  }
  /** {@inheritDoc} */
  @Override
  public void onCollision(
      Collection<GridCollisionJobContext> waitJobs,
      Collection<GridCollisionJobContext> activeJobs) {
    assert waitJobs != null;
    assert activeJobs != null;

    int activeSize = F.size(activeJobs, RUNNING_JOBS);

    waitingCnt.set(waitJobs.size());
    runningCnt.set(activeSize);
    heldCnt.set(activeJobs.size() - activeSize);

    int waitSize = waitJobs.size();

    int activateCnt = parallelJobsNum - activeSize;

    if (activateCnt > 0 && !waitJobs.isEmpty()) {
      if (waitJobs.size() <= activateCnt) {
        for (GridCollisionJobContext waitJob : waitJobs) {
          waitJob.activate();

          waitSize--;
        }
      } else {
        List<GridCollisionJobContext> passiveList =
            new ArrayList<GridCollisionJobContext>(waitJobs);

        Collections.sort(
            passiveList,
            new Comparator<GridCollisionJobContext>() {
              /** {@inheritDoc} */
              @Override
              public int compare(GridCollisionJobContext o1, GridCollisionJobContext o2) {
                int p1 = getJobPriority(o1);
                int p2 = getJobPriority(o2);

                return p1 < p2 ? 1 : p1 == p2 ? 0 : -1;
              }
            });

        if (preventStarvation) bumpPriority(waitJobs, passiveList);

        for (int i = 0; i < activateCnt; i++) {
          passiveList.get(i).activate();

          waitSize--;
        }
      }
    }

    if (waitSize > waitJobsNum) {
      List<GridCollisionJobContext> waitList = new ArrayList<GridCollisionJobContext>(waitJobs);

      // Put jobs with highest priority first.
      Collections.sort(
          waitList,
          new Comparator<GridCollisionJobContext>() {
            /** {@inheritDoc} */
            @Override
            public int compare(GridCollisionJobContext o1, GridCollisionJobContext o2) {
              int p1 = getJobPriority(o1);
              int p2 = getJobPriority(o2);

              return p1 < p2 ? 1 : p1 == p2 ? 0 : -1;
            }
          });

      int skip = waitJobs.size() - waitSize;

      int i = 0;

      for (GridCollisionJobContext waitCtx : waitList) {
        if (++i >= skip) {
          waitCtx.cancel();

          if (--waitSize <= waitJobsNum) break;
        }
      }
    }
  }
 /** {@inheritDoc} */
 @Override
 public Map<String, Object> getNodeAttributes() throws GridSpiException {
   return F.<String, Object>asMap(
       createSpiAttributeName(PRIORITY_ATTRIBUTE_KEY), getPriorityAttributeKey());
 }
  /**
   * @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;
  }