/** Tests concurrent grid initialization */
  public void testConcurrentGridGetOrStartCon() throws Exception {
    final IgniteConfiguration cfg = getConfiguration(null);

    final AtomicReference<Ignite> ref = new AtomicReference<>();

    try {
      GridTestUtils.runMultiThreaded(
          new Runnable() {
            @Override
            public void run() {
              // must return same instance in each thread

              try {
                Ignite ignite = Ignition.getOrStart(cfg);

                boolean set = ref.compareAndSet(null, ignite);

                if (!set) assertEquals(ref.get(), ignite);
              } catch (IgniteException e) {
                throw new RuntimeException("Ignite error", e);
              }
            }
          },
          CONCURRENCY,
          "GridCreatorThread");
    } catch (Exception ignored) {
      fail("Exception is not expected");
    }

    G.stopAll(true);

    assertTrue(G.allGrids().isEmpty());
  }
  /** @throws Exception If failed. */
  public void testStartMultipleNonDefaultGrids() throws Exception {
    try {
      multithreaded(
          new Callable<Object>() {
            @Nullable
            @Override
            public Object call() throws Exception {
              try {
                IgniteConfiguration cfg = new IgniteConfiguration();

                cfg.setGridName("TEST_NAME");
                cfg.setConnectorConfiguration(null);

                G.start(cfg);
              } catch (Throwable t) {
                error("Caught exception while starting grid.", t);
              }

              info("Thread finished.");

              return null;
            }
          },
          5,
          "grid-starter");

      assert G.allGrids().size() == 1;

      assert G.ignite("TEST_NAME") != null;
    } finally {
      G.stopAll(true);
    }
  }
  /** @throws Exception If failed. */
  public void testStopCancel() throws Exception {
    IgniteConfiguration cfg = new IgniteConfiguration();

    cfg.setConnectorConfiguration(null);

    Ignite ignite = G.start(cfg);

    ignite.compute().execute(TestTask.class, null);

    G.stop(true);
  }
  /** @throws Exception If failed. */
  public void testLockSerialization() throws Exception {
    final IgniteLock lock = grid(0).reentrantLock("lock", true, true, true);

    info("Lock created: " + lock);

    lock.isFailoverSafe();
    lock.isFair();

    grid(ThreadLocalRandom.current().nextInt(G.allGrids().size()))
        .compute()
        .broadcast(
            new IgniteCallable<Object>() {
              @Nullable
              @Override
              public Object call() throws Exception {
                Thread.sleep(1000);

                lock.lock();

                try {
                  info("Inside lock: " + lock.getHoldCount());
                } finally {
                  lock.unlock();
                }

                return null;
              }
            });
  }
  /**
   * 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);
  }
    /**
     * Checks state of the bean.
     *
     * @param gridName Grid name.
     * @param exec Try to execute something on the grid.
     */
    void checkState(String gridName, boolean exec) {
      assert log != null;
      assert appCtx != null;

      assert F.eq(gridName, ignite.name());

      if (exec)
        // Execute any grid method.
        G.ignite(gridName).events().localQuery(F.<Event>alwaysTrue());
    }
  /**
   * @param evt Error event.
   * @param gridErr Grid error flag.
   * @throws Exception If failed.
   */
  private void checkOnStop(LifecycleEventType evt, boolean gridErr) throws Exception {
    bean = new LifeCycleExceptionBean(evt, gridErr);

    try {
      startGrid();

      assertEquals(IgniteState.STARTED, G.state(getTestGridName()));
    } catch (IgniteCheckedException ignore) {
      assertTrue(false);
    } finally {
      try {
        stopAllGrids();

        assertEquals(IgniteState.STOPPED, G.state(getTestGridName()));
      } catch (Exception ignore) {
        assertTrue(false);
      }
    }
  }
  /**
   * Start grid with IGFS.
   *
   * @param gridName Grid name.
   * @param igfsName IGFS name
   * @param mode IGFS mode.
   * @param secondaryFs Secondary file system (optional).
   * @param restCfg Rest configuration string (optional).
   * @return Started grid instance.
   * @throws Exception If failed.
   */
  protected Ignite startGridWithIgfs(
      String gridName,
      String igfsName,
      IgfsMode mode,
      @Nullable IgfsSecondaryFileSystem secondaryFs,
      @Nullable IgfsIpcEndpointConfiguration restCfg)
      throws Exception {
    FileSystemConfiguration igfsCfg = new FileSystemConfiguration();

    igfsCfg.setDataCacheName("dataCache");
    igfsCfg.setMetaCacheName("metaCache");
    igfsCfg.setName(igfsName);
    igfsCfg.setBlockSize(IGFS_BLOCK_SIZE);
    igfsCfg.setDefaultMode(mode);
    igfsCfg.setIpcEndpointConfiguration(restCfg);
    igfsCfg.setSecondaryFileSystem(secondaryFs);
    igfsCfg.setPrefetchBlocks(PREFETCH_BLOCKS);
    igfsCfg.setSequentialReadsBeforePrefetch(SEQ_READS_BEFORE_PREFETCH);

    CacheConfiguration dataCacheCfg = defaultCacheConfiguration();

    dataCacheCfg.setName("dataCache");
    dataCacheCfg.setCacheMode(PARTITIONED);
    dataCacheCfg.setNearConfiguration(null);
    dataCacheCfg.setWriteSynchronizationMode(CacheWriteSynchronizationMode.FULL_SYNC);
    dataCacheCfg.setAffinityMapper(new IgfsGroupDataBlocksKeyMapper(2));
    dataCacheCfg.setBackups(0);
    dataCacheCfg.setAtomicityMode(TRANSACTIONAL);
    dataCacheCfg.setOffHeapMaxMemory(0);

    CacheConfiguration metaCacheCfg = defaultCacheConfiguration();

    metaCacheCfg.setName("metaCache");
    metaCacheCfg.setCacheMode(REPLICATED);
    metaCacheCfg.setWriteSynchronizationMode(CacheWriteSynchronizationMode.FULL_SYNC);
    metaCacheCfg.setAtomicityMode(TRANSACTIONAL);

    IgniteConfiguration cfg = new IgniteConfiguration();

    cfg.setGridName(gridName);

    TcpDiscoverySpi discoSpi = new TcpDiscoverySpi();

    discoSpi.setIpFinder(new TcpDiscoveryVmIpFinder(true));

    cfg.setDiscoverySpi(discoSpi);
    cfg.setCacheConfiguration(dataCacheCfg, metaCacheCfg);
    cfg.setFileSystemConfiguration(igfsCfg);

    cfg.setLocalHost("127.0.0.1");
    cfg.setConnectorConfiguration(null);

    return G.start(cfg);
  }
  /** @throws Exception If failed. */
  public void testStartGridWithConfigUrl() throws Exception {
    GridEmbeddedHttpServer srv = null;
    String gridName = "grid_with_url_config";

    try {
      srv =
          GridEmbeddedHttpServer.startHttpServer()
              .withFileDownloadingHandler(
                  null,
                  GridTestUtils.resolveIgnitePath(
                      "modules/core/src/test/config/default-spring-url-testing.xml"));

      Ignite ignite = G.start(new URL(srv.getBaseUrl()));

      assert gridName.equals(ignite.name()) : "Unexpected grid name: " + ignite.name();
    } finally {
      if (srv != null) srv.stop(1);

      G.stop(gridName, false);
    }
  }
  /**
   * 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);
    }
  }
  /** @throws Exception If failed. */
  public void testNoErrors() throws Exception {
    bean = new LifeCycleBaseBean();

    startGrid();

    try {
      assertEquals(IgniteState.STARTED, G.state(getTestGridName()));

      assertEquals(1, bean.count(BEFORE_NODE_START));
      assertEquals(1, bean.count(AFTER_NODE_START));
      assertEquals(0, bean.count(BEFORE_NODE_STOP));
      assertEquals(0, bean.count(AFTER_NODE_STOP));
    } finally {
      stopAllGrids();
    }

    assertEquals(IgniteState.STOPPED, G.state(getTestGridName()));

    assertEquals(1, bean.count(BEFORE_NODE_START));
    assertEquals(1, bean.count(AFTER_NODE_START));
    assertEquals(1, bean.count(BEFORE_NODE_STOP));
    assertEquals(1, bean.count(AFTER_NODE_STOP));
  }
  /**
   * @param args Arguments.
   * @throws Exception If failed.
   */
  public static void main(String[] args) throws Exception {
    // resetLog4j("org.apache.ignite.internal.processors.cache.distributed.dht.preloader",
    // Level.DEBUG, false, 0);

    // G.start("modules/tests/config/spring-multicache.xml");
    // G.start("examples/config/example-cache.xml");

    G.start();

    // Wait until Ok is pressed.
    JOptionPane.showMessageDialog(
        null,
        new JComponent[] {
          new JLabel("Ignite started."),
          new JLabel(
              "<html>" + "You can use JMX console at <u>http://localhost:1234</u>" + "</html>"),
          new JLabel("Press OK to stop Ignite.")
        },
        "Ignite Startup JUnit",
        JOptionPane.INFORMATION_MESSAGE);

    G.stop(true);
  }
  @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter")
  protected void checkSupplyContextMapIsEmpty() {
    for (Ignite g : G.allGrids()) {
      for (GridCacheAdapter c : ((IgniteEx) g).context().cache().internalCaches()) {

        Object supplier = U.field(c.preloader(), "supplier");

        Map map = U.field(supplier, "scMap");

        synchronized (map) {
          assert map.isEmpty();
        }
      }
    }
  }
  /** @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();
    }
  }
  /** @throws Exception If failed. */
  @SuppressWarnings({"SynchronizationOnLocalVariableOrMethodParameter"})
  public void testStartMultipleGridsFromSpring() throws Exception {
    File cfgFile =
        GridTestUtils.resolveIgnitePath(
            GridTestProperties.getProperty("loader.self.multipletest.config"));

    assert cfgFile != null;

    String path = cfgFile.getAbsolutePath();

    info("Loading Grid from configuration file: " + path);

    final GridTuple<IgniteState> gridState1 = F.t(null);
    final GridTuple<IgniteState> gridState2 = F.t(null);

    final Object mux = new Object();

    IgnitionListener factoryLsnr =
        new IgnitionListener() {
          @Override
          public void onStateChange(String name, IgniteState state) {
            synchronized (mux) {
              if ("grid-factory-test-1".equals(name)) gridState1.set(state);
              else if ("grid-factory-test-2".equals(name)) gridState2.set(state);
            }
          }
        };

    G.addListener(factoryLsnr);

    G.start(path);

    assert G.ignite("grid-factory-test-1") != null;
    assert G.ignite("grid-factory-test-2") != null;

    synchronized (mux) {
      assert gridState1.get() == STARTED
          : "Invalid grid state [expected=" + STARTED + ", returned=" + gridState1 + ']';
      assert gridState2.get() == STARTED
          : "Invalid grid state [expected=" + STARTED + ", returned=" + gridState2 + ']';
    }

    G.stop("grid-factory-test-1", true);
    G.stop("grid-factory-test-2", true);

    synchronized (mux) {
      assert gridState1.get() == STOPPED
          : "Invalid grid state [expected=" + STOPPED + ", returned=" + gridState1 + ']';
      assert gridState2.get() == STOPPED
          : "Invalid grid state [expected=" + STOPPED + ", returned=" + gridState2 + ']';
    }
  }
  /** Tests named grid */
  public void testNamedGridGetOrStart() throws Exception {
    IgniteConfiguration cfg = getConfiguration("test");
    try (Ignite ignite = Ignition.getOrStart(cfg)) {
      try {
        Ignition.start(cfg);

        fail("Expected exception after grid started");
      } catch (IgniteException ignored) {
        // No-op.
      }

      Ignite ignite2 = Ignition.getOrStart(cfg);

      assertEquals("Must return same instance", ignite, ignite2);
    }

    assertTrue(G.allGrids().isEmpty());
  }
  /** @throws Exception If any error occurs. */
  public void testTopologyVersion() throws Exception {
    clientFlagGlobal = false;

    startGridsMultiThreaded(GRID_CNT);

    long prev = 0;

    for (Ignite g : G.allGrids()) {
      IgniteKernal kernal = (IgniteKernal) g;

      long ver = kernal.context().discovery().topologyVersion();

      info("Top ver: " + ver);

      if (prev == 0) prev = ver;
    }

    info("Test finished.");
  }
  /**
   * @param gridErr Grid error flag.
   * @throws Exception If failed.
   */
  private void checkAfterStart(boolean gridErr) throws Exception {
    bean = new LifeCycleExceptionBean(AFTER_NODE_START, gridErr);

    try {
      startGrid();

      assertTrue(false); // Should never get here.
    } catch (IgniteCheckedException expected) {
      info("Got expected exception: " + expected);

      assertEquals(IgniteState.STOPPED, G.state(getTestGridName()));
    } finally {
      stopAllGrids();
    }

    assertEquals(1, bean.count(BEFORE_NODE_START));
    assertEquals(0, bean.count(AFTER_NODE_START));
    assertEquals(1, bean.count(BEFORE_NODE_STOP));
    assertEquals(1, bean.count(AFTER_NODE_STOP));
  }
  /**
   * @param lockName Reentrant lock name.
   * @throws Exception If failed.
   */
  private void removeReentrantLock(String lockName, final boolean fair) throws Exception {
    IgniteLock lock = grid(RND.nextInt(NODES_CNT)).reentrantLock(lockName, false, fair, true);

    assert lock != null;

    // Remove lock on random node.
    IgniteLock lock0 = grid(RND.nextInt(NODES_CNT)).reentrantLock(lockName, false, fair, true);

    assertNotNull(lock0);

    lock0.close();

    // Ensure reentrant lock is removed on all nodes.
    for (Ignite g : G.allGrids())
      assertNull(
          ((IgniteKernal) g)
              .context()
              .dataStructures()
              .reentrantLock(lockName, false, fair, false));

    checkRemovedReentrantLock(lock);
  }
  /** {@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;
  }
  /**
   * Checks that GridTaskListener is only called once per task.
   *
   * @throws Exception If failed.
   */
  @SuppressWarnings({"BusyWait", "unchecked"})
  public void testGridTaskListener() throws Exception {
    final AtomicInteger cnt = new AtomicInteger(0);

    IgniteInClosure<IgniteFuture<?>> lsnr =
        new CI1<IgniteFuture<?>>() {
          @Override
          public void apply(IgniteFuture<?> fut) {
            assert fut != null;

            cnt.incrementAndGet();
          }
        };

    Ignite ignite = G.ignite(getTestGridName());

    assert ignite != null;

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

    ComputeTaskFuture<?> fut = executeAsync(ignite.compute(), TestTask.class.getName(), null);

    fut.listen(lsnr);

    fut.get();

    while (cnt.get() == 0) {
      try {
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        error("Got interrupted while sleep.", e);

        break;
      }
    }

    assert cnt.get() == 1
        : "Unexpected GridTaskListener apply count [count=" + cnt.get() + ", expected=1]";
  }
  /**
   * @param gridName Grid name.
   * @throws Exception If test failed.
   */
  protected void doMultiNodeTest(String gridName) throws Exception {
    startLatch = new CountDownLatch(3);

    read1Latch = new CountDownLatch(1);
    read1FinishedLatch = new CountDownLatch(2);

    read2Latch = new CountDownLatch(1);
    read2FinishedLatch = new CountDownLatch(2);

    read3Latch = new CountDownLatch(1);
    read3FinishedLatch = new CountDownLatch(2);

    rmvLatch = new CountDownLatch(1);

    try {
      startGrid(gridName + 1);

      Ignite ignite = startGrid(gridName);

      ComputeTaskFuture fut =
          executeAsync(ignite.compute(), new GridMultiNodeGlobalConsumerTask(), null);

      executeAsync(ignite.compute(), GridMultiNodeTestCheckPointTask.class, null)
          .get(2 * 60 * 1000);

      fut.get();

      for (Ignite g : G.allGrids()) {
        assert checkCheckpointManager(g)
            : "Session IDs got stuck after task completion [grid="
                + g.name()
                + ", sesIds="
                + checkpoints(g).sessionIds()
                + ']';
      }
    } finally {
      stopAllGrids();
    }
  }
 /**
  * @param args Args.
  * @throws Exception If failed.
  */
 public static void main(String[] args) throws Exception {
   G.start("modules/core/src/test/config/jobs-load-server.xml");
 }
  /** {@inheritDoc} */
  @Override
  protected void beforeTestsStarted() throws Exception {
    Ignite ignite = G.ignite(getTestGridName());

    assert ignite != null;
  }
  /** @throws InterruptedException If interrupted. */
  @SuppressWarnings("BusyWait")
  protected void awaitPartitionMapExchange() throws InterruptedException {
    for (Ignite g : G.allGrids()) {
      IgniteKernal g0 = (IgniteKernal) g;

      for (IgniteCacheProxy<?, ?> c : g0.context().cache().jcaches()) {
        CacheConfiguration cfg = c.context().config();

        if (cfg.getCacheMode() == PARTITIONED
            && cfg.getRebalanceMode() != NONE
            && g.cluster().nodes().size() > 1) {
          AffinityFunction aff = cfg.getAffinity();

          GridDhtCacheAdapter<?, ?> dht = dht(c);

          GridDhtPartitionTopology top = dht.topology();

          for (int p = 0; p < aff.partitions(); p++) {
            long start = 0;

            for (int i = 0; ; i++) {
              boolean match = false;

              AffinityTopologyVersion readyVer =
                  dht.context().shared().exchange().readyAffinityVersion();

              if (readyVer.topologyVersion() > 0 && c.context().started()) {
                // Must map on updated version of topology.
                Collection<ClusterNode> affNodes =
                    g0.affinity(cfg.getName()).mapPartitionToPrimaryAndBackups(p);

                int exp = affNodes.size();

                GridDhtTopologyFuture topFut = top.topologyVersionFuture();

                Collection<ClusterNode> owners =
                    (topFut != null && topFut.isDone())
                        ? top.nodes(p, AffinityTopologyVersion.NONE)
                        : Collections.<ClusterNode>emptyList();

                int actual = owners.size();

                if (affNodes.size() != owners.size() || !affNodes.containsAll(owners)) {
                  LT.warn(
                      log(),
                      null,
                      "Waiting for topology map update ["
                          + "grid="
                          + g.name()
                          + ", cache="
                          + cfg.getName()
                          + ", cacheId="
                          + dht.context().cacheId()
                          + ", topVer="
                          + top.topologyVersion()
                          + ", topFut="
                          + topFut
                          + ", p="
                          + p
                          + ", affNodesCnt="
                          + exp
                          + ", ownersCnt="
                          + actual
                          + ", affNodes="
                          + affNodes
                          + ", owners="
                          + owners
                          + ", locNode="
                          + g.cluster().localNode()
                          + ']');
                } else match = true;
              } else {
                LT.warn(
                    log(),
                    null,
                    "Waiting for topology map update ["
                        + "grid="
                        + g.name()
                        + ", cache="
                        + cfg.getName()
                        + ", cacheId="
                        + dht.context().cacheId()
                        + ", topVer="
                        + top.topologyVersion()
                        + ", started="
                        + dht.context().started()
                        + ", p="
                        + p
                        + ", readVer="
                        + readyVer
                        + ", locNode="
                        + g.cluster().localNode()
                        + ']');
              }

              if (!match) {
                if (i == 0) start = System.currentTimeMillis();

                if (System.currentTimeMillis() - start > 30_000)
                  throw new IgniteException(
                      "Timeout of waiting for topology map update ["
                          + "grid="
                          + g.name()
                          + ", cache="
                          + cfg.getName()
                          + ", cacheId="
                          + dht.context().cacheId()
                          + ", topVer="
                          + top.topologyVersion()
                          + ", p="
                          + p
                          + ", readVer="
                          + readyVer
                          + ", locNode="
                          + g.cluster().localNode()
                          + ']');

                Thread.sleep(200); // Busy wait.

                continue;
              }

              if (i > 0)
                log()
                    .warning(
                        "Finished waiting for topology map update [grid="
                            + g.name()
                            + ", p="
                            + p
                            + ", duration="
                            + (System.currentTimeMillis() - start)
                            + "ms]");

              break;
            }
          }
        }
      }
    }
  }
 /** @throws Exception If test failed. */
 public void testCollisionJobContext() throws Exception {
   G.ignite(getTestGridName()).compute().execute(new GridTestTask(), "some-arg");
 }
  /**
   * Aspect implementation which executes grid-enabled methods on remote nodes.
   *
   * <p>{@inheritDoc}
   */
  @SuppressWarnings({
    "ProhibitedExceptionDeclared",
    "ProhibitedExceptionThrown",
    "CatchGenericClass",
    "unchecked"
  })
  @Override
  public Object invoke(MethodInvocation invoc) throws Throwable {
    Method mtd = invoc.getMethod();

    Gridify ann = mtd.getAnnotation(Gridify.class);

    assert ann != null : "Intercepted method does not have gridify annotation.";

    // Since annotations in Java don't allow 'null' as default value
    // we have accept an empty string and convert it here.
    // NOTE: there's unintended behavior when user specifies an empty
    // string as intended grid name.
    // NOTE: the 'ann.gridName() == null' check is added to mitigate
    // annotation bugs in some scripting languages (e.g. Groovy).
    String gridName = F.isEmpty(ann.gridName()) ? null : ann.gridName();

    if (G.state(gridName) != STARTED)
      throw new IgniteCheckedException("Grid is not locally started: " + gridName);

    // Initialize defaults.
    GridifyArgument arg =
        new GridifyArgumentAdapter(
            mtd.getDeclaringClass(),
            mtd.getName(),
            mtd.getParameterTypes(),
            invoc.getArguments(),
            invoc.getThis());

    if (!ann.interceptor().equals(GridifyInterceptor.class)) {
      // Check interceptor first.
      if (!ann.interceptor().newInstance().isGridify(ann, arg)) return invoc.proceed();
    }

    if (!ann.taskClass().equals(GridifyDefaultTask.class) && !ann.taskName().isEmpty())
      throw new IgniteCheckedException(
          "Gridify annotation must specify either Gridify.taskName() or "
              + "Gridify.taskClass(), but not both: "
              + ann);

    try {
      Ignite ignite = G.ignite(gridName);

      if (!ann.taskClass().equals(GridifyDefaultTask.class))
        return ignite
            .compute()
            .withTimeout(ann.timeout())
            .execute((Class<? extends ComputeTask<GridifyArgument, Object>>) ann.taskClass(), arg);

      // If task name was not specified.
      if (ann.taskName().isEmpty())
        return ignite
            .compute()
            .withTimeout(ann.timeout())
            .execute(new GridifyDefaultTask(invoc.getMethod().getDeclaringClass()), arg);

      // If task name was specified.
      return ignite.compute().withTimeout(ann.timeout()).execute(ann.taskName(), arg);
    } catch (Exception e) {
      for (Class<?> ex : invoc.getMethod().getExceptionTypes()) {
        // Descend all levels down.
        Throwable cause = e.getCause();

        while (cause != null) {
          if (ex.isAssignableFrom(cause.getClass())) throw cause;

          cause = cause.getCause();
        }

        if (ex.isAssignableFrom(e.getClass())) throw e;
      }

      throw new GridifyRuntimeException("Undeclared exception thrown: " + e.getMessage(), e);
    }
  }
  /**
   * @param gridName Grid name ({@code null} for default grid).
   * @throws Exception If failed.
   */
  private void checkConcurrentStartStop(@Nullable final String gridName) throws Exception {
    final AtomicInteger startedCnt = new AtomicInteger();
    final AtomicInteger stoppedCnt = new AtomicInteger();

    IgnitionListener lsnr =
        new IgnitionListener() {
          @SuppressWarnings("StringEquality")
          @Override
          public void onStateChange(@Nullable String name, IgniteState state) {
            assert name == gridName;

            info("On state change fired: " + state);

            if (state == STARTED) startedCnt.incrementAndGet();
            else {
              assert state == STOPPED : "Unexpected state: " + state;

              stoppedCnt.incrementAndGet();
            }
          }
        };

    G.addListener(lsnr);

    try {
      final int iterCnt = 3;

      multithreaded(
          new Callable<Object>() {
            @Nullable
            @Override
            public Object call() throws Exception {
              for (int i = 0; i < iterCnt; i++) {
                try {
                  IgniteConfiguration cfg = getConfiguration(gridName);

                  G.start(cfg);
                } catch (Exception e) {
                  String msg = e.getMessage();

                  if (msg != null
                      && (msg.contains("Default Ignite instance has already been started.")
                          || msg.contains(
                              "Ignite instance with this name has already been started:")))
                    info("Caught expected exception: " + msg);
                  else throw e; // Unexpected exception.
                } finally {
                  stopGrid(gridName);
                }
              }

              info("Thread finished.");

              return null;
            }
          },
          5,
          "tester");

      assert G.allGrids().isEmpty();

      assert startedCnt.get() == iterCnt;
      assert stoppedCnt.get() == iterCnt;
    } finally {
      G.removeListener(lsnr);

      G.stopAll(true);
    }
  }
  /** @throws Exception If failed. */
  public void testStartMultipleInstanceSpi() throws Exception {
    IgniteConfiguration cfg1 = getConfiguration();
    IgniteConfiguration cfg2 = getConfiguration();
    IgniteConfiguration cfg3 = getConfiguration();

    cfg1.setCollisionSpi(new TestMultipleInstancesCollisionSpi());
    cfg2.setCollisionSpi(new TestMultipleInstancesCollisionSpi());
    cfg3.setCollisionSpi(new TestMultipleInstancesCollisionSpi());

    cfg2.setGridName(getTestGridName() + '1');

    G.start(cfg2);

    G.start(cfg1);

    cfg3.setGridName(getTestGridName() + '2');

    G.start(cfg3);

    assert G.state(cfg1.getGridName()) == STARTED;
    assert G.state(getTestGridName() + '1') == STARTED;
    assert G.state(getTestGridName() + '2') == STARTED;

    G.stop(getTestGridName() + '2', false);
    G.stop(cfg1.getGridName(), false);
    G.stop(getTestGridName() + '1', false);

    assert G.state(cfg1.getGridName()) == STOPPED;
    assert G.state(getTestGridName() + '1') == STOPPED;
    assert G.state(getTestGridName() + '2') == STOPPED;
  }
 /** @throws Exception If failed. */
 @Override
 protected void afterTest() throws Exception {
   G.stopAll(false);
 }