/**
   * Collectorの処理を実行する。
   *
   * @param args コマンドライン引数
   * @return 処理全体の終了コード
   * @see Constants#EXIT_CODE_SUCCESS
   * @see Constants#EXIT_CODE_WARNING
   * @see Constants#EXIT_CODE_ERROR
   */
  protected int execute(String[] args) {
    if (args.length != 5) {
      System.err.println("Collectorに指定する引数の数が不正です。 引数の数:" + args.length);
      return Constants.EXIT_CODE_ERROR;
    }
    String targetName = args[0];
    String batchId = args[1];
    String jobflowId = args[2];
    String executionId = args[3];
    String user = args[4];

    try {
      // 初期処理
      if (!BulkLoaderInitializer.initHadoopCluster(jobflowId, executionId, PROPERTIES)) {
        LOG.error(
            "TG-COLLECTOR-01003", new Date(), targetName, batchId, jobflowId, executionId, user);
        return Constants.EXIT_CODE_ERROR;
      }

      // 開始ログ出力
      LOG.info("TG-COLLECTOR-01001", new Date(), targetName, batchId, jobflowId, executionId, user);

      // パラメータオブジェクトを作成
      ExporterBean bean = createBean(targetName, batchId, jobflowId, executionId);
      if (bean == null) {
        // パラメータのチェックでエラー
        LOG.error(
            "TG-COLLECTOR-01006", new Date(), targetName, batchId, jobflowId, executionId, user);
        return Constants.EXIT_CODE_ERROR;
      }

      // ExportファイルをDBサーバに送信
      LOG.info("TG-COLLECTOR-01008", targetName, batchId, jobflowId, executionId, user);
      ExportFileSend fileSend = createExportFileSend();
      if (!fileSend.sendExportFile(bean, user)) {
        // Exportファイルの送信に失敗
        LOG.error(
            "TG-COLLECTOR-01007", new Date(), targetName, batchId, jobflowId, executionId, user);
        return Constants.EXIT_CODE_ERROR;
      } else {
        LOG.info("TG-COLLECTOR-01009", targetName, batchId, jobflowId, executionId, user);
      }

      // 正常終了
      LOG.info("TG-COLLECTOR-01002", new Date(), targetName, batchId, jobflowId, executionId, user);
      return Constants.EXIT_CODE_SUCCESS;
    } catch (Exception e) {
      try {
        LOG.error(
            e, "TG-COLLECTOR-01004", new Date(), targetName, batchId, jobflowId, executionId, user);
        return Constants.EXIT_CODE_ERROR;
      } catch (Exception e1) {
        System.err.print("Collectorで不明なエラーが発生しました。");
        e1.printStackTrace();
        return Constants.EXIT_CODE_ERROR;
      }
    }
  }
  /**
   * 標準入力を読み込んでDFSにファイルを書き出す。 {@link FileList}形式で受け取ったTSV形式のファイルをModelオブジェクトに変換して、テンポラリ入力データとして配置する。
   * 出力先はプロトコルの形式によって異なる。 利用可能なプロトコルは以下のとおり。
   *
   * <ul>
   *   <li>{@link com.asakusafw.bulkloader.transfer.FileProtocol.Kind#CONTENT}
   *   <li>{@link com.asakusafw.bulkloader.transfer.FileProtocol.Kind#CREATE_CACHE}
   *   <li>{@link com.asakusafw.bulkloader.transfer.FileProtocol.Kind#UPDATE_CACHE}
   * </ul>
   *
   * @param bean パラメータを保持するBean
   * @param user OSのユーザー名
   * @return 出力結果(true:正常終了、false:異常終了)
   */
  public boolean importFile(ImportBean bean, String user) {
    // 標準入力を取得
    FileList.Reader reader;
    try {
      reader = FileList.createReader(getInputStream());
    } catch (IOException e) {
      LOG.error(e, "TG-EXTRACTOR-02001", "標準入力からFileListの取得に失敗");
      return false;
    }
    try {
      // FileListの終端まで繰り返す
      List<Future<?>> running = new ArrayList<>();
      while (reader.next()) {
        FileProtocol protocol = reader.getCurrentProtocol();
        try (InputStream content = reader.openContent()) {
          switch (protocol.getKind()) {
            case CONTENT:
              importContent(protocol, content, bean, user);
              break;

            case CREATE_CACHE:
            case UPDATE_CACHE:
              long recordCount = putCachePatch(protocol, content, bean, user);
              Callable<?> builder = createCacheBuilder(protocol, bean, user, recordCount);
              if (builder != null) {
                LOG.debugMessage(
                    "Submitting cache builder: {0} {1}",
                    protocol.getKind(), protocol.getInfo().getTableName());
                running.add(executor.submit(builder));
              }
              break;

            default:
              throw new AssertionError(protocol.getKind());
          }
        }
      }

      waitForCompleteTasks(bean, running);
      // 正常終了
      return true;

    } catch (BulkLoaderSystemException e) {
      LOG.log(e);
    } catch (IOException e) {
      // FileListの展開に失敗
      LOG.error(e, "TG-EXTRACTOR-02001", "標準入力からFileListの取得に失敗");
    } finally {
      try {
        reader.close();
      } catch (IOException e) {
        // ここで例外が発生した場合は握りつぶす
        e.printStackTrace();
      }
    }
    return false;
  }
  private void waitForCompleteTasks(ImportBean bean, List<Future<?>> running)
      throws BulkLoaderSystemException {
    assert bean != null;
    assert running != null;
    if (running.isEmpty()) {
      return;
    }
    LOG.info(
        "TG-EXTRACTOR-12006",
        bean.getTargetName(),
        bean.getBatchId(),
        bean.getJobflowId(),
        bean.getExecutionId());

    boolean sawError = false;
    LinkedList<Future<?>> rest = new LinkedList<>(running);
    while (rest.isEmpty() == false) {
      Future<?> future = rest.removeFirst();
      try {
        future.get(1, TimeUnit.SECONDS);
      } catch (TimeoutException e) {
        // continue...
        rest.addLast(future);
      } catch (InterruptedException e) {
        cancel(rest);
        throw new BulkLoaderSystemException(
            e,
            getClass(),
            "TG-EXTRACTOR-12007",
            bean.getTargetName(),
            bean.getBatchId(),
            bean.getJobflowId(),
            bean.getExecutionId());
      } catch (ExecutionException e) {
        cancel(rest);
        Throwable cause = e.getCause();
        if (cause instanceof RuntimeException) {
          throw (RuntimeException) cause;
        } else if (cause instanceof Error) {
          throw (Error) cause;
        } else if (cause instanceof BulkLoaderSystemException) {
          LOG.log((BulkLoaderSystemException) cause);
          sawError = true;
        } else {
          LOG.error(
              e,
              "TG-EXTRACTOR-12008",
              bean.getTargetName(),
              bean.getBatchId(),
              bean.getJobflowId(),
              bean.getExecutionId());
          sawError = true;
        }
      }
    }
    if (sawError) {
      throw new BulkLoaderSystemException(
          getClass(),
          "TG-EXTRACTOR-12008",
          bean.getTargetName(),
          bean.getBatchId(),
          bean.getJobflowId(),
          bean.getExecutionId());
    } else {
      LOG.info(
          "TG-EXTRACTOR-12009",
          bean.getTargetName(),
          bean.getBatchId(),
          bean.getJobflowId(),
          bean.getExecutionId());
    }
  }