In the first part of the Karaf Tutorial we learned how to use maven and blueprint to offer and use pojo services and how to use the http service to publish a servlet.
In this second part we concentrate on configuration for our OSGi bundles. Unlike servlet containers OSGi contains a very good specification for configuration: The Config Admin Service from the OSGi enterprise spec. In this tutorial we will cover ussing the Config Admin Service with pure OSGi and blueprint and how to automatically deploy config files with your bundles.
The practical parts of this tutorial can be found on github in https://github.com/cschneider/Karaf-Tutorial/tree/master/configadmin
We will first get a fast overview of the Configuration Admin Service spec. There two main interfaces for us to use:
- ConfigurationAdmin - Allows to retrieve and change configurations. This service is offered by the Config Admin Service implementation
- ManagedService - Allows to react on configuration changes. You have to implement this and register it as a service to get notified
So basically a configuration in the Config Admin Service is a Dictionary that contains attributes and their values. The Dictionary is identified by a persistent identifier (pid). This is simply a String that should uniquely identify the configuration.
While you can retrieve a configuration using the ConfigurationAdmin.getConfiguration interface I would not recommend to do so. OSGi is very dynamic so it may happen that your bundle starts before the config admin service or that the config admin service did not yet read the configuration. So you may end up sometimes getting Null for the configuration.
So the recommended way is to use a ManagedService and react on updates. If your bundle can not start without config then it is a good idea to create the pojo class to be configured on the first update received.
As we want to implement a clean style of how to work with configuration the class to be configured should be a pure pojo. While it is of course possible to simply implement the ManagedService interface and work with the Dictionary directly this will make you depend on OSGi and the current Config Admin Service spec. So instead we use a simple bean class that has a title property. Additionally I added a refresh method that should be called after all configuration was changed.
So our goal is to configure the title when the configuration changes and then call refresh. We will do this in pure OSGi and in blueprint.
The first practical part in this tutorial shows how to use the config admin service using just OSGi interfaces. While this is probably not the way you will do it later it helps to understand what happens under the hood.
You can find the implementation in the subdirectory configapp (https://github.com/cschneider/Karaf-Tutorial/tree/master/configadmin/configapp)
So we first need a pom file for the maven build. You best start with the pom from the configapp example.
If you start fresh you will have to use the maven-bundle-plugin to make your project a OSGi bundle and you need to add two dependencies:
The first is for the config admin service interfaces and the second is to be able to create the Activator and contains the basic OSGi interfaces.
Now we will care about updating the MyApp class. The following little class does the trick. We implement the ManagedService interface to talk to the Config Admin Service. So we get called whenever the config changes. The first thing is to check for null as this can happen when the config is removed. We could a this point stop our MyApp but to keep it simple we just ignore those. The next step is to create the MyApp class. Normally you would do this in the Activator but then you would have to be able to work with an empty configuration which is not always desired. The last part is to simply call the setter with the value from the config and call refresh after all settings were made.
Of course this does not yet do anything. The last step is to register the ConfigUpdater in the Activator.start. We simply use registerService like for every other service. The only special thing is that you have to set the property SERVICE_PID to your config pid so the Config Admin Service knows what config you want to watch.
- build the project with mvn install.
- Start a fresh Karaf instance
- Copy the configapp.jar bundle from the target dir to the Karaf deploy dir
Now we notice that nothing seems to happen. Calling list in the Karaf console you should be able to see that the bundle is indeed started but it will not do create any output as there is no config.
We still need to create the config file and set the title.
- copy the existing file /configadmin-features/src/main/resources/ConfigApp.cfg to the /etc dir of the Karaf instance
The important part here is that the filename has to be <pid>.cfg. So the config admin service will find it.
Now the fileinstall bundle will detect the new file in etc. As the ending is .cfg it will consider it to be a config admin resource and create or update the Config Admin Service configuration with the pid determined from the file name.
So you should now see the following in the Karaf console. This shows that the configuration change was correctly detected and forwarded. If you now change the file using an editor and save the change will alsobe propagated.
Type the following in the Karaf console:
Among other configs you should find the above config "ConfigApp". The config shows where it has been loaded from, the pid and of course all properties we set in the file.
We can also change the config:
We see that the change is directly propagated to our bundle. If you look into the config file in etc you can see that the change is also persisted to the file. So the change will still be there if we restart Karaf.
After we have worked with the Config Admin Service in pure OSGi we will now look how the same can be achieved in Blueprint. Fortunately this is quite easy as Blueprint does most of the work for us.
We simply define a cm:property-placeholder element. This works similar to property place holder with files but works with the Config Admin Service. We need to supply the config PID and the update strategy.
As strategy we select "reload". This means that after a change the blueprint context is reloaded to reflect the changes. We also set default properties that will be used when the config PID is not found or an attribute is not present.
The integration with our bean class is mostly a simple bean definition where we define the title property and assign the placeholder which will be resolved using the config admin service. The only special thing is the init-method. This is used to give us the chance to react after all changes were made like in the pure OSGi example.
For bluenprint we do not need any maven dependencies as our Java Code is a pure Java bean. The blueprint context is simply activated by putting it in the OSGI-INF/blueprint directory and by having the blueprint extender loaded. As blueprint is always loaded in Karaf we do not need anything else.
In the above xml please remove the backslashes around title. This is just to avoid confluence interpreting it as a wiki macro.
After we have successfully used the Config Admin Service the only thing that remains to go into production is to deploy our bundle together with a default configuration. This can be done using a Karaf feature file. We define our feature with the bundles it needs and simply add a configfile element. This makes Karaf deploy the given file into the etc directory of our Karaf installation. If the file is already present then it will not be overwritten.
So one last question is how to deploy the config to maven for the configfile element to find it. This happens like for the feature with the build-helper-maven-plugin in Karaf See the pom file for details how to use it.
During this tutorial we have learned how the Config Admin Service works and how to use it with pure OSGi and blueprint. We have also seen how to build and deploy our projects together with documentation.
While this is already very usefull some small things are missing in my opinion. The first thing is that configfile does not really seem to be consistent with the config admin service. In fact Karaf does not use the config admin service to deploy the file. So what I would like to see is that the also existing config element not only writes the config to the config admin service but also persists it. Fortunately my colleague Jean Baptiste is already working on this. See https://issues.apache.org/jira/browse/KARAF-888
The other thing is that for enterprise environments a config admin service with some additional features is needed. One thing is that it should be possible to do configuration on a whole network of servers with a central source for configuration and a nice UI. The other thing is that you would like to not only deploy the default config but also the config the admin really wants for the system. So I imagine that you should be able to define a deployment plan with bundles and features to install but also with the required configuration changes. If this is done right it will allow good audits of deployment and config changes and will also allow an admin to roll back a change in case something goes wrong. I hope we can provide some of this in one of the next Talend ESB EE releases.
Back to Karaf Tutorials