/**
   * Change topology.
   *
   * @param parent Grid to execute tasks on.
   * @param add New nodes count.
   * @param rmv Remove nodes count.
   * @param type Type of nodes to manipulate.
   */
  private static void changeTopology(Ignite parent, int add, int rmv, String type) {
    Collection<ComputeTaskFuture<?>> tasks = new ArrayList<>();

    IgniteCompute comp = parent.compute().withAsync();

    // Start nodes in parallel.
    while (add-- > 0) {
      comp.execute(ClientStartNodeTask.class, type);

      tasks.add(comp.future());
    }

    for (ComputeTaskFuture<?> task : tasks) task.get();

    // Stop nodes in sequence.
    while (rmv-- > 0) parent.compute().execute(ClientStopNodeTask.class, type);

    // Wait for node stops.
    // U.sleep(1000);

    Collection<String> gridNames = new ArrayList<>();

    for (Ignite g : G.allGrids()) gridNames.add(g.name());

    parent.log().info(">>> Available grids: " + gridNames);
  }
  /**
   * Listen to events that happen only on local node.
   *
   * @throws IgniteException If failed.
   */
  private static void localListen() throws IgniteException {
    System.out.println();
    System.out.println(">>> Local event listener example.");

    Ignite ignite = Ignition.ignite();

    IgnitePredicate<TaskEvent> lsnr =
        evt -> {
          System.out.println(
              "Received task event [evt=" + evt.name() + ", taskName=" + evt.taskName() + ']');

          return true; // Return true to continue listening.
        };

    // Register event listener for all local task execution events.
    ignite.events().localListen(lsnr, EVTS_TASK_EXECUTION);

    // Generate task events.
    ignite
        .compute()
        .withName("example-event-task")
        .run(() -> System.out.println("Executing sample job."));

    // Unsubscribe local task event listener.
    ignite.events().stopLocalListen(lsnr);
  }
  /** @throws Exception If failed. */
  @SuppressWarnings({"AssignmentToCatchBlockParameter"})
  public void testCancel() throws Exception {
    Ignite ignite = G.ignite(getTestGridName());

    ignite
        .compute()
        .localDeployTask(GridCancelTestTask.class, GridCancelTestTask.class.getClassLoader());

    ComputeTaskFuture<?> res0 =
        executeAsync(
            ignite.compute().withTimeout(maxJobExecTime * 2),
            GridCancelTestTask.class.getName(),
            null);

    try {
      Object res = res0.get();

      info("Cancel test result: " + res);

      synchronized (mux) {
        // Every execute must be called.
        assert execCnt <= SPLIT_COUNT : "Invalid execute count: " + execCnt;

        // Job returns 1 if was cancelled.
        assert (Integer) res <= SPLIT_COUNT : "Invalid task result: " + res;

        // Should be exactly the same as Jobs number.
        assert cancelCnt <= SPLIT_COUNT : "Invalid cancel count: " + cancelCnt;

        // One per start and one per stop and some that come with heartbeats.
        assert colResolutionCnt > SPLIT_COUNT + 1
            : "Invalid collision resolution count: " + colResolutionCnt;
      }
    } catch (ComputeTaskTimeoutException e) {
      error("Task execution got timed out.", e);
    } catch (Exception e) {
      assert e.getCause() != null;

      if (e.getCause() instanceof IgniteCheckedException) e = (Exception) e.getCause();

      if (e.getCause() instanceof IOException) e = (Exception) e.getCause();

      assert e.getCause() instanceof InterruptedException
          : "Invalid exception cause: " + e.getCause();
    }
  }
  /**
   * Listen to events coming from all cluster nodes.
   *
   * @throws IgniteException If failed.
   */
  private static void remoteListen() throws IgniteException {
    System.out.println();
    System.out.println(">>> Remote event listener example.");

    // This optional local callback is called for each event notification
    // that passed remote predicate listener.
    IgniteBiPredicate<UUID, TaskEvent> locLsnr =
        (nodeId, evt) -> {
          // Remote filter only accepts tasks whose name being with "good-task" prefix.
          assert evt.taskName().startsWith("good-task");

          System.out.println(
              "Received task event [evt=" + evt.name() + ", taskName=" + evt.taskName());

          return true; // Return true to continue listening.
        };

    // Remote filter which only accepts tasks whose name begins with "good-task" prefix.
    IgnitePredicate<TaskEvent> rmtLsnr = evt -> evt.taskName().startsWith("good-task");

    Ignite ignite = Ignition.ignite();

    // Register event listeners on all nodes to listen for task events.
    ignite.events().remoteListen(locLsnr, rmtLsnr, EVTS_TASK_EXECUTION);

    // Generate task events.
    for (int i = 0; i < 10; i++) {
      ignite
          .compute()
          .withName(i < 5 ? "good-task-" + i : "bad-task-" + i)
          .run(
              new IgniteRunnable() {
                // Auto-inject task session.
                @TaskSessionResource private ComputeTaskSession ses;

                @Override
                public void run() {
                  System.out.println("Executing sample job for task: " + ses.getTaskName());
                }
              });
    }
  }
  /** {@inheritDoc} */
  @Override
  protected Object executeJob(int gridSize, String type) {
    log.info(">>> Starting new grid node [currGridSize=" + gridSize + ", arg=" + type + "]");

    if (type == null) throw new IllegalArgumentException("Node type to start should be specified.");

    IgniteConfiguration cfg = getConfig(type);

    // Generate unique for this VM grid name.
    String gridName = cfg.getGridName() + " (" + UUID.randomUUID() + ")";

    // Update grid name (required to be unique).
    cfg.setGridName(gridName);

    // Start new node in current VM.
    Ignite g = G.start(cfg);

    log.info(
        ">>> Grid started [nodeId=" + g.cluster().localNode().id() + ", name='" + g.name() + "']");

    return true;
  }
  /**
   * Example for start/stop node tasks.
   *
   * @param args Not used.
   */
  public static void main(String[] args) {
    String nodeType = "tcp+ssl";

    // Start initial node = 1
    try (Ignite g = G.start(NODE_CFG.get(nodeType))) {
      // Change topology.
      changeTopology(g, 4, 1, nodeType);
      changeTopology(g, 1, 4, nodeType);

      // Stop node by id = 0
      g.compute().execute(ClientStopNodeTask.class, g.cluster().localNode().id().toString());

      // Wait for node stops.
      // U.sleep(1000);

      assert G.allGrids().isEmpty();
    } catch (Exception e) {
      System.err.println("Uncaught exception: " + e.getMessage());

      e.printStackTrace(System.err);
    }
  }