Apache Celix is a framework for C and C++14 to develop dynamic modular software applications using component and in-process service-oriented programming. Apache Celix is inspired by the OSGi specification adapted for C and C++.
Modularity in Celix is achieved by runtime installable bundles and dynamic - in process - services.
A Celix bundle is set of resources packed in a zip containing at least a manifest and almost always
some shared library containing the bundle functionality.
A Celix bundle can be created using the Celix CMake function add_celix_bundle
.
A Celix bundle is activated by executing the bundle entry points. For C++ bundles these bundle entry points are generated using the CELIX_GEN_CXX_BUNDLE_ACTIVATOR
macro.
Celix applications (Celix containers) can be created with the Celix CMake function add_celix_container
.
This function generates a C++ main function and is also used to configure default installed bundles.
This can be bundles provided by Celix, an other project or build by the project self.
//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)
add_celix_bundle(MyBundle
SOURCES src/MyBundleActivator.cc
)
add_celix_container(MyContainer
BUNDLES
Celix::ShellCxx
Celix::shell_tui
MyBundle
)
#bash
#goto project dir
cd cmake-build-debug #assuming clion cmake-build-debug dir
cd deploy/MyContainer
./MyContainer
#Celix shell
-> lb -a
#list of all installed bundles
-> help
#list of all available Celix shell commands
-> help celix::lb
#Help info about the shell command `celix::lb`
-> stop 3
#stops MyBundle
-> start 3
#starts MyBundle
-> stop 0
#stops the Celix framework
In the Celix framework, a service is a C++ object or C struct registered in the Celix framework service registry under one interface together with properties (meta information). Services can be discovered and used by bundles.
//include/ICalc.h
#pragma once
class ICalc {
public:
virtual ~ICalc() noexcept = default;
virtual int add(int a, int b) = 0;
};
//src/CalcProviderBundleActivator.cc
#include "ICalc.h"
#include "celix/BundleActivator.h"
class CalcProvider : public ICalc {
public:
~CalcProvider() noexcept override = default;
int add(int a, int b) override { return a + b; }
};
class CalcProviderBundleActivator {
public:
explicit CalcProviderBundleActivator(const std::shared_ptr<celix::BundleContext>& ctx) {
reg = ctx->registerService<ICalc>(std::make_shared<CalcProvider>())
.build();
}
private:
std::shared_ptr<celix::ServiceRegistration> reg{};
};
CELIX_GEN_CXX_BUNDLE_ACTIVATOR(CalcProviderBundleActivator)
#CMakeLists.txt
find_package(Celix REQUIRED)
add_celix_bundle(CalcProviderBundle
SOURCES src/CalcProviderBundleActivator.cc
)
target_include_directories(CalcProviderBundle PRIVATE include)
add_celix_container(CalcProviderContainer
BUNDLES
Celix::ShellCxx
Celix::shell_tui
CalcProviderBundle
)
#bash
#goto project dir
cd cmake-build-debug #assuming clion cmake-build-debug dir
cd deploy/CalcProviderBundle
./CalcProviderBundle
//include/ICalc.h
#pragma once
class ICalc {
public:
virtual ~ICalc() noexcept = default;
virtual int add(int a, int b) = 0;
};
//src/CalcUserBundleActivator.cc
#include <iostream>
#include "ICalc.h"
#include "celix/BundleActivator.h"
class CalcUserBundleActivator {
public:
explicit CalcUserBundleActivator(const std::shared_ptr<celix::BundleContext>& ctx) {
ctx->useService<ICalc>()
.addUseCallback([](ICalc& calc) {
std::cout << "result is " << calc.add(2, 3) << std::endl;
})
.setTimeout(std::chrono::seconds{1}) //wait for 1 second if a service is not directly found
.build();
}
};
CELIX_GEN_CXX_BUNDLE_ACTIVATOR(CalcUserBundleActivator)
#CMakeLists.txt
find_package(Celix REQUIRED)
add_celix_bundle(CalcUserBundle
SOURCES src/CalcUserBundleActivator.cc
)
target_include_directories(CalcUserBundle PRIVATE include)
add_celix_container(CalcUserContainer
BUNDLES
Celix::ShellCxx
Celix::shell_tui
CalcProviderBundle
CalcUserBundle
)
#bash
#goto project dir
cd cmake-build-debug #assuming clion cmake-build-debug dir
cd deploy/CalcUserContainer
./CalcUserContainer
//include/ICalc.h
#pragma once
class ICalc {
public:
virtual ~ICalc() noexcept = default;
virtual int add(int a, int b) = 0;
};
//src/CalcTrackerBundleActivator.cc
#include <mutex>
#include "ICalc.h"
#include "celix/BundleActivator.h"
class CalcTrackerBundleActivator {
public:
explicit CalcTrackerBundleActivator(const std::shared_ptr<celix::BundleContext>& ctx) {
tracker = ctx->trackServices<ICalc>()
.build();
for (auto& calc : tracker->getServices()) {
std::cout << "result is " << std::to_string(calc->add(2, 3)) << std::endl;
}
}
private:
std::shared_ptr<celix::ServiceTracker<ICalc>> tracker{};
};
CELIX_GEN_CXX_BUNDLE_ACTIVATOR(CalcTrackerBundleActivator)
find_package(Celix REQUIRED)
add_celix_bundle(CalcTrackerBundle
SOURCES src/CalcTrackerBundleActivator.cc
)
target_include_directories(CalcTrackerBundle PRIVATE include)
add_celix_container(CalcTrackerContainer
BUNDLES
Celix::ShellCxx
Celix::shell_tui
CalcProviderBundle
CalcTrackerBundle
)
#bash
#goto project dir
cd cmake-build-debug #assuming clion cmake-build-debug dir
cd deploy/CalcTrackerContainer
./CalcTrackerContainer
//src/FilterExampleBundleActivator.cc
#include <iostream>
#include "celix/BundleActivator.h"
#include "celix/IShellCommand.h"
class HelloWorldShellCommand : public celix::IShellCommand {
public:
void executeCommand(const std::string& /*commandLine*/, const std::vector<std::string>& /*commandArgs*/, FILE* outStream, FILE* /*errorStream*/) {
fprintf(outStream, "Hello World\n");
}
};
class FilterExampleBundleActivator {
public:
explicit FilterExampleBundleActivator(const std::shared_ptr<celix::BundleContext>& ctx) {
auto reg1 = ctx->registerService<celix::IShellCommand>(std::make_shared<HelloWorldShellCommand>())
.addProperty(celix::IShellCommand::COMMAND_NAME, "command1")
.build();
auto reg2 = ctx->registerService<celix::IShellCommand>(std::make_shared<HelloWorldShellCommand>())
.addProperty(celix::IShellCommand::COMMAND_NAME, "command2")
.build();
regs.push_back(reg1);
regs.push_back(reg2);
auto serviceIdsNoFilter = ctx->findServices<celix::IShellCommand>();
auto serviceIdsWithFilter = ctx->findServices<celix::IShellCommand>(std::string{"("} + celix::IShellCommand::COMMAND_NAME + "=" + "command1)");
std::cout << "Found " << std::to_string(serviceIdsNoFilter.size()) << " IShelLCommand services and found ";
std::cout << std::to_string(serviceIdsWithFilter.size()) << " IShellCommand service with name command1" << std::endl;
}
private:
std::vector<std::shared_ptr<celix::ServiceRegistration>> regs{};
};
CELIX_GEN_CXX_BUNDLE_ACTIVATOR(FilterExampleBundleActivator)
#CMakeLists.txt
find_package(Celix REQUIRED)
add_celix_bundle(FilterExampleBundle
SOURCES src/FilterExampleBundleActivator.cc
)
target_link_libraries(FilterExampleBundle PRIVATE Celix::shell_api) #adds celix/IShellCommand.h to the include path
add_celix_container(FilterExampleContainer
BUNDLES
Celix::ShellCxx
Celix::shell_tui
FilterExampleBundle
)
#bash
#goto project dir
cd cmake-build-debug #assuming clion cmake-build-debug dir
cd deploy/FilterExampleContainer
./FilterExampleContainer
#Celix shell
-> command1
-> command2
-> help