Fork me on GitHub
<< back to documentation

Apache Celix Bundles

An Apache Celix Bundle contains a collection of shared libraries, configuration files and optional an activation entry combined in a zip file. Bundles can be dynamically installed and started in an Apache Celix framework.

The anatomy of a Celix Bundle

Technically an Apache Celix Bundle is a zip file with the following content:

  • META-INF/MANIFEST.MF: The required bundle manifest, containing information about the bundle (name, activator library etc)
  • Bundle shared libraries (so/dylib files): Optionally a bundle has 1 or more shared libraries. The bundle manifest configures which libraries will be loaded (private libs) and which - if any - library is used when activating the bundle.
  • Bundle resource files: A bundle can also contain additional resource files. This could be configuration files, html files, etc.
    It is also possible to have bundles which no shared library, but only resource files. Note that bundles can access other bundles resources files.

If a jar command is available the Celix CMake commands will use that (instead of the zip command) to create bundle zip files so that the MANIFEST.MF is always the first entry in the zip file.

#unpacking celix_shell_wui.zip bundle file from a cmake build `cmake-build-debug`.
#The celix_shell_wui.zip file is the Celix Shell Web UI bundle. Which provides a web ui interface to the Celix 
#interactive shell; It contains a manifest file, shared libraries, and additional web resources 
#which can be picked up by the `Celix::http_admin` bundle. 
% unzip cmake-build-debug/bundles/shell/shell_wui/celix_shell_wui.zip -d unpacked_bundle_dir 
% find unpacked_bundle_dir 
unpacked_bundle_dir
unpacked_bundle_dir/resources
unpacked_bundle_dir/resources/index.html
unpacked_bundle_dir/resources/ansi_up.js
unpacked_bundle_dir/resources/script.js
unpacked_bundle_dir/META-INF
unpacked_bundle_dir/META-INF/MANIFEST.MF
unpacked_bundle_dir/libcivetweb_shared.so #or dylib for OSX
unpacked_bundle_dir/libshell_wui.1.so #or dylib for OSX    

Bundle lifecycle

An Apache Celix Bundle has its own lifecycle with the following states:

  • Installed - The bundle has been installed into the Celix framework, but it is not yet resolved. For Celix this currently means that not all bundle libraries can or have been loaded.
  • Resolved - The bundle is installed and its requirements have been met. For Celix this currently means that the bundle libraries have been loaded.
  • Starting - Starting is a temporary state while the bundle activator’s create and start callbacks are being executed.
  • Active - The bundle is active.
  • Stopping - Stopping is a temporary state while the bundle activator stop and destroy callbacks are being executed.
  • Uninstalled - The bundle has been removed from the Celix framework.

State diagram of the bundle lifecycle

Bundle activation

Bundles can be installed and started dynamically. When a bundle is started it will be activated by looking up the bundle activator entry points (using dlsym). The entry points signatures are:

  • celix_status_t celix_bundleActivator_create(celix_bundle_context_t *ctx, void **userData): Called to create the bundle activator.
  • celix_status_t celix_bundleActivator_start(void *userData, celix_bundle_context_t *ctx): Called to start the bundle.
  • celix_status_t celix_bundleActivator_stop(void *userData, celix_bundle_context_t *ctx): Called to stop the bundle.
  • celix_status_t celix_bundleActivator_destroy(void *userData, celix_bundle_context_t* ctx): Called to destroy (free mem) the bundle activator.

The most convenient way to create a bundle activator in C is to use the macro CELIX_GEN_BUNDLE_ACTIVATOR defined in celix_bundle_activator.h. This macro requires two functions (start,stop), these function can be static and use a typed bundle activator struct instead of void*.

For C++, the macro CELIX_GEN_CXX_BUNDLE_ACTIVATOR defined in celix/BundleActivator.h must be used to create a bundle activator. For C++ a RAII approach is used for bundle activation. This means that a C++ bundle is started by creating a bundle activator object and stopped by letting the bundle activator object go out of scope.

Bundle and Bundle Context

A bundle can interact with the Apache Celix framework using a bundle execution context or bundle context in short. The bundle context provides functions/methods to:

  • Register and un-register services.
  • Install, start, stop or uninstall bundles.
  • Track for service being added or removed.
  • Track for bundles being installed, started, stopped or uninstalled.
  • Track for service tracker being started or stopped
  • Find service ids for a given filter.
  • Use services directly (without manually creating a service tracker).
  • Use bundles directly (without manually creating a bundle tracker).
  • Wait for events in the Celix event thread.
  • Retrieve framework property values.
  • Retrieve the bundle object associated with the bundle context.

Hello World Bundle Example

The hello world bundle example is a simple example which print a “Hello world” and “Goodbye world” line when starting / stopping the bundle.

Knowledge about C, C++ and CMake is expected to understand the examples.

The C and C++ examples exists of a single source file which contains the bundle activator and some Apache Celix CMake commands to create a bundle and a container.

Both containers example uses 3 bundles: the Apache Celix Shell bundle, the Apache Celix Shell Textual UI bundle and the Hello World bundle. The Apache Celix Shell bundle provides a set of interactive shell commands and the Apache Celix Shell Textual UI bundle can be used to run these command from a console terminal.

When the C or C++ Hello World bundle example container is started, the following commands can be used to dynamically stop and start the Hello World bundle.

stop 3 #Stopping the Hello World bundle. Note that the Hello World is the third bundle, so it will get a bundle id 3.
start 3 #Starting the Hello World bundle again.
uninstall 3 #Stoping and uninstalling the Hello World bundle.
stop 0 #stop the Apache Celix framework

The see what other Apache Celix shell commands are available run the celix::help command:

help #note can also be triggered with celix::help (the fully qualified command name). 
help celix::start 
help celix::lb
stop 0 #stop the Apache Celix framework

C Example

//src/my_bundle_activator.c
#include <celix_api.h>

typedef struct my_bundle_activator_data {
    /*the hello world bundle activator struct is empty*/
} my_bundle_activator_data_t;

static celix_status_t my_bundle_start(my_bundle_activator_data_t *data, celix_bundle_context_t *ctx) {
    printf("Hello world from bundle with id %li\n", celix_bundleContext_getBundleId(ctx));
    return CELIX_SUCCESS;
}

static celix_status_t my_bundle_stop(my_bundle_activator_data_t *data, celix_bundle_context_t *ctx) {
    printf("Goodbye world\n");
    return CELIX_SUCCESS;
}

CELIX_GEN_BUNDLE_ACTIVATOR(my_bundle_activator_data_t, my_bundle_start, my_bundle_stop)
#CMakeLists.txt
find_package(Celix REQUIRED)

#With `make all`, `make celix-bundles` this bundle will be created at:
#  ${CMAKE_CURRENT_BINARY_DIR}/my_bundle.zip.
add_celix_bundle(my_bundle
    VERSION 1.0.0 
    SOURCES src/my_bundle_activator.c
)

#With `make all`, `make celix-containers` or `make my_container` this Celix container executable will be created at:
# ${CMAKE_BINARY_DIR}/deploy/my_container/my_container
add_celix_container(my_container
    C
    BUNDLES
        Celix::shell
        Celix::shell_tui
        my_bundle
)

C++ Example

//src/MyBundleActivator.cc
#include <iostream>
#include "celix/BundleActivator.h"

class MyBundleActivator {
public:
    explicit MyBundleActivator(const std::shared_ptr<celix::BundleContext>& ctx) {
        std::cout << "Hello world from bundle with id " << ctx->getBundleId() << std::endl;
    }

    ~MyBundleActivator() noexcept {
        std::cout << "Goodbye world" << std::endl;
    }
};

CELIX_GEN_CXX_BUNDLE_ACTIVATOR(MyBundleActivator)
#CMakeLists.txt
find_package(Celix REQUIRED)

#With `make all`, `make celix-bundles` this bundle will be created at:
#  ${CMAKE_CURRENT_BINARY_DIR}/MyBundle.zip.
add_celix_bundle(MyBundle
    SOURCES src/MyBundleActivator.cc
)

#With `make all`, `make celix-containers` or `make MyContainer` this Celix container executable will be created at:
# ${CMAKE_BINARY_DIR}/deploy/my_container/MyContainer
add_celix_container(MyContainer
    CXX
    BUNDLES
        Celix::ShellCxx
        Celix::shell_tui
        MyBundle
)

Interaction between bundles

By design bundles cannot directly access the symbols of another bundle. Interaction between bundles must be done using Apache Celix services. This means that unless functionality is provided by means of an Apache Celix service, bundle functionality is private to the bundle. In Apache Celix symbols are kept private by loading bundle libraries locally (dlopen with RTLD_LOCAL).

Installing bundles

Apache Celix bundles can be installed on the system with the Apache Celix CMake command install_celix_bundle. Bundles will be installed as zip files in the package (default the CMAKE_PROJECT_NAME) share directory (e.g /use/share/celix/bundles).

It is also possible to use Apache Celix bundles as CMake imported targets, but this requires a more complex CMake installation setup.

Installing Celix CMake targets

The install_celix_targets can be used to generate a CMake file with the imported Apache Celix Bundle CMake targets and this is ideally coupled with a CMake config file so that the bundles are made available when CMake’s find_package is used.

Example:

#Project setup
project(ExamplePackage C CXX)
find_package(Celix REQUIRED)

#Create bundles
add_celix_bundle(ExampleBundleA ...)
add_celix_bundle(ExampleBundleB ...)

#Install bundle zips
install_celix_bundle(ExampleBundleA EXPORT MyExport)
install_celix_bundle(ExampleBundleB EXPORT MyExport)
#install exported Celix CMake targets
install_celix_targets(MyExport NAMESPACE ExamplePackage:: DESTINATION share/ExamplePackage/cmake FILE CelixTargets)

#Install Package CMake configuration
file(GENERATE OUTPUT ${CMAKE_BINARY_DIR}/ExamplePackageConfig.cmake CONTENT "
  # relative install dir from lib/CMake/ExamplePackage.
  get_filename_component(REL_INSTALL_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
  get_filename_component(REL_INSTALL_DIR "${REL_INSTALL_DIR}" PATH)
  get_filename_component(REL_INSTALL_DIR "${REL_INSTALL_DIR}" PATH)
  get_filename_component(REL_INSTALL_DIR "${REL_INSTALL_DIR}" PATH)
  include(${REL_INSTALL_DIR}/share/celix/cmake/CelixTargets.cmake)
")

install(FILES
  ${CMAKE_BINARY_DIR}/ExamplePackageConfig.cmake
  DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/ExamplePackage)

Downstream Usage Example:

project(UsageExample C CXX)
find_package(Celix REQUIRED)
find_package(ExamplePackage REQUIRED)
add_celix_container(test_container BUNDLES
  Celix::shell
  Celix::shell_tui
  ExamplePackage::ExampleBundleA
  ExamplePackage::ExampleBundleB
)

See Apache Celix CMake Commands for more detailed information.

The celix::lb shell command

To interactively see the installed bundles the celix::lb shell command (list bundles) can be used.

Examples of supported lb command lines are:

  • celix::lb - Show an overview of the installed bundles with their bundle id, bundle state, bundle name and bundle group.
  • lb - Same as celix::lb (as long as there is no colliding other lb commands).
  • lb -s - Same as celix::lb but instead of showing the bundle name the bundle symbolic name is printed.
  • lb -u - Same as celix::lb but instead of showing the bundle name the bundle update location is printed.