public void prepare(String path, String tx_log_dir) {
    try {
      asadmin("create-cluster", CLUSTER_NAME);
      asadmin(
          "set",
          "configs.config."
              + CLUSTER_NAME
              + "-config.transaction-service.property.delegated-recovery=true");
      asadmin(
          "set",
          "configs.config."
              + CLUSTER_NAME
              + "-config.transaction-service.property.add-wait-point-during-recovery=20");
      asadmin("create-system-properties", "--target", CLUSTER_NAME, "TX-LOG-DIR=" + tx_log_dir);
      asadmin("create-local-instance", "--cluster", CLUSTER_NAME, INSTANCE1_NAME);
      asadmin("create-local-instance", "--cluster", CLUSTER_NAME, INSTANCE2_NAME);
      asadmin("create-local-instance", "--cluster", CLUSTER_NAME, INSTANCE3_NAME);
      if (Boolean.getBoolean("enableShoalLogger")) {
        asadmin("set-log-levels", "ShoalLogger=FINER");
        asadmin("set-log-levels", "--target", CLUSTER_NAME, "ShoalLogger=FINER");
      }
      asadmin("start-cluster", CLUSTER_NAME);
      System.out.println("Started cluster. Setting up resources.");

      asadmin("create-resource-ref", "--target", CLUSTER_NAME, DEF_RESOURCE);
      asadmin("create-resource-ref", "--target", CLUSTER_NAME, XA_RESOURCE);
      asadmin("deploy", "--target", CLUSTER_NAME, path);
      System.out.println("Deployed " + path);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
  static {
    if (!Boolean.getBoolean(GridSystemProperties.GG_JETTY_LOG_NO_OVERRIDE)) {
      String ctgrJetty = "org.eclipse.jetty"; // WARN for this category.
      String ctgrJettyUtil = "org.eclipse.jetty.util.log"; // ERROR for this...
      String ctgrJettyUtilComp = "org.eclipse.jetty.util.component"; // ...and this.

      try {
        Class<?> logCls = Class.forName("org.apache.log4j.Logger");

        Object logJetty = logCls.getMethod("getLogger", String.class).invoke(logCls, ctgrJetty);
        Object logJettyUtil =
            logCls.getMethod("getLogger", String.class).invoke(logCls, ctgrJettyUtil);
        Object logJettyUtilComp =
            logCls.getMethod("getLogger", String.class).invoke(logCls, ctgrJettyUtilComp);

        Class<?> lvlCls = Class.forName("org.apache.log4j.Level");

        Object warnLvl = lvlCls.getField("WARN").get(null);
        Object errLvl = lvlCls.getField("ERROR").get(null);

        logJetty.getClass().getMethod("setLevel", lvlCls).invoke(logJetty, warnLvl);
        logJettyUtil.getClass().getMethod("setLevel", lvlCls).invoke(logJetty, errLvl);
        logJettyUtilComp.getClass().getMethod("setLevel", lvlCls).invoke(logJetty, errLvl);
      } catch (Exception ignored) {
        // No-op.
      }
    }
  }
 public static void fetchDeviceNames() {
   String nametable = System.getProperty("hm2mqtt.hm.jsonNameTable");
   if (nametable != null) {
     fetchDeviceNamesFromNameTable(nametable);
     return;
   }
   if (Boolean.getBoolean("hm2mqtt.hm.disableReGa")) return;
   fetchDeviceNamesFromReGa();
 }
 public static boolean readBooleanSystemProperty(String name) {
   boolean result = false;
   try {
     result = Boolean.getBoolean(name);
   } catch (Throwable t) {
     // ignore
   }
   return result;
 }
 public ISPNTestSuiteAnalyzer() {
   writeToFile = Boolean.getBoolean(WRITE_TO_FILE_PROPERTY);
   String property = System.getProperty(CLASS_LIST_PROPERTY);
   if (property == null) {
     classList = Collections.emptySet();
   } else {
     classList = extractClasses(property);
   }
   property = System.getProperty(METHOD_LIST_PROPERTY);
   if (property == null) {
     methodList = Collections.emptySet();
   } else {
     methodList = new HashSet<String>(Arrays.asList(property.split(",")));
   }
   testStateMap = new HashMap<String, TestState>();
 }
  /** The log initializion bloc. */
  static {
    boolean useStackTrace = false;

    try {
      useStackTrace = Boolean.getBoolean(DSTACKTRACE);
    } catch (Exception ex) {
      // ignore
    }

    STACKTRACE = useStackTrace;

    boolean log = false;

    try {
      log = Boolean.getBoolean(DLOG);
    } catch (Exception ex) {
      // ignore
    }

    LOG = log;

    boolean useTrace = false;

    try {
      if (STACKTRACE) {
        useTrace = true;
      } else {
        useTrace = Boolean.getBoolean(DTRACE);
      }
    } catch (Exception ex) {
      // ignore
    }

    TRACE = useTrace;

    if (LOG) {
      System.out.println(DLOG + " enabled.");
      PrintWriter logfile = createLogFile();

      Debug.log("IzPack LogFile created at ");

      // ** write some runtime system properties into the logfile **
      Debug.log("System.Properties:", LOG_WITH_TIME_STAMP);

      Properties sysProps = System.getProperties();

      Enumeration spe = sysProps.keys();

      while (spe.hasMoreElements()) {
        String aKey = (String) spe.nextElement();
        Debug.log(aKey + "  =  " + sysProps.getProperty(aKey), LOG_WITHOUT_DATE);
      }
      Debug.log("\n==========================================\n", LOG_WITHOUT_DATE);
      Debug.log("\n IzPack runs on: \n", LOG_WITHOUT_DATE);
      Debug.log(OsVersion.getOsDetails(), LOG_WITHOUT_DATE);
      Debug.log("\n==========================================\n", LOG_WITHOUT_DATE);
    }

    if (TRACE) {
      System.out.println(DTRACE + " enabled.");
    }

    if (STACKTRACE) {
      System.out.println(DSTACKTRACE + " enabled.");
    }
  }
  /**
   * Factory method for creating <tt>AttributeInfo</tt> structures.
   *
   * <p>An <tt>AttributeInfo</tt> of the appropriate subtype from the <tt>attributes</tt> package is
   * created unless the type of the attribute is unknown in which case an instance of
   * <tt>AttributeInfo</tt> is returned.
   *
   * <p>
   *
   * <p>Attributes are skipped if the environment variable <tt>SYSTEM_PROPERTY_SKIP_ATTRIBUTES</tt>
   * is set to true.
   *
   * @param in the <tt>DataInput</tt> from which to read the <tt>AttributeInfo</tt> structure
   * @param classFile the parent class file of the structure to be created
   * @return the new <tt>AttributeInfo</tt> structure
   * @throws InvalidByteCodeException if the byte code is invalid
   * @throws IOException if an exception occurs with the <tt>DataInput</tt>
   */
  public static AttributeInfo createOrSkip(DataInput in, ClassFile classFile)
      throws InvalidByteCodeException, IOException {

    AttributeInfo attributeInfo = null;

    if (Boolean.getBoolean(SYSTEM_PROPERTY_SKIP_ATTRIBUTES)) {
      in.skipBytes(2);
      in.skipBytes(in.readInt());
    } else {
      int attributeNameIndex = in.readUnsignedShort();
      int attributeLength = in.readInt();

      ConstantUtf8Info cpInfoName = classFile.getConstantPoolUtf8Entry(attributeNameIndex);
      String attributeName = null;

      if (cpInfoName == null) {
        return null;
      }

      attributeName = cpInfoName.getString();

      if (ConstantValueAttribute.ATTRIBUTE_NAME.equals(attributeName)) {
        attributeInfo = new ConstantValueAttribute();

      } else if (CodeAttribute.ATTRIBUTE_NAME.equals(attributeName)) {
        attributeInfo = new CodeAttribute();

      } else if (ExceptionsAttribute.ATTRIBUTE_NAME.equals(attributeName)) {
        attributeInfo = new ExceptionsAttribute();

      } else if (InnerClassesAttribute.ATTRIBUTE_NAME.equals(attributeName)) {
        attributeInfo = new InnerClassesAttribute();

      } else if (SyntheticAttribute.ATTRIBUTE_NAME.equals(attributeName)) {
        attributeInfo = new SyntheticAttribute();

      } else if (SourceFileAttribute.ATTRIBUTE_NAME.equals(attributeName)) {
        attributeInfo = new SourceFileAttribute();

      } else if (LineNumberTableAttribute.ATTRIBUTE_NAME.equals(attributeName)) {
        attributeInfo = new LineNumberTableAttribute();

      } else if (LocalVariableTableAttribute.ATTRIBUTE_NAME.equals(attributeName)) {
        attributeInfo = new LocalVariableTableAttribute();

      } else if (DeprecatedAttribute.ATTRIBUTE_NAME.equals(attributeName)) {
        attributeInfo = new DeprecatedAttribute();

      } else if (EnclosingMethodAttribute.ATTRIBUTE_NAME.equals(attributeName)) {
        attributeInfo = new EnclosingMethodAttribute();

      } else if (SignatureAttribute.ATTRIBUTE_NAME.equals(attributeName)) {
        attributeInfo = new SignatureAttribute();

      } else if (LocalVariableTypeTableAttribute.ATTRIBUTE_NAME.equals(attributeName)) {
        attributeInfo = new LocalVariableTypeTableAttribute();

      } else if (RuntimeVisibleAnnotationsAttribute.ATTRIBUTE_NAME.equals(attributeName)) {
        attributeInfo = new RuntimeVisibleAnnotationsAttribute();

      } else if (RuntimeInvisibleAnnotationsAttribute.ATTRIBUTE_NAME.equals(attributeName)) {
        attributeInfo = new RuntimeInvisibleAnnotationsAttribute();

      } else if (AnnotationDefaultAttribute.ATTRIBUTE_NAME.equals(attributeName)) {
        attributeInfo = new AnnotationDefaultAttribute();

      } else if (BootstrapMethodsAttribute.ATTRIBUTE_NAME.equals(attributeName)) {
        attributeInfo = new BootstrapMethodsAttribute(attributeLength);

      } else {
        attributeInfo = new AttributeInfo(attributeLength);
      }
      attributeInfo.setAttributeNameIndex(attributeNameIndex);
      attributeInfo.setClassFile(classFile);
      attributeInfo.read(in);
    }

    return attributeInfo;
  }
  public DaemonStartupInfo startDaemon() {
    String daemonUid = UUID.randomUUID().toString();

    GradleInstallation gradleInstallation = CurrentGradleInstallation.get();
    ModuleRegistry registry = new DefaultModuleRegistry(gradleInstallation);
    ClassPath classpath;
    List<File> searchClassPath;
    if (gradleInstallation == null) {
      // When not running from a Gradle distro, need runtime impl for launcher plus the search path
      // to look for other modules
      classpath = new DefaultClassPath();
      for (Module module : registry.getModule("gradle-launcher").getAllRequiredModules()) {
        classpath = classpath.plus(module.getClasspath());
      }
      searchClassPath = registry.getAdditionalClassPath().getAsFiles();
    } else {
      // When running from a Gradle distro, only need launcher jar. The daemon can find everything
      // from there.
      classpath = registry.getModule("gradle-launcher").getImplementationClasspath();
      searchClassPath = Collections.emptyList();
    }
    if (classpath.isEmpty()) {
      throw new IllegalStateException(
          "Unable to construct a bootstrap classpath when starting the daemon");
    }

    versionValidator.validate(daemonParameters);

    List<String> daemonArgs = new ArrayList<String>();
    daemonArgs.add(daemonParameters.getEffectiveJvm().getJavaExecutable().getAbsolutePath());

    List<String> daemonOpts = daemonParameters.getEffectiveJvmArgs();
    daemonArgs.addAll(daemonOpts);
    daemonArgs.add("-cp");
    daemonArgs.add(CollectionUtils.join(File.pathSeparator, classpath.getAsFiles()));

    if (Boolean.getBoolean("org.gradle.daemon.debug")) {
      daemonArgs.add("-Xdebug");
      daemonArgs.add("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005");
    }
    LOGGER.debug("Using daemon args: {}", daemonArgs);

    daemonArgs.add(GradleDaemon.class.getName());
    // Version isn't used, except by a human looking at the output of jps.
    daemonArgs.add(GradleVersion.current().getVersion());

    // Serialize configuration to daemon via the process' stdin
    ByteArrayOutputStream serializedConfig = new ByteArrayOutputStream();
    FlushableEncoder encoder =
        new KryoBackedEncoder(new EncodedStream.EncodedOutput(serializedConfig));
    try {
      encoder.writeString(daemonParameters.getGradleUserHomeDir().getAbsolutePath());
      encoder.writeString(daemonDir.getBaseDir().getAbsolutePath());
      encoder.writeSmallInt(daemonParameters.getIdleTimeout());
      encoder.writeString(daemonUid);
      encoder.writeSmallInt(daemonOpts.size());
      for (String daemonOpt : daemonOpts) {
        encoder.writeString(daemonOpt);
      }
      encoder.writeSmallInt(searchClassPath.size());
      for (File file : searchClassPath) {
        encoder.writeString(file.getAbsolutePath());
      }
      encoder.flush();
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
    ByteArrayInputStream stdInput = new ByteArrayInputStream(serializedConfig.toByteArray());

    DaemonStartupInfo daemonInfo = startProcess(daemonArgs, daemonDir.getVersionedDir(), stdInput);
    listener.daemonStarted(daemonInfo);
    return daemonInfo;
  }
/**
 * Test suite for distributing JUnit3 tests. Simply add tests to this suite just like you would for
 * regular JUnit3 suites, and these tests will be executed in parallel on the grid. Note that if
 * there are no other grid nodes, this suite will still ensure parallel test execution within single
 * JVM.
 *
 * <p>Below is an example of distributed JUnit3 test suite:
 *
 * <pre name="code" class="java">
 * public class GridJunit3ExampleTestSuite {
 *     // Standard JUnit3 static suite method.
 *     public static TestSuite suite() {
 *         TestSuite suite = new GridJunit3TestSuite("Example Grid Test Suite");
 *
 *         // Add tests.
 *         suite.addTestSuite(TestA.class);
 *         suite.addTestSuite(TestB.class);
 *         suite.addTestSuite(TestC.class);
 *
 *         return suite;
 *     }
 * }
 * </pre>
 *
 * If you have four tests A, B, C, and D, and if you need to run A and B sequentially, then you
 * should create a nested test suite with test A and B as follows:
 *
 * <pre name="code" class="java">
 * public class GridJunit3ExampleTestSuite {
 *     // Standard JUnit3 static suite method.
 *     public static TestSuite suite() {
 *         TestSuite suite = new GridJunit3TestSuite("Example Grid Test Suite");
 *
 *         // Nested test suite to run tests A and B sequentially.
 *         TestSuite nested = new TestSuite("Example Nested Sequential Suite");
 *
 *         nested.addTestSuite(TestA.class);
 *         nested.addTestSuite(TestB.class);
 *
 *         // Add tests A and B.
 *         suite.addTest(nested);
 *
 *         // Add other tests.
 *         suite.addTestSuite(TestC.class);
 *
 *         return suite;
 *     }
 * }
 * </pre>
 *
 * <p>Note that you can also grid-enable existing JUnit3 tests using {@link
 * GridifyTest @GridifyTest} annotation which you can attach to your {@code suite()} methods of
 * existing test suite. Refer to {@link GridifyTest @GridifyTest} documentation for more
 * information.
 *
 * <p>Also note that some tests can only be executed locally mostly due to some environment issues.
 * However they still can benefit from parallel execution with other tests. GridGain supports it via
 * {@link GridJunit3LocalTestSuite} suites that can be added to {@code GridJunit3TestSuite}. Refer
 * to {@link GridJunit3LocalTestSuite} documentation for more information.
 *
 * <h1 class="header">Logging</h1>
 *
 * When running distributed JUnit, all the logging that is done to {@link System#out} or {@link
 * System#err} is preserved. GridGain will accumulate all logging that is done on remote nodes, send
 * them back to originating node and associate all log statements with their corresponding tests.
 * This way, for example, if you are running tests from and IDEA or Eclipse (or any other IDE) you
 * would still see the logs as if it was a local run. However, since remote nodes keep all log
 * statements done within a single individual test case in memory, you must make sure that enough
 * memory is allocated on every node and that individual test cases do not spit out gigabytes of log
 * statements. Also note, that logs will be sent back to originating node upon completion of every
 * test, so don't be alarmed if you don't see any log statements for a while and then all of them
 * appear at once.
 *
 * <p>GridGain achieves such log transparency via reassigning {@link System#out} or {@link
 * System#err} to internal {@link PrintStream} implementation. However, when using {@code Log4J} (or
 * any other logging framework) within your tests you must make sure that it is configured with
 * {@link ConsoleAppender} and that {@link ConsoleAppender#setFollow(boolean)} attribute is set to
 * {@code true}. Logging to files is not supported yet and is planned for next point release.
 *
 * <p>
 *
 * <h1 class="header">Test Suite Nesting</h1>
 *
 * {@code GridJunit3TestSuite} instances can be nested within each other as deep as needed. However
 * all nested distributed test suites will be treated just like regular JUnit test suites, and not
 * as distributed test suites. This approach becomes convenient when you have several distributed
 * test suites that you would like to be able to execute separately in distributed fashion, but at
 * the same time you would like to be able to execute them as a part of larger distributed suites.
 *
 * <p>
 *
 * <h1 class="header">Configuration</h1>
 *
 * To run distributed JUnit tests you need to start other instances of GridGain. You can do so by
 * running {@code GRIDGAIN_HOME/bin/ggjunit.{sh|bat}} script, which will start default
 * configuration. If configuration other than default is required, then use regular {@code
 * GRIDGAIN_HOME/bin/ggstart.{sh|bat}} script and pass your own Spring XML configuration file as a
 * parameter to the script.
 *
 * <p>You can use the following configuration parameters to configure distributed test suite
 * locally. Note that many parameters can be overridden by setting corresponding VM parameters
 * defined in {@link GridTestVmParameters} at VM startup.
 *
 * <table class="doctable">
 *   <tr>
 *     <th>GridConfiguration Method</th>
 *     <th>Default Value</th>
 *     <th>Description</th>
 *   </tr>
 *   <tr>
 *     <td>{@link #setDisabled(boolean) setDisabled(boolean)}</td>
 *     <td>{@code false}</td>
 *     <td>
 *       If {@code true} then GridGain will be turned off and suite will run locally.
 *       This value can be overridden by setting {@link GridTestVmParameters#GRIDGAIN_DISABLED} VM
 *       parameter to {@code true}. This parameter comes handy when you would like to
 *       turn off GridGain without changing the actual code.
 *     </td>
 *   </tr>
 *   <tr>
 *     <td>{@link #setConfigurationPath(String) setConfigurationPath(String)}</td>
 *     <td>{@link #DFLT_JUNIT_CONFIG DFLT_JUNIT_CONFIG}</td>
 *     <td>
 *       Optional path to GridGain Spring XML configuration file for running JUnit tests. This
 *       property can be overridden by setting {@link GridTestVmParameters#GRIDGAIN_CONFIG} VM
 *       parameter. Note that the value can be either absolute value or relative to
 *       ${GRIDGAIN_HOME} installation folder.
 *     </td>
 *   </tr>
 *   <tr>
 *     <td>{@link #setRouterClassName(String) setRouterClassName(String)}</td>
 *     <td>{@link #DFLT_JUNIT_ROUTER DFLT_JUNIT_ROUTER}</td>
 *     <td>
 *       Optional name of test router class that implements {@link GridTestRouter} interface.
 *       If not provided, then tests will be routed in round-robin fashion using default
 *       {@link GridTestRouterAdapter}. The value of this parameter can be overridden by setting
 *       {@link GridTestVmParameters#GRIDGAIN_TEST_ROUTER} VM parameter to the name of your
 *       own customer router class.
 *     </td>
 *   </tr>
 *   <tr>
 *     <td>{@link #setRouterClass(Class) setRouterClass(Class)}</td>
 *     <td>{@code null}</td>
 *     <td>
 *       Same as {@link #setRouterClassName(String) setRouterClassName(String)}, but sets the
 *       actual class instead of the name.
 *     </td>
 *   </tr>
 *   <tr>
 *     <td>{@link #setTimeout(long) setTimeout(long)}</td>
 *     <td>{@code 0} which means that tests will never timeout.</td>
 *     <td>
 *       Maximum timeout value in milliseconds after which test suite will return without
 *       waiting for the remaining tests to complete. This value can be overridden by setting
 *       {@link GridTestVmParameters#GRIDGAIN_TEST_TIMEOUT} VM parameter to the timeout value
 *       for the tests.
 *     </td>
 *   </tr>
 * </table>
 *
 * @author 2005-2011 Copyright (C) GridGain Systems, Inc.
 * @version 3.1.1c.19062011
 */
public class GridJunit3TestSuite extends TestSuite {
  /**
   * Default GridGain configuration file for JUnits (value is {@code
   * config/junit/junit-spring.xml}).
   */
  public static final String DFLT_JUNIT_CONFIG = "config/junit/junit-spring.xml";

  /**
   * Default JUnit test router (value is {@link GridTestRouterAdapter
   * GridTestRouterAdapter.class.getName()}).
   */
  public static final String DFLT_JUNIT_ROUTER = GridTestRouterAdapter.class.getName();

  /** */
  private final Collection<String> locTests = new HashSet<String>();

  /** JUnit3 JavaAssist proxy. */
  private final GridJunit3ProxyFactory factory = new GridJunit3ProxyFactory();

  /** Flag indicating whether grid was started in this suite. */
  private boolean selfStarted;

  /** Junit3 Spring configuration path. */
  private String cfgPath =
      System.getProperty(GRIDGAIN_CONFIG.name()) == null
          ? DFLT_JUNIT_CONFIG
          : System.getProperty(GRIDGAIN_CONFIG.name());

  /**
   * Check if GridGain is disabled by checking {@link GridTestVmParameters#GRIDGAIN_DISABLED} system
   * property.
   */
  private boolean isDisabled = Boolean.getBoolean(GRIDGAIN_DISABLED.name());

  /** JUnit test router class name. */
  private String routerClsName =
      System.getProperty(GRIDGAIN_TEST_ROUTER.name()) == null
          ? DFLT_JUNIT_ROUTER
          : System.getProperty(GRIDGAIN_TEST_ROUTER.name());

  /** JUnit test router class. */
  private Class<? extends GridTestRouter> routerCls;

  /**
   * Local suite in case if grid is disabled or if this is a nested suite within other distributed
   * suite.
   */
  private TestSuite copy;

  /** JUnit grid name. */
  private String gridName;

  /** Test timeout. */
  private long timeout =
      Long.getLong(GRIDGAIN_TEST_TIMEOUT.name()) == null
          ? 0
          : Long.getLong(GRIDGAIN_TEST_TIMEOUT.name());

  /** */
  private ClassLoader clsLdr;

  /** Empty test suite. */
  public GridJunit3TestSuite() {
    if (copy == null) {
      copy = new TestSuite();
    }
  }

  /** @param name Test suite name. */
  public GridJunit3TestSuite(String name) {
    super(name);

    if (copy == null) {
      copy = new TestSuite(name);
    }
  }

  /**
   * Test suite for one class.
   *
   * @param cls Class for test suite.
   */
  public GridJunit3TestSuite(Class<? extends TestCase> cls) {
    super(cls);

    if (copy == null) {
      copy = new TestSuite(cls);
    }
  }

  /**
   * Test suite for a given test class with specified test name.
   *
   * @param cls Test class.
   * @param name Test name.
   */
  public GridJunit3TestSuite(Class<? extends TestCase> cls, String name) {
    super(cls, name);

    if (copy == null) {
      copy = new TestSuite(cls, name);
    }
  }

  /**
   * Copies non-distributed test suite into distributed one.
   *
   * @param suite Test suite to copy.
   */
  public GridJunit3TestSuite(TestSuite suite) {
    super(suite.getName());

    if (copy == null) {
      copy = new TestSuite(suite.getName());
    }

    for (int i = 0; i < suite.testCount(); i++) {
      addTest(suite.testAt(i));
    }
  }

  /**
   * Empty test suite with given class loader.
   *
   * @param clsLdr Tests class loader.
   */
  public GridJunit3TestSuite(ClassLoader clsLdr) {
    this();

    assert clsLdr != null;

    this.clsLdr = clsLdr;
  }

  /**
   * @param name Test suite name.
   * @param clsLdr Tests class loader.
   */
  public GridJunit3TestSuite(String name, ClassLoader clsLdr) {
    this(name);

    assert clsLdr != null;

    this.clsLdr = clsLdr;
  }

  /**
   * Test suite for one class.
   *
   * @param cls Class for test suite.
   * @param clsLdr Tests class loader.
   */
  public GridJunit3TestSuite(Class<? extends TestCase> cls, ClassLoader clsLdr) {
    this(cls);

    assert clsLdr != null;

    this.clsLdr = clsLdr;
  }

  /**
   * Test suite for a given test class with specified test name.
   *
   * @param cls Test class.
   * @param name Test name.
   * @param clsLdr Tests class loader.
   */
  public GridJunit3TestSuite(Class<? extends TestCase> cls, String name, ClassLoader clsLdr) {
    this(cls, name);

    assert clsLdr != null;

    this.clsLdr = clsLdr;
  }

  /**
   * Copies non-distributed test suite into distributed one.
   *
   * @param suite Test suite to copy.
   * @param clsLdr Tests class loader.
   */
  public GridJunit3TestSuite(TestSuite suite, ClassLoader clsLdr) {
    this(suite);

    assert clsLdr != null;

    this.clsLdr = clsLdr;
  }

  /**
   * Sets path to GridGain configuration file. By default {@code
   * {GRIDGAIN_HOME}/config/junit/junit-spring.xml} is used.
   *
   * @param cfgPath Path to GridGain configuration file.
   */
  public void setConfigurationPath(String cfgPath) {
    this.cfgPath = cfgPath;
  }

  /**
   * Gets path to GridGain configuration file. By default {@code
   * {GRIDGAIN_HOME}/config/junit/junit-spring.xml} is used.
   *
   * @return Path to GridGain configuration file.
   */
  public String getConfigurationPath() {
    return cfgPath;
  }

  /**
   * Disables GridGain. If set to {@code true} then this suite will execute locally as if GridGain
   * was not in a picture at all.
   *
   * @param disabled If set to {@code true} then this suite will execute locally as if GridGain was
   *     not in a picture at all.
   */
  public void setDisabled(boolean disabled) {
    isDisabled = disabled;
  }

  /**
   * Gets flag indicating whether GridGain should be enabled or not. If set to {@code true} then
   * this suite will execute locally as if GridGain was not in a picture at all.
   *
   * @return Flag indicating whether GridGain should be enabled or not. If set to {@code true} then
   *     this suite will execute locally as if GridGain was not in a picture at all.
   */
  public boolean isDisabled() {
    return isDisabled;
  }

  /**
   * Sets name of class for routing JUnit tests. By default {@link #DFLT_JUNIT_ROUTER} class name is
   * used.
   *
   * @param routerClsName Junit test router class name.
   */
  public void setRouterClassName(String routerClsName) {
    this.routerClsName = routerClsName;
  }

  /**
   * Gets JUnit test router class name.
   *
   * @return JUnit test router class name.
   */
  public String getRouterClassName() {
    return routerClsName;
  }

  /**
   * Sets router class. By default {@link GridTestRouterAdapter} is used.
   *
   * @param routerCls Router class to use for test routing.
   */
  public void setRouterClass(Class<? extends GridTestRouter> routerCls) {
    this.routerCls = routerCls;
  }

  /**
   * Gets router class used for test routing.
   *
   * @return Router class used for test routing.
   */
  public Class<? extends GridTestRouter> getRouterClass() {
    return routerCls;
  }

  /**
   * Gets identical suite for local (non-distributed) execution.
   *
   * @return Local suite.
   */
  public TestSuite getLocalCopy() {
    return copy;
  }

  /** {@inheritDoc} */
  @Override
  public void setName(String name) {
    if (copy != null) {
      copy.setName(name);
    }

    super.setName(name);
  }

  /**
   * Gets timeout for running distributed test suite.
   *
   * @return Timeout for tests.
   */
  public long getTimeout() {
    return timeout;
  }

  /**
   * Sets timeout for running distributed test suite. By default, test execution does not expire.
   *
   * @param timeout Timeout for tests.
   */
  public void setTimeout(long timeout) {
    this.timeout = timeout;
  }

  /** {@inheritDoc} */
  @Override
  public Test testAt(int index) {
    return isDisabled ? copy.testAt(index) : super.testAt(index);
  }

  /** {@inheritDoc} */
  @Override
  public int testCount() {
    return isDisabled ? copy.testCount() : super.testCount();
  }

  /** {@inheritDoc} */
  @Override
  public Enumeration<Test> tests() {
    return isDisabled ? copy.tests() : super.tests();
  }

  /**
   * The added suite will be always executed locally, but in parallel with other locally or remotely
   * running tests. This comes handy for tests that cannot be distributed for some environmental
   * reasons, but still would benefit from parallel execution.
   *
   * <p>Note, that local suites will be executed on local node even if grid topology only allows
   * remote nodes.
   *
   * @param localSuite Test to execute locally in parallel with other local or distributed tests.
   */
  @SuppressWarnings({"TypeMayBeWeakened"})
  public void addTest(GridJunit3LocalTestSuite localSuite) {
    if (!locTests.contains(localSuite.getName())) {
      locTests.add(localSuite.getName());
    }

    addTest((Test) localSuite);
  }

  /**
   * Adds a test to be executed on the grid. In case of test suite, all tests inside of test suite
   * will be executed sequentially on some remote node.
   *
   * @param test Test to add.
   */
  @Override
  public void addTest(Test test) {
    if (copy == null) {
      copy = new TestSuite(getName());
    }

    // Add test to the list of local ones.
    if (test instanceof GridJunit3LocalTestSuite) {
      String testName = ((TestSuite) test).getName();

      if (!locTests.contains(testName)) {
        locTests.add(testName);
      }
    }

    if (test instanceof GridJunit3TestSuite) {
      copy.addTest(((GridJunit3TestSuite) test).copy);

      super.addTest(new GridJunit3TestSuiteProxy(((GridJunit3TestSuite) test).copy, factory));
    } else if (test instanceof GridJunit3TestSuiteProxy) {
      copy.addTest(((GridJunit3TestSuiteProxy) test).getOriginal());

      super.addTest(test);
    } else if (test instanceof GridJunit3TestCaseProxy) {
      copy.addTest(((GridJunit3TestCaseProxy) test).getGridGainJunit3OriginalTestCase());

      super.addTest(test);
    } else if (test instanceof TestSuite) {
      copy.addTest(test);

      super.addTest(new GridJunit3TestSuiteProxy((TestSuite) test, factory));
    } else {
      assert test instanceof TestCase
          : "Test must be either instance of TestSuite or TestCase: " + test;

      copy.addTest(test);

      super.addTest(factory.createProxy((TestCase) test));
    }
  }

  /**
   * Creates JUnit test router. Note that router must have a no-arg constructor.
   *
   * @return JUnit router instance.
   */
  @SuppressWarnings({"unchecked"})
  private GridTestRouter createRouter() {
    try {
      if (routerCls == null) {
        routerCls = (Class<? extends GridTestRouter>) Class.forName(routerClsName);
      } else {
        routerClsName = routerCls.getName();
      }

      return routerCls.newInstance();
    } catch (ClassNotFoundException e) {
      throw new GridRuntimeException("Failed to initialize JUnit router: " + routerClsName, e);
    } catch (IllegalAccessException e) {
      throw new GridRuntimeException("Failed to initialize JUnit router: " + routerClsName, e);
    } catch (InstantiationException e) {
      throw new GridRuntimeException("Failed to initialize JUnit router: " + routerClsName, e);
    }
  }

  /**
   * Runs all tests belonging to this test suite on the grid.
   *
   * @param result Test result collector.
   */
  @Override
  public void run(TestResult result) {
    if (isDisabled) {
      copy.run(result);
    } else {
      GridTestRouter router = createRouter();

      Grid grid = startGrid();

      try {
        List<GridTaskFuture<?>> futs = new ArrayList<GridTaskFuture<?>>(testCount());

        List<GridJunit3SerializableTest> tests =
            new ArrayList<GridJunit3SerializableTest>(testCount());

        for (int i = 0; i < testCount(); i++) {
          Test junit = testAt(i);

          GridJunit3SerializableTest test;

          if (junit instanceof TestSuite) {
            test = new GridJunit3SerializableTestSuite((TestSuite) junit);
          } else {
            assert junit instanceof TestCase
                : "Test must be either TestSuite or TestCase: " + junit;

            test = new GridJunit3SerializableTestCase((TestCase) junit);
          }

          tests.add(test);

          if (clsLdr == null) {
            clsLdr = U.detectClassLoader(junit.getClass());
          }

          futs.add(
              grid.execute(
                  new GridJunit3Task(junit.getClass(), clsLdr),
                  new GridJunit3Argument(router, test, locTests.contains(test.getName())),
                  timeout));
        }

        for (int i = 0; i < testCount(); i++) {
          GridTaskFuture<?> fut = futs.get(i);

          GridJunit3SerializableTest origTest = tests.get(i);

          try {
            GridJunit3SerializableTest resTest = (GridJunit3SerializableTest) fut.get();

            origTest.setResult(resTest);

            origTest.getTest().run(result);
          } catch (GridException e) {
            handleFail(result, origTest, e);
          }
        }
      } finally {
        stopGrid();
      }
    }
  }

  /**
   * Handles test fail.
   *
   * @param result Test result.
   * @param origTest Original JUnit test.
   * @param e Exception thrown from grid.
   */
  private void handleFail(TestResult result, GridJunit3SerializableTest origTest, Throwable e) {
    // Simulate that all tests were run.
    origTest.getTest().run(result);

    // For the tests suite we assume that all tests failed because
    // entire test suite execution failed and there is no way to get
    // broken tests.
    if (origTest.getTest() instanceof GridJunit3TestSuiteProxy) {
      TestSuite suite = (((TestSuite) origTest.getTest()));

      for (int j = 0; j < suite.testCount(); j++) {
        result.addError(suite.testAt(j), e);
      }
    } else if (origTest.getTest() instanceof GridJunit3TestCaseProxy) {
      result.addError(origTest.getTest(), e);
    }
  }

  /**
   * Starts Grid instance. Note that if grid is already started, then it will be looked up and
   * returned from this method.
   *
   * @return Started grid.
   */
  private Grid startGrid() {
    Properties props = System.getProperties();

    gridName = props.getProperty(GRIDGAIN_NAME.name());

    if (!props.containsKey(GRIDGAIN_NAME.name()) || G.state(gridName) != GridFactoryState.STARTED) {
      selfStarted = true;

      // Set class loader for the spring.
      ClassLoader curCl = Thread.currentThread().getContextClassLoader();

      // Add no-op logger to remove no-appender warning.
      Appender app = new NullAppender();

      Logger.getRootLogger().addAppender(app);

      try {
        Thread.currentThread().setContextClassLoader(getClass().getClassLoader());

        Grid grid = G.start(cfgPath);

        gridName = grid.name();

        System.setProperty(GRIDGAIN_NAME.name(), grid.name());

        return grid;
      } catch (GridException e) {
        throw new GridRuntimeException("Failed to start grid: " + cfgPath, e);
      } finally {
        Logger.getRootLogger().removeAppender(app);

        Thread.currentThread().setContextClassLoader(curCl);
      }
    }

    return G.grid(gridName);
  }

  /** Stops grid only if it was started by this test suite. */
  private void stopGrid() {
    // Only stop grid if it was started here.
    if (selfStarted) {
      G.stop(gridName, true);
    }
  }
}
  /**
   * Main function
   *
   * @param args
   */
  public static void main(String[] args) {
    JWhiteBoard whiteBoard = null;
    String props = null;
    boolean no_channel = false;
    boolean jmx = true;
    boolean use_state = false;
    String group_name = null;
    long state_timeout = 5000;
    boolean use_unicasts = false;
    String name = null;
    boolean send_own_state_on_merge = true;
    AddressGenerator generator = null;

    // Get startup parameters for JWhiteBoard
    for (int i = 0; i < args.length; i++) {
      // Show help
      if ("-help".equals(args[i])) {
        help();
        return;
      }
      // Properties for Channel
      if ("-props".equals(args[i])) {
        props = args[++i];
        continue;
      }
      // If existed, use no channel
      if ("-no_channel".equals(args[i])) {
        no_channel = true;
        continue;
      }
      // Use Java Management Extensions or not
      if ("-jmx".equals(args[i])) {
        jmx = Boolean.parseBoolean(args[++i]);
        continue;
      }
      // If existed, set name for the Group
      if ("-clustername".equals(args[i])) {
        group_name = args[++i];
        continue;
      }
      // If existed, save Group's state
      if ("-state".equals(args[i])) {
        use_state = true;
        continue;
      }
      // If existed, set timeout for state
      if ("-timeout".equals(args[i])) {
        state_timeout = Long.parseLong(args[++i]);
        continue;
      }
      if ("-bind_addr".equals(args[i])) {
        System.setProperty("jgroups.bind_addr", args[++i]);
        continue;
      }
      if ("-use_unicasts".equals(args[i])) {
        use_unicasts = true;
        continue;
      }
      if ("-name".equals(args[i])) {
        name = args[++i];
        continue;
      }
      if ("-send_own_state_on_merge".equals(args[i])) {
        send_own_state_on_merge = Boolean.getBoolean(args[++i]);
        continue;
      }
      if ("-uuid".equals(args[i])) {
        generator = new OneTimeAddressGenerator(Long.valueOf(args[++i]));
        continue;
      }

      help();
      return;
    }

    try {
      whiteBoard =
          new JWhiteBoard(
              props,
              no_channel,
              jmx,
              use_state,
              state_timeout,
              use_unicasts,
              name,
              send_own_state_on_merge,
              generator);
      if (group_name == null) whiteBoard.setGroupName(group_name);
      whiteBoard.go();
    } catch (Throwable e) {
      e.printStackTrace(System.err);
      System.exit(0);
    }
  }
public class BaseTestJAXBTranslator implements Runnable {

  private static final Logger LOG = Logger.getLogger(BaseTestJAXBTranslator.class);

  private static final String DEFAULT_FILENAMES_PATTERN = ".*\\.xml";

  private static final String DEFAULT_FILENAMES_TO_OMIT_PATTERN = null;

  private static final String DIFFXML_CLASS_NAME = "org.diffxml.diffxml.DiffFactory";

  protected static boolean performXMLDiff =
      Boolean.getBoolean(BaseTestJAXBTranslator.class.getName() + ".performXMLDiff");

  protected static boolean reportDiffAsXMLIndented =
      Boolean.getBoolean(BaseTestJAXBTranslator.class.getName() + ".reportDiffAsXMLIndented");

  protected static String diffCommand =
      System.getProperty(BaseTestJAXBTranslator.class.getName() + ".diffCommand", "diff -w")
          + " "; // Make sure there's a trailing space
  // to keep the file names distinct from the command.

  protected static Translator translator;
  /** The {@link StatisticsBean} instance used to report performance data. */
  protected static StatisticsBean statisticsBean;

  protected static ServiceValidator serviceValidator;

  static {
    System.out.println("Working dir:" + System.getProperty("user.dir"));
    try {

      translator = TranslatorFactory.buildTranslator();

      statisticsBean = StatisticsBeanFactory.buildStatisticsBean();

      serviceValidator = ServiceValidatorFactory.buildServiceValidator();

    } catch (ToolkitException e) {

      LOG.error(e);
      throw new ExceptionInInitializerError(e);
    }
  }

  public void doTest() throws FileNotFoundException, ToolkitException {

    String sampleFilesDirectory =
        System.getProperty(
            BaseTestJAXBTranslator.class.getName() + ".sampleFilesDir",
            "src/test/data/sampleMessages");
    if (sampleFilesDirectory == null) {
      Assert.fail(
          "Test failed because system property "
              + BaseTestJAXBTranslator.class.getName()
              + ".sampleFilesDir was not set. It must be set to a directory containing sample NCIP messages.");
    }

    String fileNamesPattern =
        System.getProperty(
            BaseTestJAXBTranslator.class.getName() + ".fileNamesPattern",
            DEFAULT_FILENAMES_PATTERN);

    String fileNamesToOmitPattern =
        System.getProperty(
            BaseTestJAXBTranslator.class.getName() + ".fileNamesToOmitPattern",
            DEFAULT_FILENAMES_TO_OMIT_PATTERN);

    int iterations = 1;

    String iterationsString =
        System.getProperty(BaseTestJAXBTranslator.class.getName() + ".iterations");
    if (iterationsString != null) {

      iterations = Integer.parseInt(iterationsString);
    }

    File dir = new File(sampleFilesDirectory);

    FileFilter fileFilter =
        new FileFilter() {
          public boolean accept(File file) {
            return file.isFile();
          }
        };

    StringBuilder failuresList = new StringBuilder();
    File[] files = dir.listFiles(fileFilter);
    long initTranslateStreamToSvcElapsedTime = 0;
    long respTranslateStreamToSvcElapsedTime = 0;
    long initTranslateSvcToStreamElapsedTime = 0;
    long respTranslateSvcToStreamElapsedTime = 0;
    long initMsgStreamToSvcCount = 0;
    long respMsgStreamToSvcCount = 0;
    long initMsgSvcToStreamCount = 0;
    long respMsgSvcToStreamCount = 0;

    if (files != null) {

      long startTime = 0;
      long endTime = 0;

      for (int iteration = 0; iteration < iterations; ++iteration) {

        for (File file : files) {

          String fileName = file.getName();

          if ((fileNamesPattern == null
                  || fileNamesPattern.length() == 0
                  || fileName.matches(fileNamesPattern))
              && (fileNamesToOmitPattern == null
                  || fileNamesToOmitPattern.length() == 0
                  || !fileName.matches(fileNamesToOmitPattern))) {

            InputStream inStream = new FileInputStream(file);

            LOG.info("Testing " + fileName);

            ServiceContext serviceContext = serviceValidator.getInitialServiceContext();

            if (fileName.contains("Response")) {

              try {

                startTime = System.currentTimeMillis();

                NCIPResponseData responseData =
                    translator.createResponseData(serviceContext, inStream);

                endTime = System.currentTimeMillis();
                respTranslateStreamToSvcElapsedTime += (endTime - startTime);
                respMsgStreamToSvcCount++;

                Assert.assertNotNull(
                    "createResponseData returned null for " + fileName + ".", responseData);

                if (responseData != null) {

                  try {

                    startTime = System.currentTimeMillis();

                    InputStream responseStream =
                        translator.createResponseMessageStream(serviceContext, responseData);

                    endTime = System.currentTimeMillis();
                    respTranslateSvcToStreamElapsedTime += (endTime - startTime);
                    respMsgSvcToStreamCount++;

                    Assert.assertNotNull(responseStream);

                    if (performXMLDiff) {

                      performXMLDiff(responseStream, failuresList, file);
                    }

                  } catch (ServiceException e) {

                    LOG.debug(
                        "Exception in createResponseMessageStream for '" + fileName + "'.", e);
                    failuresList.append(
                        collectException("createResponseMessageStream", fileName, e));

                  } catch (ValidationException e) {

                    LOG.debug(
                        "Exception in createResponseMessageStream for '" + fileName + "'.", e);
                    failuresList.append(
                        collectException("createResponseMessageStream", fileName, e));
                  }
                }

              } catch (ServiceException e) {

                LOG.debug("Exception in createResponseData for '" + fileName + "'.", e);
                failuresList.append(collectException("createResponseData", fileName, e));

              } catch (ValidationException e) {

                LOG.debug("Exception in createResponseData for '" + fileName + "'.", e);
                failuresList.append(collectException("createResponseData", fileName, e));
              }

            } else {

              try {

                startTime = System.currentTimeMillis();

                NCIPInitiationData initiationData =
                    translator.createInitiationData(serviceContext, inStream);

                endTime = System.currentTimeMillis();
                initTranslateStreamToSvcElapsedTime += (endTime - startTime);
                initMsgStreamToSvcCount++;

                Assert.assertNotNull(
                    "createInitiationData returned null for " + fileName + ".", initiationData);

                if (initiationData != null) {

                  try {

                    startTime = System.currentTimeMillis();

                    InputStream initiationStream =
                        translator.createInitiationMessageStream(serviceContext, initiationData);

                    Assert.assertNotNull(initiationStream);

                    endTime = System.currentTimeMillis();
                    initTranslateSvcToStreamElapsedTime += (endTime - startTime);
                    initMsgSvcToStreamCount++;

                    if (performXMLDiff) {

                      performXMLDiff(initiationStream, failuresList, file);
                    }

                  } catch (ServiceException e) {

                    LOG.debug(
                        "Exception in createInitiationMessageStream for '" + fileName + "'.", e);
                    failuresList.append(
                        collectException("createInitiationMessageStream", fileName, e));

                  } catch (ValidationException e) {

                    LOG.debug(
                        "Exception in createInitiationMessageStream for '" + fileName + "'.", e);
                    failuresList.append(
                        collectException("createInitiationMessageStream", fileName, e));
                  }
                }

              } catch (ServiceException e) {

                LOG.debug("Exception in createInitiationData for '" + fileName + "'.", e);
                failuresList.append(collectException("createInitiationData", fileName, e));

              } catch (ValidationException e) {

                LOG.debug("Exception in createInitiationData for '" + fileName + "'.", e);
                failuresList.append(collectException("createInitiationData", fileName, e));
              }
            }
          }
        }
      }

      Assert.assertTrue(
          "One or more messages failed:" + System.getProperty("line.separator") + failuresList,
          failuresList.length() == 0);

    } else {
      Assert.fail(
          "No files were found in "
              + sampleFilesDirectory
              + ". Note: do not include a trailing slash in the path."
              + "Working directory: "
              + System.getProperty("user.dir"));
    }

    LOG.info(
        "Elapsed time for translating stream-to-service for initiation messages: "
            + initTranslateStreamToSvcElapsedTime
            + " for "
            + initMsgStreamToSvcCount
            + " messages; average "
            + computeAverage(initTranslateStreamToSvcElapsedTime, initMsgStreamToSvcCount));
    LOG.info(
        "Elapsed time for translating stream-to-service for response messages: "
            + respTranslateStreamToSvcElapsedTime
            + " for "
            + respMsgStreamToSvcCount
            + " messages; average "
            + computeAverage(respTranslateStreamToSvcElapsedTime, respMsgStreamToSvcCount));
    LOG.info(
        "Elapsed time for translating service-to-stream for initiation messages: "
            + initTranslateSvcToStreamElapsedTime
            + " for "
            + initMsgSvcToStreamCount
            + " messages; average "
            + computeAverage(initTranslateSvcToStreamElapsedTime, initMsgSvcToStreamCount));
    LOG.info(
        "Elapsed time for translating service-to-stream for response messages: "
            + respTranslateSvcToStreamElapsedTime
            + " for "
            + respMsgSvcToStreamCount
            + " messages; average "
            + computeAverage(respTranslateSvcToStreamElapsedTime, respMsgSvcToStreamCount));

    String statsReport = statisticsBean.createCSVReport();
    System.out.println(statsReport);
  }

  protected float computeAverage(long dividend, long divisor) {

    if (divisor != 0) {

      return dividend / divisor;

    } else {

      return Float.MAX_VALUE;
    }
  }

  protected static StringBuilder collectException(String methodName, String fileName, Throwable e) {

    StringBuilder message = new StringBuilder(ServiceHelper.convertExceptionToString(e));
    message
        .append("' in '")
        .append(methodName)
        .append("' converting the sample file ")
        .append(fileName)
        .append(".")
        .append(System.getProperty("line.separator"));

    return message;
  }

  public static void main(String[] args) {

    Thread threads[];

    int numThreads = 1;
    String numThreadsString =
        System.getProperty(BaseTestJAXBTranslator.class.getName() + ".threads");
    if (numThreadsString != null) {

      numThreads = Integer.parseInt(numThreadsString);
    }

    threads = new Thread[numThreads];

    for (int i = 0; i < threads.length; ++i) {

      threads[i] = new Thread(new BaseTestJAXBTranslator());
    }

    for (int i = 0; i < threads.length; ++i) {

      threads[i].start();
    }

    for (int i = 0; i < threads.length; ++i) {
      try {
        threads[i].join();

      } catch (InterruptedException e) {

        LOG.info("Thread " + i + " interrupted; will wait for remaining threads.", e);
      }
    }

    String statsReport = statisticsBean.createCSVReport();
    System.out.println(statsReport);
  }

  protected void performXMLDiff(InputStream msgStream, StringBuilder failuresList, File file)
      throws ServiceException {

    String fileName = file.getName();

    try {

      File prettyPrintedXMLFile = File.createTempFile("BaseTestJAXBTranslator", ".xml");
      prettyPrintedXMLFile.deleteOnExit();

      Writer outWriter = null;
      try {

        outWriter = new FileWriter(prettyPrintedXMLFile);

      } catch (IOException e) {

        throw new ServiceException(ServiceError.RUNTIME_ERROR, e);
      }

      ToolkitHelper.prettyPrintXML(msgStream, outWriter);

      // Compare the two XML files
      // Because the org.diffxml.diffxml uses a GNU License, not an MIT license,
      // it is not provided as part of the Toolkit, we use reflection to invoke
      // it here so we can avoid problems compiling this class in the absence of that
      // package. Note that, if the package is absent this "diff" will be silently
      // skipped.
      try {
        // The following 3 lines or code are equivalent to this:
        // Diff diffInstance = DiffFactory.createDiff();
        Class<?> diffFactoryClass = Class.forName(DIFFXML_CLASS_NAME);

        try {

          Method createDiffMethod = diffFactoryClass.getMethod("createDiff");
          Object diffInstance = createDiffMethod.invoke(null);

          // The following 2 lines of code are equivalent to this:
          // Document delta = diffInstance.diff(file, temp);
          Method diffMethod = diffInstance.getClass().getMethod("diff", File.class, File.class);
          Document deltaDocument =
              (Document) diffMethod.invoke(diffInstance, file, prettyPrintedXMLFile);

          if (deltaDocument.getDocumentElement().hasChildNodes()) {

            if (reportDiffAsXMLIndented) {

              OutputStream byteStream = new ByteArrayOutputStream();
              // DOMOps.outputXMLIndented(deltaDocument, byteStream);
              Class<?> DOMOpsClass = Class.forName("org.diffxml.diffxml.DOMOps");
              Method outputXMLIndentedMethod =
                  DOMOpsClass.getMethod("outputXMLIndented", Document.class, OutputStream.class);
              outputXMLIndentedMethod.invoke(null, deltaDocument, byteStream);

              failuresList
                  .append(
                      new StringBuilder()
                          .append("performXMLDiff detected differences for ")
                          .append(fileName)
                          .append(":\n")
                          .append('\t')
                          .append("Result XML file does not match input:\n")
                          .append(byteStream.toString()))
                  .append('\n');

            } else {

              try {
                String[] parms = {file.getAbsolutePath(), prettyPrintedXMLFile.getAbsolutePath()};
                String diffOutput = runCmd(diffCommand, parms);
                if (diffOutput != null && diffOutput.length() > 0) {

                  failuresList.append(
                      new StringBuilder()
                          .append("performXMLDiff detected differences for ")
                          .append(fileName)
                          .append(":\n")
                          .append('\t')
                          .append("Input XML file (on the left) is not matched ")
                          .append("by result XML file (on the right):\n")
                          .append(diffOutput));
                }

              } catch (ServiceException e) {

                failuresList.append(collectException("performXMLDiff", fileName, e));
              }
            }
          }

        } catch (ClassNotFoundException e) {

          failuresList.append(collectException("performXMLDiff", fileName, e));

        } catch (InvocationTargetException e) {

          failuresList.append(collectException("performXMLDiff", fileName, e));

        } catch (NoSuchMethodException e) {

          failuresList.append(collectException("performXMLDiff", fileName, e));

        } catch (IllegalAccessException e) {

          failuresList.append(collectException("performXMLDiff", fileName, e));

        } catch (Exception e) {

          failuresList.append(collectException("performXMLDiff", fileName, e));
        }
      } catch (ClassNotFoundException e) {

        LOG.warn(
            "ClassNotFoundException trying to load "
                + DIFFXML_CLASS_NAME
                + ". Skipping diffXML comparison. Exception was:",
            e);

      } finally {

        prettyPrintedXMLFile.delete();
      }

    } catch (IOException e) {

      failuresList.append(collectException("performXMLDiff", fileName, e));
    }
  }

  public String runCmd(String command, String[] parms) throws ServiceException {
    String result = "";
    try {
      String osName = System.getProperty("os.name");
      LOG.debug("OS name: " + osName);
      String[] cmd;
      int nextParm;
      if (osName.equals("Windows NT") || osName.equals("Windows XP")) {
        cmd = new String[3 + parms.length];
        cmd[0] = "cmd.exe";
        cmd[1] = "/C";
        nextParm = 2;
      } else if (osName.equals("Windows 95")) {
        cmd = new String[3 + parms.length];
        cmd[0] = "command.com";
        cmd[1] = "/C";
        nextParm = 2;
      } else // Assuming that else is unix or unix-like in this regard of not needing an explicit
             // shell program.
      {
        cmd = new String[1 + parms.length];
        nextParm = 0;
      }

      // Example values to use for 'command' here:
      //   If using cygwin on a Windows OS:
      //     "c:\\cygwin\\bin\\telnet.exe 192.168.1.101"
      //     "c:\\cygwin\\bin\\expect.exe /tmp/telnettojbdesktop.exp"
      //   If using a Unix (I'm guessing here):
      //     "telnet 192.168.1.101"
      //     "expect /tmp/telnettojbdesktop.exp"
      cmd[nextParm++] = command;

      for (String p : parms) {
        cmd[nextParm++] = p;
      }

      if (LOG.isDebugEnabled()) {
        StringBuffer buffer = new StringBuffer();
        for (String a : cmd) {
          buffer.append(a).append(" ");
        }
        LOG.debug("Executing " + buffer.toString());
      }

      Runtime rt = Runtime.getRuntime();
      Process proc = rt.exec(cmd);

      StreamGobbler errorGobbler = new StreamGobbler(proc.getErrorStream(), "ERROR");

      StreamGobbler outputGobbler = new StreamGobbler(proc.getInputStream(), "OUTPUT");

      errorGobbler.start();
      outputGobbler.start();

      // This is the stream used to pass INPUT to the process. (It's called the "output stream"
      // because
      // it's an *output* stream from the perspective of this class.)
      OutputStream outputStream = proc.getOutputStream();

      int exitVal = proc.waitFor();
      if (exitVal != 0) {
        LOG.error("Exit value from " + cmd[2] + ": " + exitVal);
      }

      String stdErr = errorGobbler.getBuffer();
      if (stdErr.length() > 0) {
        result = stdErr;
      }

      String stdOut = outputGobbler.getBuffer();
      if (stdOut.length() > 0) {
        // It's intentional that stdOut replaces what was in stdErr if there *is* anything in
        // stdOut.
        result = stdOut;
      }
    } catch (Throwable t) {
      throw new ServiceException(ServiceError.RUNTIME_ERROR, t);
    }
    return result;
  }

  @Override
  public void run() {

    try {

      doTest();

    } catch (ToolkitException e) {

      e.printStackTrace();

    } catch (FileNotFoundException e) {

      e.printStackTrace();
    }
  }

  class StreamGobbler extends Thread {
    InputStream is;
    String type;
    StringBuffer stringBuffer = new StringBuffer();

    StreamGobbler(InputStream is, String type) {
      this.is = is;
      this.type = type;
    }

    public void run() {
      try {
        InputStreamReader isr = new InputStreamReader(is);
        BufferedReader br = new BufferedReader(isr);
        String line = null;
        while ((line = br.readLine()) != null) {
          stringBuffer.append(line).append(System.getProperty("line.separator"));
        }
      } catch (IOException ioe) {
        ioe.printStackTrace();
      }
    }

    public String getBuffer() {
      return stringBuffer.toString();
    }
  }
}
Exemple #12
0
  private void initializeRead(int imageIndex, J2KImageReadParamJava param, J2KMetadata metadata) {
    try {
      iis.mark();
      in = new IISRandomAccessIO(iis);

      // **** File Format ****
      // If the codestream is wrapped in the jp2 fileformat, Read the
      // file format wrapper
      ff = new FileFormatReader(in, metadata);
      ff.readFileFormat();
      in.seek(ff.getFirstCodeStreamPos());

      hi = new HeaderInfo();
      try {
        hd = new HeaderDecoder(in, j2krparam, hi);
      } catch (EOFException e) {
        throw new RuntimeException(I18N.getString("J2KReadState2"));
      } catch (IOException ioe) {
        throw new RuntimeException(ioe);
      }

      this.width = hd.getImgWidth();
      this.height = hd.getImgHeight();

      Rectangle sourceRegion = param.getSourceRegion();
      sourceOrigin = new Point();
      sourceRegion = new Rectangle(hd.getImgULX(), hd.getImgULY(), this.width, this.height);

      // if the subsample rate for components are not consistent
      boolean compConsistent = true;
      stepX = hd.getCompSubsX(0);
      stepY = hd.getCompSubsY(0);
      for (int i = 1; i < nComp; i++) {
        if (stepX != hd.getCompSubsX(i) || stepY != hd.getCompSubsY(i))
          throw new RuntimeException(I18N.getString("J2KReadState12"));
      }

      // Get minimum number of resolution levels available across
      // all tile-components.
      int minResLevels = hd.getDecoderSpecs().dls.getMin();

      // Set current resolution level.
      this.resolution = param != null ? param.getResolution() : minResLevels;
      if (resolution < 0 || resolution > minResLevels) {
        resolution = minResLevels;
      }

      // Convert source region to lower resolution level.
      if (resolution != minResLevels || stepX != 1 || stepY != 1) {
        sourceRegion =
            J2KImageReader.getReducedRect(sourceRegion, minResLevels, resolution, stepX, stepY);
      }

      destinationRegion = (Rectangle) sourceRegion.clone();

      J2KImageReader.computeRegionsWrapper(
          param,
          false,
          this.width,
          this.height,
          param.getDestination(),
          sourceRegion,
          destinationRegion);

      sourceOrigin = new Point(sourceRegion.x, sourceRegion.y);
      scaleX = param.getSourceXSubsampling();
      scaleY = param.getSourceYSubsampling();
      xOffset = param.getSubsamplingXOffset();
      yOffset = param.getSubsamplingYOffset();

      this.width = destinationRegion.width;
      this.height = destinationRegion.height;

      Point tileOffset = hd.getTilingOrigin(null);

      this.tileWidth = hd.getNomTileWidth();
      this.tileHeight = hd.getNomTileHeight();

      // Convert tile 0 to lower resolution level.
      if (resolution != minResLevels || stepX != 1 || stepY != 1) {
        Rectangle tileRect = new Rectangle(tileOffset);
        tileRect.width = tileWidth;
        tileRect.height = tileHeight;
        tileRect = J2KImageReader.getReducedRect(tileRect, minResLevels, resolution, stepX, stepY);
        tileOffset = tileRect.getLocation();
        tileWidth = tileRect.width;
        tileHeight = tileRect.height;
      }

      tileXOffset = tileOffset.x;
      tileYOffset = tileOffset.y;

      // Set the tile step sizes. These values are used because it
      // is possible that tiles will be empty. In particular at lower
      // resolution levels when subsampling is used this may be the
      // case. This method of calculation will work at least for
      // Profile-0 images.
      if (tileWidth * (1 << (minResLevels - resolution)) * stepX > hd.getNomTileWidth()) {
        tileStepX =
            (tileWidth * (1 << (minResLevels - resolution)) * stepX + hd.getNomTileWidth() - 1)
                / hd.getNomTileWidth();
      } else {
        tileStepX = 1;
      }

      if (tileHeight * (1 << (minResLevels - resolution)) * stepY > hd.getNomTileHeight()) {
        tileStepY =
            (tileHeight * (1 << (minResLevels - resolution)) * stepY + hd.getNomTileHeight() - 1)
                / hd.getNomTileHeight();
      } else {
        tileStepY = 1;
      }

      if (!destinationRegion.equals(sourceRegion)) noTransform = false;

      // **** Header decoder ****
      // Instantiate header decoder and read main header
      decSpec = hd.getDecoderSpecs();

      // **** Instantiate decoding chain ****
      // Get demixed bitdepths
      nComp = hd.getNumComps();

      int[] depth = new int[nComp];
      for (int i = 0; i < nComp; i++) depth[i] = hd.getOriginalBitDepth(i);

      // Get channel mapping
      ChannelDefinitionBox cdb = null;
      if (metadata != null)
        cdb = (ChannelDefinitionBox) metadata.getElement("JPEG2000ChannelDefinitionBox");

      channelMap = new int[nComp];
      if (cdb != null && metadata.getElement("JPEG2000PaletteBox") == null) {
        short[] assoc = cdb.getAssociation();
        short[] types = cdb.getTypes();
        short[] channels = cdb.getChannel();

        for (int i = 0; i < types.length; i++)
          if (types[i] == 0) channelMap[channels[i]] = assoc[i] - 1;
          else if (types[i] == 1 || types[i] == 2) channelMap[channels[i]] = channels[i];
      } else {
        for (int i = 0; i < nComp; i++) channelMap[i] = i;
      }

      // **** Bitstream reader ****
      try {
        boolean logJJ2000Messages = Boolean.getBoolean("jj2000.j2k.decoder.log");
        breader =
            BitstreamReaderAgent.createInstance(in, hd, j2krparam, decSpec, logJJ2000Messages, hi);
      } catch (IOException e) {
        throw new RuntimeException(
            I18N.getString("J2KReadState3")
                + " "
                + ((e.getMessage() != null) ? (":\n" + e.getMessage()) : ""));
      } catch (IllegalArgumentException e) {
        throw new RuntimeException(
            I18N.getString("J2KReadState4")
                + " "
                + ((e.getMessage() != null) ? (":\n" + e.getMessage()) : ""));
      }

      // **** Entropy decoder ****
      try {
        entdec = hd.createEntropyDecoder(breader, j2krparam);
      } catch (IllegalArgumentException e) {
        throw new RuntimeException(
            I18N.getString("J2KReadState5")
                + " "
                + ((e.getMessage() != null) ? (":\n" + e.getMessage()) : ""));
      }

      // **** ROI de-scaler ****
      try {
        roids = hd.createROIDeScaler(entdec, j2krparam, decSpec);
      } catch (IllegalArgumentException e) {
        throw new RuntimeException(
            I18N.getString("J2KReadState6")
                + " "
                + ((e.getMessage() != null) ? (":\n" + e.getMessage()) : ""));
      }

      // **** Dequantizer ****
      try {
        deq = hd.createDequantizer(roids, depth, decSpec);
      } catch (IllegalArgumentException e) {
        throw new RuntimeException(
            I18N.getString("J2KReadState7")
                + " "
                + ((e.getMessage() != null) ? (":\n" + e.getMessage()) : ""));
      }

      // **** Inverse wavelet transform ***
      try {
        // full page inverse wavelet transform
        invWT = InverseWT.createInstance(deq, decSpec);
      } catch (IllegalArgumentException e) {
        throw new RuntimeException(
            I18N.getString("J2KReadState8")
                + " "
                + ((e.getMessage() != null) ? (":\n" + e.getMessage()) : ""));
      }

      int res = breader.getImgRes();
      int mrl = decSpec.dls.getMin();
      invWT.setImgResLevel(res);

      // **** Data converter **** (after inverse transform module)
      converter = new ImgDataConverter(invWT, 0);

      // **** Inverse component transformation ****
      ictransf = new InvCompTransf(converter, decSpec, depth);

      // If the destination band is set used it
      sourceBands = j2krparam.getSourceBands();

      if (sourceBands == null) {
        sourceBands = new int[nComp];
        for (int i = 0; i < nComp; i++) sourceBands[i] = i;
      }

      nComp = sourceBands.length;

      destinationBands = j2krparam.getDestinationBands();
      if (destinationBands == null) {
        destinationBands = new int[nComp];
        for (int i = 0; i < nComp; i++) destinationBands[i] = i;
      }

      J2KImageReader.checkReadParamBandSettingsWrapper(
          param, hd.getNumComps(), destinationBands.length);

      levelShift = new int[nComp];
      minValues = new int[nComp];
      maxValues = new int[nComp];
      fracBits = new int[nComp];
      dataBlocks = new DataBlkInt[nComp];

      depth = new int[nComp];
      bandOffsets = new int[nComp];
      maxDepth = 0;
      isSigned = false;
      for (int i = 0; i < nComp; i++) {
        depth[i] = hd.getOriginalBitDepth(sourceBands[i]);
        if (depth[i] > maxDepth) maxDepth = depth[i];
        dataBlocks[i] = new DataBlkInt();

        // XXX: may need to change if ChannelDefinition is used to
        // define the color channels, such as BGR order
        bandOffsets[i] = i;
        if (hd.isOriginalSigned(sourceBands[i])) isSigned = true;
        else {
          levelShift[i] = 1 << (ictransf.getNomRangeBits(sourceBands[i]) - 1);
        }

        // Get the number of bits in the image, and decide what the max
        // value should be, depending on whether it is signed or not
        int nomRangeBits = ictransf.getNomRangeBits(sourceBands[i]);
        maxValues[i] = (1 << (isSigned == true ? (nomRangeBits - 1) : nomRangeBits)) - 1;
        minValues[i] = isSigned ? -(maxValues[i] + 1) : 0;

        fracBits[i] = ictransf.getFixedPoint(sourceBands[i]);
      }

      iis.reset();
    } catch (IllegalArgumentException e) {
      throw new RuntimeException(e.getMessage(), e);
    } catch (Error e) {
      if (e.getMessage() != null) throw new RuntimeException(e.getMessage(), e);
      else {
        throw new RuntimeException(I18N.getString("J2KReadState9"), e);
      }
    } catch (RuntimeException e) {
      if (e.getMessage() != null)
        throw new RuntimeException(I18N.getString("J2KReadState10") + " " + e.getMessage(), e);
      else {
        throw new RuntimeException(I18N.getString("J2KReadState10"), e);
      }
    } catch (Throwable e) {
      throw new RuntimeException(I18N.getString("J2KReadState10"), e);
    }
  }
Exemple #13
0
/**
 * This class implements a simple utility for creating files in the JAR (Java Archive) file format.
 * The JAR format is based on the ZIP file format, with optional meta-information stored in a
 * MANIFEST entry.
 */
public class Main {
  String program;
  PrintStream out, err;
  String fname, mname, ename, pname;
  String zname = "";
  String[] files;
  String rootjar = null;

  // An entryName(path)->File map generated during "expand", it helps to
  // decide whether or not an existing entry in a jar file needs to be
  // replaced, during the "update" operation.
  Map<String, File> entryMap = new HashMap<String, File>();

  // All files need to be added/updated.
  Set<File> entries = new LinkedHashSet<File>();

  // Directories specified by "-C" operation.
  Set<String> paths = new HashSet<String>();

  /*
   * cflag: create
   * uflag: update
   * xflag: xtract
   * tflag: table
   * vflag: verbose
   * flag0: no zip compression (store only)
   * Mflag: DO NOT generate a manifest file (just ZIP)
   * iflag: generate jar index
   */
  boolean cflag, uflag, xflag, tflag, vflag, flag0, Mflag, iflag;

  static final String MANIFEST_DIR = "META-INF/";
  static final String VERSION = "1.0";

  // valid values for Profile attribute
  private static final String[] PROFILES = {"compact1", "compact2", "compact3"};

  private static ResourceBundle rsrc;

  /**
   * If true, maintain compatibility with JDK releases prior to 6.0 by timestamping extracted files
   * with the time at which they are extracted. Default is to use the time given in the archive.
   */
  private static final boolean useExtractionTime =
      Boolean.getBoolean("sun.tools.jar.useExtractionTime");

  /** Initialize ResourceBundle */
  static {
    try {
      rsrc = ResourceBundle.getBundle("sun.tools.jar.resources.jar");
    } catch (MissingResourceException e) {
      throw new Error("Fatal: Resource for jar is missing");
    }
  }

  private String getMsg(String key) {
    try {
      return (rsrc.getString(key));
    } catch (MissingResourceException e) {
      throw new Error("Error in message file");
    }
  }

  private String formatMsg(String key, String arg) {
    String msg = getMsg(key);
    String[] args = new String[1];
    args[0] = arg;
    return MessageFormat.format(msg, (Object[]) args);
  }

  private String formatMsg2(String key, String arg, String arg1) {
    String msg = getMsg(key);
    String[] args = new String[2];
    args[0] = arg;
    args[1] = arg1;
    return MessageFormat.format(msg, (Object[]) args);
  }

  public Main(PrintStream out, PrintStream err, String program) {
    this.out = out;
    this.err = err;
    this.program = program;
  }

  /**
   * Creates a new empty temporary file in the same directory as the specified file. A variant of
   * File.createTempFile.
   */
  private static File createTempFileInSameDirectoryAs(File file) throws IOException {
    File dir = file.getParentFile();
    if (dir == null) dir = new File(".");
    return File.createTempFile("jartmp", null, dir);
  }

  private boolean ok;

  /** Starts main program with the specified arguments. */
  public synchronized boolean run(String args[]) {
    ok = true;
    if (!parseArgs(args)) {
      return false;
    }
    try {
      if (cflag || uflag) {
        if (fname != null) {
          // The name of the zip file as it would appear as its own
          // zip file entry. We use this to make sure that we don't
          // add the zip file to itself.
          zname = fname.replace(File.separatorChar, '/');
          if (zname.startsWith("./")) {
            zname = zname.substring(2);
          }
        }
      }
      if (cflag) {
        Manifest manifest = null;
        InputStream in = null;

        if (!Mflag) {
          if (mname != null) {
            in = new FileInputStream(mname);
            manifest = new Manifest(new BufferedInputStream(in));
          } else {
            manifest = new Manifest();
          }
          addVersion(manifest);
          addCreatedBy(manifest);
          if (isAmbiguousMainClass(manifest)) {
            if (in != null) {
              in.close();
            }
            return false;
          }
          if (ename != null) {
            addMainClass(manifest, ename);
          }
          if (pname != null) {
            if (!addProfileName(manifest, pname)) {
              if (in != null) {
                in.close();
              }
              return false;
            }
          }
        }
        OutputStream out;
        if (fname != null) {
          out = new FileOutputStream(fname);
        } else {
          out = new FileOutputStream(FileDescriptor.out);
          if (vflag) {
            // Disable verbose output so that it does not appear
            // on stdout along with file data
            // error("Warning: -v option ignored");
            vflag = false;
          }
        }
        expand(null, files, false);
        create(new BufferedOutputStream(out, 4096), manifest);
        if (in != null) {
          in.close();
        }
        out.close();
      } else if (uflag) {
        File inputFile = null, tmpFile = null;
        FileInputStream in;
        FileOutputStream out;
        if (fname != null) {
          inputFile = new File(fname);
          tmpFile = createTempFileInSameDirectoryAs(inputFile);
          in = new FileInputStream(inputFile);
          out = new FileOutputStream(tmpFile);
        } else {
          in = new FileInputStream(FileDescriptor.in);
          out = new FileOutputStream(FileDescriptor.out);
          vflag = false;
        }
        InputStream manifest = (!Mflag && (mname != null)) ? (new FileInputStream(mname)) : null;
        expand(null, files, true);
        boolean updateOk = update(in, new BufferedOutputStream(out), manifest, null);
        if (ok) {
          ok = updateOk;
        }
        in.close();
        out.close();
        if (manifest != null) {
          manifest.close();
        }
        if (ok && fname != null) {
          // on Win32, we need this delete
          inputFile.delete();
          if (!tmpFile.renameTo(inputFile)) {
            tmpFile.delete();
            throw new IOException(getMsg("error.write.file"));
          }
          tmpFile.delete();
        }
      } else if (tflag) {
        replaceFSC(files);
        if (fname != null) {
          list(fname, files);
        } else {
          InputStream in = new FileInputStream(FileDescriptor.in);
          try {
            list(new BufferedInputStream(in), files);
          } finally {
            in.close();
          }
        }
      } else if (xflag) {
        replaceFSC(files);
        if (fname != null && files != null) {
          extract(fname, files);
        } else {
          InputStream in =
              (fname == null) ? new FileInputStream(FileDescriptor.in) : new FileInputStream(fname);
          try {
            extract(new BufferedInputStream(in), files);
          } finally {
            in.close();
          }
        }
      } else if (iflag) {
        genIndex(rootjar, files);
      }
    } catch (IOException e) {
      fatalError(e);
      ok = false;
    } catch (Error ee) {
      ee.printStackTrace();
      ok = false;
    } catch (Throwable t) {
      t.printStackTrace();
      ok = false;
    }
    out.flush();
    err.flush();
    return ok;
  }

  /** Parses command line arguments. */
  boolean parseArgs(String args[]) {
    /* Preprocess and expand @file arguments */
    try {
      args = CommandLine.parse(args);
    } catch (FileNotFoundException e) {
      fatalError(formatMsg("error.cant.open", e.getMessage()));
      return false;
    } catch (IOException e) {
      fatalError(e);
      return false;
    }
    /* parse flags */
    int count = 1;
    try {
      String flags = args[0];
      if (flags.startsWith("-")) {
        flags = flags.substring(1);
      }
      for (int i = 0; i < flags.length(); i++) {
        switch (flags.charAt(i)) {
          case 'c':
            if (xflag || tflag || uflag || iflag) {
              usageError();
              return false;
            }
            cflag = true;
            break;
          case 'u':
            if (cflag || xflag || tflag || iflag) {
              usageError();
              return false;
            }
            uflag = true;
            break;
          case 'x':
            if (cflag || uflag || tflag || iflag) {
              usageError();
              return false;
            }
            xflag = true;
            break;
          case 't':
            if (cflag || uflag || xflag || iflag) {
              usageError();
              return false;
            }
            tflag = true;
            break;
          case 'M':
            Mflag = true;
            break;
          case 'v':
            vflag = true;
            break;
          case 'f':
            fname = args[count++];
            break;
          case 'm':
            mname = args[count++];
            break;
          case '0':
            flag0 = true;
            break;
          case 'i':
            if (cflag || uflag || xflag || tflag) {
              usageError();
              return false;
            }
            // do not increase the counter, files will contain rootjar
            rootjar = args[count++];
            iflag = true;
            break;
          case 'e':
            ename = args[count++];
            break;
          case 'p':
            pname = args[count++];
            break;
          default:
            error(formatMsg("error.illegal.option", String.valueOf(flags.charAt(i))));
            usageError();
            return false;
        }
      }
    } catch (ArrayIndexOutOfBoundsException e) {
      usageError();
      return false;
    }
    if (!cflag && !tflag && !xflag && !uflag && !iflag) {
      error(getMsg("error.bad.option"));
      usageError();
      return false;
    }
    /* parse file arguments */
    int n = args.length - count;
    if (n > 0) {
      int k = 0;
      String[] nameBuf = new String[n];
      try {
        for (int i = count; i < args.length; i++) {
          if (args[i].equals("-C")) {
            /* change the directory */
            String dir = args[++i];
            dir = (dir.endsWith(File.separator) ? dir : (dir + File.separator));
            dir = dir.replace(File.separatorChar, '/');
            while (dir.indexOf("//") > -1) {
              dir = dir.replace("//", "/");
            }
            paths.add(dir.replace(File.separatorChar, '/'));
            nameBuf[k++] = dir + args[++i];
          } else {
            nameBuf[k++] = args[i];
          }
        }
      } catch (ArrayIndexOutOfBoundsException e) {
        usageError();
        return false;
      }
      files = new String[k];
      System.arraycopy(nameBuf, 0, files, 0, k);
    } else if (cflag && (mname == null)) {
      error(getMsg("error.bad.cflag"));
      usageError();
      return false;
    } else if (uflag) {
      if ((mname != null) || (ename != null) || (pname != null)) {
        /* just want to update the manifest */
        return true;
      } else {
        error(getMsg("error.bad.uflag"));
        usageError();
        return false;
      }
    }
    return true;
  }

  /**
   * Expands list of files to process into full list of all files that can be found by recursively
   * descending directories.
   */
  void expand(File dir, String[] files, boolean isUpdate) {
    if (files == null) {
      return;
    }
    for (int i = 0; i < files.length; i++) {
      File f;
      if (dir == null) {
        f = new File(files[i]);
      } else {
        f = new File(dir, files[i]);
      }
      if (f.isFile()) {
        if (entries.add(f)) {
          if (isUpdate) entryMap.put(entryName(f.getPath()), f);
        }
      } else if (f.isDirectory()) {
        if (entries.add(f)) {
          if (isUpdate) {
            String dirPath = f.getPath();
            dirPath = (dirPath.endsWith(File.separator)) ? dirPath : (dirPath + File.separator);
            entryMap.put(entryName(dirPath), f);
          }
          expand(f, f.list(), isUpdate);
        }
      } else {
        error(formatMsg("error.nosuch.fileordir", String.valueOf(f)));
        ok = false;
      }
    }
  }

  /** Creates a new JAR file. */
  void create(OutputStream out, Manifest manifest) throws IOException {
    ZipOutputStream zos = new JarOutputStream(out);
    if (flag0) {
      zos.setMethod(ZipOutputStream.STORED);
    }
    if (manifest != null) {
      if (vflag) {
        output(getMsg("out.added.manifest"));
      }
      ZipEntry e = new ZipEntry(MANIFEST_DIR);
      e.setTime(System.currentTimeMillis());
      e.setSize(0);
      e.setCrc(0);
      zos.putNextEntry(e);
      e = new ZipEntry(MANIFEST_NAME);
      e.setTime(System.currentTimeMillis());
      if (flag0) {
        crc32Manifest(e, manifest);
      }
      zos.putNextEntry(e);
      manifest.write(zos);
      zos.closeEntry();
    }
    for (File file : entries) {
      addFile(zos, file);
    }
    zos.close();
  }

  private char toUpperCaseASCII(char c) {
    return (c < 'a' || c > 'z') ? c : (char) (c + 'A' - 'a');
  }

  /**
   * Compares two strings for equality, ignoring case. The second argument must contain only
   * upper-case ASCII characters. We don't want case comparison to be locale-dependent (else we have
   * the notorious "turkish i bug").
   */
  private boolean equalsIgnoreCase(String s, String upper) {
    assert upper.toUpperCase(java.util.Locale.ENGLISH).equals(upper);
    int len;
    if ((len = s.length()) != upper.length()) return false;
    for (int i = 0; i < len; i++) {
      char c1 = s.charAt(i);
      char c2 = upper.charAt(i);
      if (c1 != c2 && toUpperCaseASCII(c1) != c2) return false;
    }
    return true;
  }

  /** Updates an existing jar file. */
  boolean update(InputStream in, OutputStream out, InputStream newManifest, JarIndex jarIndex)
      throws IOException {
    ZipInputStream zis = new ZipInputStream(in);
    ZipOutputStream zos = new JarOutputStream(out);
    ZipEntry e = null;
    boolean foundManifest = false;
    boolean updateOk = true;

    if (jarIndex != null) {
      addIndex(jarIndex, zos);
    }

    // put the old entries first, replace if necessary
    while ((e = zis.getNextEntry()) != null) {
      String name = e.getName();

      boolean isManifestEntry = equalsIgnoreCase(name, MANIFEST_NAME);

      if ((jarIndex != null && equalsIgnoreCase(name, INDEX_NAME)) || (Mflag && isManifestEntry)) {
        continue;
      } else if (isManifestEntry && ((newManifest != null) || (ename != null) || (pname != null))) {
        foundManifest = true;
        if (newManifest != null) {
          // Don't read from the newManifest InputStream, as we
          // might need it below, and we can't re-read the same data
          // twice.
          FileInputStream fis = new FileInputStream(mname);
          boolean ambiguous = isAmbiguousMainClass(new Manifest(fis));
          fis.close();
          if (ambiguous) {
            return false;
          }
        }

        // Update the manifest.
        Manifest old = new Manifest(zis);
        if (newManifest != null) {
          old.read(newManifest);
        }
        if (!updateManifest(old, zos)) {
          return false;
        }
      } else {
        if (!entryMap.containsKey(name)) { // copy the old stuff
          // do our own compression
          ZipEntry e2 = new ZipEntry(name);
          e2.setMethod(e.getMethod());
          e2.setTime(e.getTime());
          e2.setComment(e.getComment());
          e2.setExtra(e.getExtra());
          if (e.getMethod() == ZipEntry.STORED) {
            e2.setSize(e.getSize());
            e2.setCrc(e.getCrc());
          }
          zos.putNextEntry(e2);
          copy(zis, zos);
        } else { // replace with the new files
          File f = entryMap.get(name);
          addFile(zos, f);
          entryMap.remove(name);
          entries.remove(f);
        }
      }
    }

    // add the remaining new files
    for (File f : entries) {
      addFile(zos, f);
    }
    if (!foundManifest) {
      if (newManifest != null) {
        Manifest m = new Manifest(newManifest);
        updateOk = !isAmbiguousMainClass(m);
        if (updateOk) {
          if (!updateManifest(m, zos)) {
            updateOk = false;
          }
        }
      } else if (ename != null || pname != null) {
        if (!updateManifest(new Manifest(), zos)) {
          updateOk = false;
        }
      }
    }
    zis.close();
    zos.close();
    return updateOk;
  }

  private void addIndex(JarIndex index, ZipOutputStream zos) throws IOException {
    ZipEntry e = new ZipEntry(INDEX_NAME);
    e.setTime(System.currentTimeMillis());
    if (flag0) {
      CRC32OutputStream os = new CRC32OutputStream();
      index.write(os);
      os.updateEntry(e);
    }
    zos.putNextEntry(e);
    index.write(zos);
    zos.closeEntry();
  }

  private boolean updateManifest(Manifest m, ZipOutputStream zos) throws IOException {
    addVersion(m);
    addCreatedBy(m);
    if (ename != null) {
      addMainClass(m, ename);
    }
    if (pname != null) {
      if (!addProfileName(m, pname)) {
        return false;
      }
    }
    ZipEntry e = new ZipEntry(MANIFEST_NAME);
    e.setTime(System.currentTimeMillis());
    if (flag0) {
      crc32Manifest(e, m);
    }
    zos.putNextEntry(e);
    m.write(zos);
    if (vflag) {
      output(getMsg("out.update.manifest"));
    }
    return true;
  }

  private String entryName(String name) {
    name = name.replace(File.separatorChar, '/');
    String matchPath = "";
    for (String path : paths) {
      if (name.startsWith(path) && (path.length() > matchPath.length())) {
        matchPath = path;
      }
    }
    name = name.substring(matchPath.length());

    if (name.startsWith("/")) {
      name = name.substring(1);
    } else if (name.startsWith("./")) {
      name = name.substring(2);
    }
    return name;
  }

  private void addVersion(Manifest m) {
    Attributes global = m.getMainAttributes();
    if (global.getValue(Attributes.Name.MANIFEST_VERSION) == null) {
      global.put(Attributes.Name.MANIFEST_VERSION, VERSION);
    }
  }

  private void addCreatedBy(Manifest m) {
    Attributes global = m.getMainAttributes();
    if (global.getValue(new Attributes.Name("Created-By")) == null) {
      String javaVendor = System.getProperty("java.vendor");
      String jdkVersion = System.getProperty("java.version");
      global.put(new Attributes.Name("Created-By"), jdkVersion + " (" + javaVendor + ")");
    }
  }

  private void addMainClass(Manifest m, String mainApp) {
    Attributes global = m.getMainAttributes();

    // overrides any existing Main-Class attribute
    global.put(Attributes.Name.MAIN_CLASS, mainApp);
  }

  private boolean addProfileName(Manifest m, String profile) {
    // check profile name
    boolean found = false;
    int i = 0;
    while (i < PROFILES.length) {
      if (profile.equals(PROFILES[i])) {
        found = true;
        break;
      }
      i++;
    }
    if (!found) {
      error(formatMsg("error.bad.pvalue", profile));
      return false;
    }

    // overrides any existing Profile attribute
    Attributes global = m.getMainAttributes();
    global.put(Attributes.Name.PROFILE, profile);
    return true;
  }

  private boolean isAmbiguousMainClass(Manifest m) {
    if (ename != null) {
      Attributes global = m.getMainAttributes();
      if ((global.get(Attributes.Name.MAIN_CLASS) != null)) {
        error(getMsg("error.bad.eflag"));
        usageError();
        return true;
      }
    }
    return false;
  }

  /** Adds a new file entry to the ZIP output stream. */
  void addFile(ZipOutputStream zos, File file) throws IOException {
    String name = file.getPath();
    boolean isDir = file.isDirectory();
    if (isDir) {
      name = name.endsWith(File.separator) ? name : (name + File.separator);
    }
    name = entryName(name);

    if (name.equals("") || name.equals(".") || name.equals(zname)) {
      return;
    } else if ((name.equals(MANIFEST_DIR) || name.equals(MANIFEST_NAME)) && !Mflag) {
      if (vflag) {
        output(formatMsg("out.ignore.entry", name));
      }
      return;
    }

    long size = isDir ? 0 : file.length();

    if (vflag) {
      out.print(formatMsg("out.adding", name));
    }
    ZipEntry e = new ZipEntry(name);
    e.setTime(file.lastModified());
    if (size == 0) {
      e.setMethod(ZipEntry.STORED);
      e.setSize(0);
      e.setCrc(0);
    } else if (flag0) {
      crc32File(e, file);
    }
    zos.putNextEntry(e);
    if (!isDir) {
      copy(file, zos);
    }
    zos.closeEntry();
    /* report how much compression occurred. */
    if (vflag) {
      size = e.getSize();
      long csize = e.getCompressedSize();
      out.print(formatMsg2("out.size", String.valueOf(size), String.valueOf(csize)));
      if (e.getMethod() == ZipEntry.DEFLATED) {
        long ratio = 0;
        if (size != 0) {
          ratio = ((size - csize) * 100) / size;
        }
        output(formatMsg("out.deflated", String.valueOf(ratio)));
      } else {
        output(getMsg("out.stored"));
      }
    }
  }

  /**
   * A buffer for use only by copy(InputStream, OutputStream). Not as clean as allocating a new
   * buffer as needed by copy, but significantly more efficient.
   */
  private byte[] copyBuf = new byte[8192];

  /**
   * Copies all bytes from the input stream to the output stream. Does not close or flush either
   * stream.
   *
   * @param from the input stream to read from
   * @param to the output stream to write to
   * @throws IOException if an I/O error occurs
   */
  private void copy(InputStream from, OutputStream to) throws IOException {
    int n;
    while ((n = from.read(copyBuf)) != -1) to.write(copyBuf, 0, n);
  }

  /**
   * Copies all bytes from the input file to the output stream. Does not close or flush the output
   * stream.
   *
   * @param from the input file to read from
   * @param to the output stream to write to
   * @throws IOException if an I/O error occurs
   */
  private void copy(File from, OutputStream to) throws IOException {
    InputStream in = new FileInputStream(from);
    try {
      copy(in, to);
    } finally {
      in.close();
    }
  }

  /**
   * Copies all bytes from the input stream to the output file. Does not close the input stream.
   *
   * @param from the input stream to read from
   * @param to the output file to write to
   * @throws IOException if an I/O error occurs
   */
  private void copy(InputStream from, File to) throws IOException {
    OutputStream out = new FileOutputStream(to);
    try {
      copy(from, out);
    } finally {
      out.close();
    }
  }

  /**
   * Computes the crc32 of a Manifest. This is necessary when the ZipOutputStream is in STORED mode.
   */
  private void crc32Manifest(ZipEntry e, Manifest m) throws IOException {
    CRC32OutputStream os = new CRC32OutputStream();
    m.write(os);
    os.updateEntry(e);
  }

  /** Computes the crc32 of a File. This is necessary when the ZipOutputStream is in STORED mode. */
  private void crc32File(ZipEntry e, File f) throws IOException {
    CRC32OutputStream os = new CRC32OutputStream();
    copy(f, os);
    if (os.n != f.length()) {
      throw new JarException(formatMsg("error.incorrect.length", f.getPath()));
    }
    os.updateEntry(e);
  }

  void replaceFSC(String files[]) {
    if (files != null) {
      for (int i = 0; i < files.length; i++) {
        files[i] = files[i].replace(File.separatorChar, '/');
      }
    }
  }

  @SuppressWarnings("serial")
  Set<ZipEntry> newDirSet() {
    return new HashSet<ZipEntry>() {
      public boolean add(ZipEntry e) {
        return ((e == null || useExtractionTime) ? false : super.add(e));
      }
    };
  }

  void updateLastModifiedTime(Set<ZipEntry> zes) throws IOException {
    for (ZipEntry ze : zes) {
      long lastModified = ze.getTime();
      if (lastModified != -1) {
        File f = new File(ze.getName().replace('/', File.separatorChar));
        f.setLastModified(lastModified);
      }
    }
  }

  /** Extracts specified entries from JAR file. */
  void extract(InputStream in, String files[]) throws IOException {
    ZipInputStream zis = new ZipInputStream(in);
    ZipEntry e;
    // Set of all directory entries specified in archive.  Disallows
    // null entries.  Disallows all entries if using pre-6.0 behavior.
    Set<ZipEntry> dirs = newDirSet();
    while ((e = zis.getNextEntry()) != null) {
      if (files == null) {
        dirs.add(extractFile(zis, e));
      } else {
        String name = e.getName();
        for (String file : files) {
          if (name.startsWith(file)) {
            dirs.add(extractFile(zis, e));
            break;
          }
        }
      }
    }

    // Update timestamps of directories specified in archive with their
    // timestamps as given in the archive.  We do this after extraction,
    // instead of during, because creating a file in a directory changes
    // that directory's timestamp.
    updateLastModifiedTime(dirs);
  }

  /** Extracts specified entries from JAR file, via ZipFile. */
  void extract(String fname, String files[]) throws IOException {
    ZipFile zf = new ZipFile(fname);
    Set<ZipEntry> dirs = newDirSet();
    Enumeration<? extends ZipEntry> zes = zf.entries();
    while (zes.hasMoreElements()) {
      ZipEntry e = zes.nextElement();
      InputStream is;
      if (files == null) {
        dirs.add(extractFile(zf.getInputStream(e), e));
      } else {
        String name = e.getName();
        for (String file : files) {
          if (name.startsWith(file)) {
            dirs.add(extractFile(zf.getInputStream(e), e));
            break;
          }
        }
      }
    }
    zf.close();
    updateLastModifiedTime(dirs);
  }

  /**
   * Extracts next entry from JAR file, creating directories as needed. If the entry is for a
   * directory which doesn't exist prior to this invocation, returns that entry, otherwise returns
   * null.
   */
  ZipEntry extractFile(InputStream is, ZipEntry e) throws IOException {
    ZipEntry rc = null;
    String name = e.getName();
    File f = new File(e.getName().replace('/', File.separatorChar));
    if (e.isDirectory()) {
      if (f.exists()) {
        if (!f.isDirectory()) {
          throw new IOException(formatMsg("error.create.dir", f.getPath()));
        }
      } else {
        if (!f.mkdirs()) {
          throw new IOException(formatMsg("error.create.dir", f.getPath()));
        } else {
          rc = e;
        }
      }

      if (vflag) {
        output(formatMsg("out.create", name));
      }
    } else {
      if (f.getParent() != null) {
        File d = new File(f.getParent());
        if (!d.exists() && !d.mkdirs() || !d.isDirectory()) {
          throw new IOException(formatMsg("error.create.dir", d.getPath()));
        }
      }
      try {
        copy(is, f);
      } finally {
        if (is instanceof ZipInputStream) ((ZipInputStream) is).closeEntry();
        else is.close();
      }
      if (vflag) {
        if (e.getMethod() == ZipEntry.DEFLATED) {
          output(formatMsg("out.inflated", name));
        } else {
          output(formatMsg("out.extracted", name));
        }
      }
    }
    if (!useExtractionTime) {
      long lastModified = e.getTime();
      if (lastModified != -1) {
        f.setLastModified(lastModified);
      }
    }
    return rc;
  }

  /** Lists contents of JAR file. */
  void list(InputStream in, String files[]) throws IOException {
    ZipInputStream zis = new ZipInputStream(in);
    ZipEntry e;
    while ((e = zis.getNextEntry()) != null) {
      /*
       * In the case of a compressed (deflated) entry, the entry size
       * is stored immediately following the entry data and cannot be
       * determined until the entry is fully read. Therefore, we close
       * the entry first before printing out its attributes.
       */
      zis.closeEntry();
      printEntry(e, files);
    }
  }

  /** Lists contents of JAR file, via ZipFile. */
  void list(String fname, String files[]) throws IOException {
    ZipFile zf = new ZipFile(fname);
    Enumeration<? extends ZipEntry> zes = zf.entries();
    while (zes.hasMoreElements()) {
      printEntry(zes.nextElement(), files);
    }
    zf.close();
  }

  /** Outputs the class index table to the INDEX.LIST file of the root jar file. */
  void dumpIndex(String rootjar, JarIndex index) throws IOException {
    File jarFile = new File(rootjar);
    Path jarPath = jarFile.toPath();
    Path tmpPath = createTempFileInSameDirectoryAs(jarFile).toPath();
    try {
      if (update(Files.newInputStream(jarPath), Files.newOutputStream(tmpPath), null, index)) {
        try {
          Files.move(tmpPath, jarPath, REPLACE_EXISTING);
        } catch (IOException e) {
          throw new IOException(getMsg("error.write.file"), e);
        }
      }
    } finally {
      Files.deleteIfExists(tmpPath);
    }
  }

  private HashSet<String> jarPaths = new HashSet<String>();

  /** Generates the transitive closure of the Class-Path attribute for the specified jar file. */
  List<String> getJarPath(String jar) throws IOException {
    List<String> files = new ArrayList<String>();
    files.add(jar);
    jarPaths.add(jar);

    // take out the current path
    String path = jar.substring(0, Math.max(0, jar.lastIndexOf('/') + 1));

    // class path attribute will give us jar file name with
    // '/' as separators, so we need to change them to the
    // appropriate one before we open the jar file.
    JarFile rf = new JarFile(jar.replace('/', File.separatorChar));

    if (rf != null) {
      Manifest man = rf.getManifest();
      if (man != null) {
        Attributes attr = man.getMainAttributes();
        if (attr != null) {
          String value = attr.getValue(Attributes.Name.CLASS_PATH);
          if (value != null) {
            StringTokenizer st = new StringTokenizer(value);
            while (st.hasMoreTokens()) {
              String ajar = st.nextToken();
              if (!ajar.endsWith("/")) { // it is a jar file
                ajar = path.concat(ajar);
                /* check on cyclic dependency */
                if (!jarPaths.contains(ajar)) {
                  files.addAll(getJarPath(ajar));
                }
              }
            }
          }
        }
      }
    }
    rf.close();
    return files;
  }

  /** Generates class index file for the specified root jar file. */
  void genIndex(String rootjar, String[] files) throws IOException {
    List<String> jars = getJarPath(rootjar);
    int njars = jars.size();
    String[] jarfiles;

    if (njars == 1 && files != null) {
      // no class-path attribute defined in rootjar, will
      // use command line specified list of jars
      for (int i = 0; i < files.length; i++) {
        jars.addAll(getJarPath(files[i]));
      }
      njars = jars.size();
    }
    jarfiles = jars.toArray(new String[njars]);
    JarIndex index = new JarIndex(jarfiles);
    dumpIndex(rootjar, index);
  }

  /** Prints entry information, if requested. */
  void printEntry(ZipEntry e, String[] files) throws IOException {
    if (files == null) {
      printEntry(e);
    } else {
      String name = e.getName();
      for (String file : files) {
        if (name.startsWith(file)) {
          printEntry(e);
          return;
        }
      }
    }
  }

  /** Prints entry information. */
  void printEntry(ZipEntry e) throws IOException {
    if (vflag) {
      StringBuilder sb = new StringBuilder();
      String s = Long.toString(e.getSize());
      for (int i = 6 - s.length(); i > 0; --i) {
        sb.append(' ');
      }
      sb.append(s).append(' ').append(new Date(e.getTime()).toString());
      sb.append(' ').append(e.getName());
      output(sb.toString());
    } else {
      output(e.getName());
    }
  }

  /** Prints usage message. */
  void usageError() {
    error(getMsg("usage"));
  }

  /** A fatal exception has been caught. No recovery possible */
  void fatalError(Exception e) {
    e.printStackTrace();
  }

  /** A fatal condition has been detected; message is "s". No recovery possible */
  void fatalError(String s) {
    error(program + ": " + s);
  }

  /** Print an output message; like verbose output and the like */
  protected void output(String s) {
    out.println(s);
  }

  /** Print an error mesage; like something is broken */
  protected void error(String s) {
    err.println(s);
  }

  /** Main routine to start program. */
  public static void main(String args[]) {
    Main jartool = new Main(System.out, System.err, "jar");
    System.exit(jartool.run(args) ? 0 : 1);
  }

  /**
   * An OutputStream that doesn't send its output anywhere, (but could). It's here to find the CRC32
   * of an input file, necessary for STORED mode in ZIP.
   */
  private static class CRC32OutputStream extends java.io.OutputStream {
    final CRC32 crc = new CRC32();
    long n = 0;

    CRC32OutputStream() {}

    public void write(int r) throws IOException {
      crc.update(r);
      n++;
    }

    public void write(byte[] b, int off, int len) throws IOException {
      crc.update(b, off, len);
      n += len;
    }

    /** Updates a ZipEntry which describes the data read by this output stream, in STORED mode. */
    public void updateEntry(ZipEntry e) {
      e.setMethod(ZipEntry.STORED);
      e.setSize(n);
      e.setCrc(crc.getValue());
    }
  }
}
  /**
   * Returns boolean value from system property or provided function.
   *
   * @param propName System property host.
   * @param dflt Function that returns {@code Boolean}.
   * @return {@code Boolean} value
   */
  public static boolean boolValue(String propName, boolean dflt) {
    String sysProp = getProperty(propName);

    return (sysProp != null && !sysProp.isEmpty()) ? Boolean.getBoolean(sysProp) : dflt;
  }
public class UnitTestObserverImpl implements UnitTestObserver {

  private static DUnitRun dunitRun = null;

  private static boolean dunitRecordResults = Boolean.getBoolean("dunitRecordResults");

  public UnitTestObserverImpl() {}

  public void incCurrentTestCount() {
    // TODO Auto-generated method stub
    SwarmBB.incCurrentTestCount();
  }

  public void testTypeDetected(TestType t) {
    // TODO Auto-generated method stub
    SwarmBB.setTestType(t);
  }

  public void totalTestCountDetected(int count) {
    // TODO Auto-generated method stub
    SwarmBB.setTotalTestCount(count);
  }

  public long getCurrentTestCount() {
    return SwarmBB.getCurrentTestCount();
  }

  public long getTotalTestCount() {
    return SwarmBB.getTotalTestCount();
  }

  private long startTime;
  private String testInProgress = null;
  String lastPassClass = null;
  String lastFailClass = null;
  String lastFailMethod = null;

  Throwable lastThrowable = null;

  public void startTest(Test test) {
    this.startTime = System.currentTimeMillis();
    String s = "zzzzzSTART " + test;
    Log.getLogWriter().info(s);
    testInProgress = test.toString();
  }

  public void endTest(Test test) {
    if (!dunitRecordResults) {
      Log.getLogWriter().info("Not recording results");
      return;
    }
    long delta = System.currentTimeMillis() - this.startTime;
    String s = "zzzzzEND " + test + " (took " + delta + "ms)";
    Log.getLogWriter().info(s);
    testInProgress = null;
    String className = getClassName(test);
    String methodName = getMethodName(test);

    if (className.equals(lastFailClass)) {
      if (methodName.equals(lastFailMethod)) {
        // Ok the test that just failed is ending
        recordFail(className, methodName, delta);
      } else {
        lastFailMethod = null;
        lastThrowable = null;
        // I think this means another method passed in a failing test
        recordPass(className, methodName, delta);
      }
      // Test failed, do nothing
      // recordFail = true;
      return;
    } else if (lastPassClass == null) {
      // The first test to pass
      lastPassClass = className;
      lastFailMethod = null;
      lastFailClass = null;
      recordPass(className, methodName, delta);
    } else if (!lastPassClass.equals(className)) {
      // Note that the previous test passed
      lastPassClass = className;
      lastFailMethod = null;
      lastFailClass = null;
      recordPass(className, methodName, delta);
    } else {
      recordPass(className, methodName, delta);
    }
  }

  public void recordPass(String className, String methodName, long tookMs) {
    try {
      DUnitRun du = getDUnitRun();
      DUnitClassInfo duci = Swarm.getOrCreateDUnitClassInfo(className);
      DUnitMethodInfo dumi = Swarm.getOrCreateDUnitMethodInfo(methodName, duci.getId());
      Swarm.recordPass(du, dumi, tookMs);
    } catch (SQLException se) {
      se.printStackTrace();
    }
  }

  public void recordFail(String className, String methodName, long tookMs) {
    try {
      DUnitRun du = getDUnitRun();
      DUnitClassInfo duci = Swarm.getOrCreateDUnitClassInfo(className);
      DUnitMethodInfo dumi = Swarm.getOrCreateDUnitMethodInfo(methodName, duci.getId());
      Swarm.recordFailure(du, dumi, lastThrowable, tookMs);
    } catch (SQLException se) {
      se.printStackTrace();
    }
  }

  public void addError(Test test, Throwable t) {
    StringBuffer sb = new StringBuffer();
    sb.append(test.toString());
    sb.append("\n");
    StringWriter sw = new StringWriter();
    t.printStackTrace(new PrintWriter(sw, true));
    sb.append(sw.toString());
    Log.getLogWriter().severe("zzzzzERROR IN " + test, t);
    // reportFailure(test, sb.toString());
    lastFailClass = getClassName(test);
    lastFailMethod = getMethodName(test);
    lastThrowable = t;
  }

  public void addFailure(Test test, AssertionFailedError t) {
    StringBuffer sb = new StringBuffer();
    sb.append(test.toString());
    sb.append("\n");
    StringWriter sw = new StringWriter();
    t.printStackTrace(new PrintWriter(sw, true));
    sb.append(sw.toString());
    Log.getLogWriter().severe("zzzzzFAILURE IN " + test, t);
    // reportFailure(test, sb.toString());
    lastFailClass = getClassName(test);
    lastFailMethod = getMethodName(test);
    lastThrowable = t;
  }

  private static Pattern methodpattern = Pattern.compile("(.*\\w+)\\(.*\\)");

  private static Pattern classpattern = Pattern.compile(".*\\w+\\((.*)\\)");
  //                   \\w+\\((.*)\\)

  /** Returns the name of the given test's class */
  protected static String getClassName(Test test) {
    String className;
    String desc = test.toString();
    Matcher matcher = classpattern.matcher(desc);
    if (matcher.matches()) {
      className = matcher.group(1);

    } else {
      className = desc;
    }

    return className;
  }

  protected static String getMethodName(Test test) {
    String className;
    String desc = test.toString();
    Matcher matcher = methodpattern.matcher(desc);
    if (matcher.matches()) {
      className = matcher.group(1);

    } else {
      className = desc;
    }

    return className;
  }

  /** Returns true if this VM has acquired the right to run this TestTask */
  private static DUnitRun getDUnitRun() {
    if (dunitRun != null) {
      System.out.println("BBB dunitRun not null returning");
      return dunitRun;
    }

    try {
      SwarmBB.getBB().getSharedLock().lock();
      Integer runId = (Integer) SwarmBB.getBB().getSharedMap().get(SwarmBB.RUN_ID);
      System.out.println("BBB runID=" + runId);
      if (runId != null) {
        dunitRun = Swarm.getDUnitRun(runId);
        System.out.println("BBB lookedUp RUN:" + dunitRun);
      } else {
        dunitRun = Swarm.generateNewDUnitRun();
        System.out.println(
            "BBB GENNED UP A RUN:" + dunitRun + " mapping to id:" + dunitRun.getId());
        SwarmBB.getBB().getSharedMap().put(SwarmBB.RUN_ID, dunitRun.getId());
      }
    } catch (SQLException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      SwarmBB.getBB().getSharedLock().unlock();
    }
    return dunitRun;
  }
}