MINIFICPP-1408 thrown objects should be derived from std::exception

Signed-off-by: Arpad Boda <aboda@apache.org>

This closes #938
diff --git a/extensions/sftp/client/SFTPClient.cpp b/extensions/sftp/client/SFTPClient.cpp
index c56b327..94861b9 100644
--- a/extensions/sftp/client/SFTPClient.cpp
+++ b/extensions/sftp/client/SFTPClient.cpp
@@ -67,19 +67,19 @@
 static SFTPError libssh2_sftp_error_to_sftp_error(unsigned long libssh2_sftp_error) {
   switch (libssh2_sftp_error) {
     case LIBSSH2_FX_OK:
-      return SFTPError::SFTP_ERROR_OK;
+      return SFTPError::Ok;
     case LIBSSH2_FX_NO_SUCH_FILE:
     case LIBSSH2_FX_NO_SUCH_PATH:
-      return SFTPError::SFTP_ERROR_FILE_NOT_EXISTS;
+      return SFTPError::FileDoesNotExist;
     case LIBSSH2_FX_FILE_ALREADY_EXISTS:
-      return SFTPError::SFTP_ERROR_FILE_ALREADY_EXISTS;
+      return SFTPError::FileAlreadyExists;
     case LIBSSH2_FX_PERMISSION_DENIED:
     case LIBSSH2_FX_WRITE_PROTECT:
     case LIBSSH2_FX_LOCK_CONFLICT:
-      return SFTPError::SFTP_ERROR_PERMISSION_DENIED;
+      return SFTPError::PermissionDenied;
     case LIBSSH2_FX_NO_CONNECTION:
     case LIBSSH2_FX_CONNECTION_LOST:
-      return SFTPError::SFTP_ERROR_COMMUNICATIONS_FAILURE;
+      return SFTPError::CommunicationFailure;
     case LIBSSH2_FX_EOF:
     case LIBSSH2_FX_FAILURE:
     case LIBSSH2_FX_BAD_MESSAGE:
@@ -94,7 +94,7 @@
     case LIBSSH2_FX_INVALID_FILENAME:
     case LIBSSH2_FX_LINK_LOOP:
     default:
-      return SFTPError::SFTP_ERROR_UNEXPECTED;
+      return SFTPError::Unexpected;
   }
 }
 
@@ -103,16 +103,16 @@
 LastSFTPError::LastSFTPError()
     : sftp_error_set_(false)
     , libssh2_sftp_error_(LIBSSH2_FX_OK)
-    , sftp_error_(SFTPError::SFTP_ERROR_OK) {
+    , sftp_error_(SFTPError::Ok) {
 }
 
-LastSFTPError& LastSFTPError::operator=(unsigned long libssh2_sftp_error) {
+LastSFTPError& LastSFTPError::setLibssh2Error(unsigned long libssh2_sftp_error) {
   sftp_error_set_ = false;
   libssh2_sftp_error_ = libssh2_sftp_error;
   return *this;
 }
 
-LastSFTPError& LastSFTPError::operator=(const SFTPError& sftp_error) {
+LastSFTPError& LastSFTPError::setSftpError(const SFTPError& sftp_error) {
   sftp_error_set_ = true;
   sftp_error_ = sftp_error;
   return *this;
@@ -140,18 +140,7 @@
       hostname_(hostname),
       port_(port),
       username_(username),
-      ssh_known_hosts_(nullptr),
-      strict_host_checking_(false),
-      password_authentication_enabled_(false),
-      public_key_authentication_enabled_(false),
-      data_timeout_(0),
-      send_keepalive_(false),
-      curl_errorbuffer_(CURL_ERROR_SIZE, '\0'),
-      easy_(nullptr),
-      ssh_session_(nullptr),
-      sftp_session_(nullptr),
-      connected_(false),
-      last_error_() {
+      curl_errorbuffer_(CURL_ERROR_SIZE, '\0') {
   easy_ = curl_easy_init();
   if (easy_ == nullptr) {
     throw std::runtime_error("Cannot create curl easy handle");
@@ -496,10 +485,10 @@
     int ssh_errno = libssh2_session_last_errno(ssh_session_);
     /* We can only get the sftp error in this case if the ssh error is a protocol error */
     if (ssh_errno == LIBSSH2_ERROR_SFTP_PROTOCOL) {
-      last_error_ = libssh2_sftp_last_error(sftp_session_);
+      last_error_.setLibssh2Error(libssh2_sftp_last_error(sftp_session_));
       logger_->log_error("Failed to open remote file \"%s\", error: %s", path.c_str(), sftp_strerror(last_error_));
     } else {
-      last_error_ = SFTPError::SFTP_ERROR_IO_ERROR;
+      last_error_.setSftpError(SFTPError::IoError);
       char *err_msg = nullptr;
       libssh2_session_last_error(ssh_session_, &err_msg, nullptr, 0);
       logger_->log_error("Failed to open remote file \"%s\" due to an underlying SSH error: %s", path.c_str(), err_msg);
@@ -516,7 +505,7 @@
   do {
     ssize_t read_ret = libssh2_sftp_read(file_handle, reinterpret_cast<char*>(buf.data()), buf.size());
     if (read_ret < 0) {
-      last_error_ = SFTPError::SFTP_ERROR_IO_ERROR;
+      last_error_.setSftpError(SFTPError::IoError);
       logger_->log_error("Failed to read remote file \"%s\"", path.c_str());
       return false;
     } else if (read_ret == 0) {
@@ -529,7 +518,7 @@
     while (remaining > 0) {
       int write_ret = output.write(buf.data() + (read_ret - remaining), remaining);
       if (write_ret < 0) {
-        last_error_ = LIBSSH2_FX_OK;
+        last_error_.setLibssh2Error(LIBSSH2_FX_OK);
         logger_->log_error("Failed to write output");
         return false;
       }
@@ -538,7 +527,7 @@
   } while (true);
 
   if (expected_size >= 0 && total_read != expected_size) {
-    last_error_ = LIBSSH2_FX_OK;
+    last_error_.setLibssh2Error(LIBSSH2_FX_OK);
     logger_->log_error("Remote file \"%s\" has unexpected size, expected: %ld, actual: %lu", path.c_str(), expected_size, total_read);
     return false;
   }
@@ -554,10 +543,10 @@
     int ssh_errno = libssh2_session_last_errno(ssh_session_);
     /* We can only get the sftp error in this case if the ssh error is a protocol error */
     if (ssh_errno == LIBSSH2_ERROR_SFTP_PROTOCOL) {
-      last_error_ = libssh2_sftp_last_error(sftp_session_);
+      last_error_.setLibssh2Error(libssh2_sftp_last_error(sftp_session_));
       logger_->log_error("Failed to open remote file \"%s\", error: %s", path.c_str(), sftp_strerror(last_error_));
     } else {
-      last_error_ = SFTPError::SFTP_ERROR_IO_ERROR;
+      last_error_.setSftpError(SFTPError::IoError);
       char *err_msg = nullptr;
       libssh2_session_last_error(ssh_session_, &err_msg, nullptr, 0);
       logger_->log_error("Failed to open remote file \"%s\" due to an underlying SSH error: %s", path.c_str(), err_msg);
@@ -579,7 +568,7 @@
   do {
     int read_ret = input.read(buf.data(), buf.size());
     if (read_ret < 0) {
-      last_error_ = LIBSSH2_FX_OK;
+      last_error_.setLibssh2Error(LIBSSH2_FX_OK);
       logger_->log_error("Error while reading input");
       return false;
     } else if (read_ret == 0) {
@@ -592,7 +581,7 @@
     while (remaining > 0) {
       int write_ret = libssh2_sftp_write(file_handle, reinterpret_cast<char*>(buf.data() + (read_ret - remaining)), remaining);
       if (write_ret < 0) {
-        last_error_ = SFTPError::SFTP_ERROR_IO_ERROR;
+        last_error_.setSftpError(SFTPError::IoError);
         logger_->log_error("Failed to write remote file \"%s\"", path.c_str());
         return false;
       }
@@ -602,7 +591,7 @@
   } while (true);
 
   if (expected_size >= 0 && total_read != expected_size) {
-    last_error_ = LIBSSH2_FX_OK;
+    last_error_.setLibssh2Error(LIBSSH2_FX_OK);
     logger_->log_error("Input has unexpected size, expected: %ld, actual: %lu", path.c_str(), expected_size, total_read);
     return false;
   }
@@ -634,7 +623,7 @@
       }
       continue;
     }
-    last_error_ = libssh2_sftp_last_error(sftp_session_);
+    last_error_.setLibssh2Error(libssh2_sftp_last_error(sftp_session_));
     logger_->log_error("Failed to rename remote file \"%s\" to \"%s\", error: %s",
         source_path.c_str(),
         target_path.c_str(),
@@ -646,7 +635,7 @@
 
 bool SFTPClient::createDirectoryHierarchy(const std::string& path) {
   if (path.empty()) {
-    last_error_ = LIBSSH2_FX_OK;
+    last_error_.setLibssh2Error(LIBSSH2_FX_OK);
     return false;
   }
   bool absolute = path[0] == '/';
@@ -664,7 +653,7 @@
       if (err != LIBSSH2_FX_FILE_ALREADY_EXISTS &&
           err != LIBSSH2_FX_FAILURE &&
           err != LIBSSH2_FX_PERMISSION_DENIED) {
-        last_error_ = err;
+        last_error_.setLibssh2Error(err);
         logger_->log_error("Failed to create remote directory \"%s\", error: %s", current_dir.c_str(), sftp_strerror(last_error_));
         return false;
       } else {
@@ -677,7 +666,7 @@
 
 bool SFTPClient::removeFile(const std::string& path) {
   if (libssh2_sftp_unlink(sftp_session_, path.c_str()) != 0) {
-    last_error_ = libssh2_sftp_last_error(sftp_session_);
+    last_error_.setLibssh2Error(libssh2_sftp_last_error(sftp_session_));
     logger_->log_error("Failed to remove remote file \"%s\", error: %s", path.c_str(), sftp_strerror(last_error_));
     return false;
   }
@@ -686,7 +675,7 @@
 
 bool SFTPClient::removeDirectory(const std::string& path) {
   if (libssh2_sftp_rmdir(sftp_session_, path.c_str()) != 0) {
-    last_error_ = libssh2_sftp_last_error(sftp_session_);
+    last_error_.setLibssh2Error(libssh2_sftp_last_error(sftp_session_));
     logger_->log_error("Failed to remove remote directory \"%s\", error: %s", path.c_str(), sftp_strerror(last_error_));
     return false;
   }
@@ -702,7 +691,7 @@
                                                           0 /* mode */,
                                                           LIBSSH2_SFTP_OPENDIR);
   if (dir_handle == nullptr) {
-    last_error_ = libssh2_sftp_last_error(sftp_session_);
+    last_error_.setLibssh2Error(libssh2_sftp_last_error(sftp_session_));
     logger_->log_error("Failed to open remote directory \"%s\", error: %s", path.c_str(), sftp_strerror(last_error_));
     return false;
   }
@@ -721,7 +710,7 @@
                                       longentry.size(),
                                       &attrs);
     if (ret < 0) {
-      last_error_ = libssh2_sftp_last_error(sftp_session_);
+      last_error_.setLibssh2Error(libssh2_sftp_last_error(sftp_session_));
       logger_->log_error("Failed to read remote directory \"%s\", error: %s", path.c_str(), sftp_strerror(last_error_));
       return false;
     } else if (ret == 0) {
@@ -746,7 +735,7 @@
                             path.length(),
                             follow_symlinks ? LIBSSH2_SFTP_STAT : LIBSSH2_SFTP_LSTAT,
                             &result) != 0) {
-    last_error_ = libssh2_sftp_last_error(sftp_session_);
+    last_error_.setLibssh2Error(libssh2_sftp_last_error(sftp_session_));
     logger_->log_debug("Failed to stat remote path \"%s\", error: %s", path.c_str(), sftp_strerror(last_error_));
     return false;
   }
@@ -790,7 +779,7 @@
                            path.length(),
                            LIBSSH2_SFTP_SETSTAT,
                            &attrs) != 0) {
-    last_error_ = libssh2_sftp_last_error(sftp_session_);
+    last_error_.setLibssh2Error(libssh2_sftp_last_error(sftp_session_));
     logger_->log_debug("Failed to setstat on remote path \"%s\", error: %s", path.c_str(), sftp_strerror(last_error_));
     return false;
   }
diff --git a/extensions/sftp/client/SFTPClient.h b/extensions/sftp/client/SFTPClient.h
index 6bf2af8..aba8307 100644
--- a/extensions/sftp/client/SFTPClient.h
+++ b/extensions/sftp/client/SFTPClient.h
@@ -26,6 +26,8 @@
 #include <string>
 #include <vector>
 
+#include "Exception.h"
+#include "utils/Enum.h"
 #include "utils/HTTPClient.h"
 #include "core/logging/Logger.h"
 #include "core/logging/LoggerConfiguration.h"
@@ -39,14 +41,24 @@
 namespace minifi {
 namespace utils {
 
-enum class SFTPError : uint8_t {
-  SFTP_ERROR_OK = 0,
-  SFTP_ERROR_PERMISSION_DENIED,
-  SFTP_ERROR_FILE_NOT_EXISTS,
-  SFTP_ERROR_FILE_ALREADY_EXISTS,
-  SFTP_ERROR_COMMUNICATIONS_FAILURE,
-  SFTP_ERROR_IO_ERROR,
-  SFTP_ERROR_UNEXPECTED
+SMART_ENUM(SFTPError,
+    (Ok, "Ok"),
+    (PermissionDenied, "Permission denied"),
+    (FileDoesNotExist, "File does not exist"),
+    (FileAlreadyExists, "File already exists"),
+    (CommunicationFailure, "Communication failure"),
+    (IoError, "IO error"),
+    (Unexpected, "Unexpected"));
+
+class SFTPException : public Exception {
+ public:
+  explicit SFTPException(const SFTPError err)
+      :Exception{ExceptionType::FILE_OPERATION_EXCEPTION, fmt::format("SFTP Error: {0}", err.toString())},
+      error_{err}
+  {}
+  SFTPError error() const noexcept { return error_; }
+ private:
+  SFTPError error_;
 };
 
 class LastSFTPError {
@@ -58,8 +70,8 @@
   LastSFTPError& operator=(const LastSFTPError&) = delete;
   LastSFTPError& operator=(LastSFTPError&&) = delete;
 
-  LastSFTPError& operator=(unsigned long libssh2_sftp_error);
-  LastSFTPError& operator=(const SFTPError& sftp_error);
+  LastSFTPError& setLibssh2Error(unsigned long libssh2_sftp_error);
+  LastSFTPError& setSftpError(const SFTPError& sftp_error);
   operator unsigned long() const;
   operator SFTPError() const;
 
@@ -148,7 +160,6 @@
   bool setAttributes(const std::string& path, const SFTPAttributes& attrs);
 
  protected:
-
   /*
    * The maximum size libssh2 is willing to read or write in one go is 30000 bytes.
    * (See MAX_SFTP_OUTGOING_SIZE and MAX_SFTP_READ_SIZE).
@@ -162,27 +173,27 @@
   const uint16_t port_;
   const std::string username_;
 
-  LIBSSH2_KNOWNHOSTS *ssh_known_hosts_;
-  bool strict_host_checking_;
+  LIBSSH2_KNOWNHOSTS *ssh_known_hosts_ = nullptr;
+  bool strict_host_checking_ = false;
 
-  bool password_authentication_enabled_;
+  bool password_authentication_enabled_ = false;
   std::string password_;
 
-  bool public_key_authentication_enabled_;
+  bool public_key_authentication_enabled_ = false;
   std::string private_key_file_path_;
   std::string private_key_passphrase_;
 
-  int64_t data_timeout_;
+  int64_t data_timeout_ = 0;
 
-  bool send_keepalive_;
+  bool send_keepalive_ = false;
 
   std::vector<char> curl_errorbuffer_;
 
-  CURL *easy_;
-  LIBSSH2_SESSION *ssh_session_;
-  LIBSSH2_SFTP *sftp_session_;
+  CURL *easy_ = nullptr;
+  LIBSSH2_SESSION *ssh_session_ = nullptr;
+  LIBSSH2_SFTP *sftp_session_ = nullptr;
 
-  bool connected_;
+  bool connected_ = false;
 
   LastSFTPError last_error_;
 };
diff --git a/extensions/sftp/processors/FetchSFTP.cpp b/extensions/sftp/processors/FetchSFTP.cpp
index d5bc1f9..560e98f 100644
--- a/extensions/sftp/processors/FetchSFTP.cpp
+++ b/extensions/sftp/processors/FetchSFTP.cpp
@@ -159,7 +159,7 @@
 
 int64_t FetchSFTP::WriteCallback::process(const std::shared_ptr<io::BaseStream>& stream) {
   if (!client_.getFile(remote_file_, *stream)) {
-    throw client_.getLastError();
+    throw utils::SFTPException{client_.getLastError()};
   }
   return stream->size();
 }
@@ -214,18 +214,19 @@
   WriteCallback write_callback(remote_file, *client);
   try {
     session->write(flow_file, &write_callback);
-  } catch (const utils::SFTPError& error) {
-    switch (error) {
-      case utils::SFTPError::SFTP_ERROR_PERMISSION_DENIED:
+  } catch (const utils::SFTPException& ex) {
+    logger_->log_debug(ex.what());
+    switch (ex.error().value()) {
+      case utils::SFTPError::PermissionDenied:
         session->transfer(flow_file, PermissionDenied);
         put_connection_back_to_cache();
         return;
-      case utils::SFTPError::SFTP_ERROR_FILE_NOT_EXISTS:
+      case utils::SFTPError::FileDoesNotExist:
         session->transfer(flow_file, NotFound);
         put_connection_back_to_cache();
         return;
-      case utils::SFTPError::SFTP_ERROR_COMMUNICATIONS_FAILURE:
-      case utils::SFTPError::SFTP_ERROR_IO_ERROR:
+      case utils::SFTPError::CommunicationFailure:
+      case utils::SFTPError::IoError:
         session->transfer(flow_file, CommsFailure);
         return;
       default:
diff --git a/extensions/sftp/processors/PutSFTP.cpp b/extensions/sftp/processors/PutSFTP.cpp
index e9c1a8f..1cb6115 100644
--- a/extensions/sftp/processors/PutSFTP.cpp
+++ b/extensions/sftp/processors/PutSFTP.cpp
@@ -215,7 +215,7 @@
       *stream,
       conflict_resolution_ == CONFLICT_RESOLUTION_REPLACE /*overwrite*/,
       stream->size() /*expected_size*/)) {
-    throw client_.getLastError();
+    throw utils::SFTPException{client_.getLastError()};
   }
   return stream->size();
 }
@@ -325,7 +325,7 @@
     std::string target_path = utils::file::FileUtils::concat_path(remote_path, filename, true /*force_posix*/);
     LIBSSH2_SFTP_ATTRIBUTES attrs;
     if (!client->stat(target_path, true /*follow_symlinks*/, attrs)) {
-      if (client->getLastError() != utils::SFTPError::SFTP_ERROR_FILE_NOT_EXISTS) {
+      if (client->getLastError() != utils::SFTPError::FileDoesNotExist) {
         logger_->log_error("Failed to stat %s", target_path.c_str());
         session->transfer(flow_file, Failure);
         return true;
@@ -362,7 +362,7 @@
           possible_resolved_filename = possible_resolved_filename_ss.str();
           std::string possible_resolved_path = utils::file::FileUtils::concat_path(remote_path, possible_resolved_filename, true /*force_posix*/);
           if (!client->stat(possible_resolved_path, true /*follow_symlinks*/, attrs)) {
-            if (client->getLastError() == utils::SFTPError::SFTP_ERROR_FILE_NOT_EXISTS) {
+            if (client->getLastError() == utils::SFTPError::FileDoesNotExist) {
               unique_name_generated = true;
               break;
             } else {
@@ -427,7 +427,8 @@
   ReadCallback read_callback(target_path.c_str(), *client, conflict_resolution_);
   try {
     session->read(flow_file, &read_callback);
-  } catch (const utils::SFTPError&) {
+  } catch (const utils::SFTPException& ex) {
+    logger_->log_debug(ex.what());
     session->transfer(flow_file, Failure);
     return true;
   }
diff --git a/extensions/sftp/processors/SFTPProcessorBase.cpp b/extensions/sftp/processors/SFTPProcessorBase.cpp
index 97f0449..96e97d5 100644
--- a/extensions/sftp/processors/SFTPProcessorBase.cpp
+++ b/extensions/sftp/processors/SFTPProcessorBase.cpp
@@ -430,7 +430,7 @@
   if (!disable_directory_listing) {
     LIBSSH2_SFTP_ATTRIBUTES attrs;
     if (!client.stat(remote_path, true /*follow_symlinks*/, attrs)) {
-      if (client.getLastError() != utils::SFTPError::SFTP_ERROR_FILE_NOT_EXISTS) {
+      if (client.getLastError() != utils::SFTPError::FileDoesNotExist) {
         logger_->log_error("Failed to stat %s", remote_path.c_str());
       }
       should_create_directory = true;
@@ -448,10 +448,10 @@
       LIBSSH2_SFTP_ATTRIBUTES attrs;
       if (!client.stat(remote_path, true /*follow_symlinks*/, attrs)) {
         auto last_error = client.getLastError();
-        if (last_error == utils::SFTPError::SFTP_ERROR_FILE_NOT_EXISTS) {
+        if (last_error == utils::SFTPError::FileDoesNotExist) {
           logger_->log_error("Could not find remote directory %s after creating it", remote_path.c_str());
           return CreateDirectoryHierarchyError::CREATE_DIRECTORY_HIERARCHY_ERROR_NOT_FOUND;
-        } else if (last_error == utils::SFTPError::SFTP_ERROR_PERMISSION_DENIED) {
+        } else if (last_error == utils::SFTPError::PermissionDenied) {
           logger_->log_error("Permission denied when reading remote directory %s after creating it", remote_path.c_str());
           return CreateDirectoryHierarchyError::CREATE_DIRECTORY_HIERARCHY_ERROR_PERMISSION_DENIED;
         } else {
diff --git a/libminifi/src/core/yaml/YamlConfiguration.cpp b/libminifi/src/core/yaml/YamlConfiguration.cpp
index 3c80f37..a53c0db 100644
--- a/libminifi/src/core/yaml/YamlConfiguration.cpp
+++ b/libminifi/src/core/yaml/YamlConfiguration.cpp
@@ -250,11 +250,10 @@
         parentGroup->addProcessor(processor);
       }
     } else {
-      throw new std::invalid_argument("Cannot instantiate a MiNiFi instance without a defined Processors configuration node.");
+      throw std::invalid_argument("Cannot instantiate a MiNiFi instance without a defined Processors configuration node.");
     }
   } else {
-    throw new std::invalid_argument("Cannot instantiate a MiNiFi instance without a defined "
-                                    "Processors configuration node.");
+    throw std::invalid_argument("Cannot instantiate a MiNiFi instance without a defined Processors configuration node.");
   }
 }
 
diff --git a/libminifi/test/RandomServerSocket.cpp b/libminifi/test/RandomServerSocket.cpp
index f912fbb..a532f44 100644
--- a/libminifi/test/RandomServerSocket.cpp
+++ b/libminifi/test/RandomServerSocket.cpp
@@ -27,6 +27,7 @@
 #include <string>
 #include <memory>
 
+#include "Exception.h"
 #include "core/logging/Logger.h"
 #include "core/logging/LoggerConfiguration.h"
 
@@ -44,7 +45,7 @@
     auto logger = logging::LoggerFactory<RandomServerSocket>::getLogger();
     for (uint16_t i = 0; i < retries; ++i) {
       setPort(dis(gen));
-      if (initialize() == 0) {
+      if (RandomServerSocket::initialize() == 0) {
         logger->log_info("Created socket listens on generated port: %hu", getPort());
         return;
       }
@@ -52,7 +53,7 @@
     std::stringstream error;
     error << "Couldn't bind to a port between " << offset << " and " << offset+range << " in " << retries << " try!";
     logger->log_error(error.str().c_str());
-    throw error.str();
+    throw Exception{ExceptionType::GENERAL_EXCEPTION, error.str()};
   }
 
 } /* namespace io */