public static void main(String[] args) throws Exception {
    OptionParser parser = new OptionParser();

    ArgumentAcceptingOptionSpec<String> actionOpt =
        parser.accepts("action", "The choice of the action only includes list,view,rollback,update")
            .withRequiredArg()
            .describedAs("action")
            .ofType(String.class);

    ArgumentAcceptingOptionSpec<String> zkServerOpt =
        parser.accepts("zkServer", "The address of ZooKeeper server. This option is required.")
            .withRequiredArg()
            .describedAs("zk_server")
            .ofType(String.class);

    ArgumentAcceptingOptionSpec<String> storePathOpt = parser.accepts("storePath",
        "The root path of helix property store in the ZooKeeper. "
            + "Must start with /, and must not end with /. It is recommended to make root path in the form of "
            + "/ambry/<clustername>/helixPropertyStore. This option is required.")
        .withRequiredArg()
        .describedAs("helix_store_path")
        .ofType(String.class);

    ArgumentAcceptingOptionSpec<Integer> zkConnectionTimeoutMsOpt = parser.accepts("zkConnectionTimeout",
        "Optional timeout in millisecond for connecting to the ZooKeeper server. This option is not required, "
            + "and the default value is 5000.")
        .withRequiredArg()
        .describedAs("zk_connection_timeout")
        .ofType(Integer.class)
        .defaultsTo(ZK_CLIENT_CONNECTION_TIMEOUT_MS);

    ArgumentAcceptingOptionSpec<Integer> zkSessionTimeoutMsOpt = parser.accepts("zkSessionTimeout",
        "Optional timeout in millisecond for session to the ZooKeeper server. This option is not required, "
            + "and the default value is 20000.")
        .withRequiredArg()
        .describedAs("zk_session_timeout")
        .ofType(Integer.class)
        .defaultsTo(ZK_CLIENT_SESSION_TIMEOUT_MS);

    ArgumentAcceptingOptionSpec<String> zkLayoutPathOpt = parser.accepts("zkLayoutPath",
        "The path to the json file containing zookeeper connect info. This should be of the following form: \n{\n"
            + "  \"zkInfo\" : [\n" + "     {\n" + "       \"datacenter\":\"dc1\",\n"
            + "       \"zkConnectStr\":\"abc.example.com:2199\",\n" + "     },\n" + "     {\n"
            + "       \"datacenter\":\"dc2\",\n" + "       \"zkConnectStr\":\"def.example.com:2300\",\n" + "     },\n"
            + "     {\n" + "       \"datacenter\":\"dc3\",\n" + "       \"zkConnectStr\":\"ghi.example.com:2400\",\n"
            + "     }\n" + "  ]\n" + "}").
        withRequiredArg().
        describedAs("zk_connect_info_path").
        ofType(String.class);

    ArgumentAcceptingOptionSpec<String> clusterNameOpt =
        parser.accepts("clustername", "Cluster name of current machine.")
            .withRequiredArg()
            .describedAs("cluster name")
            .ofType(String.class);

    ArgumentAcceptingOptionSpec<String> hostnameOpt = parser.accepts("hostname",
        "Optional hostname of current machine. The option is not required and will defaulted to localhost")
        .withRequiredArg()
        .describedAs("hostname")
        .ofType(String.class)
        .defaultsTo(DEFAULT_HOSTNAME);

    ArgumentAcceptingOptionSpec<String> dcnameOpt = parser.accepts("dcname",
        "Optional dc name of current machine. The option is not required and will defaulted to \"dc\"")
        .withRequiredArg()
        .describedAs("dc name")
        .ofType(String.class);

    ArgumentAcceptingOptionSpec<Integer> versionOpt = parser.accepts("version",
        "Required when the action is rollback or view. When the action is rollback, this tool would rollback the account metadata"
            + " to the given version and create a new version on top of the latest version. When the action is view, this tool will print out"
            + " the account metadata for the given version")
        .withRequiredArg()
        .describedAs("version")
        .ofType(Integer.class);

    ArgumentAcceptingOptionSpec<String> accountJsonPathOpt = parser.accepts("accountJsonPath",
        "The path to the account json file. The json file must be in the form of a json array, with each"
            + "entry to be an account in its json form.")
        .withRequiredArg()
        .describedAs("account_json_path")
        .ofType(String.class);

    ArgumentAcceptingOptionSpec<String> hardwareLayoutOpt = parser.accepts("hardwareLayout",
        "The hardware layout json file. This is optional if you provide a zklayout file.")
        .withRequiredArg()
        .describedAs("hardware_layout")
        .ofType(String.class);

    ArgumentAcceptingOptionSpec<String> partitionLayoutOpt = parser.accepts("partitionLayout",
        "The partition layout json file. This is optional if you provide a zklayout file.")
        .withRequiredArg()
        .describedAs("partition_layout")
        .ofType(String.class);

    parser.accepts("help", "print this help message.");
    parser.accepts("h", "print this help message.");

    OptionSet options = parser.parse(args);
    if (options.has("help") || options.has("h")) {
      parser.printHelpOn(System.out);
      System.exit(0);
    }

    ToolUtils.ensureOrExit(
        Arrays.asList(actionOpt, zkServerOpt, storePathOpt, zkLayoutPathOpt, dcnameOpt, clusterNameOpt), options,
        parser);
    String action = options.valueOf(actionOpt);
    Action act = null;
    try {
      act = Enum.valueOf(Action.class, action.toUpperCase());
    } catch (Exception e) {
      System.out.println("Invalid action: " + action);
      parser.printHelpOn(System.out);
      System.exit(0);
    }
    String zkServer = options.valueOf(zkServerOpt);
    String storePath = options.valueOf(storePathOpt);
    String hostname = options.valueOf(hostnameOpt);
    String dcname = options.valueOf(dcnameOpt);
    Integer zkConnectionTimeoutMs = options.valueOf(zkConnectionTimeoutMsOpt);
    Integer zkSessionTimeoutMs = options.valueOf(zkSessionTimeoutMsOpt);
    String zkLayoutPath = options.valueOf(zkLayoutPathOpt);
    String clusterMapDcsZkConnectString = Utils.readStringFromFile(zkLayoutPath);
    String clusterName = options.valueOf(clusterNameOpt);
    String hardwareLayout = options.valueOf(hardwareLayoutOpt);
    String partitionLayout = options.valueOf(partitionLayoutOpt);

    if ((zkLayoutPath != null && clusterName == null) || (zkLayoutPath == null && clusterName != null)) {
      System.out.println("You have to provide clustername and zklayout at the same time");
      parser.printHelpOn(System.out);
      System.exit(0);
    }

    if (zkLayoutPath == null) {
      if ((hardwareLayout != null && partitionLayout == null) || (hardwareLayout == null && partitionLayout != null)) {
        System.out.println("You have to provide hardwareLayout and partitionLayout at the same time");
        parser.printHelpOn(System.out);
        System.exit(0);
      }
    }

    int version = 0;
    if (act == Action.VIEW || act == Action.ROLLBACK) {
      ToolUtils.ensureOrExit(Arrays.asList(versionOpt), options, parser);
      version = options.valueOf(versionOpt);
    }

    String accountJsonPath = null;
    if (act == Action.UPDATE) {
      ToolUtils.ensureOrExit(Arrays.asList(accountJsonPathOpt), options, parser);
      accountJsonPath = options.valueOf(accountJsonPathOpt);
    }

    AccountTool accountTool =
        new AccountTool(zkServer, storePath, zkConnectionTimeoutMs, zkSessionTimeoutMs, hostname, dcname, clusterName,
            clusterMapDcsZkConnectString, hardwareLayout, partitionLayout);
    try {
      boolean succeeded = false;
      switch (act) {
        case LIST:
          List<Integer> versions = accountTool.listVersions();
          if (versions != null) {
            System.out.println("The versions are " + versions);
          } else {
            System.out.println("Some error occurs.");
          }
          break;
        case VIEW:
          Collection<String> accounts = accountTool.viewAccountMetadata(version);
          if (accounts != null) {
            System.out.println("The account metadata at version " + version + " are " + accounts);
          } else {
            System.out.println("Some error occurs");
          }
          break;
        case ROLLBACK:
          try {
            accountTool.rollback(version);
            System.out.println("Successfully rollback account metadata to version " + version);
          } catch (Exception e) {
            System.out.println("Fail to rollback account metadata to version " + version);
            throw e;
          }
          break;
        case UPDATE:
          try {
            accountTool.updateFromFile(accountJsonPath);
            System.out.println("Successfully update account metadata from file " + accountJsonPath);
          } catch (Exception e) {
            System.out.println("Fail to update account metadata from file " + accountJsonPath);
            throw e;
          }
          break;
      }
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      accountTool.close();
      System.exit(0);
    }
  }
Update the account metadata by overwriting it with the content provided by a file.
ambry-tools/src/main/java/com/github/ambry/account/AccountTool.java