Skip to content

Commit

Permalink
Fix failure with initializing a component config after a config update (
Browse files Browse the repository at this point in the history
#666)

* Fix failure with initializing a component config after a config update

This fixes a failure when a component configuration with at least service reference and a dependency on a config object would never be registered if the config object became satisfied before the component configuration was able to register the config change listeners.

Signed-off-by: The MathWorks, Inc. <jdicleme@mathworks.com>
  • Loading branch information
jeffdiclemente committed May 13, 2022
1 parent e75fa22 commit 49028dd
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ void ComponentConfigurationImpl::Initialize()
configListenerTokens.emplace_back(listenerToken);
}
configManager->Initialize();
if (referenceManagers.empty() && configManager->IsConfigSatisfied()) {
if (AreReferencesSatisfied() && configManager->IsConfigSatisfied()) {
GetState()->Register(*this);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@
#include <random>

#include "../src/SCRAsyncWorkService.hpp"
#include "../src/manager/BundleOrPrototypeComponentConfiguration.hpp"
#include "../src/manager/ComponentConfigurationImpl.hpp"
#include "../src/manager/ReferenceManager.hpp"
#include "../src/manager/SingletonComponentConfiguration.hpp"
#include "../src/manager/states/CCActiveState.hpp"
#include "../src/manager/states/CCRegisteredState.hpp"
#include "../src/manager/states/CCUnsatisfiedReferenceState.hpp"
Expand Down Expand Up @@ -835,5 +837,75 @@ TEST_F(ComponentConfigurationImplTest, TestComponentWithUniqueName)
auto svc = testBundle.GetBundleContext().GetService<test::Interface1>(svcRef);
EXPECT_NE(svc, nullptr);
}

TEST_F(ComponentConfigurationImplTest, VerifyStateChangeWithSvcRefAndConfig)
{
auto mockMetadata = std::make_shared<metadata::ComponentMetadata>();
// Test that a call to Register with a component containing a service,
// a service reference and a config object dependency will trigger a state change
// when the config object is satisfied before the config object change listener is
// registered.
mockMetadata->serviceMetadata.interfaces = { us_service_interface_iid<dummy::Reference1>() };
scrimpl::metadata::ReferenceMetadata refMetadata{};
refMetadata.interfaceName = "cppmicroservices::scrimpl::dummy::ServiceImpl";
mockMetadata->refsMetadata.push_back(refMetadata);
mockMetadata->configurationPolicy = "require";
mockMetadata->configurationPids = {"foo"};

auto fakeLogger = std::make_shared<FakeLogger>();
auto asyncWorkService =
std::make_shared<cppmicroservices::scrimpl::SCRAsyncWorkService>(
GetFramework().GetBundleContext(), fakeLogger);
auto notifier = std::make_shared<ConfigurationNotifier>(
GetFramework().GetBundleContext(), fakeLogger, asyncWorkService);

auto fakeCompConfig = std::make_shared<SingletonComponentConfigurationImpl>(
mockMetadata,
GetFramework(),
std::make_shared<MockComponentRegistry>(),
fakeLogger,
notifier,
std::make_shared<std::vector<std::shared_ptr<ComponentManager>>>());

auto fakeBundleProtoCompConfig = std::make_shared<BundleOrPrototypeComponentConfigurationImpl>(
mockMetadata,
GetFramework(),
std::make_shared<MockComponentRegistry>(),
fakeLogger,
notifier,
std::make_shared<std::vector<std::shared_ptr<ComponentManager>>>());

auto svcReg = GetFramework().GetBundleContext().RegisterService<dummy::ServiceImpl>(std::make_shared<dummy::ServiceImpl>());

// update config object to satisfy component configuration
test::InstallAndStartConfigAdmin(GetFramework().GetBundleContext());
auto svcRef = GetFramework().GetBundleContext().GetServiceReference<cppmicroservices::service::cm::ConfigurationAdmin>();
ASSERT_TRUE(svcRef);
auto configAdminSvc = GetFramework().GetBundleContext().GetService(svcRef);
ASSERT_TRUE(configAdminSvc);
auto fooConfig = configAdminSvc->GetConfiguration("foo");
cppmicroservices::AnyMap configData(cppmicroservices::AnyMap::UNORDERED_MAP_CASEINSENSITIVE_KEYS);
configData["bar"] = std::string{"baz"};
// update the config object before calling fakeCompConfig->Initialize(), which simulates the config object
// being updated before the ComponentConfigurationImpl has a chance to setup config change listeners.
ASSERT_NO_THROW(fooConfig->UpdateIfDifferent(configData).second.get());

// Initialize should set the component state to Satisfied, even if the config object update was missed
// by the config object change listener.
fakeCompConfig->Initialize();
EXPECT_EQ(cppmicroservices::service::component::runtime::dto::ComponentState::SATISFIED,
fakeCompConfig->GetConfigState());

fakeBundleProtoCompConfig->Initialize();
EXPECT_EQ(cppmicroservices::service::component::runtime::dto::ComponentState::SATISFIED,
fakeBundleProtoCompConfig->GetConfigState());

fakeCompConfig->Deactivate();
fakeCompConfig->Stop();
fakeBundleProtoCompConfig->Deactivate();
fakeBundleProtoCompConfig->Stop();
svcReg.Unregister();
svcReg = nullptr;
}
}
}
16 changes: 16 additions & 0 deletions compendium/DeclarativeServices/test/TestUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,22 @@ std::string GetConfigAdminRuntimePluginFilePath()
return PathToLib(libName);
}

void InstallAndStartConfigAdmin(::cppmicroservices::BundleContext frameworkCtx)
{
std::vector<cppmicroservices::Bundle> bundles;
auto cmPluginPath = test::GetConfigAdminRuntimePluginFilePath();

#if defined(US_BUILD_SHARED_LIBS)
bundles = frameworkCtx.InstallBundles(cmPluginPath);
#else
bundles = frameworkCtx.GetBundles();
#endif

for (auto b : bundles) {
b.Start();
}
}

bool isBundleLoadedInThisProcess(std::string bundleName)
{
#if defined(US_PLATFORM_WINDOWS)
Expand Down

0 comments on commit 49028dd

Please sign in to comment.