Question about DS component configuration

262 views
Skip to first unread message

David Leangen

unread,
Sep 18, 2015, 2:21:51 AM9/18/15
to bndtool...@googlegroups.com

Hi!

(Apologies for the double post. I wrote to the Felix Users list yesterday, but no reply yet, so I am giving this a try here…)

(Using Felix SCR) I am trying to obtain a component’s properties object by:
  ConfigurationAdmin cm = {obtain the cm}
  Configuration componentConfig = cm.getConfiguration( componentPid );
  componentConfig.getProperties();

I can confirm that I am getting the correct Configuration object (when I add a property and update the Configuration, it does indeed update the component), however, I am getting a null result when I try to obtain the properties before doing an update, and an “incomplete” properties object after the update (containing only the service.pid property).

Am I misunderstanding what should happen? Or is my understanding correct, but I am for some reason not getting the correct result?


Thanks!
=David

BJ Hargrave

unread,
Sep 18, 2015, 4:06:36 AM9/18/15
to bndtool...@googlegroups.com
If you are using DS then your component should not be interacting directly with Config Admin. DS does that for you. If you make an activate method, DS will call it and pass you component properties to your component.

--
You received this message because you are subscribed to the Google Groups "bndtools-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to bndtools-user...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
--
--
BJ

David Leangen

unread,
Sep 18, 2015, 4:16:19 AM9/18/15
to bndtool...@googlegroups.com

Hi BJ,

Thanks for the info.

>> (Using Felix SCR) I am trying to obtain a component’s properties object by:
>> ConfigurationAdmin cm = {obtain the cm}
>> Configuration componentConfig = cm.getConfiguration( componentPid );
>> componentConfig.getProperties();
>>
>> I can confirm that I am getting the correct Configuration object (when I add a property and update the Configuration, it does indeed update the component), however, I am getting a null result when I try to obtain the properties before doing an update, and an “incomplete” properties object after the update (containing only the service.pid property).
>>
>> Am I misunderstanding what should happen? Or is my understanding correct, but I am for some reason not getting the correct result?
>>
>
> If you are using DS then your component should not be interacting directly with Config Admin. DS does that for you. If you make an activate method, DS will call it and pass you component properties to your component.

Yes, I understand that a component gets its own configuration via the activate method. I am having no troubles with this. Sorry for the missing context.

What I am trying to do is have a different management component (in a different bundle) configure this component. The configuration given depends on the defaults of the component (which I include in the @Component annotation). For that reason, I am trying to get my management component to get the current properties of the component.


Cheers,
=David


BJ Hargrave

unread,
Sep 18, 2015, 5:00:06 AM9/18/15
to bndtool...@googlegroups.com

Well the configuration properties in the Config Admin configuration will only be a subset of a component's properties since the component can specify properties.

You may want to use the new configuration property types from DS 1.3. Then all the properties used by the component will be expressed in a type safe manner and the default values (from the default keywords in the configuration property types) will be stored in the generated component XML.

--
BJ Hargrave

David Leangen

unread,
Sep 18, 2015, 5:08:47 AM9/18/15
to bndtool...@googlegroups.com

Well the configuration properties in the Config Admin configuration will only be a subset of a component's properties since the component can specify properties.

You may want to use the new configuration property types from DS 1.3. Then all the properties used by the component will be expressed in a type safe manner and the default values (from the default keywords in the configuration property types) will be stored in the generated component XML.

Meaning, I assume that ConfigAdmin will “see” these default value, since they are in the XML.

Ok, I’ll give that a try. Thank you very much for the suggestion!


Cheers,
=David


BJ Hargrave

unread,
Sep 18, 2015, 5:15:35 AM9/18/15
to bndtool...@googlegroups.com

No. Config Admin has no knowledge of DS.

Your code however knows about both. Since you need to know the details of a component's properties, you can process the XML to know their names, types and default values as you set the component's configuration.

--
BJ Hargrave

David Leangen

unread,
Sep 18, 2015, 5:27:47 AM9/18/15
to bndtool...@googlegroups.com
I appreciate the follow-up. Thanks.

No. Config Admin has no knowledge of DS.

Your code however knows about both. Since you need to know the details of a component's properties, you can process the XML to know their names, types and default values as you set the component's configuration.

So are you saying:

 1) Default values are set in the component’s XML file
 1) My management component would need to locate the bundle that contains the target component
 2) Knowing the location of that bundle, I could parse the raw DS XML file directly myself
 3) Since the properties are known, the management component can read them from the XML file

This seems unusually difficult for what I am trying to do, which leads me to believe that I am going about this the wrong way… I really don’t intend to go against the grain here, and it’s certainly not my intent to bring back old memories of XML processing.

Is there a simpler way you could recommend that would allow a management component to configure a target component? I would have assumed that this is a basic use-case, but perhaps the spec does not provide for it? Or am I (again) missing something basic here?  :-/


Cheers,
=David


Neil Bartlett

unread,
Sep 18, 2015, 5:59:22 AM9/18/15
to bndtool...@googlegroups.com
The management agent is responsible for the application configuration. It’s the component’s responsibility to combine that configuration with its defaults where required. So the management agent doesn’t normally need to know about the component’s defaults, because they are internal to the component.

On the other hand, it’s quite normal for a management agent to need to know the *types* of configuration data required by each component so that it can construct valid configuration records. This is the role of the OSGi Metatype Specification (see compendium chapter 105). In this specification, a component’s configuration is defined by an Object Class Definition (OCD) which consists of a set of Attribute Definitions (ADs). Each AD indicates a data type, cardinality and may include a default value. The metatype XML document can be generated by bnd.

Regards,
Neil

David Jencks

unread,
Sep 18, 2015, 9:13:22 AM9/18/15
to bndtool...@googlegroups.com
I’d like to go into a little more detail.

1 use bnd3
2. use felix ds 2
3. I think either felix or equinox metatype service and config admin should work. There might be others I don’t know about.

4. Use the DS 1.3 feature of configuring your component through annotations.

5. annotation the configuration annotation with the metatype 1.3 annotations

6. bnd will generation the component xml and the metatype xml from the annotations and put them into your bundle

7. your management agent will get the metatype for the pid from the metatype service and can use the defaults specified there to fill in the blanks in the configuration.  Your code does not need to deal with xml.

For example (not tested, may contain errors)

@ObjectClassDefinition
@interface my.Config {
  String attr() default”foo”;  //“foo” will end up as default in both component xml and metatype xml.  your config agent will use it from metatype
  @AttributeDefinition(default={“42”})
  int intAttr(); //default will appear only in metatype xml 
}

@Component
@Designate(my.Config.class)
public class my.Component {

@Activate
void activate(my.Config config) {}
}


david jencks

David Leangen

unread,
Sep 19, 2015, 12:19:20 AM9/19/15
to bndtool...@googlegroups.com

Well, thanks Neil and David for the additional information. This gives me quite a bit to ponder, as it touches on the architecture of my whole system, but is exactly the type of information I needed. I re-read the relevant parts of the spec with your advice in mind, and I see the connections you make.

The management agent is responsible for the application configuration. It’s the component’s responsibility to combine that configuration with its defaults where required. So the management agent doesn’t normally need to know about the component’s defaults, because they are internal to the component.

Not arguing the point itself, but just so I know: is this your opinion, a general or best practice that you are explaining, or what the spec is intending to prescribe?

BTW, I was very curious about this description of “Management Agent”, so I tried looking it up in both the core and compendium specifications (v6). The term is mentioned often enough, sometimes as “Management Agent” (big “M” big “A”), and sometimes as “management agent” (little “m” little “a”). The former is not actually formally defined anywhere (except in Core 9.1.2 and Cmpn 110.1.2, so the meaning is local to those chapters) though it is related to an “OSGi concept” (Cmpn 122.2), and the latter appears to be synonymous with “Operator” (Core 4.4.1). Anyway, I think I get the idea, but the spec does not seem very clear on this particular point. :-)

Thanks again.

Cheers,
=David

David Jencks

unread,
Sep 19, 2015, 7:29:11 AM9/19/15
to bndtool...@googlegroups.com
On Sep 19, 2015, at 12:19 AM, David Leangen <david....@gmail.com> wrote:


Well, thanks Neil and David for the additional information. This gives me quite a bit to ponder, as it touches on the architecture of my whole system, but is exactly the type of information I needed. I re-read the relevant parts of the spec with your advice in mind, and I see the connections you make.

The management agent is responsible for the application configuration. It’s the component’s responsibility to combine that configuration with its defaults where required. So the management agent doesn’t normally need to know about the component’s defaults, because they are internal to the component.

Not arguing the point itself, but just so I know: is this your opinion, a general or best practice that you are explaining, or what the spec is intending to prescribe?


While I totally agree that the management agent has no need or ability to know about the component defaults, I”m not sure that is a very accurate or useful description of what is going on.  The SCR runtime combines configuration information from several sources, combines it, and supplies it to the component.  The component doesn’t do any merging itself.

The sources are, in order of increasing priority (later wins):

component xml
config admin configuration
(factory component only) arguments to newInstance.

Using DS annotations, the component.xml contents may be from the annotation (“property” member) or a property file (“properties” member).  Note that the latter may be used to put the “same” component in different bundles with different default properties by including the class from a common location but supplying different properties files for each bundle.

With a DS 1.3 component, you can combine multiple config admin configurations (multiple pids).  At most one can be a factory configuration.

It’s unusual to find a good use for factory components.

While all the configuration properties are merged together by the runtime to provide a single map, they may also be delivered to the component in multiple configuration annotations.  However any members with the same name are going to get the same value (although possibly coerced to different types).

BTW, I was very curious about this description of “Management Agent”, so I tried looking it up in both the core and compendium specifications (v6). The term is mentioned often enough, sometimes as “Management Agent” (big “M” big “A”), and sometimes as “management agent” (little “m” little “a”). The former is not actually formally defined anywhere (except in Core 9.1.2 and Cmpn 110.1.2, so the meaning is local to those chapters) though it is related to an “OSGi concept” (Cmpn 122.2), and the latter appears to be synonymous with “Operator” (Core 4.4.1). Anyway, I think I get the idea, but the spec does not seem very clear on this particular point. :-)


I agree.  In this case I think it means some combination of code and people that combine metatype and information and stuff it into config admin.  For instance (at work) I use something that combines metatype and a framework-wide xml configuration file to generate the config admin Configurations. I believe other people use the felix web console, which IIUC is metatype-aware, to enter configurations.

david jencks

Peter Kriens

unread,
Sep 22, 2015, 5:35:06 AM9/22/15
to bndtool...@googlegroups.com
I am not really sure what you try to achieve but maybe I can shed some light how this is supposed to work. If this does not work it might help to create a real albeit small project on Github that demonstrates your requirements.

# Declarative Services

In Declarative Services there are a rather large number of different mechanisms at work. I just list them here in increasing complexity and then will describe them then one by one:

* Trees & Forest
* PID
* Component service properties
* Default configuration
* Singleton configuration
* Factory configuration
* Mandatory configuration
* PIDs are Public API
* Management Agent
* Metatypes
* Component Factory
* Targets, or how to Wire Services

## Trees & Forest

The OSGi specifications are very cohesive and decoupled. Though this provides numerous benefits it clearly makes it hard to see how the different parts interact for newcomers. More important, how should you actually use them together?

The best advice is to start very simple and use defaults. This basically means the skeleton of your component is:

package com.acme;
@Component 
public class Foo implements Bar {

@Reference
EventAdmin eventAdmin;

@Reference
final List<Foo> foo = new CopyOnWriteArrayList<>();

@interface Config {
int port();
}

@Activate void activate( Config config ) {
}

@Deactivate void close() {
}
}

This gives you a static component that can be be fully configured through the `com.acme.Foo` PID with Configuration Admin whatever _Management Agent_ you pick.

service.pid : com.acme.Foo
port : 8080

If you use bndtools, use the osgi.enroute.configurer.simple.provider bundle. This allows you to specify the configuration data in JSON in a bundle in the `configuration/configuration.json` resource. The best practice is to create one ‘application’ project that only holds configuration data and no code. This application project pulls together the components that are used in a specific application and configures them.

If you run into a problem with this basic model (though realize that 95% of the components can get away with these defaults) that does not seem to have a simple solution then first try to see if you redefine the problem to fit the simple model. (Yes, numerous times people define the problem in terms of a specific solution and not of actual requirements. Taking a step back and trying to see what is really required can make a surprising difference.) 

If that does not work, ask on a mailing list. Declarative Services are so widely used that is extremely unlikely there is no solution to your actual requirements.

## The PID

The core idea is the PID: an identifier that identifies the configuration for the a component _type_. For example, the following component has a PID of `com.acme.FooImpl`:

package com.acme;

@Component
public class FooImpl { }

## Default Configuration 

Let’s forget Configuration Admin for a moment. If you run this component without any configuration then the component will be started since by default it can live without configuration. 

## Component Service Properties

If you want to create a service property to be registered (and provided to the `activate` method) then you can provide such a property in the @Component annotation:

@Component(
property=“foo=bar”
)
public class Foo { 
@Activate void activate( Map<String,Object> map) {
assert “bar”.equals(map.get(“foo”));
}
}

## Configuration Properties Suck

Properties suck but are often a necessary evil. They do not refactor easily, are error prone in their spelling, require messy conversions to their desired Java types, require casting, and in general look awful in your code with their yelling upper case (assuming you turn the keys into constants). How nice would it be if you could just use type safe constructs? 

Well, you can. In the previous examples we used a `Map<String,Object>` to get the service properties in the `activate` method. However, we can also replace this map with any annotation type:

@interface Config {
String foo() default “bar”;
}

@Activate void activate( Foo.Config config ) {
assert “bar”equals( config.foo() );
}

## Factory Configuration

In most cases it makes sense to be able to create multiple instances of a component. For example, you are polling a host for some information in your application. It is common that you want the final application to be able to poll different hosts and not just one. Even if you want it to be just one, it is bad practice to choose a singleton configuration if it is theoretically possible that one day you need two or more. 

This is where Configuration Admin comes in. Let’s ignore how we manage Configuration Admin but assume that there are two factory configurations for the ‘com.acme.Foo’ Factory PID:

service.pid : com.acme.Foo-126121312-12112
service.factoryPid : com.acme.Foo
foo : one

service.pid : com.acme.Foo-456781232-83622
service.factoryPid : com.acme.Foo
foo : two

With this configuration the `com.acme.Foo` component will be instantiated twice. One component will get foo=one and the other will get foo=two

@Activate void activate( Foo.Config config) {
String foo = config.foo();
assert “one”.equals(foo) || “two”.equals(foo);
}

## Singleton Configuration

There are cases where a singleton configuration is necessary. If it is logically impossible to have more than one instance then a singleton configuration should be used. However, these cases are more rare than most developers realize. For example, the Apache Felix jetty configuration is a singleton configuration but should likely have been a factory configuration because it is possible to have multiple web servers on different ports. This would have simplified the configuration and made it more flexible.

Assume that there is a singleton Configuration for the ‘com.acme.Foo’ PID:

service.pid : com.acme.Foo
foo : singleton

If we now run our system, the activate method gets called with `foo=singleton`

@Activate void activate( Foo.Config config) {
assert “singleton”.equals( config.foo() );
}

## Mandatory Configuration

Maybe you’ve noticed, but factories now have a rather strange life cycle. If there is no configuration for the given PID, then the component gets instantiated once with the service properties defined in the @Component annotation. If you then create a factory configuration for that PID, the default component is deleted and the factory configuration is used to instantiate a new component. The consequence of this is that you cannot have zero components, there will always be at least one. Though a default instance is sometimes handy, in general it does not make sense. In that case you can make the configuration mandatory: only if the configuration is set (either singleton or factory) will the component be instantiated.

@Component(
configurationPolicy = ConfigurationPolicy.MANDATORY
)

With a mandatory configuration the component is only instantiated when there is a Configuration for the corresponding PID. The following table shows how the configuration interacts with this policy:

Mandatory Optional Ignore
no config no instance default default
singleton config 1 instance      1 instance      default
one factory config      1 instance      1 instance      default
two factory config      2 instances     2 instances     default

## PIDs are Public API

PIDs are public API since external parties will be aware of them when they configure your component. For this reason it is sometimes better to create a name that can be kept constant over time even if implementations change.

@Component( name = “com.acme.foo” )
public class FooImpl { }

## Management Agent

The previous section should make it clear how the instances of a component can be controlled through the configurations associated with the component’s PID. Components follow Configuration Admin and, except for some default service properties, should never ever try to configure themselves. Things are a lot simpler if components just do what they are being told by Configuration Admin!

However, that leaves the question: Who sets the Configurations for a given PID? This role is in general fulfilled by the _management agent_. This is not magic entity in OSGi, it is just one or more bundles that perform the role of managing the framework. One of it is roles is to configure the components. This is not a single solution. On the contrary, there are lots of viable solutions. The following lists describes a list of popular management agents:

* FileInstall – Watches a directory and synchronizes any configuration in that directory to Configuration Admin. File Install allows you to manage singleton and factory configurations just by adding/removing/changing a file in a directory. In a cluster, a directory can be shared (even over for example Dropbox). Very convenient.
* Web Console – Apache Felix Web Console is a Web based tool that allows you manage configurations. The Web Console is excellent during debugging and small systems because it is very interactive.
* Configurer – The OSGi enRoute configurer reads JSON resources from active bundles and installs them as configurations. The Configurer is very suitable for production since it delivers configuration as a bundle, just as code bundles.
* Deployment Admin – Provides a means to deliver configuration through Deployment Packages and a Resource Processor. I think Apache Ace uses this.

Of course there is always the option to write your own management agent. The Configuration Admin API is quite flexible though be aware of some of the quirks (locations!). However, postponing the decision to handle your own configuration until there is no alternative and you’ve also become quite experienced is the recommended (in)action.

## Metatype

Using the annotation types significantly cleaned up our code. However, with File Install, Configurer, and Web Console we’re still having to use properties. Since properties are evil, we’d like to have a more elegant solution. This elegant solution is called _metatype_. It is an OSGi service that enables components to describe their configuration types. User agents can then use a metatype to automatically create a form for that configuration. Apache Felix Web Console fully supports metatypes in its web based user interface.

So how do we link our annotation type to a metatype? Though the metatypes are defined in XML there are fortunately (build) annotations for it. 

@ObjectClassDefinition
@interface Config {
@AttributeDefinition
String foo();
}

@Designate( ocd = Config.class, factory=true )
@Component
public class Foo {}

The @ObjectClassDefinition and @AttributeDefinition can describe the ‘properties’ and provide additional information to control the user interface. The @Designate annotation links a configuration type (or as we call it: an Object Class Definition or ocd) to a component’s PID. This will create the proper XML for the Metatype service to link it all together.

## Targets, or how to Wire Services

Services are linked to each other by their object classes. I.e. one component requires an instance of the Foo service type. You create that dependency with the @Reference annotation. An @Component annotated type can then register this service. However, sometimes you cannot avoid but make the _wiring_ of the services more complicated. Sometimes you really need to ensure that a component Bar is really wired to a specific instance of Foo. 

This can be done with the `target` field in the @Reference annotation. 

@Reference( target=“(foo=bar)” )
Foo foo;

A component Foo can then register its service with a number of properties under Configuration Admin control. 
@Component public class FooImpl implements Foo {
}

Another component can depend on this property.

@Component public class Bar {
@Reference( target=“(foo=bar)” )
Foo foo;
}

We can now create a configuration for FooImpl 

service.pid : com.acme.FooImpl-131231412-15322
service.factoryPid : com.acme.FooImpl
foo : ‘bar’

This will create an instance of the Foo service with a service property ‘foo=bar’. The Bar service has a dependency on this service so it will be wired to it.

Obviously this is not symmetric. We need to know the name of the service property ahead of time in the Bar service. It is therefore possible to set the target field of the @Reference annotation with configuration admin:

service.pid : com.acme.Bar
target.foo : ‘(foo=bar)’

## Component Factory

This part barely made it in the spec and in general it is not advised to use it except when you really know what you’re doing.

David Leangen

unread,
Sep 22, 2015, 9:17:41 PM9/22/15
to bndtool...@googlegroups.com

Thank you very much, Peter! This is great information.

There is a lot to digest here, so I’m going to study more and experiment a bit first before I open my trap. :-)


Cheers,
=David

Peter Kriens

unread,
Sep 23, 2015, 4:48:01 AM9/23/15
to bndtool...@googlegroups.com
I have copied this information to enroute.osgi.org. There is more information there that you might also want to take a look at http://enroute.osgi.org/services/org.osgi.service.cm.htmlhttp://enroute.osgi.org/services/org.osgi.service.metatype.html, and http://enroute.osgi.org/services/org.osgi.service.component.html. And there is of course the example projects: https://github.com/osgi/osgi.enroute.examples. The https://github.com/osgi/osgi.enroute.examples/tree/master/osgi.enroute.examples.cm.application and https://github.com/osgi/osgi.enroute.examples/tree/master/osgi.enroute.examples.component.application are a nice playground.

I hope you will help me to improve by provide pull requests for any errors, ommissions, or blunders you might find.

Kind regards,

Peter Kriens
Reply all
Reply to author
Forward
0 new messages