This is an automated email from the ASF dual-hosted git repository.
szaszm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi-minifi-cpp.git
The following commit(s) were added to refs/heads/main by this push:
new 8c3152cb4 MINIFICPP-2769 Move Kafka Extension to stable C API (#2175)
8c3152cb4 is described below
commit 8c3152cb400605b8f78c00cb05b1267925bb0b5d
Author: Martin Zink <[email protected]>
AuthorDate: Mon Jun 15 16:22:11 2026 +0200
MINIFICPP-2769 Move Kafka Extension to stable C API (#2175)
---
.../ubuntu_22_04_clang_arm_manifest.json | 1 +
CMakeLists.txt | 2 +-
.../include/core/PropertyDefinitionBuilder.h | 22 ++-
.../include/api/core/ProcessContext.h | 4 +-
.../cpp-extension-lib/mocklib/CMakeLists.txt | 6 +-
.../mocklib/include/MockProcessContext.h | 2 +-
.../mocklib/src/MockProcessContext.cpp | 2 +-
.../mocklib/src/mock-minifi-c.cpp | 4 +-
.../cpp-extension-lib/src/core/ProcessContext.cpp | 4 +-
extensions/kafka/CMakeLists.txt | 10 +-
extensions/kafka/ConsumeKafka.cpp | 171 ++++++-----------
extensions/kafka/ConsumeKafka.h | 37 ++--
.../processors => kafka}/ExtensionInitializer.cpp | 19 +-
extensions/kafka/KafkaConnection.cpp | 44 +----
extensions/kafka/KafkaConnection.h | 15 --
extensions/kafka/KafkaProcessorBase.cpp | 20 +-
extensions/kafka/KafkaProcessorBase.h | 18 +-
extensions/kafka/PublishKafka.cpp | 208 ++++++++++-----------
extensions/kafka/PublishKafka.h | 22 +--
extensions/kafka/rdkafka_utils.cpp | 64 ++++++-
extensions/kafka/rdkafka_utils.h | 22 ++-
extensions/kafka/tests/CMakeLists.txt | 6 +-
extensions/kafka/tests/PublishKafkaTests.cpp | 79 ++++----
extensions/llamacpp/CMakeLists.txt | 2 +-
.../{processors => }/ExtensionInitializer.cpp | 6 +-
libminifi/src/minifi-c.cpp | 10 +-
minifi-api/include/minifi-c/minifi-c.h | 2 +-
minifi-api/minifi-c-api.def | 2 +-
28 files changed, 379 insertions(+), 425 deletions(-)
diff --git a/.github/references/ubuntu_22_04_clang_arm_manifest.json
b/.github/references/ubuntu_22_04_clang_arm_manifest.json
index 863fe0405..976ba021b 100644
--- a/.github/references/ubuntu_22_04_clang_arm_manifest.json
+++ b/.github/references/ubuntu_22_04_clang_arm_manifest.json
@@ -579,6 +579,7 @@
"Max Poll Time": {
"name": "Max Poll Time",
"description": "Specifies the maximum amount of time
the consumer can use for polling data from the brokers. Polling is a blocking
operation, so the upper limit of this value is specified in 4 seconds.",
+ "validator": "TIME_PERIOD_VALIDATOR",
"required": "true",
"sensitive": "false",
"expressionLanguageScope": "NONE",
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 168aa6f24..5af9c071c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -750,7 +750,7 @@ set(EXTENSIONS_ENABLED_BY_DEFAULT (
minifi-grafana-loki
minifi-archive-extensions
minifi-mqtt-extensions
- minifi-rdkafka-extensions
+ minifi-kafka
minifi-pdh
minifi-prometheus
minifi-rocksdb-repos
diff --git a/core-framework/common/include/core/PropertyDefinitionBuilder.h
b/core-framework/common/include/core/PropertyDefinitionBuilder.h
index a9640837e..d206720e8 100644
--- a/core-framework/common/include/core/PropertyDefinitionBuilder.h
+++ b/core-framework/common/include/core/PropertyDefinitionBuilder.h
@@ -27,7 +27,21 @@ namespace org::apache::nifi::minifi::core {
namespace detail {
template<typename... Types>
inline constexpr auto TypeNames = std::array<std::string_view,
sizeof...(Types)>{core::className<Types>()...};
-}
+
+template <size_t N>
+struct StringLiteral {
+ char value[N];
+ constexpr StringLiteral(const char (&str)[N]) { // NOLINT(runtime/explicit)
+ for (size_t i = 0; i < N; ++i) {
+ value[i] = str[i];
+ }
+ }
+};
+
+// A variable template that creates permanent static memory for the span to
point to
+template <StringLiteral str>
+inline constexpr auto StaticAllowedType = std::array<std::string_view,
1>{std::string_view{str.value, sizeof(str.value) - 1}};
+} // namespace detail
template<size_t NumAllowedValues = 0>
struct PropertyDefinitionBuilder {
@@ -81,6 +95,12 @@ struct PropertyDefinitionBuilder {
return *this;
}
+ template <detail::StringLiteral TypeName>
+ constexpr PropertyDefinitionBuilder<NumAllowedValues> withAllowedType() {
+ property.allowed_types = detail::StaticAllowedType<TypeName>;
+ return *this;
+ }
+
constexpr PropertyDefinitionBuilder<NumAllowedValues> withValidator(const
PropertyValidator& property_validator) {
#if defined(__clang__) && (__clang_major__ <= 18)
property.validator = &property_validator;
diff --git
a/extension-framework/cpp-extension-lib/include/api/core/ProcessContext.h
b/extension-framework/cpp-extension-lib/include/api/core/ProcessContext.h
index ee9eb0f72..87209577c 100644
--- a/extension-framework/cpp-extension-lib/include/api/core/ProcessContext.h
+++ b/extension-framework/cpp-extension-lib/include/api/core/ProcessContext.h
@@ -44,7 +44,7 @@ class ProcessContext {
[[nodiscard]] virtual bool hasNonEmptyProperty(std::string_view name) const
= 0;
[[nodiscard]] virtual std::map<std::string, std::string>
getDynamicProperties(const FlowFile* flow_file) const = 0;
- [[nodiscard]] virtual std::expected<utils::net::SslData, std::error_code>
getSslData(std::string_view name) const = 0;
+ [[nodiscard]] virtual std::expected<utils::net::SslData, std::error_code>
getSslData(const minifi::core::PropertyReference& prop) const = 0;
};
class CffiProcessContext : public ProcessContext {
@@ -58,7 +58,7 @@ class CffiProcessContext : public ProcessContext {
[[nodiscard]] std::map<std::string, std::string> getDynamicProperties(const
FlowFile* flow_file) const override;
[[nodiscard]] bool hasNonEmptyProperty(std::string_view name) const override;
- [[nodiscard]] std::expected<utils::net::SslData, std::error_code>
getSslData(std::string_view name) const override;
+ [[nodiscard]] std::expected<utils::net::SslData, std::error_code>
getSslData(const minifi::core::PropertyReference& prop) const override;
private:
[[nodiscard]] std::expected<std::string, std::error_code>
getProperty(std::string_view name, const FlowFile* flow_file) const;
diff --git a/extension-framework/cpp-extension-lib/mocklib/CMakeLists.txt
b/extension-framework/cpp-extension-lib/mocklib/CMakeLists.txt
index f93870f37..28aaede16 100644
--- a/extension-framework/cpp-extension-lib/mocklib/CMakeLists.txt
+++ b/extension-framework/cpp-extension-lib/mocklib/CMakeLists.txt
@@ -19,6 +19,6 @@
file(GLOB SRC_FILES CONFIGURE_DEPENDS "src/*.cpp")
-add_library(libmock-minifi STATIC ${SRC_FILES})
-target_link_libraries(libmock-minifi PUBLIC minifi-cpp-extension-lib
minifi-core-framework-common minifi-c-api)
-target_include_directories(libmock-minifi PUBLIC include)
+add_library(mock-minifi STATIC ${SRC_FILES})
+target_link_libraries(mock-minifi PUBLIC minifi-cpp-extension-lib
minifi-core-framework-common minifi-c-api)
+target_include_directories(mock-minifi PUBLIC include)
diff --git
a/extension-framework/cpp-extension-lib/mocklib/include/MockProcessContext.h
b/extension-framework/cpp-extension-lib/mocklib/include/MockProcessContext.h
index 62a0bc57a..76a13c8d8 100644
--- a/extension-framework/cpp-extension-lib/mocklib/include/MockProcessContext.h
+++ b/extension-framework/cpp-extension-lib/mocklib/include/MockProcessContext.h
@@ -34,7 +34,7 @@ class MockProcessContext : public api::core::ProcessContext {
[[nodiscard]] std::map<std::string, std::string> getDynamicProperties(const
api::core::FlowFile* flow_file) const override;
[[nodiscard]] bool hasNonEmptyProperty(std::string_view name) const override;
- [[nodiscard]] std::expected<api::utils::net::SslData, std::error_code>
getSslData(std::string_view name) const override;
+ [[nodiscard]] std::expected<api::utils::net::SslData, std::error_code>
getSslData(const minifi::core::PropertyReference& prop) const override;
std::map<std::string, std::string, std::less<>> properties_;
diff --git
a/extension-framework/cpp-extension-lib/mocklib/src/MockProcessContext.cpp
b/extension-framework/cpp-extension-lib/mocklib/src/MockProcessContext.cpp
index b8bb1e5fc..afd5b9b2f 100644
--- a/extension-framework/cpp-extension-lib/mocklib/src/MockProcessContext.cpp
+++ b/extension-framework/cpp-extension-lib/mocklib/src/MockProcessContext.cpp
@@ -44,7 +44,7 @@ bool MockProcessContext::hasNonEmptyProperty(const
std::string_view name) const
return properties_.contains(name);
}
-std::expected<api::utils::net::SslData, std::error_code>
MockProcessContext::getSslData(std::string_view) const {
+std::expected<api::utils::net::SslData, std::error_code>
MockProcessContext::getSslData(const minifi::core::PropertyReference&) const {
return api::utils::net::SslData{};
}
} // namespace org::apache::nifi::minifi::mock
diff --git
a/extension-framework/cpp-extension-lib/mocklib/src/mock-minifi-c.cpp
b/extension-framework/cpp-extension-lib/mocklib/src/mock-minifi-c.cpp
index a546f07ad..a50ca712b 100644
--- a/extension-framework/cpp-extension-lib/mocklib/src/mock-minifi-c.cpp
+++ b/extension-framework/cpp-extension-lib/mocklib/src/mock-minifi-c.cpp
@@ -19,6 +19,7 @@
#include "minifi-c.h"
+extern "C" {
MinifiExtension* MINIFI_REGISTER_EXTENSION_FN(MinifiExtensionContext*, const
MinifiExtensionDefinition*) {
throw std::runtime_error("Not implemented");
}
@@ -51,7 +52,7 @@ void
MinifiProcessContextGetDynamicProperties(MinifiProcessContext*, MinifiFlowF
throw std::runtime_error("Not implemented");
}
-MinifiStatus MinifiProcessContextGetSslData(MinifiProcessContext*,
MinifiStringView,
+MinifiStatus MinifiProcessContextGetSslDataFromProperty(MinifiProcessContext*,
MinifiStringView,
void (*)(void* user_ctx, const MinifiSslData* ssl_data), void*) {
throw std::runtime_error("Not implemented");
}
@@ -133,3 +134,4 @@ MinifiStatus
MinifiControllerServiceContextGetProperty(MinifiControllerServiceCo
void (*)(void* user_ctx, MinifiStringView property_value), void*) {
throw std::runtime_error("Not implemented");
}
+} // extern "C"
diff --git a/extension-framework/cpp-extension-lib/src/core/ProcessContext.cpp
b/extension-framework/cpp-extension-lib/src/core/ProcessContext.cpp
index a4dd9505c..90c333f0d 100644
--- a/extension-framework/cpp-extension-lib/src/core/ProcessContext.cpp
+++ b/extension-framework/cpp-extension-lib/src/core/ProcessContext.cpp
@@ -69,10 +69,10 @@ std::map<std::string, std::string>
CffiProcessContext::getDynamicProperties(cons
return result;
}
-std::expected<utils::net::SslData, std::error_code>
CffiProcessContext::getSslData(const std::string_view name) const {
+std::expected<utils::net::SslData, std::error_code>
CffiProcessContext::getSslData(const minifi::core::PropertyReference& prop)
const {
auto ssl_data = utils::net::SslData{};
- if (const auto status = MinifiProcessContextGetSslData(impl_,
utils::minifiStringView(name), [](void* data, const MinifiSslData*
minifi_ssl_data) {
+ if (const auto status = MinifiProcessContextGetSslDataFromProperty(impl_,
utils::minifiStringView(prop.name), [](void* data, const MinifiSslData*
minifi_ssl_data) {
auto* my_ssl_data = static_cast<utils::net::SslData*>(data);
my_ssl_data->ca_loc =
utils::toString(minifi_ssl_data->ca_certificate_file);
my_ssl_data->cert_loc =
utils::toString(minifi_ssl_data->certificate_file);
diff --git a/extensions/kafka/CMakeLists.txt b/extensions/kafka/CMakeLists.txt
index 13f4b3a38..3e7bfa1e2 100644
--- a/extensions/kafka/CMakeLists.txt
+++ b/extensions/kafka/CMakeLists.txt
@@ -27,13 +27,13 @@ include(${CMAKE_SOURCE_DIR}/extensions/ExtensionHeader.txt)
file(GLOB SOURCES "*.cpp")
-add_minifi_library(minifi-rdkafka-extensions SHARED ${SOURCES})
+add_minifi_library(minifi-kafka SHARED ${SOURCES})
-target_link_libraries(minifi-rdkafka-extensions ${LIBMINIFI} Threads::Threads)
+target_link_libraries(minifi-kafka minifi-cpp-extension-lib Threads::Threads)
get_target_property(RDKAFKA_INCLUDE_DIRS rdkafka INCLUDE_DIRECTORIES)
-target_include_directories(minifi-rdkafka-extensions SYSTEM PUBLIC
${RDKAFKA_INCLUDE_DIRS})
-target_link_libraries(minifi-rdkafka-extensions rdkafka)
+target_include_directories(minifi-kafka SYSTEM PUBLIC ${RDKAFKA_INCLUDE_DIRS})
+target_link_libraries(minifi-kafka rdkafka)
-register_extension(minifi-rdkafka-extensions "RDKAFKA EXTENSIONS"
RDKAFKA-EXTENSIONS "This Enables librdkafka functionality including
PublishKafka" "extensions/kafka/tests")
+register_c_api_extension(minifi-kafka "RDKAFKA EXTENSIONS" RDKAFKA-EXTENSIONS
"This Enables librdkafka functionality including PublishKafka"
"extensions/kafka/tests")
diff --git a/extensions/kafka/ConsumeKafka.cpp
b/extensions/kafka/ConsumeKafka.cpp
index adbff6630..c01c20402 100644
--- a/extensions/kafka/ConsumeKafka.cpp
+++ b/extensions/kafka/ConsumeKafka.cpp
@@ -20,14 +20,11 @@
#include <algorithm>
#include <ranges>
-#include "minifi-cpp/core/FlowFile.h"
-#include "core/ProcessSession.h"
-#include "minifi-cpp/core/PropertyValidator.h"
-#include "core/Resource.h"
-#include "utils/OptionalUtils.h"
-#include "utils/AttributeErrors.h"
-#include "utils/ProcessorConfigUtils.h"
+#include "api/utils/ProcessorConfigUtils.h"
#include "minifi-cpp/utils/gsl.h"
+#include "utils/AttributeErrors.h"
+#include "utils/Hash.h"
+#include "utils/OptionalUtils.h"
#include "utils/expected.h"
using namespace std::literals::chrono_literals;
@@ -41,33 +38,27 @@ struct
std::hash<org::apache::nifi::minifi::processors::ConsumeKafka::KafkaMessa
};
namespace org::apache::nifi::minifi::processors {
-// The upper limit for Max Poll Time is 4 seconds. This is because Watchdog
would potentially start
-// reporting issues with the processor health otherwise
-bool consume_kafka::ConsumeKafkaMaxPollTimePropertyValidator::validate(const
std::string_view input) const {
- const auto parsed_time =
parsing::parseDurationMinMax<std::chrono::nanoseconds>(input, 0ms, 4s);
- return parsed_time.has_value();
-}
-
-void ConsumeKafka::initialize() {
- setSupportedProperties(Properties);
- setSupportedRelationships(Relationships);
-}
-void ConsumeKafka::onSchedule(core::ProcessContext& context,
core::ProcessSessionFactory&) {
+MinifiStatus ConsumeKafka::onScheduleImpl(api::core::ProcessContext& context) {
+ using utils::KafkaEncoding;
+ namespace utils = api::utils;
// Required properties
- topic_names_ = utils::string::splitAndTrim(utils::parseProperty(context,
TopicNames), ",");
+ topic_names_ =
minifi::utils::string::splitAndTrim(utils::parseProperty(context, TopicNames),
",");
topic_name_format_ =
utils::parseEnumProperty<consume_kafka::TopicNameFormatEnum>(context,
TopicNameFormat);
commit_policy_ =
utils::parseEnumProperty<consume_kafka::CommitPolicyEnum>(context,
CommitPolicy);
- key_attribute_encoding_ =
utils::parseEnumProperty<utils::KafkaEncoding>(context, KeyAttributeEncoding);
- message_header_encoding_ =
utils::parseEnumProperty<utils::KafkaEncoding>(context, MessageHeaderEncoding);
+ key_attribute_encoding_ = utils::parseEnumProperty<KafkaEncoding>(context,
KeyAttributeEncoding);
+ message_header_encoding_ = utils::parseEnumProperty<KafkaEncoding>(context,
MessageHeaderEncoding);
duplicate_header_handling_ =
utils::parseEnumProperty<consume_kafka::MessageHeaderPolicyEnum>(context,
DuplicateHeaderHandling);
max_poll_time_milliseconds_ = utils::parseDurationProperty(context,
MaxPollTime);
+ if (max_poll_time_milliseconds_ > 4s) {
+ throw Exception(PROCESS_SCHEDULE_EXCEPTION, fmt::format("MaxPollTime ({})
is too large (max is 4s)", max_poll_time_milliseconds_));
+ }
max_poll_records_ = gsl::narrow<uint32_t>(utils::parseU64Property(context,
MaxPollRecords));
// Optional properties
message_demarcator_ = utils::parseOptionalProperty(context,
MessageDemarcator);
- headers_to_add_as_attributes_ = parseOptionalProperty(context,
HeadersToAddAsAttributes)
- | utils::transform([](const std::string& headers_to_add_str) { return
utils::string::splitAndTrim(headers_to_add_str, ","); });
+ headers_to_add_as_attributes_ = utils::parseOptionalProperty(context,
HeadersToAddAsAttributes)
+ | minifi::utils::transform([](const std::string& headers_to_add_str) {
return minifi::utils::string::splitAndTrim(headers_to_add_str, ","); });
if (message_demarcator_ && headers_to_add_as_attributes_) {
logger_->log_error("Message merging with header extraction is not yet
supported");
throw Exception(PROCESS_SCHEDULE_EXCEPTION, "Message merging with header
extraction is not yet supported");
@@ -76,40 +67,9 @@ void ConsumeKafka::onSchedule(core::ProcessContext& context,
core::ProcessSessio
configureNewConnection(context);
if (commit_policy_ ==
consume_kafka::CommitPolicyEnum::CommitFromIncomingFlowFiles) {
setTriggerWhenEmpty(true);
- } else if (context.hasIncomingConnections()) {
- logger_->log_error("Incoming connections are not allowed with {}",
magic_enum::enum_name(commit_policy_));
- throw Exception(PROCESS_SCHEDULE_EXCEPTION, fmt::format("Incoming
connections are not allowed with {}", magic_enum::enum_name(commit_policy_)));
- }
-}
-
-namespace {
-void rebalance_cb(rd_kafka_t* rk, rd_kafka_resp_err_t trigger,
rd_kafka_topic_partition_list_t* partitions, void* /*opaque*/) {
- // Cooperative, incremental assignment is not supported in the current
librdkafka version
- std::shared_ptr<core::logging::Logger>
logger{core::logging::LoggerFactory<ConsumeKafka>::getLogger()};
- logger->log_debug("Rebalance triggered.");
- rd_kafka_resp_err_t assign_error = RD_KAFKA_RESP_ERR_NO_ERROR;
- switch (trigger) {
- case RD_KAFKA_RESP_ERR__ASSIGN_PARTITIONS:
- logger->log_debug("assigned:");
- if (logger->should_log(core::logging::LOG_LEVEL::debug)) {
utils::print_topics_list(*logger, *partitions); }
- assign_error = rd_kafka_assign(rk, partitions);
- break;
-
- case RD_KAFKA_RESP_ERR__REVOKE_PARTITIONS:
- logger->log_debug("revoked:");
- rd_kafka_commit(rk, partitions, /* async = */ 0); // Sync commit, maybe
unnecessary
- if (logger->should_log(core::logging::LOG_LEVEL::debug)) {
utils::print_topics_list(*logger, *partitions); }
- assign_error = rd_kafka_assign(rk, nullptr);
- break;
-
- default:
- logger->log_debug("failed: {}", rd_kafka_err2str(trigger));
- assign_error = rd_kafka_assign(rk, nullptr);
- break;
}
- logger->log_debug("assign failure: {}", rd_kafka_err2str(assign_error));
+ return MINIFI_STATUS_SUCCESS;
}
-} // namespace
void ConsumeKafka::createTopicPartitionList() {
kf_topic_partition_list_ = utils::rd_kafka_topic_partition_list_unique_ptr{
@@ -136,25 +96,17 @@ void ConsumeKafka::createTopicPartitionList() {
}
}
-void ConsumeKafka::extendConfigFromDynamicProperties(const
core::ProcessContext& context) const {
- using utils::setKafkaConfigurationField;
-
- const std::vector<std::string> dynamic_prop_keys =
context.getDynamicPropertyKeys();
- if (dynamic_prop_keys.empty()) { return; }
- logger_->log_info("Loading {} extra kafka configuration fields from
ConsumeKafka dynamic properties:", dynamic_prop_keys.size());
- for (const std::string& key: dynamic_prop_keys) {
- std::string value = context.getDynamicProperty(key)
- | utils::orThrow(fmt::format("This shouldn't happen, dynamic property
{} is expected because we just queried the list of dynamic properties", key));
- logger_->log_info("{}: {}", key.c_str(), value.c_str());
- setKafkaConfigurationField(*conf_, key, value);
+void ConsumeKafka::extendConfigFromDynamicProperties(const
api::core::ProcessContext& context) const {
+ for (const auto& [key, value] : context.getDynamicProperties(nullptr)) {
+ logger_->log_trace("{}: {}", key, value);
+ utils::setKafkaConfigurationField(*conf_, key, value);
}
}
-void ConsumeKafka::configureNewConnection(core::ProcessContext& context) {
- using utils::setKafkaConfigurationField;
-
+void ConsumeKafka::configureNewConnection(api::core::ProcessContext& context) {
conf_ = {rd_kafka_conf_new(), utils::rd_kafka_conf_deleter()};
if (conf_ == nullptr) { throw Exception(PROCESS_SCHEDULE_EXCEPTION, "Failed
to create rd_kafka_conf_t object"); }
+ rd_kafka_conf_set_opaque(conf_.get(), &kafka_opaque_);
// Set rebalance callback for use with coordinated consumer group balancing
// Rebalance handlers are needed for the initial configuration of the
consumer
@@ -162,15 +114,14 @@ void
ConsumeKafka::configureNewConnection(core::ProcessContext& context) {
// Registering a rebalance_cb turns off librdkafka's automatic partition
assignment/revocation and instead delegates that
// responsibility to the application's rebalance_cb.
if (commit_policy_ !=
consume_kafka::CommitPolicyEnum::CommitFromIncomingFlowFiles) {
- rd_kafka_conf_set_rebalance_cb(conf_.get(), rebalance_cb);
+ rd_kafka_conf_set_rebalance_cb(conf_.get(),
utils::KafkaOpaque::rebalanceCallback);
}
- // Uncomment this for librdkafka debug logs:
- // logger_->log_info("Enabling all debug logs for kafka consumer.");
- // setKafkaConfigurationField(*conf_, "debug", "all");
-
setKafkaAuthenticationParameters(context, gsl::make_not_null(conf_.get()));
+ using utils::setKafkaConfigurationField;
+ namespace utils = api::utils;
+
setKafkaConfigurationField(*conf_, "bootstrap.servers",
utils::parseProperty(context, KafkaBrokers));
setKafkaConfigurationField(*conf_, "allow.auto.create.topics", "true");
setKafkaConfigurationField(*conf_,
@@ -188,7 +139,7 @@ void
ConsumeKafka::configureNewConnection(core::ProcessContext& context) {
extendConfigFromDynamicProperties(context);
std::array<char, 512U> errstr{};
- consumer_ = {rd_kafka_new(RD_KAFKA_CONSUMER, conf_.release(), errstr.data(),
errstr.size()), utils::rd_kafka_consumer_deleter()};
+ consumer_ = {rd_kafka_new(RD_KAFKA_CONSUMER, conf_.release(), errstr.data(),
errstr.size()), minifi::utils::rd_kafka_consumer_deleter()};
if (consumer_ == nullptr) {
const std::string error_msg{errstr.data()};
throw Exception(PROCESS_SCHEDULE_EXCEPTION, "Failed to create Kafka
consumer " + error_msg);
@@ -252,7 +203,7 @@ std::string ConsumeKafka::resolve_duplicate_headers(const
std::vector<std::strin
switch (duplicate_header_handling_) {
case consume_kafka::MessageHeaderPolicyEnum::KEEP_FIRST: return
matching_headers.front();
case consume_kafka::MessageHeaderPolicyEnum::KEEP_LATEST: return
matching_headers.back();
- case consume_kafka::MessageHeaderPolicyEnum::COMMA_SEPARATED_MERGE: return
utils::string::join(", ", matching_headers);
+ case consume_kafka::MessageHeaderPolicyEnum::COMMA_SEPARATED_MERGE: return
minifi::utils::string::join(", ", matching_headers);
default: throw Exception(PROCESSOR_EXCEPTION, "\"Duplicate Header
Handling\" property not recognized.");
}
}
@@ -294,25 +245,25 @@ std::vector<std::pair<std::string, std::string>>
ConsumeKafka::getFlowFilesAttri
return attributes_from_headers;
}
-void ConsumeKafka::addAttributesToSingleMessageFlowFile(core::FlowFile&
flow_file, const rd_kafka_message_t& message) const {
- flow_file.setAttribute(KafkaCountAttribute.name, "1");
+void
ConsumeKafka::addAttributesToSingleMessageFlowFile(api::core::ProcessSession&
session, api::core::FlowFile& flow_file, const rd_kafka_message_t& message)
const {
+ session.setAttribute(flow_file, KafkaCountAttribute.name, "1");
if (const auto message_key = get_encoded_message_key(message,
key_attribute_encoding_)) {
- flow_file.setAttribute(KafkaKeyAttribute.name, *message_key);
+ session.setAttribute(flow_file, KafkaKeyAttribute.name, *message_key);
}
- flow_file.setAttribute(KafkaOffsetAttribute.name,
std::to_string(message.offset));
- flow_file.setAttribute(KafkaPartitionAttribute.name,
std::to_string(message.partition));
- flow_file.setAttribute(KafkaTopicAttribute.name,
rd_kafka_topic_name(message.rkt));
+ session.setAttribute(flow_file, KafkaOffsetAttribute.name,
std::to_string(message.offset));
+ session.setAttribute(flow_file, KafkaPartitionAttribute.name,
std::to_string(message.partition));
+ session.setAttribute(flow_file, KafkaTopicAttribute.name,
rd_kafka_topic_name(message.rkt));
if (headers_to_add_as_attributes_) {
- for (const auto& [attr_key, attr_value]:
getFlowFilesAttributesFromMessageHeaders(message)) {
flow_file.setAttribute(attr_key, attr_value); }
+ for (const auto& [attr_key, attr_value]:
getFlowFilesAttributesFromMessageHeaders(message)) {
session.setAttribute(flow_file, attr_key, attr_value); }
}
}
-void ConsumeKafka::addAttributesToMessageBundleFlowFile(core::FlowFile&
flow_file, const MessageBundle& message_bundle) const {
+void
ConsumeKafka::addAttributesToMessageBundleFlowFile(api::core::ProcessSession&
session, api::core::FlowFile& flow_file, const MessageBundle& message_bundle)
const {
gsl_Assert(!headers_to_add_as_attributes_);
- flow_file.setAttribute(KafkaCountAttribute.name,
std::to_string(message_bundle.getMessages().size()));
- flow_file.setAttribute(KafkaOffsetAttribute.name,
std::to_string(message_bundle.getLargestOffset()));
- flow_file.setAttribute(KafkaPartitionAttribute.name,
std::to_string(message_bundle.getMessages().front()->partition));
- flow_file.setAttribute(KafkaTopicAttribute.name,
rd_kafka_topic_name(message_bundle.getMessages().front()->rkt));
+ session.setAttribute(flow_file, KafkaCountAttribute.name,
std::to_string(message_bundle.getMessages().size()));
+ session.setAttribute(flow_file, KafkaOffsetAttribute.name,
std::to_string(message_bundle.getLargestOffset()));
+ session.setAttribute(flow_file, KafkaPartitionAttribute.name,
std::to_string(message_bundle.getMessages().front()->partition));
+ session.setAttribute(flow_file, KafkaTopicAttribute.name,
rd_kafka_topic_name(message_bundle.getMessages().front()->rkt));
}
void ConsumeKafka::commitOffsetsFromMessages(const
std::unordered_map<KafkaMessageLocation, MessageBundle>& message_bundles) const
{
@@ -333,19 +284,19 @@ void ConsumeKafka::commitOffsetsFromMessages(const
std::unordered_map<KafkaMessa
}
}
-void ConsumeKafka::commitOffsetsFromIncomingFlowFiles(core::ProcessSession&
session) const {
- std::vector<std::pair<std::shared_ptr<core::FlowFile>, core::Relationship>>
flow_files;
- while (auto ff = session.get()) { flow_files.push_back({ff, Committed}); }
+void
ConsumeKafka::commitOffsetsFromIncomingFlowFiles(api::core::ProcessSession&
session) const {
+ std::vector<std::pair<api::core::FlowFile, core::Relationship>> flow_files;
+ while (auto ff = session.get()) { flow_files.emplace_back(std::move(ff),
Committed); }
if (flow_files.empty()) { return; }
std::unordered_map<KafkaMessageLocation, int64_t> max_offsets;
for (auto& [flow_file, relationship]: std::ranges::reverse_view(flow_files))
{
- auto topic_name = flow_file->getAttribute(KafkaTopicAttribute.name)
+ auto topic_name = session.getAttribute(flow_file, KafkaTopicAttribute.name)
|
utils::toExpected(make_error_code(core::AttributeErrorCode::MissingAttribute));
- const auto offset = flow_file->getAttribute(KafkaOffsetAttribute.name)
+ const auto offset = session.getAttribute(flow_file,
KafkaOffsetAttribute.name)
|
utils::toExpected(make_error_code(core::AttributeErrorCode::MissingAttribute))
| utils::andThen(parsing::parseIntegral<int64_t>);
- const auto partition =
flow_file->getAttribute(KafkaPartitionAttribute.name)
+ const auto partition = session.getAttribute(flow_file,
KafkaPartitionAttribute.name)
|
utils::toExpected(make_error_code(core::AttributeErrorCode::MissingAttribute))
| utils::andThen(parsing::parseIntegral<int32_t>);
if (!topic_name || !offset || !partition) {
@@ -367,8 +318,7 @@ void
ConsumeKafka::commitOffsetsFromIncomingFlowFiles(core::ProcessSession& sess
for (const auto& [location, max_offset] : max_offsets) {
rd_kafka_topic_partition_list_add(partitions.get(), location.topic.data(),
location.partition)->offset = max_offset + 1;
}
- const auto commit_res = rd_kafka_commit(consumer_.get(), partitions.get(),
0);
- switch (commit_res) {
+ switch (const auto commit_res = rd_kafka_commit(consumer_.get(),
partitions.get(), 0)) {
case RD_KAFKA_RESP_ERR_NO_ERROR: {
logger_->log_debug("Commit successfully from {} flowfiles",
flow_files.size());
break;
@@ -386,24 +336,24 @@ void
ConsumeKafka::commitOffsetsFromIncomingFlowFiles(core::ProcessSession& sess
throw Exception(PROCESS_SESSION_EXCEPTION, fmt::format("Committing
offset failed: {}: {}", magic_enum::enum_underlying(commit_res),
rd_kafka_err2str(commit_res)));
}
}
- for (const auto& [ff, relationship]: flow_files) { session.transfer(ff,
relationship); }
+ for (auto& [ff, relationship]: flow_files) { session.transfer(std::move(ff),
relationship); }
}
-void ConsumeKafka::processMessages(core::ProcessSession& session, const
std::unordered_map<KafkaMessageLocation, MessageBundle>& message_bundles) const
{
+MinifiStatus ConsumeKafka::processMessages(api::core::ProcessSession& session,
const std::unordered_map<KafkaMessageLocation, MessageBundle>& message_bundles)
const {
for (const auto& msg_bundle: message_bundles | std::views::values) {
for (const auto& message: msg_bundle.getMessages()) {
std::string message_content = extractMessage(*message);
auto flow_file = session.create();
session.writeBuffer(flow_file, message_content);
- addAttributesToSingleMessageFlowFile(*flow_file, *message);
- session.transfer(flow_file, Success);
+ addAttributesToSingleMessageFlowFile(session, flow_file, *message);
+ session.transfer(std::move(flow_file), Success);
}
}
- session.commit();
if (commit_policy_ == consume_kafka::CommitPolicyEnum::CommitAfterBatch) {
commitOffsetsFromMessages(message_bundles); }
+ return MINIFI_STATUS_SUCCESS;
}
-void ConsumeKafka::processMessageBundles(core::ProcessSession& session,
+MinifiStatus ConsumeKafka::processMessageBundles(api::core::ProcessSession&
session,
const std::unordered_map<KafkaMessageLocation, MessageBundle>&
message_bundles, const std::string_view message_demarcator) const {
for (const auto& msg_bundle: message_bundles | std::views::values) {
auto flow_file = session.create();
@@ -411,24 +361,23 @@ void
ConsumeKafka::processMessageBundles(core::ProcessSession& session,
return extractMessage(*message);
});
session.writeBuffer(flow_file, merged_message_content);
- addAttributesToMessageBundleFlowFile(*flow_file, msg_bundle);
- session.transfer(flow_file, Success);
+ addAttributesToMessageBundleFlowFile(session, flow_file, msg_bundle);
+ session.transfer(std::move(flow_file), Success);
}
+ return MINIFI_STATUS_SUCCESS;
}
-void ConsumeKafka::onTrigger(core::ProcessContext&, core::ProcessSession&
session) {
+MinifiStatus ConsumeKafka::onTriggerImpl(api::core::ProcessContext&,
api::core::ProcessSession& session) {
if (commit_policy_ ==
consume_kafka::CommitPolicyEnum::CommitFromIncomingFlowFiles) {
commitOffsetsFromIncomingFlowFiles(session); }
const auto message_bundles = pollKafkaMessages();
if (message_bundles.empty()) {
logger_->log_debug("No new messages");
- return;
+ return MINIFI_STATUS_PROCESSOR_YIELD;
}
if (!message_demarcator_) {
- processMessages(session, message_bundles);
- } else {
- processMessageBundles(session, message_bundles, *message_demarcator_);
+ return processMessages(session, message_bundles);
}
+ return processMessageBundles(session, message_bundles, *message_demarcator_);
}
-REGISTER_RESOURCE(ConsumeKafka, Processor);
} // namespace org::apache::nifi::minifi::processors
diff --git a/extensions/kafka/ConsumeKafka.h b/extensions/kafka/ConsumeKafka.h
index 96dbe58e5..ded235f09 100644
--- a/extensions/kafka/ConsumeKafka.h
+++ b/extensions/kafka/ConsumeKafka.h
@@ -28,24 +28,15 @@
#include "core/PropertyDefinitionBuilder.h"
#include "minifi-cpp/core/PropertyValidator.h"
#include "minifi-cpp/core/RelationshipDefinition.h"
-#include "core/logging/LoggerFactory.h"
-#include "io/StreamPipe.h"
#include "rdkafka.h"
#include "rdkafka_utils.h"
#include "utils/ArrayUtils.h"
+#include "api/core/ProcessSession.h"
+#include "api/core/ProcessContext.h"
+#include "minifi-cpp/core/Annotation.h"
namespace org::apache::nifi::minifi::processors::consume_kafka {
-class ConsumeKafkaMaxPollTimePropertyValidator final : public
minifi::core::PropertyValidator {
- public:
- constexpr ~ConsumeKafkaMaxPollTimePropertyValidator() override { } //
NOLINT see comment at grandparent
-
- [[nodiscard]] bool validate(std::string_view input) const override;
- [[nodiscard]] std::optional<std::string_view>
getEquivalentNifiStandardValidatorName() const override { return std::nullopt; }
-};
-
-inline constexpr ConsumeKafkaMaxPollTimePropertyValidator
CONSUME_KAFKA_MAX_POLL_TIME_TYPE{};
-
enum class CommitPolicyEnum { NoCommit, AutoCommit, CommitAfterBatch,
CommitFromIncomingFlowFiles };
enum class OffsetResetPolicyEnum { earliest, latest, none };
@@ -215,7 +206,7 @@ class ConsumeKafka final : public KafkaProcessorBase {
.withDescription(
"Specifies the maximum amount of time the consumer can use for
polling data from the brokers. "
"Polling is a blocking operation, so the upper limit of this
value is specified in 4 seconds.")
- .withValidator(consume_kafka::CONSUME_KAFKA_MAX_POLL_TIME_TYPE)
+
.withValidator(core::StandardPropertyValidators::TIME_PERIOD_VALIDATOR)
.withDefaultValue(DEFAULT_MAX_POLL_TIME)
.isRequired(true)
.build();
@@ -284,7 +275,6 @@ class ConsumeKafka final : public KafkaProcessorBase {
EXTENSIONAPI static constexpr auto KafkaOffsetAttribute =
core::OutputAttributeDefinition<>{
"kafka.offset", {Success}, "The offset of the message (or largest offset
of the message bundle)"};
- ADD_COMMON_VIRTUAL_FUNCTIONS_FOR_PROCESSORS
using KafkaProcessorBase::KafkaProcessorBase;
@@ -294,9 +284,8 @@ class ConsumeKafka final : public KafkaProcessorBase {
ConsumeKafka& operator=(ConsumeKafka&&) = delete;
~ConsumeKafka() override = default;
- void onSchedule(core::ProcessContext& context, core::ProcessSessionFactory&
session_factory) override;
- void onTrigger(core::ProcessContext& context, core::ProcessSession& session)
override;
- void initialize() override;
+ MinifiStatus onScheduleImpl(api::core::ProcessContext& context) override;
+ MinifiStatus onTriggerImpl(api::core::ProcessContext& context,
api::core::ProcessSession& session) override;
private:
struct KafkaMessageLocation {
@@ -323,21 +312,21 @@ class ConsumeKafka final : public KafkaProcessorBase {
friend struct ::std::hash<KafkaMessageLocation>;
void createTopicPartitionList();
- void extendConfigFromDynamicProperties(const core::ProcessContext& context)
const;
- void configureNewConnection(core::ProcessContext& context);
+ void extendConfigFromDynamicProperties(const api::core::ProcessContext&
context) const;
+ void configureNewConnection(api::core::ProcessContext& context);
static std::string extractMessage(const rd_kafka_message_t& rkmessage);
std::unordered_map<KafkaMessageLocation, MessageBundle> pollKafkaMessages();
std::string resolve_duplicate_headers(const std::vector<std::string>&
matching_headers) const;
std::vector<std::string> get_matching_headers(const rd_kafka_message_t&
message, const std::string& header_name) const;
std::vector<std::pair<std::string, std::string>>
getFlowFilesAttributesFromMessageHeaders(const rd_kafka_message_t& message)
const;
- void addAttributesToSingleMessageFlowFile(core::FlowFile& flow_file, const
rd_kafka_message_t& message) const;
- void addAttributesToMessageBundleFlowFile(core::FlowFile& flow_file, const
MessageBundle& message_bundle) const;
- void processMessages(core::ProcessSession& session, const
std::unordered_map<KafkaMessageLocation, MessageBundle>& message_bundles) const;
- void processMessageBundles(core::ProcessSession& session, const
std::unordered_map<KafkaMessageLocation, MessageBundle>& message_bundles,
+ void addAttributesToSingleMessageFlowFile(api::core::ProcessSession&
session, api::core::FlowFile& flow_file, const rd_kafka_message_t& message)
const;
+ void addAttributesToMessageBundleFlowFile(api::core::ProcessSession&
session, api::core::FlowFile& flow_file, const MessageBundle& message_bundle)
const;
+ MinifiStatus processMessages(api::core::ProcessSession& session, const
std::unordered_map<KafkaMessageLocation, MessageBundle>& message_bundles) const;
+ MinifiStatus processMessageBundles(api::core::ProcessSession& session, const
std::unordered_map<KafkaMessageLocation, MessageBundle>& message_bundles,
std::string_view message_demarcator) const;
void commitOffsetsFromMessages(const
std::unordered_map<KafkaMessageLocation, MessageBundle>& message_bundles) const;
- void commitOffsetsFromIncomingFlowFiles(core::ProcessSession& session) const;
+ void commitOffsetsFromIncomingFlowFiles(api::core::ProcessSession& session)
const;
std::vector<std::string> topic_names_{};
consume_kafka::TopicNameFormatEnum topic_name_format_ =
consume_kafka::TopicNameFormatEnum::Names;
diff --git a/extensions/llamacpp/processors/ExtensionInitializer.cpp
b/extensions/kafka/ExtensionInitializer.cpp
similarity index 66%
copy from extensions/llamacpp/processors/ExtensionInitializer.cpp
copy to extensions/kafka/ExtensionInitializer.cpp
index 8e06bf0cc..792ab083b 100644
--- a/extensions/llamacpp/processors/ExtensionInitializer.cpp
+++ b/extensions/kafka/ExtensionInitializer.cpp
@@ -1,5 +1,5 @@
/**
-* Licensed to the Apache Software Foundation (ASF) under one or more
+ * Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
@@ -15,7 +15,8 @@
* limitations under the License.
*/
-#include "RunLlamaCppInference.h"
+#include "ConsumeKafka.h"
+#include "PublishKafka.h"
#include "api/core/Resource.h"
#include "api/utils/minifi-c-utils.h"
@@ -27,14 +28,10 @@ namespace minifi = org::apache::nifi::minifi;
CEXTENSIONAPI const uint32_t MinifiApiVersion = MINIFI_API_VERSION;
CEXTENSIONAPI void MinifiInitExtension(MinifiExtensionContext*
extension_context) {
- MinifiExtensionDefinition extension_definition{
- .name = minifi::api::utils::minifiStringView(MAKESTRING(EXTENSION_NAME)),
- .version =
minifi::api::utils::minifiStringView(MAKESTRING(EXTENSION_VERSION)),
- .deinit = nullptr,
- .user_data = nullptr
- };
+ const MinifiExtensionDefinition extension_definition{.name =
minifi::api::utils::minifiStringView(MAKESTRING(EXTENSION_NAME)),
+ .version =
minifi::api::utils::minifiStringView(MAKESTRING(EXTENSION_VERSION)),
+ .deinit = nullptr,
+ .user_data = nullptr};
auto* extension = MinifiRegisterExtension(extension_context,
&extension_definition);
-
minifi::api::core::useProcessorClassDefinition<minifi::extensions::llamacpp::processors::RunLlamaCppInference>([&]
(const MinifiProcessorClassDefinition& definition) {
- MinifiRegisterProcessor(extension, &definition);
- });
+ minifi::api::core::registerProcessors<minifi::processors::PublishKafka,
minifi::processors::ConsumeKafka>(extension);
}
diff --git a/extensions/kafka/KafkaConnection.cpp
b/extensions/kafka/KafkaConnection.cpp
index ac1387fa9..e7b09f8af 100644
--- a/extensions/kafka/KafkaConnection.cpp
+++ b/extensions/kafka/KafkaConnection.cpp
@@ -25,8 +25,7 @@
namespace org::apache::nifi::minifi::processors {
KafkaConnection::KafkaConnection(KafkaConnectionKey key)
- : logger_(core::logging::LoggerFactory<KafkaConnection>::getLogger()),
- initialized_(false),
+ : initialized_(false),
key_(std::move(key)),
poll_(false) {}
@@ -40,18 +39,13 @@ void KafkaConnection::remove() {
}
void KafkaConnection::removeConnection() {
- logger_->log_trace("KafkaConnection::removeConnection START: Client = {} --
Broker = {}", key_.client_id_, key_.brokers_);
stopPoll();
if (kafka_connection_) {
rd_kafka_flush(kafka_connection_, 10 * 1000); /* wait for max 10 seconds */
rd_kafka_destroy(kafka_connection_);
- modifyLoggers([&](std::unordered_map<const rd_kafka_t*,
std::weak_ptr<core::logging::Logger>>& loggers) {
- loggers.erase(kafka_connection_);
- });
kafka_connection_ = nullptr;
}
initialized_ = false;
- logger_->log_trace("KafkaConnection::removeConnection FINISH: Client = {} --
Broker = {}", key_.client_id_, key_.brokers_);
}
bool KafkaConnection::initialized() const {
@@ -62,9 +56,6 @@ void
KafkaConnection::setConnection(utils::rd_kafka_producer_unique_ptr producer
removeConnection();
kafka_connection_ = gsl::owner<rd_kafka_t*>{producer.release()}; //
kafka_connection_ takes ownership from producer
initialized_ = true;
- modifyLoggers([&](std::unordered_map<const rd_kafka_t*,
std::weak_ptr<core::logging::Logger>>& loggers) {
- loggers[kafka_connection_] = logger_;
- });
startPoll();
}
@@ -89,37 +80,4 @@ void KafkaConnection::putTopic(const std::string& topicName,
const std::shared_p
topics_[topicName] = topic;
}
-void KafkaConnection::logCallback(const rd_kafka_t* rk, const int level, const
char* /*fac*/, const char* buf) {
- std::shared_ptr<core::logging::Logger> logger;
- try {
- modifyLoggers([&](const std::unordered_map<const rd_kafka_t*,
std::weak_ptr<core::logging::Logger>>& loggers) {
- logger = loggers.at(rk).lock();
- });
- } catch (...) {}
-
- if (!logger) { return; }
-
- switch (level) {
- case 0: // LOG_EMERG
- case 1: // LOG_ALERT
- case 2: // LOG_CRIT
- logger->log_critical("{}", buf);
- break;
- case 3: // LOG_ERR
- logger->log_error("{}", buf);
- break;
- case 4: // LOG_WARNING
- logger->log_warn("{}", buf);
- break;
- case 5: // LOG_NOTICE
- case 6: // LOG_INFO
- logger->log_info("{}", buf);
- break;
- case 7: // LOG_DEBUG
- logger->log_debug("{}", buf);
- break;
- default: gsl_FailFast();
- }
-}
-
} // namespace org::apache::nifi::minifi::processors
diff --git a/extensions/kafka/KafkaConnection.h
b/extensions/kafka/KafkaConnection.h
index 121ab2f3d..2960f0172 100644
--- a/extensions/kafka/KafkaConnection.h
+++ b/extensions/kafka/KafkaConnection.h
@@ -24,7 +24,6 @@
#include <string>
#include <unordered_map>
-#include "core/logging/LoggerFactory.h"
#include "minifi-cpp/core/logging/Logger.h"
#include "rdkafka.h"
#include "KafkaTopic.h"
@@ -73,11 +72,7 @@ class KafkaConnection {
void putTopic(const std::string &topicName, const
std::shared_ptr<KafkaTopic> &topic);
- static void logCallback(const rd_kafka_t* rk, int level, const char*
/*fac*/, const char* buf);
-
private:
- std::shared_ptr<core::logging::Logger> logger_;
-
bool initialized_;
KafkaConnectionKey key_;
@@ -89,17 +84,8 @@ class KafkaConnection {
std::atomic<bool> poll_;
std::thread thread_kafka_poll_;
- static void modifyLoggers(const std::function<void(std::unordered_map<const
rd_kafka_t*, std::weak_ptr<core::logging::Logger>>&)>& func) {
- static std::mutex loggers_mutex;
- static std::unordered_map<const rd_kafka_t*,
std::weak_ptr<core::logging::Logger>> loggers;
-
- std::lock_guard<std::mutex> lock(loggers_mutex);
- func(loggers);
- }
-
void stopPoll() {
poll_ = false;
- logger_->log_debug("Stop polling");
if (thread_kafka_poll_.joinable()) {
thread_kafka_poll_.join();
}
@@ -107,7 +93,6 @@ class KafkaConnection {
void startPoll() {
poll_ = true;
- logger_->log_debug("Start polling");
thread_kafka_poll_ = std::thread([this]{
while (this->poll_) {
rd_kafka_poll(this->kafka_connection_, 1000);
diff --git a/extensions/kafka/KafkaProcessorBase.cpp
b/extensions/kafka/KafkaProcessorBase.cpp
index 250bef64b..02233edff 100644
--- a/extensions/kafka/KafkaProcessorBase.cpp
+++ b/extensions/kafka/KafkaProcessorBase.cpp
@@ -16,22 +16,24 @@
*/
#include "KafkaProcessorBase.h"
-#include "minifi-cpp/controllers/SSLContextServiceInterface.h"
+#include "api/utils/ProcessorConfigUtils.h"
#include "rdkafka_utils.h"
-#include "utils/ProcessorConfigUtils.h"
namespace org::apache::nifi::minifi::processors {
-std::optional<utils::net::SslData>
KafkaProcessorBase::getSslData(core::ProcessContext& context) const {
- return utils::net::getSslData(context, SSLContextService, logger_);
+KafkaProcessorBase::KafkaProcessorBase(core::ProcessorMetadata metadata) :
ProcessorImpl(std::move(metadata)), kafka_opaque_(*logger_) {
}
-void
KafkaProcessorBase::setKafkaAuthenticationParameters(core::ProcessContext&
context, gsl::not_null<rd_kafka_conf_t*> config) {
- security_protocol_ =
utils::parseEnumProperty<kafka::SecurityProtocolOption>(context,
SecurityProtocol);
+std::optional<api::utils::net::SslData>
KafkaProcessorBase::getSslData(api::core::ProcessContext& context) const {
+ return context.getSslData(SSLContextService) | utils::toOptional();
+}
+
+void
KafkaProcessorBase::setKafkaAuthenticationParameters(api::core::ProcessContext&
context, gsl::not_null<rd_kafka_conf_t*> config) { //
NOLINT(performance-unnecessary-value-param)
+ security_protocol_ =
api::utils::parseEnumProperty<kafka::SecurityProtocolOption>(context,
SecurityProtocol);
utils::setKafkaConfigurationField(*config, "security.protocol",
std::string{magic_enum::enum_name(security_protocol_)});
logger_->log_debug("Kafka security.protocol [{}]",
magic_enum::enum_name(security_protocol_));
if (security_protocol_ == kafka::SecurityProtocolOption::ssl ||
security_protocol_ == kafka::SecurityProtocolOption::sasl_ssl) {
- if (auto ssl_data = getSslData(context)) {
+ if (auto ssl_data = context.getSslData(SSLContextService) |
utils::toOptional()) {
if (ssl_data->ca_loc.empty() && ssl_data->cert_loc.empty() &&
ssl_data->key_loc.empty() && ssl_data->key_pw.empty()) {
logger_->log_warn("Security protocol is set to {}, but no valid
security parameters are set in the properties or in the SSL Context Service.",
magic_enum::enum_name(security_protocol_));
@@ -48,12 +50,12 @@ void
KafkaProcessorBase::setKafkaAuthenticationParameters(core::ProcessContext&
}
}
- auto sasl_mechanism =
utils::parseEnumProperty<kafka::SASLMechanismOption>(context, SASLMechanism);
+ auto sasl_mechanism =
api::utils::parseEnumProperty<kafka::SASLMechanismOption>(context,
SASLMechanism);
utils::setKafkaConfigurationField(*config, "sasl.mechanism",
std::string{magic_enum::enum_name(sasl_mechanism)});
logger_->log_debug("Kafka sasl.mechanism [{}]",
magic_enum::enum_name(sasl_mechanism));
auto setKafkaConfigIfNotEmpty = [this, &context, config](const
core::PropertyReference& property, const std::string& kafka_config_name, bool
log_value = true) {
- const std::string value = context.getProperty(property).value_or("");
+ const std::string value = context.getProperty(property,
nullptr).value_or("");
if (!value.empty()) {
utils::setKafkaConfigurationField(*config, kafka_config_name, value);
if (log_value) {
diff --git a/extensions/kafka/KafkaProcessorBase.h
b/extensions/kafka/KafkaProcessorBase.h
index 129882ac4..6cdc2feec 100644
--- a/extensions/kafka/KafkaProcessorBase.h
+++ b/extensions/kafka/KafkaProcessorBase.h
@@ -20,12 +20,13 @@
#include <optional>
#include <string_view>
-#include "minifi-cpp/controllers/SSLContextServiceInterface.h"
-#include "core/ProcessorImpl.h"
+#include "api/core/ProcessorImpl.h"
+#include "api/utils/Export.h"
+#include "api/utils/Ssl.h"
#include "core/PropertyDefinitionBuilder.h"
#include "rdkafka_utils.h"
#include "utils/Enum.h"
-#include "utils/net/Ssl.h"
+#include "minifi-c.h"
namespace org::apache::nifi::minifi::processors {
@@ -35,12 +36,12 @@ enum class SecurityProtocolOption { plaintext, ssl,
sasl_plaintext, sasl_ssl };
enum class SASLMechanismOption { GSSAPI, PLAIN };
} // namespace kafka
-class KafkaProcessorBase : public core::ProcessorImpl {
+class KafkaProcessorBase : public api::core::ProcessorImpl {
public:
EXTENSIONAPI static constexpr auto SSLContextService =
core::PropertyDefinitionBuilder<>::createProperty("SSL Context Service")
.withDescription("SSL Context Service Name")
- .withAllowedTypes<minifi::controllers::SSLContextServiceInterface>()
+ .withAllowedType<MINIFI_SSL_CONTEXT_SERVICE_PROPERTY_TYPE>()
.build();
EXTENSIONAPI static constexpr auto SecurityProtocol =
core::PropertyDefinitionBuilder<magic_enum::enum_count<kafka::SecurityProtocolOption>()>::createProperty(
@@ -88,7 +89,7 @@ class KafkaProcessorBase : public core::ProcessorImpl {
EXTENSIONAPI static constexpr auto Properties =
std::to_array<core::PropertyReference>({SSLContextService,
SecurityProtocol, KerberosServiceName, KerberosPrincipal,
KerberosKeytabPath, SASLMechanism, Username, Password});
- using ProcessorImpl::ProcessorImpl;
+ explicit KafkaProcessorBase(core::ProcessorMetadata metadata);
KafkaProcessorBase(const KafkaProcessorBase&) = delete;
KafkaProcessorBase(KafkaProcessorBase&&) = delete;
@@ -97,10 +98,11 @@ class KafkaProcessorBase : public core::ProcessorImpl {
~KafkaProcessorBase() override = default;
protected:
- virtual std::optional<utils::net::SslData> getSslData(core::ProcessContext&
context) const;
- void setKafkaAuthenticationParameters(core::ProcessContext& context,
gsl::not_null<rd_kafka_conf_t*> config);
+ virtual std::optional<api::utils::net::SslData>
getSslData(api::core::ProcessContext& context) const;
+ void setKafkaAuthenticationParameters(api::core::ProcessContext& context,
gsl::not_null<rd_kafka_conf_t*> config);
kafka::SecurityProtocolOption security_protocol_{};
+ utils::KafkaOpaque kafka_opaque_;
};
} // namespace org::apache::nifi::minifi::processors
diff --git a/extensions/kafka/PublishKafka.cpp
b/extensions/kafka/PublishKafka.cpp
index 6f04186cd..05f3de3e2 100644
--- a/extensions/kafka/PublishKafka.cpp
+++ b/extensions/kafka/PublishKafka.cpp
@@ -21,14 +21,15 @@
#include <string>
#include <vector>
-#include "minifi-cpp/core/ProcessContext.h"
-#include "core/ProcessSession.h"
-#include "core/Resource.h"
+#include "api/core/ProcessSession.h"
+#include "api/core/Resource.h"
+#include "api/utils/ProcessorConfigUtils.h"
+#include "minifi-cpp/io/InputStream.h"
+#include "minifi-cpp/io/Stream.h"
+#include "minifi-cpp/utils/gsl.h"
#include "range/v3/algorithm/all_of.hpp"
#include "rdkafka_utils.h"
-#include "utils/ProcessorConfigUtils.h"
#include "utils/StringUtils.h"
-#include "minifi-cpp/utils/gsl.h"
namespace org::apache::nifi::minifi::processors {
@@ -158,12 +159,13 @@ class ReadCallback {
});
}
- static utils::rd_kafka_headers_unique_ptr make_headers(const core::FlowFile&
flow_file,
+ static utils::rd_kafka_headers_unique_ptr make_headers(const
api::core::FlowFile& flow_file,
+ const api::core::ProcessSession& session,
const std::optional<utils::Regex>& attribute_name_regex) {
utils::rd_kafka_headers_unique_ptr result{rd_kafka_headers_new(8)};
if (!result) { throw std::bad_alloc{}; }
- for (const auto& [attribute_key, attribute_value]:
flow_file.getAttributes()) {
+ for (const auto& [attribute_key, attribute_value]:
session.getAttributes(flow_file)) {
if (attribute_name_regex && utils::regexMatch(attribute_key,
*attribute_name_regex)) {
rd_kafka_header_add(result.get(),
attribute_key.c_str(),
@@ -228,15 +230,15 @@ class ReadCallback {
public:
ReadCallback(const uint64_t max_seg_size, std::string key, rd_kafka_topic_t*
const rkt, rd_kafka_t* const rk,
- const core::FlowFile& flowFile, const std::optional<utils::Regex>&
attributeNameRegex,
- std::shared_ptr<PublishKafka::Messages> messages, const size_t
flow_file_index, const bool fail_empty_flow_files,
- std::shared_ptr<core::logging::Logger> logger)
- : flow_size_(flowFile.getSize()),
+ uint64_t flow_file_size, const api::core::FlowFile& flowFile, const
api::core::ProcessSession& session,
+ const std::optional<utils::Regex>& attributeNameRegex,
std::shared_ptr<PublishKafka::Messages> messages,
+ const size_t flow_file_index, const bool fail_empty_flow_files,
std::shared_ptr<core::logging::Logger> logger)
+ : flow_size_(flow_file_size),
max_seg_size_(max_seg_size == 0 || flow_size_ < max_seg_size ?
flow_size_ : max_seg_size),
key_(std::move(key)),
rkt_(rkt),
rk_(rk),
- hdrs(make_headers(flowFile, attributeNameRegex)),
+ hdrs(make_headers(flowFile, session, attributeNameRegex)),
messages_(std::move(messages)),
flow_file_index_(flow_file_index),
fail_empty_flow_files_(fail_empty_flow_files),
@@ -326,33 +328,28 @@ void messageDeliveryCallback(rd_kafka_t* rk, const
rd_kafka_message_t* rkmessage
}
} // namespace
-void PublishKafka::initialize() {
- setSupportedProperties(Properties);
- setSupportedRelationships(Relationships);
-}
-
-void PublishKafka::onSchedule(core::ProcessContext& context,
core::ProcessSessionFactory&) {
+MinifiStatus PublishKafka::onScheduleImpl(api::core::ProcessContext& context) {
interrupted_ = false;
// Try to get a KafkaConnection
- std::string client_id = utils::parseProperty(context, ClientName);
- std::string brokers = utils::parseProperty(context, SeedBrokers);
+ std::string client_id = api::utils::parseProperty(context, ClientName);
+ std::string brokers = api::utils::parseProperty(context, SeedBrokers);
// Get some properties not (only) used directly to set up librdkafka
// Batch Size
- batch_size_ = utils::parseU64Property(context, BatchSize);
+ batch_size_ = api::utils::parseU64Property(context, BatchSize);
logger_->log_debug("PublishKafka: Batch Size [{}]", batch_size_);
// Target Batch Payload Size
- target_batch_payload_size_ = utils::parseDataSizeProperty(context,
TargetBatchPayloadSize);
+ target_batch_payload_size_ = api::utils::parseDataSizeProperty(context,
TargetBatchPayloadSize);
logger_->log_debug("PublishKafka: Target Batch Payload Size [{}]",
target_batch_payload_size_);
// Max Flow Segment Size
- max_flow_seg_size_ = utils::parseDataSizeProperty(context, MaxFlowSegSize);
+ max_flow_seg_size_ = api::utils::parseDataSizeProperty(context,
MaxFlowSegSize);
logger_->log_debug("PublishKafka: Max Flow Segment Size [{}]",
max_flow_seg_size_);
// Attributes to Send as Headers
- attributeNameRegex_ = context.getProperty(AttributeNameRegex)
+ attributeNameRegex_ = context.getProperty(AttributeNameRegex, nullptr)
| utils::transform([](auto pattern_str) { return
utils::Regex{std::move(pattern_str)}; })
| utils::toOptional();
@@ -362,15 +359,16 @@ void PublishKafka::onSchedule(core::ProcessContext&
context, core::ProcessSessio
conn_ = std::make_unique<KafkaConnection>(key_);
configureNewConnection(context);
- if (const auto message_key_field = context.getProperty(MessageKeyField);
message_key_field && !message_key_field->empty()) {
+ if (const auto message_key_field = context.getProperty(MessageKeyField,
nullptr); message_key_field && !message_key_field->empty()) {
logger_->log_error("The {} property is set. This property is DEPRECATED
and has no effect; please use Kafka Key instead.", MessageKeyField.name);
}
logger_->log_debug("Successfully configured PublishKafka");
+ return MINIFI_STATUS_SUCCESS;
}
-void PublishKafka::notifyStop() {
- logger_->log_debug("notifyStop called");
+void PublishKafka::onUnSchedule() {
+ logger_->log_debug("onUnSchedule called");
interrupted_ = true;
{
// Normally when we need both connection_mutex_ and messages_mutex_, we
need to take connection_mutex_ first to avoid a
@@ -386,18 +384,20 @@ void PublishKafka::notifyStop() {
conn_.reset();
}
-bool PublishKafka::configureNewConnection(core::ProcessContext& context) {
+void PublishKafka::configureNewConnection(api::core::ProcessContext& context) {
std::array<char, 512U> err_chars{};
rd_kafka_conf_res_t result = RD_KAFKA_CONF_OK;
constexpr std::string_view PREFIX_ERROR_MSG = "PublishKafka: configure error
result: ";
- utils::rd_kafka_conf_unique_ptr conf_{rd_kafka_conf_new()};
- if (conf_ == nullptr) { throw Exception(PROCESS_SCHEDULE_EXCEPTION, "Failed
to create rd_kafka_conf_t object"); }
+ utils::rd_kafka_conf_unique_ptr conf{rd_kafka_conf_new()};
+ if (conf == nullptr) { throw Exception(PROCESS_SCHEDULE_EXCEPTION, "Failed
to create rd_kafka_conf_t object"); }
+
+ rd_kafka_conf_set_opaque(conf.get(), &kafka_opaque_);
const auto* const key = conn_->getKey();
if (key->brokers_.empty()) { throw Exception(PROCESS_SCHEDULE_EXCEPTION,
"There are no brokers"); }
- result = rd_kafka_conf_set(conf_.get(), "bootstrap.servers",
key->brokers_.c_str(), err_chars.data(), err_chars.size());
+ result = rd_kafka_conf_set(conf.get(), "bootstrap.servers",
key->brokers_.c_str(), err_chars.data(), err_chars.size());
logger_->log_debug("PublishKafka: bootstrap.servers [{}]", key->brokers_);
if (result != RD_KAFKA_CONF_OK) {
auto error_msg = utils::string::join_pack(PREFIX_ERROR_MSG,
err_chars.data());
@@ -405,15 +405,15 @@ bool
PublishKafka::configureNewConnection(core::ProcessContext& context) {
}
if (key->client_id_.empty()) { throw Exception(PROCESS_SCHEDULE_EXCEPTION,
"Client id is empty"); }
- result = rd_kafka_conf_set(conf_.get(), "client.id",
key->client_id_.c_str(), err_chars.data(), err_chars.size());
+ result = rd_kafka_conf_set(conf.get(), "client.id", key->client_id_.c_str(),
err_chars.data(), err_chars.size());
logger_->log_debug("PublishKafka: client.id [{}]", key->client_id_);
if (result != RD_KAFKA_CONF_OK) {
auto error_msg = utils::string::join_pack(PREFIX_ERROR_MSG,
err_chars.data());
throw Exception(PROCESS_SCHEDULE_EXCEPTION, error_msg);
}
- if (const auto debug_context = context.getProperty(DebugContexts)) {
- result = rd_kafka_conf_set(conf_.get(), "debug", debug_context->c_str(),
err_chars.data(), err_chars.size());
+ if (const auto debug_context = context.getProperty(DebugContexts, nullptr)) {
+ result = rd_kafka_conf_set(conf.get(), "debug", debug_context->c_str(),
err_chars.data(), err_chars.size());
logger_->log_debug("PublishKafka: debug [{}]", *debug_context);
if (result != RD_KAFKA_CONF_OK) {
auto error_msg = utils::string::join_pack(PREFIX_ERROR_MSG,
err_chars.data());
@@ -421,8 +421,8 @@ bool
PublishKafka::configureNewConnection(core::ProcessContext& context) {
}
}
- if (const auto max_message_size = context.getProperty(MaxMessageSize);
max_message_size && !max_message_size->empty()) {
- result = rd_kafka_conf_set(conf_.get(), "message.max.bytes",
max_message_size->c_str(), err_chars.data(), err_chars.size());
+ if (const auto max_message_size = context.getProperty(MaxMessageSize,
nullptr); max_message_size && !max_message_size->empty()) {
+ result = rd_kafka_conf_set(conf.get(), "message.max.bytes",
max_message_size->c_str(), err_chars.data(), err_chars.size());
logger_->log_debug("PublishKafka: message.max.bytes [{}]",
*max_message_size);
if (result != RD_KAFKA_CONF_OK) {
auto error_msg = utils::string::join_pack(PREFIX_ERROR_MSG,
err_chars.data());
@@ -430,13 +430,13 @@ bool
PublishKafka::configureNewConnection(core::ProcessContext& context) {
}
}
- if (const auto queue_buffer_max_message =
utils::parseOptionalU64Property(context, QueueBufferMaxMessage)) {
+ if (const auto queue_buffer_max_message =
api::utils::parseOptionalU64Property(context, QueueBufferMaxMessage)) {
if (*queue_buffer_max_message < batch_size_) {
throw Exception(PROCESS_SCHEDULE_EXCEPTION, "Invalid configuration:
Batch Size cannot be larger than Queue Max Message");
}
const auto value = std::to_string(*queue_buffer_max_message);
- result = rd_kafka_conf_set(conf_.get(), "queue.buffering.max.messages",
value.c_str(), err_chars.data(), err_chars.size());
+ result = rd_kafka_conf_set(conf.get(), "queue.buffering.max.messages",
value.c_str(), err_chars.data(), err_chars.size());
logger_->log_debug("PublishKafka: queue.buffering.max.messages [{}]",
value);
if (result != RD_KAFKA_CONF_OK) {
auto error_msg = utils::string::join_pack(PREFIX_ERROR_MSG,
err_chars.data());
@@ -444,10 +444,10 @@ bool
PublishKafka::configureNewConnection(core::ProcessContext& context) {
}
}
- if (const auto queue_buffer_max_size =
utils::parseOptionalDataSizeProperty(context, QueueBufferMaxSize)) {
+ if (const auto queue_buffer_max_size =
api::utils::parseOptionalDataSizeProperty(context, QueueBufferMaxSize)) {
auto valInt = *queue_buffer_max_size / 1024;
auto valueConf = std::to_string(valInt);
- result = rd_kafka_conf_set(conf_.get(), "queue.buffering.max.kbytes",
valueConf.c_str(), err_chars.data(), err_chars.size());
+ result = rd_kafka_conf_set(conf.get(), "queue.buffering.max.kbytes",
valueConf.c_str(), err_chars.data(), err_chars.size());
logger_->log_debug("PublishKafka: queue.buffering.max.kbytes [{}]",
valueConf);
if (result != RD_KAFKA_CONF_OK) {
auto error_msg = utils::string::join_pack(PREFIX_ERROR_MSG,
err_chars.data());
@@ -455,9 +455,9 @@ bool
PublishKafka::configureNewConnection(core::ProcessContext& context) {
}
}
- if (const auto queue_buffer_max_time =
utils::parseOptionalDurationProperty(context, QueueBufferMaxTime)) {
+ if (const auto queue_buffer_max_time =
api::utils::parseOptionalDurationProperty(context, QueueBufferMaxTime)) {
auto valueConf = std::to_string(queue_buffer_max_time->count());
- result = rd_kafka_conf_set(conf_.get(), "queue.buffering.max.ms",
valueConf.c_str(), err_chars.data(), err_chars.size());
+ result = rd_kafka_conf_set(conf.get(), "queue.buffering.max.ms",
valueConf.c_str(), err_chars.data(), err_chars.size());
logger_->log_debug("PublishKafka: queue.buffering.max.ms [{}]", valueConf);
if (result != RD_KAFKA_CONF_OK) {
auto error_msg = utils::string::join_pack(PREFIX_ERROR_MSG,
err_chars.data());
@@ -465,9 +465,9 @@ bool
PublishKafka::configureNewConnection(core::ProcessContext& context) {
}
}
- if (const auto batch_size = utils::parseOptionalU64Property(context,
BatchSize)) {
+ if (const auto batch_size = api::utils::parseOptionalU64Property(context,
BatchSize)) {
auto value = std::to_string(*batch_size);
- result = rd_kafka_conf_set(conf_.get(), "batch.num.messages",
value.c_str(), err_chars.data(), err_chars.size());
+ result = rd_kafka_conf_set(conf.get(), "batch.num.messages",
value.c_str(), err_chars.data(), err_chars.size());
logger_->log_debug("PublishKafka: batch.num.messages [{}]", value);
if (result != RD_KAFKA_CONF_OK) {
auto error_msg = utils::string::join_pack(PREFIX_ERROR_MSG,
err_chars.data());
@@ -475,8 +475,8 @@ bool
PublishKafka::configureNewConnection(core::ProcessContext& context) {
}
}
- if (const auto compress_codec = context.getProperty(CompressCodec);
compress_codec && !compress_codec->empty() && *compress_codec != "none") {
- result = rd_kafka_conf_set(conf_.get(), "compression.codec",
compress_codec->c_str(), err_chars.data(), err_chars.size());
+ if (const auto compress_codec = context.getProperty(CompressCodec, nullptr);
compress_codec && !compress_codec->empty() && *compress_codec != "none") {
+ result = rd_kafka_conf_set(conf.get(), "compression.codec",
compress_codec->c_str(), err_chars.data(), err_chars.size());
logger_->log_debug("PublishKafka: compression.codec [{}]",
*compress_codec);
if (result != RD_KAFKA_CONF_OK) {
auto error_msg = utils::string::join_pack(PREFIX_ERROR_MSG,
err_chars.data());
@@ -484,49 +484,47 @@ bool
PublishKafka::configureNewConnection(core::ProcessContext& context) {
}
}
- setKafkaAuthenticationParameters(context, gsl::make_not_null(conf_.get()));
+ setKafkaAuthenticationParameters(context, gsl::make_not_null(conf.get()));
// Add all the dynamic properties as librdkafka configurations
- const auto& dynamic_prop_keys = context.getDynamicPropertyKeys();
- logger_->log_info("PublishKafka registering {} librdkafka dynamic
properties", dynamic_prop_keys.size());
-
- for (const auto& prop_key : dynamic_prop_keys) {
- if (const auto dynamic_property_value =
context.getDynamicProperty(prop_key, nullptr); dynamic_property_value &&
!dynamic_property_value->empty()) {
- logger_->log_debug("PublishKafka: DynamicProperty: [{}] -> [{}]",
prop_key, *dynamic_property_value);
- result = rd_kafka_conf_set(conf_.get(), prop_key.c_str(),
dynamic_property_value->c_str(), err_chars.data(), err_chars.size());
- if (result != RD_KAFKA_CONF_OK) {
- auto error_msg = utils::string::join_pack(PREFIX_ERROR_MSG,
err_chars.data());
- throw Exception(PROCESS_SCHEDULE_EXCEPTION, error_msg);
- }
- } else {
+
+ std::optional<std::string> error_during_on_dyn_props = std::nullopt;
+ for (auto [prop_key, prop_value] : context.getDynamicProperties(nullptr)) {
+ if (prop_value.empty()) {
logger_->log_warn(
- "PublishKafka Dynamic Property '{}' is empty and therefore will not "
- "be configured",
+ "PublishKafka Dynamic Property '{}' is empty and therefore will not
be configured",
prop_key);
+ continue;
+ }
+ logger_->log_debug("PublishKafka: DynamicProperty: [{}] -> [{}]",
prop_key, prop_value);
+ result = rd_kafka_conf_set(conf.get(), prop_key.data(), prop_value.data(),
err_chars.data(), err_chars.size());
+ if (result != RD_KAFKA_CONF_OK) {
+ error_during_on_dyn_props = utils::string::join_pack(PREFIX_ERROR_MSG,
err_chars.data());
}
}
+ if (error_during_on_dyn_props) {
+ throw Exception(PROCESS_SCHEDULE_EXCEPTION, *error_during_on_dyn_props);
+ }
// Set the delivery callback
- rd_kafka_conf_set_dr_msg_cb(conf_.get(), &messageDeliveryCallback);
+ rd_kafka_conf_set_dr_msg_cb(conf.get(), &messageDeliveryCallback);
// Set the logger callback
- rd_kafka_conf_set_log_cb(conf_.get(), &KafkaConnection::logCallback);
+ rd_kafka_conf_set_log_cb(conf.get(), &utils::KafkaOpaque::logCallback);
// The producer takes ownership of the configuration, we must not free it
utils::rd_kafka_producer_unique_ptr producer{
- rd_kafka_new(RD_KAFKA_PRODUCER, conf_.release(), err_chars.data(),
err_chars.size())};
+ rd_kafka_new(RD_KAFKA_PRODUCER, conf.release(), err_chars.data(),
err_chars.size())};
if (producer == nullptr) {
auto error_msg = utils::string::join_pack("Failed to create Kafka producer
", err_chars.data());
throw Exception(PROCESS_SCHEDULE_EXCEPTION, error_msg);
}
conn_->setConnection(std::move(producer));
-
- return true;
}
-bool PublishKafka::createNewTopic(core::ProcessContext& context, const
std::string& topic_name,
- const std::shared_ptr<core::FlowFile>& flow_file) const {
+bool PublishKafka::createNewTopic(const api::core::ProcessContext& context,
const std::string& topic_name,
+ const api::core::FlowFile& flow_file) const {
utils::rd_kafka_topic_conf_unique_ptr topic_conf_{rd_kafka_topic_conf_new()};
if (topic_conf_ == nullptr) {
logger_->log_error("Failed to create rd_kafka_topic_conf_t object");
@@ -536,7 +534,7 @@ bool PublishKafka::createNewTopic(core::ProcessContext&
context, const std::stri
rd_kafka_conf_res_t result = RD_KAFKA_CONF_OK;
std::array<char, 512U> err_chars{};
- if (auto delivery_guarantee = context.getProperty(DeliveryGuarantee,
flow_file.get())) {
+ if (auto delivery_guarantee = api::utils::parseOptionalProperty(context,
DeliveryGuarantee, &flow_file)) {
/*
* Because of a previous error in this processor, the default value of
this property was "DELIVERY_ONE_NODE".
* As this is not a valid value for "request.required.acks", the following
rd_kafka_topic_conf_set call failed,
@@ -560,7 +558,7 @@ bool PublishKafka::createNewTopic(core::ProcessContext&
context, const std::stri
}
}
- if (const auto request_timeout =
utils::parseOptionalDurationProperty(context, RequestTimeOut)) {
+ if (const auto request_timeout =
api::utils::parseOptionalDurationProperty(context, RequestTimeOut)) {
auto valueConf = std::to_string(request_timeout->count());
result = rd_kafka_topic_conf_set(topic_conf_.get(), "request.timeout.ms",
valueConf.c_str(), err_chars.data(), err_chars.size());
logger_->log_debug("PublishKafka: request.timeout.ms [{}]", valueConf);
@@ -570,7 +568,7 @@ bool PublishKafka::createNewTopic(core::ProcessContext&
context, const std::stri
}
}
- if (const auto message_timeout =
utils::parseOptionalDurationProperty(context, MessageTimeOut)) {
+ if (const auto message_timeout =
api::utils::parseOptionalDurationProperty(context, MessageTimeOut)) {
auto valueConf = std::to_string(message_timeout->count());
result = rd_kafka_topic_conf_set(topic_conf_.get(), "message.timeout.ms",
valueConf.c_str(), err_chars.data(), err_chars.size());
logger_->log_debug("PublishKafka: message.timeout.ms [{}]", valueConf);
@@ -596,25 +594,24 @@ bool PublishKafka::createNewTopic(core::ProcessContext&
context, const std::stri
return true;
}
-std::optional<utils::net::SslData>
PublishKafka::getSslData(core::ProcessContext& context) const {
- if (auto result = KafkaProcessorBase::getSslData(context); result) { return
result; }
+std::optional<api::utils::net::SslData>
PublishKafka::getSslData(api::core::ProcessContext& context) const {
+ if (auto result = context.getSslData(SSLContextService); result) { return
*result; }
- utils::net::SslData ssl_data;
- if (auto security_ca = context.getProperty(SecurityCA)) { ssl_data.ca_loc =
*security_ca; }
- if (auto security_cert = context.getProperty(SecurityCert)) {
ssl_data.cert_loc = *security_cert; }
- if (auto security_private_key = context.getProperty(SecurityPrivateKey)) {
ssl_data.key_loc = *security_private_key; }
- if (auto security_private_key_pass =
context.getProperty(SecurityPrivateKeyPassWord)) {
+ api::utils::net::SslData ssl_data;
+ if (auto security_ca = context.getProperty(SecurityCA, nullptr)) {
ssl_data.ca_loc = *security_ca; }
+ if (auto security_cert = context.getProperty(SecurityCert, nullptr)) {
ssl_data.cert_loc = *security_cert; }
+ if (auto security_private_key = context.getProperty(SecurityPrivateKey,
nullptr)) { ssl_data.key_loc = *security_private_key; }
+ if (auto security_private_key_pass =
context.getProperty(SecurityPrivateKeyPassWord, nullptr)) {
ssl_data.key_pw = *security_private_key_pass;
}
return ssl_data;
}
-void PublishKafka::onTrigger(core::ProcessContext& context,
core::ProcessSession& session) {
+MinifiStatus PublishKafka::onTriggerImpl(api::core::ProcessContext& context,
api::core::ProcessSession& session) {
// Check whether we have been interrupted
if (interrupted_) {
logger_->log_info("The processor has been interrupted, not running
onTrigger");
- context.yield();
- return;
+ return MINIFI_STATUS_PROCESSOR_YIELD;
}
std::lock_guard<std::mutex> lock_connection(connection_mutex_);
@@ -622,17 +619,16 @@ void PublishKafka::onTrigger(core::ProcessContext&
context, core::ProcessSession
// Collect FlowFiles to process
uint64_t actual_bytes = 0U;
- std::vector<std::shared_ptr<core::FlowFile>> flowFiles;
+ std::vector<api::core::FlowFile> flowFiles;
for (uint64_t i = 0; i < batch_size_; i++) {
- std::shared_ptr<core::FlowFile> flowFile = session.get();
+ auto flowFile = session.get();
if (flowFile == nullptr) { break; }
- actual_bytes += flowFile->getSize();
+ actual_bytes += session.getFlowFileSize(flowFile);
flowFiles.emplace_back(std::move(flowFile));
if (target_batch_payload_size_ != 0U && actual_bytes >=
target_batch_payload_size_) { break; }
}
if (flowFiles.empty()) {
- context.yield();
- return;
+ return MINIFI_STATUS_PROCESSOR_YIELD;
}
logger_->log_debug("Processing {} flow files with a total size of {} B",
flowFiles.size(), actual_bytes);
@@ -649,15 +645,17 @@ void PublishKafka::onTrigger(core::ProcessContext&
context, core::ProcessSession
});
// Process FlowFiles
- for (auto& flowFile: flowFiles) {
+ for (auto& flow_file: flowFiles) {
const size_t flow_file_index = messages->addFlowFile();
// Get Topic (FlowFile-dependent EL property)
- const auto topic = context.getProperty(Topic, flowFile.get());
+ const auto topic = api::utils::parseOptionalProperty(context, Topic,
&flow_file);
+ const std::string flow_file_id = session.getFlowFileId(flow_file);
+ const uint64_t flow_file_size = session.getFlowFileSize(flow_file);
if (topic) {
- logger_->log_debug("PublishKafka: topic for flow file {} is '{}'",
flowFile->getUUIDStr(), *topic);
+ logger_->log_debug("PublishKafka: topic for flow file {} is '{}'",
flow_file_id, *topic);
} else {
- logger_->log_error("Flow file {} does not have a valid Topic",
flowFile->getUUIDStr());
+ logger_->log_error("Flow file {} does not have a valid Topic",
flow_file_id);
messages->modifyResult(flow_file_index,
[](FlowFileResult& flow_file_result) {
flow_file_result.flow_file_error = true; });
continue;
@@ -665,7 +663,7 @@ void PublishKafka::onTrigger(core::ProcessContext& context,
core::ProcessSession
// Add topic to the connection if needed
if (!conn_->hasTopic(*topic)) {
- if (!createNewTopic(context, *topic, flowFile)) {
+ if (!createNewTopic(context, *topic, flow_file)) {
logger_->log_error("Failed to add topic {}", *topic);
messages->modifyResult(flow_file_index, [](FlowFileResult&
flow_file_result) {
flow_file_result.flow_file_error = true;
@@ -674,7 +672,7 @@ void PublishKafka::onTrigger(core::ProcessContext& context,
core::ProcessSession
}
}
- std::string kafkaKey = context.getProperty(KafkaKey,
flowFile.get()).value_or(flowFile->getUUIDStr());
+ std::string kafkaKey = api::utils::parseOptionalProperty(context,
KafkaKey, &flow_file).value_or(flow_file_id);
logger_->log_debug("PublishKafka: Message Key [{}]", kafkaKey);
@@ -687,30 +685,33 @@ void PublishKafka::onTrigger(core::ProcessContext&
context, core::ProcessSession
continue;
}
- bool failEmptyFlowFiles = utils::parseBoolProperty(context,
FailEmptyFlowFiles);
+ bool failEmptyFlowFiles = api::utils::parseBoolProperty(context,
FailEmptyFlowFiles);
+
ReadCallback callback(max_flow_seg_size_,
kafkaKey,
thisTopic->getTopic(),
conn_->getConnection(),
- *flowFile,
+ flow_file_size,
+ flow_file,
+ session,
attributeNameRegex_,
messages,
flow_file_index,
failEmptyFlowFiles,
logger_);
- session.read(flowFile, std::ref(callback));
+ session.read(flow_file, std::ref(callback));
if (!callback.called_) {
// workaround: call callback since ProcessSession doesn't do so for
empty flow files without resource claims
callback(nullptr);
}
- if (flowFile->getSize() == 0 && failEmptyFlowFiles) {
+ if (flow_file_size == 0 && failEmptyFlowFiles) {
logger_->log_debug(
"Deprecated behavior, use connections to drop empty flow files! "
"Failing empty flow file with uuid: {}",
- flowFile->getUUIDStr());
+ flow_file_id);
messages->modifyResult(flow_file_index,
[](FlowFileResult& flow_file_result) {
flow_file_result.flow_file_error = true; });
}
@@ -748,33 +749,32 @@ void PublishKafka::onTrigger(core::ProcessContext&
context, core::ProcessSession
logger_->log_error(
"Waiting for delivery confirmation was interrupted for flow "
"file {} segment {}",
- flowFiles[index]->getUUIDStr(),
+ session.getFlowFileId(flowFiles[index]),
segment_num);
break;
case MessageStatus::Error:
success = false;
logger_->log_error("Failed to deliver flow file {} segment {},
error: {}",
- flowFiles[index]->getUUIDStr(),
+ session.getFlowFileId(flowFiles[index]),
segment_num,
rd_kafka_err2str(message.err_code));
break;
case MessageStatus::Success:
logger_->log_debug("Successfully delivered flow file {} segment
{}",
- flowFiles[index]->getUUIDStr(),
+ session.getFlowFileId(flowFiles[index]),
segment_num);
break;
}
}
}
if (success) {
- session.transfer(flowFiles[index], Success);
+ session.transfer(std::move(flowFiles[index]), Success);
} else {
session.penalize(flowFiles[index]);
- session.transfer(flowFiles[index], Failure);
+ session.transfer(std::move(flowFiles[index]), Failure);
}
});
+ return MINIFI_STATUS_SUCCESS;
}
-REGISTER_RESOURCE(PublishKafka, Processor);
-
} // namespace org::apache::nifi::minifi::processors
diff --git a/extensions/kafka/PublishKafka.h b/extensions/kafka/PublishKafka.h
index 61cd6ccd3..682e4218e 100644
--- a/extensions/kafka/PublishKafka.h
+++ b/extensions/kafka/PublishKafka.h
@@ -29,19 +29,16 @@
#include "KafkaConnection.h"
#include "KafkaProcessorBase.h"
-#include "minifi-cpp/controllers/SSLContextServiceInterface.h"
-#include "core/Core.h"
-#include "minifi-cpp/core/FlowFile.h"
-#include "core/ProcessSession.h"
+#include "api/core/ProcessSession.h"
#include "minifi-cpp/core/PropertyDefinition.h"
#include "core/PropertyDefinitionBuilder.h"
#include "minifi-cpp/core/RelationshipDefinition.h"
#include "minifi-cpp/core/logging/Logger.h"
-#include "core/logging/LoggerFactory.h"
#include "minifi-cpp/core/PropertyValidator.h"
#include "rdkafka.h"
#include "utils/ArrayUtils.h"
#include "utils/RegexUtils.h"
+#include "minifi-cpp/core/Annotation.h"
namespace org::apache::nifi::minifi::processors {
@@ -245,8 +242,6 @@ class PublishKafka final : public KafkaProcessorBase {
EXTENSIONAPI static constexpr auto InputRequirement =
core::annotation::Input::INPUT_REQUIRED;
EXTENSIONAPI static constexpr bool IsSingleThreaded = false;
- ADD_COMMON_VIRTUAL_FUNCTIONS_FOR_PROCESSORS
-
using KafkaProcessorBase::KafkaProcessorBase;
PublishKafka(const PublishKafka&) = delete;
@@ -255,18 +250,17 @@ class PublishKafka final : public KafkaProcessorBase {
PublishKafka& operator=(PublishKafka&&) = delete;
~PublishKafka() override = default;
- void onTrigger(core::ProcessContext& context, core::ProcessSession& session)
override;
- void initialize() override;
- void onSchedule(core::ProcessContext& context, core::ProcessSessionFactory&
sessionFactory) override;
- void notifyStop() override;
+ MinifiStatus onTriggerImpl(api::core::ProcessContext& context,
api::core::ProcessSession& session) override;
+ MinifiStatus onScheduleImpl(api::core::ProcessContext& context) override;
+ void onUnSchedule() override;
class Messages;
protected:
- bool configureNewConnection(core::ProcessContext& context);
+ void configureNewConnection(api::core::ProcessContext& context);
bool createNewTopic(
- core::ProcessContext& context, const std::string& topic_name, const
std::shared_ptr<core::FlowFile>& flow_file) const;
- std::optional<utils::net::SslData> getSslData(core::ProcessContext& context)
const override;
+ const api::core::ProcessContext& context, const std::string& topic_name,
const api::core::FlowFile& flow_file) const;
+ std::optional<api::utils::net::SslData>
getSslData(api::core::ProcessContext& context) const override;
private:
KafkaConnectionKey key_;
diff --git a/extensions/kafka/rdkafka_utils.cpp
b/extensions/kafka/rdkafka_utils.cpp
index d3c38dab1..a9e85f975 100644
--- a/extensions/kafka/rdkafka_utils.cpp
+++ b/extensions/kafka/rdkafka_utils.cpp
@@ -18,8 +18,10 @@
#include "rdkafka_utils.h"
#include <array>
+#include <vector>
#include "minifi-cpp/Exception.h"
+#include "minifi-cpp/core/logging/Logger.h"
#include "utils/StringUtils.h"
namespace org::apache::nifi::minifi::utils {
@@ -34,12 +36,70 @@ void setKafkaConfigurationField(rd_kafka_conf_t&
configuration, const std::strin
}
}
-void print_topics_list(core::logging::Logger& logger, const
rd_kafka_topic_partition_list_t& kf_topic_partition_list) {
+void KafkaOpaque::printTopicsList(const rd_kafka_topic_partition_list_t&
kf_topic_partition_list) const {
+ if (!logger_.should_log(core::logging::debug))
+ return;
for (int i = 0; i < kf_topic_partition_list.cnt; ++i) {
- logger.log_debug("kf_topic_partition_list: topic: {}, partition: {},
offset: {}.", kf_topic_partition_list.elems[i].topic,
+ logger_.log_debug("kf_topic_partition_list: topic: {}, partition: {},
offset: {}.", kf_topic_partition_list.elems[i].topic,
kf_topic_partition_list.elems[i].partition,
kf_topic_partition_list.elems[i].offset);
}
}
+void KafkaOpaque::logCallback(const rd_kafka_t* rk, const int level, const
char*, const char* buf) {
+ const auto kafka_opaque = static_cast<KafkaOpaque*>(rd_kafka_opaque(rk));
+
+ if (!kafka_opaque) { return; }
+
+ switch (level) {
+ case 0: // LOG_EMERG
+ case 1: // LOG_ALERT
+ case 2: // LOG_CRIT
+ kafka_opaque->logger_.log_critical("{}", buf);
+ break;
+ case 3: // LOG_ERR
+ kafka_opaque->logger_.log_error("{}", buf);
+ break;
+ case 4: // LOG_WARNING
+ kafka_opaque->logger_.log_warn("{}", buf);
+ break;
+ case 5: // LOG_NOTICE
+ case 6: // LOG_INFO
+ kafka_opaque->logger_.log_info("{}", buf);
+ break;
+ case 7: // LOG_DEBUG
+ kafka_opaque->logger_.log_debug("{}", buf);
+ break;
+ default: gsl_FailFast();
+ }
+}
+void KafkaOpaque::rebalanceCallback(rd_kafka_t* rk, const rd_kafka_resp_err_t
trigger, rd_kafka_topic_partition_list_t* partitions, void* opaque_ptr) {
+ const auto* kafka_opaque = static_cast<KafkaOpaque*>(opaque_ptr);
+ if (!kafka_opaque) {
+ return;
+ }
+ kafka_opaque->logger_.log_debug("Rebalance triggered.");
+ rd_kafka_resp_err_t assign_error = RD_KAFKA_RESP_ERR_NO_ERROR;
+ switch (trigger) {
+ case RD_KAFKA_RESP_ERR__ASSIGN_PARTITIONS:
+ kafka_opaque->logger_.log_debug("assigned:");
+ kafka_opaque->printTopicsList(*partitions);
+ assign_error = rd_kafka_assign(rk, partitions);
+ break;
+
+ case RD_KAFKA_RESP_ERR__REVOKE_PARTITIONS:
+ kafka_opaque->logger_.log_debug("revoked:");
+ rd_kafka_commit(rk, partitions, /* async = */ 0); // Sync commit, maybe
unnecessary
+ kafka_opaque->printTopicsList(*partitions);
+
+ assign_error = rd_kafka_assign(rk, nullptr);
+ break;
+
+ default:
+ kafka_opaque->logger_.log_debug("failed: {}", rd_kafka_err2str(trigger));
+ assign_error = rd_kafka_assign(rk, nullptr);
+ break;
+ }
+ kafka_opaque->logger_.log_debug("assign failure: {}",
rd_kafka_err2str(assign_error));
+}
std::string get_human_readable_kafka_message_timestamp(const
rd_kafka_message_t& rkmessage) {
rd_kafka_timestamp_type_t tstype{};
diff --git a/extensions/kafka/rdkafka_utils.h b/extensions/kafka/rdkafka_utils.h
index b09e51a8a..96d0d79fe 100644
--- a/extensions/kafka/rdkafka_utils.h
+++ b/extensions/kafka/rdkafka_utils.h
@@ -17,16 +17,14 @@
#pragma once
-#include <chrono>
#include <optional>
#include <string>
#include <thread>
-#include <utility>
-#include <vector>
+#include <span>
-#include "core/logging/LoggerFactory.h"
#include "rdkafka.h"
-#include "utils/net/Ssl.h"
+#include "api/utils/Ssl.h"
+#include "minifi-cpp/core/logging/Logger.h"
namespace org::apache::nifi::minifi::utils {
@@ -87,18 +85,28 @@ template<typename T>
void kafka_headers_for_each(const rd_kafka_headers_t& headers, T
key_value_handle) {
const char* key = nullptr; // Null terminated, not to be freed
const void* value = nullptr;
- std::size_t size;
+ std::size_t size = 0;
for (std::size_t i = 0; RD_KAFKA_RESP_ERR_NO_ERROR ==
rd_kafka_header_get_all(&headers, i, &key, &value, &size); ++i) {
key_value_handle(std::string(key), std::span<const char>(static_cast<const
char*>(value), size));
}
}
void setKafkaConfigurationField(rd_kafka_conf_t& configuration, const
std::string& field_name, const std::string& value);
-void print_topics_list(core::logging::Logger& logger, const
rd_kafka_topic_partition_list_t& kf_topic_partition_list);
void print_kafka_message(const rd_kafka_message_t& rkmessage,
core::logging::Logger& logger);
std::string get_encoded_string(const std::string& input, KafkaEncoding
encoding);
std::optional<std::string> get_encoded_message_key(const rd_kafka_message_t&
message, KafkaEncoding encoding);
+class KafkaOpaque {
+ public:
+ explicit KafkaOpaque(core::logging::Logger& logger) : logger_(logger) {}
+
+ void printTopicsList(const rd_kafka_topic_partition_list_t&
kf_topic_partition_list) const;
+ static void logCallback(const rd_kafka_t* rk, int level, const char*
/*fac*/, const char* buf);
+ static void rebalanceCallback(rd_kafka_t* rk, rd_kafka_resp_err_t trigger,
rd_kafka_topic_partition_list_t* partitions, void* opaque_ptr);
+
+ private:
+ core::logging::Logger& logger_;
+};
} // namespace org::apache::nifi::minifi::utils
namespace magic_enum::customize {
diff --git a/extensions/kafka/tests/CMakeLists.txt
b/extensions/kafka/tests/CMakeLists.txt
index 98341cf8f..c1e285873 100644
--- a/extensions/kafka/tests/CMakeLists.txt
+++ b/extensions/kafka/tests/CMakeLists.txt
@@ -24,10 +24,10 @@ FOREACH (testfile ${KAFKA_INTEGRATION_TESTS})
get_filename_component(testfilename "${testfile}" NAME_WE)
add_minifi_executable("${testfilename}" "${testfile}")
target_include_directories(${testfilename} BEFORE PRIVATE
"${CMAKE_SOURCE_DIR}/extensions/kafka")
- createTests("${testfilename}")
+ set_target_properties(${testfilename} PROPERTIES RUNTIME_OUTPUT_DIRECTORY
"${CMAKE_BINARY_DIR}/bin")
target_link_libraries(${testfilename} Catch2WithMain)
- target_link_libraries(${testfilename} minifi-rdkafka-extensions)
- target_link_libraries(${testfilename} minifi-standard-processors)
+ target_link_libraries(${testfilename} minifi-kafka)
+ target_link_libraries(${testfilename} mock-minifi)
MATH(EXPR EXTENSIONS_TEST_COUNT "${EXTENSIONS_TEST_COUNT}+1")
add_test(NAME "${testfilename}" COMMAND "${testfilename}"
WORKING_DIRECTORY ${TEST_DIR})
set_tests_properties("${testfilename}" PROPERTIES LABELS
"librdkafka;memchecked")
diff --git a/extensions/kafka/tests/PublishKafkaTests.cpp
b/extensions/kafka/tests/PublishKafkaTests.cpp
index aeb286531..367b776e0 100644
--- a/extensions/kafka/tests/PublishKafkaTests.cpp
+++ b/extensions/kafka/tests/PublishKafkaTests.cpp
@@ -15,57 +15,44 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#include "unit/TestBase.h"
-#include "unit/TestUtils.h"
-#include "unit/Catch.h"
+#include "MockLogger.h"
+#include "MockProcessContext.h"
+#include "MockProcessSession.h"
+#include "MockUtils.h"
#include "PublishKafka.h"
-#include "unit/SingleProcessorTestController.h"
+#include "catch2/catch_test_macros.hpp"
+#include "catch2/matchers/catch_matchers.hpp"
-namespace org::apache::nifi::minifi::test {
+namespace org::apache::nifi::minifi::processors::test {
-TEST_CASE("Scheduling should fail when batch size is larger than the max queue
message count", "[testPublishKafka]") {
- LogTestController::getInstance().setTrace<TestPlan>();
- LogTestController::getInstance().setTrace<processors::PublishKafka>();
- SingleProcessorTestController
test_controller(minifi::test::utils::make_processor<processors::PublishKafka>("PublishKafka"));
- const auto publish_kafka = test_controller.getProcessor();
-
REQUIRE(publish_kafka->setProperty(processors::PublishKafka::ClientName.name,
"test_client"));
-
REQUIRE(publish_kafka->setProperty(processors::PublishKafka::SeedBrokers.name,
"test_seedbroker"));
-
REQUIRE(publish_kafka->setProperty(processors::PublishKafka::QueueBufferMaxMessage.name,
"1000"));
- REQUIRE(publish_kafka->setProperty(processors::PublishKafka::BatchSize.name,
"1500"));
- REQUIRE_THROWS_WITH(test_controller.trigger(""), "Process Schedule
Operation: Invalid configuration: Batch Size cannot be larger than Queue Max
Message");
+TEST_CASE("Batch Size cannot be larger than Queue Max Message",
"[PublishKafka]") {
+ auto publish_kafka = PublishKafka(mock::getMockMetadata());
+ auto context = mock::MockProcessContext{};
+ context.properties_.emplace(PublishKafka::ClientName.name, "test_client");
+ context.properties_.emplace(PublishKafka::SeedBrokers.name,
"test_seedbroker");
+ context.properties_.emplace(PublishKafka::QueueBufferMaxMessage.name,
"1000");
+ context.properties_.emplace(PublishKafka::BatchSize.name, "1500");
+
+ REQUIRE_THROWS_WITH(publish_kafka.onScheduleImpl(context),
+ "Process Schedule Operation: Invalid configuration: Batch Size cannot be
larger than Queue Max Message");
}
-TEST_CASE("Compress Codec property") {
- using processors::PublishKafka;
- SingleProcessorTestController
test_controller(minifi::test::utils::make_processor<PublishKafka>("PublishKafka"));
-
REQUIRE(test_controller.getProcessor<PublishKafka>()->setProperty(PublishKafka::ClientName.name,
"test_client"));
-
REQUIRE(test_controller.getProcessor<PublishKafka>()->setProperty(PublishKafka::SeedBrokers.name,
"test_seedbroker"));
-
REQUIRE(test_controller.getProcessor<PublishKafka>()->setProperty(PublishKafka::Topic.name,
"test_topic"));
-
REQUIRE(test_controller.getProcessor<PublishKafka>()->setProperty(PublishKafka::MessageTimeOut.name,
"10ms"));
+TEST_CASE("Trigger without valid broker", "[PublishKafka]") {
+ const auto logger = std::make_shared<mock::MockLogger>();
+ auto publish_kafka = PublishKafka(mock::metadataWithLogger(logger));
+ auto context = mock::MockProcessContext{};
+ context.properties_.emplace(PublishKafka::ClientName.name, "test_client");
+ context.properties_.emplace(PublishKafka::SeedBrokers.name,
"test_seedbroker");
+ context.properties_.emplace(PublishKafka::Topic.name, "test_topic");
+ context.properties_.emplace(PublishKafka::MessageTimeOut.name, "10 ms");
+
+
+ REQUIRE_NOTHROW(publish_kafka.onScheduleImpl(context));
+ auto session = mock::MockProcessSession{};
+
session.addInputFlowFile(std::make_unique<MinifiFlowFile>("test_input_flow_file"));
+ REQUIRE_NOTHROW(publish_kafka.onTriggerImpl(context, session));
- SECTION("none") {
-
REQUIRE(test_controller.getProcessor()->setProperty(PublishKafka::CompressCodec.name,
"none"));
- REQUIRE_NOTHROW(test_controller.trigger("input"));
- }
- SECTION("gzip") {
-
REQUIRE(test_controller.getProcessor()->setProperty(PublishKafka::CompressCodec.name,
"gzip"));
- REQUIRE_NOTHROW(test_controller.trigger("input"));
- }
- SECTION("snappy") {
-
REQUIRE(test_controller.getProcessor()->setProperty(PublishKafka::CompressCodec.name,
"snappy"));
- REQUIRE_NOTHROW(test_controller.trigger("input"));
- }
- SECTION("lz4") {
-
REQUIRE(test_controller.getProcessor()->setProperty(PublishKafka::CompressCodec.name,
"lz4"));
- REQUIRE_NOTHROW(test_controller.trigger("input"));
- }
- SECTION("zstd") {
-
REQUIRE(test_controller.getProcessor()->setProperty(PublishKafka::CompressCodec.name,
"zstd"));
- REQUIRE_NOTHROW(test_controller.trigger("input"));
- }
- SECTION("foo") {
-
REQUIRE_FALSE(test_controller.getProcessor()->setProperty(PublishKafka::CompressCodec.name,
"foo"));
- }
+ CHECK_FALSE(logger->logs_.empty());
}
-} // namespace org::apache::nifi::minifi::test
+} // namespace org::apache::nifi::minifi::processors::test
diff --git a/extensions/llamacpp/CMakeLists.txt
b/extensions/llamacpp/CMakeLists.txt
index 1de6be458..d8fd187a2 100644
--- a/extensions/llamacpp/CMakeLists.txt
+++ b/extensions/llamacpp/CMakeLists.txt
@@ -25,7 +25,7 @@ include(LlamaCpp)
include(${CMAKE_SOURCE_DIR}/extensions/ExtensionHeader.txt)
-file(GLOB SOURCES "processors/*.cpp")
+file(GLOB SOURCES "processors/*.cpp" "ExtensionInitializer.cpp")
add_minifi_library(minifi-llamacpp SHARED ${SOURCES})
target_include_directories(minifi-llamacpp PUBLIC
"${CMAKE_SOURCE_DIR}/extensions/llamacpp")
diff --git a/extensions/llamacpp/processors/ExtensionInitializer.cpp
b/extensions/llamacpp/ExtensionInitializer.cpp
similarity index 85%
rename from extensions/llamacpp/processors/ExtensionInitializer.cpp
rename to extensions/llamacpp/ExtensionInitializer.cpp
index 8e06bf0cc..09b142ff2 100644
--- a/extensions/llamacpp/processors/ExtensionInitializer.cpp
+++ b/extensions/llamacpp/ExtensionInitializer.cpp
@@ -15,9 +15,9 @@
* limitations under the License.
*/
-#include "RunLlamaCppInference.h"
#include "api/core/Resource.h"
#include "api/utils/minifi-c-utils.h"
+#include "processors/RunLlamaCppInference.h"
#define MKSOC(x) #x
#define MAKESTRING(x) MKSOC(x) // NOLINT(cppcoreguidelines-macro-usage)
@@ -34,7 +34,5 @@ CEXTENSIONAPI void
MinifiInitExtension(MinifiExtensionContext* extension_context
.user_data = nullptr
};
auto* extension = MinifiRegisterExtension(extension_context,
&extension_definition);
-
minifi::api::core::useProcessorClassDefinition<minifi::extensions::llamacpp::processors::RunLlamaCppInference>([&]
(const MinifiProcessorClassDefinition& definition) {
- MinifiRegisterProcessor(extension, &definition);
- });
+
minifi::api::core::registerProcessors<minifi::extensions::llamacpp::processors::RunLlamaCppInference>(extension);
}
diff --git a/libminifi/src/minifi-c.cpp b/libminifi/src/minifi-c.cpp
index 1c5d98fcf..4cb3da553 100644
--- a/libminifi/src/minifi-c.cpp
+++ b/libminifi/src/minifi-c.cpp
@@ -410,7 +410,7 @@ MinifiLogLevel MinifiLoggerLevel(MinifiLogger* logger) {
}
MINIFI_OWNED gsl::owner<MinifiPublishedMetrics*>
MinifiPublishedMetricsCreate(const size_t count, const MinifiStringView*
metric_names, const double* metric_values) {
- const gsl::owner<std::vector<minifi::state::PublishedMetric>*> metrics = new
std::vector<minifi::state::PublishedMetric>(); // NOLINT(modernize-use-auto)
+ gsl::owner<std::vector<minifi::state::PublishedMetric>*> metrics = new
std::vector<minifi::state::PublishedMetric>(); // NOLINT(modernize-use-auto,
cppcoreguidelines-owning-memory)
metrics->reserve(count);
for (size_t i = 0; i < count; i++) {
metrics->emplace_back(minifi::state::PublishedMetric{toString(metric_names[i]),
metric_values[i], {}});
@@ -613,13 +613,15 @@ void
MinifiProcessContextGetDynamicProperties(MinifiProcessContext* context, Min
}
}
-MinifiStatus MinifiProcessContextGetSslData(MinifiProcessContext*
process_context, MinifiStringView controller_service_name,
+MinifiStatus MinifiProcessContextGetSslDataFromProperty(MinifiProcessContext*
process_context, MinifiStringView property_name,
void (*cb)(void* user_ctx, const MinifiSslData* ssl_data), void* user_ctx)
{
gsl_Assert(process_context != MINIFI_NULL);
try {
const auto context =
reinterpret_cast<minifi::core::ProcessContext*>(process_context);
- const auto name_str = std::string{toStringView(controller_service_name)};
- const auto service_shared_ptr = context->getControllerService(name_str,
context->getProcessorInfo().getUUID());
+ const auto property_name_str = std::string{toStringView(property_name)};
+ const auto name_str = context->getProperty(property_name_str, nullptr);
+ if (!name_str) { return MINIFI_STATUS_PROPERTY_NOT_SET; }
+ const auto service_shared_ptr = context->getControllerService(*name_str,
context->getProcessorInfo().getUUID());
if (!service_shared_ptr) { return MINIFI_STATUS_VALIDATION_FAILED; }
if (const auto ssl_context_service =
dynamic_cast<minifi::controllers::SSLContextServiceInterface*>(service_shared_ptr.get()))
{
const std::string ca_cert_file =
ssl_context_service->getCACertificate().string();
diff --git a/minifi-api/include/minifi-c/minifi-c.h
b/minifi-api/include/minifi-c/minifi-c.h
index e846efbdc..b885cd614 100644
--- a/minifi-api/include/minifi-c/minifi-c.h
+++ b/minifi-api/include/minifi-c/minifi-c.h
@@ -280,7 +280,7 @@ typedef struct MinifiSslData {
MinifiStringView passphrase;
} MinifiSslData;
-MinifiStatus MinifiProcessContextGetSslData(MinifiProcessContext*
process_context, MinifiStringView controller_service_name,
+MinifiStatus MinifiProcessContextGetSslDataFromProperty(MinifiProcessContext*
process_context, MinifiStringView property_name,
void (*cb)(void* user_ctx, const MinifiSslData* ssl_data), void* user_ctx);
#ifdef __cplusplus
diff --git a/minifi-api/minifi-c-api.def b/minifi-api/minifi-c-api.def
index 0fcaf2ad9..0baa3063c 100644
--- a/minifi-api/minifi-c-api.def
+++ b/minifi-api/minifi-c-api.def
@@ -29,4 +29,4 @@ EXPORTS
MinifiProcessSessionGetFlowFileSize
MinifiProcessSessionGetFlowFileId
MinifiProcessContextGetDynamicProperties
- MinifiProcessContextGetSslData
+ MinifiProcessContextGetSslDataFromProperty