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

Reply via email to