@Test
  public void testDefaultJoinGroupHelper() throws UnknownMasterException, DatabaseException {

    for (int i = 0; i < repEnvInfo.length; i++) {
      RepEnvInfo ri = repEnvInfo[i];
      if ((i + 1) == repEnvInfo.length) {
        /* Use a non-master helper for the last replicator. */
        ReplicationConfig config = RepTestUtils.createRepConfig((short) (i + 1));
        String hpPairs = "";
        // Skip the master, use all the other nodes
        for (int j = 1; j < i; j++) {
          hpPairs += "," + repEnvInfo[j].getRepConfig().getNodeHostPort();
        }
        hpPairs = hpPairs.substring(1);
        config.setHelperHosts(hpPairs);
        File envHome = ri.getEnvHome();
        ri =
            repEnvInfo[i] =
                new RepEnvInfo(
                    envHome, config, RepTestUtils.createEnvConfig(Durability.COMMIT_SYNC));
      }
      ri.openEnv();
      State state = ri.getEnv().getState();
      assertEquals((i == 0) ? State.MASTER : State.REPLICA, state);
    }
  }
    /* Start a replication group with 2 nodes and returns the master. */
    private ReplicatedEnvironment getMaster() throws Exception {

      Durability durability =
          new Durability(
              Durability.SyncPolicy.WRITE_NO_SYNC,
              Durability.SyncPolicy.WRITE_NO_SYNC,
              Durability.ReplicaAckPolicy.ALL);
      EnvironmentConfig envConfig = RepTestUtils.createEnvConfig(durability);
      repEnvInfo = RepTestUtils.setupEnvInfos(envRoot, 2, envConfig);

      return RepTestUtils.joinGroup(repEnvInfo);
    }
  private void createRepEnvInfo(String sleepTime) throws Throwable {

    /*
     * Set a large buffer size and disable the checkpointing, so the
     * data in the buffer can only be flushed by the LogFlushTask.
     */
    EnvironmentConfig envConfig = RepTestUtils.createEnvConfig(Durability.COMMIT_NO_SYNC);
    envConfig.setConfigParam(EnvironmentParams.MAX_MEMORY.getName(), "20000000");
    envConfig.setConfigParam(EnvironmentParams.LOG_MEM_SIZE.getName(), "120000000");
    envConfig.setConfigParam(EnvironmentParams.NUM_LOG_BUFFERS.getName(), "4");
    envConfig.setConfigParam(EnvironmentConfig.ENV_RUN_CHECKPOINTER, "false");

    /* Configure the log flush task. */
    ReplicationConfig repConfig = new ReplicationConfig();
    repConfig.setConfigParam(ReplicationConfig.LOG_FLUSH_TASK_INTERVAL, sleepTime);
    repEnvInfo = RepTestUtils.setupEnvInfos(envRoot, 3, envConfig, repConfig);
  }
    public void openEnv() {
      try {
        Durability durability =
            new Durability(
                Durability.SyncPolicy.WRITE_NO_SYNC,
                Durability.SyncPolicy.WRITE_NO_SYNC,
                Durability.ReplicaAckPolicy.ALL);
        EnvironmentConfig envConfig = RepTestUtils.createEnvConfig(durability);
        ReplicationConfig repConfig = RepTestUtils.createRepConfig(1);
        repEnvInfo = RepTestUtils.setupEnvInfo(envHome, envConfig, repConfig, null);
        repEnvInfo.openEnv();
        Thread.sleep(sleepTime);
      } catch (EnvironmentLockedException e) {

        /*
         * Exit the process with value 1, don't print out the exception
         * since it's expected.
         */
        System.exit(1);
      } catch (UnsupportedOperationException e) {

        /*
         * Exit the process with value 2, don't print out the exception
         * since it's expected.
         *
         * Note: this exception thrown because we can't start a
         * replicated Environment on an existed standalone Environment.
         */
        System.exit(2);
      } catch (Exception e) {
        /* Dump unexpected exceptions, exit processs with value 3. */
        e.printStackTrace();
        System.exit(3);
      } finally {
        if (repEnvInfo.getEnv() != null) {
          repEnvInfo.closeEnv();
        }
      }
    }
  /** Creates a 3 node group and initializes the app classes. */
  private void open() throws Exception {

    /*
     * ReplicaAckPolicy.ALL is used to ensure that when a master operation
     * is committed, the change is immediately available on the replica for
     * testing -- no waiting in the test is needed.
     */
    repEnvInfo =
        RepTestUtils.setupEnvInfos(
            envRoot,
            3,
            RepTestUtils.createEnvConfig(
                new Durability(
                    Durability.SyncPolicy.WRITE_NO_SYNC,
                    Durability.SyncPolicy.WRITE_NO_SYNC,
                    Durability.ReplicaAckPolicy.ALL)),
            new ReplicationConfig());
    masterEnv = RepTestUtils.joinGroup(repEnvInfo);
    replicaEnv1 = repEnvInfo[1].getEnv();
    replicaEnv2 = repEnvInfo[2].getEnv();

    /* Load app classes with custom class loader. */
    final File evolveParentDir = new File(System.getProperty("testevolvedir"));
    final ClassLoader parentClassLoader = Thread.currentThread().getContextClassLoader();
    for (int i = 0; i < N_APP_VERSIONS; i += 1) {
      final ClassLoader myLoader =
          new SimpleClassLoader(parentClassLoader, new File(evolveParentDir, "dplUpgrade." + i));
      appClasses[i] = Class.forName(APP_IMPL, true /*initialize*/, myLoader);
    }

    /* Open v0 app objects. */
    masterApp = newAppObject(0);
    masterApp.open(masterEnv);
    replicaApp1 = newAppObject(0);
    replicaApp1.open(replicaEnv1);
    replicaApp2 = newAppObject(0);
    replicaApp2.open(replicaEnv2);
  }
  /** Test the basic configuration of LogFlusher. */
  @Test
  public void testBasicConfig() throws Throwable {

    try {
      EnvironmentConfig envConfig = RepTestUtils.createEnvConfig(Durability.COMMIT_NO_SYNC);
      ReplicationConfig repConfig = new ReplicationConfig();
      repConfig.setConfigParam(ReplicationMutableConfig.LOG_FLUSH_TASK_INTERVAL, "30 s");
      repEnvInfo = RepTestUtils.setupEnvInfos(envRoot, 3, envConfig, repConfig);
      RepTestUtils.joinGroup(repEnvInfo);
      assertTrue(repEnvInfo[0].isMaster());
      /* Check the LogFlusher configuration. */
      TimerTask[] oldTasks = new TimerTask[repEnvInfo.length];
      for (int i = 0; i < repEnvInfo.length; i++) {
        LogFlusher flusher = repEnvInfo[i].getRepNode().getLogFlusher();
        oldTasks[i] = flusher.getFlushTask();
        assertTrue(flusher != null);
        assertTrue(flusher.getFlushTask() != null);
        assertTrue(flusher.getFlushInterval() == 30000);
      }

      /* Check that those configuratins are mutable. */
      repConfig.setConfigParam(ReplicationMutableConfig.LOG_FLUSH_TASK_INTERVAL, "50 s");
      for (int i = 0; i < repEnvInfo.length; i++) {
        repEnvInfo[i].getEnv().setRepMutableConfig(repConfig);
      }

      for (int i = 0; i < repEnvInfo.length; i++) {
        LogFlusher flusher = repEnvInfo[i].getRepNode().getLogFlusher();
        assertTrue(flusher != null);
        assertTrue(flusher.getFlushTask() != null);
        assertTrue(flusher.getFlushTask() != oldTasks[i]);
        assertTrue(flusher.getFlushInterval() == 50000);
      }

      repConfig.setConfigParam(ReplicationMutableConfig.RUN_LOG_FLUSH_TASK, "false");
      for (int i = 0; i < repEnvInfo.length; i++) {
        repEnvInfo[i].getEnv().setRepMutableConfig(repConfig);
      }

      for (int i = 0; i < repEnvInfo.length; i++) {
        LogFlusher flusher = repEnvInfo[i].getRepNode().getLogFlusher();
        assertTrue(flusher != null);
        assertTrue(flusher.getFlushTask() == null);
      }

      RepTestUtils.shutdownRepEnvs(repEnvInfo);
      RepTestUtils.removeRepEnvironments(envRoot);

      repConfig.setConfigParam(ReplicationConfig.RUN_LOG_FLUSH_TASK, "false");
      repEnvInfo = RepTestUtils.setupEnvInfos(envRoot, 3, envConfig, repConfig);
      RepTestUtils.joinGroup(repEnvInfo);
      /* Check that the task is disabled. */
      for (int i = 0; i < repEnvInfo.length; i++) {
        assertTrue(repEnvInfo[i].getRepNode().getLogFlusher() == null);
      }
    } catch (Throwable t) {
      t.printStackTrace();
      throw t;
    } finally {
      RepTestUtils.shutdownRepEnvs(repEnvInfo);
    }
  }