/** @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 + ']';
    }
  }
  /**
   * @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);
    }
  }