/**
   * Checks that unacceptable property value prevents SPI from being started.
   *
   * @param spi Spi to test property on.
   * @param propName name of property to check.
   * @param val An illegal value.
   * @param checkExMsg If {@code true} then additional info will be added to failure.
   * @throws Exception If check failed.
   */
  protected void checkNegativeSpiProperty(
      IgniteSpi spi, String propName, Object val, boolean checkExMsg) throws Exception {
    assert spi != null;
    assert propName != null;

    getTestData().getTestResources().inject(spi);

    String mtdName = "set" + propName.substring(0, 1).toUpperCase() + propName.substring(1);

    Method mtd = null;

    for (Method m : spi.getClass().getMethods())
      if (m.getName().equals(mtdName)) {
        mtd = m;

        break;
      }

    assert mtd != null : "The setter is not found for property: " + propName;

    boolean err = false;

    try {
      mtd.invoke(spi, val);
    } catch (InvocationTargetException e) {
      info("SPI property setter thrown exception: " + e);

      if (e.getCause() instanceof IllegalArgumentException) err = true;
      else throw e;
    }

    if (!err)
      try {
        if (!(spi instanceof DiscoverySpi)) spi.getNodeAttributes();

        spi.spiStart(getTestGridName());
      } catch (IgniteSpiException e) {
        info("SPI start thrown exception: " + e);

        if (checkExMsg)
          assert e.getMessage().contains("SPI parameter failed condition check: ")
              : "SPI has returned wrong exception message [propName="
                  + propName
                  + ", msg="
                  + e
                  + ']';

        err = true;
      }

    assert err : "No check for property [property=" + propName + ", value=" + val + ']';
  }
  /** {@inheritDoc} */
  @Override
  public void printStackTrace(PrintStream s) {
    super.printStackTrace(s);

    for (Throwable t : causes) t.printStackTrace(s);
  }