@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 Map<String, BulkLoaderScript> toScripts(IoContext context) {
    assert context != null;
    Map<String, List<Input>> inputs = new HashMap<String, List<Input>>();
    for (Input input : context.getInputs()) {
      String target = extract(input.getDescription()).getTargetName();
      List<Input> in = inputs.get(target);
      if (in == null) {
        in = new ArrayList<Input>();
        inputs.put(target, in);
      }
      in.add(input);
    }

    Map<String, List<Output>> outputs = new HashMap<String, List<Output>>();
    for (Output output : context.getOutputs()) {
      String target = extract(output.getDescription()).getTargetName();
      List<Output> out = outputs.get(target);
      if (out == null) {
        out = new ArrayList<Output>();
        outputs.put(target, out);
      }
      out.add(output);
    }

    Set<String> targets = new HashSet<String>();
    targets.addAll(inputs.keySet());
    targets.addAll(outputs.keySet());

    Map<String, BulkLoaderScript> results = new HashMap<String, BulkLoaderScript>();
    for (String target : targets) {
      List<Input> in = inputs.get(target);
      List<Output> out = outputs.get(target);
      in = (in == null) ? Collections.<Input>emptyList() : in;
      out = (out == null) ? Collections.<Output>emptyList() : out;
      results.put(target, toScript(in, out));
    }

    return results;
  }
  @Override
  public List<CompiledStage> emitEpilogue(IoContext context) throws IOException {
    if (context.getOutputs().isEmpty()) {
      return Collections.emptyList();
    }

    List<Slot> slots = new ArrayList<Slot>();
    for (Output output : context.getOutputs()) {
      Slot slot = toSlot(output);
      slots.add(slot);
    }
    List<ResolvedSlot> resolved = new SlotResolver(getEnvironment()).resolve(slots);
    if (getEnvironment().hasError()) {
      return Collections.emptyList();
    }

    ParallelSortClientEmitter emitter = new ParallelSortClientEmitter(getEnvironment());
    CompiledStage stage =
        emitter.emit(MODULE_NAME, resolved, getEnvironment().getEpilogueLocation(MODULE_NAME));

    return Collections.singletonList(stage);
  }