This is an automated email from the ASF dual-hosted git repository. pnoltes pushed a commit to branch feature/585-celix-conditions in repository https://gitbox.apache.org/repos/asf/celix.git
commit f07f6e2d9905f7213606617132fdd59806e6d5b2 Author: Pepijn Noltes <[email protected]> AuthorDate: Thu Jul 6 22:50:49 2023 +0200 Add component.ready framework condition --- .../framework/gtest/src/CondTestBundleActivator.cc | 21 +++- .../gtest/src/FrameworkBundleTestSuite.cc | 40 +++++-- .../FrameworkBundleWithErrorInjectionTestSuite.cc | 28 ++++- libs/framework/include/celix_condition.h | 8 ++ libs/framework/src/celix_framework_bundle.c | 121 ++++++++++++--------- libs/framework/src/celix_framework_bundle.h | 12 +- libs/framework/src/framework.c | 2 + 7 files changed, 159 insertions(+), 73 deletions(-) diff --git a/libs/framework/gtest/src/CondTestBundleActivator.cc b/libs/framework/gtest/src/CondTestBundleActivator.cc index 2f6ac983..e349cbcc 100644 --- a/libs/framework/gtest/src/CondTestBundleActivator.cc +++ b/libs/framework/gtest/src/CondTestBundleActivator.cc @@ -20,12 +20,27 @@ #include "celix/BundleActivator.h" #include "celix_condition.h" -class CndTestBundleActivator { +/** + * @brief Empty Test Component for testing the condition service + */ +class CondComponent { +public: + CondComponent() = default; +}; + +class CondTestBundleActivator { public: - explicit CndTestBundleActivator(const std::shared_ptr<celix::BundleContext>& ctx) { + explicit CondTestBundleActivator(const std::shared_ptr<celix::BundleContext>& ctx) { registration = ctx->registerUnmanagedService<celix_condition>(&condition, CELIX_CONDITION_SERVICE_NAME) .addProperty(CELIX_CONDITION_ID, "test") .build(); + + auto& cmp = ctx->getDependencyManager()->createComponent<CondComponent>(); + cmp.createServiceDependency<celix_condition>(CELIX_CONDITION_SERVICE_NAME) + .setFilter("(condition.id=does-not-exists)") + .setRequired(true); + + cmp.build(); } private: @@ -33,4 +48,4 @@ class CndTestBundleActivator { celix_condition condition{}; }; -CELIX_GEN_CXX_BUNDLE_ACTIVATOR(CndTestBundleActivator) \ No newline at end of file +CELIX_GEN_CXX_BUNDLE_ACTIVATOR(CondTestBundleActivator) \ No newline at end of file diff --git a/libs/framework/gtest/src/FrameworkBundleTestSuite.cc b/libs/framework/gtest/src/FrameworkBundleTestSuite.cc index 1eb7c626..bd5d6985 100644 --- a/libs/framework/gtest/src/FrameworkBundleTestSuite.cc +++ b/libs/framework/gtest/src/FrameworkBundleTestSuite.cc @@ -27,15 +27,17 @@ class FrameworkBundleTestSuite : public ::testing::Test { public: const int USE_SERVICE_TIMEOUT_IN_MS = 500; const std::string trueFilter = std::string{"("} + CELIX_CONDITION_ID + "=" + CELIX_CONDITION_ID_TRUE + ")"; - const std::string readyFilter = + const std::string frameworkReadyFilter = std::string{"("} + CELIX_CONDITION_ID + "=" + CELIX_CONDITION_ID_FRAMEWORK_READY + ")"; - const std::string errorFilter = + const std::string frameworkErrorFilter = std::string{"("} + CELIX_CONDITION_ID + "=" + CELIX_CONDITION_ID_FRAMEWORK_ERROR + ")"; + const std::string componentsReadyFilter = + std::string{"("} + CELIX_CONDITION_ID + "=" + CELIX_CONDITION_ID_COMPONENTS_READY + ")"; FrameworkBundleTestSuite() = default; }; -TEST_F(FrameworkBundleTestSuite, ConditionTrueAndFrameworkReadyTest) { +TEST_F(FrameworkBundleTestSuite, ConditionTrueFrameworkReadyAndComponentsReadyTest) { // Given a Celix framework (with conditions enabled (default)) auto fw = celix::createFramework(); auto ctx = fw->getFrameworkBundleContext(); @@ -46,14 +48,21 @@ TEST_F(FrameworkBundleTestSuite, ConditionTrueAndFrameworkReadyTest) { // And the condition service with id "framework.error" will not become available count = ctx->useService<celix_condition>(CELIX_CONDITION_SERVICE_NAME) - .setFilter(errorFilter) + .setFilter(frameworkErrorFilter) .setTimeout(std::chrono::milliseconds{USE_SERVICE_TIMEOUT_IN_MS}) .build(); EXPECT_EQ(0, count); // But the condition service with id "framework.ready" will become available count = ctx->useService<celix_condition>(CELIX_CONDITION_SERVICE_NAME) - .setFilter(readyFilter) + .setFilter(frameworkReadyFilter) + .setTimeout(std::chrono::milliseconds{USE_SERVICE_TIMEOUT_IN_MS}) + .build(); + EXPECT_EQ(1, count); + + // And the condition service with id "components.ready" will become available + count = ctx->useService<celix_condition>(CELIX_CONDITION_SERVICE_NAME) + .setFilter(componentsReadyFilter) .setTimeout(std::chrono::milliseconds{USE_SERVICE_TIMEOUT_IN_MS}) .build(); EXPECT_EQ(1, count); @@ -70,20 +79,20 @@ TEST_F(FrameworkBundleTestSuite, ConditionTrueAndFrameworkErrorTest) { // And the condition service with id "framework.ready" does not become available (framework startup error) count = ctx->useService<celix_condition>(CELIX_CONDITION_SERVICE_NAME) - .setFilter(readyFilter) + .setFilter(frameworkReadyFilter) .setTimeout(std::chrono::milliseconds{USE_SERVICE_TIMEOUT_IN_MS}) .build(); EXPECT_EQ(0, count); // But the condition service with id "framework.error" will become available count = ctx->useService<celix_condition>(CELIX_CONDITION_SERVICE_NAME) - .setFilter(errorFilter) + .setFilter(frameworkErrorFilter) .setTimeout(std::chrono::milliseconds{USE_SERVICE_TIMEOUT_IN_MS}) .build(); EXPECT_EQ(1, count); } -TEST_F(FrameworkBundleTestSuite, FrameworkReadyRegisteredLastTest) { +TEST_F(FrameworkBundleTestSuite, FrameworkReadyRegisteredLaterTest) { // Given a Celix framework which is configured to start a bundle with a condition test service auto fw = celix::createFramework({{celix::AUTO_START_0, COND_TEST_BUNDLE_LOC}}); auto ctx = fw->getFrameworkBundleContext(); @@ -94,14 +103,14 @@ TEST_F(FrameworkBundleTestSuite, FrameworkReadyRegisteredLastTest) { // And the condition service with id "framework.error" will not become available count = ctx->useService<celix_condition>(CELIX_CONDITION_SERVICE_NAME) - .setFilter(errorFilter) + .setFilter(frameworkErrorFilter) .setTimeout(std::chrono::milliseconds{USE_SERVICE_TIMEOUT_IN_MS}) .build(); EXPECT_EQ(0, count); // But the condition service with id "framework.ready" will become available count = ctx->useService<celix_condition>(CELIX_CONDITION_SERVICE_NAME) - .setFilter(readyFilter) + .setFilter(frameworkReadyFilter) .setTimeout(std::chrono::milliseconds{USE_SERVICE_TIMEOUT_IN_MS}) .build(); EXPECT_EQ(1, count); @@ -116,7 +125,16 @@ TEST_F(FrameworkBundleTestSuite, FrameworkReadyRegisteredLastTest) { // And the service.id of the framework.ready condition is higher than the service.id of the test condition //(white-box test, framework.ready condition is registered last) - long readySvcId = ctx->findServiceWithName(CELIX_CONDITION_SERVICE_NAME, readyFilter); + long readySvcId = ctx->findServiceWithName(CELIX_CONDITION_SERVICE_NAME, frameworkReadyFilter); long testySvcId = ctx->findServiceWithName(CELIX_CONDITION_SERVICE_NAME, testFilter); EXPECT_GT(readySvcId, testySvcId); + + // And the "components.ready" condition is not available, because the test bundle contains a component which will + // not become active + count = ctx->useService<celix_condition>(CELIX_CONDITION_SERVICE_NAME) + .setFilter(componentsReadyFilter) + .setTimeout(std::chrono::milliseconds{USE_SERVICE_TIMEOUT_IN_MS}) + .build(); + EXPECT_EQ(0, count); } + diff --git a/libs/framework/gtest/src/FrameworkBundleWithErrorInjectionTestSuite.cc b/libs/framework/gtest/src/FrameworkBundleWithErrorInjectionTestSuite.cc index 3f9f4938..1feef016 100644 --- a/libs/framework/gtest/src/FrameworkBundleWithErrorInjectionTestSuite.cc +++ b/libs/framework/gtest/src/FrameworkBundleWithErrorInjectionTestSuite.cc @@ -30,8 +30,10 @@ class FrameworkBundleWithErrorInjectionTestSuite : public ::testing::Test { public: const int USE_SERVICE_TIMEOUT_IN_MS = 500; - const std::string readyFilter = + const std::string frameworkReadyFilter = std::string{"("} + CELIX_CONDITION_ID + "=" + CELIX_CONDITION_ID_FRAMEWORK_READY + ")"; + const std::string componentsReadyFilter = + std::string{"("} + CELIX_CONDITION_ID + "=" + CELIX_CONDITION_ID_COMPONENTS_READY + ")"; FrameworkBundleWithErrorInjectionTestSuite() = default; @@ -59,7 +61,7 @@ TEST_F(FrameworkBundleWithErrorInjectionTestSuite, ErroCreatingFrameworkBundleTe } TEST_F(FrameworkBundleWithErrorInjectionTestSuite, ErrorStartingFrameworkBundleTest) { - // Wen an error injection for celix_properties_create is primed when called (indirectly) from + // When an error injection for celix_properties_create is primed when called (indirectly) from // celix_frameworkBundle_start celix_ei_expect_celix_properties_create((void*)celix_frameworkBundle_start, 1, nullptr); @@ -68,8 +70,8 @@ TEST_F(FrameworkBundleWithErrorInjectionTestSuite, ErrorStartingFrameworkBundleT } TEST_F(FrameworkBundleWithErrorInjectionTestSuite, ErrorRegisteringFrameworkReadyConditionTest) { - // When an error injection for celix_properties_create is primed when called from celix_frameworkBundle_readyCheck - celix_ei_expect_celix_properties_create((void*)celix_frameworkBundle_readyCheck, 0, nullptr); + // When an error injection for celix_properties_create is primed when called from celix_frameworkBundle_handleFrameworkEvent + celix_ei_expect_celix_properties_create((void*)celix_frameworkBundle_handleFrameworkEvent, 0, nullptr); // And a framework instance is created auto fw = celix::createFramework(); @@ -77,7 +79,23 @@ TEST_F(FrameworkBundleWithErrorInjectionTestSuite, ErrorRegisteringFrameworkRead // Then the framework.ready condition will not become available, due to an error creating properties for the service auto count = ctx->useService<celix_condition>(CELIX_CONDITION_SERVICE_NAME) - .setFilter(readyFilter) + .setFilter(frameworkReadyFilter) + .setTimeout(std::chrono::milliseconds {USE_SERVICE_TIMEOUT_IN_MS}) + .build(); + EXPECT_EQ(count, 0); +} + +TEST_F(FrameworkBundleWithErrorInjectionTestSuite, ErrorRegisteringComponentsReadyConditionTest) { + // When an error injection for celix_properties_create is primed when called from celix_frameworkBundle_componentsCheck + celix_ei_expect_celix_properties_create((void*)celix_frameworkBundle_componentsCheck, 0, nullptr); + + // And a framework instance is created + auto fw = celix::createFramework(); + auto ctx = fw->getFrameworkBundleContext(); + + // Then the components.ready condition will not become available, due to an error creating properties for the service + auto count = ctx->useService<celix_condition>(CELIX_CONDITION_SERVICE_NAME) + .setFilter(componentsReadyFilter) .setTimeout(std::chrono::milliseconds {USE_SERVICE_TIMEOUT_IN_MS}) .build(); EXPECT_EQ(count, 0); diff --git a/libs/framework/include/celix_condition.h b/libs/framework/include/celix_condition.h index ee6642ce..f901e61c 100644 --- a/libs/framework/include/celix_condition.h +++ b/libs/framework/include/celix_condition.h @@ -89,6 +89,14 @@ typedef struct celix_condition { */ #define CELIX_CONDITION_ID_FRAMEWORK_ERROR "framework.error" +/*! + * @brief The unique identifier for the components.ready condition. + * + * The components ready condition is registered by the framework if the framework.ready condition is registered + * and all components active. + */ +#define CELIX_CONDITION_ID_COMPONENTS_READY "components.ready" + #ifdef __cplusplus } #endif diff --git a/libs/framework/src/celix_framework_bundle.c b/libs/framework/src/celix_framework_bundle.c index 65c91b8e..883d5c50 100644 --- a/libs/framework/src/celix_framework_bundle.c +++ b/libs/framework/src/celix_framework_bundle.c @@ -22,6 +22,7 @@ #include "celix_condition.h" #include "celix_constants.h" #include "celix_threads.h" +#include "celix_dependency_manager.h" #include "framework_private.h" #define CELIX_FRAMEWORK_CONDITION_SERVICES_ENABLED_DEFAULT true @@ -36,29 +37,13 @@ typedef struct celix_framework_bundle_activator { celix_thread_mutex_t mutex; /**< protects below. */ long trueConditionSvcId; /**< service id of the condition service which is always true. */ - bool frameworkStartedEventReceived; /**< true if the framework started event is received. */ - bool frameworkErrorEventReceived; /**< true if the framework error event is received. */ long frameworkReadyOrErrorConditionSvcId; /**< service id of the condition service which is set when the framework is ready or started up with an error */ - long checkFrameworkScheduledEventId; /**< event id of the scheduled event to check if the framework is ready. */ + long checkComponentsScheduledEventId; /**< event id of the scheduled event to check if the framework is ready. */ + long componentsReadyConditionSvcId; /**< service id of the condition service which is set when all components are + ready. */ } celix_framework_bundle_activator_t; -static celix_status_t celix_frameworkBundle_frameworkEvent(void* handle, framework_event_t* event) { - framework_listener_t* listener = handle; - celix_framework_bundle_activator_t* act = listener->handle; - if (event->type == OSGI_FRAMEWORK_EVENT_STARTED || event->type == OSGI_FRAMEWORK_EVENT_ERROR) { - celixThreadMutex_lock(&act->mutex); - if (event->type == OSGI_FRAMEWORK_EVENT_STARTED) { - act->frameworkStartedEventReceived = true; - } else { - act->frameworkErrorEventReceived = true; - } - celix_bundleContext_wakeupScheduledEvent(act->ctx, act->checkFrameworkScheduledEventId); - celixThreadMutex_unlock(&act->mutex); - } - return CELIX_SUCCESS; -} - celix_status_t celix_frameworkBundle_create(celix_bundle_context_t* ctx, void** userData) { *userData = NULL; celix_framework_bundle_activator_t* act = calloc(1, sizeof(*act)); @@ -75,11 +60,10 @@ celix_status_t celix_frameworkBundle_create(celix_bundle_context_t* ctx, void** act->ctx = ctx; act->trueConditionSvcId = -1L; act->listener.handle = act; - act->listener.frameworkEvent = celix_frameworkBundle_frameworkEvent; - act->frameworkStartedEventReceived = false; - act->frameworkErrorEventReceived = false; + act->listener.frameworkEvent = celix_frameworkBundle_handleFrameworkEvent; act->frameworkReadyOrErrorConditionSvcId = -1L; - act->checkFrameworkScheduledEventId = -1L; + act->checkComponentsScheduledEventId = -1L; + act->componentsReadyConditionSvcId = -1L; act->conditionInstance.handle = act; *userData = act; @@ -100,48 +84,79 @@ static void celix_frameworkBundle_registerTrueCondition(celix_framework_bundle_a } } -void celix_frameworkBundle_readyCheck(void* data) { - celix_framework_bundle_activator_t* act = data; - celixThreadMutex_lock(&act->mutex); - - bool ready = act->frameworkStartedEventReceived && - celix_framework_isEventQueueEmpty(celix_bundleContext_getFramework(act->ctx)); - bool error = act->frameworkErrorEventReceived; +celix_status_t celix_frameworkBundle_handleFrameworkEvent(void* handle, framework_event_t* event) { + framework_listener_t* listener = handle; + celix_framework_bundle_activator_t* act = listener->handle; + if (event->type == OSGI_FRAMEWORK_EVENT_STARTED || event->type == OSGI_FRAMEWORK_EVENT_ERROR) { + celixThreadMutex_lock(&act->mutex); - if (ready || error) { celix_service_registration_options_t opts = CELIX_EMPTY_SERVICE_REGISTRATION_OPTIONS; opts.serviceName = CELIX_CONDITION_SERVICE_NAME; opts.serviceVersion = CELIX_CONDITION_SERVICE_VERSION; opts.svc = &act->conditionInstance; opts.properties = celix_properties_create(); if (opts.properties) { - if (ready) { + if (event->type == OSGI_FRAMEWORK_EVENT_STARTED) { celix_properties_set(opts.properties, CELIX_CONDITION_ID, CELIX_CONDITION_ID_FRAMEWORK_READY); + celix_bundleContext_log( + act->ctx, + CELIX_LOG_LEVEL_DEBUG, + "Framework started event received -> registering framework.ready condition service"); } else /*error*/ { celix_properties_set(opts.properties, CELIX_CONDITION_ID, CELIX_CONDITION_ID_FRAMEWORK_ERROR); - celix_bundleContext_log(act->ctx, - CELIX_LOG_LEVEL_INFO, - "Framework error received -> no framework.ready condition service will be registered"); + celix_bundleContext_log( + act->ctx, + CELIX_LOG_LEVEL_INFO, + "Framework error event received -> registering framework.error condition service"); } - celix_bundleContext_log(act->ctx, CELIX_LOG_LEVEL_INFO, "Registering framework.ready/framework.error condition service %s", celix_properties_get(opts.properties, CELIX_CONDITION_ID, "!ERROR!")); - act->frameworkReadyOrErrorConditionSvcId = celix_bundleContext_registerServiceWithOptionsAsync(act->ctx, &opts); + act->frameworkReadyOrErrorConditionSvcId = + celix_bundleContext_registerServiceWithOptionsAsync(act->ctx, &opts); + celix_bundleContext_wakeupScheduledEvent(act->ctx, act->checkComponentsScheduledEventId); + } else { + celix_bundleContext_log(act->ctx, + CELIX_LOG_LEVEL_ERROR, + "Cannot create properties for framework.ready/framework.error condition service"); + } + celixThreadMutex_unlock(&act->mutex); + } + return CELIX_SUCCESS; +} + +void celix_frameworkBundle_componentsCheck(void* data) { + celix_framework_bundle_activator_t* act = data; + celix_dependency_manager_t* mng = celix_bundleContext_getDependencyManager(act->ctx); + + celixThreadMutex_lock(&act->mutex); + bool allComponentsActive = celix_dependencyManager_allComponentsActive(mng); + bool ready = allComponentsActive && act->frameworkReadyOrErrorConditionSvcId >= 0; + if (ready) { + celix_service_registration_options_t opts = CELIX_EMPTY_SERVICE_REGISTRATION_OPTIONS; + opts.serviceName = CELIX_CONDITION_SERVICE_NAME; + opts.serviceVersion = CELIX_CONDITION_SERVICE_VERSION; + opts.svc = &act->conditionInstance; + opts.properties = celix_properties_create(); + if (opts.properties) { + celix_properties_set(opts.properties, CELIX_CONDITION_ID, CELIX_CONDITION_ID_COMPONENTS_READY); + celix_bundleContext_log(act->ctx, CELIX_LOG_LEVEL_DEBUG, "Registering components.ready condition service"); + act->componentsReadyConditionSvcId = celix_bundleContext_registerServiceWithOptionsAsync(act->ctx, &opts); } else { celix_bundleContext_log( - act->ctx, CELIX_LOG_LEVEL_ERROR, "Cannot create properties for framework.ready/framework.error condition service"); + act->ctx, CELIX_LOG_LEVEL_ERROR, "Cannot create properties for components.ready condition service"); } - celix_bundleContext_removeScheduledEventAsync(act->ctx, act->checkFrameworkScheduledEventId); - act->checkFrameworkScheduledEventId = -1L; + celix_bundleContext_removeScheduledEventAsync(act->ctx, act->checkComponentsScheduledEventId); + act->checkComponentsScheduledEventId = -1L; } celixThreadMutex_unlock(&act->mutex); } -static void celix_frameworkBundle_startReadyCheck(celix_framework_bundle_activator_t* act) { +static void celix_frameworkBundle_startComponentsCheck(celix_framework_bundle_activator_t* act) { celix_scheduled_event_options_t opts = CELIX_EMPTY_SCHEDULED_EVENT_OPTIONS; - opts.name = "celix_frameworkBundle_readyCheck"; - opts.callback = celix_frameworkBundle_readyCheck; + opts.name = "celix_frameworkBundle_componentsCheck"; + opts.callback = celix_frameworkBundle_componentsCheck; opts.callbackData = act; + opts.initialDelayInSeconds = 1; //note will be wakeup by framework event opts.intervalInSeconds = 0.001; - act->checkFrameworkScheduledEventId = celix_bundleContext_scheduleEvent(act->ctx, &opts); + act->checkComponentsScheduledEventId = celix_bundleContext_scheduleEvent(act->ctx, &opts); } celix_status_t celix_frameworkBundle_start(void* userData, celix_bundle_context_t* ctx) { @@ -160,7 +175,7 @@ celix_status_t celix_frameworkBundle_start(void* userData, celix_bundle_context_ fw_addFrameworkListener( celix_bundleContext_getFramework(ctx), celix_bundleContext_getBundle(ctx), &act->listener); - celix_frameworkBundle_startReadyCheck(act); + celix_frameworkBundle_startComponentsCheck(act); return CELIX_SUCCESS; } @@ -174,20 +189,24 @@ celix_status_t celix_frameworkBundle_stop(void* userData, celix_bundle_context_t // stop ready check celixThreadMutex_lock(&act->mutex); - long checkEventId = act->checkFrameworkScheduledEventId; - act->checkFrameworkScheduledEventId = -1L; + long checkEventId = act->checkComponentsScheduledEventId; + act->checkComponentsScheduledEventId = -1L; celixThreadMutex_unlock(&act->mutex); celix_bundleContext_removeScheduledEvent(ctx, checkEventId); // remove framework true condition service and - if present - framework.ready condition service, celixThreadMutex_lock(&act->mutex); - long trueSvcId = act->trueConditionSvcId; - long readyOrErrorSvcId = act->frameworkReadyOrErrorConditionSvcId; + long trueConditionSvcId = act->trueConditionSvcId; + long frameworkReadyOrErrorConditionSvcId = act->frameworkReadyOrErrorConditionSvcId; + long componentsReadyConditionSvcId = act->componentsReadyConditionSvcId; act->trueConditionSvcId = -1L; act->frameworkReadyOrErrorConditionSvcId = -1L; + act->componentsReadyConditionSvcId = -1L; celixThreadMutex_unlock(&act->mutex); - celix_bundleContext_unregisterService(ctx, readyOrErrorSvcId); - celix_bundleContext_unregisterService(ctx, trueSvcId); + + celix_bundleContext_unregisterService(ctx, componentsReadyConditionSvcId); + celix_bundleContext_unregisterService(ctx, frameworkReadyOrErrorConditionSvcId); + celix_bundleContext_unregisterService(ctx, trueConditionSvcId); // framework shutdown celix_framework_shutdownAsync(framework); diff --git a/libs/framework/src/celix_framework_bundle.h b/libs/framework/src/celix_framework_bundle.h index 5a43cd03..850cf968 100644 --- a/libs/framework/src/celix_framework_bundle.h +++ b/libs/framework/src/celix_framework_bundle.h @@ -22,6 +22,7 @@ #include "celix_errno.h" #include "celix_bundle_context.h" +#include "framework_event.h" #ifdef __cplusplus extern "C" { @@ -48,11 +49,16 @@ celix_status_t celix_frameworkBundle_stop(void* userData, celix_bundle_context_t celix_status_t celix_frameworkBundle_destroy(void* userData, celix_bundle_context_t* ctx); /** - * @brief The scheduled event callback for the framework bundle bundle ready check. + * @brief The framework bundle framework event handler. * @note Part of the header for testing purposes. - * @param[in] data The framework bundle bundle activator. */ -void celix_frameworkBundle_readyCheck(void* data); +celix_status_t celix_frameworkBundle_handleFrameworkEvent(void* handle, framework_event_t* event); + +/** + * @brief The scheduled event callback for the framework components.ready check. + * @note Part of the header for testing purposes. + */ +void celix_frameworkBundle_componentsCheck(void* data); #ifdef __cplusplus } diff --git a/libs/framework/src/framework.c b/libs/framework/src/framework.c index 745c4f51..a3353034 100644 --- a/libs/framework/src/framework.c +++ b/libs/framework/src/framework.c @@ -480,6 +480,8 @@ celix_status_t framework_start(celix_framework_t* framework) { celix_status_t startStatus = framework_autoStartConfiguredBundles(framework); celix_status_t installStatus = framework_autoInstallConfiguredBundles(framework); if (startStatus == CELIX_SUCCESS && installStatus == CELIX_SUCCESS) { + //fire started event if all bundles are started/installed and the event queue is empty + celix_framework_waitForEmptyEventQueue(framework); fw_fireFrameworkEvent(framework, OSGI_FRAMEWORK_EVENT_STARTED, CELIX_SUCCESS); } else { //note not returning a error, because the framework is started, but not all bundles are started/installed
