public void dispose(StepMetaInterface smi, StepDataInterface sdi) {
    meta = (OraBulkLoaderMeta) smi;
    data = (OraBulkLoaderData) sdi;

    super.dispose(smi, sdi);

    // close output stream (may terminate running sqlldr)
    if (output != null) {
      // Close the output
      try {
        output.close();
      } catch (IOException e) {
        logError("Error while closing output", e);
      }

      output = null;
    }
    // running sqlldr process must be terminated
    if (sqlldrProcess != null) {
      try {
        int exitVal = sqlldrProcess.waitFor();
        sqlldrProcess = null;
        logBasic(BaseMessages.getString(PKG, "OraBulkLoader.Log.ExitValueSqlldr", "" + exitVal));
      } catch (InterruptedException e) {
        /* process should be destroyed */
        e.printStackTrace();
        if (sqlldrProcess != null) {
          sqlldrProcess.destroy();
        }
      }
    }

    if (!preview && meta.isEraseFiles()) {
      // Erase the created cfg/dat files if requested. We don't erase
      // the rest of the files because it would be "stupid" to erase them
      // right after creation. If you don't want them, don't fill them in.
      FileObject fileObject = null;

      String method = meta.getLoadMethod();
      // OraBulkLoaderMeta.METHOD_AUTO_CONCURRENT.equals(method) ||
      if (OraBulkLoaderMeta.METHOD_AUTO_END.equals(method)) {
        if (meta.getControlFile() != null) {
          try {
            fileObject =
                KettleVFS.getFileObject(
                    environmentSubstitute(meta.getControlFile()), getTransMeta());
            fileObject.delete();
            fileObject.close();
          } catch (Exception ex) {
            logError(
                "Error deleting control file \'"
                    + KettleVFS.getFilename(fileObject)
                    + "\': "
                    + ex.getMessage(),
                ex);
          }
        }
      }

      if (OraBulkLoaderMeta.METHOD_AUTO_END.equals(method)) {
        // In concurrent mode the data is written to the control file.
        if (meta.getDataFile() != null) {
          try {
            fileObject =
                KettleVFS.getFileObject(environmentSubstitute(meta.getDataFile()), getTransMeta());
            fileObject.delete();
            fileObject.close();
          } catch (Exception ex) {
            logError(
                "Error deleting data file \'"
                    + KettleVFS.getFilename(fileObject)
                    + "\': "
                    + ex.getMessage(),
                ex);
          }
        }
      }

      if (OraBulkLoaderMeta.METHOD_MANUAL.equals(method)) {
        logBasic("Deletion of files is not compatible with \'manual load method\'");
      }
    }
  }
  public boolean processRow(StepMetaInterface smi, StepDataInterface sdi) throws KettleException {
    meta = (OraBulkLoaderMeta) smi;
    data = (OraBulkLoaderData) sdi;

    try {
      Object[] r = getRow(); // Get row from input rowset & set row busy!
      if (r == null) {
        // no more input to be expected...

        setOutputDone();

        if (!preview) {
          if (output != null) {
            // Close the output
            try {
              output.close();
            } catch (IOException e) {
              throw new KettleException("Error while closing output", e);
            }

            output = null;
          }

          String loadMethod = meta.getLoadMethod();
          if (OraBulkLoaderMeta.METHOD_AUTO_END.equals(loadMethod)) {
            // if this is the first line, we do not need to execute loader
            // control file may not exists
            if (!first) {
              execute(meta, true);
              sqlldrProcess = null;
            }
          } else if (OraBulkLoaderMeta.METHOD_AUTO_CONCURRENT.equals(meta.getLoadMethod())) {
            try {
              if (sqlldrProcess != null) {
                int exitVal = sqlldrProcess.waitFor();
                sqlldrProcess = null;
                logBasic(
                    BaseMessages.getString(PKG, "OraBulkLoader.Log.ExitValueSqlldr", "" + exitVal));
                checkExitVal(exitVal);
              } else if (!first) {
                throw new KettleException("Internal error: no sqlldr process running");
              }
            } catch (Exception ex) {
              throw new KettleException("Error while executing sqlldr", ex);
            }
          }
        }
        return false;
      }

      if (!preview) {
        if (first) {
          first = false;

          String recTerm = Const.CR;
          if (!Utils.isEmpty(meta.getAltRecordTerm())) {
            recTerm = substituteRecordTerminator(meta.getAltRecordTerm());
          }

          createControlFile(environmentSubstitute(meta.getControlFile()), r, meta);
          output = new OraBulkDataOutput(meta, recTerm);

          if (OraBulkLoaderMeta.METHOD_AUTO_CONCURRENT.equals(meta.getLoadMethod())) {
            execute(meta, false);
          }
          output.open(this, sqlldrProcess);
        }
        output.writeLine(getInputRowMeta(), r);
      }
      putRow(getInputRowMeta(), r);
      incrementLinesOutput();

    } catch (KettleException e) {
      logError(BaseMessages.getString(PKG, "OraBulkLoader.Log.ErrorInStep") + e.getMessage());
      setErrors(1);
      stopAll();
      setOutputDone(); // signal end to receiver(s)
      return false;
    }

    return true;
  }