Value created by Developers

The following is a non serious article about using generative AI on top of silly ideas.

Recently I was reading a paper on the internet about developers creating value. At some point it hit me, that you can actually combine those two words “value” and “developer” into a single one: “devalueper”. As it is so obvious to do this, I searched the internet for them, but didn’t find anything. So I asked my new friend copilot about the definition of that term. And I was really surprised that copilot new about it and also came up with another term. While I thought about “devalueper” being something positive, a developer creating value, it seems it is quite the opposite. This is what copilot came up with:

Devaluepers is a term that combines the words “developers” and “devalue”. It is used to describe developers who produce low-quality code, cause bugs, or harm the value of a project. It is often used as an insult or a joke by other developers. Some examples of devaluepers are:

  • Developers who copy and paste code from the internet without understanding it or testing it.
  • Developers who write code that is hard to read, maintain, or debug.
  • Developers who ignore best practices, standards, or guidelines for coding.
  • Developers who introduce security vulnerabilities or performance issues in their code.
  • Developers who do not document their code or write tests for it.

Devaluepers are the opposite of valuepers, who are developers who create high-quality code, solve problems, and add value to a project. Valuepers are respected and appreciated by their peers and clients. Some examples of valuepers are:

  • Developers who write clean, elegant, and efficient code that follows the principles of software engineering.
  • Developers who use appropriate tools, frameworks, and libraries to enhance their productivity and functionality.
  • Developers who adhere to coding conventions, style guides, and best practices for their language and platform.
  • Developers who test their code thoroughly, fix bugs, and optimize performance.
  • Developers who document their code clearly, write unit tests, and use version control.

If you want to be a valueper and not a devalueper, you should:

  • Learn the fundamentals of programming and computer science.
  • Keep your skills and knowledge up to date with the latest trends and technologies.
  • Choose the right tools and languages for your project and use them effectively.
  • Write code that is easy to understand, modify, and reuse.
  • Follow the standards and guidelines for your code quality, security, and performance.
  • Test your code regularly, debug it carefully, and refactor it when necessary.
  • Document your code properly, write unit tests, and use version control.
  • Seek feedback, collaborate, and communicate with other developers.

The Truth about AEM Run Modes

The Adobe Experience Manager as a Cloud Service (AEM) is a very flexible platform. It allows (customer) developers to extend the two main services, author and publish via Java code. In many cases the exact same code needs to run on both services. However, there are use cases where the code should only run on one of them or behave differently. Similar, a different behaviour or configuration should be used in a development environment versus the production environment.

AEM provides the concept of run modes. A run mode indicates the type of the service as well as the environment type the service is running in. Therefore mutual exclusive run modes exists for author and publish as well as for dev, stage, and prod.

It is important to note that the concept of run modes is not a runtime concept. Following the twelve-factory methodology it has been designed as a deployment and provisioning concept. However, over time some bad pracices spread using run mode information at runtime to select different configurations or code paths.

Don’t Deploy Unused Code

If you want to run different code on author and publish, make the distinction through different deployments: deploy different code on author than on publish. This can be easily achieved by putting common code into a shared bundle and creating separate bundles for author or publish. Use a similar approach for components residing in the repository. This way you do not deploy unused or dead code.

It is bad practice to deploy the exact same code and then clutter the code with if statements checking for the run mode. The clean way is to just deploy what needs to run on a service.

Use Environment-Specific Configuration Values

It is a common use case to have different configuration values for different environments. For example when connecting to external services, different endpoints might be used for development than for production. For this create an OSGi configuration that uses placeholders:

  "service.endpoint": "$[env:EXTERNAL_SERVICE_URL]",

Use Service-Specific Configurations

Similar to environment-specific configuration values there are use cases where you want to have a different configuration for author than for publish. For this just create two OSGi configurations: one is bound to the author run mode, the other one is bound to the publish run mode.

This can also be combined with placeholders for having environment-specific values which also differ between author and publish.

Advanced Use Cases

Following the above three rules gives a clear separation and code. It avoids special casing within code which is over time hard to understand and maintain. Even more advanced use cases can be handled this way. For example if you want to have the same service on author and publish but with a different algorithm: Author is always using fresh data within a service, while the data is cached on publish within that service. In such case you might want to use a behavioural pattern like the strategy pattern. This goes back to the first rule. Create different code deployments for author and publish and then use the code through OSGi services from some common shared code base.

Use Run Modes for Deployment and Provisioning

In summary, it is good practice to leverage run modes for describing the deployment and provisioning. Define precisely which bundles and configurations apply to both, author and publish, as well as which of them only apply for one service.

Leverage environment-specific configuration values to provision different values for each environment type. But refrain from trying to deploy different code per environment type.

Guide on Using OSGi Configurations

Configuration Admin is amongst the most used services from the OSGi specification. But on the other hand, the usage is barely noticed. Component frameworks like Declarative Services (DS) or CDI Integration take care of the heavy lifting. Configuration Admin allows to create, update and delete configurations. It is up to the implementation where these configurations are stored. A configuration has a unique persistent identifier (PID) and a dictionary of properties. This guide explains how to best interact with Configuration Admin when writing OSGi code. It is based on over a decade of experience working on large enterprise applications using OSGi. It might not apply to every OSGi application.

Do not talk to Configuration Admin

The basic rule for dealing with OSGi configurations is very simply: do not write code that talks to Configuration Admin directly. But of course you should use OSGi configurations to configure your application. So how does that work? The answer is very easy: let someone else talk to Configuration Admin and do the work for you.

A very nice way to develop OSGi components and services is to use Declarative Services (DS). By using Java annotations, the component code tells DS which configuration(s) it wants to consume. DS does all the hard work behind the scenes. Defining a Component Property Type simplifies the usage further. A component property type provides type conversion and default values.

But if it is that easy why am I writing this? It is good to know some of the details about configuration handling – especially in cases where you cannot use DS.

Be Aware of Configuration Plugins

OSGi configurations are used to configure the whole system. In many cases that includes configurations for connections to other systems or services. Such configurations usually include endpoints and credentials. The values for these properties might depend on your environment: a different endpoint is used in development than in production. In addition, you don’t want to store credentials or any other secrets directly in your code or configuration.

A good mechanism for managing such configurations is to use “late binding” of the values. Instead of storing the values directly in the OSGi configuration you use placeholders. The placeholders get replaced at runtime with the real values. Support for such placeholders usually comes in the form of Configuration Plugins. The Apache Felix Interpolation Plugin is a very nice plugin supporting environment variables, system properties and secrets. For example, a configuration for a connection could look like this (using the Configurator JSON notation) :

  "" : {
    "url" : "$[env:BUSINESS_SERVER_URL]",
    "user": "$[env:BUSINESS_SERVER_USER]",
    "password" : "$[]"

It uses environment variables for the url and the username and a secret for the password. With such an approach, the configuration stored in Configuration Admin only contains the above visible placeholders. However, when the configurations is provided to the code using the configuration, a plugin like the Apache Felix Interpolation Plugin needs to be invoked, replacing the placeholders with real values and then handing out the configuration.

The good news is, if you are using Declarative Services this happens automatically and you don’t have to worry about it.


However, sometimes there are use cases where you either can’t or want to use Declarative Services (or a similar component framework). The second best way to deal with configurations is to register either a ManagedService or ManagedServiceFactory. These are basically callbacks which are called by Configuration Admin with the configurations, the registered service is interested in. And the good part is, Configuration Admin calls the configuration plugins before handing out the configuration.

Threefore it is advisable to either use Declarative Services or register a managed service for consuming OSGi configurations. But there might be some very rare use cases where both is not possible and you need to talk to Configuration Admin directly.

How to Talk to Configuration Admin

First, as explained, try to avoid this situation. If you can’t, be aware of the following points. If you want to get a configuration from Configuration Admin do not use one of the getter methods like getConfiguration or getFactoryConfiguration. These methods have side effects and will create such a configuration if it does not exist! Use listConfigurations instead:

ConfigurationAdmin ca = ...;
Configuration[] configs =  
if ( configs != null && configs.length > 0 ) {
    // use configuration (we just assume we got only one configuration back)
    Configuration cfg = configs[0];

Once you have the configuration, the next thing is to get its properties – there is a method called getProperties on a Configuration object – but do you remember the placeholders and configuration plugins? If you call getProperties these are not invoked, and the values will be the placeholders itself! Therefore don’t use that method for consuming configurations – use getProcessedProperties instead. That method invokes all plugins before handing out the properties.

To identify the caller getProcessedProperties takes a required ServiceReference as a parameter. If you already have a reference at hand, use it – if not, you need to register a service in the service registry.

Avoid Configuration Binding

Finally, if you want to create configurations, the previously mentioned getter methods (getConfiguration and getFactoryConfiguration) can be used to do so. Make sure that you use the variant which takes the additional location parameter and pass in null for the location. If you don’t do this, then the configuration gets bound to your bundle and will not be delivered to other consumers anymore. Configuration binding and targeting is a complicated topic – which is best to avoid. Of course, as always, there are good use cases for these features. Only use it if you really need it. This article contains some information about configuration binding and DS.

I hope with these tips, handling OSGi configurations becomes very easy. Just as a recap:

  • Use component frameworks like Declarative Services – all hard work is done for you
  • If that’s not possible, use ManagedService or ManagedServiceFactory – again most of the hard work is done for you
  • If that’s also not possible, make sure to not create configurations as a side effect and use the right method to get properties
  • Avoid configuration binding and targeting if possible

OSGi Declarative Services – Configuration Binding

As explained in OSGi Components – Simply Simple – Part II when using Declarative Services (DS) you get support for configurations stored in a Configuration Admin service for free. When debugging a system, you want to find out if a component is actually using a certain configuration. This post gives you some tips on inspecting your application.

The ServiceComponentRuntime Service

Since R6 of the OSGi specification, the Declarative Service Specification defines the ServiceComponentRuntime service. This service can be used at runtime to get the status of the components and services managed by DS. That service returns the top level DTO named ComponentDescriptionDTO. This is basically a representation of the XML provided by a bundle and read by DS. So the first step is to find the ComponentDescriptionDTO for your component.

Once you have found the description you can use the ServiceComponentRuntime service to get the ComponentConfigurationDTO(s) for the description. This DTO has some useful information about the status of your component. Most importantly it shows whether the component is satisfied. If the component is not satisfied more information about the reason is provided. For example, a reference might not be satisfied. You can now iterate over the references and find the one(s) which is not satisfied.

For configurations, the first check is to see whether the PID of the configuration you are interested in is listed in the configurationPid field of the ComponentDescriptionDTO. However this does not provide you the information whether such a configuration exists and is used by your component.

Therefore you have to check the properties from the ComponentConfigurationDTO and check whether the properties contain a property and whether the value for this property contains the configuration PID you are interested in. Note that the type of this property can be a String, an array of Strings or a collection of Strings.

This requires a little bit of coding, iterating over the various DTOs and searching for the info you are interested in.

The Declarative Services WebConsole Plugin

Another way of figuring this out without writing code is to use the famous Apache Felix Web Console. The web console allows you to manage and monitor a running OSGi application through a web interface. The Apache Felix project has a plugin for the web console. The Apache Maven coordinates for the current release are org.apache.felix/org.apache.felix.webconsole.plugins.ds/2.0.6.

Once this plugin is installed it gives you an UI for the previously mentioned ServiceComponentRuntime service and you can browse through the DTOs and find the ones you’re interested in.

Bound OSGi Configurations

An OSGi configuration supports the concept of configuration binding. By checking the bundle location field of a configuration object, it is possible to figure our whether a managed service is using this configuration.

However, don’t use this information to figure out whether a configuration is used by a DS component. Configuration binding is considered legacy and more important a DS implementation is not required to bind a configuration to the bundle of a bundle containing the component. While older implementations of the Apache Felix SCR did the configuration binding, newer versions don’t.

Therefore don’t even look there if you want to figure out whether your configuration is used by a DS component.

New OSGi Configuration Features in R7

The specification groups of the OSGi Alliance are currently in the process of finalizing the work for the upcoming OSGi R7 release. The major part of the specification work is done. This post explains some new functionality around OSGi configurations. Let’s start with a very brief introduction.

Configuration Admin

One of the most used but on the other hand barely noticed services from the OSGi compendium specification is the Configuration Admin. This service allows to create, update and delete configurations. It is up to the implementation where these configurations are stored. A configuration has a unique persistent identifier (PID) and a dictionary of properties.

Usually configurations are tied to an implementation of an OSGi service but configurations can be used for any purpose like database connections, current temperature or the set of available nodes in a cloud setup. While the Configuration Admin service has an API to find configurations or create them, it supports a more inversion of control like behavior by supporting a callback mechanism. The callback (known as the ManagedService interface) gets invoked for existing/created configurations of a certain PID and also if that configuration is deleted.

While this callback exists, its very often not used directly. The most common and easiest way to develop OSGi components and services is to use Declarative Services. Declarative Services (DS) provides build-in support for configurations. Simply by implementing an activation method the component can get it’s configuration. The implementor of that component does not have to worry whether such a configuration exists, gets deleted or is modified. DS takes care of all of this and invokes the right actions and the component.

In addition to single configurations, Configuration Admin provides support for factory configurations: multiple configurations of the same type, like for example different logger configurations for different categories or different database connection configurations. These factory configurations have a factory PID which is the same for all configurations of that type and a configuration PID to distinguish those configurations.

This should explain big picture, if you’re interested in more details, I refer you to the OSGi specifications. Especially the chapters about Configuration Admin and Declarative Services provide very valuable information. Let’s start talking about the new things.

Configurator Specification

R7 adds chapter 150, Configurator Specification. It basically consists of two parts. The first part describes a common textual representation of a set of configurations. Before this specification each and every tool was using its own format for provisioning configurations. For example, the famous Apache Felix File Install uses a properties like format. Other tools use slightly different formats. One problem is that you can’t simply switch from one tool to another and the other major problem is that some of the formats do not allow to specify the real type of a property value. For example, the value for the service ranking property must be of type Integer or your special implementation is expecting (for whatever reason) a value to be of type Byte. However some tools are simply always using a Long to represent numbers.

Therefore a common definition eliminates these problems and allows interchangeability of configurations between various tools. The format is JSON based and uses the PIDs of a configuration as the keys. The value is the configuration object with the properties:

  "": { 
    "port:Integer" : 300, 
    "array:int[]" : [2, 3, 4], 
    "collection:Collection<Integer>" : [2,3,4], 
    "complex": { 
      "a" : 1, 
      "b" : "two"

As you can see in the example, it is possible to specify the runtime type of a configuration property by separating the property name from the type using a colon. For example, the “port” property value is of type Integer, the “array” property value is an array of ints and the “collection” property value is of type Collection<Integer>. You can specify all allowed types for a configuration and the configurator implementation uses converting rules as defined by the Converter Specification – another of the new specifications of R7.

In addition, a configuration property can hold structured JSON as a string value. In the example above “complex” contains at runtime a string value of the specified JSON.

Factory configurations can be specified by using the following syntax: FACTORY_PID~NAME. With the R7 Configuration Admin it is possible to use a meaningful name to address factory components, the name (see below). The tilde separates the factory PID from the name:

  "my.factory.component~foo" : {
  "my.factory.component~bar" : {

OSGi Bundle Configurations

The second part of the configurator specification describes a new extender based mechanism that picks up configurations from within a bundle and applies them. A bundle can contain one or more JSON files with configurations and once the bundle is started the configurations will be put into Configuration Admin by the Configurator. The configurator manages the state handling and ordering in a deterministic way. For example, if two bundles contain a configuration for the same PID, a ranking mechanism is used to specify which configuration is put into Configuration Admin, regardless of their installation or start order.

With this new feature, provisioning of bundles becomes part of the OSGi specifications. The specification of the Configurator has driven the update of the Configuration Admin specification. So let’s talk about the most important new features.

Improved Factory Configuration Handling

With the upcoming R7 specification handling factory configurations has been greatly improved. Before, when you create a factory configuration, the PID part is randomly generated which makes identifying a particular factory configuration later on much harder. In addition, as the PID is auto generated, it has no meaning. With R7 it is now possible to specify the PID of a factory configuration, removing those problems in the future.

New methods on the Configuration Admin allow to create and retrieve factory configurations based on the factory PID and a name. These methods behave the same as the already existing methods for plain configurations. The PID for those factory components is generated by appending the name to the factory PID separated by a tilde. The Configurator uses this syntax to specify factory configurations.

Improved Configuration Plugin Handling

When a configuration is delivered to a managed service, the configuration is passed through registered configuration plugin services. Such a service can manipulate the configuration. One common use case is to handle place holders in the configuration properties and replace them with real values when delivered. For example  a property of a database connection configuration could just contain the value “${database.url}” which is replaced with the actual url when this configuration is passed to the component processing the configuration. Or if you have sensitive configuration data, you can store it encrypted in the configuration and just decrypt it in a configuration plugin before it is passed to the managed service.

While this mechanism sounds useful, it is only useful if you register a managed service. However, when you’re using Declarative Services for your components, the plugins are not called at all. Starting with R7 this gap is closed and the DS implementation uses a new functionality of the Configuration Admin service and calls the plugins before it is passing the configuration to components. This ensures, regardless how you get your configuration, plugins will be called making those use cases mentioned above possible.


The standard format for OSGi configurations is a great step forward for tooling and the Configurator implementation allows to deploy configurations through bundles in a standardized and well specified way. The update of the Configuration Admin resolves some long outstanding issues and allows for new use cases around configuration management. Of course, the final R7 specification will continue all the details.

Http Whiteboard – Simply Simple – Part III

This is the next part in the series about the OSGi http whiteboard. In my last post I explained how to create an own application servlet context and how to register web components with this context.

Bundle Resources

Doing as outlined in that post, resource handling (the get resource methods on the servlet context) might not work as you expect as you can’t get resources from the bundle where you web component is defined in. If you want this, you need first of all need to register your ServletContextHelper with the service scope Bundle – this ensures that every using bundle gets its own instance of the ServletContextHelper. And with that a servlet from bundle A gets only resources from bundle A, while the servlet from bundle B gets only the resources from that bundle – although they are registered within the same servlet context.

Now using DS this looks a little bit complicated, here is the code:

import java.util.Set;

import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.ServiceScope;
import org.osgi.service.http.context.ServletContextHelper;
import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;

        service = ServletContextHelper.class,
        scope = ServiceScope.BUNDLE,
        property = {
                HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME + "=" + AppServletContext.NAME,
                HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH + "=/guessinggame"
public class AppServletContext extends ServletContextHelper {

    public static final String NAME = "game";

    private ServletContextHelper delegatee;

    private void activate(final ComponentContext ctx) {
        delegatee = new ServletContextHelper(ctx.getUsingBundle()) {};

    public URL getResource(String name) {
        return delegatee.getResource(name);

    public String getMimeType(String name) {
        return delegatee.getMimeType(name);

    public Set<String> getResourcePaths(String path) {
        return delegatee.getResourcePaths(path);

    public String getRealPath(String path) {
        return delegatee.getRealPath(path);

As you can see in the above example, the ServletContextHelper is registered with the Bundle scope. In order to use the provided functionality of the abstract class ServletContextHelper in the activate method a delegatee is created with the bundle using the ServletContextHelper. The different methods then delegate to the delegatee.

This example looks a little bit complicated, but it is due to the fact that ServletContextHelper only provides to set the bundle through the constructor. And with Declarative Services this is currently a little bit more work to do. But for the upcoming OSGi R7 release, constructor injection is planned, then this should look much cleaner.

Http Whiteboard – Simply Simple – Part II

Developing web components with OSGi can be very simple if you’re using the OSGi Http Whiteboard Specification (see OSGi Compendium Chapter 140). For example the Apache Felix Http Implementation supports this standard.

Building your own Application

With traditional web application development, you usually package your application into its own web application archive (war file) and deploy this into an application server (servlet engine). Each application has its own servlet context and is mounted at some path. You can either mount an application at the root or any other path, e.g. at /my-app.

Using OSGI and the OSGi Http Whiteboard Specification, when you develop a web application you usually but the web components into a bundle and deploy this bundle. Depending on your needs you might also split up the web components into different bundles. But how do you create the equivalent of a servlet context known from web archive deployments?

You can create an own servlet context for your application through the Http Whiteboard. But there is no special deployment format, you simply deploy your app through bundles. And in contrast to traditional webapp deployment, this is leveraging all the well-known OSGi features. And of course source a context is represented through a service.

Let’s start with creating a simple application. The first thing you do is you create your own servlet context. This is done by registering a ServletContextHelper service (again for the examples I’m using Declarative Services and the corresponding annotations):

        service = ServletContextHelper.class,
        property = {
                HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME + "=" + AppServletContext.NAME,
                HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH + "=/guessinggame"
public class AppServletContext extends ServletContextHelper {}

As you can see from the example, ServletContextHelper is not an interface but an abstract class and your implementation gets all the default behaviour by simply extending this class. An abstract class has been chosen over the interface in order to be able to add methods to the service without breaking existing implementations. More important are the two required properties: a unique name and a context paths. The path is equivalent to the context path of a normal web application. The name is used to reference this servlet context. If you want to associate a servlet, resource, servlet filter, or listener to this context, you can do this by adding an additional property to select this context:

@Component(service = Servlet.class,
           property = { HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN + "=/game",
                        HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT + "=(" + HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME +"=" + AppServletContext.NAME + ")"})
public class GameServlet extends HttpServlet {...}

The context select property defines a filter expression to select the servlet context. While you can use any filter expression there, it is pretty common to simply filter against the name of the servlet context. If the context select property is missing, your web component is added to the default context.

The path of the servlet context is prepended before the path of the web component, so in the example above the servlet is mounted at “guessinggame/game”.

Building OSGi Bundles with Apache Maven

It seems that still today some developers think that creating an OSGi bundle is complicated – in contrast to simply creating a jar. Well, an OSGi bundle is a jar. The factor which distinguishes the two is that an OSGi bundle has additional manifest entries. Adding those using the right tooling is really simple.

The more difficult part is creating good modules: clearly separating public API from the implementation and correctly version the API based on the changes you made. But this is a general problem not related to OSGi at all and applies to any Java coding. There are some simple guidelines:

  • Leverage packages – put the API in a different package than your implementation. It’s also good still to use a package name for your implementation which makes clear that this is the implementation, e.g. by using impl as one part of the package name.
  • Start private and only make public if necessary. Once something is public, you never can make it private without breaking your clients. Starting private makes things easier.
  • Use semantic versioning. Whenever you change your public API, make sure to correctly increase the package version depending on the type of change you made.

Again, these are general guidelines and have nothing to do with OSGi. But OSGi uses headers in the manifest of your jar to know which packages from inside the jar are public API and the version of those packages. This is the export package header. Packages not listed there are private and not accessible to other modules. The other important header is the import package header, specifying which other packages this module is using. This usually comes with a version range, like a bundle is using package version 1.1 or higher up to but not including 2.0.

Therefore, the only required thing to make a bundle out of your jar is more or less calculating these export and import packages headers and adding them to the manifest. But don’t fear, the tooling does this automatically for you. Let’s build a bundle/jar.

Using Apache Maven to Create a OSGi Bundle

There is always the year old debate on which build tool is the best one, I will not get into this discussion. I simply use what I know best and what I’m forced to use anyway: Apache Maven.

By default, Maven is not adding the additional manifest information to the jar, but there are different plugins for Maven available. For this tutorial, I’ll use the newer bnd Maven Plugin from the bnd project.

Add the following two plugin configurations to your pom. It’s usually a good idea to put this into your parent pom:

             -exportcontents: ${packages;VERSIONED}

The first adds the bnd maven plugin which creates the manifest data and the second instructs the default jar plugin to use the manifest created by the bnd plugin. And that’s it. If you now build your jar project using Maven you’ll have additional manifest entries in there.

Standard OSGi Bundle Headers

The first entries are standard headers which make your jar a bundle and contain some metadata:

  • Bundle-ManifestVersion: This header is required and marks the jar as a bundle, the value is always 2 (newer OSGi specifications might add more features in which case the number would be increased).
  • Bundle-Name: A human readable name for your bundle. This defaults to your project name from the pom.
  • Bundle-SymbolicName: In combination with the bundle version (see below) the symbolic name provides a key to uniquely identify a bundle. This name should be based on the reverse domain name convention and defaults to the artifact id of your project. I’ll talk a little bit more about this soon.
  • Bundle-Version: The version of your bundle, this defaults to the version of your project.

There is no magic in the above entries and having them in your jar does not hurt at all. As mentioned above, the symbolic name should be based on the reverse domain name convention. Usually in the Maven world you follow this rule for your group id, like org.mycompany.myproject and then use a simple name for the artifact id like scheduler-api. However a good practice is to use a full qualified name for the artifact id (which usually means you prefix the artifact id with the group id), e.g. org.mycompany.myproject.scheduler-api. Why is this a good idea? It has nothing to do with OSGi and simply makes sure that the final name of the artifact is fully qualified. If you don’t add the group id to the artifact id, the jar filename would be scheduler-api-1.2.3.jar – which is not very descriptive and gives you no clue what this thing is (and it might clash with other projects using the same artifact id).

The bundle version as any version in software development should increase with continued development/releases. For OSGi this information together with the symbolic name provides a unique key for a bundle and allows OSGi to verify if this exact bundle (name and version) is already installed in the OSGi framework. On a technical level, the bundle version information is more a marketing version.

Public API

More important than versioning your bundle (which still is important), it’s more important to correctly version your public API which might be used by others. For this add the following dependency to your pom:


The above dependency adds some annotations to your project – using the scope “provided” is a good practice as this avoids dragging in transitive dependencies.

Your public API should be in a separate Java package than your implementation – again this is a general good style and not tied to OSGi at all. If you want to export a package to be used by others, add a file within your package:


If it’s the first version of your package, simply use 1.0 as the version – from now on once you have released this API as version 1.0, follow semantic versioning. Build your project again and you will see a header similar to this:

Export-Package      ;version="1.0"

As you can see creating the export is really easy, just use the above annotation and done. And the imports are even easier. The plugin analyses your classes and calculates the imports for you. For example for my sample project it looks like this:

Import-Package       javax.servlet;version="[3.1,4)",

And that’s it, your jar is now a bundle and can be used in any OSGi installation as a first class citizen. It’s really simple – and in 96% of your cases the automatic calculation by the tooling is sufficient. There are only rare and special cases where you want to have them differently. In that case, you can configure the plugin accordingly.

Migrating from the Apache Felix SCR Annotations to the OSGi Declarative Services Annotations

The Java annotations of the Apache Felix SCR Plugin were one of the first options to use annotations to create the descriptors for OSGi Declarative Services components and OSGi metatype descriptions for the configuration of such components. With the OSGi R6 release from 2015, the annotations of the OSGi specifications provide the same functionality and go even beyond.

Migrating from the Apache Felix SCR Annotations

Whenever there are competing solutions for the same problem, the question of which one to use arises. In this case, the answer is clear: use the official annotations from the OSGi specifications – for one, they are defined in a standard, but equally important these annotations support all features of Declarative Services R6 – while the Apache Felix annotations only support R5 and it is very unlikely that they will be updated. And that’s the other reason why you should not use the Apache Felix annotations – they are in maintenance mode. Of course, should any bug or problem arise, this will be fixed.

The second question usually is: Should I migrate existing code? There is no need to bulk migrate existing code. As said, the Apache Felix plugin is still maintained, is open source and simply works. However, if you have to touch your code, migrating the annotations might be a good idea. It also gives you the chance to simplify your code by leveraging the R6 features. That said, if you do so, this of course ties your implementation to an R6 implementation of Declarative Services, like Apache Felix SCR, version 2.0 or higher.

Mapping to OSGi Declarative Services Annotations

The below table gives you an overview of how to map the annotations.

Annotation Mapping

Apache Felix SCR AnnotationDescriptionOSGi Declarative Services AnnotationDescription
@ComponentThe @Component annotation marks a Java class to be used as a component.@ComponentThis is more or less a strict one-to-one replacement. Only difference is the default behavior for services. See below.
@ServiceMarks the component as a service and optionally lists the provided services (classes)@ComponentThe OSGi annotation has a service attribute which should be used to list the provided services. Be careful, if your component should not provide any service, set this attribute to an empty array.
@ReferenceReference to services, can be used on unary fields or on class level with bind/unbind methods.@ReferenceField references can directly be migrated, for event based references (methods), the @Reference annotation must be put on the bind method. In addition, more options for field references exist.
@Activate, @Deactivate, and @ModifiedMark a method being the activation, deactivation or modified method@Activate, @Deactivate, and @ModifiedStraight one-to-one migration.
@PropertyConfiguration properties and metatype.Component Property Type and metatype annotationsInstead of using a set of @Property annotations, the configuration is described through an annotation (component property type) and OSGi Metatype annotations can be used to add the metatype info.
Mapping between Apache Felix SCR Annotations and OSGi Declarative Services Annotations

The above table is of course just a short reference. You’ll find more information in the OSGi specifications and in my Declarative Services Tutorials: Part I, Part II, and Part III.

Example OSGi Web Application

In some of my OSGi talks I use the “Guessing Game” as the next great web application to conquer the world. It’s a web app that generates a random number and let’s you guess it, telling you whether you made it, are too high or too low.

You can find the latest sample code on github.

Some slides from the last OSGi Community event featuring this app: