Celix Design

Since Celix tries to follow the OSGi specification as close as possible, most of the design can be found in these specifications. Some key aspects of OSGi can not be mapped on C, these differences are detailed on this page.

The following important differences are:

Services and Service Interfaces

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. To register a service, in the activator, the actual functions are assigned to the pointers of the struct. This struct is registered as service implementation.

Object and Object State

Most notably is the use of Objects in Java, and the lack of Objects in C. To solve this and be able to pass instance data through the framework, structs are used. Every framework method follows the signature as defined in the specification, with one addition; the first argument is a pointer to a struct instance. The structure of these structs are hidden behind a typedef to be able to control access to, and to limit/prevent modifications to the struct its data. So basically the pointer is merely used as a handle to be able to track instances.


Java uses packages for the namespace of classes, and methods are grouped by class. In C there is no alternative. To prevent name clashes, function names are prefixed with the name of the class defined in the specification.

Function in C:

celix_status_t bundleContext_registerService(BUNDLE_CONTEXT context, char * serviceName, void * svcObj, HASHTABLE properties, SERVICE_REGISTRATION *registration);

Method in Java:

public ServiceRegistration registerService(String clazz, Object service, Dictionary properties);

More details about the mapping from Java to C can be found at Mapping

Imported and Exported Packages

The OSGi Specification uses packages for importing and exporting service interfaces and additional types. As explained before, C does not have packages, so it is not possible to export packages. Instead, services are exported and imported. This implies that the service struct has to be available to the using components. There is also no programmatic protection to types used in the service implementation. If the definition of types are available, they can be used. So it is up to the implementer of a service to define the struct and public types separately from the inner types of the component. The public definitions can be published in a header file.

Exception Handling

OSGi uses exceptions to report errors and problems. Celix return a status from every call. This requires that each method must return an integer which indicates the state. For returned pointers/values (OUT) call-by-reference must be used.