diff --git a/extension-framework/cpp-extension-lib/include/api/core/ProcessorImpl.h b/extension-framework/cpp-extension-lib/include/api/core/ProcessorImpl.h index 57e13e6f49..a5753b2d3f 100644 --- a/extension-framework/cpp-extension-lib/include/api/core/ProcessorImpl.h +++ b/extension-framework/cpp-extension-lib/include/api/core/ProcessorImpl.h @@ -74,8 +74,8 @@ class ProcessorImpl { static constexpr auto OutputAttributes = std::array{}; std::string getName() const; - utils::Identifier getUUID() const; - utils::SmallString<36> getUUIDStr() const; + minifi::utils::Identifier getUUID() const; + minifi::utils::SmallString<36> getUUIDStr() const; virtual PublishedMetrics calculateMetrics() const {return {};} diff --git a/libminifi/include/core/extension/Extension.h b/libminifi/include/core/extension/Extension.h index 2b0753f799..060c889f06 100644 --- a/libminifi/include/core/extension/Extension.h +++ b/libminifi/include/core/extension/Extension.h @@ -62,6 +62,10 @@ class Extension { return info_; } + void addClass(std::string class_name) { + classes_.push_back(std::move(class_name)); + } + private: #ifdef WIN32 std::map resource_mapping_; @@ -85,6 +89,7 @@ class Extension { gsl::owner handle_ = nullptr; std::optional info_; + std::vector classes_; uint32_t api_version_{0}; const std::shared_ptr logger_; diff --git a/libminifi/src/core/extension/Extension.cpp b/libminifi/src/core/extension/Extension.cpp index 1e600d3b51..1d210d3b6a 100644 --- a/libminifi/src/core/extension/Extension.cpp +++ b/libminifi/src/core/extension/Extension.cpp @@ -39,6 +39,7 @@ #include "minifi-c/minifi-c.h" #include "minifi-cpp/agent/agent_docs.h" #include "utils/RegexUtils.h" +#include "core/ClassLoader.h" namespace org::apache::nifi::minifi::core::extension { @@ -116,6 +117,11 @@ Extension::~Extension() { if (info_ && info_->deinit) { info_->deinit(info_->user_data); } + if (info_) { + for (auto& class_name : classes_) { + core::ClassLoader::getDefaultClassLoader().getClassLoader(info_->name).unregisterClass(class_name); + } + } unload(); const std::string bundle_name = info_ ? info_->name : library_name_; diff --git a/libminifi/src/minifi-c.cpp b/libminifi/src/minifi-c.cpp index 44152eb25d..9a556e99ce 100644 --- a/libminifi/src/minifi-c.cpp +++ b/libminifi/src/minifi-c.cpp @@ -243,7 +243,8 @@ MinifiExtension* MinifiRegisterExtension(MinifiExtensionContext* extension_conte MinifiStatus MinifiRegisterProcessor(MinifiExtension* extension_handle, const MinifiProcessorClassDefinition* processor) { gsl_Assert(extension_handle); gsl_Assert(processor); - auto extension_info = reinterpret_cast(extension_handle)->getInfo(); + auto* extension = reinterpret_cast(extension_handle); + auto extension_info = extension->getInfo(); if (!extension_info) { return MINIFI_STATUS_UNKNOWN_ERROR; } @@ -257,6 +258,7 @@ MinifiStatus MinifiRegisterProcessor(MinifiExtension* extension_handle, const Mi c_class_description.name, std::make_unique(extension_info->name, toString(processor->full_name), c_class_description)); bundle_components.processors.emplace_back(description); + extension->addClass(c_class_description.name); }); return MINIFI_STATUS_SUCCESS; } diff --git a/libminifi/test/integration/extension-verification-test/CApiExtension.cpp b/libminifi/test/integration/extension-verification-test/CApiExtension.cpp index eb66d4e1b5..2b2b981ae8 100644 --- a/libminifi/test/integration/extension-verification-test/CApiExtension.cpp +++ b/libminifi/test/integration/extension-verification-test/CApiExtension.cpp @@ -18,12 +18,28 @@ #include "api/core/Resource.h" #include "api/utils/minifi-c-utils.h" +#include "api/core/ProcessorImpl.h" #define MKSOC(x) #x #define MAKESTRING(x) MKSOC(x) // NOLINT(cppcoreguidelines-macro-usage) namespace minifi = org::apache::nifi::minifi; +class DummyCProcessor : public minifi::api::core::ProcessorImpl { + public: + using ProcessorImpl::ProcessorImpl; + + static constexpr const char* Description = "C processor that does nothing"; + + static constexpr auto Relationships = std::array{}; + static constexpr auto Properties = std::array{}; + static constexpr auto OutputAttributes = std::array{}; + static constexpr bool SupportsDynamicProperties = false; + static constexpr bool SupportsDynamicRelationships = false; + static constexpr minifi::core::annotation::Input InputRequirement = minifi::core::annotation::Input::INPUT_ALLOWED; + static constexpr bool IsSingleThreaded = true; +}; + CEXTENSIONAPI const uint32_t MinifiApiVersion = MINIFI_TEST_API_VERSION; CEXTENSIONAPI void MinifiInitExtension(MinifiExtensionContext* extension_context) { @@ -33,5 +49,8 @@ CEXTENSIONAPI void MinifiInitExtension(MinifiExtensionContext* extension_context .deinit = nullptr, .user_data = nullptr }; - MinifiRegisterExtension(extension_context, &extension_definition); + auto extension = MinifiRegisterExtension(extension_context, &extension_definition); + minifi::api::core::useProcessorClassDescription([&] (const MinifiProcessorClassDefinition& description) { + MinifiRegisterProcessor(extension, &description); + }); } diff --git a/libminifi/test/integration/extension-verification-test/CMakeLists.txt b/libminifi/test/integration/extension-verification-test/CMakeLists.txt index e38accb0ea..05739901eb 100644 --- a/libminifi/test/integration/extension-verification-test/CMakeLists.txt +++ b/libminifi/test/integration/extension-verification-test/CMakeLists.txt @@ -85,3 +85,12 @@ create_loading_test_extension(test-extension-loading-missing-init InvalidMissing create_loading_test_extension(test-extension-loading-create-not-called CreateNotCalled.cpp) target_link_libraries(test-extension-loading-create-not-called minifi-cpp-extension-lib) target_compile_definitions(test-extension-loading-create-not-called PRIVATE MINIFI_TEST_API_VERSION=10) + +### Extension Loading Tests +add_minifi_executable(ExtensionLoadingTests ExtensionLoadingTests.cpp) +createIntegrationTests(ExtensionLoadingTests) +target_link_libraries(ExtensionLoadingTests core-minifi Catch2WithMain Threads::Threads) +add_test(NAME ExtensionLoadingTests COMMAND ExtensionLoadingTests) + +create_c_test_extension(c-resources 10) +add_dependencies(ExtensionLoadingTests test-extension-loading-c-resources) diff --git a/libminifi/test/integration/extension-verification-test/ExtensionLoadingTests.cpp b/libminifi/test/integration/extension-verification-test/ExtensionLoadingTests.cpp new file mode 100644 index 0000000000..0fb14bc967 --- /dev/null +++ b/libminifi/test/integration/extension-verification-test/ExtensionLoadingTests.cpp @@ -0,0 +1,54 @@ +/** +* + * 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 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define CUSTOM_EXTENSION_INIT + +#include "unit/TestBase.h" +#include "unit/Catch.h" +#include "unit/TestUtils.h" +#include "core/extension/ExtensionManager.h" +#include "core/extension/ApiVersion.h" +#include "core/ClassLoader.h" + +namespace minifi = org::apache::nifi::minifi; + +class ExtensionLoadingTestController { + public: + explicit ExtensionLoadingTestController(std::string pattern): extension_manager_{[&] () { + LogTestController::getInstance().clear(); + LogTestController::getInstance().setTrace(); + LogTestController::getInstance().setTrace(); + minifi::core::extension::test_setAgentApiVersion(10); + minifi::core::extension::test_setMinSupportedApiVersion(5); + auto config = minifi::Configure::create(); + config->set(minifi::Configuration::nifi_extension_path, pattern); + return config; + }()} {} + + std::optional extension_manager_; +}; + +TEST_CASE("Loading an extension makes the processors available") { + CHECK_FALSE(core::ClassLoader::getDefaultClassLoader().instantiate("DummyCProcessor", "dummy")); + { + ExtensionLoadingTestController controller{"*test-extension-loading-c-resources*"}; + CHECK(core::ClassLoader::getDefaultClassLoader().instantiate("DummyCProcessor", "dummy")); + } + CHECK_FALSE(core::ClassLoader::getDefaultClassLoader().instantiate("DummyCProcessor", "dummy")); +} +