Although OSGi doesn't force us, we should consider to do configuration the OSGi-way. If we would not find an appropriate solution, it would be possible to implement an own or use another configuration service if there is one. Doing it the OSGi-way would enable other bundles to interact with james configuration and give us more interoperability.
How The ConfigurationAdmin works
In OSGi configuration is handled by the ConfigurationAdmin service. Every component that needs to be configured does subscribe to a configuration by using a service.pid. The service.pid is a system-wide unique identifier that is e.g. constructed by using the full qualified class name. The configuration for a component consists of a java-style properties like dictionary of key/value pairs.
When the component first subscribes to the configuration or every time the configuration is changed ConfigurationAdmin calls the components that implements ManagedService update method.
public void updated(java.util.Dictionary properties) throws ConfigurationException
Note: Spring OSGi will even support direct injection of values into setters. But maybe this is even not appropriate at all, in some cases.
And what about fancy nested XML configuration? For updating a single bean or a static hierarchy of beans the flat approach should be sufficient and actually the most handy:
component.option1=test component.bean1.option1=test component.bean1.option2=test component.bean2.option1=test component.bean2.bean1.option1=test
For components that have 0..n child beans OSGi provides a ManagedServiceFactory interface. For each child that is added/modified the update method gets called together with a new unique pid.
public void updated(java.lang.String pid, java.util.Dictionary properties) throws ConfigurationException
This pid could be usually combined with the full class name and an incrementing number for each child.
If the child gets deleted, deleted is called
public void deleted(java.lang.String pid)
The component should hold an own map pid -> child to manage update/delete properly.
As an example from the James world, you could think of multiple mailets that get attached to a spoolmanager processor.
ConfigurationAdmin And Persistence
Although some people dislike overloaded nested xml file configuration a long pseudo-nested properties-file could be really unhandy, too. So how comes the configuration into the ConfigurationAdmin?
ConfigurationAdmin service itself allows also the modification of configuration.
Here an example for a ManagedServiceFactory: add a new child to Component1
Configuration conf = getConfigurationAdmin().createFactoryConfiguration( "org.apache.james.Component1"); String newPid=conf.getPid()); Dictionary newOptions = new Hashtable(); newOptions.put("key", "value"); conf.update(newOptions);
And now we modify it using the pid newPid
Configuration conf = getConfigurationAdmin().getConfiguration(newPid); Dictionary options = conf.getOptions(); options.put("key2","value2"); conf.update(options);
In both cases Component1.updated(pid,dictionary) gets called.
To delete the child named newPid:
Configuration conf = getConfigurationAdmin().getConfiguration(newPid); conf.delete();
The changes made to the ConfigurationAdmin are automatically made persistent. But the format used by the ConfigurationAdmin to save the data depends on its implementation. So there is a solution needed to get the data into the ConfigurationAdmin. Because the possibilities to make configuration are not limited this approach makes sense. (UI/files/Ldap...) At the moment the only existing solution I know is a console offered by knopflerfish. This is great for making small quick only changes but not an option for bigger reconfiguration.
The idea I have in mind is a custom import/export service. This would allow the use of configuration files. At startup and every time a change in the files is signaled (manually or automatically) the custom configuration gets imported into the ConfigurationAdmin. If desired it is imaginable to even allow an export of online changed options.
As input file we could choose xml/properties as appropriately. We have to find a way how the input data could be mapped to ConfigurationAdmin properties without the need of (too much) depended code. An ideal way would be to do it by convention or e.g. a xml-description file.
But isn't that much more complicated as what we are doing now?
At the moment our components deal directly with the structure of the config.xml which makes them very dependent. By only passing a key/value map we are avoiding the dependence but the actual work of mapping human-readable config file to attributes has to be done somewhere.
So isn't the persistence done by the ConfigurationAdmin just overhead?
If configuration is done only via external config files, yes. But there are examples where it becomes useful:
- When config is changed online the user may decide to discard his
changes by reloading the config files
- If config is provided by an LDAP server the admin could decide to allow the server to start with the last known config values when the LDAP server is unavailable at startup.
- there maybe situations where configuration is changed so fast that
an export to an external place (big config file/slow and overloaded ldap server) on every change is not appropriate.