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 116f92f00092734f94b1e559963cff775f92ef6f
Author: Pepijn Noltes <[email protected]>
AuthorDate: Fri Jun 30 20:26:09 2023 +0200

    Add FrameworkConditionsTestSuite and refactor fw conditions impl
---
 bundles/shell/shell/gtest/src/ShellTestSuite.cc    |   4 +
 libs/framework/gtest/CMakeLists.txt                |   7 +-
 .../BundleArchiveWithErrorInjectionTestSuite.cc    |   5 +-
 .../framework/gtest/src/CondTestBundleActivator.cc |  36 ++++++
 .../gtest/src/FrameworkConditionsTestSuite.cc      | 128 +++++++++++++++++++++
 libs/framework/include/celix/Constants.h           |   8 ++
 libs/framework/include/celix_condition.h           |  46 +++++---
 libs/framework/include/celix_constants.h           |   9 ++
 libs/framework/src/celix_framework_bundle.c        | 101 ++++++++++------
 libs/framework/src/framework.c                     |   4 +-
 10 files changed, 288 insertions(+), 60 deletions(-)

diff --git a/bundles/shell/shell/gtest/src/ShellTestSuite.cc 
b/bundles/shell/shell/gtest/src/ShellTestSuite.cc
index 4117f0ad..6f85f70d 100644
--- a/bundles/shell/shell/gtest/src/ShellTestSuite.cc
+++ b/bundles/shell/shell/gtest/src/ShellTestSuite.cc
@@ -25,6 +25,7 @@
 #include "celix_bundle_context.h"
 #include "celix_shell.h"
 #include "celix_framework_utils.h"
+#include "celix_constants.h"
 
 class ShellTestSuite : public ::testing::Test {
 public:
@@ -40,6 +41,9 @@ public:
         celix_properties_set(properties, "org.osgi.framework.storage", 
".cacheShellTestSuite");
         celix_properties_set(properties, 
"CELIX_LOGGING_DEFAULT_ACTIVE_LOG_LEVEL", "trace");
 
+        //to ensure "query 0" is still a test case for am empty result.
+        celix_properties_setBool(properties, 
CELIX_FRAMEWORK_CONDITION_SERVICES_ENABLED, false);
+
         auto* cFw = celix_frameworkFactory_createFramework(properties);
         auto cCtx = celix_framework_getFrameworkContext(cFw);
 
diff --git a/libs/framework/gtest/CMakeLists.txt 
b/libs/framework/gtest/CMakeLists.txt
index eb3231b4..695053ab 100644
--- a/libs/framework/gtest/CMakeLists.txt
+++ b/libs/framework/gtest/CMakeLists.txt
@@ -30,7 +30,8 @@ celix_bundle_headers(bundle_with_bad_export "Export-Library: 
$<SEMICOLON>")
 add_celix_bundle(simple_cxx_bundle SOURCES src/HelloWorldCxxActivator.cc 
VERSION 1.0.0)
 celix_bundle_libs(simple_cxx_bundle "PRIVATE" TRUE Celix::framework)
 add_celix_bundle(simple_cxx_dep_man_bundle SOURCES 
src/HelloWorldCxxActivatorWithDepMan.cc VERSION 1.0.0)
-add_celix_bundle(cmp_test_bundle SOURCES src/CmpTestBundleActivator.cc)
+add_celix_bundle(cmp_test_bundle SOURCES src/CmpTestBundleActivator.cc VERSION 
1.0.0)
+add_celix_bundle(cond_test_bundle SOURCES src/CondTestBundleActivator.cc 
VERSION 1.0.0)
 add_subdirectory(subdir) #simple_test_bundle4, simple_test_bundle5 and sublib
 
 add_celix_bundle(unresolvable_bundle SOURCES src/nop_activator.c VERSION 1.0.0)
@@ -58,6 +59,7 @@ set(CELIX_FRAMEWORK_TEST_SOURCES
     src/CelixLauncherTestSuite.cc
     src/CelixBundleCacheTestSuite.cc
     src/ScheduledEventTestSuite.cc
+    src/FrameworkConditionsTestSuite.cc
 )
 
 add_executable(test_framework ${CELIX_FRAMEWORK_TEST_SOURCES})
@@ -99,6 +101,7 @@ celix_get_bundle_filename(unresolvable_bundle 
UNRESOLVABLE_BUNDLE)
 celix_get_bundle_file(simple_cxx_bundle SIMPLE_CXX_BUNDLE_LOC)
 celix_get_bundle_file(simple_cxx_dep_man_bundle SIMPLE_CXX_DEP_MAN_BUNDLE_LOC)
 celix_get_bundle_file(cmp_test_bundle CMP_TEST_BUNDLE_LOC)
+celix_get_bundle_file(cond_test_bundle COND_TEST_BUNDLE_LOC)
 
 configure_file(config.properties.in config.properties @ONLY)
 configure_file(framework1.properties.in framework1.properties @ONLY)
@@ -118,6 +121,7 @@ target_compile_definitions(test_framework PRIVATE
         CMP_TEST_BUNDLE_LOC="${CMP_TEST_BUNDLE_LOC}"
         SIMPLE_CXX_DEP_MAN_BUNDLE_LOC="${SIMPLE_CXX_DEP_MAN_BUNDLE_LOC}"
         CMP_TEST_BUNDLE_LOC="${CMP_TEST_BUNDLE_LOC}"
+        COND_TEST_BUNDLE_LOC="${COND_TEST_BUNDLE_LOC}"
         
INSTALL_AND_START_BUNDLES_CONFIG_PROPERTIES_FILE="${CMAKE_CURRENT_BINARY_DIR}/install_and_start_bundles.properties"
 )
 add_test(NAME test_framework COMMAND test_framework)
@@ -220,6 +224,7 @@ if (ENABLE_TESTING_FOR_CXX14)
             CMP_TEST_BUNDLE_LOC="${CMP_TEST_BUNDLE_LOC}"
             
SIMPLE_CXX_DEP_MAN_BUNDLE_LOC="${SIMPLE_CXX_DEP_MAN_WITH_CXX11_BUNDLE_LOC}"
             CMP_TEST_BUNDLE_LOC="${CMP_TEST_BUNDLE_LOC}"
+            COND_TEST_BUNDLE_LOC="${COND_TEST_BUNDLE_LOC}"
             
INSTALL_AND_START_BUNDLES_CONFIG_PROPERTIES_FILE="${CMAKE_CURRENT_BINARY_DIR}/install_and_start_bundles.properties"
     )
     add_test(NAME test_framework_with_cxx14 COMMAND test_framework_with_cxx14)
diff --git 
a/libs/framework/gtest/src/BundleArchiveWithErrorInjectionTestSuite.cc 
b/libs/framework/gtest/src/BundleArchiveWithErrorInjectionTestSuite.cc
index d0363d02..a5a3e70d 100644
--- a/libs/framework/gtest/src/BundleArchiveWithErrorInjectionTestSuite.cc
+++ b/libs/framework/gtest/src/BundleArchiveWithErrorInjectionTestSuite.cc
@@ -40,8 +40,9 @@
 class BundleArchiveWithErrorInjectionTestSuite : public ::testing::Test {
   public:
     BundleArchiveWithErrorInjectionTestSuite() {
-        fw = celix::createFramework(
-            {{"CELIX_LOGGING_DEFAULT_ACTIVE_LOG_LEVEL", "trace"}, 
{CELIX_FRAMEWORK_CLEAN_CACHE_DIR_ON_CREATE, "true"}});
+        fw = 
celix::createFramework({{"CELIX_LOGGING_DEFAULT_ACTIVE_LOG_LEVEL", "trace"},
+                                     
{CELIX_FRAMEWORK_CLEAN_CACHE_DIR_ON_CREATE, "true"},
+                                     
{CELIX_FRAMEWORK_CONDITION_SERVICES_ENABLED, "false"}});
         ctx = fw->getFrameworkBundleContext();
     }
 
diff --git a/libs/framework/gtest/src/CondTestBundleActivator.cc 
b/libs/framework/gtest/src/CondTestBundleActivator.cc
new file mode 100644
index 00000000..2f6ac983
--- /dev/null
+++ b/libs/framework/gtest/src/CondTestBundleActivator.cc
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+#include "celix/BundleActivator.h"
+#include "celix_condition.h"
+
+class CndTestBundleActivator {
+  public:
+    explicit CndTestBundleActivator(const 
std::shared_ptr<celix::BundleContext>& ctx) {
+        registration = 
ctx->registerUnmanagedService<celix_condition>(&condition, 
CELIX_CONDITION_SERVICE_NAME)
+                .addProperty(CELIX_CONDITION_ID, "test")
+                .build();
+    }
+
+  private:
+    std::shared_ptr<celix::ServiceRegistration> registration{};
+    celix_condition condition{};
+};
+
+CELIX_GEN_CXX_BUNDLE_ACTIVATOR(CndTestBundleActivator)
\ No newline at end of file
diff --git a/libs/framework/gtest/src/FrameworkConditionsTestSuite.cc 
b/libs/framework/gtest/src/FrameworkConditionsTestSuite.cc
new file mode 100644
index 00000000..cb955f99
--- /dev/null
+++ b/libs/framework/gtest/src/FrameworkConditionsTestSuite.cc
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include "celix/Constants.h"
+#include "celix/FrameworkFactory.h"
+#include "celix_condition.h"
+
+class FrameworkConditionsTestSuite : public ::testing::Test {
+  public:
+    const int USE_SERVICE_TIMEOUT_IN_MS = 500;
+
+    FrameworkConditionsTestSuite() = default;
+};
+
+TEST_F(FrameworkConditionsTestSuite, ConditionTrueAndFrameworkReadyTest) {
+    // Given a Celix framework (with conditions enabled (default))
+    auto fw = celix::createFramework();
+    auto ctx = fw->getFrameworkBundleContext();
+
+    // Then the condition service with id "true" is available
+    auto filter = std::string{"("} + CELIX_CONDITION_ID + "=" + 
CELIX_CONDITION_ID_TRUE + ")";
+    auto count = 
ctx->useService<celix_condition>(CELIX_CONDITION_SERVICE_NAME).setFilter(filter).build();
+    EXPECT_EQ(1, count);
+
+    // And the condition service with id "framework.error" will not become 
available
+    filter = std::string{"("} + CELIX_CONDITION_ID + "=" + 
CELIX_CONDITION_ID_FRAMEWORK_ERROR + ")";
+    count = ctx->useService<celix_condition>(CELIX_CONDITION_SERVICE_NAME)
+                .setFilter(filter)
+                
.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
+    filter = std::string{"("} + CELIX_CONDITION_ID + "=" + 
CELIX_CONDITION_ID_FRAMEWORK_READY + ")";
+    count = ctx->useService<celix_condition>(CELIX_CONDITION_SERVICE_NAME)
+                .setFilter(filter)
+                
.setTimeout(std::chrono::milliseconds{USE_SERVICE_TIMEOUT_IN_MS})
+                .build();
+    EXPECT_EQ(1, count);
+}
+
+TEST_F(FrameworkConditionsTestSuite, ConditionTrueAndFrameworkErrorTest) {
+    // Given a Celix framework which is configured to start an invalid bundle
+    auto fw = celix::createFramework({{celix::AUTO_START_0, 
"non-existing-bundle.zip"}});
+    auto ctx = fw->getFrameworkBundleContext();
+
+    // Then the condition service with id "true" is available
+    auto filter = std::string{"("} + CELIX_CONDITION_ID + "=" + 
CELIX_CONDITION_ID_TRUE + ")";
+    auto count = 
ctx->useService<celix_condition>(CELIX_CONDITION_SERVICE_NAME).setFilter(filter).build();
+    EXPECT_EQ(1, count);
+
+    // And the condition service with id "framework.ready" does not become 
available (framework startup error)
+    filter = std::string{"("} + CELIX_CONDITION_ID + "=" + 
CELIX_CONDITION_ID_FRAMEWORK_READY + ")";
+    count = ctx->useService<celix_condition>(CELIX_CONDITION_SERVICE_NAME)
+                .setFilter(filter)
+                
.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
+    filter = std::string{"("} + CELIX_CONDITION_ID + "=" + 
CELIX_CONDITION_ID_FRAMEWORK_ERROR + ")";
+    count = ctx->useService<celix_condition>(CELIX_CONDITION_SERVICE_NAME)
+                .setFilter(filter)
+                
.setTimeout(std::chrono::milliseconds{USE_SERVICE_TIMEOUT_IN_MS})
+                .build();
+    EXPECT_EQ(1, count);
+}
+
+TEST_F(FrameworkConditionsTestSuite, FrameworkReadyRegisteredLastTest) {
+    // 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();
+
+    // Then the condition service with id "true" is available
+    auto filter = std::string{"("} + CELIX_CONDITION_ID + "=" + 
CELIX_CONDITION_ID_TRUE + ")";
+    auto count = 
ctx->useService<celix_condition>(CELIX_CONDITION_SERVICE_NAME).setFilter(filter).build();
+    EXPECT_EQ(1, count);
+
+    // And the condition service with id "framework.error" will not become 
available
+    filter = std::string{"("} + CELIX_CONDITION_ID + "=" + 
CELIX_CONDITION_ID_FRAMEWORK_ERROR + ")";
+    count = ctx->useService<celix_condition>(CELIX_CONDITION_SERVICE_NAME)
+                .setFilter(filter)
+                
.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
+    filter = std::string{"("} + CELIX_CONDITION_ID + "=" + 
CELIX_CONDITION_ID_FRAMEWORK_READY + ")";
+    count = ctx->useService<celix_condition>(CELIX_CONDITION_SERVICE_NAME)
+                .setFilter(filter)
+                
.setTimeout(std::chrono::milliseconds{USE_SERVICE_TIMEOUT_IN_MS})
+                .build();
+    EXPECT_EQ(1, count);
+
+    // And the condition service with id "test" is available
+    filter = std::string{"("} + CELIX_CONDITION_ID + "=test)";
+    count = ctx->useService<celix_condition>(CELIX_CONDITION_SERVICE_NAME)
+                .setFilter(filter)
+                
.setTimeout(std::chrono::milliseconds{USE_SERVICE_TIMEOUT_IN_MS})
+                .build();
+    EXPECT_EQ(1, count);
+
+    // 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)
+    filter = std::string{"("} + CELIX_CONDITION_ID + "=" + 
CELIX_CONDITION_ID_FRAMEWORK_READY + ")";
+    long readySvcId = ctx->findServiceWithName(CELIX_CONDITION_SERVICE_NAME, 
filter);
+    filter = std::string{"("} + CELIX_CONDITION_ID + "=test)";
+    long testySvcId = ctx->findServiceWithName(CELIX_CONDITION_SERVICE_NAME, 
filter);
+    EXPECT_GT(readySvcId, testySvcId);
+}
diff --git a/libs/framework/include/celix/Constants.h 
b/libs/framework/include/celix/Constants.h
index f2efbf8b..13046110 100644
--- a/libs/framework/include/celix/Constants.h
+++ b/libs/framework/include/celix/Constants.h
@@ -218,4 +218,12 @@ namespace celix {
      *
      */
     constexpr const char * const LOAD_BUNDLES_WITH_NODELETE = 
CELIX_LOAD_BUNDLES_WITH_NODELETE;
+
+    /**
+     * @brief Celix framework environment property (named 
"CELIX_FRAMEWORK_CONDITION_SERVICES_ENABLED") to configure 
+     * whether framework condition services are enabled or not.
+     * Default is true.
+     * Should be a boolean value.
+     */
+    constexpr const char* const FRAMEWORK_CONDITION_SERVICES_ENABLED = 
CELIX_FRAMEWORK_CONDITION_SERVICES_ENABLED;
 }
diff --git a/libs/framework/include/celix_condition.h 
b/libs/framework/include/celix_condition.h
index 8c442e8b..ee6642ce 100644
--- a/libs/framework/include/celix_condition.h
+++ b/libs/framework/include/celix_condition.h
@@ -40,23 +40,6 @@ extern "C" {
  */
 #define CELIX_CONDITION_ID "condition.id"
 
-/*!
- * @brief The unique identifier for the default True condition.
- * The default True condition is registered by the framework during framework 
initialization and therefore
- * can always be relied upon.
- */
-#define CELIX_CONDITION_ID_TRUE "true"
-
-/*!
- * @brief The unique identifier for the framework ready condition.
- * The framework ready condition is registered by the framework after all 
install configured bundles are installed,
- * all start configured bundles are started and after the Apache Celix Event 
Queue becomes empty.
- *
- * Note that after the framework ready condition is registered, the event 
queue can become non-empty again.
- * For example if a component depends on the "framework.ready" condition.
- */
-#define CELIX_CONDITION_ID_FRAMEWORK_READY "framework.ready"
-
 /**
  * @brief Celix condition service struct.
  *
@@ -77,6 +60,35 @@ typedef struct celix_condition {
                      sizeof(celix_condition_t) != 0  */
 } celix_condition_t;
 
+/*!
+ * @brief The unique identifier for the default framework true condition.
+ * The default True condition is registered by the framework during framework 
initialization and therefore
+ * can always be relied upon.
+ */
+#define CELIX_CONDITION_ID_TRUE "true"
+
+/*!
+ * @brief The unique identifier for the framework.ready condition.
+ * The framework ready condition is registered by the framework after all 
configured bundles are installed - and
+ * if configured - started and after the Apache Celix Event Queue becomes 
empty.
+ *
+ * Note that after the framework ready condition is registered, the event 
queue can become non-empty again.
+ * For example if a component depends on the "framework.ready" condition.
+ *
+ * Either a framework.ready or framework.error condition is registered.
+ */
+#define CELIX_CONDITION_ID_FRAMEWORK_READY "framework.ready"
+
+/*!
+ * @brief The unique identifier for the framework.error condition.
+
+ * The framework error condition is registered by the framework after all 
configured bundles are processed,
+ * but an error occurred while installing or starting a bundle.
+ *
+ * Either a framework.ready or framework.error condition is registered.
+ */
+#define CELIX_CONDITION_ID_FRAMEWORK_ERROR "framework.error"
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libs/framework/include/celix_constants.h 
b/libs/framework/include/celix_constants.h
index c2ad3a8c..76920c5b 100644
--- a/libs/framework/include/celix_constants.h
+++ b/libs/framework/include/celix_constants.h
@@ -337,6 +337,15 @@ extern "C" {
  */
 #define CELIX_DEFAULT_ALLOWED_PROCESSING_TIME_FOR_SCHEDULED_EVENT_IN_SECONDS 
2.0
 
+/**
+ * @brief Celix framework environment property (named 
"CELIX_FRAMEWORK_CONDITION_SERVICES_ENABLED") to configure 
+ * whether framework condition services are enabled or not.
+ * Default is true.
+ * Should be a boolean value.
+ */
+#define CELIX_FRAMEWORK_CONDITION_SERVICES_ENABLED 
"CELIX_FRAMEWORK_CONDITION_SERVICES_ENABLED"
+
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libs/framework/src/celix_framework_bundle.c 
b/libs/framework/src/celix_framework_bundle.c
index 282f5511..00e2aecb 100644
--- a/libs/framework/src/celix_framework_bundle.c
+++ b/libs/framework/src/celix_framework_bundle.c
@@ -20,33 +20,40 @@
 #include "celix_framework_bundle.h"
 
 #include "celix_condition.h"
+#include "celix_constants.h"
 #include "celix_threads.h"
 #include "framework_private.h"
 
-#define CELIX_FRAMEWORK_CONDITION_SERVICES_ENABLED                             
                                        \
-    "CELIX_FRAMEWORK_CONDITION_SERVICES_ENABLED"                // TODO move 
to constants.h
-#define CELIX_FRAMEWORK_CONDITION_SERVICES_ENABLED_DEFAULT true // TODO move 
to constants.h
+#define CELIX_FRAMEWORK_CONDITION_SERVICES_ENABLED_DEFAULT true
 
+/**
+ * @brief Celix framework bundle activator struct.
+ */
 typedef struct celix_framework_bundle_activator {
     celix_bundle_context_t* ctx;
     celix_condition_t conditionInstance; /**< condition instance which can be 
used for multiple condition services.*/
-    long trueConditionSvcId;             /**< service id of the condition 
service which is always true. */
     framework_listener_t listener;       /**< framework listener to check if 
the framework is ready. */
 
-    celix_thread_mutex_t mutex; /**< protects below. */
-    bool frameworkStartedEventReceived; /**< true if the framework started 
event is received. */
-    long frameworkReadyConditionSvcId;   /**< service id of the condition 
service which is set when the framework is
-                                            ready. */
+    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. */
-
 } 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) {
+    if (event->type == OSGI_FRAMEWORK_EVENT_STARTED || event->type == 
OSGI_FRAMEWORK_EVENT_ERROR) {
         celixThreadMutex_lock(&act->mutex);
-        act->frameworkStartedEventReceived = true;
+        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;
@@ -61,7 +68,8 @@ celix_status_t 
celix_frameworkBundle_create(celix_bundle_context_t* ctx, void**
         act->listener.handle = act;
         act->listener.frameworkEvent = celix_frameworkBundle_frameworkEvent;
         act->frameworkStartedEventReceived = false;
-        act->frameworkReadyConditionSvcId = -1L;
+        act->frameworkErrorEventReceived = false;
+        act->frameworkReadyOrErrorConditionSvcId = -1L;
         act->checkFrameworkScheduledEventId = -1L;
         act->conditionInstance.handle = act;
         celixThreadMutex_create(&act->mutex, NULL);
@@ -79,8 +87,6 @@ static void 
celix_frameworkBundle_registerTrueCondition(celix_framework_bundle_a
     if (opts.properties) {
         celix_properties_set(opts.properties, CELIX_CONDITION_ID, 
CELIX_CONDITION_ID_TRUE);
         act->trueConditionSvcId = 
celix_bundleContext_registerServiceWithOptionsAsync(act->ctx, &opts);
-        celix_bundleContext_log(
-            act->ctx, CELIX_LOG_LEVEL_INFO, "Registered true condition service 
with id %li", act->trueConditionSvcId);
     } else {
         celix_bundleContext_log(act->ctx, CELIX_LOG_LEVEL_ERROR, "Cannot 
create properties for true condition service");
     }
@@ -88,34 +94,48 @@ static void 
celix_frameworkBundle_registerTrueCondition(celix_framework_bundle_a
 
 static void celix_frameworkBundle_readyCheck(void* data) {
     celix_framework_bundle_activator_t* act = data;
-    celix_bundleContext_log(act->ctx, CELIX_LOG_LEVEL_INFO, 
"celix_frameworkBundle_readyCheck");
     celixThreadMutex_lock(&act->mutex);
+    
     bool ready = act->frameworkStartedEventReceived &&
                  
celix_framework_isEventQueueEmpty(celix_bundleContext_getFramework(act->ctx));
-    if (ready) {
+    bool error = act->frameworkErrorEventReceived;
+
+    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) {
-            celix_properties_set(opts.properties, CELIX_CONDITION_ID, 
CELIX_CONDITION_ID_FRAMEWORK_READY);
-            act->frameworkReadyConditionSvcId = 
celix_bundleContext_registerServiceWithOptionsAsync(act->ctx, &opts);
+            if (ready) {
+                celix_properties_set(opts.properties, CELIX_CONDITION_ID, 
CELIX_CONDITION_ID_FRAMEWORK_READY);
+            } 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, 
"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);
         } else {
             celix_bundleContext_log(
-                act->ctx, CELIX_LOG_LEVEL_ERROR, "Cannot create properties for 
framework.ready condition service");
+                act->ctx, CELIX_LOG_LEVEL_ERROR, "Cannot create properties for 
framework.ready/framework.error condition service");
         }
-    } else {
-        // not ready yet, schedule a new check
-        celix_scheduled_event_options_t opts = 
CELIX_EMPTY_SCHEDULED_EVENT_OPTIONS;
-        opts.callback = celix_frameworkBundle_readyCheck;
-        opts.callbackData = act;
-        opts.initialDelayInSeconds = 0.01; // TBD use a small delay or accept 
a lot of scheduled events during startup.
-        act->checkFrameworkScheduledEventId = 
celix_bundleContext_scheduleEvent(act->ctx, &opts);
+        celix_bundleContext_removeScheduledEventAsync(act->ctx, 
act->checkFrameworkScheduledEventId);
+        act->checkFrameworkScheduledEventId = -1L;
     }
     celixThreadMutex_unlock(&act->mutex);
 }
 
+static void 
celix_frameworkBundle_startReadyCheck(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.callbackData = act;
+    opts.intervalInSeconds = 0.001;
+    act->checkFrameworkScheduledEventId = 
celix_bundleContext_scheduleEvent(act->ctx, &opts);
+}
+
 celix_status_t celix_frameworkBundle_start(void* userData, 
celix_bundle_context_t* ctx) {
     celix_framework_bundle_activator_t* act = userData;
 
@@ -123,8 +143,9 @@ celix_status_t celix_frameworkBundle_start(void* userData, 
celix_bundle_context_
         ctx, CELIX_FRAMEWORK_CONDITION_SERVICES_ENABLED, 
CELIX_FRAMEWORK_CONDITION_SERVICES_ENABLED_DEFAULT);
     if (conditionsEnabled) {
         celix_frameworkBundle_registerTrueCondition(act);
-        fw_addFrameworkListener(celix_bundleContext_getFramework(ctx), 
celix_bundleContext_getBundle(ctx), &act->listener);
-        celix_frameworkBundle_readyCheck(act);
+        fw_addFrameworkListener(
+            celix_bundleContext_getFramework(ctx), 
celix_bundleContext_getBundle(ctx), &act->listener);
+        celix_frameworkBundle_startReadyCheck(act);
         return act->trueConditionSvcId >= 0 ? CELIX_SUCCESS : 
CELIX_BUNDLE_EXCEPTION;
     }
 
@@ -133,31 +154,35 @@ celix_status_t celix_frameworkBundle_start(void* 
userData, celix_bundle_context_
 
 celix_status_t celix_frameworkBundle_stop(void* userData, 
celix_bundle_context_t* ctx) {
     celix_framework_bundle_activator_t* act = userData;
+    celix_framework_t* framework = celix_bundleContext_getFramework(ctx);
     celix_status_t status = CELIX_SUCCESS;
 
     // remove framework listener
-    fw_removeFrameworkListener(celix_bundleContext_getFramework(ctx), 
celix_bundleContext_getBundle(ctx), &act->listener);
+    fw_removeFrameworkListener(framework, celix_bundleContext_getBundle(ctx), 
&act->listener);
 
-    // stop ready check and remove framework ready condition service if present
+    // stop ready check
     celixThreadMutex_lock(&act->mutex);
-    celix_bundleContext_tryRemoveScheduledEventAsync(ctx, 
act->checkFrameworkScheduledEventId);
+    long checkEventId = act->checkFrameworkScheduledEventId;
     act->checkFrameworkScheduledEventId = -1L;
-    celix_bundleContext_unregisterServiceAsync(ctx, 
act->frameworkReadyConditionSvcId, NULL, NULL);
-    act->frameworkReadyConditionSvcId = -1L;
     celixThreadMutex_unlock(&act->mutex);
+    celix_bundleContext_removeScheduledEvent(ctx, checkEventId);
 
-    // remove true condition service
-    celix_bundleContext_unregisterServiceAsync(ctx, act->trueConditionSvcId, 
NULL, NULL);
+    // remove framework true condition service and - if present - 
framework.ready condition service,
+    celixThreadMutex_lock(&act->mutex);
+    long trueSvcId = act->trueConditionSvcId;
+    long readyOrErrorSvcId = act->frameworkReadyOrErrorConditionSvcId;
     act->trueConditionSvcId = -1L;
+    act->frameworkReadyOrErrorConditionSvcId = -1L;
+    celixThreadMutex_unlock(&act->mutex);
+    celix_bundleContext_unregisterService(ctx, readyOrErrorSvcId);
+    celix_bundleContext_unregisterService(ctx, trueSvcId);
 
     // framework shutdown
-    celix_framework_t* framework = celix_bundleContext_getFramework(ctx);
     celix_framework_shutdownAsync(framework);
     return status;
 }
 
-celix_status_t celix_frameworkBundle_destroy(void* userData,
-                                                      celix_bundle_context_t* 
ctx __attribute__((unused))) {
+celix_status_t celix_frameworkBundle_destroy(void* userData, 
celix_bundle_context_t* ctx __attribute__((unused))) {
     celix_framework_bundle_activator_t* act = userData;
     if (act) {
         celixThreadMutex_destroy(&act->mutex);
diff --git a/libs/framework/src/framework.c b/libs/framework/src/framework.c
index d9015c2d..d84b2a22 100644
--- a/libs/framework/src/framework.c
+++ b/libs/framework/src/framework.c
@@ -489,11 +489,11 @@ 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) {
-        fw_fireFrameworkEvent(framework, OSGI_FRAMEWORK_EVENT_STARTED, 
framework->bundleId); 
+        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
         fw_logCode(framework->logger, CELIX_LOG_LEVEL_ERROR, status, "Could 
not auto start or install all configured bundles");
-        fw_fireFrameworkEvent(framework, OSGI_FRAMEWORK_EVENT_ERROR, 
framework->bundleId);
+        fw_fireFrameworkEvent(framework, OSGI_FRAMEWORK_EVENT_ERROR, 
CELIX_BUNDLE_EXCEPTION);
     }
 
     if (status == CELIX_SUCCESS) {

Reply via email to