This is an automated email from the ASF dual-hosted git repository. pnoltes pushed a commit to branch feature/list_all_bundles in repository https://gitbox.apache.org/repos/asf/celix.git
commit 52d72cf73d53372f98cf5ef58652cb60c8070945 Author: Pepijn Noltes <[email protected]> AuthorDate: Tue Dec 21 20:17:19 2021 +0100 Reintroduces a way to install - but not start - bundles with a config/containers. Also introduces a celix_ api to query installed bundle ids. --- cmake/cmake_celix/ContainerPackaging.cmake | 29 ++++++-- documents/cmake_commands/README.md | 67 +++++++++++------- .../celix-examples/http_example/CMakeLists.txt | 6 +- libs/framework/gtest/CMakeLists.txt | 44 ++++++++---- .../gtest/install_and_start_bundles.properties.in | 28 ++++++++ .../gtest/src/CxxBundleContextTestSuite.cc | 8 ++- .../gtest/src/bundle_context_bundles_tests.cpp | 12 ++-- libs/framework/gtest/src/single_framework_test.cpp | 80 ++++++++++++++++++++++ libs/framework/include/celix/BundleContext.h | 35 +++++++--- libs/framework/include/celix/Constants.h | 12 ++++ libs/framework/include/celix_bundle_context.h | 12 +++- libs/framework/include/celix_constants.h | 13 ++++ libs/framework/include/celix_framework.h | 21 +++++- libs/framework/src/bundle_context.c | 14 ++-- libs/framework/src/framework.c | 50 ++++++++++++-- 15 files changed, 353 insertions(+), 78 deletions(-) diff --git a/cmake/cmake_celix/ContainerPackaging.cmake b/cmake/cmake_celix/ContainerPackaging.cmake index 60c2bd4..d034799 100644 --- a/cmake/cmake_celix/ContainerPackaging.cmake +++ b/cmake/cmake_celix/ContainerPackaging.cmake @@ -67,8 +67,9 @@ Optional Arguments: - GROUP: If configured the build location will be prefixed the GROUP. Default is empty. - NAME: The name of the executable. Default is <celix_container_name>. Only useful for generated/LAUNCHER_SRC Celix launchers. - DIR: The base build directory of the Celix container. Default is `<cmake_build_dir>/deploy`. -- BUNDLES: A list of bundles to configured for the Celix container to install and start. +- BUNDLES: A list of bundles for the Celix container to install and start. These bundle will be configured for run level 3. See 'celix_container_bundles' for more info. +- INSTALL_BUNDLES: A list of bundles for the Celix container to install (but not start). - PROPERTIES: A list of configuration properties, these can be used to configure the Celix framework and/or bundles. Normally this will be EMBEDED_PROPERTIES, but if the USE_CONFIG option is used this will be RUNTIME_PROPERTIES. See the framework library or bundles documentation about the available configuration options. @@ -86,6 +87,7 @@ add_celix_container(<celix_container_name> [NAME celix_container_name] [DIR dir] [BUNDLES <bundle1> <bundle2> ...] + [INSTALL_BUNDLES <bundle1> <bundle2> ...] [PROPERTIES "prop1=val1" "prop2=val2" ...] [EMBEDDED_PROPERTIES "prop1=val1" "prop2=val2" ...] [RUNTIME_PROPERTIES "prop1=val1" "prop2=val2" ...] @@ -104,6 +106,7 @@ add_celix_container(<celix_container_name> [NAME celix_container_name] [DIR dir] [BUNDLES <bundle1> <bundle2> ...] + [INSTALL_BUNDLES <bundle1> <bundle2> ...] [PROPERTIES "prop1=val1" "prop2=val2" ...] [EMBEDDED_PROPERTIES "prop1=val1" "prop2=val2" ...] [RUNTIME_PROPERTIES "prop1=val1" "prop2=val2" ...] @@ -122,6 +125,7 @@ add_celix_container(<celix_container_name> [NAME celix_container_name] [DIR dir] [BUNDLES <bundle1> <bundle2> ...] + [INSTALL_BUNDLES <bundle1> <bundle2> ...] [PROPERTIES "prop1=val1" "prop2=val2" ...] [EMBEDDED_PROPERTIES "prop1=val1" "prop2=val2" ...] [RUNTIME_PROPERTIES "prop1=val1" "prop2=val2" ...] @@ -134,7 +138,7 @@ function(add_celix_container) set(OPTIONS COPY C CXX USE_CONFIG) set(ONE_VAL_ARGS GROUP NAME LAUNCHER LAUNCHER_SRC DIR) - set(MULTI_VAL_ARGS BUNDLES PROPERTIES EMBEDDED_PROPERTIES RUNTIME_PROPERTIES) + set(MULTI_VAL_ARGS BUNDLES INSTALL_BUNDLES PROPERTIES EMBEDDED_PROPERTIES RUNTIME_PROPERTIES) cmake_parse_arguments(CONTAINER "${OPTIONS}" "${ONE_VAL_ARGS}" "${MULTI_VAL_ARGS}" ${ARGN}) ##### Check arguments ##### @@ -203,6 +207,7 @@ $<$<BOOL:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_3>>:CELIX $<$<BOOL:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_4>>:CELIX_AUTO_START_4=$<JOIN:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_4>, >\\n>\\ $<$<BOOL:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_5>>:CELIX_AUTO_START_5=$<JOIN:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_5>, >\\n>\\ $<$<BOOL:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_6>>:CELIX_AUTO_START_6=$<JOIN:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_6>, >\\n>\\ +$<$<BOOL:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_INSTALL>>:CELIX_AUTO_INSTALL=$<JOIN:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_INSTALL>, >\\n>\\ $<JOIN:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_EMBEDDED_PROPERTIES>,\\n\\ >\"; @@ -248,6 +253,7 @@ $<$<BOOL:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_3>>:CELIX $<$<BOOL:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_4>>:CELIX_AUTO_START_4=$<JOIN:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_4>, >> $<$<BOOL:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_5>>:CELIX_AUTO_START_5=$<JOIN:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_5>, >> $<$<BOOL:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_6>>:CELIX_AUTO_START_6=$<JOIN:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_6>, >> +$<$<BOOL:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_INSTALL>>:CELIX_AUTO_INSTALL=$<JOIN:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_INSTALL>, >> $<JOIN:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_RUNTIME_PROPERTIES>, >" ) @@ -313,6 +319,7 @@ $<JOIN:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_RUNTIME_PROPERTIES>, set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_BUNDLES_LEVEL_4" "") #bundles to deploy for the container for startup level 4 set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_BUNDLES_LEVEL_5" "") #bundles to deploy for the container for startup level 5 set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_BUNDLES_LEVEL_6" "") #bundles to deploy for the container for startup level 6 + set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_BUNDLES_INSTALL" "") #bundles to install for the container set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_COPY_BUNDLES" ${CONTAINER_COPY}) #copy bundles in bundle dir or link using abs paths. NOTE this cannot be changed after a add_deploy command #deploy specific @@ -325,6 +332,7 @@ $<JOIN:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_RUNTIME_PROPERTIES>, ##### celix_container_bundles(${CONTAINER_TARGET} LEVEL 3 ${CONTAINER_BUNDLES}) + celix_container_bundles(${CONTAINER_TARGET} INSTALL ${CONTAINER_INSTALL_BUNDLES}) if (CONTAINER_USE_CONFIG) celix_container_runtime_properties(${CONTAINER_TARGET} ${CONTAINER_PROPERTIES}) else () @@ -446,10 +454,13 @@ Within a run level the order of configured decides the start order; bundles adde Optional Arguments: - LEVEL: The run level for the added bundles. Default is 3. +- INSTALL: If this option is present, the bundles will only be installed instead of the default install and start. + The bundles will be installed after all bundle in LEVEL 0..6 are installed and started. ```CMake celix_container_bundles(<celix_container_target_name> [LEVEL (0..6)] + [INSTALL] bundle1 bundle2 ... @@ -462,7 +473,7 @@ function(celix_container_bundles) list(GET ARGN 0 CONTAINER_TARGET) list(REMOVE_AT ARGN 0) - set(OPTIONS ) + set(OPTIONS INSTALL) set(ONE_VAL_ARGS LEVEL) set(MULTI_VAL_ARGS ) cmake_parse_arguments(BUNDLES "${OPTIONS}" "${ONE_VAL_ARGS}" "${MULTI_VAL_ARGS}" ${ARGN}) @@ -472,7 +483,11 @@ function(celix_container_bundles) set(BUNDLES_LEVEL 3) endif () - get_target_property(BUNDLES ${CONTAINER_TARGET} "CONTAINER_BUNDLES_LEVEL_${BUNDLES_LEVEL}") + if (BUNDLES_INSTALL) + get_target_property(BUNDLES ${CONTAINER_TARGET} "CONTAINER_BUNDLES_INSTALL") + else () #bundle level 0,1,2,3,4,5 or 6 + get_target_property(BUNDLES ${CONTAINER_TARGET} "CONTAINER_BUNDLES_LEVEL_${BUNDLES_LEVEL}") + endif () get_target_property(COPY ${CONTAINER_TARGET} "CONTAINER_COPY_BUNDLES") get_target_property(DEPS ${CONTAINER_TARGET} "CONTAINER_TARGET_DEPS") @@ -521,7 +536,11 @@ function(celix_container_bundles) endif() endforeach() - set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_BUNDLES_LEVEL_${BUNDLES_LEVEL}" "${BUNDLES}") + if (BUNDLES_INSTALL) + set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_BUNDLES_INSTALL" "${BUNDLES}") + else () #bundle level 0,1,2,3,4,5 or 6 + set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_BUNDLES_LEVEL_${BUNDLES_LEVEL}" "${BUNDLES}") + endif () set_target_properties(${CONTAINER_TARGET} PROPERTIES "CONTAINER_TARGET_DEPS" "${DEPS}") if(COPY) diff --git a/documents/cmake_commands/README.md b/documents/cmake_commands/README.md index d6f79dd..21f4b8c 100644 --- a/documents/cmake_commands/README.md +++ b/documents/cmake_commands/README.md @@ -276,37 +276,47 @@ There are three variants of 'add_celix_container': - If no launcher is specified a custom Celix launcher will be generated. This launcher also contains the configured properties. - If a LAUNCHER_SRC is provided a Celix launcher will be build using the provided sources. Additional sources can be added with the CMake 'target_sources' command. -- If a LAUNCHER (absolute path to a executable of CMake `add_executable` target) is provided that will be used as Celix launcher. +- If a LAUNCHER (absolute path to a executable of CMake `add_executable` target) is provided that will be used as Celix launcher. Creating a Celix containers using 'add_celix_container' will lead to a CMake executable target (expect if a LAUNCHER is used). These targets can be used to run/debug Celix containers from a IDE (if the IDE supports CMake). Optional Arguments: -- COPY: With this option the used bundles are copied to the container build dir in the 'bundles' dir. - A additional result of this is that the configured references to the bundles are then relative instead of absolute. -- CXX: With this option the generated Celix launcher (if used) will be a C++ source instead of a C source. - A additional result of this is that Celix launcher is also linked against stdlibc++. +- COPY: With this option the used bundles are copied to the container build dir in the 'bundles' dir. + A additional result of this is that the configured references to the bundles are then relative instead of absolute. + Default is COPY +- NO_COPY: With this option the used bundles configured for the container with absolute paths. + Default is COPY +- CXX: With this option the generated Celix launcher (if used) will be a C++ source. (Default is CXX) + This ensures that the Celix launcher is linked against stdlibc++. + Default is CXX +- C: With this option the generated Celix launcher (if used) will be a C source. + Default is CXX - USE_CONFIG: With this option config properties are generated in a 'config.properties' instead of embedded in the Celix launcher. - GROUP: If configured the build location will be prefixed the GROUP. Default is empty. - NAME: The name of the executable. Default is <celix_container_name>. Only useful for generated/LAUNCHER_SRC Celix launchers. - DIR: The base build directory of the Celix container. Default is `<cmake_build_dir>/deploy`. -- BUNDLES: A list of bundles to configured for the Celix container to install and start. +- BUNDLES: A list of bundles for the Celix container to install and start. These bundle will be configured for run level 3. See 'celix_container_bundles' for more info. -- PROPERTIES: A list of configuration properties, these can be used to configure the Celix framework and/or bundles. - Normally this will be EMBEDED_PROPERTIES, but if the USE_CONFIG option is used this will be RUNTIME_PROPERTIES. +- INSTALL_BUNDLES: A list of bundles for the Celix container to install (but not start). +- PROPERTIES: A list of configuration properties, these can be used to configure the Celix framework and/or bundles. + Normally this will be EMBEDED_PROPERTIES, but if the USE_CONFIG option is used this will be RUNTIME_PROPERTIES. See the framework library or bundles documentation about the available configuration options. -- EMBEDDED_PROPERTIES: A list of configuration properties which will be used in the generated Celix launcher. +- EMBEDDED_PROPERTIES: A list of configuration properties which will be used in the generated Celix launcher. - RUNTIME_PROPERTIES: A list of configuration properties which will be used in the generated config.properties file. ```CMake add_celix_container(<celix_container_name> [COPY] + [NO_COPY] [CXX] + [C] [USE_CONFIG] [GROUP group_name] [NAME celix_container_name] [DIR dir] [BUNDLES <bundle1> <bundle2> ...] + [INSTALL_BUNDLES <bundle1> <bundle2> ...] [PROPERTIES "prop1=val1" "prop2=val2" ...] [EMBEDDED_PROPERTIES "prop1=val1" "prop2=val2" ...] [RUNTIME_PROPERTIES "prop1=val1" "prop2=val2" ...] @@ -317,12 +327,15 @@ add_celix_container(<celix_container_name> add_celix_container(<celix_container_name> LAUNCHER launcher [COPY] + [NO_COPY] [CXX] + [C] [USE_CONFIG] [GROUP group_name] [NAME celix_container_name] [DIR dir] [BUNDLES <bundle1> <bundle2> ...] + [INSTALL_BUNDLES <bundle1> <bundle2> ...] [PROPERTIES "prop1=val1" "prop2=val2" ...] [EMBEDDED_PROPERTIES "prop1=val1" "prop2=val2" ...] [RUNTIME_PROPERTIES "prop1=val1" "prop2=val2" ...] @@ -331,36 +344,42 @@ add_celix_container(<celix_container_name> ```CMake add_celix_container(<celix_container_name> - LAUNCHER_SRC launcher_src - [COPY] - [CXX] - [USE_CONFIG] - [GROUP group_name] - [NAME celix_container_name] - [DIR dir] - [BUNDLES <bundle1> <bundle2> ...] - [PROPERTIES "prop1=val1" "prop2=val2" ...] - [EMBEDDED_PROPERTIES "prop1=val1" "prop2=val2" ...] - [RUNTIME_PROPERTIES "prop1=val1" "prop2=val2" ...] -) + LAUNCHER_SRC launcher_src + [COPY] + [NO_COPY] + [CXX] + [C] + [USE_CONFIG] + [GROUP group_name] + [NAME celix_container_name] + [DIR dir] + [BUNDLES <bundle1> <bundle2> ...] + [INSTALL_BUNDLES <bundle1> <bundle2> ...] + [PROPERTIES "prop1=val1" "prop2=val2" ...] + [EMBEDDED_PROPERTIES "prop1=val1" "prop2=val2" ...] + [RUNTIME_PROPERTIES "prop1=val1" "prop2=val2" ...] + ) ``` ## celix_container_bundles Add the selected bundles to the Celix container. These bundles are (if configured) copied to the container build dir and are added to the configuration properties so that they are installed and started when the Celix container executed. -The Celix framework support 7 (0 - 6) run levels. Run levels can be used to control the start and stop order of bundles. -Bundles in run level 0 are started first and bundles in run level 6 are started last. +The Celix framework supports 7 (0 - 6) run levels. Run levels can be used to control the start and stop order of bundles. +Bundles in run level 0 are started first and bundles in run level 6 are started last. When stopping bundles in run level 6 are stopped first and bundles in run level 0 are stopped last. Within a run level the order of configured decides the start order; bundles added earlier are started first. Optional Arguments: - LEVEL: The run level for the added bundles. Default is 3. +- INSTALL: If this option is present, the bundles will only be installed instead of the default install and start. + The bundles will be installed after all bundle in LEVEL 0..6 are installed and started. ```CMake celix_container_bundles(<celix_container_target_name> - [LEVEL (0..5)] + [LEVEL (0..6)] + [INSTALL] bundle1 bundle2 ... diff --git a/examples/celix-examples/http_example/CMakeLists.txt b/examples/celix-examples/http_example/CMakeLists.txt index 03bfad6..a52b8c9 100644 --- a/examples/celix-examples/http_example/CMakeLists.txt +++ b/examples/celix-examples/http_example/CMakeLists.txt @@ -35,10 +35,10 @@ if (TARGET Celix::http_admin AND TARGET Celix::shell AND TARGET Celix::shell_wui BUNDLES Celix::http_admin Celix::shell - Celix::shell_wui + Celix::shell_wui #web ui (webpage to access Celix shell using static html + websocket) + Celix::shell_tui #text ui (uses console at access Celix shell) + #INSTALL_BUNDLES #Enabled if you want to manually start the http_example bundle using the shell: `start 5` http_example - PROPERTIES - #SHELL_USE_ANSI_COLORS=false ) endif () diff --git a/libs/framework/gtest/CMakeLists.txt b/libs/framework/gtest/CMakeLists.txt index 7c19404..d08796f 100644 --- a/libs/framework/gtest/CMakeLists.txt +++ b/libs/framework/gtest/CMakeLists.txt @@ -28,15 +28,15 @@ add_celix_bundle(simple_cxx_dep_man_bundle SOURCES src/HelloWorldCxxActivatorWit add_celix_bundle(cmp_test_bundle SOURCES src/CmpTestBundleActivator.cc) add_subdirectory(subdir) #simple_test_bundle4, simple_test_bundle5 and sublib -add_celix_bundle(unresolveable_bundle SOURCES src/nop_activator.c VERSION 1.0.0) +add_celix_bundle(unresolvable_bundle SOURCES src/nop_activator.c VERSION 1.0.0) if (CMAKE_BUILD_TYPE STREQUAL "Debug") set(POSTFIX ${CMAKE_DEBUG_POSTFIX}) endif() -target_link_libraries(unresolveable_bundle PRIVATE "-L${CMAKE_CURRENT_BINARY_DIR}/subdir -lsublib${POSTFIX}") +target_link_libraries(unresolvable_bundle PRIVATE "-L${CMAKE_CURRENT_BINARY_DIR}/subdir -lsublib${POSTFIX}") if(NOT APPLE) - set_target_properties(unresolveable_bundle PROPERTIES LINK_FLAGS -Wl,--no-as-needed) + set_target_properties(unresolvable_bundle PROPERTIES LINK_FLAGS -Wl,--no-as-needed) endif() -add_dependencies(unresolveable_bundle sublib) +add_dependencies(unresolvable_bundle sublib) add_executable(test_framework src/single_framework_test.cpp @@ -55,27 +55,41 @@ add_celix_bundle_dependencies(test_framework simple_test_bundle5 bundle_with_exception unresolveable_bundle simple_cxx) target_include_directories(test_framework PRIVATE ../src) +celix_get_bundle_file(simple_test_bundle1 SIMPLE_TEST_BUNDLE1) +celix_get_bundle_file(simple_test_bundle2 SIMPLE_TEST_BUNDLE2) +celix_get_bundle_file(simple_test_bundle3 SIMPLE_TEST_BUNDLE3) +celix_get_bundle_file(simple_test_bundle4 SIMPLE_TEST_BUNDLE4) +celix_get_bundle_file(simple_test_bundle5 SIMPLE_TEST_BUNDLE5) +celix_get_bundle_filename(simple_test_bundle4 SIMPLE_TEST_BUNDLE4_FILENAME) +celix_get_bundle_filename(simple_test_bundle5 SIMPLE_TEST_BUNDLE5_FILENAME) + +celix_get_bundle_filename(bundle_with_exception BUNDLE_WITH_EXCEPTION) +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) + +configure_file(config.properties.in config.properties @ONLY) +configure_file(framework1.properties.in framework1.properties @ONLY) +configure_file(framework2.properties.in framework2.properties @ONLY) +configure_file(install_and_start_bundles.properties.in install_and_start_bundles.properties @ONLY) + target_compile_definitions(test_framework PRIVATE - SIMPLE_TEST_BUNDLE1_LOCATION="$<TARGET_PROPERTY:simple_test_bundle1,BUNDLE_FILE>" - SIMPLE_TEST_BUNDLE2_LOCATION="$<TARGET_PROPERTY:simple_test_bundle2,BUNDLE_FILE>" - SIMPLE_TEST_BUNDLE3_LOCATION="$<TARGET_PROPERTY:simple_test_bundle3,BUNDLE_FILE>" - SIMPLE_TEST_BUNDLE4_LOCATION="$<TARGET_PROPERTY:simple_test_bundle4,BUNDLE_FILENAME>" - SIMPLE_TEST_BUNDLE5_LOCATION="$<TARGET_PROPERTY:simple_test_bundle5,BUNDLE_FILENAME>" - TEST_BUNDLE_WITH_EXCEPTION_LOCATION="$<TARGET_PROPERTY:bundle_with_exception,BUNDLE_FILE>" - TEST_BUNDLE_UNRESOLVEABLE_LOCATION="$<TARGET_PROPERTY:unresolveable_bundle,BUNDLE_FILE>" + SIMPLE_TEST_BUNDLE1_LOCATION="${SIMPLE_TEST_BUNDLE1}" + SIMPLE_TEST_BUNDLE2_LOCATION="${SIMPLE_TEST_BUNDLE2}" + SIMPLE_TEST_BUNDLE3_LOCATION="${SIMPLE_TEST_BUNDLE3}" + SIMPLE_TEST_BUNDLE4_LOCATION="${SIMPLE_TEST_BUNDLE4_FILENAME}" + SIMPLE_TEST_BUNDLE5_LOCATION="${SIMPLE_TEST_BUNDLE5_FILENAME}" + TEST_BUNDLE_WITH_EXCEPTION_LOCATION="${BUNDLE_WITH_EXCEPTION}" + TEST_BUNDLE_UNRESOLVABLE_LOCATION="${UNRESOLVABLE_BUNDLE}" SIMPLE_CXX_BUNDLE_LOC="${SIMPLE_CXX_BUNDLE_LOC}" 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}" + INSTALL_AND_START_BUNDLES_CONFIG_PROPERTIES_FILE="${CMAKE_CURRENT_BINARY_DIR}/install_and_start_bundles.properties" ) -configure_file(config.properties.in config.properties @ONLY) -configure_file(framework1.properties.in framework1.properties @ONLY) -configure_file(framework2.properties.in framework2.properties @ONLY) - add_test(NAME test_framework COMMAND test_framework) setup_target_for_coverage(test_framework SCAN_DIR ..) diff --git a/libs/framework/gtest/install_and_start_bundles.properties.in b/libs/framework/gtest/install_and_start_bundles.properties.in new file mode 100644 index 0000000..ad52f2c --- /dev/null +++ b/libs/framework/gtest/install_and_start_bundles.properties.in @@ -0,0 +1,28 @@ +# 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. + + +#3 bundles, where 2 are valid and 1 non existing +CELIX_AUTO_START_1=@SIMPLE_TEST_BUNDLE1@ @SIMPLE_TEST_BUNDLE2@ Invalid1.zip + +#3 bundles, 1 is valid, 1 non existing and 1 already started +CELIX_AUTO_START_3=@SIMPLE_TEST_BUNDLE2@ @SIMPLE_TEST_BUNDLE3@ Invalid2.zip + +#4 bundles, 2 valid, 1 non existing and 1 already started +CELIX_AUTO_INSTALL=@SIMPLE_TEST_BUNDLE3@ @SIMPLE_TEST_BUNDLE4@ @SIMPLE_TEST_BUNDLE5@ Invalid3.zip + + diff --git a/libs/framework/gtest/src/CxxBundleContextTestSuite.cc b/libs/framework/gtest/src/CxxBundleContextTestSuite.cc index f02114f..51446c5 100644 --- a/libs/framework/gtest/src/CxxBundleContextTestSuite.cc +++ b/libs/framework/gtest/src/CxxBundleContextTestSuite.cc @@ -392,12 +392,18 @@ TEST_F(CxxBundleContextTestSuite, OnRegisterAndUnregisterCallbacks) { TEST_F(CxxBundleContextTestSuite, InstallCxxBundle) { EXPECT_EQ(0, ctx->listBundleIds().size()); + EXPECT_EQ(0, ctx->listInstalledBundleIds().size()); std::string loc{SIMPLE_CXX_BUNDLE_LOC}; ASSERT_FALSE(loc.empty()); - long bndId = ctx->installBundle(loc); + long bndId = ctx->installBundle(loc, false); EXPECT_GE(bndId, 0); + EXPECT_EQ(0, ctx->listBundleIds().size()); + EXPECT_EQ(1, ctx->listInstalledBundleIds().size()); + + ctx->startBundle(bndId); EXPECT_EQ(1, ctx->listBundleIds().size()); + EXPECT_EQ(1, ctx->listInstalledBundleIds().size()); } TEST_F(CxxBundleContextTestSuite, LoggingUsingContext) { diff --git a/libs/framework/gtest/src/bundle_context_bundles_tests.cpp b/libs/framework/gtest/src/bundle_context_bundles_tests.cpp index 8891411..0d3fc4d 100644 --- a/libs/framework/gtest/src/bundle_context_bundles_tests.cpp +++ b/libs/framework/gtest/src/bundle_context_bundles_tests.cpp @@ -41,7 +41,7 @@ public: const char * const TEST_BND4_LOC = "" SIMPLE_TEST_BUNDLE4_LOCATION ""; const char * const TEST_BND5_LOC = "" SIMPLE_TEST_BUNDLE5_LOCATION ""; const char * const TEST_BND_WITH_EXCEPTION_LOC = "" TEST_BUNDLE_WITH_EXCEPTION_LOCATION ""; - const char * const TEST_BND_UNRESOLVEABLE_LOC = "" TEST_BUNDLE_UNRESOLVEABLE_LOCATION ""; + const char * const TEST_BND_UNRESOLVABLE_LOC = "" TEST_BUNDLE_UNRESOLVABLE_LOCATION ""; CelixBundleContextBundlesTests() { properties = properties_create(); @@ -175,7 +175,7 @@ TEST_F(CelixBundleContextBundlesTests, startBundleWithException) { } TEST_F(CelixBundleContextBundlesTests, startUnresolveableBundle) { - long bndId = celix_bundleContext_installBundle(ctx, TEST_BND_UNRESOLVEABLE_LOC, true); + long bndId = celix_bundleContext_installBundle(ctx, TEST_BND_UNRESOLVABLE_LOC, true); ASSERT_TRUE(bndId > 0); //bundle is installed, but not resolved bool called = celix_framework_useBundle(fw, false, bndId, nullptr, [](void *, const celix_bundle_t *bnd) { @@ -218,9 +218,13 @@ TEST_F(CelixBundleContextBundlesTests, StopStartTest) { ASSERT_FALSE(celix_bundleContext_isBundleInstalled(ctx, 600 /*non existing*/)); + celix_array_list_t *ids = celix_bundleContext_listInstalledBundles(ctx); + size_t size = celix_arrayList_size(ids); + ASSERT_EQ(3, size); + celix_arrayList_destroy(ids); - celix_array_list_t *ids = celix_bundleContext_listBundles(ctx); - size_t size = arrayList_size(ids); + ids = celix_bundleContext_listBundles(ctx); + size = celix_arrayList_size(ids); ASSERT_EQ(3, size); int count = 0; diff --git a/libs/framework/gtest/src/single_framework_test.cpp b/libs/framework/gtest/src/single_framework_test.cpp index 8765753..98e1dad 100644 --- a/libs/framework/gtest/src/single_framework_test.cpp +++ b/libs/framework/gtest/src/single_framework_test.cpp @@ -95,6 +95,52 @@ TEST_F(CelixFramework, testAsyncInstallStartStopAndUninstallBundle) { EXPECT_FALSE(celix_framework_isBundleInstalled(framework.get(), bndId)); } +TEST_F(CelixFramework, listBundles) { + auto list = celix_framework_listBundles(framework.get()); + EXPECT_EQ(0, celix_arrayList_size(list)); + celix_arrayList_destroy(list); + list = celix_framework_listInstalledBundles(framework.get()); + EXPECT_EQ(0, celix_arrayList_size(list)); + celix_arrayList_destroy(list); + + long bndId = celix_framework_installBundle(framework.get(), SIMPLE_TEST_BUNDLE1_LOCATION, false); + EXPECT_GT(bndId, 0); + + list = celix_framework_listBundles(framework.get()); + EXPECT_EQ(0, celix_arrayList_size(list)); //installed, but not started + celix_arrayList_destroy(list); + list = celix_framework_listInstalledBundles(framework.get()); + EXPECT_EQ(1, celix_arrayList_size(list)); + celix_arrayList_destroy(list); + + celix_framework_startBundle(framework.get(), bndId); + + list = celix_framework_listBundles(framework.get()); + EXPECT_EQ(1, celix_arrayList_size(list)); + celix_arrayList_destroy(list); + list = celix_framework_listInstalledBundles(framework.get()); + EXPECT_EQ(1, celix_arrayList_size(list)); + celix_arrayList_destroy(list); + + celix_framework_stopBundle(framework.get(), bndId); + + list = celix_framework_listBundles(framework.get()); + EXPECT_EQ(0, celix_arrayList_size(list)); + celix_arrayList_destroy(list); + list = celix_framework_listInstalledBundles(framework.get()); + EXPECT_EQ(1, celix_arrayList_size(list)); //stopped, but still installed + celix_arrayList_destroy(list); + + celix_framework_uninstallBundle(framework.get(), bndId); + + list = celix_framework_listBundles(framework.get()); + EXPECT_EQ(0, celix_arrayList_size(list)); + celix_arrayList_destroy(list); + list = celix_framework_listInstalledBundles(framework.get()); + EXPECT_EQ(0, celix_arrayList_size(list)); + celix_arrayList_destroy(list); +} + class FrameworkFactory : public ::testing::Test { public: FrameworkFactory() = default; @@ -192,3 +238,37 @@ TEST_F(FrameworkFactory, restartFramework) { framework_destroy(fw); } + +TEST_F(CelixFramework, testLaunchFrameworkWithConfig) { + /* Rule: When a Celix framework is started with a config for auto starting bundles and installing bundles, + * the specified bundle are installed and if needed started. + */ + + auto* config = celix_properties_load(INSTALL_AND_START_BUNDLES_CONFIG_PROPERTIES_FILE); + ASSERT_TRUE(config != nullptr); + + framework_t* fw = celix_frameworkFactory_createFramework(config); + ASSERT_TRUE(fw != nullptr); + + auto* startedBundleIds = celix_framework_listBundles(fw); + auto* installedBundleIds = celix_framework_listInstalledBundles(fw); + + /* + * 3 started: simple_test_bundle1, simple_test_bundle2 and simple_test_bundle3 + */ + EXPECT_EQ(celix_arrayList_size(startedBundleIds), 3); + + /* + * 3 started: simple_test_bundle1, simple_test_bundle2 and simple_test_bundle3 + * 2 installed: simple_test_bundle4 and simple_test_bundle5 + */ + EXPECT_EQ(celix_arrayList_size(installedBundleIds), 5); + + celix_arrayList_destroy(startedBundleIds); + celix_arrayList_destroy(installedBundleIds); + + framework_stop(fw); + framework_waitForStop(fw); + framework_destroy(fw); +} + diff --git a/libs/framework/include/celix/BundleContext.h b/libs/framework/include/celix/BundleContext.h index 66c79a0..ff5ee48 100644 --- a/libs/framework/include/celix/BundleContext.h +++ b/libs/framework/include/celix/BundleContext.h @@ -375,17 +375,22 @@ namespace celix { /** * @brief List the installed and started bundle ids. - * The bundle ids does not include the framework bundle (bundle id celix::FRAMEWORK_BUNDLE_ID). + * The bundle ids does not include the framework bundle (bundle id CELIX_FRAMEWORK_BUNDLE_ID). + * + * @return A vector with bundle ids. */ [[nodiscard]] std::vector<long> listBundleIds() const { - std::vector<long> result{}; - auto* ids = celix_bundleContext_listBundles(cCtx.get()); - result.reserve(celix_arrayList_size(ids)); - for (int i = 0 ; i < celix_arrayList_size(ids); ++i) { - result.push_back(celix_arrayList_getLong(ids, i)); - } - celix_arrayList_destroy(ids); - return result; + return listBundlesInternal(true); + } + + /** + * @brief List the installed bundle ids. + * The bundle ids does not include the framework bundle (bundle id CELIX_FRAMEWORK_BUNDLE_ID). + * + * @return A vector with bundle ids. + */ + [[nodiscard]] std::vector<long> listInstalledBundleIds() { + return listBundlesInternal(false); } /** @@ -600,6 +605,18 @@ namespace celix { } } private: + [[nodiscard]] std::vector<long> listBundlesInternal(bool activeOnly) const { + std::vector<long> result{}; + auto* ids = activeOnly ? + celix_bundleContext_listBundles(getCBundleContext()) : + celix_bundleContext_listInstalledBundles(getCBundleContext()); + result.reserve(celix_arrayList_size(ids)); + for (int i = 0; i < celix_arrayList_size(ids); ++i) { + result.push_back(celix_arrayList_getLong(ids, i)); + } + celix_arrayList_destroy(ids); + return result; + } const std::shared_ptr<celix_bundle_context_t> cCtx; const std::shared_ptr<celix::dm::DependencyManager> dm; diff --git a/libs/framework/include/celix/Constants.h b/libs/framework/include/celix/Constants.h index 50e7939..2354222 100644 --- a/libs/framework/include/celix/Constants.h +++ b/libs/framework/include/celix/Constants.h @@ -180,6 +180,18 @@ namespace celix { */ constexpr const char * const AUTO_START_6 = CELIX_AUTO_START_6; + /** + * @brief Celix framework environment property (named "CELIX_AUTO_INSTALL") which specified a (ordered) space + * separated set of bundles to install when the Celix framework is started. + * + * The Celix framework will first install and start bundles defined in the properties CELIX_AUTO_START_0 till + * CELIX_AUTO_START_6 and then install (ano not start!) the bundles listed in CELIX_AUTO_INSTALL. + * + * When the Celix framework stops the bundles are stopped in the reverse order. Started bundles in CELIX_AUTO_INSTALL + * are stopped first and of those bundles, the bundle mentioned last in a CELIX_AUTO_INSTALL set is stopped first. + * Then bundles defined in CELIX_AUTO_START_6 are stopped, followed by bundles defined in CELIX_AUTO_START_5, etc. + */ + constexpr const char * const AUTO_INSTALL = CELIX_AUTO_INSTALL; /** * @brief Celix framework environment property (named "CELIX_BUNDLES_PATH") which specified a `;` separated diff --git a/libs/framework/include/celix_bundle_context.h b/libs/framework/include/celix_bundle_context.h index 80416d3..b9f55ae 100644 --- a/libs/framework/include/celix_bundle_context.h +++ b/libs/framework/include/celix_bundle_context.h @@ -834,12 +834,22 @@ size_t celix_bundleContext_useServicesWithOptions( * @brief List the installed and started bundle ids. * The bundle ids does not include the framework bundle (bundle id CELIX_FRAMEWORK_BUNDLE_ID). * - * @param ctx The bundle context + * @param ctx The bundle context. * @return A array with bundle ids (long). The caller is responsible for destroying the array. */ celix_array_list_t* celix_bundleContext_listBundles(celix_bundle_context_t *ctx); /** + * @brief List the installed bundle ids. + * The bundle ids does not include the framework bundle (bundle id CELIX_FRAMEWORK_BUNDLE_ID). + * + * @param ctx The bundle context. + * @return A array with bundle ids (long). The caller is responsible for destroying the array. + */ +celix_array_list_t* celix_bundleContext_listInstalledBundles(celix_bundle_context_t *ctx); + + +/** * @brief Check whether a bundle is installed. * @param ctx The bundle context. * @param bndId The bundle id to check diff --git a/libs/framework/include/celix_constants.h b/libs/framework/include/celix_constants.h index 525187d..730f2b2 100644 --- a/libs/framework/include/celix_constants.h +++ b/libs/framework/include/celix_constants.h @@ -273,6 +273,19 @@ extern "C" { */ #define CELIX_AUTO_START_6 "CELIX_AUTO_START_6" +/** + * @brief Celix framework environment property (named "CELIX_AUTO_INSTALL") which specified a (ordered) space + * separated set of bundles to install when the Celix framework is started. + * + * The Celix framework will first install and start bundles defined in the properties CELIX_AUTO_START_0 till + * CELIX_AUTO_START_6 and then install (ano not start!) the bundles listed in CELIX_AUTO_INSTALL. + * + * When the Celix framework stops the bundles are stopped in the reverse order. Started bundles in CELIX_AUTO_INSTALL + * are stopped first and of those bundles, the bundle mentioned last in a CELIX_AUTO_INSTALL set is stopped first. + * Then bundles defined in CELIX_AUTO_START_6 are stopped, followed by bundles defined in CELIX_AUTO_START_5, etc. + */ +#define CELIX_AUTO_INSTALL "CELIX_AUTO_INSTALL" + #ifdef __cplusplus } #endif diff --git a/libs/framework/include/celix_framework.h b/libs/framework/include/celix_framework.h index 5758795..c819a71 100644 --- a/libs/framework/include/celix_framework.h +++ b/libs/framework/include/celix_framework.h @@ -24,6 +24,7 @@ #include "celix_types.h" #include "celix_properties.h" #include "celix_log_level.h" +#include "celix_array_list.h" #ifdef __cplusplus extern "C" { @@ -182,6 +183,24 @@ void celix_framework_stopBundleAsync(celix_framework_t *fw, long bndId); void celix_framework_startBundleAsync(celix_framework_t *fw, long bndId); /** + * @brief List the installed and started bundle ids. + * The bundle ids does not include the framework bundle (bundle id CELIX_FRAMEWORK_BUNDLE_ID). + * + * @param framework The Celix framework. + * @return A array with bundle ids (long). The caller is responsible for destroying the array. + */ +celix_array_list_t* celix_framework_listBundles(celix_framework_t* framework); + +/** + * @brief List the installed bundle ids. + * The bundle ids does not include the framework bundle (bundle id CELIX_FRAMEWORK_BUNDLE_ID). + * + * @param framework The Celix framework. + * @return A array with bundle ids (long). The caller is responsible for destroying the array. + */ +celix_array_list_t* celix_framework_listInstalledBundles(celix_framework_t* framework); + +/** * @brief Wait until the framework event queue is empty. * * The Celix framework has an event queue which (among others) handles bundle events. @@ -244,8 +263,6 @@ void celix_framework_waitForGenericEvent(celix_framework_t *fw, long eventId); */ void celix_framework_waitForStop(celix_framework_t *framework); - - #ifdef __cplusplus } #endif diff --git a/libs/framework/src/bundle_context.c b/libs/framework/src/bundle_context.c index 43b345b..08a96bf 100644 --- a/libs/framework/src/bundle_context.c +++ b/libs/framework/src/bundle_context.c @@ -1082,18 +1082,12 @@ long celix_bundleContext_installBundle(bundle_context_t *ctx, const char *bundle return celix_framework_installBundle(ctx->framework, bundleLoc, autoStart); } -static void bundleContext_listBundlesCallback(void *handle, const bundle_t *c_bnd) { - celix_array_list_t* ids = handle; - long id = celix_bundle_getId(c_bnd); - if (id > 0) { //note skipping framework bundle id (0) - celix_arrayList_addLong(ids, id); - } +celix_array_list_t* celix_bundleContext_listBundles(celix_bundle_context_t *ctx) { + return celix_framework_listBundles(ctx->framework); } -celix_array_list_t* celix_bundleContext_listBundles(celix_bundle_context_t *ctx) { - celix_array_list_t *result = celix_arrayList_create(); - celix_bundleContext_useBundles(ctx, result, bundleContext_listBundlesCallback); - return result; +celix_array_list_t* celix_bundleContext_listInstalledBundles(celix_bundle_context_t *ctx) { + return celix_framework_listInstalledBundles(ctx->framework); } bool celix_bundleContext_isBundleInstalled(celix_bundle_context_t *ctx, long bndId) { diff --git a/libs/framework/src/framework.c b/libs/framework/src/framework.c index 9b5caa1..c2d139c 100644 --- a/libs/framework/src/framework.c +++ b/libs/framework/src/framework.c @@ -168,6 +168,7 @@ static celix_status_t frameworkActivator_stop(void * userData, bundle_context_t static celix_status_t frameworkActivator_destroy(void * userData, bundle_context_t *context); static void framework_autoStartConfiguredBundles(celix_framework_t *fw); +static void framework_autoInstallConfiguredBundles(celix_framework_t *fw); static void framework_autoInstallConfiguredBundlesForList(celix_framework_t *fw, const char *autoStart, celix_array_list_t *installedBundles); static void framework_autoStartConfiguredBundlesForList(celix_framework_t* fw, const celix_array_list_t *installedBundles); static void celix_framework_addToEventQueue(celix_framework_t *fw, const celix_framework_event_t* event); @@ -514,6 +515,7 @@ celix_status_t framework_start(framework_pt framework) { } framework_autoStartConfiguredBundles(framework); + framework_autoInstallConfiguredBundles(framework); if (status == CELIX_SUCCESS) { fw_log(framework->logger, CELIX_LOG_LEVEL_INFO, "Celix framework started"); @@ -542,6 +544,14 @@ static void framework_autoStartConfiguredBundles(celix_framework_t* fw) { celix_arrayList_destroy(installedBundles); } +static void framework_autoInstallConfiguredBundles(celix_framework_t* fw) { + bundle_context_t *fwCtx = framework_getContext(fw); + const char* autoInstall = celix_bundleContext_getProperty(fwCtx, CELIX_AUTO_INSTALL, NULL); + if (autoInstall != NULL) { + framework_autoInstallConfiguredBundlesForList(fw, autoInstall, NULL); + } +} + static void framework_autoInstallConfiguredBundlesForList(celix_framework_t* fw, const char *autoStartIn, celix_array_list_t *installedBundles) { bundle_context_t *fwCtx = framework_getContext(fw); @@ -556,7 +566,9 @@ static void framework_autoInstallConfiguredBundlesForList(celix_framework_t* fw, bundle_t *bnd = NULL; celix_status_t rc = bundleContext_installBundle(fwCtx, location, &bnd); if (rc == CELIX_SUCCESS) { - celix_arrayList_add(installedBundles, bnd); + if (installedBundles != NULL) { + celix_arrayList_add(installedBundles, bnd); + } } else { printf("Could not install bundle '%s'\n", location); } @@ -572,9 +584,13 @@ static void framework_autoStartConfiguredBundlesForList(celix_framework_t* fw, c long bndId = -1; bundle_t *bnd = celix_arrayList_get(installedBundles, i); bundle_getBundleId(bnd, &bndId); - bool started = celix_framework_startBundle(fw, bndId); - if (!started) { - fw_log(fw->logger, CELIX_LOG_LEVEL_ERROR, "Could not start bundle %s (bnd id = %li)\n", bnd->symbolicName, bndId); + if (celix_bundle_getState(bnd) != OSGI_FRAMEWORK_BUNDLE_ACTIVE) { + bool started = celix_framework_startBundle(fw, bndId); + if (!started) { + fw_log(fw->logger, CELIX_LOG_LEVEL_ERROR, "Could not start bundle %s (bnd id = %li)\n", bnd->symbolicName, bndId); + } + } else { + fw_log(fw->logger, CELIX_LOG_LEVEL_TRACE, "Cannot start bundle %s (bnd id = %li), because it is already started\n", bnd->symbolicName, bndId); } } } @@ -2595,6 +2611,32 @@ celix_status_t celix_framework_startBundleEntry(celix_framework_t* framework, ce return status; } +static celix_array_list_t* celix_framework_listBundlesInternal(celix_framework_t* framework, bool activeOnly) { + celix_array_list_t* result = celix_arrayList_create(); + celixThreadMutex_lock(&framework->dispatcher.mutex); + for (int i = 0; i < celix_arrayList_size(framework->installedBundles.entries); ++i) { + celix_framework_bundle_entry_t* entry = celix_arrayList_get(framework->installedBundles.entries, i); + if (entry->bndId == CELIX_FRAMEWORK_BUNDLE_ID) { + continue; + } + if (!activeOnly) { + celix_arrayList_addLong(result, entry->bndId); + } else if (celix_bundle_getState(entry->bnd) == OSGI_FRAMEWORK_BUNDLE_ACTIVE) { + celix_arrayList_addLong(result, entry->bndId); + } + } + celixThreadMutex_unlock(&framework->dispatcher.mutex); + return result; +} + +celix_array_list_t* celix_framework_listBundles(celix_framework_t* framework) { + return celix_framework_listBundlesInternal(framework, true); +} + +celix_array_list_t* celix_framework_listInstalledBundles(celix_framework_t* framework) { + return celix_framework_listBundlesInternal(framework, false); +} + void celix_framework_waitForEmptyEventQueue(celix_framework_t *fw) { assert(!celix_framework_isCurrentThreadTheEventLoop(fw));
