Bnd / Components aQute - Software Consultancy
Search
*

10 Service Components

Version 2.2

The preferred way to use components is to use annotations. Since release 5, the OSGi has a set of standardized annotations for Declarative Services; these annotations were derived from the bnd annotations. So you have to choose which annotations to use. If you want to use the OSGi standard annotations you should set:

    -dsannotations: *

If you want to use the bnd annotations (which have a few functions not yet available in the standard OSGi annotations) then use:

    Service-Component: *

In both headers, the * can be replaced with a set of filters on fully qualified. class names. It is also no problem to use both.

Service Component Header

The Service-Component header is compatible with the standard OSGi header syntax. Any element in the list that does not have attributes must have a resource in the JAR and is copied as is to the manifest. However, simple components can also be defined inline, and it is also possible to pickup annotations.

The syntax for these component definitions is:

  component ::= <name> ( ';' parameter ) * 
  parameter ::= provide | reference | multiple | optional
              | reference | properties | factory | servicefactory
              | immediate | enabled | implementation 
              | activate | deactivate | modified | configuration-policy
              | version | designate

  reference ::= <name> '=' <interface-class> 
                 ( '(' <target-filter> ')')? cardinality?
  cardinality ::= '?' | '*' | '+' | '~'
  provide  ::= 'provide:=' LIST 
  multiple  ::= 'multiple:=' LIST 
  optional  ::= 'optional:=' LIST 
  dynamic   ::= 'dynamic:=' LIST
  designate  ::= ( 'designate' | 'designateFactory' ) CLASS
  factory   ::= 'factory:=' true | false
  servicefactory := 'servicefactory:=' true | false
  immediate ::= 'immediate:=' true | false
  enabled   ::= 'enabled:=' true | false
  configuration-policy ::= "configuration-policy:=' 
       ( 'optional' | 'require' | 'ignore' )
  activate  ::= 'activate:=' METHOD
  modified  ::= 'modified:=' METHOD
  deactivate::= 'deactivate:=' METHOD
  implementation::= 'implementation:=' <implementation-class>
  properties::= 'properties:=' key '=' value  \
                ( ',' key '=' value ) *
  key       ::= NAME (( '@' | ':' ) type )?
  value     ::= value ( '|' value )*

If the name of the component maps to a resource, or ends in XML, or there are attributes set, then that clause is copied to the output Service-Component header.

If the name can be expanded to one or more classes that have component annotations (they must be inside the JAR), then each of those classes is analyzed for its component annotations. These annotations are then merged with the attributes from the header, where the header attributes override annotations. The expansion uses the normal wildcard rules. For example, biz.aQute.components.* will search for component annotated classes in the biz.aQute.components package or one of its descendants. The classes must be present in the JAR. If no classes with annotations can be found for the name then it is assumed to be name or implementation class name without annotations.

The name of the component is also the implementation class (unless overridden by the implementation: directive). It is then followed with a number of references and directives. A reference defines a name that can be used with the locateService method from the ComponentContext class. If the name starts with a lower case character, it is assume to be a bean property. In that case the reference is augmented with a set<Name> and unset<Name> method according to the standard bean rules. Bnd will interpret the header, read the annotations if possible, and create the corresponding resources in the output jar under the name OSGI-INF/<id>.xml.

Annotations are only recognized on the component class, super classes are not inspected for the components.

The standard OSGi annotations are described in their specification. The supported annotations in the aQute.bnd.annotations.component package are:

Component

Annotated the class, indicates this class is required to be a component. It has the following properties:

provideClass[]Service interfaces, the default is all directly implemented interfaces
nameStringName of the component
factoryBooleanFactory component
servicefactoryBooleanService Factory
immediateBooleanImmediate activation
designateCLASSDesignate a class as a MetaType interface used for configurations for unitary configurations, see . This changes the default of the configurationPolicy to require.
designateFactoryCLASSDesignate a class as a MetaType interface used for configurations for factory configurations, see . This changes the default of the configurationPolicy to require.
configurationPolicyOPTIONAL, REQUIRE, IGNOREConfiguration Policy
enabledBooleanEnabled component
propertiesString[]Properties specified as an array of key=value. The property type can be specified in the key as name:Integer. The value can contain multiple values, the parts must then be separated by a vertical bar ('|') or a line feed (\n), for example properties = {"primes:Integer==1|2|3|5|7|11"}.
Reference

On a method. Indicates this method is the activate method. It has the following attributes

nameStringName of the reference. Default this is the name of the method without set on it.
serviceClassThe service type, default is the argument type of the method. The unset method is derived from this name. I.e. setXX will have an unsetXX method to unset the reference.
typeCharacterStandard cardinality type '?', '*', '+','~'
targetStringA filter expression applied to the properties of the target service
unbindStringOptional name of the unbind method. By default this is the same name as the bind method name but with set/add replaced with unset/remove. E.g. setFoo() bind method becomes unsetFoo() unbind method.

@Reference automatically sets the bind method. The unbind method is set by using a derived name from the bind method or providing it with the name of the unbind method. The following name patterns are supported:

bindunbind
setXunsetX
addXremoveX
xxxXunxxxX

For example:

  @Reference
  protected void setFoo(LogService l) { ... }
  protected void unsetFoo(LogService l) { ... }

If you want to override this, use

  @Reference(unbind="IRefuseToCallMyMethodUnFoo");
  protected void foo(LogService l) {}
  protected void IRefuseToCallMyMethodUnFoo(LogService l) {}

Unfortunately Java has no method references so it is not type safe.A non existent @UnReference annotation is not very useful because that still requires linking it up symbolically to the associated @Reference.

Activate, Modified, and Deactivate

The life cycle methods. These annotations have no properties.

Assume the JAR contains the following class:

  package com.acme;
  import org.osgi.service.event.*;
  import org.osgi.service.log.*;
  import aQute.bnd.annotation.component.*;

  @Component
  public class AnnotatedComponent implements EventHandler {
    LogService log;

    @Reference
    void setLog(LogService log) { this.log=log; }

    public void handleEvent(Event event) {
      log.log(LogService.LOG_INFO, event.getTopic());
    }
  }

The only thing necessary to register the Declarative Service component is to add the following Service-Component header:

  Service-Component: com.acme.*

This header will look for annotations in all com.acme sub-packages for an annotated component. The resulting XML will look like:

  OSGI-INF/com.acme.AnnotatedComponent.xml:
    <?xml version='1.0' encoding='utf-8'?>
      <component name='com.acme.AnnotatedComponent'>
        <implementation class='com.acme.AnnotatedComponent'/>
        <service>
          <provide interface='org.osgi.service.event.EventHandler'/>
        </service>
        <reference name='log'
          interface='org.osgi.service.log.LogService' 
          bind='setLog'
          unbind='unsetLog'/>
      </component>

The following example shows a component that is bound to the log service via the setLog method without annotations:

  Service-Component=aQute.tutorial.component.World; \
    log=org.osgi.service.log.LogService  

The Service Component Runtime (SCR) offers a variety of options on the reference. Some options like the target can be used by adding the target filter after the interface name (this likely requires putting quotes around the interface name+filter).

References can be suffixed with the following characters to indicate their cardinality:

  Char          Cardinality    Policy
  ?             0..1           dynamic
  *             0..n           dynamic
  +             1..n           dynamic
  ~             0..1           static
                1              static

For a more complex example:

  Service-Component=aQute.tutorial.component.World; \
    log=org.osgi.service.log.LogService?; \
    http=org.osgi.service.http.HttpService; \
    PROCESSORS="xierpa.service.processor.Processor(priority>1)+"; \
    properties:="wazaabi=true"

The previous example will result in the following service component in the resource OSGI-INF/aQute.tutorial.component.World.xml:

  <?xml version="1.0" encoding="utf-8" ?>
   <component name="aQute.tutorial.component.World">
     <implementation class="aQute.tutorial.component.World" /> 
     <reference name="log" 
       interface="org.osgi.service.log.LogService" 
       cardinality="0..1" 
       bind="setLog" 
       unbind="unsetLog" 
       policy="dynamic" /> 
     <reference name="http" 
       interface="org.osgi.service.http.HttpService" 
       bind="setHttp" 
       unbind="unsetHttp" />
     <reference name="PROCESSORS" 
       interface="xierpa.service.processor.Processor" 
       cardinality="1..n" 
       policy="dynamic" 
       target="(&(priority>=1)(link=false))" /> 
   </component> 

The description also supports the immediate, enabled, factory, target, servicefactory, configuration-policy, activate, deactivate, and modified attributes. Refer to the Declarative Services definition for their semantics.

If any feature of the V1.1 namespace is used, then bnd will declare the namespace in the component element. A specific namespace version can be set with the version directive. This detection only works when components are used.

Bnd also supports setting the policy and cardinality through the following directives:

  multiple:= LIST    names of references that have x..n
  optional:= LIST    names of references that have 0..x
  dynamic:=  LIST    names of references that are dynamic

Components and Metatype

The Service Component Runtime works closely together with the Configuration Admin to allow the components to be controlled through configuration. Configuration Admin knows two types of configuration:

  • Unitary
  • Factory

A unitary configuration can be set and changed but there is at most one of them. A Factory configuration can be used to create multiple instances of the same component. A component has a configuration policy that defines when no configuration is set.

  • optional - If no configuration is set (either unitary or factory) then the component is still created.
  • require - This requires a unitary configuration to be set or one or more factory configurations before a component is created.
  • ignore - Ignore configuration information

A related standard is the Metatype standard. The Metatype Service provides a repository of property descriptors. Bundles can provide these descriptors in their bundles in the OSGI-INF/metatype directory. There are tools, like the Felix Webconsole, that can provide an editing window for a configuration that is typed with a metatype description.

In practice, this is a powerful model that provides a lot of configurability for your components with easy editing but getting it all right is not trivial. To make this easier, bnd has made it ease to use configurations.

In this model, configurations are declared in an interface. For example, the following interface defines a simple message:

  interface Config {
    String message(); // message to give
  }

To create a component that can work with this config, we need to designate that interface as the configuration interface for a component.

  @Component(designate=Config.class)
  public class BasicComponent {
      Config config;

      @Activate void activate(Map<String,Object> props) {
         config = Configurable.createConfigurable(props);
         System.out.println("Hi " + config.message());
      }

      @Deactivate void deactivate() {
         System.out.println("Bye " + config.message());
      }
  }

This is an immediate component because it does not implement a service interface. It also requires a configuration because we have not specified this explicitly. When you use designate (or designateFactory) the default becomes require. This means that your component will only be created when there is actually configuration for it set.

To run this component, make sure you have the Felix Webconsole running and the MetaType service installed. In the Webconsole, you can click on Configuration, your component should be listed on this page. By Clicking on the component with the name Basic Component Config you get an editor window.

The editor is aware of the proper types, it uses the MetaType standard to describe the properties. bnd uses the type information on the interface as well as the optional Metadata annotations to create a rich description that allows the web console to provide a good editor.

You can fill in the message in the Message field. If you save the editor, your component prints the message with "Hi" in front of it. Deleting the configuration will print the message with "Bye".

If you change the message, you will see that the component is first deactivated and then reactivated again. This is the only possibility for the SCR because the component has not implemented a modified method. Adding the following method will change this, now changes to the configuration are signaled to the component and the component can continue to work. This is more complicated then recycling the component but it can create a more optimized system.

  @Modified
  void modified( Map<String,Object> props) {
    // reuse activate method
    activate(props);
  }

It is also possible to take advantage of the configuration factories. In this model

An example, that implements a simple socket server on a configurable port and returns a message when a telnet session is opened to that port can be found on Github.

Copyright 2006 aQute SARL, All Rights Reserved