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) {
