@Override
  public ExternalIoCommandProvider createCommandProvider(IoContext context) {
    String primary = null;
    Set<String> targets = new TreeSet<String>();
    for (Input input : context.getInputs()) {
      BulkLoadImporterDescription desc = extract(input.getDescription());
      String target = desc.getTargetName();
      if (desc.getMode() == Mode.PRIMARY) {
        assert primary == null || primary.equals(target);
        primary = target;
      }
      targets.add(target);
    }
    for (Output output : context.getOutputs()) {
      BulkLoadExporterDescription desc = extract(output.getDescription());
      String target = desc.getTargetName();
      assert primary == null || primary.equals(target);
      primary = target;
      targets.add(target);
    }
    if (primary != null) {
      targets.remove(primary);
    }

    return new CommandProvider(
        getEnvironment().getBatchId(),
        getEnvironment().getFlowId(),
        primary,
        new ArrayList<String>(targets));
  }
  private boolean checkAssignment(List<InputDescription> inputs, List<OutputDescription> outputs) {
    assert inputs != null;
    assert outputs != null;
    Set<String> primaryTargets = new HashSet<String>();
    Set<String> secondaryTargets = new HashSet<String>();
    for (InputDescription description : inputs) {
      BulkLoadImporterDescription desc = extract(description);
      if (desc.getMode() == Mode.PRIMARY) {
        primaryTargets.add(desc.getTargetName());
      } else {
        secondaryTargets.add(desc.getTargetName());
        if (desc.getLockType() != BulkLoadImporterDescription.LockType.UNUSED) {
          getEnvironment().error("補助インポーターはロックを指定できません: {0}", desc.getClass().getName());
          return false;
        }
      }
    }
    if (primaryTargets.size() >= 2) {
      getEnvironment()
          .error(
              "ジョブフロー内で複数のインポーターを起動できません。{1}を利用してください: {0}",
              primaryTargets, SecondaryImporterDescription.class.getSimpleName());
      return false;
    }

    for (String primary : primaryTargets) {
      if (secondaryTargets.contains(primary)) {
        LOG.warn("補助インポーターの指定がある{}は通常のインポーターでロードされます", primary);
      }
    }

    Set<String> exportTargets = new HashSet<String>();
    for (OutputDescription description : outputs) {
      BulkLoadExporterDescription desc = extract(description);
      exportTargets.add(desc.getTargetName());
    }
    if (exportTargets.size() >= 2) {
      getEnvironment().error("ジョブフロー内で複数のエクスポーターを起動できません: {0}", primaryTargets);
      return false;
    }
    if (primaryTargets.isEmpty() || exportTargets.isEmpty()) {
      return true;
    }
    if (primaryTargets.equals(exportTargets) == false) {
      getEnvironment()
          .error("インポーターとエクスポーターの対象データベースが一致しません: {0}, {1}", primaryTargets, exportTargets);
      return false;
    }

    return true;
  }
 private ImportTable convert(InputDescription input) {
   assert input != null;
   BulkLoadImporterDescription desc = extract(input);
   LockType lockType;
   LockedOperation lockedOperation;
   switch (desc.getLockType()) {
     case CHECK:
       lockType = LockType.UNLOCKED;
       lockedOperation = LockedOperation.ERROR;
       break;
     case ROW:
       lockType = LockType.ROW;
       lockedOperation = LockedOperation.ERROR;
       break;
     case ROW_OR_SKIP:
       lockType = LockType.ROW;
       lockedOperation = LockedOperation.SKIP;
       break;
     case TABLE:
       lockType = LockType.TABLE;
       lockedOperation = LockedOperation.ERROR;
       break;
     case UNUSED:
       lockType = LockType.UNLOCKED;
       lockedOperation = LockedOperation.FORCE;
       break;
     default:
       throw new AssertionError(desc.getLockType());
   }
   return new ImportTable(
       desc.getModelType(),
       desc.getTableName(),
       desc.getColumnNames(),
       desc.getWhere(),
       desc.isCacheEnabled() ? Cache.ENABLED : Cache.DISABLED,
       lockType,
       lockedOperation,
       getInputLocation(input));
 }