Adding configuration to OSGi Component(s) is as simple as annotating your class with @Designate(ocd=””) annotation with the “ocd” property, and to make the @Activate method accepts an config param; as indicated below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | // /com/sourcedcode/services/impl/MyserviceImpl.java import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.metatype.annotations.Designate; @Component(service = MyService.class, immediate = true) @Designate(ocd = SourcedCodeSiteSettingsConfig.class) public class MyserviceImpl extends MyService { @Activate protected void activate(SourcedCodeSiteSettingsConfig config) { // do something // String name = config.name(); } } |
The “ocd” property must be set with a configuration interface class that is annotated with @ObjectClassDefinition; the annotated interface class can be an imported Java interface class or inner Java interface class.
Once the @Designate annotation on the OSGI Component’s class and the @ObjectClassDefinition configuration interface are configured, the OSGI Component(s) will have enabled the configuration successfully; you should be able to find the configuration in the OSGI Apache Felix Console, under http://localhost:4502/system/console/configMgr.
This article will highlight two different ways to enable configuration to your OSGI Component(s).
What is the @ObjectClassDefinition annotation?
The configuration interface is annotated with @ObjectClassDefinition where properties can be passed into the @ObjectClassDefinition annotation. The most common properties are @ObjectClassDefinition(name=””, description=””). The “name” property will be used as the title of the configuration popup within the OSGI Felix console, while the description property is used for the “description” of the popup.
1 2 3 4 5 6 7 8 9 10 11 12 13 | // Example: package com.sourcedcode.core.services; import org.osgi.service.metatype.annotations.ObjectClassDefinition; @ObjectClassDefinition(name = "SourcedCode - Site Settings Configuration", description = "dialog description" ) public @interface SourcedCodeSiteSettingsConfigExample { // config1 @AttributeDefinition() // config2 @AttributeDefinition() // config3 @AttributeDefinition() } |
What is the @AttributeDefinition annotation?
Declaration of the @AttributeDefinition annotations defines each configuration by identifying the type, the configuration reference name, config name and description.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | package com.sourcedcode.core.services; import org.osgi.service.metatype.annotations.AttributeDefinition; import org.osgi.service.metatype.annotations.AttributeType; import org.osgi.service.metatype.annotations.ObjectClassDefinition; @ObjectClassDefinition(name = "SourcedCode - Site Settings Configuration", description = "dialog description") public @interface SourcedCodeSiteSettingsConfigExample { @AttributeDefinition( name = "String[] Label", description = "String[] Config Example Description", type = AttributeType.STRING) String[] config_string_array_example() default {"item1", "item2"}; // config2 @AttributeDefinition() // config3 @AttributeDefinition() } |
To wrap your head around some of the essentials of @AttributeDefinition, checkout this blog article : OSGI R6 Configuration @AttributeDefinition Essentials Reference Guide.
1. Imported Class – Defining OSGI Component’s Configuration with Configuration Interface
The title of this section speaks for itself. The examples below "Step A" example provides sample code of a configuration interface class that is prepared to be imported OSGI Service Component, indicated in "Step B". This class have one responsibility, and its to provide the definition of the OSGI Component’s configuration interface.
Step A. Defining the annotated Configuration Interface
Isolated configuration interface class that prepared to be imported and referenced by the OSGI Service Component.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | package com.sourcedcode.core.services; import org.osgi.service.metatype.annotations.AttributeDefinition; import org.osgi.service.metatype.annotations.AttributeType; import org.osgi.service.metatype.annotations.ObjectClassDefinition; import org.osgi.service.metatype.annotations.Option; @ObjectClassDefinition(name = "SourcedCode - Site Settings Configuration", description = "dialog description") public @interface SourcedCodeSiteSettingsConfig { @AttributeDefinition( name = "String Label", description = "String Config Example Description", type = AttributeType.STRING) String config_string_example() default "Default String"; @AttributeDefinition( name = "String[] Label", description = "String[] Config Example Description", type = AttributeType.STRING) String[] config_string_array_example() default {"item1", "item2"}; @AttributeDefinition( name = "Long Label", description = "Long Config Example Description", type = AttributeType.LONG) long config_long_example() default 0L; @AttributeDefinition( name = "int Label", description = "innt Config Example Description", type = AttributeType.INTEGER) int config_number_example() default 0; @AttributeDefinition( name = "Short Label", description = "Short Config Example Description", type = AttributeType.SHORT) short config_short_example() default 0; @AttributeDefinition( name = "Char Label", description = "Char Config Example Description", type = AttributeType.CHARACTER) char config_char_example() default 0; @AttributeDefinition( name = "Byte Label", description = "Byte Config Example Description", type = AttributeType.BYTE) byte config_byte_example() default 0; @AttributeDefinition( name = "Double Label", description = "Double Config Example Description", type = AttributeType.DOUBLE) double config_double_example() default 0; @AttributeDefinition( name = "Float Label", description = "Float Config Example Description", type = AttributeType.FLOAT) float config_float_example() default 0; @AttributeDefinition( name = "Boolean Label", description = "Boolean Config Example Description", type = AttributeType.BOOLEAN) boolean config_boolean_example() default true; @AttributeDefinition( name = "Password Label", description = "Password Config Example Description", type = AttributeType.PASSWORD) String config_password_config_example() default ""; @AttributeDefinition( name = "Dropdown Label", description = "Dropdown Config Example Description", options = { @Option(label = "PRODUCTION", value = "PRODUCTION"), @Option(label = "STAGING", value = "STAGING"), @Option(label = "UAT", value = "UAT"), @Option(label = "QA", value = "QA"), @Option(label = "DEVELOP", value = "DEVELOP") } ) String config_dropdown_example() default "DEVELOP"; } |
Step B. Consuming the Configuration Interface by Imported Class
To use the configuration interface within our OSGI service component, we would need to annotation our service class with @Designate, with the ocd param pointing to the imported configuration interface class. Next, it would be as simple as setting up the initial @Activate method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | package com.sourcedcode.core.services; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.metatype.annotations.Designate; import com.sourcedcode.core.services.SourcedCodeSiteSettingsConfig; @Component(service = SourcedCodeSiteSettingsService.class, immediate = true) @Designate(ocd = SourcedCodeSiteSettingsConfig.class) public class SourcedCodeSiteSettingsService { private String config_string_example; private String[] config_string_array_example; private long config_long_example; private int config_number_example; private short config_short_example; private char config_char_example; private byte config_byte_example; private double config_double_example; private float config_float_example; private Boolean config_boolean_example; private String config_password_getConfig_example; private String config_dropdown_example; @Activate protected void activate(SourcedCodeSiteSettingsConfig config) { this.config_string_example = config.config_string_example(); this.config_string_array_example = config.config_string_array_example(); this.config_long_example = config.config_long_example(); this.config_number_example = config.config_number_example(); this.config_short_example = config.config_short_example(); this.config_char_example = config.config_char_example(); this.config_byte_example = config.config_byte_example(); this.config_double_example = config.config_double_example(); this.config_float_example = config.config_float_example(); this.config_boolean_example = config.config_boolean_example(); this.config_password_getConfig_example = config.config_password_config_example(); this.config_dropdown_example = config.config_dropdown_example(); } } |
The outcome of the OSGI Service and Configuration will result in an editable configuration within the OSGI Apache Felix Console.
2. Inner Class – Defining OSGI Component’s Configuration with Configuration Interface
You can also define the annotated @ObjectClassDefinition interface as an inner class as shown below. To use the inner class configuration interface within our OSGI service, we would need to annotation our service class with @Designate, with the ocd param pointing to the inner interface class. Next, it would be as simple as setting up the initial @Activate method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | package com.sourcedcode.core.services.impl; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.metatype.annotations.*; @Component(service = SourcedCodeSiteSettingsServiceInlineExample.class, immediate = true) @Designate(ocd = SourcedCodeSiteSettingsServiceInlineExample.SourcedCodeSiteSettingsConfig.class) public class SourcedCodeSiteSettingsServiceInlineExample { @ObjectClassDefinition(name = "SourcedCode - Site Settings Configuration", description = "dialog description") protected @interface SourcedCodeSiteSettingsConfig { @AttributeDefinition( name = "String Label", description = "String Config Example Description", type = AttributeType.STRING) String config_string_example() default "Default String"; @AttributeDefinition( name = "String[] Label", description = "String[] Config Example Description", type = AttributeType.STRING) String[] config_string_array_example() default {"item1", "item2"}; @AttributeDefinition( name = "Long Label", description = "Long Config Example Description", type = AttributeType.LONG) long config_long_example() default 0L; } @Activate protected void activate(SourcedCodeSiteSettingsConfig config) { } } |
Thoughts…
I typically try my best to keep the configuration interface decoupled with OSGi components. This promotes better readability and maintainability. I would recommend you to stick with the Single Responsibility Principle; to separate concerns.
@AttributeDefinitions are the underlying formatting of how each OSGI configuration interaces are put together. If you wish to learn more about this topic, checkout this article: OSGI R6 Configuration @AttributeDefinition Essentials Reference Guide.
This is a really useful article. I am wondering is there any way to add two or more configuration in the same class using @Designate
Based on my experience, you can only select 1 SettingsConfig class for @Designate.