public boolean processRow(StepMetaInterface smi, StepDataInterface sdi) throws KettleException {
    meta = (ExecProcessMeta) smi;
    data = (ExecProcessData) sdi;

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

      setOutputDone();
      return false;
    }

    if (first) {
      first = false;
      // get the RowMeta
      data.previousRowMeta = getInputRowMeta().clone();
      data.NrPrevFields = data.previousRowMeta.size();
      data.outputRowMeta = data.previousRowMeta;
      meta.getFields(data.outputRowMeta, getStepname(), null, null, this, repository, metaStore);

      // Check is process field is provided
      if (Const.isEmpty(meta.getProcessField())) {
        logError(BaseMessages.getString(PKG, "ExecProcess.Error.ProcessFieldMissing"));
        throw new KettleException(
            BaseMessages.getString(PKG, "ExecProcess.Error.ProcessFieldMissing"));
      }

      // cache the position of the field
      if (data.indexOfProcess < 0) {
        data.indexOfProcess = data.previousRowMeta.indexOfValue(meta.getProcessField());
        if (data.indexOfProcess < 0) {
          // The field is unreachable !
          logError(
              BaseMessages.getString(PKG, "ExecProcess.Exception.CouldnotFindField")
                  + "["
                  + meta.getProcessField()
                  + "]");
          throw new KettleException(
              BaseMessages.getString(
                  PKG, "ExecProcess.Exception.CouldnotFindField", meta.getProcessField()));
        }
      }
    } // End If first

    Object[] outputRow = RowDataUtil.allocateRowData(data.outputRowMeta.size());
    for (int i = 0; i < data.NrPrevFields; i++) {
      outputRow[i] = r[i];
    }
    // get process to execute
    String processString = data.previousRowMeta.getString(r, data.indexOfProcess);

    ProcessResult processResult = new ProcessResult();

    try {
      if (Const.isEmpty(processString)) {
        throw new KettleException(BaseMessages.getString(PKG, "ExecProcess.ProcessEmpty"));
      }

      // execute and return result
      execProcess(processString, processResult);

      if (meta.isFailWhenNotSuccess()) {
        if (processResult.getExistStatus() != 0) {
          String errorString = processResult.getErrorStream();
          if (Const.isEmpty(errorString)) {
            errorString = processResult.getOutputStream();
          }
          throw new KettleException(errorString);
        }
      }

      // Add result field to input stream
      int rowIndex = data.NrPrevFields;
      outputRow[rowIndex++] = processResult.getOutputStream();

      // Add result field to input stream
      outputRow[rowIndex++] = processResult.getErrorStream();

      // Add result field to input stream
      outputRow[rowIndex++] = processResult.getExistStatus();

      // add new values to the row.
      putRow(data.outputRowMeta, outputRow); // copy row to output rowset(s);

      if (log.isRowLevel()) {
        logRowlevel(
            BaseMessages.getString(
                PKG,
                "ExecProcess.LineNumber",
                getLinesRead() + " : " + getInputRowMeta().getString(r)));
      }
    } catch (KettleException e) {

      boolean sendToErrorRow = false;
      String errorMessage = null;

      if (getStepMeta().isDoingErrorHandling()) {
        sendToErrorRow = true;
        errorMessage = e.toString();
      } else {
        logError(BaseMessages.getString(PKG, "ExecProcess.ErrorInStepRunning") + e.getMessage());
        setErrors(1);
        stopAll();
        setOutputDone(); // signal end to receiver(s)
        return false;
      }
      if (sendToErrorRow) {
        // Simply add this row to the error row
        putError(
            getInputRowMeta(), r, 1, errorMessage, meta.getResultFieldName(), "ExecProcess001");
      }
    }

    return true;
  }