public synchronized void openServerSocket(BaseStep baseStep) throws IOException {
    this.baseStep = baseStep;
    int portNumber = Integer.parseInt(baseStep.environmentSubstitute(port));

    SocketRepository socketRepository = baseStep.getSocketRepository();
    serverSocket =
        socketRepository.openServerSocket(
            portNumber, baseStep.getTransMeta().getName() + " - " + baseStep.toString());

    // Add this socket to the steps server socket list
    // That way, the socket can be closed during transformation cleanup
    // That is called when the cluster has finished processing.
    //
    baseStep.getServerSockets().add(serverSocket);
  }
  public synchronized BlockingRowSet openReaderSocket(final BaseStep baseStep)
      throws IOException, KettleException {
    this.baseStep = baseStep;

    final BlockingRowSet rowSet = new BlockingRowSet(baseStep.getTransMeta().getSizeRowset());

    // Make sure we handle the case with multiple step copies running on a
    // slave...
    //
    rowSet.setThreadNameFromToCopy(sourceStep, sourceStepCopyNr, targetStep, targetStepCopyNr);
    rowSet.setRemoteSlaveServerName(targetSlaveServerName);

    final int portNumber = Integer.parseInt(baseStep.environmentSubstitute(port));
    final String realHostname = baseStep.environmentSubstitute(hostname);

    // Connect to the server socket (started during BaseStep.init())
    // Because the accept() call on the server socket can be called after we
    // reached this code
    // it is best to build in a retry loop with a time-out here.
    //
    long startTime = System.currentTimeMillis();
    boolean connected = false;
    KettleException lastException = null;

    // // timeout with retry until connected
    while (!connected
        && (TIMEOUT_IN_SECONDS > (System.currentTimeMillis() - startTime) / 1000)
        && !baseStep.isStopped()) {
      try {
        socket = new Socket();
        socket.setReuseAddress(true);

        baseStep.logDetailed(
            "Step variable MASTER_HOST : [" + baseStep.getVariable("MASTER_HOST") + "]");
        baseStep.logDetailed(
            "Opening client (reader) socket to server ["
                + Const.NVL(realHostname, "")
                + ":"
                + port
                + "]");
        socket.connect(new InetSocketAddress(realHostname, portNumber), 5000);

        connected = true;

        if (compressingStreams) {
          gzipInputStream = new GZIPInputStream(socket.getInputStream());
          bufferedInputStream = new BufferedInputStream(gzipInputStream, bufferSize);
        } else {
          bufferedInputStream = new BufferedInputStream(socket.getInputStream(), bufferSize);
        }
        inputStream = new DataInputStream(bufferedInputStream);

        lastException = null;
      } catch (Exception e) {
        lastException =
            new KettleException(
                "Unable to open socket to server " + realHostname + " port " + portNumber, e);
      }
      if (lastException != null) {
        // Sleep for a while
        try {
          Thread.sleep(250);
        } catch (InterruptedException e) {
          if (socket != null) {
            socket.shutdownInput();
            socket.shutdownOutput();
            socket.close();
            baseStep.logDetailed(
                "Closed connection to server socket to read rows from remote step on server "
                    + realHostname
                    + " port "
                    + portNumber
                    + " - Local port="
                    + socket.getLocalPort());
          }

          throw new KettleException(
              "Interrupted while trying to connect to server socket: " + e.toString());
        }
      }
    }

    // See if all was OK...
    if (lastException != null) {

      baseStep.logError("Error initialising step: " + lastException.toString());
      if (socket != null) {
        socket.shutdownInput();
        socket.shutdownOutput();
        socket.close();
        baseStep.logDetailed(
            "Closed connection to server socket to read rows from remote step on server "
                + realHostname
                + " port "
                + portNumber
                + " - Local port="
                + socket.getLocalPort());
      }
      throw lastException;
    } else {
      if (inputStream == null)
        throw new KettleException(
            "Unable to connect to the SocketWriter in the "
                + TIMEOUT_IN_SECONDS
                + "s timeout period.");
    }

    baseStep.logDetailed(
        "Opened connection to server socket to read rows from remote step on server "
            + realHostname
            + " port "
            + portNumber
            + " - Local port="
            + socket.getLocalPort());

    // Create a thread to take care of the reading from the client socket.
    // The rows read will be put in a RowSet buffer.
    // That buffer will hand over the rows to the step that has this RemoteStep
    // object defined
    // as a remote input step.
    //
    Runnable runnable =
        new Runnable() {
          public void run() {
            try {

              // First read the row meta data from the socket...
              //
              RowMetaInterface rowMeta = null;
              while (!baseStep.isStopped() && rowMeta == null) {
                try {
                  rowMeta = new RowMeta(inputStream);
                } catch (SocketTimeoutException e) {
                  rowMeta = null;
                }
              }

              if (rowMeta == null) {
                throw new KettleEOFException(); // leave now.
              }

              // And a first row of data...
              //
              Object[] rowData = getRowOfData(rowMeta);

              // Now get the data itself, row by row...
              //
              while (rowData != null && !baseStep.isStopped()) {
                baseStep.incrementLinesInput();
                baseStep.decrementLinesRead();

                if (baseStep.log.isDebug())
                  baseStep.logDebug("Received row from remote step: " + rowMeta.getString(rowData));

                baseStep.putRowTo(rowMeta, rowData, rowSet);
                baseStep.decrementLinesWritten();
                rowData = getRowOfData(rowMeta);
              }
            } catch (KettleEOFException e) {
              // Nothing, we're simply done reading...
              //
              if (baseStep.log.isDebug())
                baseStep.logDebug(
                    "Finished reading from remote step on server "
                        + hostname
                        + " port "
                        + portNumber);

            } catch (Exception e) {
              baseStep.logError("Error reading from client socket to remote step", e);
              baseStep.setErrors(1);
              baseStep.stopAll();
            } finally {
              // Close the input socket
              if (socket != null && !socket.isClosed() && !socket.isInputShutdown()) {
                try {
                  socket.shutdownInput();
                } catch (Exception e) {
                  baseStep.logError(
                      "Error shutting down input channel on client socket connection to remote step",
                      e);
                }
              }
              if (socket != null && !socket.isClosed() && !socket.isOutputShutdown()) {
                try {
                  socket.shutdownOutput();
                } catch (Exception e) {
                  baseStep.logError(
                      "Error shutting down output channel on client socket connection to remote step",
                      e);
                }
              }
              if (socket != null && !socket.isClosed()) {
                try {
                  socket.close();
                } catch (Exception e) {
                  baseStep.logError(
                      "Error shutting down client socket connection to remote step", e);
                }
              }
              if (inputStream != null) {
                try {
                  inputStream.close();
                } catch (Exception e) {
                  baseStep.logError(
                      "Error closing input stream on socket connection to remote step", e);
                }
                inputStream = null;
              }
              if (bufferedInputStream != null) {
                try {
                  bufferedInputStream.close();
                } catch (Exception e) {
                  baseStep.logError(
                      "Error closing input stream on socket connection to remote step", e);
                }
              }
              bufferedInputStream = null;
              if (gzipInputStream != null) {
                try {
                  gzipInputStream.close();
                } catch (Exception e) {
                  baseStep.logError(
                      "Error closing input stream on socket connection to remote step", e);
                }
              }
              gzipInputStream = null;
              baseStep.logDetailed(
                  "Closed connection to server socket to read rows from remote step on server "
                      + realHostname
                      + " port "
                      + portNumber
                      + " - Local port="
                      + socket.getLocalPort());
            }

            // signal baseStep that nothing else comes from this step.
            //
            rowSet.setDone();
          }
        };
    new Thread(runnable).start();

    return rowSet;
  }
  /**
   * Open a socket for writing.
   *
   * @return the RowSet created that will accept the rows for the remote step
   * @throws IOException
   */
  public synchronized BlockingRowSet openWriterSocket() throws IOException {

    // Create an output row set: to be added to BaseStep.outputRowSets
    //
    final BlockingRowSet rowSet = new BlockingRowSet(baseStep.getTransMeta().getSizeRowset());

    // Set the details for the source and target step as well as the target slave server.
    // This will help us determine the pre-calculated partition nr later in the game. (putRow())
    //
    rowSet.setThreadNameFromToCopy(sourceStep, sourceStepCopyNr, targetStep, targetStepCopyNr);
    rowSet.setRemoteSlaveServerName(targetSlaveServerName);

    // Start a thread that will read out the output row set and send the data over the wire...
    // This will make everything else transparent, copying, distributing, including partitioning,
    // etc.
    //
    Runnable runnable =
        new Runnable() {

          public void run() {
            try {
              // Accept the socket, create a connection
              // This blocks until something comes through...
              //
              socket = serverSocket.accept();

              // Create the output stream...
              if (compressingStreams) {
                gzipOutputStream = new GZIPOutputStream(socket.getOutputStream(), 50000);
                bufferedOutputStream = new BufferedOutputStream(gzipOutputStream, bufferSize);
              } else {
                bufferedOutputStream =
                    new BufferedOutputStream(socket.getOutputStream(), bufferSize);
              }
              outputStream = new DataOutputStream(bufferedOutputStream);

              baseStep.logBasic(
                  "Server socket accepted for port ["
                      + port
                      + "], reading from server "
                      + targetSlaveServerName);

              // get a row of data...
              //
              Object[] rowData = baseStep.getRowFrom(rowSet);
              if (rowData != null) {
                rowSet.getRowMeta().writeMeta(outputStream);
              }

              // Send that row to the remote step
              //
              while (rowData != null && !baseStep.isStopped()) {
                // It's too confusing to count these twice, so decrement
                baseStep.decrementLinesRead();
                baseStep.decrementLinesWritten();

                // Write the row to the remote step via the output stream....
                //
                rowSet.getRowMeta().writeData(outputStream, rowData);
                baseStep.incrementLinesOutput();

                if (baseStep.log.isDebug())
                  baseStep.logDebug(
                      "Sent row to port " + port + " : " + rowSet.getRowMeta().getString(rowData));
                rowData = baseStep.getRowFrom(rowSet);
              }

              if (compressingStreams) {
                outputStream.flush();
                gzipOutputStream.finish();
              } else {
                outputStream.flush();
              }

            } catch (Exception e) {
              baseStep.logError("Error writing to remote step", e);
              baseStep.setErrors(1);
              baseStep.stopAll();
            } finally {
              try {
                socket.shutdownOutput();
              } catch (Exception e) {
                baseStep.logError(
                    "Error shutting down output channel on the server socket of remote step", e);
                baseStep.setErrors(1L);
                baseStep.stopAll();
              }
              try {
                if (outputStream != null) {
                  outputStream.flush();
                  outputStream.close();
                  bufferedOutputStream.close();
                  if (gzipOutputStream != null) {
                    gzipOutputStream.close();
                  }
                }
              } catch (Exception e) {
                baseStep.logError(
                    "Error shutting down output streams on the server socket of remote step", e);
                baseStep.setErrors(1L);
                baseStep.stopAll();
              }
              outputStream = null;
              bufferedOutputStream = null;
              gzipOutputStream = null;

              //
              // Now we can't close the server socket.
              // This would immediately kill all the remaining data on the client side.
              // The close of the server socket will happen when all the transformation in the
              // cluster have finished.
              // Then Trans.cleanup() will be called.
            }
          }
        };

    // Fire this off in the in a separate thread...
    //
    new Thread(runnable).start();

    // Return the rowSet to be added to the output row set of baseStep
    //
    return rowSet;
  }