Services
A service is an object that is registered to the service registry of the framework, associated to a set of specifications it implements and to properties.
The bundle that registers the service must keep track of the
ServiceRegistration
object returned by the framework.
It allows to update the service properties and to unregister the service.
This object shall not be accessible by other bundles/services, as it gives
access and control over the life cycle of the service it represents.
Finally, all services must be unregistered when their bundle is stopped.
A consumer can look for a service that matches a specification and a set of
properties, using its BundleContext
.
The framework will return a ServiceReference
object, which provides a
read-only access to the description of its associated service:
properties, registering bundle, bundles using it, etc.
Properties
When registered and while it is available, the properties of a service can be set and updated by its provider.
Although, some properties are reserved for the framework, each service has at least the following properties:
Name |
Type |
Description |
---|---|---|
objectClass |
list of str |
List of the specifications implemented by this service |
service.id |
int |
Identifier of the service. Unique in a framework instance |
service.scope |
str |
OSGi service scope: singleton, prototype or factory |
The framework also uses the following property to sort the result of a service look up:
Name |
Type |
Description |
---|---|---|
service.ranking |
int |
The rank/priority of the service. The lower the rank, the more priority |
Service Factory
A service factory is a pseudo-service with a specific flag, which can create individual instances of service objects for different bundles. Sometimes a service needs to be differently configured depending on which bundle uses the service. For example, the log service needs to be able to print the logging bundle’s id, otherwise the log would be hard to read.
A service factory is registered in exactly the same way as a normal service,
using register_service()
, with the
factory
argument set to True
.
The only difference is an indirection step before the actual service object is
handed out.
The client using the service need not, and should not, care if a service is generated by a factory or by a plain object.
A simple service factory example
class ServiceInstance:
def __init__(self, value):
self.__value = value
def cleanup(self):
self.__value = None
def get_value(self):
return self.__value
class ServiceFactory:
def __init__(self):
# Bundle -> Instance
self._instances = {}
def get_service(self, bundle, registration):
"""
Called each time a new bundle requires the service
"""
instance = ServiceInstance(bundle.get_bundle_id())
self._instances[bundle] = instance
return instance
def unget_service(self, bundle, registration):
"""
Called when a bundle has released all its references
to the service
"""
# Release connections, ...
self._instances.pop(bundle).cleanup()
bundle_context.register_service(
"sample.factory", ServiceFactory(), {}, factory=True)
Note
The framework will cache generated service objects. Thus, at most one service can be generated per client bundle.
Prototype Service Factory
A prototype service factory is a pseudo-service with a specific flag, which can create multiple instances of service objects for different bundles.
Each time a bundle requires the service, the prototype service factory is
called and can return a different instance.
When called, the framework gives the factory the Bundle
object
requesting the service and the ServiceRegistration
of the requested
service.
This allows a single factory to be registered for multiple services.
Note that there is no Prototype Service Factory implemented in the core Pelix/iPOPO Framework.
A Prototype Service Factory is registered in exactly the same way as a normal
service, using register_service()
,
with the prototype
argument set to True
.
A simple prototype service factory example:
class ServiceInstance:
def __init__(self, value):
self.__value = value
def cleanup(self):
self.__value = None
def get_value(self):
return self.__value
class PrototypeServiceFactory:
def __init__(self):
# Bundle -> [instances]
self._instances = {}
def get_service(self, bundle, registration):
"""
Called each time ``get_service()`` is called
"""
bnd_instances = self._instances.setdefault(bundle, [])
instance = ServiceInstance(
[bundle.get_bundle_id(), len(bnd_instances)])
bnd_instances.append(instance)
return instance
def unget_service_instance(self, bundle, registration, service):
"""
Called when a bundle releases an instance of the service
"""
bnd_instances[bundle].remove(service)
service.cleanup()
def unget_service(self, bundle, registration):
"""
Called when a bundle has released all its references
to the service
"""
# Release global resources...
# When this method is called, all instances will have been cleaned
# up individually in ``unget_service_instance``
if len(self._instances.pop(bundle)) != 0:
raise ValueError("Should never happen")
bundle_context.register_service(
"sample.proto", PrototypeServiceFactory(), {}, factory=True)
Note
A Prototype Service Factory is considered as a Service Factory, hence
both is_factory()
and
is_prototype()
will return True
for this kind
of service
API
The service provider has access to the ServiceRegistration
object
created by the framework when register_service()
is
called.
- class pelix.framework.ServiceRegistration(framework: Framework, reference: ServiceReference[T], properties: Dict[str, Any], update_callback: Callable[[ServiceReference[T]], None])
Represents a service registration object
- Parameters:
framework – The host framework
reference – A service reference
properties – A reference to the ServiceReference properties dictionary object
update_callback – Method to call when the sort key is modified
- get_reference() ServiceReference[T]
Returns the reference associated to this registration
- Returns:
A ServiceReference object
- set_properties(properties: Dict[str, Any]) None
Updates the service properties
- Parameters:
properties – The new properties
- Raises:
TypeError – The argument is not a dictionary
- unregister() None
Unregisters the service
Consumers can access the service using its ServiceReference
object,
unique and constant for each service.
This object can be retrieved using the BundleContext
and its
get_service_reference*
methods.
A consumer can check the properties of a service through this object, before
consuming it.
- class pelix.framework.ServiceReference(bundle: Bundle, properties: Dict[str, Any])
Represents a reference to a service
- Parameters:
bundle – The bundle registering the service
properties – The service properties
- Raises:
BundleException – The properties doesn’t contain mandatory entries
- get_bundle() Bundle
Returns the bundle that registered this service
- Returns:
the bundle that registered this service
- get_properties() Dict[str, Any]
Returns a copy of the service properties
- Returns:
A copy of the service properties
- get_property(name: str) Any
Retrieves the property value for the given name
- Returns:
The property value, None if not found
- get_property_keys() Tuple[str, ...]
Returns an array of the keys in the properties of the service
- Returns:
An array of property keys.
- get_using_bundles() List[Bundle]
Returns the list of bundles that use this service
- Returns:
A list of Bundle objects
- is_factory() bool
Returns True if this reference points to a service factory
- Returns:
True if the service provides from a factory
- is_prototype() bool
Returns True if this reference points to a prototype service factory
- Returns:
True if the service provides from a prototype factory
Finally, here are the methods of the BundleContext class that can be used to handle services:
- class pelix.framework.BundleContext(framework: Framework, bundle: Bundle)
The bundle context is the link between a bundle and the framework. It is unique for a bundle and is created by the framework once the bundle is installed.
- Parameters:
framework – Hosting framework
bundle – The associated bundle
- get_all_service_references(clazz: None | str | Type[T] = None, ldap_filter: None | str | LDAPFilter | LDAPCriteria = None) List[ServiceReference[T]] | None
Returns an array of ServiceReference objects. The returned array of ServiceReference objects contains services that were registered under the specified class and match the specified filter expression.
- Parameters:
clazz – Class implemented by the service
ldap_filter – Service filter
- Returns:
The sorted list of all matching service references, or None
- get_service(reference: ServiceReference[T]) T
Returns the service described with the given reference
- Parameters:
reference – A ServiceReference object
- Returns:
The service object itself
- get_service_reference(clazz: None | str | Type[T], ldap_filter: None | str | LDAPFilter | LDAPCriteria = None) ServiceReference[T] | None
Returns a ServiceReference object for a service that implements and was registered under the specified class
- Parameters:
clazz – The class name with which the service was registered.
ldap_filter – A filter on service properties
- Returns:
A service reference, None if not found
- get_service_references(clazz: None | str | Type[T], ldap_filter: None | str | LDAPFilter | LDAPCriteria = None) List[ServiceReference[T]] | None
Returns the service references for services that were registered under the specified class by this bundle and matching the given filter
- Parameters:
clazz – The class name with which the service was registered.
ldap_filter – A filter on service properties
- Returns:
The list of references to the services registered by the calling bundle and matching the filters.
- register_service(clazz: str | Type[T] | Iterable[str | Type[Any]], service: T, properties: Dict[str, Any] | None, send_event: bool = True, factory: bool = False, prototype: bool = False) ServiceRegistration[T]
Registers a service
- Parameters:
clazz – Class or Classes (list) implemented by this service
service – The service instance
properties – The services properties (dictionary)
send_event – If not, doesn’t trigger a service registered event
factory – If True, the given service is a service factory
prototype – If True, the given service is a prototype service factory (the factory argument is considered True)
- Returns:
A ServiceRegistration object
- Raises:
BundleException – An error occurred while registering the service
- unget_service(reference: ServiceReference[Any]) bool
Disables a reference to the service
- Returns:
True if the bundle was using this reference, else False
Listening to service events
The bundle context can be used to register to service events, using the following methods:
- class pelix.framework.BundleContext(framework: Framework, bundle: Bundle)
The bundle context is the link between a bundle and the framework. It is unique for a bundle and is created by the framework once the bundle is installed.
- Parameters:
framework – Hosting framework
bundle – The associated bundle
- add_service_listener(listener: ServiceListener, ldap_filter: None | LDAPCriteria | LDAPFilter | str = None, specification: str | Type[Any] | Iterable[str | Type[Any]] | None = None) bool
Registers a service listener
The service listener must have a method with the following prototype:
def service_changed(self, event): ''' Called by Pelix when some service properties changes event: A ServiceEvent object ''' # ...
- Parameters:
listener – The listener to register
ldap_filter – Filter that must match the service properties (optional, None to accept all services)
specification – The specification that must provide the service (optional, None to accept all services)
- Returns:
True if the listener has been successfully registered
- remove_service_listener(listener: ServiceListener) bool
Unregisters a service listener
- Parameters:
listener – The service listener
- Returns:
True if the listener has been unregistered
A bundle listener must implement the following interface:
- class pelix.internals.registry.ServiceListener(*args, **kwargs)
Protocol that must be implemented by a service listener
- service_changed(event: ServiceEvent[Any]) None
Notified when a service event occurred
- Parameters:
event – Service event