Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 1FFF5200B58 for ; Wed, 27 Jul 2016 21:18:28 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id 1E792160A6F; Wed, 27 Jul 2016 19:18:28 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 1D499160A90 for ; Wed, 27 Jul 2016 21:18:25 +0200 (CEST) Received: (qmail 86908 invoked by uid 500); 27 Jul 2016 19:18:25 -0000 Mailing-List: contact commits-help@celix.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@celix.apache.org Delivered-To: mailing list commits@celix.apache.org Received: (qmail 86896 invoked by uid 99); 27 Jul 2016 19:18:25 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 27 Jul 2016 19:18:25 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 2416FE02A2; Wed, 27 Jul 2016 19:18:25 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: pnoltes@apache.org To: commits@celix.apache.org Date: Wed, 27 Jul 2016 19:18:25 -0000 Message-Id: <67b6c670b1694c42a37054a6b23b9ba9@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [1/2] celix git commit: CELIX-368: Adds intro documentation. Refactors some doc names / links archived-at: Wed, 27 Jul 2016 19:18:28 -0000 Repository: celix Updated Branches: refs/heads/develop bb1c702d4 -> 41341c957 CELIX-368: Adds intro documentation. Refactors some doc names / links Project: http://git-wip-us.apache.org/repos/asf/celix/repo Commit: http://git-wip-us.apache.org/repos/asf/celix/commit/c710cc60 Tree: http://git-wip-us.apache.org/repos/asf/celix/tree/c710cc60 Diff: http://git-wip-us.apache.org/repos/asf/celix/diff/c710cc60 Branch: refs/heads/develop Commit: c710cc60b56f26b3daf7206533a96427cee52634 Parents: bb1c702 Author: Pepijn Noltes Authored: Wed Jul 27 17:22:47 2016 +0200 Committer: Pepijn Noltes Committed: Wed Jul 27 21:04:12 2016 +0200 ---------------------------------------------------------------------- README.md | 8 +- dependency_manager/README.md | 123 ------- dependency_manager/readme.md | 123 +++++++ dependency_manager_cxx/README.md | 64 ---- dependency_manager_cxx/readme.md | 64 ++++ documents/best_practices/README.md | 585 ------------------------------- documents/best_practices/readme.md | 585 +++++++++++++++++++++++++++++++ documents/building/readme.md | 39 ++- documents/getting_started/readme.md | 2 +- documents/intro/readme.md | 140 ++++++++ documents/subprojects/readme.md | 6 +- 11 files changed, 956 insertions(+), 783 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/celix/blob/c710cc60/README.md ---------------------------------------------------------------------- diff --git a/README.md b/README.md index bef8d53..ad81b4b 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,11 @@ Apache Celix is an implementation of the OSGi specification adapted to C. It will follow the API as close as possible, but since the OSGi specification is written primarily for Java, there will be differences (Java is OO, C is procedural). An important aspect of the implementation is interoperability between Java and C. This interoperability is achieved by porting and implementing the Remote Services specification in Celix. ##Building -For information how to build Apache Celix see BUILDING +For information how to build Apache Celix see [Building Apache Celix](documents/building/readme.md) ##Introduction to Apache Celix -For an introduction into Apache Celix see [Getting Started Guide](https://celix.apache.org/documentation/getting_started.html) +For an introduction into Apache Celix see [Apache Celix Intro](documents/intro/readme.md) + +##Getting Started with Apache Celix +For a guide how to start writing your own bundles and services see [Getting Started Guide](documents/intro/readme.md) + http://git-wip-us.apache.org/repos/asf/celix/blob/c710cc60/dependency_manager/README.md ---------------------------------------------------------------------- diff --git a/dependency_manager/README.md b/dependency_manager/README.md deleted file mode 100644 index 2f9b2fd..0000000 --- a/dependency_manager/README.md +++ /dev/null @@ -1,123 +0,0 @@ -# Apache Celix Dependency Manager - -## 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. - -![Component Life Cycle](doc-images/statediagram.png) - -Changes in the state of the component will trigger the following life cycle callback functions: - - `init`, - `start`, - `stop` and - `deinit`. - -The callback functions can be specified by using the component_setCallbacks. - -## DM Parts - -The Dependency Manager consist out of four main parts: `DM (Dependency Manager) Activator`, `Dependency Manager`, `DM Component` and `DM Service Dependency`. - -### DM Activator - -The `DM Activator` implements a "normal" Celix bundle activator and depends on four functions which needs to be implemented by the user of the Depedency 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. - - -### Dependency Manager - -The `Dependency Manager` act as an entry point to add or remove DM Components. The `Dependency Manager` is provided to the `dm_init` functoin. - -### DM Component - -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. - -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. - -### Dm Service Dependency - -The `DM Service Dependency` can be used to specify service dependencies for a component. i - -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. - -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. - -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). - -### Snippets - -#### DM Bundle Activator - -The next snippet shows a dm bundle activator and how to add components to the dependency manager. -```C - -//exmpl_activator.c -#include -#include - -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; - - act->exmpl = exmpl_create(); - if (act->exmpl != NULL) { - dm_component_pt cmp; - component_create(context, "Example Component", &cmp); - component_setImplementation(cmp, act->exmpl); - - dependencyManager_add(manager, cmp); - } else { - status = CELIX_ENOMEM; - } - - return status; -} - -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; - - if (act->exmpl != NULL) { - exmpl_destroy(act->exmpl); - } - free(act); - - return CELIX_SUCCESS; -} -``` -### References - -For more information examples please see - -- [Best practices](../documents/best_practices/README.md): A introduction how to work with services using the dependency manager -- [The Dependency Manager API](public/include): The dependency manager header files -- [Best practice example](../examples/best_practice_example): A best practice example (also refered to in the Best practices documentation -- [Dm example](../examples/dm_example): A DM example. http://git-wip-us.apache.org/repos/asf/celix/blob/c710cc60/dependency_manager/readme.md ---------------------------------------------------------------------- diff --git a/dependency_manager/readme.md b/dependency_manager/readme.md new file mode 100644 index 0000000..867fe95 --- /dev/null +++ b/dependency_manager/readme.md @@ -0,0 +1,123 @@ +# Apache Celix Dependency Manager + +## 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. + +![Component Life Cycle](doc-images/statediagram.png) + +Changes in the state of the component will trigger the following life cycle callback functions: + + `init`, + `start`, + `stop` and + `deinit`. + +The callback functions can be specified by using the component_setCallbacks. + +## DM Parts + +The Dependency Manager consist out of four main parts: `DM (Dependency Manager) Activator`, `Dependency Manager`, `DM Component` and `DM Service Dependency`. + +### DM Activator + +The `DM Activator` implements a "normal" Celix bundle activator and depends on four functions which needs to be implemented by the user of the Depedency 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. + + +### Dependency Manager + +The `Dependency Manager` act as an entry point to add or remove DM Components. The `Dependency Manager` is provided to the `dm_init` functoin. + +### DM Component + +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. + +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. + +### Dm Service Dependency + +The `DM Service Dependency` can be used to specify service dependencies for a component. i + +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. + +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. + +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). + +### Snippets + +#### DM Bundle Activator + +The next snippet shows a dm bundle activator and how to add components to the dependency manager. +```C + +//exmpl_activator.c +#include +#include + +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; + + act->exmpl = exmpl_create(); + if (act->exmpl != NULL) { + dm_component_pt cmp; + component_create(context, "Example Component", &cmp); + component_setImplementation(cmp, act->exmpl); + + dependencyManager_add(manager, cmp); + } else { + status = CELIX_ENOMEM; + } + + return status; +} + +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; + + if (act->exmpl != NULL) { + exmpl_destroy(act->exmpl); + } + free(act); + + return CELIX_SUCCESS; +} +``` +### References + +For more information examples please see + +- [Best practices](../documents/best_practices/readme.md): A introduction how to work with services using the dependency manager +- [The Dependency Manager API](public/include): The dependency manager header files +- [Best practice example](../examples/best_practice_example): A best practice example (also refered to in the Best practices documentation +- [Dm example](../examples/dm_example): A DM example. http://git-wip-us.apache.org/repos/asf/celix/blob/c710cc60/dependency_manager_cxx/README.md ---------------------------------------------------------------------- diff --git a/dependency_manager_cxx/README.md b/dependency_manager_cxx/README.md deleted file mode 100644 index f147131..0000000 --- a/dependency_manager_cxx/README.md +++ /dev/null @@ -1,64 +0,0 @@ -# Apache Celix C++ Dependency Manager - -## Introduction - -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). - -The C++ Dependency Manager uses fluent interface to make specifying DM components and service dependencies very concise. - - -## C++ and C Dependency Manager - -The C++ Dependency Manager is build on top of the C Dependency Manager. -To get an good overview of the C++ Dependency Manager alse read the [Dependency Manager documentation](../dependency_manager/README.md) - -## DM Parts - -The C++ Dependency Manager consist out of four main parts: `celix::dm::DmActivator`, `celix::dm::DependencyManager`, `celix::dm::Component` and `celix::dm::ServiceDependency`. - -### DmActivator - -The `DmActivator` class should be inherited by a bundle specific Activator. - -- 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 ocerridden if some cleanup is needed when a bundle is stopped. - -### Dependency Manager - -The `DependencyManager` act as an entry point to add or remove (DM) Components. - -### Component - - - -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. - -The `Component::setInstance` 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` can be used to specify one additional C service provided by the component. -The `component::addCInterface` can be used to specify one additional C++ service provided by the component. -The `component::add` can be used to specify one additional service dependency. - -### ServiceDependency and CServiceDependency - -The (DM) `ServiceDependency` can be used to specify C++ service dependencies for a component. -The (DM) `CServiceDependency` can be used to specify C service dependencies for a component. - -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. - -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_cxx example for more details. - -The `(C)ServiceDependency::setCallbacks` function can be used to specify the function callback used when services are added, set, removed or modified. -The `(C)ServiceDependency::setRequired` function can be used to specify if a service dependency is required. -The `(C)ServiceDependency::setStrategy` function can be used to specify a service dependency update strategy (suspend or locking). - -### References - -For more information examples please see - -- [The C++ Dependency Manager API](include/celix/dm): The c++ dependency manager header files -- [Dm C++ example](../examples/dm_example_cxx): A DM C++ example. -- [Best practices](../documents/best_practices/README.md): A introduction how to work with services using the C dependency manager http://git-wip-us.apache.org/repos/asf/celix/blob/c710cc60/dependency_manager_cxx/readme.md ---------------------------------------------------------------------- diff --git a/dependency_manager_cxx/readme.md b/dependency_manager_cxx/readme.md new file mode 100644 index 0000000..9915d47 --- /dev/null +++ b/dependency_manager_cxx/readme.md @@ -0,0 +1,64 @@ +# Apache Celix C++ Dependency Manager + +## Introduction + +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). + +The C++ Dependency Manager uses fluent interface to make specifying DM components and service dependencies very concise. + + +## C++ and C Dependency Manager + +The C++ Dependency Manager is build on top of the C Dependency Manager. +To get an good overview of the C++ Dependency Manager alse read the [Dependency Manager documentation](../dependency_manager/README.md) + +## DM Parts + +The C++ Dependency Manager consist out of four main parts: `celix::dm::DmActivator`, `celix::dm::DependencyManager`, `celix::dm::Component` and `celix::dm::ServiceDependency`. + +### DmActivator + +The `DmActivator` class should be inherited by a bundle specific Activator. + +- 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 ocerridden if some cleanup is needed when a bundle is stopped. + +### Dependency Manager + +The `DependencyManager` act as an entry point to add or remove (DM) Components. + +### Component + + + +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. + +The `Component::setInstance` 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` can be used to specify one additional C service provided by the component. +The `component::addCInterface` can be used to specify one additional C++ service provided by the component. +The `component::add` can be used to specify one additional service dependency. + +### ServiceDependency and CServiceDependency + +The (DM) `ServiceDependency` can be used to specify C++ service dependencies for a component. +The (DM) `CServiceDependency` can be used to specify C service dependencies for a component. + +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. + +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_cxx example for more details. + +The `(C)ServiceDependency::setCallbacks` function can be used to specify the function callback used when services are added, set, removed or modified. +The `(C)ServiceDependency::setRequired` function can be used to specify if a service dependency is required. +The `(C)ServiceDependency::setStrategy` function can be used to specify a service dependency update strategy (suspend or locking). + +### References + +For more information examples please see + +- [The C++ Dependency Manager API](include/celix/dm): The c++ dependency manager header files +- [Dm C++ example](../examples/dm_example_cxx): A DM C++ example. +- [Best practices](../documents/best_practices/readme.md): A introduction how to work with services using the C dependency manager http://git-wip-us.apache.org/repos/asf/celix/blob/c710cc60/documents/best_practices/README.md ---------------------------------------------------------------------- diff --git a/documents/best_practices/README.md b/documents/best_practices/README.md deleted file mode 100644 index 1e5ab76..0000000 --- a/documents/best_practices/README.md +++ /dev/null @@ -1,585 +0,0 @@ -# Apache Celix Best Practices - -## Intro - -This example should give a best practice approach for providing and using service with Apache Celix. - -## Services - -To start-of, services in Celix are just a pointer to a memory location registered in the service registry using a name and an optional set of key/value pairs. - -By convention use the following service layout: - -```C -//example.h -#ifndef EXAMPLE_H_ -#define EXAMPLE_H_ - -#define EXAMPLE_NAME "org.example" -#define EXAMPLE_VERSION "1.0.0" -#define EXAMPLE_CONSUMER_RANGE "[1.0.0,2.0.0)" - - -struct example_struct { - void *handle; - int (*method)(void *handle, int arg1, double arg2, double *result); -} ; - -typedef struct example_struct example_t; - -#endif /* EXAMPLE_H_ */ - -``` - - -For a Celix service a service name, service version and service consumer range should be declared. -This is explicitly done with macros to prevent symbols so to that no linking dependencies are introduced. - -Then the actual struct for the service needs to be declared. -The first element of the service struct should be a handle which can be used to store the service context, as convention we keep this pointer a void pointer to explicitly make it opaque. -Note that also an opaque struct could be used (e.g a declared but not defined struct), but this can become problematic concerning components registering multiple services. -In that case explicit cast are needed to prevent warning and this can be confusing for the To prevent that issues void pointers are preferred. - -The rest of the element should be function pointers, which by convention should return an celix_status_t or int (which is technically the same). -The return value is used as a way of handling errors and is also needed to be able to make remote services (e.g. to be able to handle remote exceptions). - -The first argument of a service function should be the service handle and if there is a result the last argument should be a output parameter (either pre allocated (e.g. double *) or not (e.g. double **)). -It is also possible to create typedef of the pointer to the service struct (e.g. typedef struct example_service *example_service_pt), but this is not needed. -If you do not create typedefs with pointers it is easier to include service struct in an parent struct (without memory allocation) and also make it's possible to use the const on the struct instead of the pointer. - -### Semantic Versioning - -For versioning, semantic versioning should be used. - -A backward incompatible change should lead to a major version increase (e.g. 1.0.0 -> 2.0.0). -For a C Service change that are incompatible are: - -- Removing a function -- Adding a function to before any other function -- Moving a function to an other location in the service struct -- Changing the signature of a function -- Changing the semantics of a argument (e.g. changing range input from "range in kilometer" to "range in meters") - -A backwards compatible change which extend the functionality should lead to a minor version increase (e.g. 1.0.0 -> 1.1.0). -Changes considered backwards compatible which extend the functionality are: - -- Adding a function to the back of the service struct - -A backwards compatible change which does not extend the functionality should lead to a micro version increase (e.g. 1.0.0 -> 1.0.1). -Changes considered backwards compatible which does not extend the functionaility are: - -- Changes in the documentation -- Renaming of arguments - - -## Components - -Component should use the ADT principle (see [ADT in C](http://inst.eecs.berkeley.edu/~selfpace/studyguide/9C.sg/Output/ADTs.in.C.html)). - -Components should have a `_create` and `_destroy` function. -Components can have a `_start` and `_stop` function to start/stop threads or invoke functionality needed a fully created component. -The start function will only be called if all required service are available and the stop function will be called when some required are going or if the component needs to be stopped. - -Components can also have a `_init` and `_deinit` function which will be called before and after respectively the start and stop function. -The init function can be used to add additional (even required) service dependencies. -The use case for init/deinit component functions are exceptional. - - -## Code Examples - -The next code block contains some code examples of components to indicate how to handle service dependencies, how to specify providing services and how to cope with locking/synchronizing. -The complete example can be found [here](../../examples/best_practice_example). - -The error checking is very minimal in these example to keep the focus on how to interact with services and how to deal with errors in C / Celix. - - -### Bar example - -The bar example is a simple component providing the `example` service. - -```C -//bar.h -#ifndef BAR_H_ -#define BAR_H_ - -#include "example.h" - -typedef struct bar_struct bar_t; - -bar_t* bar_create(void); -void bar_destroy(bar_t *self); - -int bar_method(bar_t *self, int arg1, double arg2, double *out); - -#endif //BAR_H_ -``` - -```C -//bar.c -#define OK 0 -#define ERROR 1 - -struct bar_struct { - double prefValue; -}; - -bar_t* bar_create(void) { - bar_t *self = calloc(1, sizeof(*self)); - if (self != NULL) { - self->prefValue = 42; - } else { - //log error - } - return self; -}; - -void bar_destroy(bar_t *self) { - free(self); -} - -int bar_method(bar_t *self, int arg1, double arg2, double *out) { - double update = (self->prefValue + arg1) * arg2; - self->prefValue = update; - *out = update; - return OK; -} -``` - -```C -//bar_activator.c -#include "dm_activator.h" -#include "bar.h" - -#include - -struct activator { - bar_t *bar; - example_t exampleService; -}; - -celix_status_t dm_create(bundle_context_pt context, void **userData) { - celix_status_t status = CELIX_SUCCESS; - struct activator *act = calloc(1, sizeof(*act)); - if (act != NULL) { - - act->bar = bar_create(); - act->exampleService.handle = act->bar; - act->exampleService.method = (void*) bar_method; - - if (act->bar != NULL) { - *userData = act; - } else { - free(act); - } - } else { - status = CELIX_ENOMEM; - } - return status; -} - -celix_status_t dm_init(void *userData, bundle_context_pt context, dm_dependency_manager_pt manager) { - celix_status_t status = CELIX_SUCCESS; - struct activator *activator = userData; - - dm_component_pt cmp = NULL; - component_create(context, "BAR", &cmp); - component_setImplementation(cmp, activator->bar); - component_addInterface(cmp, EXAMPLE_NAME, EXAMPLE_VERSION, &activator->exampleService, NULL); - - dependencyManager_add(manager, cmp); - return status; -} - -celix_status_t dm_destroy(void *userData, bundle_context_pt context, dm_dependency_manager_pt manager) { - celix_status_t status = CELIX_SUCCESS; - struct activator *activator = userData; - bar_destroy(activator->bar); - free(activator); - return status; -}; -``` - -### Foo1 example - -The Foo1 example shows how add a service dependency, implement the callback, invoke a service and how to protect the usage of service with use of a mutex. - -```C -//foo1.h -#ifndef FOO1_H_ -#define FOO1_H_ - -#include "example.h" - -typedef struct foo1_struct foo1_t; - -foo1_t* foo1_create(void); -void foo1_destroy(foo1_t *self); - -int foo1_start(foo1_t *self); -int foo1_stop(foo1_t *self); - -int foo1_setExample(foo1_t *self, const example_t *example); - - -#endif //FOO1_H_ -``` - -```C -//foo1.c -#include "foo1.h" - -#include -#include -#include -#include -#include -#include -#include -#include - - -#define OK 0 -#define ERROR 1 - -static void* foo1_thread(void*); - -struct foo1_struct { - const example_t *example; - pthread_mutex_t mutex; //protecting example - pthread_t thread; - bool running; -}; - -foo1_t* foo1_create(void) { - foo1_t *self = calloc(1, sizeof(*self)); - if (self != NULL) { - pthread_mutex_init(&self->mutex, NULL); - self->running = false; - } else { - //log error - } - return self; -}; - -void foo1_destroy(foo1_t *self) { - assert(!self->running); - pthread_mutex_destroy(&self->mutex); - free(self); -} - -int foo1_start(foo1_t *self) { - self->running = true; - pthread_create(&self->thread, NULL, foo1_thread, self); - return OK; -} - -int foo1_stop(foo1_t *self) { - self->running = false; - pthread_kill(self->thread, SIGUSR1); - pthread_join(self->thread, NULL); - return OK; -} - -int foo1_setExample(foo1_t *self, const example_t *example) { - pthread_mutex_lock(&self->mutex); - self->example = example; //NOTE could be NULL if req is not mandatory - pthread_mutex_unlock(&self->mutex); - return OK; -} - -static void* foo1_thread(void *userdata) { - foo1_t *self = userdata; - double result; - int rc; - while (self->running) { - pthread_mutex_lock(&self->mutex); - if (self->example != NULL) { - rc = self->example->method(self->example->handle, 1, 2.0, &result); - if (rc == 0) { - printf("Result is %f\n", result); - } else { - printf("Error invoking method for example\n"); - } - } - pthread_mutex_unlock(&self->mutex); - usleep(10000000); - } - return NULL; -} -``` - -```C -//foo1_activator.c -#include "dm_activator.h" -#include "foo1.h" - -#include - -struct activator { - foo1_t *foo; -}; - -celix_status_t dm_create(bundle_context_pt context, void **userData) { - celix_status_t status = CELIX_SUCCESS; - struct activator *act = calloc(1, sizeof(*act)); - if (act != NULL) { - act->foo = foo1_create(); - if (act->foo != NULL) { - *userData = act; - } else { - free(act); - } - } else { - status = CELIX_ENOMEM; - } - return status; -} - -celix_status_t dm_init(void *userData, bundle_context_pt context, dm_dependency_manager_pt manager) { - celix_status_t status = CELIX_SUCCESS; - struct activator *activator = userData; - - dm_component_pt cmp = NULL; - component_create(context, "FOO1", &cmp); - component_setImplementation(cmp, activator->foo); - - /* - With the component_setCallbacksSafe we register callbacks when a component is started / stopped using a component - with type foo1_t* - */ - component_setCallbacksSafe(cmp, foo1_t*, NULL, foo1_start, foo1_stop, NULL); - - dm_service_dependency_pt dep = NULL; - serviceDependency_create(&dep); - serviceDependency_setRequired(dep, true); - serviceDependency_setService(dep, EXAMPLE_NAME, EXAMPLE_CONSUMER_RANGE, NULL); - serviceDependency_setStrategy(dep, DM_SERVICE_DEPENDENCY_STRATEGY_LOCKING); - - /* - With the serviceDependency_setCallbacksSafe we register callbacks when a service - is added and about to be removed for the component type foo1_t* and service type example_t*. - - We should protect the usage of the - service because after removal of the service the memory location of that service - could be freed - */ - serviceDependency_setCallbacksSafe(dep, foo1_t*, const example_t*, foo1_setExample, NULL, NULL, NULL, NULL); - component_addServiceDependency(cmp, dep); - - dependencyManager_add(manager, cmp); - - return status; -} - -celix_status_t dm_destroy(void *userData, bundle_context_pt context, dm_dependency_manager_pt manager) { - celix_status_t status = CELIX_SUCCESS; - struct activator *activator = userData; - foo1_destroy(activator->foo); - free(activator); - return status; -}; - -``` - -### Foo2 example - -The Foo2 example shows how to cope with multiple services and how to remove the need for locking by ensuring only access to the services and the services container by a single thread. - -```C -//foo2.h -#ifndef FOO2_H_ -#define FOO2_H_ - -#include "example.h" - -typedef struct foo2_struct foo2_t; - -foo2_t* foo2_create(void); -void foo2_destroy(foo2_t *self); - -int foo2_start(foo2_t *self); -int foo2_stop(foo2_t *self); - -int foo2_addExample(foo2_t *self, const example_t *example); -int foo2_removeExample(foo2_t *self, const example_t *example); - -#endif //FOO2_H_ -``` - -```C -//foo2.c -#include "foo2.h" - -#include "array_list.h" - -#include -#include -#include -#include -#include -#include -#include -#include - - -#define OK 0 -#define ERROR 1 - -static void* foo2_thread(void*); - -struct foo2_struct { - array_list_pt examples; - pthread_t thread; - bool running; -}; - -foo2_t* foo2_create(void) { - foo2_t *self = calloc(1, sizeof(*self)); - if (self != NULL) { - self->examples = NULL; - arrayList_create(&self->examples); - self->running = false; - } else { - //log error - } - return self; -}; - -void foo2_destroy(foo2_t *self) { - assert(!self->running); - arrayList_destroy(self->examples); - free(self); -} - -int foo2_start(foo2_t *self) { - self->running = true; - pthread_create(&self->thread, NULL, foo2_thread, self); - return OK; -} - -int foo2_stop(foo2_t *self) { - self->running = false; - pthread_kill(self->thread, SIGUSR1); - pthread_join(self->thread, NULL); - return OK; -} - -int foo2_addExample(foo2_t *self, const example_t *example) { - //NOTE foo2 is suspended -> thread is not running -> safe to update - int status = OK; - status = arrayList_add(self->examples, (void *)example); - return status; -} - -int foo2_removeExample(foo2_t *self, const example_t *example) { - //NOTE foo2 is suspended -> thread is not running -> safe to update - int status = OK; - status = arrayList_removeElement(self->examples, (void*)example); - return status; -} - -static void* foo2_thread(void *userdata) { - foo2_t *self = userdata; - double result; - int rc; - while (self->running) { - unsigned int size = arrayList_size(self->examples); - int i; - for (i = 0; i < size; i += 1) { - const example_t* example = arrayList_get(self->examples, i); - rc = example->method(example->handle, 1, 2.0, &result); - if (rc == 0) { - printf("Result is %f\n", result); - } else { - printf("Error invoking method for example\n"); - } - } - usleep(10000000); - } - return NULL; - -``` - -```C -//foo2_activator.c -#include "dm_activator.h" -#include "foo2.h" - -#include - -struct activator { - foo2_t *foo; -}; - -celix_status_t dm_create(bundle_context_pt context, void **userData) { - celix_status_t status = CELIX_SUCCESS; - struct activator *act = calloc(1, sizeof(*act)); - if (act != NULL) { - act->foo = foo2_create(); - if (act->foo != NULL) { - *userData = act; - } else { - free(act); - } - } else { - status = CELIX_ENOMEM; - } - return status; -} - -celix_status_t dm_init(void *userData, bundle_context_pt context, dm_dependency_manager_pt manager) { - celix_status_t status = CELIX_SUCCESS; - struct activator *activator = userData; - - dm_component_pt cmp = NULL; - component_create(context, "FOO2", &cmp); - component_setImplementation(cmp, activator->foo); - - /* - With the component_setCallbacksSafe we register callbacks when a component is started / stopped using a component - with type foo1_t* - */ - component_setCallbacksSafe(cmp, foo2_t*, NULL, foo2_start, foo2_stop, NULL); - - dm_service_dependency_pt dep = NULL; - serviceDependency_create(&dep); - serviceDependency_setRequired(dep, false); - serviceDependency_setService(dep, EXAMPLE_NAME, EXAMPLE_CONSUMER_RANGE, NULL); - serviceDependency_setStrategy(dep, DM_SERVICE_DEPENDENCY_STRATEGY_SUSPEND); - - /* - With the serviceDependency_setCallbacksSafe we register callbacks when a service - is added and about to be removed for the component type foo1_t* and service type example_t*. - - We should protect the usage of the - service because after removal of the service the memory location of that service - could be freed - */ - serviceDependency_setCallbacksSafe(dep, foo2_t*, const example_t*, NULL, foo2_addExample, foo2_removeExample, NULL, NULL); - component_addServiceDependency(cmp, dep); - - dependencyManager_add(manager, cmp); - - return status; -} - -celix_status_t dm_destroy(void *userData, bundle_context_pt context, dm_dependency_manager_pt manager) { - celix_status_t status = CELIX_SUCCESS; - struct activator *activator = userData; - foo2_destroy(activator->foo); - free(activator); - return status; -}; -``` - -## Locking and Suspending - -As you may notice, the Foo1 example uses locks. -In principle, locking is necessary in order to ensure coherence in case service dependencies are removed/added/changed; on the other hands, locking increases latency and, when misused, can lead to poor performance. -For this reason, the serviceDependecy interface gives the possibility to choose between a locking and suspend (a non-locking) strategy through the serviceDependency_setStrategy function, as is used in the Foo2 example. - -The locking strategy `DM_SERVICE_DEPENDENCY_STRATEGY_LOCKING` notifies the component in case the dependencies' set changes (e.g. a dependency is added/removed): the component is responsible for protecting via locks the dependencies' list and check (always under lock) if the service he's depending on is still available. -The suspend or non-locking strategy `DM_SERVICE_DEPENDENCY_STRATEGY_SUSPEND` (default when no strategy is explicitly set) reliefs the programmer from dealing with service dependencies' consistency issues: in case this strategy is adopted, the component is stopped and restarted (i.e. temporarily suspended) upon service dependencies' changes. - -The suspend strategy has the advantage of reducing locks' usage: of course, suspending the component has its own overhead (e.g. stopping and restarting threads), but this overhead is "paid" only in case of changes in service dependencies, while the locking overhead is always paid. - http://git-wip-us.apache.org/repos/asf/celix/blob/c710cc60/documents/best_practices/readme.md ---------------------------------------------------------------------- diff --git a/documents/best_practices/readme.md b/documents/best_practices/readme.md new file mode 100644 index 0000000..b098f48 --- /dev/null +++ b/documents/best_practices/readme.md @@ -0,0 +1,585 @@ +#Apache Celix - Best Practices + +## Intro + +This example should give a best practice approach for providing and using service with Apache Celix. + +## Services + +To start-of, services in Celix are just a pointer to a memory location registered in the service registry using a name and an optional set of key/value pairs. + +By convention use the following service layout: + +```C +//example.h +#ifndef EXAMPLE_H_ +#define EXAMPLE_H_ + +#define EXAMPLE_NAME "org.example" +#define EXAMPLE_VERSION "1.0.0" +#define EXAMPLE_CONSUMER_RANGE "[1.0.0,2.0.0)" + + +struct example_struct { + void *handle; + int (*method)(void *handle, int arg1, double arg2, double *result); +} ; + +typedef struct example_struct example_t; + +#endif /* EXAMPLE_H_ */ + +``` + + +For a Celix service a service name, service version and service consumer range should be declared. +This is explicitly done with macros to prevent symbols so to that no linking dependencies are introduced. + +Then the actual struct for the service needs to be declared. +The first element of the service struct should be a handle which can be used to store the service context, as convention we keep this pointer a void pointer to explicitly make it opaque. +Note that also an opaque struct could be used (e.g a declared but not defined struct), but this can become problematic concerning components registering multiple services. +In that case explicit cast are needed to prevent warning and this can be confusing for the To prevent that issues void pointers are preferred. + +The rest of the element should be function pointers, which by convention should return an celix_status_t or int (which is technically the same). +The return value is used as a way of handling errors and is also needed to be able to make remote services (e.g. to be able to handle remote exceptions). + +The first argument of a service function should be the service handle and if there is a result the last argument should be a output parameter (either pre allocated (e.g. double *) or not (e.g. double **)). +It is also possible to create typedef of the pointer to the service struct (e.g. typedef struct example_service *example_service_pt), but this is not needed. +If you do not create typedefs with pointers it is easier to include service struct in an parent struct (without memory allocation) and also make it's possible to use the const on the struct instead of the pointer. + +### Semantic Versioning + +For versioning, semantic versioning should be used. + +A backward incompatible change should lead to a major version increase (e.g. 1.0.0 -> 2.0.0). +For a C Service change that are incompatible are: + +- Removing a function +- Adding a function to before any other function +- Moving a function to an other location in the service struct +- Changing the signature of a function +- Changing the semantics of a argument (e.g. changing range input from "range in kilometer" to "range in meters") + +A backwards compatible change which extend the functionality should lead to a minor version increase (e.g. 1.0.0 -> 1.1.0). +Changes considered backwards compatible which extend the functionality are: + +- Adding a function to the back of the service struct + +A backwards compatible change which does not extend the functionality should lead to a micro version increase (e.g. 1.0.0 -> 1.0.1). +Changes considered backwards compatible which does not extend the functionaility are: + +- Changes in the documentation +- Renaming of arguments + + +## Components + +Component should use the ADT principle (see [ADT in C](http://inst.eecs.berkeley.edu/~selfpace/studyguide/9C.sg/Output/ADTs.in.C.html)). + +Components should have a `_create` and `_destroy` function. +Components can have a `_start` and `_stop` function to start/stop threads or invoke functionality needed a fully created component. +The start function will only be called if all required service are available and the stop function will be called when some required are going or if the component needs to be stopped. + +Components can also have a `_init` and `_deinit` function which will be called before and after respectively the start and stop function. +The init function can be used to add additional (even required) service dependencies. +The use case for init/deinit component functions are exceptional. + + +## Code Examples + +The next code block contains some code examples of components to indicate how to handle service dependencies, how to specify providing services and how to cope with locking/synchronizing. +The complete example can be found [here](../../examples/best_practice_example). + +The error checking is very minimal in these example to keep the focus on how to interact with services and how to deal with errors in C / Celix. + + +### Bar example + +The bar example is a simple component providing the `example` service. + +```C +//bar.h +#ifndef BAR_H_ +#define BAR_H_ + +#include "example.h" + +typedef struct bar_struct bar_t; + +bar_t* bar_create(void); +void bar_destroy(bar_t *self); + +int bar_method(bar_t *self, int arg1, double arg2, double *out); + +#endif //BAR_H_ +``` + +```C +//bar.c +#define OK 0 +#define ERROR 1 + +struct bar_struct { + double prefValue; +}; + +bar_t* bar_create(void) { + bar_t *self = calloc(1, sizeof(*self)); + if (self != NULL) { + self->prefValue = 42; + } else { + //log error + } + return self; +}; + +void bar_destroy(bar_t *self) { + free(self); +} + +int bar_method(bar_t *self, int arg1, double arg2, double *out) { + double update = (self->prefValue + arg1) * arg2; + self->prefValue = update; + *out = update; + return OK; +} +``` + +```C +//bar_activator.c +#include "dm_activator.h" +#include "bar.h" + +#include + +struct activator { + bar_t *bar; + example_t exampleService; +}; + +celix_status_t dm_create(bundle_context_pt context, void **userData) { + celix_status_t status = CELIX_SUCCESS; + struct activator *act = calloc(1, sizeof(*act)); + if (act != NULL) { + + act->bar = bar_create(); + act->exampleService.handle = act->bar; + act->exampleService.method = (void*) bar_method; + + if (act->bar != NULL) { + *userData = act; + } else { + free(act); + } + } else { + status = CELIX_ENOMEM; + } + return status; +} + +celix_status_t dm_init(void *userData, bundle_context_pt context, dm_dependency_manager_pt manager) { + celix_status_t status = CELIX_SUCCESS; + struct activator *activator = userData; + + dm_component_pt cmp = NULL; + component_create(context, "BAR", &cmp); + component_setImplementation(cmp, activator->bar); + component_addInterface(cmp, EXAMPLE_NAME, EXAMPLE_VERSION, &activator->exampleService, NULL); + + dependencyManager_add(manager, cmp); + return status; +} + +celix_status_t dm_destroy(void *userData, bundle_context_pt context, dm_dependency_manager_pt manager) { + celix_status_t status = CELIX_SUCCESS; + struct activator *activator = userData; + bar_destroy(activator->bar); + free(activator); + return status; +}; +``` + +### Foo1 example + +The Foo1 example shows how add a service dependency, implement the callback, invoke a service and how to protect the usage of service with use of a mutex. + +```C +//foo1.h +#ifndef FOO1_H_ +#define FOO1_H_ + +#include "example.h" + +typedef struct foo1_struct foo1_t; + +foo1_t* foo1_create(void); +void foo1_destroy(foo1_t *self); + +int foo1_start(foo1_t *self); +int foo1_stop(foo1_t *self); + +int foo1_setExample(foo1_t *self, const example_t *example); + + +#endif //FOO1_H_ +``` + +```C +//foo1.c +#include "foo1.h" + +#include +#include +#include +#include +#include +#include +#include +#include + + +#define OK 0 +#define ERROR 1 + +static void* foo1_thread(void*); + +struct foo1_struct { + const example_t *example; + pthread_mutex_t mutex; //protecting example + pthread_t thread; + bool running; +}; + +foo1_t* foo1_create(void) { + foo1_t *self = calloc(1, sizeof(*self)); + if (self != NULL) { + pthread_mutex_init(&self->mutex, NULL); + self->running = false; + } else { + //log error + } + return self; +}; + +void foo1_destroy(foo1_t *self) { + assert(!self->running); + pthread_mutex_destroy(&self->mutex); + free(self); +} + +int foo1_start(foo1_t *self) { + self->running = true; + pthread_create(&self->thread, NULL, foo1_thread, self); + return OK; +} + +int foo1_stop(foo1_t *self) { + self->running = false; + pthread_kill(self->thread, SIGUSR1); + pthread_join(self->thread, NULL); + return OK; +} + +int foo1_setExample(foo1_t *self, const example_t *example) { + pthread_mutex_lock(&self->mutex); + self->example = example; //NOTE could be NULL if req is not mandatory + pthread_mutex_unlock(&self->mutex); + return OK; +} + +static void* foo1_thread(void *userdata) { + foo1_t *self = userdata; + double result; + int rc; + while (self->running) { + pthread_mutex_lock(&self->mutex); + if (self->example != NULL) { + rc = self->example->method(self->example->handle, 1, 2.0, &result); + if (rc == 0) { + printf("Result is %f\n", result); + } else { + printf("Error invoking method for example\n"); + } + } + pthread_mutex_unlock(&self->mutex); + usleep(10000000); + } + return NULL; +} +``` + +```C +//foo1_activator.c +#include "dm_activator.h" +#include "foo1.h" + +#include + +struct activator { + foo1_t *foo; +}; + +celix_status_t dm_create(bundle_context_pt context, void **userData) { + celix_status_t status = CELIX_SUCCESS; + struct activator *act = calloc(1, sizeof(*act)); + if (act != NULL) { + act->foo = foo1_create(); + if (act->foo != NULL) { + *userData = act; + } else { + free(act); + } + } else { + status = CELIX_ENOMEM; + } + return status; +} + +celix_status_t dm_init(void *userData, bundle_context_pt context, dm_dependency_manager_pt manager) { + celix_status_t status = CELIX_SUCCESS; + struct activator *activator = userData; + + dm_component_pt cmp = NULL; + component_create(context, "FOO1", &cmp); + component_setImplementation(cmp, activator->foo); + + /* + With the component_setCallbacksSafe we register callbacks when a component is started / stopped using a component + with type foo1_t* + */ + component_setCallbacksSafe(cmp, foo1_t*, NULL, foo1_start, foo1_stop, NULL); + + dm_service_dependency_pt dep = NULL; + serviceDependency_create(&dep); + serviceDependency_setRequired(dep, true); + serviceDependency_setService(dep, EXAMPLE_NAME, EXAMPLE_CONSUMER_RANGE, NULL); + serviceDependency_setStrategy(dep, DM_SERVICE_DEPENDENCY_STRATEGY_LOCKING); + + /* + With the serviceDependency_setCallbacksSafe we register callbacks when a service + is added and about to be removed for the component type foo1_t* and service type example_t*. + + We should protect the usage of the + service because after removal of the service the memory location of that service + could be freed + */ + serviceDependency_setCallbacksSafe(dep, foo1_t*, const example_t*, foo1_setExample, NULL, NULL, NULL, NULL); + component_addServiceDependency(cmp, dep); + + dependencyManager_add(manager, cmp); + + return status; +} + +celix_status_t dm_destroy(void *userData, bundle_context_pt context, dm_dependency_manager_pt manager) { + celix_status_t status = CELIX_SUCCESS; + struct activator *activator = userData; + foo1_destroy(activator->foo); + free(activator); + return status; +}; + +``` + +### Foo2 example + +The Foo2 example shows how to cope with multiple services and how to remove the need for locking by ensuring only access to the services and the services container by a single thread. + +```C +//foo2.h +#ifndef FOO2_H_ +#define FOO2_H_ + +#include "example.h" + +typedef struct foo2_struct foo2_t; + +foo2_t* foo2_create(void); +void foo2_destroy(foo2_t *self); + +int foo2_start(foo2_t *self); +int foo2_stop(foo2_t *self); + +int foo2_addExample(foo2_t *self, const example_t *example); +int foo2_removeExample(foo2_t *self, const example_t *example); + +#endif //FOO2_H_ +``` + +```C +//foo2.c +#include "foo2.h" + +#include "array_list.h" + +#include +#include +#include +#include +#include +#include +#include +#include + + +#define OK 0 +#define ERROR 1 + +static void* foo2_thread(void*); + +struct foo2_struct { + array_list_pt examples; + pthread_t thread; + bool running; +}; + +foo2_t* foo2_create(void) { + foo2_t *self = calloc(1, sizeof(*self)); + if (self != NULL) { + self->examples = NULL; + arrayList_create(&self->examples); + self->running = false; + } else { + //log error + } + return self; +}; + +void foo2_destroy(foo2_t *self) { + assert(!self->running); + arrayList_destroy(self->examples); + free(self); +} + +int foo2_start(foo2_t *self) { + self->running = true; + pthread_create(&self->thread, NULL, foo2_thread, self); + return OK; +} + +int foo2_stop(foo2_t *self) { + self->running = false; + pthread_kill(self->thread, SIGUSR1); + pthread_join(self->thread, NULL); + return OK; +} + +int foo2_addExample(foo2_t *self, const example_t *example) { + //NOTE foo2 is suspended -> thread is not running -> safe to update + int status = OK; + status = arrayList_add(self->examples, (void *)example); + return status; +} + +int foo2_removeExample(foo2_t *self, const example_t *example) { + //NOTE foo2 is suspended -> thread is not running -> safe to update + int status = OK; + status = arrayList_removeElement(self->examples, (void*)example); + return status; +} + +static void* foo2_thread(void *userdata) { + foo2_t *self = userdata; + double result; + int rc; + while (self->running) { + unsigned int size = arrayList_size(self->examples); + int i; + for (i = 0; i < size; i += 1) { + const example_t* example = arrayList_get(self->examples, i); + rc = example->method(example->handle, 1, 2.0, &result); + if (rc == 0) { + printf("Result is %f\n", result); + } else { + printf("Error invoking method for example\n"); + } + } + usleep(10000000); + } + return NULL; + +``` + +```C +//foo2_activator.c +#include "dm_activator.h" +#include "foo2.h" + +#include + +struct activator { + foo2_t *foo; +}; + +celix_status_t dm_create(bundle_context_pt context, void **userData) { + celix_status_t status = CELIX_SUCCESS; + struct activator *act = calloc(1, sizeof(*act)); + if (act != NULL) { + act->foo = foo2_create(); + if (act->foo != NULL) { + *userData = act; + } else { + free(act); + } + } else { + status = CELIX_ENOMEM; + } + return status; +} + +celix_status_t dm_init(void *userData, bundle_context_pt context, dm_dependency_manager_pt manager) { + celix_status_t status = CELIX_SUCCESS; + struct activator *activator = userData; + + dm_component_pt cmp = NULL; + component_create(context, "FOO2", &cmp); + component_setImplementation(cmp, activator->foo); + + /* + With the component_setCallbacksSafe we register callbacks when a component is started / stopped using a component + with type foo1_t* + */ + component_setCallbacksSafe(cmp, foo2_t*, NULL, foo2_start, foo2_stop, NULL); + + dm_service_dependency_pt dep = NULL; + serviceDependency_create(&dep); + serviceDependency_setRequired(dep, false); + serviceDependency_setService(dep, EXAMPLE_NAME, EXAMPLE_CONSUMER_RANGE, NULL); + serviceDependency_setStrategy(dep, DM_SERVICE_DEPENDENCY_STRATEGY_SUSPEND); + + /* + With the serviceDependency_setCallbacksSafe we register callbacks when a service + is added and about to be removed for the component type foo1_t* and service type example_t*. + + We should protect the usage of the + service because after removal of the service the memory location of that service + could be freed + */ + serviceDependency_setCallbacksSafe(dep, foo2_t*, const example_t*, NULL, foo2_addExample, foo2_removeExample, NULL, NULL); + component_addServiceDependency(cmp, dep); + + dependencyManager_add(manager, cmp); + + return status; +} + +celix_status_t dm_destroy(void *userData, bundle_context_pt context, dm_dependency_manager_pt manager) { + celix_status_t status = CELIX_SUCCESS; + struct activator *activator = userData; + foo2_destroy(activator->foo); + free(activator); + return status; +}; +``` + +## Locking and Suspending + +As you may notice, the Foo1 example uses locks. +In principle, locking is necessary in order to ensure coherence in case service dependencies are removed/added/changed; on the other hands, locking increases latency and, when misused, can lead to poor performance. +For this reason, the serviceDependecy interface gives the possibility to choose between a locking and suspend (a non-locking) strategy through the serviceDependency_setStrategy function, as is used in the Foo2 example. + +The locking strategy `DM_SERVICE_DEPENDENCY_STRATEGY_LOCKING` notifies the component in case the dependencies' set changes (e.g. a dependency is added/removed): the component is responsible for protecting via locks the dependencies' list and check (always under lock) if the service he's depending on is still available. +The suspend or non-locking strategy `DM_SERVICE_DEPENDENCY_STRATEGY_SUSPEND` (default when no strategy is explicitly set) reliefs the programmer from dealing with service dependencies' consistency issues: in case this strategy is adopted, the component is stopped and restarted (i.e. temporarily suspended) upon service dependencies' changes. + +The suspend strategy has the advantage of reducing locks' usage: of course, suspending the component has its own overhead (e.g. stopping and restarting threads), but this overhead is "paid" only in case of changes in service dependencies, while the locking overhead is always paid. + http://git-wip-us.apache.org/repos/asf/celix/blob/c710cc60/documents/building/readme.md ---------------------------------------------------------------------- diff --git a/documents/building/readme.md b/documents/building/readme.md index 8df49c0..f3963dd 100644 --- a/documents/building/readme.md +++ b/documents/building/readme.md @@ -1,19 +1,33 @@ #Apache Celix - Building and Installing +Apache Celix aims to be support a broad range of UNIX platforms. + +Currently the [continuous integration build server](https://travis-ci.org/apache/celix) build and tests Apache Celix for: + +* Ubuntu Trusty Tahr (14.04) + * GCC + * CLang +* OSX + * GCC + * CLang +* Android (cross-compiled on Ubuntu Trusty Tahr) + * GCC #Preparing The following packages (libraries + headers) should be installed on your system: * Development Environment - * build-essentials + * build-essentials (gcc/g++ or clang/clang++) * git * java (for packaging bundles) - * cmake (3 or higher) + * cmake (3.2 or higher) * Apache Celix Dependencies * curl * jansson + * libffi + * libxml2 (for remote services and bonjour shell) + For a debian based systems, the following should work: -
 sudo apt-get install -yq --no-install-recommends \
 	build-essential \
@@ -23,7 +37,9 @@ sudo apt-get install -yq --no-install-recommends \
   	libjansson-dev \
   	libcurl4-openssl-dev \
     java \
-  	cmake 
+  	cmake \
+  	libffi-dev \
+  	libxml2-dev
 
For Fedora based systems (dnf), the following should work: @@ -35,9 +51,18 @@ sudo dnf install \ git \ java \ libcurl-devel \ - jansson-devel + jansson-devel \ + libffi-devel \ + libxml2-devel +For a OSX with brew installed, the following should work: +
+    brew update && \
+    brew install lcov libffi cmake && \
+    brew link --force libffi
+ 
+ ##Download the Apache Celix sources To get started you first have to download the Apache Celix sources. This can be done by cloning the Apache Celix git repository: @@ -97,7 +122,9 @@ sudo make install ##Running Apache Celix If Apache Celix is succesfully installed running - celix +```bash +celix +``` should give the following output: "Error: invalid or non-existing configuration file: 'config.properties'.No such file or directory" http://git-wip-us.apache.org/repos/asf/celix/blob/c710cc60/documents/getting_started/readme.md ---------------------------------------------------------------------- diff --git a/documents/getting_started/readme.md b/documents/getting_started/readme.md index cdb1779..7b82180 100644 --- a/documents/getting_started/readme.md +++ b/documents/getting_started/readme.md @@ -1,4 +1,4 @@ -#Apache Celix - Getting Started +#Apache Celix - Getting Started Guide ##Intro This page is intended for first time users of Apache Celix. It should guide you through building & installing Apache Celix, setting up a new project, creating your first bundle, setting up the project for use with Eclipse project and finally running and debugging your bundle directly from eclipse workspace. http://git-wip-us.apache.org/repos/asf/celix/blob/c710cc60/documents/intro/readme.md ---------------------------------------------------------------------- diff --git a/documents/intro/readme.md b/documents/intro/readme.md new file mode 100644 index 0000000..200ecc1 --- /dev/null +++ b/documents/intro/readme.md @@ -0,0 +1,140 @@ +#Apache Celix Introduction + +##What is Apache Celix +Apache Celix is an implementation of the [OSGi specification](https://www.osgi.org/developer/specifications) adapted to C . + +OSGi is a specification describing a dynamic modular system composed out of components. +Components are packaged in, runtime, installable bundles and should be implemented using a (dynamic) service-oriented programming style. + +Apache Celix also has support for C++ providing a higher abstraction API built on top of the Apache Celix C API. + +##C and Objects +C is a procedural programming language and as result has no direct support for the notion of a object. +To be able to follow the OSGi specification, a standard mapping from C to Java is used. This mapping takes care of how instances, parameters, return values and exceptions (error codes) work in Apache Celix. + +###Example +Before going into detail, here is an example of the mapping from a method in Java to a function in C: + +```Java +//Java signature +public interface BundleContext { + public ServiceReference[] getServiceReferences(String clazz, String filter) throws InvalidSyntaxException; +} +``` + +```C +//bundle_context.h + +//C prototype +celix_status_t bundleContext_getServiceReferences(bundle_context_pt context, const char* serviceName, const char* filter, array_list_pt* service_references); +``` + +###Object methods +Using the provided example, the following templates can be extracted for mapping a object method to a C function: + +```C +/** + * 1st template + * celix_status_t: return type of the status code + * + * typeName: name of the object/type this function is part of + * functionName: the name of the function + * + * typeName_t: The actual instance to "invoke" this function on + * parameters: default function parameters + * output parameter: the output which the caller can use + */ +celix_status_t _(_t* self [,parameters, ] [, output parameter]); + +//OR + +/** +* 2nd template + * celix_status_t: return type of the status code + * + * typeName: name of the object/type this function is part of + * functionName: the name of the function + * + * typeName_t: The actual instance to "invoke" this function on + * parameters: default function parameters + * output parameter: the output which the caller can use + */ +celix_status_t _(_pt self [,parameters, ] [, output parameter]); +``` + +Note that although the first template is preferred, Apache Celix still uses the second template. + +Unless stated otherwise, the caller is owner of the output and should destroy/deallocate the result. +An exception is a const output parameters, this indicates the callee is still owner. + +###Creating and destroying Objects +Objects in Apache Celix can generally be created and destroyed using a create and destroy functions. +For example: + +```C +celix_status_t bundleContext_create(framework_pt framework, framework_logger_pt, bundle_pt bundle, bundle_context_pt *bundle_context); +celix_status_t bundleContext_destroy(bundle_context_pt context); +``` + +For some types a separate allocate and init and a separate deallocate and deinit are also available. +This gives a user the option the initialize and deinitialize a object on the stack. the hash_map_iterator uses this: + +```C +hash_map_iterator_pt hashMapIterator_create(hash_map_pt map); +void hashMapIterator_destroy(hash_map_iterator_pt iterator); + +hash_map_iterator_pt hashMapIterator_alloc(void); +void hashMapIterator_dealloc(hash_map_iterator_pt iterator); + +void hashMapIterator_init(hash_map_pt map, hash_map_iterator_pt iterator); +void hashMapIterator_deinit(hash_map_iterator_pt iterator); +``` + +###OSGi documentation and Apache Celix +Apache Celix follows the OSGi API as close as possible, but since the OSGi specification is written primarily for Java, there will be differences (Java is OO, C is procedural). +Taking into account those differences and mapping explained before the OSGi javadoc can be used for a more in depth description of what the Apache Celix API offers. + +[OSGi core specification 4.3](https://osgi.org/javadoc/r4v43/core/index.html) +[OSGi compendium specification 4.3](https://osgi.org/javadoc/r4v43/cmpn/index.html) + +##What is a OSGi service? +A OSGi service is a Java object register to the OSGi framework under a certain set of properties. +OSGi services are generally registered as a well known interface (using the `objectClass` property). + +Consumers can dynamically lookup the services providing a filter to specify what kind of services their are interested in. + +##C services in Apache Celix +As mentioned OSGi uses Java Interfaces to define a service. Since C does not have Interfaces as compilable unit, this is not possible for Celix. To be able to define a service which hides implementation details, Celix uses structs with function pointers. + +See [Apache Celix Best Practices](../best_practices/README.md) for a more in depth look at services and service usage. + +##Impact of dynamic services +Services in Apache Celix are dynamic, meaning that they can come and go at any moment. +How to cope with this dynamic behaviour is very critical for creating a stable solution. + +For Java OSGi this is already a challenge to program correctly, but less critical because generally speaking the garbage collector will arrange that objects still exists even if the providing bundle is deinstalled. +Taking into account that C has no garbage collection handling the dynamic behaviour correctly is even more critical; If a bundle providing a certain services is removed the code segment / memory allocated for the service will be removed / deallocated. + +Apache Celix offers different solutions how to cope with this dynamic behaviour: + +* Bundle Context & Service References - This (low level) [API](../framework/public/include/bundle_context.h) exists to be compatible with the OSGi standard. This should not be used in production code, because no locking/syncing mechanisms are available. +* Service Listener - This (log level) [API](../framework/public/include/service_listener.h) can be used to retrieve event when services are being removed or are added. Combined with locking this can be used to safely monitor and use services. +* Service Tracker - This [API](../framework/public/include/service_tracker.h) can be used to register callbacks function when services are being removed or are added. Combined with locking this can be used to safely use services. +* Dependency Manager - This [library](../dependency_manager/readme.md) can be used to add service dependency is a declarative way. A locking or syncing mechanism can be selected to safely use services. Note that this is not part of the OSGi standard. + +Even though the dependency manager is not part of the OSGi specification, this is the preferred way because it uses a higher abstraction and removes a lot boilerplate code. + +##C++ Support + +One of the reasons why C was chosen as implementation language is that C can act as a common denominator for (service oriented) interoperability between a range of languages. +C++ support is added with the use of a [C++ Dependency Manager](../dependency_manager_cxx/readme.md). +The Dependency Manager is arguably the most convenient way to interact with services, confers most uses cases and eliminates the necessity to port the rest of the (large) API to C++. + +##Documentation + +For more information see: + +* [Apache Celix - Building and Installing] (../building/readme.md) +* [Apache Celix - Getting Started Guide](../getting_started/readme.md) +* [Apache Celix - Best Practices](../best_practices/readme.md) +* [Apache Celix - CMake Commands](../cmake_commands/readme.md) \ No newline at end of file http://git-wip-us.apache.org/repos/asf/celix/blob/c710cc60/documents/subprojects/readme.md ---------------------------------------------------------------------- diff --git a/documents/subprojects/readme.md b/documents/subprojects/readme.md index 7e4fd50..92a5bc0 100644 --- a/documents/subprojects/readme.md +++ b/documents/subprojects/readme.md @@ -3,7 +3,8 @@ Apache Celix is organized into several subprojects. The following subproject are currently available: * [Framework](../../framework) - The Apache Celix framework, an implementation of OSGi adapted to C. -* [Dependency Manager](../../dependency_manager) - A component/dependency model for use through an API provided as library. +* [Dependency Manager](../../dependency_manager) - A C component/dependency model for use through an API provided as library. +* [C++ Dependency Manager](../../dependency_manager_cxx) - A C++ component/dependency model for use through an API provided as library. * [Device Access](../../device_access) - An implementation of the OSGi Device Access specification adapted to C. * [Examples](../../examples) - A Selection of examples showing how the framework can be used. * [Log Service](../../log_service) - An implementation of the OSGi Log Service adapated to C. @@ -18,6 +19,7 @@ Apache Celix is organized into several subprojects. The following subproject are * [Discovery SHM](../../remote_services/dicovery_shm) - A RSA Discovery implementation using shared memory. * [Shell](../../shell) - A OSGi shell implementation. * [Shell TUI](../../shell_tui) - A textual UI for the Celix Shell. -* [Remote Shell](../../remote_shell) - A remote (telnet) frontend for the Celix Shell. +* [Remote Shell](../../remote_shell) - A remote (telnet) frontend for the Celix shell. +* [Bonjour Shell](../../shell_bonjour) - A remote (Bonjour / mDNS) frontend for the Celix shell. * [Deployment Admin](../../deployment_admin) - A deployment admin implementation.