Reference Guide

Components

Components are declared by the dependency manager and can be implemented by POJOs that contain no references to the OSGi framework whatsoever. Components are the main building blocks of your OSGi application. They have a life cycle, can register themselves as services and have zero or more dependencies.

Life cycle

The dependency manager, as part of a bundle, shares the generic bundle life cycle explained in the OSGi specification. The life cycle of the dependency manager itself, and the components it manages, can be located inside the active state of the hosting bundle.

Each component you define gets its own life cycle, which is explained in the state diagram below.

state-diagram

A component is associated with an instance. This instance can either be specified directly, or you can specify its class. If you do the latter, the actual instance will be created lazily.

Changes in the state of the component will trigger the following life cycle methods:

  • init,
  • start,
  • stop and
  • destroy.

The dependency manager will look for methods with these names and one of the following signatures in this order:

  • (Component),
  • ().

If you don't specify anything, the methods with these names will be invoked on the instance. By using setCallbacks() you can however change this behavior: You can change the names of the methods to look for. Any methods that are set to {{ null }} will not be invoked at all. Another thing you can do is to specify a different instance to invoke these methods on. If you do that, you will usually want to use the first signature, which gives you a reference to the {{ Component }} whose life cycle method was invoked.

Interfaces and properties

Components in the context of the dependency manager can be published as OSGi services under one or more interface names, plus optionally a set of properties. This is no different than a normal OSGi service. It's important to mention that you don't have to register a service. If you don't, you basically created a component that can do work and have dependencies and a managed life cycle.

Composition

When implementing more complex components, you often find yourself using more than one instance. However, several of these instances might want to have dependencies injected. In such cases you need to tell the dependency manager which instances to consider. This has to be a fixed set of instances however.

Factories

Out of the box, there already is support for lazy instantiation, meaning that the dependency manager can create component instances for you when their required dependencies are resolved. However, sometimes creating a single instance using a default constructor is not enough. In those cases, you can tell the dependency manager to delegate the creation process to a factory.

Aspects

Aspects, as part of aspect oriented programming, can be used in a dynamic environment such as OSGi to "extend" existing services and add certain "capabilities" to them. Examples of these are adding a specific caching mechanism to a storage service or implementing logging. Aspects in OSGi can be applied to services and can be added and removed at runtime.

Adapters

Adapters, like aspects, are used to "extend" existing services, and can publish different services based on the existing one. An example would be implementing a management interface.

Resource Adapters

Resource adapters work just like adapters, but instead of working with services, they work with resources. Resources, represented as a URL, are an abstraction introduced to provide a generic way of dealing with "blobs" and can be resources inside a bundle, filesystem or some kind of data store.

Dependencies

The dependency manager supports many different types of dependencies, all of which can be required or optional. A dependency can be added to one or more components and it is possible to add them dynamically (even from within the component itself if necessary, which allows for some really dynamic dependency configuration).

Injection

One way to deal with dependencies is to have them injected into your component instances automatically. All you need to do is simply declare a field of the same type as your dependency, make the member volatile so any changes will become visible immediately and you're done. If a dependency is optional, a null object will be injected if the dependency is not available.

Sometimes you need more control over injection, so optionally you can even specify the name of the field to inject into. This allows you to depend on different dependencies of the same type, or simply to prevent injection into more than one field.

Callbacks

When keeping track of multiple instances of a dependency, or when you simply want something to happen whenever a dependency becomes (un)available or changes, you can define callbacks, like added, changed and removed. Optionally, you can provide the dependency manager with an instance to invoke these callback methods on. If you don't, they'll be invoked on the component instance.

Types of Dependencies

Out of the box, several types of dependencies are supported: service, bundle, configuration, resource and temporal service. However, it's quite easy to add your own custom type of dependency too.

Service

A service dependency allows you to depend on a service, either by type or by using an additional filter condition. You can even depend on an existing service directly by providing a reference to it.

Bundle

A bundle dependency allows you to depend on a bundle in a certain set of states, as indicated by a state mask. You can also use a filter condition that is matched against all manifest entries. Finally you can provide a reference to an existing bundle.

Configuration

A configuration dependency is always required, and allows you to depend on the availability of a valid configuration for your component. Optional configuration dependencies are not supported because in that case you can just as well register as a {{ ManagedService }} yourself.

Resource

A resource dependency allows you to depend on a resource. A resource is a URL and you can use a filter condition based on protocol, host, port, path and URL.

Temporal Service

A temporal service dependency is a special type of service dependency which does not go away anymore. If you invoke it, and in reality the service is temporarily not available, your call will block until a configurable time-out passes. If during that time the service becomes available again, the new service method is invoked. If not, you will get an exception indicating the time-out expired.

Implementing Your Own Dependency

All dependencies share a common API which you can implement yourself if you need a special type of dependency. Whilst not entirely trivial, this allows you to create your own types of dependencies. This can be useful for various scenarios where you want to have components that depend on things that are not services, bundles or configuration.

An example implementation can be found in one of the many test cases for the dependency manager: {{ CustomDependencyTest }}. This implements a dependency that can be made available and unavailable by manipulating a {{ Toggle }} which can be made available or unavailable. You basically have to implement two interfaces: {{ Dependency }} and {{ DependencyActivation }}. The former contains the bulk of the methods that you will need to implement and depending on the actual features you want your dependency to support, you have to implement some or all of them. The JavaDoc for each method plus the example code should get you started. The latter contains a couple of life cycle methods to start and stop tracking your custom dependency.

Monitoring and Shell

The dependency manager has shell commands that allow you to inspect at runtime the state of the individual components and their dependencies. A separate bundle exists that enables these commands, and the shells it currently supports are: Apache Felix, Gogo and Eclipse Equinox Shell.

Filter Indices

These are still experimental.

  • No labels