Configuration Admin¶
Concept¶
The Configuration Admin service allows to easily set, update and delete the configuration (a dictionary) of managed services.
The Configuration Admin service can be used by any bundle to configure a service, either by creating, updating or deleting a Configuration object.
The Configuration Admin service handles the persistence of configurations and distributes them to their target services.
Two kinds of managed services exist: * Managed Services, which handle the configuration as is * Managed Service Factories, which can handle multiple configuration of a kind
Note
Even if iPOPO doesn’t fully respect it, you can find details about the Configuration Admin Service Specification in the chapter 104 of the OSGi Compendium Services Specification.
Note
This page is highly inspired from the Configuration Admin tutorial from the Apache Felix project.
Basic Usage¶
Here is a bery basic example of a managed service able to handle a single configuration. This configuration contains a single entry: the length of a pretty printer.
The managed service must provide the pelix.configadmin.managed
specification, associated to a persistent ID (PID) identifying its
configuration (service.pid
).
The PID is just a string, which must be globally unique. Assuming a simple case where your pretty printer configurator receives the configuration has a unique class name, you may well use that name.
So lets assume, our managed service is called PrettyPrinter
and that name
is also used as the PID.
The class would be:
class PrettyPrinter:
def updated(self, props):
"""
A configuration has been updated
"""
if props is None:
# Configuration have been deleted
pass
else:
# Apply configuration from config admin
pass
Now, in your bundle activator’s start()
method you can register
PrettyPrinter
as a managed service:
@BundleActivator
class Activator:
def __init__(self):
self.svc_reg = None
def start(self, context):
svc_props = {"service.pid": "pretty.printer"}
self.svc_reg = context.register_service(
"pelix.configadmin.managed", PrettyPrinter(), svc_props)
def stop(self, context):
if self.svc_reg is not None:
self.svc_reg.unregister()
self.svc_reg = None
That’s more or less it. You may now go on to use your favourite tool to create and edit the configuration for the Pretty Printer, for example something like this:
# Get the current configuration
pid = "pretty.printer"
config = config_admin_svc.get_configuration(pid)
props = config.get_properties()
if props is None:
props = {}
# Set properties
props.put("key", "value")
# Update the configuration
config.update(props)
After the call to update()
the Configuration Admin service persists the new
configuration data and sends an update to the managed service registered with
the service PID pretty.printer
, which happens to be our PrettyPrinter class
as expected.
Managed Service Factory example¶
Registering a service as a Managed Service Factory means that it will be able to receive several different configuration dictionaries. This can be useful when used by a Service Factory, that is, a service responsible for creating a distinct instance of a service according to the bundle consuming it.
A Managed Service Factory needs to provide the
pelix.configadmin.managed.factory
specification, as shown below:
class SmsSenderFactory:
def __init__(self):
self.existing = {}
def updated(pid, props):
"""
Called when a configuration has been created or updated
"""
if pid in self.existing:
# Service already exist
self.existing[pid].configure(props)
else:
# Create the service
svc = self.create_instance()
svc.configure(props)
self.existing[pid] = service
def deleted(pid):
"""
Called when a configuration has been deleted
"""
self.existing[pid].close()
del self.existing[pid]
The example above shows that, differently from a managed service, the managed service factory is designed to manage multiple instances of a service.
In fact, the updated
method accept a PID and a dictionary as arguments,
thus allowing to associate a certain configuration dictionary to a particular
service instance (identified by the PID).
Note also that the managed service factory specification requires to implement
(besides the getName method) a deleted
method: this method is invoked when
the Configuration Admin service asks the managed service factory to delete a
specific instance.
The registration of a managed service factory follows the same steps of the managed service sample:
@BundleActivator
class Activator:
def __init__(self):
self.svc_reg = None
def start(self, context):
svc_props = {"service.pid": "sms.sender"}
self.svc_reg = context.register_service(
"pelix.configadmin.managed.factory", SmsSenderFactory(),
svc_props)
def stop(self, context):
if self.svc_reg is not None:
self.svc_reg.unregister()
self.svc_reg = None
Finally, using the ConfigurationAdmin interface, it is possible to send new or updated configuration dictionaries to the newly created managed service factory:
@BundleActivator
class Activator:
def __init__(self):
self.configs = {}
def start(self, context):
svc_ref = context.get_service_reference("pelix.configadmin")
if svc_ref is not None:
# Get the configuration admin service
config_admin_svc = context.get_service(svc_ref)
# Create a new configuration for the given factory
config = config_admin_svc.create_factory_configuration(
"sms.sender")
# Update it
props = {"key": "value"}
config.update(props)
# Store it for future use
self.configs[config.get_pid()] = config
def stop(self, context):
# Clear all configurations (for this example)
for config in self.configs:
config.delete()
self.configs.clear()