You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@celix.apache.org by pn...@apache.org on 2022/05/23 19:27:44 UTC
[celix] branch feature/update_component_and_pattern_documentation created (now 9875606d)
This is an automated email from the ASF dual-hosted git repository.
pnoltes pushed a change to branch feature/update_component_and_pattern_documentation
in repository https://gitbox.apache.org/repos/asf/celix.git
at 9875606d Merge branch 'feature/additional_component_states' into pnoltes/feature/update_component_and_pattern_documentation
This branch includes the following new commits:
new 965f7c6e Adds dm component document and examples.
new 9875606d Merge branch 'feature/additional_component_states' into pnoltes/feature/update_component_and_pattern_documentation
The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
[celix] 01/02: Adds dm component document and examples.
Posted by pn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
pnoltes pushed a commit to branch feature/update_component_and_pattern_documentation
in repository https://gitbox.apache.org/repos/asf/celix.git
commit 965f7c6e6f470b576cdcf47b64185ffb660ae2f5
Author: Pepijn Noltes <pe...@gmail.com>
AuthorDate: Sun May 22 22:17:11 2022 +0200
Adds dm component document and examples.
Also adds support for configuring a component implementation destroy function
so that C component bundle activator can operate with less boilerplate code.
---
documents/components.md | 693 +++++++++++++++++----
.../dm_example/phase1/src/phase1_activator.c | 10 +-
.../dm_example/phase2a/src/phase2a_activator.c | 10 +-
.../dm_example/phase2b/src/phase2b_activator.c | 6 +-
.../dm_example/phase3/src/phase3_activator.c | 16 +-
.../readme_c_examples/CMakeLists.txt | 20 +
.../component_with_provided_service_activator.c | 90 +++
.../component_with_service_dependency_activator.c | 123 ++++
.../src/simple_component_activator.c | 92 +++
.../readme_cxx_examples/CMakeLists.txt | 22 +-
.../src/ComponentWithProvidedServiceActivator.cc | 69 ++
.../src/ComponentWithServiceDependencyActivator.cc | 75 +++
.../src/SimpleComponentActivator.cc | 57 ++
.../gtest/src/DependencyManagerTestSuite.cc | 37 ++
libs/framework/include/celix_bundle_activator.h | 35 +-
libs/framework/include/celix_dependency_manager.h | 12 +-
libs/framework/include/celix_dm_component.h | 55 +-
libs/framework/src/dm_component_impl.c | 24 +-
18 files changed, 1256 insertions(+), 190 deletions(-)
diff --git a/documents/components.md b/documents/components.md
index f0dcb5dc..5f8c96fb 100644
--- a/documents/components.md
+++ b/documents/components.md
@@ -19,203 +19,626 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
-# Apache Celix Components
-
TODO refactor this documentation file
+TODO also describes when cmp is suspended: there is a svc dep with suspend-strategy and the set or add/rem
+callback is configured.
-## Introduction
-
-The Dependency Manager contains a static library which can be used to manage (dynamic) services on a higher abstraction level in a declarative style.
-The Apache Celix Dependency Manager is inspired by the [Apache Felix Dependency Manager](http://felix.apache.org/documentation/subprojects/apache-felix-dependency-manager.html).
-
-## Components
-
-Components are the main building blocks for OSGi applications. They can publish services, and/or they can have dependencies. These dependencies will influence their life cycle as component will only be activated when all required dependencies are available.
-
-Within Apache Celix a component is expected to have a set of functions where the first argument is a handle to the component (e.g. self/this). How this is achieved is up the the user, for some examples how this can be done see the example in the Apache Celix Project.
-
-The Dependency Manager, as part of a bundle, shares the generic bundle life cycle explained in the OSGi specification.
-Each component you define gets its own life cycle. The component life cycle is depicted in the state diagram below.
+# Apache Celix Components
+In Apache Celix, components are plain old C/C++ objects (POCOs) managed by the Apache Celix Dependency Manager (DM).
+Components can provide service and have services dependencies. Components are configured declarative using the DM api.
+
+Service dependencies will influence the
+component's lifecycle as a component will only be active when all required dependencies
+are available.
+The DM is responsible for managing the component's service dependencies, the component's lifecycle and when
+to register/unregister the component's provided services.
+
+Note that the Apache Celix Dependency Manager is inspired by the [Apache Felix Dependency Manager](http://felix.apache.org/documentation/subprojects/apache-felix-dependency-manager.html), adapted to Apache Celix
+and the C/C++ usage.
+
+# Component Lifecycle
+Each DM Component you define gets its own lifecycle.
+A component's lifecycle state model is depicted in the state diagram below.
+
+
+
+The DM can be used to configure a component's lifecycle callbacks; Lifecycle callbacks are always called from the
+Celix event thread.
+The following component's lifecycle callbacks can be configured:
+
+ - `init`
+ - `start`
+ - `stop`
+ - `deinit`
+
+These callbacks are used in the intermediate component's lifecycle states `Initializing`, `Starting`, `Suspending`, `Resuming`, `Stopping` and `Deinitializing`.
+
+A DM Component has the following lifecycle states:
+- `Inactive`: _The component is inactive and the DM is not managing the component yet._
+- `Waiting For Required`: _The component is waiting for required service dependencies._
+- `Initializing`: _The component has found its required dependencies and is initializing:
+ Calling the `init` callback._
+- `Initialized And Waiting For Required`: _The component has been initialized, but is waiting for required
+ dependencies._
+ _Note: that this can mean: that during `init` callback new service dependencies was added or that the component
+ was active, but a required service dependency is removed and as result the component is not active anymore._
+- `Starting`: _The component has found its required dependencies and is starting: Calling the `start` callback and
+ registering it's provided services.
+- `Tracking Optional`: _The component has found its required dependencies and is started. It is still tracking for
+ additional optional and required services._
+- `Suspending`: _The component has found its required dependencies, but is suspending to prepare for a service change:
+ Unregistering its provided service and calling the `stop` callback._
+- `Suspended`: _The component has found its required dependencies and is suspended so that a service change can be
+ processed._
+- `Resuming`: _The component has found its required dependencies, a service change has been processed, and it is
+ resuming: Calling the `start` callback and registering its provided services.
+- `Stopping`: _The component has lost one or more of its required dependencies and is stopping: Unregistering its
+ provided service and calling the `stop` callback._
+- `Deinitializing`: _The component is being removed and is deinitializing: Calling the `deinit` callback._
+
+## Example: Create and configure component's lifecycle callbacks in C
+The following example shows how a simple component can be created and managed with the DM in C.
+Because the component's lifecycle is managed by the DM, this also means that if configured correctly no additional
+code is needed to remove and destroy the DM component and its implementation.
+
+Remarks for the C example:
+ 1. Although this is a C component. The simple component functions have been design for a component approach,
+ using the component pointer as first argument.
+ 2. The component implementation can be any POCO, as long as its lifecycle and destroy function follow a
+ component approach (one argument with the component implementation pointer as type)
+ 3. Creates the DM component, but note that this component is not yet known to the DM. This makes it possible to
+ first configure the DM component over multiple function calls, before adding - and as result
+ activating - the DM component to the DM.
+ 4. Configures the component implementation in the DM component, so that the implementation pointer can be used
+ in the configured component callbacks.
+ 5. Configures the component lifecycle callbacks to the DM Component. These callbacks should accept the component
+ implementation as its only argument. The `CELIX_DM_COMPONENT_SET_CALLBACKS` marco is used instead of the
+ `celix_dmComponent_setCallbacks` function so that the component implementation type can directly be used
+ in the lifecycle callbacks (instead of `void*`).
+ 6. Configures the component destroy implementation callback to the Dm Component. This callback will be called when
+ the DM component is removed from the DM and has become inactive. The callback will be called from the Celix event
+ thread. The advantages of configuring this callback is that the DM manages when the callback needs to be called;
+ this removes some complexity for the users. The `CELIX_DM_COMPONENT_SET_IMPLEMENTATION_DESTROY_FUNCTION` marco
+ is used instead of the `celix_dmComponent_setImplementationDestroyFunction` function so that the component
+ implementation type can be directly used in the callback (instead of `void*`).
+ 7. Adds the DM Component the DM and as result the DM will activate manage the component.
+ 8. No additional code is needed to clean up components and as such no activator stop callback function needs to be
+ configured. The generated bundle activator will ensure that all component are removed from the DM when the
+ bundle is stopped and the DM will ensure that the components are deactivated and destroyed correctly.
+
+```C
+//src/simple_component_activator.c
+#include <stdio.h>
+#include <celix_api.h>
-
+//********************* COMPONENT *******************************/
-Changes in the state of the component will trigger the following life cycle callback functions:
+typedef struct simple_component {
+ int transitionCount; //not protected, only updated and read in the celix event thread.
+} simple_component_t;
- `init`,
- `start`,
- `stop` and
- `deinit`.
+static simple_component_t* simpleComponent_create() {
+ simple_component_t* cmp = calloc(1, sizeof(*cmp));
+ cmp->transitionCount = 1;
+ return cmp;
+}
-The callback functions can be specified by using the component_setCallbacks.
+static void simpleComponent_destroy(simple_component_t* cmp) {
+ free(cmp);
+}
-## DM Parts
+static int simpleComponent_init(simple_component_t* cmp) { // <------------------------------------------------------<1>
+ printf("Initializing simple component. Transition nr %i\n", cmp->transitionCount++);
+ return 0;
+}
-The Dependency Manager consist out of four main parts: `DM (Dependency Manager) Activator`, `Dependency Manager`, `DM Component` and `DM Service Dependency`.
+static int simpleComponent_start(simple_component_t* cmp) {
+ printf("Starting simple component. Transition nr %i\n", cmp->transitionCount++);
+ return 0;
+}
-### DM Activator
+static int simpleComponent_stop(simple_component_t* cmp) {
+ printf("Stopping simple component. Transition nr %i\n", cmp->transitionCount++);
+ return 0;
+}
-The `DM Activator` implements a "normal" Celix bundle activator and depends on four functions which needs to be implemented by the user of the Dependency Manager:
-- `dm_create` : Should be used to allocated and initialize a dm activator structure. If needed this structure can be used to store object during the lifecycle of the bundle.
-- `dm_init` : Should be used to interact with the `Dependency Manager`. Here a user can components, service dependencies and provided services.
-- `dm_destroy` : Should be used to deinitialize and deallocate objects created in the `dm_create` function.
+static int simpleComponent_deinit(simple_component_t* cmp) {
+ printf("De-initializing simple component. Transition nr %i\n", cmp->transitionCount++);
+ return 0;
+}
-### Dependency Manager
+//********************* ACTIVATOR *******************************/
+
+typedef struct simple_component_activator {
+ //nop
+} simple_component_activator_t;
+
+static celix_status_t simpleComponentActivator_start(simple_component_activator_t *act, celix_bundle_context_t *ctx) {
+ //creating component
+ simple_component_t* impl = simpleComponent_create(); // <--------------------------------------------------------<2>
+
+ //create and configuring component and its lifecycle callbacks using the Apache Celix Dependency Manager
+ celix_dm_component_t* dmCmp = celix_dmComponent_create(ctx, "simple_component_1"); // <--------------------------<3>
+ celix_dmComponent_setImplementation(dmCmp, impl); // <-----------------------------------------------------------<4>
+ CELIX_DM_COMPONENT_SET_CALLBACKS(
+ dmCmp,
+ simple_component_t,
+ simpleComponent_init,
+ simpleComponent_start,
+ simpleComponent_stop,
+ simpleComponent_deinit); // <----------------------------------------------------------------------------<5>
+ CELIX_DM_COMPONENT_SET_IMPLEMENTATION_DESTROY_FUNCTION(
+ dmCmp,
+ simple_component_t,
+ simpleComponent_destroy); // <---------------------------------------------------------------------------<6>
+
+ //Add dm component to the dm.
+ celix_dependency_manager_t* dm = celix_bundleContext_getDependencyManager(ctx);
+ celix_dependencyManager_add(dm, dmCmp); // <---------------------------------------------------------------------<7>
+ return CELIX_SUCCESS;
+}
-The `Dependency Manager` act as an entry point to add or remove DM Components. The `Dependency Manager` is provided to the `dm_init` function.
+CELIX_GEN_BUNDLE_ACTIVATOR(simple_component_activator_t, simpleComponentActivator_start, NULL) // <------------------<8>
+```
-### DM Component
+## Example: Create and configure component's lifecycle callbacks in C++
+The following example shows how a simple component can be created and managed with the DM in C++.
+For C++ the DM will manage the components and also ensures that component implementations are kept in scope for as
+long as the components are managed by the DM.
+
+Remarks for the C++ example:
+1. For C++ the DM can directly work on classes and as result lifecycle callback can be class methods.
+2. Creates a component implementation using a unique_ptr.
+3. Create a C++ DM Component and directly add it to the DM. For C++ DM Component needs to be "build" first, before the
+ DM will activate them. This way C++ components can be build using a fluent api and marked ready with a `build()`
+ method call.
+ As component implementation the DM accepts a unique_pt, shared_ptr, value type of no implementation. If no
+ implementation is provided the DM will create a component implementation using the template argument and
+ assuming a default constructor (e.g. `ctx->getDependencyManager()->createComponent<CmpWithDefaultCTOR>()`).
+4. Configures the component lifecycle callbacks as class methods. The DM will call these callbacks using the
+ component implementation as object instance.
+5. "Builds" the component. C++ component will only be managed by the DM after they are build. This makes it possible
+ to configure a component over multiple method calls before marking the component as ready (build).
+ The generated C++ bundle activator will also enable all components created during the bundle activation, to ensure
+ that the build behaviour is backwards compatible with previous released DM implementation. It is preferred that
+ users explicitly build their components when they are completely configured.
+
+```C++
+//src/SimpleComponentActivator.cc
+#include <celix/BundleActivator.h>
+
+class SimpleComponent {
+public:
+ void init() { // <-----------------------------------------------------------------------------------------------<1>
+ std::cout << "Initializing simple component. Transition nr " << transitionCount++ << std::endl;
+ }
-The `DM Component` manages the life cycle of a component. For example, when all required service dependencies are available the `DM Component` will call the `start` specified callback function of the component.
+ void start() {
+ std::cout << "starting simple component. Transition nr " << transitionCount++ << std::endl;
+ }
-The `component_setImplementation` function can be used to specify which component handle to use.
-The `component_addInterface` can be used to specify one additional service provided by the component.
-The `component_addServiceDependency` can be used to specify one additional service dependency.
+ void stop() {
+ std::cout << "Stopping simple component. Transition nr " << transitionCount++ << std::endl;
+ }
-### Dm Service Dependency
+ void deinit() {
+ std::cout << "De-initializing simple component. Transition nr " << transitionCount++ << std::endl;
+ }
+private:
+ int transitionCount = 1; //not protected, only updated and read in the celix event thread.
+};
-The `DM Service Dependency` can be used to specify service dependencies for a component. i
+class SimpleComponentActivator {
+public:
+ explicit SimpleComponentActivator(const std::shared_ptr<celix::BundleContext>& ctx) {
+ auto cmp = std::make_unique<SimpleComponent>(); // <---------------------------------------------------------<2>
+ ctx->getDependencyManager()->createComponent(std::move(cmp), "SimpleComponent1") // <------------------------<3>
+ .setCallbacks(
+ &SimpleComponent::init,
+ &SimpleComponent::start,
+ &SimpleComponent::stop,
+ &SimpleComponent::deinit) // <---------------------------------------------------------------<4>
+ .build(); // <---------------------------------------------------------------------------------------<5>
+ }
+};
-When these dependencies are set to required the `DM Component` will ensure that components will only be started when all required dependencies are available and stop the component if any of the required dependencies are removed.
-This feature should prevent a lot of boiler plating code compared to using a service tracker or services references directly.
+CELIX_GEN_CXX_BUNDLE_ACTIVATOR(SimpleComponentActivator)
+```
-A service dependency update strategy can also be specified. Default this strategy is set to `DM_SERVICE_DEPENDENCY_STRATEGY_SUSPEND` this strategy will stop and start (suspend) a component when any of the specified service dependencies change (are removed, added or modified).
-When correctly used this strategy removes the need for locking services during updates/invocation. See the dependency manager example for more details.
+# Component's Provided Services
+Components can be configured to provide services. These provided services will correspondent to service registration
+when a component is in the `Tracking Optional` state.
-The `serviceDependency_setCallbacks` function can be used to specify the function callback used when services are added, set, removed or modified.
-The `serviceDependency_setRequired` function can be used to specify if a service dependency is required.
-The `serviceDependency_setStrategy` function can be used to specify a service dependency update strategy (suspend or locking).
+If a component provide services, these service will have an additional metadata property - named "component.uuid"
+that couples the services to the component named "component.uuid".
-### Snippets
+## Example: Component with a provided service in C
+The following example shows how a component that provide a `celix_shell_command` service.
-#### DM Bundle Activator
+Remarks for the C example:
+1. C and C services do not support inheritance. So even if a C component provides a certain service it is not an
+ instance of said service. This also means the C service struct provided by a component needs to be stored
+ separately. In this example this is done storing the service struct in the bundle activator data. Note
+ that the bundle activator data "outlives" the component, because all components are removed before a bundle
+ is completely stopped.
+2. Configures a provided service (interface) for the component. The service will not directly be registered, but
+ instead will be registered if the component enters the state `Tracking Optional`.
-The next snippet shows a dm bundle activator and how to add components to the dependency manager.
```C
-
-//exmpl_activator.c
-#include <dm_activator.h>
+ //src/component_with_provided_service_activator.c
#include <stdlib.h>
+#include <celix_api.h>
+#include <celix_shell_command.h>
-struct dm_exmpl_activator {
- exmpl_t* exmpl;
-};
-
-celix_status_t dm_create(bundle_context_pt context, void **userData) {
- *userData = calloc(1, sizeof(struct dm_exmpl_activator));
- return *userData != NULL ? CELIX_SUCCESS : CELIX_ENOMEM;
-}
-
-celix_status_t dm_init(void * userData, bundle_context_pt context, dm_dependency_manager_pt manager) {
- celix_status_t status = CELIX_SUCCESS;
- struct dm_exmpl_activator *act = (struct dm_exmpl_activator*)userData;
+//********************* COMPONENT *******************************/
- act->exmpl = exmpl_create();
- if (act->exmpl != NULL) {
- dm_component_pt cmp;
- component_create(context, "Example Component", &cmp);
- component_setImplementation(cmp, act->exmpl);
+typedef struct component_with_provided_service {
+ int callCount; //atomic
+} component_with_provided_service_t;
- dependencyManager_add(manager, cmp);
- } else {
- status = CELIX_ENOMEM;
- }
-
- return status;
+static component_with_provided_service_t* componentWithProvidedService_create() {
+ component_with_provided_service_t* cmp = calloc(1, sizeof(*cmp));
+ return cmp;
}
-celix_status_t dm_destroy(void * userData, bundle_context_pt context, dm_dependency_manager_pt manager) {
- celix_status_t status = CELIX_SUCCESS;
- struct dm_exmpl_activator *act = (struct dm_exmpl_activator*)userData;
+static void componentWithProvidedService_destroy(component_with_provided_service_t* cmp) {
+ free(cmp);
+}
- if (act->exmpl != NULL) {
- exmpl_destroy(act->exmpl);
- }
- free(act);
+static bool componentWithProvidedService_executeCommand(
+ component_with_provided_service_t *cmp,
+ const char *commandLine,
+ FILE *outStream,
+ FILE *errorStream __attribute__((unused))) {
+ int count = __atomic_add_fetch(&cmp->callCount, 1, __ATOMIC_SEQ_CST);
+ fprintf(outStream, "Hello from cmp. command called %i times. commandLine: %s\n", count, commandLine);
+ return true;
+}
+//********************* ACTIVATOR *******************************/
+
+typedef struct component_with_provided_service_activator {
+ celix_shell_command_t shellCmd; // <-----------------------------------------------------------------------------<1>
+} component_with_provided_service_activator_t;
+
+static celix_status_t componentWithProvidedServiceActivator_start(component_with_provided_service_activator_t *act, celix_bundle_context_t *ctx) {
+ //creating component
+ component_with_provided_service_t* impl = componentWithProvidedService_create();
+
+ //create and configuring component and its lifecycle callbacks using the Apache Celix Dependency Manager
+ celix_dm_component_t* dmCmp = celix_dmComponent_create(ctx, "component_with_provided_service_1");
+ celix_dmComponent_setImplementation(dmCmp, impl);
+ CELIX_DM_COMPONENT_SET_IMPLEMENTATION_DESTROY_FUNCTION(
+ dmCmp,
+ component_with_provided_service_t,
+ componentWithProvidedService_destroy);
+
+ //configure provided service
+ act->shellCmd.handle = impl;
+ act->shellCmd.executeCommand = (void*)componentWithProvidedService_executeCommand;
+ celix_properties_t* props = celix_properties_create();
+ celix_properties_set(props, CELIX_SHELL_COMMAND_NAME, "hello_component");
+ celix_dmComponent_addInterface(
+ dmCmp,
+ CELIX_SHELL_COMMAND_SERVICE_NAME,
+ CELIX_SHELL_COMMAND_SERVICE_VERSION,
+ &act->shellCmd,
+ props); // <---------------------------------------------------------------------------------------------<2>
+
+
+ //Add dm component to the dm.
+ celix_dependency_manager_t* dm = celix_bundleContext_getDependencyManager(ctx);
+ celix_dependencyManager_add(dm, dmCmp);
return CELIX_SUCCESS;
-}
-```
-
-### References
+}
-For more information examples please see
+CELIX_GEN_BUNDLE_ACTIVATOR(
+ component_with_provided_service_activator_t,
+ componentWithProvidedServiceActivator_start,
+ NULL)
+```
-- [The Dependency Manager API](../../libs/framework/include/celix/dm): The dependency manager header files
-- [Getting Started: Using Service with C](../getting_started/using_services_with_c.md): A introduction how to work with services using the dependency manager
-- [Dm example](../../examples/celix-examples/dm_example): A DM example.
+## Example: Component with a provided service in C++
+The following example shows how a C++ component that provide a C++ `celix::IShellCommand` service
+and a C `elix_shell_command` service. For a C++ component it's possible to provide C and C++ services.
+
+Remarks for the C++ example:
+1. If a component provides a C++ services, it also expected that the component implementation inherits the service
+ interface.
+2. The overridden `executeCommand` method of `celix::IShellCommand`.
+3. Methods of C service interfaces can be implemented as class methods, but the bundle activator should ensure that
+ the underlining C service interface structs are assigned with compatible C function pointers.
+4. Creating a component using only a template argument. The DM will construct - using a default constructor - a
+ component implementation instance.
+5. Configures the component to provide a C++ `celix::IShellCommand` service. Note that because the component
+ implementation is an instance of `celix::IShellCommand` no additional storage is needed. The service will not
+ directly be registered, but instead will be registered if the component enters the state `Tracking Optional`.
+6. Set the C `executeCommand` function pointer of the `celix_shell_command_t` service interface struct to a
+ capture-less lambda expression. The lambda expression is used to forward the call to the `executeCCommand`
+ class method. Note the capture-less lambda expression can decay to function pointers.
+7. Configures the component to provide a C `celix_shell_command_t` service. Note that for a C service, the service
+ interface struct also needs to be stored to ensure that the service interface struct will outlive the component.
+ The service will not directly be registered, but instead will be registered if the component enters the state
+ `Tracking Optional`..
+8. "Build" the component so the DM will manage and try to activate the component.
+
+
+```C++
+//src/ComponentWithProvidedServiceActivator.cc
+#include <celix/BundleActivator.h>
+#include <celix/IShellCommand.h>
+#include <celix_shell_command.h>
+
+class ComponentWithProvidedService : public celix::IShellCommand { // <----------------------------------------------<1>
+public:
+ ~ComponentWithProvidedService() noexcept override = default;
+
+ void executeCommand(
+ const std::string& commandLine,
+ const std::vector<std::string>& /*commandArgs*/,
+ FILE* outStream,
+ FILE* /*errorStream*/) override {
+ fprintf(outStream, "Hello from cmp. C++ command called %i times. commandLine is %s\n", cxxCallCount++, commandLine.c_str());
+ } // <-----------------------------------------------------------------------------------------------------------<2>
+
+ void executeCCommand(const char* commandLine, FILE* outStream) {
+ fprintf(outStream, "Hello from cmp. C command called %i times. commandLine is %s\n", cCallCount++, commandLine);
+ } // <-----------------------------------------------------------------------------------------------------------<3>
+private:
+ std::atomic<int> cxxCallCount{1};
+ std::atomic<int> cCallCount{1};
+};
-## Dependency Manager Shell support
+class ComponentWithProvidedServiceActivator {
+public:
+ explicit ComponentWithProvidedServiceActivator(const std::shared_ptr<celix::BundleContext>& ctx) {
+ auto& cmp = ctx->getDependencyManager()->createComponent<ComponentWithProvidedService>(); // <---------------<4>
-There is support for retrieving information of the dm components with
-use of the `dm` command. This command will print all known dm component,
-their state, provided interfaces and required interfaces.
+ cmp.createProvidedService<celix::IShellCommand>()
+ .addProperty(celix::IShellCommand::COMMAND_NAME, "HelloComponent"); // <---------------------------------<5>
+ shellCmd.handle = static_cast<void*>(&cmp.getInstance());
+ shellCmd.executeCommand = [](void* handle, const char* commandLine, FILE* outStream, FILE*) -> bool {
+ auto* impl = static_cast<ComponentWithProvidedService*>(handle);
+ impl->executeCCommand(commandLine, outStream);
+ return true;
+ }; // <------------------------------------------------------------------------------------------------------<6>
+ cmp.createProvidedCService(&shellCmd, CELIX_SHELL_COMMAND_SERVICE_NAME)
+ .addProperty(CELIX_SHELL_COMMAND_NAME, "hello_component"); // < -----------------------------------------<7>
-# Apache Celix C++ Dependency Manager
+ cmp.build(); // <--------------------------------------------------------------------------------------------<8>
+ }
+private:
+ celix_shell_command_t shellCmd{nullptr, nullptr};
+};
-## Introduction
+CELIX_GEN_CXX_BUNDLE_ACTIVATOR(ComponentWithProvidedServiceActivator)
+```
-The C++ Dependency Manager contains a static library which can be used to manage (dynamic) services on a higher abstraction level in a declarative style.
-The Apache Celix C++ Dependency Manager is inspired by the [Apache Felix Dependency Manager](http://felix.apache.org/documentation/subprojects/apache-felix-dependency-manager.html).
+# Component's Service Dependencies
+Components can be configured to have service dependencies. These service dependencies will influence the component's
+lifecycle. Components can have optional and required service dependencies. When service dependencies are required the
+component can only be active if all required dependencies are available;
+
+When configuring service dependencies, callbacks can be configured when services are being added, removed or when a
+new highest ranking service is available. It is also possible to configure callbacks which only inject/remove service
+pointers, or service pointers with their properties (metadata) and even service pointers with their properties and
+their providing bundle. Service dependency callbacks will always be called from the Celix event thread.
+
+A service change (injection/removal) can be handled by the component using a Locking-strategy or a suspend-strategy.
+This strategy can be configured per service dependency and expect the following behaviour from the component
+implementation:
+- Locking-strategy: The component implementation must ensure that the service pointers (and if applicable the service
+ properties and its bundle) are protected using a locking mechanism (e.g. a mutex). This should ensure that services
+ are no longer in use after they are removed (or replaced) from a component.
+- Suspend-strategy: The DM will ensure that before service dependency callbacks are called, all provided services
+ are (temporary) unregistered and the component is suspended (using the components' stop callback). This should mean
+ that there are no active users - through the provided services or active threads - of the service dependencies
+ anymore and that service changes can safely be handling without locking. The component implementation must ensure
+ that after `stop` callback no active thread, thread pools, timers, etc that use service dependencies are active
+ anymore.
+
+## Example: Component with a service dependencies in C
+The following example shows how a C component that has two service dependency on the `celix_shell_command_t` service.
+
+One service dependency is a required dependency with a suspend-strategy and uses a `set ` callback which ensure
+that a single service is injected and that is always the highest ranking service. Note that the highest ranking
+service can be `NULL` if there are no service.
+
+The other dependency is an optional dependency with a locking-strategy and uses a `addWithProps` and
+`removeWithProps` callback. These callbacks will be called for every `celix_shell_command_t` service being added/removed
+and will be called with not only the service pointer, but also the service metadata properties.
+
+Remarks for the C example:
+1. TODO
-The C++ Dependency Manager uses fluent interface to make specifying DM components and service dependencies very concise and relies on features introduced in C++11.
+```C
+//src/component_with_service_dependency_activator.c
+#include <stdlib.h>
+#include <celix_api.h>
+#include <celix_shell_command.h>
+
+//********************* COMPONENT *******************************/
+
+typedef struct component_with_service_dependency {
+ celix_shell_command_t* highestRankingCmdShell; //only updated when component is not active or suspended
+ celix_thread_mutex_t mutex;
+ celix_array_list_t* cmdShells;
+} component_with_service_dependency_t;
+
+static component_with_service_dependency_t* componentWithServiceDependency_create() {
+ component_with_service_dependency_t* cmp = calloc(1, sizeof(*cmp));
+ celixThreadMutex_create(&cmp->mutex, NULL);
+ cmp->cmdShells = celix_arrayList_create();
+ return cmp;
+}
-## C++ and C Dependency Manager
+static void componentWithServiceDependency_destroy(component_with_service_dependency_t* cmp) {
+ celix_arrayList_destroy(cmp->cmdShells);
+ celixThreadMutex_destroy(&cmp->mutex);
+ free(cmp);
+}
-The C++ Dependency Manager is build on top of the C Dependency Manager.
-To get a good overview of the C++ Dependency Manager please read the [Dependency Manager documentation](../../documents/components/README.md)
+static void componentWithServiceDependency_setHighestRankingShellCommand(
+ component_with_service_dependency_t* cmp,
+ celix_shell_command_t* shellCmd) {
+ printf("New highest ranking service (can be NULL): %p\n", shellCmd);
+ cmp->highestRankingCmdShell = shellCmd;
+}
-## DM Parts
+static void componentWithServiceDependency_addShellCommand(
+ component_with_service_dependency_t* cmp,
+ celix_shell_command_t* shellCmd,
+ const celix_properties_t* props) {
+ long id = celix_properties_getAsLong(props, CELIX_FRAMEWORK_SERVICE_ID, -1);
+ printf("Adding shell command service with service.id %li\n", id);
+ celixThreadMutex_lock(&cmp->mutex);
+ celix_arrayList_add(cmp->cmdShells, shellCmd);
+ celixThreadMutex_unlock(&cmp->mutex);
+}
-The C++ Dependency Manager consist out of four main parts: `celix::dm::DmActivator`, `celix::dm::DependencyManager`, `celix::dm::Component` and `celix::dm::ServiceDependency`.
+static void componentWithServiceDependency_removeShellCommand(
+ component_with_service_dependency_t* cmp,
+ celix_shell_command_t* shellCmd,
+ const celix_properties_t* props) {
+ long id = celix_properties_getAsLong(props, CELIX_FRAMEWORK_SERVICE_ID, -1);
+ printf("Removing shell command service with service.id %li\n", id);
+ celixThreadMutex_lock(&cmp->mutex);
+ celix_arrayList_remove(cmp->cmdShells, shellCmd);
+ celixThreadMutex_unlock(&cmp->mutex);
+}
-### DmActivator
+//********************* ACTIVATOR *******************************/
+
+typedef struct component_with_service_dependency_activator {
+ //nop
+} component_with_service_dependency_activator_t;
+
+static celix_status_t componentWithServiceDependencyActivator_start(component_with_service_dependency_activator_t *act, celix_bundle_context_t *ctx) {
+ //creating component
+ component_with_service_dependency_t* impl = componentWithServiceDependency_create();
+
+ //create and configuring component and its lifecycle callbacks using the Apache Celix Dependency Manager
+ celix_dm_component_t* dmCmp = celix_dmComponent_create(ctx, "component_with_service_dependency_1");
+ celix_dmComponent_setImplementation(dmCmp, impl);
+ CELIX_DM_COMPONENT_SET_IMPLEMENTATION_DESTROY_FUNCTION(
+ dmCmp,
+ component_with_service_dependency_t,
+ componentWithServiceDependency_destroy);
+
+ //create mandatory service dependency with cardinality one and with a suspend-strategy
+ celix_dm_service_dependency_t* dep1 = celix_dmServiceDependency_create();
+ celix_dmServiceDependency_setService(dep1, CELIX_SHELL_COMMAND_SERVICE_NAME, NULL, NULL);
+ celix_dmServiceDependency_setStrategy(dep1, DM_SERVICE_DEPENDENCY_STRATEGY_SUSPEND);
+ celix_dmServiceDependency_setRequired(dep1, true);
+ celix_dm_service_dependency_callback_options_t opts1 = CELIX_EMPTY_DM_SERVICE_DEPENDENCY_CALLBACK_OPTIONS;
+ opts1.set = (void*)componentWithServiceDependency_setHighestRankingShellCommand;
+ celix_dmServiceDependency_setCallbacksWithOptions(dep1, &opts1);
+ celix_dmComponent_addServiceDependency(dmCmp, dep1);
+
+ //create optional service dependency with cardinality many and with a locking-strategy
+ celix_dm_service_dependency_t* dep2 = celix_dmServiceDependency_create();
+ celix_dmServiceDependency_setService(dep2, CELIX_SHELL_COMMAND_SERVICE_NAME, NULL, NULL);
+ celix_dmServiceDependency_setStrategy(dep2, DM_SERVICE_DEPENDENCY_STRATEGY_LOCKING);
+ celix_dmServiceDependency_setRequired(dep2, false);
+ celix_dm_service_dependency_callback_options_t opts2 = CELIX_EMPTY_DM_SERVICE_DEPENDENCY_CALLBACK_OPTIONS;
+ opts2.addWithProps = (void*)componentWithServiceDependency_addShellCommand;
+ opts2.removeWithProps = (void*)componentWithServiceDependency_removeShellCommand;
+ celix_dmServiceDependency_setCallbacksWithOptions(dep2, &opts2);
+ celix_dmComponent_addServiceDependency(dmCmp, dep2);
+
+ //Add dm component to the dm.
+ celix_dependency_manager_t* dm = celix_bundleContext_getDependencyManager(ctx);
+ celix_dependencyManager_add(dm, dmCmp);
+ return CELIX_SUCCESS;
+}
-The `DmActivator` class should be inherited by a bundle specific Activator.
+CELIX_GEN_BUNDLE_ACTIVATOR(
+ component_with_service_dependency_activator_t,
+ componentWithServiceDependencyActivator_start,
+ NULL)
+```
-- The static `DmActivator::create` method needs to be implemented and should return a bundle specific subclass instance of the DmActivator.
-- The `DmActivator::init` method should be overridden and can be used to specify which components to use in the bundle.
-- The `DmActivator::deinit` method can be overridden if some cleanup is needed when a bundle is stopped.
+## Example: Component with a service dependencies in C++
+The following example shows how a C++ component that has two service dependency. One
+service dependency for the C++ `celix::IShellCommand` service and one for the C `celix_shell_command_t` service.
-### Dependency Manager
+The `celix::IShellCommand` service dependency is a required dependency with a suspend-strategy and uses a
+`set ` callback which ensure that a single service is injected and that is always the highest ranking service.
+Note that the highest ranking service can be an empty shared_ptr if there are no service.
-The `DependencyManager` act as an entry point to create (DM) Components.
+The `celix_shell_command_t` service dependency is an optional dependency with a locking-strategy and uses a
+`addWithProperties` and `removeWithProperties` callback.
+These callbacks will be called for every `celix_shell_command_t` service being added/removed
+and will be called with not only the service shared_ptr, but also the service metadata properties.
-### Component
+Note that for C++ component service dependencies there is no real different between a C++ or a C service; Both
+are injected as shared_ptr, and as optional properties or bundle argument the C++ variant are provided.
-The (DM) `Component` manages the life cycle of a component (of the template type T). For example, when all required service dependencies are available the `Component` will call the `start` specified callback function of the component.
+Remarks for the C++ example:
+1. TODO
-- The `Component::setInstance` method can be used to set the component instance to used. If no instance is set the (DM) `Component` will (lazy) create a component instance using the default constructor.
-- The `Component::addInterface` method can be used to specify one additional C++ service provided by the component.
-- The `Component::addCInterface` method can be used to specify one additional C service provided by the component.
-- The `Component::createServiceDependency` method can be used to specify one additional typed C++ service dependency.
-- The `Component::createCServiceDependency` method can be used to specify one additional typed C service dependency.
+```C++
+//src/ComponentWithServiceDependencyActivator.cc
+#include <celix/BundleActivator.h>
+#include <celix/IShellCommand.h>
+#include <celix_shell_command.h>
-### ServiceDependency and CServiceDependency
+class ComponentWithServiceDependency {
+public:
+ void setHighestRankingShellCommand(const std::shared_ptr<celix::IShellCommand>& cmdSvc) {
+ std::cout << "New highest ranking service (can be NULL): " << (intptr_t)cmdSvc.get() << std::endl;
+ highestRankingShellCmd = cmdSvc;
+ }
-The (DM) `ServiceDependency` can be used to specify C++ service dependencies for a component and the (DM) `CServiceDependency` can be used to specify C service dependencies for a component.
+ void addCShellCmd(
+ const std::shared_ptr<celix_shell_command_t>& cmdSvc,
+ const std::shared_ptr<const celix::Properties>& props) {
+ auto id = props->getAsLong(celix::SERVICE_ID, -1);
+ std::cout << "Adding shell command service with service.id: " << id << std::endl;
+ std::lock_guard lck{mutex};
+ shellCommands.emplace(id, cmdSvc);
+ }
-When these dependencies are set to required the `Component` will ensure that components will only be started when all required dependencies are available and stop the component if any of the required dependencies are removed.
-This feature should prevent a lot of boiler plating code compared to using a service tracker or services references directly.
+ void removeCShellCmd(
+ const std::shared_ptr<celix_shell_command_t>& /*cmdSvc*/,
+ const std::shared_ptr<const celix::Properties>& props) {
+ auto id = props->getAsLong(celix::SERVICE_ID, -1);
+ std::cout << "Removing shell command service with service.id: " << id << std::endl;
+ std::lock_guard lck{mutex};
+ shellCommands.erase(id);
+ }
+private:
+ std::shared_ptr<celix::IShellCommand> highestRankingShellCmd{};
+ std::mutex mutex{}; //protect below
+ std::unordered_map<long, std::shared_ptr<celix_shell_command_t>> shellCommands{};
+};
-A service dependency update strategy can also be specified (suspend or locking. Default this strategy is set to `DependencyUpdateStrategy::suspend` this strategy will stop and start (suspend) a component when any of the specified service dependencies changes (are removed, added or modified).
-When correctly used this strategy removes the need for locking services during updates/invocation. See the dependency manager_cxx example for more details.
+class ComponentWithServiceDependencyActivator {
+public:
+ explicit ComponentWithServiceDependencyActivator(const std::shared_ptr<celix::BundleContext>& ctx) {
+ using Cmp = ComponentWithServiceDependency;
+ auto& cmp = ctx->getDependencyManager()->createComponent<Cmp>(); // <-------------<1>
-- The `(C)ServiceDependency::setCallbacks` methods can be used to specify the function callback used when services are added, set, removed or modified.
-- The `(C)ServiceDependency::setRequired` methods can be used to specify if a service dependency is required.
-- The `(C)ServiceDependency::setStrategy` methods can be used to specify the service dependency update strategy (suspend or locking).
+ cmp.createServiceDependency<celix::IShellCommand>()
+ .setCallbacks(&Cmp::setHighestRankingShellCommand)
+ .setRequired(true)
+ .setStrategy(DependencyUpdateStrategy::suspend);
-### References
+ cmp.createServiceDependency<celix_shell_command_t>(CELIX_SHELL_COMMAND_SERVICE_NAME)
+ .setCallbacks(&Cmp::addCShellCmd, &Cmp::removeCShellCmd)
+ .setRequired(false)
+ .setStrategy(DependencyUpdateStrategy::locking);
-For more information examples please see
+ cmp.build(); // <--------------------------------------------------------------------------------------------<8>
+ }
+};
-- [The C++ Dependency Manager API](../../libs/framework/include): The C++ dependency manager header files
-- [Dm C++ example](../../examples/celix-examples/dm_example_cxx): A DM C++ example.
-- [Getting Started: Using Services with C++](../../documents/getting_started/using_services_with_cxx.md): A introduction how to work with services using the C++ dependency manager
+CELIX_GEN_CXX_BUNDLE_ACTIVATOR(ComponentWithServiceDependencyActivator)
+```
-## Using info
+# When will a component be suspended
-If the Celix C++ Dependency Manager is installed, 'find_package(Celix)' will set:
-- The `Celix::shell_api` interface (i.e. headers only) library target
-- The `Celix::shell` bundle target
\ No newline at end of file
+TODO explain that a component will be suspended with service dependency on suspend strategy, a matching service update
+and if there is a svc inject callback configured. TODO check also start/stop method?
diff --git a/examples/celix-examples/dm_example/phase1/src/phase1_activator.c b/examples/celix-examples/dm_example/phase1/src/phase1_activator.c
index fd72d831..812d6af5 100644
--- a/examples/celix-examples/dm_example/phase1/src/phase1_activator.c
+++ b/examples/celix-examples/dm_example/phase1/src/phase1_activator.c
@@ -43,7 +43,9 @@ static celix_status_t activator_start(struct phase1_activator_struct *act, celix
celix_dm_component_t *cmp= celix_dmComponent_create(ctx, "PHASE1_PROCESSING_COMPONENT");
celix_dmComponent_setImplementation(cmp, act->phase1Cmp);
- CELIX_DMCOMPONENT_SETCALLBACKS(cmp, phase1_cmp_t *, phase1_init, phase1_start, phase1_stop, phase1_deinit);
+ CELIX_DM_COMPONENT_SET_CALLBACKS(cmp, phase1_cmp_t, phase1_init, phase1_start, phase1_stop, phase1_deinit);
+ CELIX_DM_COMPONENT_SET_IMPLEMENTATION_DESTROY_FUNCTION(cmp, phase1_cmp_t, phase1_destroy);
+
phase1_setComp(act->phase1Cmp, cmp);
celix_dmComponent_addInterface(cmp, PHASE1_NAME, PHASE1_VERSION, &act->phase1Serv, props);
@@ -57,12 +59,6 @@ static celix_status_t activator_start(struct phase1_activator_struct *act, celix
static celix_status_t activator_stop(struct phase1_activator_struct *act, celix_bundle_context_t *ctx) {
printf("PHASE1: stop\n");
- celix_dependency_manager_t *mng = celix_bundleContext_getDependencyManager(ctx);
- celix_dependencyManager_removeAllComponents(mng);
- if (act->phase1Cmp != NULL) {
- phase1_destroy(act->phase1Cmp);
- }
-
return CELIX_SUCCESS;
}
diff --git a/examples/celix-examples/dm_example/phase2a/src/phase2a_activator.c b/examples/celix-examples/dm_example/phase2a/src/phase2a_activator.c
index d92664b3..2107c3e9 100644
--- a/examples/celix-examples/dm_example/phase2a/src/phase2a_activator.c
+++ b/examples/celix-examples/dm_example/phase2a/src/phase2a_activator.c
@@ -45,9 +45,10 @@ static celix_status_t activator_start(struct phase2a_activator_struct *act, celi
celix_dm_component_t *cmp = celix_dmComponent_create(ctx, "PHASE2A_PROCESSING_COMPONENT");
celix_dmComponent_setImplementation(cmp, act->phase2aCmp);
- CELIX_DMCOMPONENT_SETCALLBACKS(cmp, phase2a_cmp_t *, phase2a_init, phase2a_start, phase2a_stop, phase2a_deinit);
- celix_dmComponent_addInterface(cmp, PHASE2_NAME, PHASE2_VERSION, &act->phase2Serv, props);
+ CELIX_DM_COMPONENT_SET_CALLBACKS(cmp, phase2a_cmp_t, phase2a_init, phase2a_start, phase2a_stop, phase2a_deinit);
+ CELIX_DM_COMPONENT_SET_IMPLEMENTATION_DESTROY_FUNCTION(cmp, phase2a_cmp_t, phase2a_destroy);
+ celix_dmComponent_addInterface(cmp, PHASE2_NAME, PHASE2_VERSION, &act->phase2Serv, props);
celix_dm_service_dependency_t *dep = celix_dmServiceDependency_create();
celix_dmServiceDependency_setService(dep, PHASE1_NAME, PHASE1_RANGE_ALL, NULL);
@@ -67,11 +68,6 @@ static celix_status_t activator_start(struct phase2a_activator_struct *act, celi
static celix_status_t activator_stop(struct phase2a_activator_struct *act, celix_bundle_context_t *ctx) {
printf("phase2a: stop\n");
- celix_dependency_manager_t *mng = celix_bundleContext_getDependencyManager(ctx);
- celix_dependencyManager_removeAllComponents(mng);
- if (act->phase2aCmp != NULL) {
- phase2a_destroy(act->phase2aCmp);
- }
return CELIX_SUCCESS;
}
diff --git a/examples/celix-examples/dm_example/phase2b/src/phase2b_activator.c b/examples/celix-examples/dm_example/phase2b/src/phase2b_activator.c
index c04ce045..cec241b6 100644
--- a/examples/celix-examples/dm_example/phase2b/src/phase2b_activator.c
+++ b/examples/celix-examples/dm_example/phase2b/src/phase2b_activator.c
@@ -45,7 +45,9 @@ static celix_status_t activator_start(struct phase2b_activator_struct *act, celi
celix_dm_component_t *cmp = celix_dmComponent_create(ctx, "PHASE2B_PROCESSING_COMPONENT");
celix_dmComponent_setImplementation(cmp, act->phase2bCmp);
- CELIX_DMCOMPONENT_SETCALLBACKS(cmp, phase2b_cmp_t *, phase2b_init, phase2b_start, phase2b_stop, phase2b_deinit);
+ CELIX_DM_COMPONENT_SET_CALLBACKS(cmp, phase2b_cmp_t, phase2b_init, phase2b_start, phase2b_stop, phase2b_deinit);\
+ //note not configuring destroy callback -> destroying component manually in the activator stop callback.
+
celix_dmComponent_addInterface(cmp, PHASE2_NAME, PHASE2_VERSION, &act->phase2Serv, props);
@@ -70,7 +72,7 @@ static celix_status_t activator_stop(struct phase2b_activator_struct *act, celix
celix_dependency_manager_t *mng = celix_bundleContext_getDependencyManager(ctx);
celix_dependencyManager_removeAllComponents(mng);
if (act->phase2bCmp != NULL) {
- phase2b_destroy(act->phase2bCmp);
+ phase2b_destroy(act->phase2bCmp); //destroy component implementation
}
return CELIX_SUCCESS;
}
diff --git a/examples/celix-examples/dm_example/phase3/src/phase3_activator.c b/examples/celix-examples/dm_example/phase3/src/phase3_activator.c
index f699454f..77453c5c 100644
--- a/examples/celix-examples/dm_example/phase3/src/phase3_activator.c
+++ b/examples/celix-examples/dm_example/phase3/src/phase3_activator.c
@@ -36,7 +36,8 @@ static celix_status_t activator_start(struct phase3_activator_struct *act, celix
celix_dm_component_t *cmp = celix_dmComponent_create(ctx, "PHASE3_PROCESSING_COMPONENT");
celix_dmComponent_setImplementation(cmp, act->phase3Cmp);
- CELIX_DMCOMPONENT_SETCALLBACKS(cmp, phase3_cmp_t *, phase3_init, phase3_start, phase3_stop, phase3_deinit);
+ CELIX_DM_COMPONENT_SET_CALLBACKS(cmp, phase3_cmp_t, phase3_init, phase3_start, phase3_stop, phase3_deinit);
+ CELIX_DM_COMPONENT_SET_IMPLEMENTATION_DESTROY_FUNCTION(cmp, phase3_cmp_t, phase3_destroy);
celix_dm_service_dependency_t *dep = celix_dmServiceDependency_create();
celix_dmServiceDependency_setService(dep, PHASE2_NAME, NULL, NULL);
@@ -57,16 +58,9 @@ static celix_status_t activator_start(struct phase3_activator_struct *act, celix
}
-static celix_status_t activator_stop(struct phase3_activator_struct *act, celix_bundle_context_t *ctx) {
- celix_status_t status = CELIX_SUCCESS;
+static celix_status_t activator_stop(struct phase3_activator_struct *act __attribute__((unused)), celix_bundle_context_t *ctx) {
printf("phase3: stop\n");
- celix_dependency_manager_t *mng = celix_bundleContext_getDependencyManager(ctx);
- celix_dependencyManager_removeAllComponents(mng);
- if (act->phase3Cmp != NULL) {
- phase3_destroy(act->phase3Cmp);
- }
- return status;
+ return CELIX_SUCCESS;
}
-
-CELIX_GEN_BUNDLE_ACTIVATOR(struct phase3_activator_struct, activator_start, activator_stop);
\ No newline at end of file
+CELIX_GEN_BUNDLE_ACTIVATOR(struct phase3_activator_struct, activator_start, activator_stop);
diff --git a/examples/celix-examples/readme_c_examples/CMakeLists.txt b/examples/celix-examples/readme_c_examples/CMakeLists.txt
index 4ea4d406..2b7d3c39 100644
--- a/examples/celix-examples/readme_c_examples/CMakeLists.txt
+++ b/examples/celix-examples/readme_c_examples/CMakeLists.txt
@@ -53,6 +53,23 @@ add_celix_bundle(tracker_command_services_bundle
)
target_link_libraries(tracker_command_services_bundle PRIVATE Celix::shell_api)
+add_celix_bundle(simple_component_bundle
+ VERSION 1.0.0
+ SOURCES src/simple_component_activator.c
+)
+
+add_celix_bundle(component_with_provided_service_bundle
+ VERSION 1.0.0
+ SOURCES src/component_with_provided_service_activator.c
+)
+target_link_libraries(component_with_provided_service_bundle PRIVATE Celix::shell_api)
+
+add_celix_bundle(component_with_service_dependency_bundle
+ VERSION 1.0.0
+ SOURCES src/component_with_service_dependency_activator.c
+)
+target_link_libraries(component_with_service_dependency_bundle PRIVATE Celix::shell_api)
+
add_celix_container(readme_c_examples_container
C
BUNDLES
@@ -61,4 +78,7 @@ add_celix_container(readme_c_examples_container
my_shell_command_provider_bundle
using_command_service_bundle
tracker_command_services_bundle
+ simple_component_bundle
+ component_with_provided_service_bundle
+ component_with_service_dependency_bundle
)
\ No newline at end of file
diff --git a/examples/celix-examples/readme_c_examples/src/component_with_provided_service_activator.c b/examples/celix-examples/readme_c_examples/src/component_with_provided_service_activator.c
new file mode 100644
index 00000000..5c79493b
--- /dev/null
+++ b/examples/celix-examples/readme_c_examples/src/component_with_provided_service_activator.c
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+
+//src/component_with_provided_service_activator.c
+#include <stdlib.h>
+#include <celix_api.h>
+#include <celix_shell_command.h>
+
+//********************* COMPONENT *******************************/
+
+typedef struct component_with_provided_service {
+ int callCount; //atomic
+} component_with_provided_service_t;
+
+static component_with_provided_service_t* componentWithProvidedService_create() {
+ component_with_provided_service_t* cmp = calloc(1, sizeof(*cmp));
+ return cmp;
+}
+
+static void componentWithProvidedService_destroy(component_with_provided_service_t* cmp) {
+ free(cmp);
+}
+
+static bool componentWithProvidedService_executeCommand(
+ component_with_provided_service_t *cmp,
+ const char *commandLine,
+ FILE *outStream,
+ FILE *errorStream __attribute__((unused))) {
+ int count = __atomic_add_fetch(&cmp->callCount, 1, __ATOMIC_SEQ_CST);
+ fprintf(outStream, "Hello from cmp. command called %i times. commandLine: %s\n", count, commandLine);
+ return true;
+}
+
+//********************* ACTIVATOR *******************************/
+
+typedef struct component_with_provided_service_activator {
+ celix_shell_command_t shellCmd; // <-----------------------------------------------------------------------------<1>
+} component_with_provided_service_activator_t;
+
+static celix_status_t componentWithProvidedServiceActivator_start(component_with_provided_service_activator_t *act, celix_bundle_context_t *ctx) {
+ //creating component
+ component_with_provided_service_t* impl = componentWithProvidedService_create();
+
+ //create and configuring component and its lifecycle callbacks using the Apache Celix Dependency Manager
+ celix_dm_component_t* dmCmp = celix_dmComponent_create(ctx, "component_with_provided_service_1");
+ celix_dmComponent_setImplementation(dmCmp, impl);
+ CELIX_DM_COMPONENT_SET_IMPLEMENTATION_DESTROY_FUNCTION(
+ dmCmp,
+ component_with_provided_service_t,
+ componentWithProvidedService_destroy);
+
+ //configure provided service
+ act->shellCmd.handle = impl;
+ act->shellCmd.executeCommand = (void*)componentWithProvidedService_executeCommand;
+ celix_properties_t* props = celix_properties_create();
+ celix_properties_set(props, CELIX_SHELL_COMMAND_NAME, "hello_component");
+ celix_dmComponent_addInterface(
+ dmCmp,
+ CELIX_SHELL_COMMAND_SERVICE_NAME,
+ CELIX_SHELL_COMMAND_SERVICE_VERSION,
+ &act->shellCmd,
+ props); // <---------------------------------------------------------------------------------------------<2>
+
+
+ //Add dm component to the dm.
+ celix_dependency_manager_t* dm = celix_bundleContext_getDependencyManager(ctx);
+ celix_dependencyManager_add(dm, dmCmp);
+ return CELIX_SUCCESS;
+}
+
+CELIX_GEN_BUNDLE_ACTIVATOR(
+ component_with_provided_service_activator_t,
+ componentWithProvidedServiceActivator_start,
+ NULL)
diff --git a/examples/celix-examples/readme_c_examples/src/component_with_service_dependency_activator.c b/examples/celix-examples/readme_c_examples/src/component_with_service_dependency_activator.c
new file mode 100644
index 00000000..e44347e1
--- /dev/null
+++ b/examples/celix-examples/readme_c_examples/src/component_with_service_dependency_activator.c
@@ -0,0 +1,123 @@
+/*
+ * 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.
+ */
+
+//src/component_with_service_dependency_activator.c
+#include <stdlib.h>
+#include <celix_api.h>
+#include <celix_shell_command.h>
+
+//********************* COMPONENT *******************************/
+
+typedef struct component_with_service_dependency {
+ celix_shell_command_t* highestRankingCmdShell; //only updated when component is not active or suspended
+ celix_thread_mutex_t mutex;
+ celix_array_list_t* cmdShells;
+} component_with_service_dependency_t;
+
+static component_with_service_dependency_t* componentWithServiceDependency_create() {
+ component_with_service_dependency_t* cmp = calloc(1, sizeof(*cmp));
+ celixThreadMutex_create(&cmp->mutex, NULL);
+ cmp->cmdShells = celix_arrayList_create();
+ return cmp;
+}
+
+static void componentWithServiceDependency_destroy(component_with_service_dependency_t* cmp) {
+ celix_arrayList_destroy(cmp->cmdShells);
+ celixThreadMutex_destroy(&cmp->mutex);
+ free(cmp);
+}
+
+static void componentWithServiceDependency_setHighestRankingShellCommand(
+ component_with_service_dependency_t* cmp,
+ celix_shell_command_t* shellCmd) {
+ printf("New highest ranking service (can be NULL): %p\n", shellCmd);
+ cmp->highestRankingCmdShell = shellCmd;
+}
+
+static void componentWithServiceDependency_addShellCommand(
+ component_with_service_dependency_t* cmp,
+ celix_shell_command_t* shellCmd,
+ const celix_properties_t* props) {
+ long id = celix_properties_getAsLong(props, CELIX_FRAMEWORK_SERVICE_ID, -1);
+ printf("Adding shell command service with service.id %li\n", id);
+ celixThreadMutex_lock(&cmp->mutex);
+ celix_arrayList_add(cmp->cmdShells, shellCmd);
+ celixThreadMutex_unlock(&cmp->mutex);
+}
+
+static void componentWithServiceDependency_removeShellCommand(
+ component_with_service_dependency_t* cmp,
+ celix_shell_command_t* shellCmd,
+ const celix_properties_t* props) {
+ long id = celix_properties_getAsLong(props, CELIX_FRAMEWORK_SERVICE_ID, -1);
+ printf("Removing shell command service with service.id %li\n", id);
+ celixThreadMutex_lock(&cmp->mutex);
+ celix_arrayList_remove(cmp->cmdShells, shellCmd);
+ celixThreadMutex_unlock(&cmp->mutex);
+}
+
+//********************* ACTIVATOR *******************************/
+
+typedef struct component_with_service_dependency_activator {
+ //nop
+} component_with_service_dependency_activator_t;
+
+static celix_status_t componentWithServiceDependencyActivator_start(component_with_service_dependency_activator_t *act, celix_bundle_context_t *ctx) {
+ //creating component
+ component_with_service_dependency_t* impl = componentWithServiceDependency_create();
+
+ //create and configuring component and its lifecycle callbacks using the Apache Celix Dependency Manager
+ celix_dm_component_t* dmCmp = celix_dmComponent_create(ctx, "component_with_service_dependency_1");
+ celix_dmComponent_setImplementation(dmCmp, impl);
+ CELIX_DM_COMPONENT_SET_IMPLEMENTATION_DESTROY_FUNCTION(
+ dmCmp,
+ component_with_service_dependency_t,
+ componentWithServiceDependency_destroy);
+
+ //create mandatory service dependency with cardinality one and with a suspend-strategy
+ celix_dm_service_dependency_t* dep1 = celix_dmServiceDependency_create();
+ celix_dmServiceDependency_setService(dep1, CELIX_SHELL_COMMAND_SERVICE_NAME, NULL, NULL);
+ celix_dmServiceDependency_setStrategy(dep1, DM_SERVICE_DEPENDENCY_STRATEGY_SUSPEND);
+ celix_dmServiceDependency_setRequired(dep1, true);
+ celix_dm_service_dependency_callback_options_t opts1 = CELIX_EMPTY_DM_SERVICE_DEPENDENCY_CALLBACK_OPTIONS;
+ opts1.set = (void*)componentWithServiceDependency_setHighestRankingShellCommand;
+ celix_dmServiceDependency_setCallbacksWithOptions(dep1, &opts1);
+ celix_dmComponent_addServiceDependency(dmCmp, dep1);
+
+ //create optional service dependency with cardinality many and with a locking-strategy
+ celix_dm_service_dependency_t* dep2 = celix_dmServiceDependency_create();
+ celix_dmServiceDependency_setService(dep2, CELIX_SHELL_COMMAND_SERVICE_NAME, NULL, NULL);
+ celix_dmServiceDependency_setStrategy(dep2, DM_SERVICE_DEPENDENCY_STRATEGY_LOCKING);
+ celix_dmServiceDependency_setRequired(dep2, false);
+ celix_dm_service_dependency_callback_options_t opts2 = CELIX_EMPTY_DM_SERVICE_DEPENDENCY_CALLBACK_OPTIONS;
+ opts2.addWithProps = (void*)componentWithServiceDependency_addShellCommand;
+ opts2.removeWithProps = (void*)componentWithServiceDependency_removeShellCommand;
+ celix_dmServiceDependency_setCallbacksWithOptions(dep2, &opts2);
+ celix_dmComponent_addServiceDependency(dmCmp, dep2);
+
+ //Add dm component to the dm.
+ celix_dependency_manager_t* dm = celix_bundleContext_getDependencyManager(ctx);
+ celix_dependencyManager_add(dm, dmCmp);
+ return CELIX_SUCCESS;
+}
+
+CELIX_GEN_BUNDLE_ACTIVATOR(
+ component_with_service_dependency_activator_t,
+ componentWithServiceDependencyActivator_start,
+ NULL)
diff --git a/examples/celix-examples/readme_c_examples/src/simple_component_activator.c b/examples/celix-examples/readme_c_examples/src/simple_component_activator.c
new file mode 100644
index 00000000..0c21fcff
--- /dev/null
+++ b/examples/celix-examples/readme_c_examples/src/simple_component_activator.c
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+//src/simple_component_activator.c
+#include <stdio.h>
+#include <celix_api.h>
+
+//********************* COMPONENT *******************************/
+
+typedef struct simple_component {
+ int transitionCount; //not protected, only updated and read in the celix event thread.
+} simple_component_t;
+
+static simple_component_t* simpleComponent_create() {
+ simple_component_t* cmp = calloc(1, sizeof(*cmp));
+ cmp->transitionCount = 1;
+ return cmp;
+}
+
+static void simpleComponent_destroy(simple_component_t* cmp) {
+ free(cmp);
+}
+
+static int simpleComponent_init(simple_component_t* cmp) { // <------------------------------------------------------<1>
+ printf("Initializing simple component. Transition nr %i\n", cmp->transitionCount++);
+ return 0;
+}
+
+static int simpleComponent_start(simple_component_t* cmp) {
+ printf("Starting simple component. Transition nr %i\n", cmp->transitionCount++);
+ return 0;
+}
+
+static int simpleComponent_stop(simple_component_t* cmp) {
+ printf("Stopping simple component. Transition nr %i\n", cmp->transitionCount++);
+ return 0;
+}
+
+static int simpleComponent_deinit(simple_component_t* cmp) {
+ printf("De-initializing simple component. Transition nr %i\n", cmp->transitionCount++);
+ return 0;
+}
+
+
+//********************* ACTIVATOR *******************************/
+
+typedef struct simple_component_activator {
+ //nop
+} simple_component_activator_t;
+
+static celix_status_t simpleComponentActivator_start(simple_component_activator_t *act, celix_bundle_context_t *ctx) {
+ //creating component
+ simple_component_t* impl = simpleComponent_create(); // <--------------------------------------------------------<2>
+
+ //create and configuring component and its lifecycle callbacks using the Apache Celix Dependency Manager
+ celix_dm_component_t* dmCmp = celix_dmComponent_create(ctx, "simple_component_1"); // <--------------------------<3>
+ celix_dmComponent_setImplementation(dmCmp, impl); // <-----------------------------------------------------------<4>
+ CELIX_DM_COMPONENT_SET_CALLBACKS(
+ dmCmp,
+ simple_component_t,
+ simpleComponent_init,
+ simpleComponent_start,
+ simpleComponent_stop,
+ simpleComponent_deinit); // <----------------------------------------------------------------------------<5>
+ CELIX_DM_COMPONENT_SET_IMPLEMENTATION_DESTROY_FUNCTION(
+ dmCmp,
+ simple_component_t,
+ simpleComponent_destroy); // <---------------------------------------------------------------------------<6>
+
+ //Add dm component to the dm.
+ celix_dependency_manager_t* dm = celix_bundleContext_getDependencyManager(ctx);
+ celix_dependencyManager_add(dm, dmCmp); // <---------------------------------------------------------------------<7>
+ return CELIX_SUCCESS;
+}
+
+CELIX_GEN_BUNDLE_ACTIVATOR(simple_component_activator_t, simpleComponentActivator_start, NULL) // <------------------<8>
\ No newline at end of file
diff --git a/examples/celix-examples/readme_cxx_examples/CMakeLists.txt b/examples/celix-examples/readme_cxx_examples/CMakeLists.txt
index 0548072c..14d7fa6a 100644
--- a/examples/celix-examples/readme_cxx_examples/CMakeLists.txt
+++ b/examples/celix-examples/readme_cxx_examples/CMakeLists.txt
@@ -115,12 +115,29 @@ add_celix_bundle(UsingCommandServicesExampleBundle
target_link_libraries(UsingCommandServicesExampleBundle PRIVATE Celix::shell_api)
add_celix_bundle(TrackingCommandServicesExampleBundle
- VERSION 1.0.0
+ VERSION 1.0.0
SOURCES
src/TrackingCommandServicesExample.cc
)
target_link_libraries(TrackingCommandServicesExampleBundle PRIVATE Celix::shell_api)
+add_celix_bundle(SimpleComponentBundle
+ VERSION 1.0.0
+ SOURCES src/SimpleComponentActivator.cc
+)
+
+add_celix_bundle(ComponentWithProvidedServiceBundle
+ VERSION 1.0.0
+ SOURCES src/ComponentWithProvidedServiceActivator.cc
+)
+target_link_libraries(ComponentWithProvidedServiceBundle PRIVATE Celix::shell_api)
+
+add_celix_bundle(ComponentWithServiceDependencyBundle
+ VERSION 1.0.0
+ SOURCES src/ComponentWithServiceDependencyActivator.cc
+)
+target_link_libraries(ComponentWithServiceDependencyBundle PRIVATE Celix::shell_api)
+
add_celix_container(ReadmeCxxExamplesContainer
BUNDLES
Celix::ShellCxx
@@ -129,6 +146,9 @@ add_celix_container(ReadmeCxxExamplesContainer
MyCShellCommandProviderBundle
UsingCommandServicesExampleBundle
TrackingCommandServicesExampleBundle
+ SimpleComponentBundle
+ ComponentWithProvidedServiceBundle
+ ComponentWithServiceDependencyBundle
)
if (TARGET my_shell_command_provider_bundle)
diff --git a/examples/celix-examples/readme_cxx_examples/src/ComponentWithProvidedServiceActivator.cc b/examples/celix-examples/readme_cxx_examples/src/ComponentWithProvidedServiceActivator.cc
new file mode 100644
index 00000000..0095a73a
--- /dev/null
+++ b/examples/celix-examples/readme_cxx_examples/src/ComponentWithProvidedServiceActivator.cc
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+//src/ComponentWithProvidedServiceActivator.cc
+#include <celix/BundleActivator.h>
+#include <celix/IShellCommand.h>
+#include <celix_shell_command.h>
+
+class ComponentWithProvidedService : public celix::IShellCommand { // <----------------------------------------------<1>
+public:
+ ~ComponentWithProvidedService() noexcept override = default;
+
+ void executeCommand(
+ const std::string& commandLine,
+ const std::vector<std::string>& /*commandArgs*/,
+ FILE* outStream,
+ FILE* /*errorStream*/) override {
+ fprintf(outStream, "Hello from cmp. C++ command called %i times. commandLine is %s\n", cxxCallCount++, commandLine.c_str());
+ } // <-----------------------------------------------------------------------------------------------------------<2>
+
+ void executeCCommand(const char* commandLine, FILE* outStream) {
+ fprintf(outStream, "Hello from cmp. C command called %i times. commandLine is %s\n", cCallCount++, commandLine);
+ } // <-----------------------------------------------------------------------------------------------------------<3>
+private:
+ std::atomic<int> cxxCallCount{1};
+ std::atomic<int> cCallCount{1};
+};
+
+class ComponentWithProvidedServiceActivator {
+public:
+ explicit ComponentWithProvidedServiceActivator(const std::shared_ptr<celix::BundleContext>& ctx) {
+ auto& cmp = ctx->getDependencyManager()->createComponent<ComponentWithProvidedService>(); // <---------------<4>
+
+ cmp.createProvidedService<celix::IShellCommand>()
+ .addProperty(celix::IShellCommand::COMMAND_NAME, "HelloComponent"); // <---------------------------------<5>
+
+ shellCmd.handle = static_cast<void*>(&cmp.getInstance());
+ shellCmd.executeCommand = [](void* handle, const char* commandLine, FILE* outStream, FILE*) -> bool {
+ auto* impl = static_cast<ComponentWithProvidedService*>(handle);
+ impl->executeCCommand(commandLine, outStream);
+ return true;
+ }; // <------------------------------------------------------------------------------------------------------<6>
+
+ cmp.createProvidedCService(&shellCmd, CELIX_SHELL_COMMAND_SERVICE_NAME)
+ .addProperty(CELIX_SHELL_COMMAND_NAME, "hello_component"); // < -----------------------------------------<7>
+
+ cmp.build(); // <--------------------------------------------------------------------------------------------<8>
+ }
+private:
+ celix_shell_command_t shellCmd{nullptr, nullptr};
+};
+
+CELIX_GEN_CXX_BUNDLE_ACTIVATOR(ComponentWithProvidedServiceActivator)
diff --git a/examples/celix-examples/readme_cxx_examples/src/ComponentWithServiceDependencyActivator.cc b/examples/celix-examples/readme_cxx_examples/src/ComponentWithServiceDependencyActivator.cc
new file mode 100644
index 00000000..1ba31500
--- /dev/null
+++ b/examples/celix-examples/readme_cxx_examples/src/ComponentWithServiceDependencyActivator.cc
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+//src/ComponentWithServiceDependencyActivator.cc
+#include <celix/BundleActivator.h>
+#include <celix/IShellCommand.h>
+#include <celix_shell_command.h>
+
+class ComponentWithServiceDependency {
+public:
+ void setHighestRankingShellCommand(const std::shared_ptr<celix::IShellCommand>& cmdSvc) {
+ std::cout << "New highest ranking service (can be NULL): " << (intptr_t)cmdSvc.get() << std::endl;
+ highestRankingShellCmd = cmdSvc;
+ }
+
+ void addCShellCmd(
+ const std::shared_ptr<celix_shell_command_t>& cmdSvc,
+ const std::shared_ptr<const celix::Properties>& props) {
+ auto id = props->getAsLong(celix::SERVICE_ID, -1);
+ std::cout << "Adding shell command service with service.id: " << id << std::endl;
+ std::lock_guard lck{mutex};
+ shellCommands.emplace(id, cmdSvc);
+ }
+
+ void removeCShellCmd(
+ const std::shared_ptr<celix_shell_command_t>& /*cmdSvc*/,
+ const std::shared_ptr<const celix::Properties>& props) {
+ auto id = props->getAsLong(celix::SERVICE_ID, -1);
+ std::cout << "Removing shell command service with service.id: " << id << std::endl;
+ std::lock_guard lck{mutex};
+ shellCommands.erase(id);
+ }
+private:
+ std::shared_ptr<celix::IShellCommand> highestRankingShellCmd{};
+ std::mutex mutex{}; //protect below
+ std::unordered_map<long, std::shared_ptr<celix_shell_command_t>> shellCommands{};
+};
+
+class ComponentWithServiceDependencyActivator {
+public:
+ explicit ComponentWithServiceDependencyActivator(const std::shared_ptr<celix::BundleContext>& ctx) {
+ using Cmp = ComponentWithServiceDependency;
+ auto& cmp = ctx->getDependencyManager()->createComponent<Cmp>(); // <-------------<1>
+
+ cmp.createServiceDependency<celix::IShellCommand>()
+ .setCallbacks(&Cmp::setHighestRankingShellCommand)
+ .setRequired(true)
+ .setStrategy(DependencyUpdateStrategy::suspend);
+
+ cmp.createServiceDependency<celix_shell_command_t>(CELIX_SHELL_COMMAND_SERVICE_NAME)
+ .setCallbacks(&Cmp::addCShellCmd, &Cmp::removeCShellCmd)
+ .setRequired(false)
+ .setStrategy(DependencyUpdateStrategy::locking);
+
+ cmp.build(); // <--------------------------------------------------------------------------------------------<8>
+ }
+};
+
+CELIX_GEN_CXX_BUNDLE_ACTIVATOR(ComponentWithServiceDependencyActivator)
diff --git a/examples/celix-examples/readme_cxx_examples/src/SimpleComponentActivator.cc b/examples/celix-examples/readme_cxx_examples/src/SimpleComponentActivator.cc
new file mode 100644
index 00000000..d1cfcd6c
--- /dev/null
+++ b/examples/celix-examples/readme_cxx_examples/src/SimpleComponentActivator.cc
@@ -0,0 +1,57 @@
+/*
+ * 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>
+
+class SimpleComponent {
+public:
+ void init() {
+ std::cout << "Initializing SimpleComponent. Transition nr " << transitionCount++ << std::endl;
+ }
+
+ void start() {
+ std::cout << "starting SimpleComponent. Transition nr " << transitionCount++ << std::endl;
+ }
+
+ void stop() {
+ std::cout << "Stopping SimpleComponent. Transition nr " << transitionCount++ << std::endl;
+ }
+
+ void deinit() {
+ std::cout << "De-initializing SimpleComponent. Transition nr " << transitionCount++ << std::endl;
+ }
+private:
+ int transitionCount = 1; //not protected, only updated and read in the celix event thread.
+};
+
+class SimpleComponentActivator {
+public:
+ explicit SimpleComponentActivator(const std::shared_ptr<celix::BundleContext>& ctx) {
+ auto cmp = std::make_unique<SimpleComponent>();
+ ctx->getDependencyManager()->createComponent(std::move(cmp), "SimpleComponent1")
+ .setCallbacks(
+ &SimpleComponent::init,
+ &SimpleComponent::start,
+ &SimpleComponent::stop,
+ &SimpleComponent::deinit)
+ .build();
+ }
+};
+
+CELIX_GEN_CXX_BUNDLE_ACTIVATOR(SimpleComponentActivator)
diff --git a/libs/framework/gtest/src/DependencyManagerTestSuite.cc b/libs/framework/gtest/src/DependencyManagerTestSuite.cc
index 5cb65b0b..db217ad9 100644
--- a/libs/framework/gtest/src/DependencyManagerTestSuite.cc
+++ b/libs/framework/gtest/src/DependencyManagerTestSuite.cc
@@ -86,6 +86,43 @@ TEST_F(DependencyManagerTestSuite, DmComponentAddRemove) {
ASSERT_EQ(0, celix_dependencyManager_nrOfComponents(mng));
}
+TEST_F(DependencyManagerTestSuite, DmComponentsWithConfiguredDestroyFunction) {
+ //Given a simple component implementation (only a name field)
+ struct CmpImpl {
+ std::string name;
+ };
+
+ void(*destroyFn)(CmpImpl*) = [](CmpImpl* impl) {
+ std::cout << "Destroying " << impl->name << std::endl;
+ delete impl;
+ };
+
+ //When 10 of these components impls are created and configured (include destroy impl function)
+ //as component in the DM.
+ auto* dm = celix_bundleContext_getDependencyManager(ctx);
+ for (int i = 0; i < 10; ++i) {
+ auto* impl = new CmpImpl{std::string{"Component"} + std::to_string(i+1)};
+ auto* dmCmp = celix_dmComponent_create(ctx, impl->name.c_str());
+ celix_dmComponent_setImplementation(dmCmp, impl);
+ CELIX_DM_COMPONENT_SET_IMPLEMENTATION_DESTROY_FUNCTION(dmCmp, CmpImpl, destroyFn);
+ celix_dependencyManager_addAsync(dm, dmCmp);
+ }
+
+ //Then all component should become activate (note components with no svc dependencies)
+ celix_dependencyManager_wait(dm);
+ celix_dependencyManager_allComponentsActive(dm);
+ EXPECT_EQ(celix_dependencyManager_nrOfComponents(dm), 10);
+
+ //When all 10 components are removed
+ celix_dependencyManager_removeAllComponents(dm);
+
+ //Then all components should be removed
+ EXPECT_EQ(celix_dependencyManager_nrOfComponents(dm), 0);
+
+ //And when the test goes out of scope, no memory should be leaked
+ //nop
+}
+
TEST_F(DependencyManagerTestSuite, DmComponentAddRemoveAsync) {
auto *mng = celix_bundleContext_getDependencyManager(ctx);
diff --git a/libs/framework/include/celix_bundle_activator.h b/libs/framework/include/celix_bundle_activator.h
index bab054c3..19dc1770 100644
--- a/libs/framework/include/celix_bundle_activator.h
+++ b/libs/framework/include/celix_bundle_activator.h
@@ -20,6 +20,7 @@
#include <stdlib.h>
#include "celix_bundle_context.h"
+#include "celix_dependency_manager.h"
#ifndef CELIX_BUNDLE_ACTIVATOR_H_
#define CELIX_BUNDLE_ACTIVATOR_H_
@@ -103,11 +104,6 @@ celix_status_t celix_bundleActivator_stop(void *userData, celix_bundle_context_t
*/
celix_status_t celix_bundleActivator_destroy(void *userData, celix_bundle_context_t* ctx);
-/**
- * @brief Returns the C bundle context.
- */
-celix_bundle_context_t* celix_bundleActivator_getBundleContext();
-
/**
* @brief This macro generates the required bundle activator functions for C.
*
@@ -118,9 +114,16 @@ celix_bundle_context_t* celix_bundleActivator_getBundleContext();
* - bundleActivator_start/stop which will call the respectively provided typed start/stop functions.
* - bundleActivator_destroy will free the allocated for the provided type.
*
- * @param type The activator type (e.g. 'struct shell_activator').
- * @param start the activator actStart function with the following signature: celix_status_t (*)(<actType>*, celix_bundle_context_t*).
- * @param stop the activator actStop function with the following signature: celix_status_t (*)(<actType>*, celix_bundle_context_t*).
+ * After the (optional) provided activator stop callback is called, the generated `celix_bundleActivator_stop`
+ * function will remove all components from the bundle's dependency manager
+ * (using `celix_dependencyManager_removeAllComponents). This will ensure that dependency manager components can be
+ * used without explicitly programming their removal and destroy functionality.
+ *
+ * @param actType The activator type (e.g. 'struct shell_activator').
+ * @param actStart The optional activator actStart function with the following signature:
+ * celix_status_t (*)(<actType>*, celix_bundle_context_t*).
+ * @param actStop The optional activator actStop function with the following signature:
+ * celix_status_t (*)(<actType>*, celix_bundle_context_t*).
*/
#define CELIX_GEN_BUNDLE_ACTIVATOR(actType, actStart, actStop) \
\
@@ -136,11 +139,23 @@ celix_status_t celix_bundleActivator_create(celix_bundle_context_t *ctx, void **
} \
\
celix_status_t celix_bundleActivator_start(void *userData, celix_bundle_context_t *ctx) { \
- return actStart((actType*)userData, ctx); \
+ celix_status_t status = CELIX_SUCCESS; \
+ celix_status_t (*fn)(actType*, celix_bundle_context_t*) = (actStart); \
+ if (fn != NULL) { \
+ status = fn((actType*)userData, ctx); \
+ } \
+ return status; \
} \
\
celix_status_t celix_bundleActivator_stop(void *userData, celix_bundle_context_t *ctx) { \
- return actStop((actType*)userData, ctx); \
+ celix_status_t status = CELIX_SUCCESS; \
+ celix_status_t (*fn)(actType*, celix_bundle_context_t*) = (actStop); \
+ if (fn != NULL) { \
+ status = fn((actType*)userData, ctx); \
+ } \
+ celix_dependency_manager_t* mng = celix_bundleContext_getDependencyManager(ctx); \
+ celix_dependencyManager_removeAllComponents(mng); \
+ return status; \
} \
\
celix_status_t celix_bundleActivator_destroy(void *userData, celix_bundle_context_t *ctx) { \
diff --git a/libs/framework/include/celix_dependency_manager.h b/libs/framework/include/celix_dependency_manager.h
index d4cf37a5..8e369375 100644
--- a/libs/framework/include/celix_dependency_manager.h
+++ b/libs/framework/include/celix_dependency_manager.h
@@ -45,9 +45,12 @@ extern "C" {
/**
* Adds a DM component to the dependency manager
*
- * After this call the components will be created and if the components can be started, they
+ * After this call the components will be build and if the components can be started, they
* will be started and the services will be registered.
*
+ * After this call the dependency manager has ownership of the component and the dependency manager will also
+ * de-active and destroy the component when the dependency manager is destroyed.
+ *
* Should not be called from the Celix event thread.
*/
celix_status_t celix_dependencyManager_add(celix_dependency_manager_t *manager, celix_dm_component_t *component);
@@ -62,16 +65,15 @@ celix_status_t celix_dependencyManager_addAsync(celix_dependency_manager_t *mana
/**
* Removes a DM component from the dependency manager and destroys it
*
- * After this call the components will be destroyed and if the components was started, the service registrations
- * and service tracked of this component will be unregistered and closed.
+ * After this call - and if the component was found - the component will be destroyed and if the component was started,
+ * the component will have been stopped and de-initialized.
*
* Should not be called from the Celix event thread.
*/
celix_status_t celix_dependencyManager_remove(celix_dependency_manager_t *manager, celix_dm_component_t *component);
/**
- * Same as celix_dependencyManager_remove, but this call will not wait until all service registrations and
- * tracker are unregistered/closed on the Celix event thread.
+ * Same as celix_dependencyManager_remove, but this call will not wait until component is deactivated.
* Can be called on the Celix event thread.
*
* The doneCallback will be called (if not NULL) with doneData as argument when the component is removed
diff --git a/libs/framework/include/celix_dm_component.h b/libs/framework/include/celix_dm_component.h
index 8302dca0..6f0b0d5a 100644
--- a/libs/framework/include/celix_dm_component.h
+++ b/libs/framework/include/celix_dm_component.h
@@ -61,6 +61,7 @@ typedef enum celix_dm_component_state_enum {
#define CELIX_DM_COMPONENT_MAX_NAME_LENGTH 128
typedef int (*celix_dm_cmp_lifecycle_fpt)(void *userData);
+typedef void (*celix_dm_cmp_impl_destroy_fpt)(void*);
/**
* Creates a DM Component with a random generated UUID.
@@ -82,14 +83,14 @@ const char* celix_dmComponent_getUUID(celix_dm_component_t* cmp);
/**
* Destroys a DM Component
*/
-void celix_dmComponent_destroy(celix_dm_component_t *cmp);
+void celix_dmComponent_destroy(celix_dm_component_t* cmp);
/**
* Destroys a DM Component on the event thread.
* Will call doneCallback (if not NULL) when done.
*
*/
-void celix_dmComponent_destroyAsync(celix_dm_component_t *cmp, void *doneData, void (*doneCallback)(void*));
+void celix_dmComponent_destroyAsync(celix_dm_component_t* cmp, void *doneData, void (*doneCallback)(void*));
/**
* Specify if a default 'service.lang=C' should be added to the properties of interfaces if no 'service.lang' has been
@@ -119,6 +120,26 @@ celix_status_t celix_dmComponent_removeInterface(celix_dm_component_t *component
*/
celix_status_t celix_dmComponent_setImplementation(celix_dm_component_t *component, void* implementation);
+/**
+ * Configures the destroy function for the component implementation.
+ *
+ * If a destroy function for the component implementation is configured, this will be used
+ * when the component is removed from the dependency manager and component is successfully de-activated.
+ *
+ * The destroy function will not be called if the component implementation is not set. e.g. if the
+ * celix_dmComponent_setImplementation is not called with a non NULL value.
+ */
+void celix_dmComponent_setImplementationDestroyFunction(celix_dm_component_t* cmp, celix_dm_cmp_impl_destroy_fpt destroyFn);
+
+/**
+ * Configures the destroy function for the component implementation using a MACRO for improving the type safety.
+ */
+#define CELIX_DM_COMPONENT_SET_IMPLEMENTATION_DESTROY_FUNCTION(dmCmp, type, destroy) \
+ do { \
+ void (*_destroyFunction)(type*) = (destroy); \
+ celix_dmComponent_setImplementationDestroyFunction((dmCmp), (void(*)(void*))_destroyFunction); \
+ } while(0)
+
/**
* Returns an arraylist of service names. The caller owns the arraylist and strings (char *)
*/
@@ -137,17 +158,22 @@ celix_status_t celix_dmComponent_removeServiceDependency(celix_dm_component_t *c
/**
* Returns the current state of the component.
*/
-celix_dm_component_state_t celix_dmComponent_currentState(celix_dm_component_t *cmp);
+celix_dm_component_state_t celix_dmComponent_currentState(celix_dm_component_t* cmp);
/**
* Returns the implementation of the component. e.g. the component handle/self/this pointer.
*/
-void * celix_dmComponent_getImplementation(celix_dm_component_t *cmp);
+void * celix_dmComponent_getImplementation(celix_dm_component_t* cmp);
+
+/**
+ * Returns the configured component implementation destroy function.
+ */
+celix_dm_cmp_impl_destroy_fpt celix_dmComponent_getImplementationDestroyFunction(celix_dm_component_t* cmp);
/**
* Returns the DM component name. This is used when printing information about the component.
*/
-const char* celix_dmComponent_getName(celix_dm_component_t *cmp);
+const char* celix_dmComponent_getName(celix_dm_component_t* cmp);
/**
* Returns bundle context for the bundle where this DM component is part of.
@@ -163,15 +189,22 @@ celix_status_t celix_dmComponent_setCallbacks(celix_dm_component_t *component, c
/**
* Set the component life cycle callbacks using a MACRO for improving the type safety.
*/
-#define CELIX_DMCOMPONENT_SETCALLBACKS(dmCmp, type, init, start, stop, deinit) \
+#define CELIX_DM_COMPONENT_SET_CALLBACKS(dmCmp, type, init, start, stop, deinit) \
do { \
- int (*tmp_init)(type) = (init); \
- int (*tmp_start)(type) = (start); \
- int (*tmp_stop)(type) = (stop); \
- int (*tmp_deinit)(type) = (deinit); \
- celix_dmComponent_setCallbacks((dmCmp), (celix_dm_cmp_lifecycle_fpt)tmp_init, (celix_dm_cmp_lifecycle_fpt)tmp_start, (celix_dm_cmp_lifecycle_fpt)tmp_stop, (celix_dm_cmp_lifecycle_fpt)tmp_deinit); \
+ int (*_tmp_init)(type*) = (init); \
+ int (*_tmp_start)(type*) = (start); \
+ int (*_tmp_stop)(type*) = (stop); \
+ int (*_tmp_deinit)(type*) = (deinit); \
+ celix_dmComponent_setCallbacks((dmCmp), (celix_dm_cmp_lifecycle_fpt)_tmp_init, (celix_dm_cmp_lifecycle_fpt)_tmp_start, (celix_dm_cmp_lifecycle_fpt)_tmp_stop, (celix_dm_cmp_lifecycle_fpt)_tmp_deinit); \
} while(0)
+/**
+ * Deprecated, use CELIX_DM_COMPONENT_SET_CALLBACKS instead.
+ */
+
+#define CELIX_DMCOMPONENT_SETCALLBACKS(dmCmp, type, init, start, stop, deinit) \
+ CELIX_DM_COMPONENT_SET_CALLBACKS(dmCmp, type*, init, start, stop, deinit)
+
/**
* Create a DM Component info struct. Containing information about the component.
* Caller has ownership.
diff --git a/libs/framework/src/dm_component_impl.c b/libs/framework/src/dm_component_impl.c
index a951efc1..befa81bb 100644
--- a/libs/framework/src/dm_component_impl.c
+++ b/libs/framework/src/dm_component_impl.c
@@ -35,6 +35,7 @@ struct celix_dm_component_struct {
celix_bundle_context_t* context;
void* implementation;
+ celix_dm_cmp_impl_destroy_fpt implementationDestroyFn;
celix_dm_cmp_lifecycle_fpt callbackInit;
celix_dm_cmp_lifecycle_fpt callbackStart;
@@ -130,6 +131,10 @@ celix_dm_component_t* celix_dmComponent_createWithUUID(bundle_context_t *context
component->callbackStart = NULL;
component->callbackStop = NULL;
component->callbackDeinit = NULL;
+
+ component->implementation= NULL;
+ component->implementationDestroyFn = NULL;
+
component->state = CELIX_DM_CMP_STATE_INACTIVE;
component->providedInterfaces = celix_arrayList_create();
@@ -178,8 +183,21 @@ struct celix_dm_component_destroy_data {
static void celix_dmComponent_destroyCallback(void *voidData) {
struct celix_dm_component_destroy_data *data = voidData;
celix_dm_component_t *component = data->cmp;
- celix_dmComponent_disable(component); //all service deregistered // all svc tracker stopped
+ celix_dmComponent_disable(component); //all service unregistered // all svc tracker stopped
if (celix_dmComponent_isDisabled(component)) {
+ if (component->implementationDestroyFn) {
+ if (component->implementation == NULL) {
+ celix_bundleContext_log(component->context, CELIX_LOG_LEVEL_ERROR,
+ "Component `%s` [uuid=%s] has a configured destroy component "
+ "implementation callback, but the implementation pointer is NULL. "
+ "Destroy callback will not be called!",
+ component->name,
+ component->uuid);
+ } else {
+ component->implementationDestroyFn(component->implementation);
+ }
+ }
+
for (int i = 0; i < celix_arrayList_size(component->providedInterfaces); ++i) {
dm_interface_t *interface = celix_arrayList_get(component->providedInterfaces, i);
@@ -975,6 +993,10 @@ celix_status_t celix_dmComponent_setImplementation(celix_dm_component_t *compone
return CELIX_SUCCESS;
}
+void celix_dmComponent_setImplementationDestroyFunction(celix_dm_component_t* component, celix_dm_cmp_impl_destroy_fpt destroyFn) {
+ component->implementationDestroyFn = destroyFn;
+}
+
celix_status_t component_getBundleContext(celix_dm_component_t *component, bundle_context_pt *context) {
*context = celix_dmComponent_getBundleContext(component);
return *context == NULL ? CELIX_BUNDLE_EXCEPTION : CELIX_SUCCESS;
[celix] 02/02: Merge branch 'feature/additional_component_states' into pnoltes/feature/update_component_and_pattern_documentation
Posted by pn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
pnoltes pushed a commit to branch feature/update_component_and_pattern_documentation
in repository https://gitbox.apache.org/repos/asf/celix.git
commit 9875606da577cb34906c62629366ac7e71651dc6
Merge: 965f7c6e fb0ebbc6
Author: Pepijn Noltes <pe...@gmail.com>
AuthorDate: Sun May 22 22:17:36 2022 +0200
Merge branch 'feature/additional_component_states' into pnoltes/feature/update_component_and_pattern_documentation
documents/diagrams/component_lifecycle.png | Bin 61347 -> 59748 bytes
.../gtest/src/DependencyManagerTestSuite.cc | 66 +++++++++++++++++----
2 files changed, 56 insertions(+), 10 deletions(-)