You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 2 Next »

Annotations - Writing Components

Component Types

Before diving into all annotations, we must first introduce the different types of components DependencyManager is supporting. In Dependency Manager, you may use the following types of components, depending on what you need:

  • Service component: This kind of component is rather like an OSGi Service: it's an implementation class that may publish an OSGi service, and/or refer to other dependencies, along with eventual service properties. A Service has a lifecycle: it is started once the component bundle is started and all required dependencies are injected, and it is then stopped if a required dependency is lost, or if the bundle is stopped. However, notice that in DependencyManager, a Service may not necessarily publish a Service into the OSGi registry: in this case, the component can still have a managed lifecycle, with dependencies.
  • Aspect Service component: A Service that provides a non-functional aspect on top of an existing service. In aspect oriented programming, an aspect, or interceptor can sit between a client and another target service used by the client. An Aspect Service first tracks a target service and is created once the target service is detected. Then the Aspect Service is provided, but with a higher ranking, and the client is transparently updated with the aspect. Aspects can be chained and may apply to the same target service (and in this case, the ranking of the Aspect service is used to chain aspects in the proper order).
  • Adapter Service  component: A Service that adapts another existing service into a new one. Like with aspects, sometimes you want to create adapters for certain services, which add certain behavior that results in the publication of (in this case) a different service. Adapters can dynamically be added and removed and allow you to keep your basic services implementations clean and simple, adding extra features on top of them in a modular way.

Now we have introduced the different types of components, here is the list of annotations, allowing to declare DependencyManager service components:

  • @Service: for declaring a component that publishes a singleton OSGi service as long as its dependencies are resolved.
  • @AspectService: provides a non-functional aspect on top of an existing service.
  • @AdapterService: creates an OSGi service that adapts another existing service into a new interface.
  • @BundleAdapterService: creates an OSGi service a service on top of a given bundle.
  • @ResourceAdapterService: creates an OSGi service on top of a specific Resource.
  • @FactoryConfigurationAdapterService: creates an OSGi service from ConfigAdmin, using a factoryPid, and a ManagedServiceFactory.

@Service

This annotation annotates an implementation class that optionally publishes an OSGi service, and optionally has some dependencies, with a managed lifecycle. The annotation has the following attributes:

  • provides: By default, the component is registered into the OSGi registry under all directly implemented interfaces. If no interfaces are implemented, then the component is not registered, but it still has a managed lifecycle, and have dependencies. If you need to explicitly define the list of interfaces (or classes) under which the component must be registered in the OSGi registry, then use the provides attribute. You can also set this property to an empty array of classes if you don't want at all your component to be exposed in the OSGi registry (even if it implements some interfaces).
  • properties: enumerates the list of properties that are part of the Service exposed by the component in the OSGi Registry. Each property is defined using the @Property annotation, which represents a key/value pair. When a value is actually an array of strings, then the values attribute of the @Property annotation can be used. This attribute is not the only way to specify OSGi Service properties (see Setting Service properties in the lifecycle section).
  • factoryMethod: may match a static method name from the annotated class which can be used to instantiate the component instance. Normally, DependencyManager instantiates the component using its class name, and with the default constructor of the class, but there are some cases where it is required to take control of how the component is created. For instance, this method may be used to create the component as a dynamic proxy ...
  • factorySet: The component factory ID. By default, a Service component is automatically instantiated as a singleton when the bundle is started, and when all required dependencies are satisfied. But when a component must be created, configured, or disposed dynamically, and when multiple instances of the same component are needed, a factorySet should be used. When you use this attribute, a java.util.Set<Dictionary> object is registered into the OSGi regitry, with a specific dm.factory.name property matching the ID you specify in the attribute. This Set<Dictionary> will act as a Factory API, and another component may define a dependency on this Set and add some configuration dictionaries in it, in order to fire some component instantiation/activation. There is one component instantiated per added dictionary, which is passed to component instances via a configurable callback method (using the factoryConfigure attribute). All public properties will be propagated along with eventual published service. A public property is a property which does not start with a dot ("."). Properties starting with a dot are considered private to the component, and won't be propagated to published service. This model is actually similar to the Declarative Service "Component Factories" concept, except that you don't have a dependency on a specific API, but rather on a basic jdk class (java.util.Set<Dictionary>). Notice that, unlike in Declarative Service, the component factory is  provided once the component bundle is started, even if required dependencies are not satisfied. This is useful when the component want to dynamically configure its dependency filters. So, to summarize:
    • Each time a new Dictionary is added into the Set, then a new instance of the annotated service will be instantiated, and this dictionary is passed to the component callback specified with the factoryConfigure attribute.
    • Each time an existing Dictionary is re-added into the Set, then the corresponding Service instance is updated, and the updated dictionary is also passed to the callback specified in the factoryConfigure attribute.
    • Each time an existing Dictionary is removed from the Set, then the corresponding component instance will be stopped and destroyed.
  • factoryConfigure: Sets the "configure" callback method name to be called with the factory configuration. This attribute only makes sense if the factorySet() attribute is used. If specified, then this attribute references a component callback method, which is called for providing the configuration supplied by the factory that instantiated this Service. The current Service properties will be also updated with all public properties (which don't start with a dot).

Usage example:

 /**
   * This Service will be activated once the bundle is started and when all required dependencies
   * are available.
   */
 @Service
 class X implements Z {
     @ConfigurationDependency(pid="MyPid")
     void configure(Dictionary conf) {
          // Configure or reconfigure our service.
     }

     @Start
     void start() {
         // Our Service is starting and is about to be registered in the OSGi registry as a Z service.
     }

     public void doService() {
         // ...
     }
 }

Example using a factorySet:

  @Service(factorySet="MyServiceFactory", factoryConfigure="configure")
  class X implements Z {
      void configure(Dictionary conf) {
          // Configure or reconfigure our service. The conf is provided by the factory,
          // and all public properties (which don't start with a dot) are propagated with the
          // Service properties specified in the properties's Service attribute.
      }

      @ServiceDependency
      void bindOtherService(OtherService other) {
          // store this require dependency
      }

      @Start
      void start() {
          // Our Service is starting and is about to be registered in the OSGi registry as a Z service.
      }

      public void doService() {
          // ... part of Z interface
      }
  }

  /**
    * This class will instantiate some X Service instances
    */
  @Service
  class Y {
      @ServiceDependency(filter="(dm.factory.name=MyServiceFactory)")
      Set<Dictionary> _XFactory; // This Set acts as a Factory API for creating X Service instances.

      @Start
      void start() {
          // Instantiate a X Service instance
          Dictionary x1 = new Hashtable() {{ put("foo", "bar1"); }};
          _XFactory.add(x1);

          // Instantiate another X Service instance
          Dictionary x2 = new Hashtable() {{ put("foo", "bar2"); }};
          _XFactory.add(x2);

          // Update the first X Service instance
          x1.put("foo", "bar1_modified");
          _XFactory.add(x1);

          // Destroy X Services (Notice that invoking XFactory.clear() will destroy all X Service instances)
          _XFactory.remove(x1);
          _XFactory.remove(x2);
      }
  }

@AspectService

@AdapterService

@BundleAdapterService

@ResourceAdapterService

@FactoryConfigurationAdapterService

  • No labels