public void send(List<Message> pendingMessages, SocketResponseHandler handler)
      throws IOException {
    for (Message message : pendingMessages) {
      writer.write(message);
    }
    writer.flush();

    // Wait until all pending requests have been replied to
    while (handler.receivedResponses() < pendingMessages.size()) {
      reader.read(handler);
    }
  }
 @Override
 public void handleIgnoredMessage() {
   super.handleIgnoredMessage();
   logger.debug(DEFAULT_DEBUG_LOGGING_FORMAT, IGNORED);
 }
 @Override
 public void handleFailureMessage(String code, String message) {
   super.handleFailureMessage(code, message);
   logger.debug("S: [FAILURE %s \"%s\"]", code, message);
 }
 @Override
 public void handleRecordMessage(Value[] fields) {
   super.handleRecordMessage(fields);
   logger.debug("S: RecordMessage{%s}", Arrays.asList(fields));
 }
 @Override
 public void handleSuccessMessage(Map<String, Value> meta) {
   super.handleSuccessMessage(meta);
   logger.debug("S: [SUCCESS %s]", meta);
 }
 @Override
 public void handleAckFailureMessage() {
   super.handleAckFailureMessage();
   logger.debug(DEFAULT_DEBUG_LOGGING_FORMAT, ACK_FAILURE);
 }
 @Override
 public void handleDiscardAllMessage() {
   super.handleDiscardAllMessage();
   logger.debug(DEFAULT_DEBUG_LOGGING_FORMAT, DISCARD_ALL);
 }
 @Override
 public void handlePullAllMessage() {
   super.handlePullAllMessage();
   logger.debug(DEFAULT_DEBUG_LOGGING_FORMAT, PULL_ALL);
 }
 @Override
 public void handleRunMessage(String statement, Map<String, Value> parameters) {
   super.handleRunMessage(statement, parameters);
   logger.debug("S: [RUN \"%s\" %s]", statement, parameters);
 }
 @Override
 public void handleInitializeMessage(String clientNameAndVersion) {
   super.handleInitializeMessage(clientNameAndVersion);
   logger.debug("S: [INITIALIZE \"%s\"]", clientNameAndVersion);
 }