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:
Now we have introduced the different types of components, here is the list of annotations, allowing to declare DependencyManager service components:
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:
Usage example:
/**
* This component will be activated once the bundle is started and when all required dependencies
* are available.
*/
@Component
class X implements Z {
@ConfigurationDependency(pid="MyPid")
void configure(Dictionary conf) {
// Configure or reconfigure our service.
}
@Start
void start() {
// Our component is starting and is about to be registered in the OSGi registry as a Z service.
}
public void doService() {
// ...
}
}
|
Example using a factorySet, where the X component is instantiated/updated/disposed by another Y component:
@Component(factorySet="MyComponentFactory", factoryConfigure="configure")
class X implements Z {
void configure(Dictionary conf) {
// Configure or reconfigure our component. The conf is provided by the factory,
// and all public properties (which don't start with a dot) are propagated with the
// Service properties eventually specified in the properties annotation attribute.
}
@ServiceDependency
void bindOtherService(OtherService other) {
// store this require dependency
}
@Start
void start() {
// Our component 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 component instances
*/
@Component
class Y {
@ServiceDependency(filter="(dm.factory.name=MyComponentFactory)")
Set<Dictionary> _XFactory; // This Set acts as a Factory API for creating X component instances.
@Start
void start() {
// Instantiate a X component instance
Dictionary x1 = new Hashtable() {{ put("foo", "bar1"); }};
_XFactory.add(x1);
// Instantiate another X component instance
Dictionary x2 = new Hashtable() {{ put("foo", "bar2"); }};
_XFactory.add(x2);
// Update the first X component instance
x1.put("foo", "bar1_modified");
_XFactory.add(x1);
// Destroy all components (Notice that invoking _XFactory.clear() also destroys every X instances)
_XFactory.remove(x1);
_XFactory.remove(x2);
}
}
|
Aspects allow you to define an interceptor, or chain of interceptors for a service (to add features like caching or logging, etc ...). The dependency manager intercepts the original service, and allows you to execute some code before invoking the original service ... The aspect will be applied to any service that matches the specified interface and filter and will be registered with the same interface and properties as the original service, plus any extra properties you supply here. It will also inherit all dependencies, and if you declare the original service as a member it will be injected.
Annotation attributes:
Usage example:
@AspectService(ranking=10), properties={@Property(name="param", value="value")})
class AspectService implements InterceptedService {
// The service we are intercepting (injected by reflection)
protected InterceptedService intercepted;
public void doWork() {
intercepted.doWork();
}
}
|
Adapters, like with @AspectService, are used to "extend" existing services, and can publish different services based on the existing one. An example would be implementing a management interface for an existing service, etc .... When you annotate an adapter class with the @AdapterService annotation, it will be applied to any service that matches the implemented interface and filter. The adapter will be registered with the specified interface and existing properties from the original service plus any extra properties you supply here. If you declare the original service as a member it will be injected.
Annotation attributes:
Usage example: Here, the AdapterService is registered into the OSGI registry each time an AdapteeService is found from the registry. The AdapterImpl class adapts the AdapteeService to the AdapterService. The AdapterService will also have a service property (param=value), and will also include eventual service properties found from the AdapteeService:
@AdapterService(adapteeService = AdapteeService.class, properties={@Property(name="param", value="value")})
class AdapterImpl implements AdapterService {
// The service we are adapting (injected by reflection)
protected AdapteeService adaptee;
public void doWork() {
adaptee.mehod1();
adaptee.method2();
}
}
|
Bundle adapters are similar to AdapterService, but instead of adapting a service, they adapt a bundle with a certain set of states (STARTED|INSTALLED|...), and provide a service on top of it.
The bundle adapter will be applied to any bundle that matches the specified bundle state mask and filter conditions, which may match some of the bundle OSGi manifest headers. For each matching bundle an adapter will be created based on the adapter implementation class. The adapter will be registered with the specified interface and with service properties found from the original bundle OSGi manifest headers plus any extra properties you supply here. If you declare the original bundle as a member it will be injected.
Annotation attributes:
Usage Examples
In the following example, a "VideoPlayer" Service is registered into the OSGi registry each time an active bundle containing a "Video-Path" manifest header is detected:
@BundleAdapterService(filter = "(Video-Path=*)", stateMask = Bundle.ACTIVE, propagate=true)
public class VideoPlayerImpl implements VideoPlayer {
Bundle bundle; // Injected by reflection
void play() {
URL mpegFile = bundle.getEntry(bundle.getHeaders().get("Video-Path"));
// play the video provided by the bundle ...
}
void stop() {}
}
|
Resource adapters are things that adapt a resource instead of a service, and provide an adapter service on top of this resource. Resources are an abstraction that is introduced by the dependency manager, represented as a URL. They can be implemented to serve resources embedded in bundles, somewhere on a file system or in an http content repository server, or database.
The adapter will be applied to any resource that matches the specified filter condition, which can match some part of the resource URL (with "path", "protocol", "port", or "host" filters). For each matching resource an adapter will be created based on the adapter implementation class. The adapter will be registered with the specified interface and with any extra service properties you supply here. Moreover, the following service properties will be propagated from the resource URL:
Usage Examples:
Here, the "VideoPlayer" service provides a video service on top of any movie resources, with service properties "host"/"port"/"protocol"/"path" extracted from the resource URL:
@ResourceAdapterService(filter = "(&(path=/videos/*.mkv)(host=localhost))", propagate = true)
public class VideoPlayerImpl implements VideoPlayer {
// Injected by reflection
URL resource;
void play() {} // play video referenced by this.resource
void stop() {} // stop playing the video
void transcode() {} // ...
}
|
Annotates a class that acts as a Factory Configuration Adapter Service. For each new Config Admin factory configuration matching the specified factoryPid, an instance of this service will be created. The adapter will be registered with the specified interface, and with the specified adapter service properties. Depending on the propagate parameter, every public factory configuration properties (which don't start with ".") will be propagated along with the adapter service properties.
Like in @ConfigurationDependency, you can optionally specify the meta types of your configurations for Web Console GUI customization (configuration heading/descriptions/default values/etc ...).
Annotation attributes:
PropertyMetaData anotation attribute:
Usage Examples
Here, a "Dictionary" service instance is instantiated for each existing factory configuration instances matching the factory pid "DictionaryServiceFactory".
@FactoryConfigurationAdapterService(factoryPid="DictionaryServiceFactory", updated="updated")
public class DictionaryImpl implements DictionaryService
{
/**
* The key of our config admin dictionary language.
*/
final static String LANG = "lang";
/**
* The key of our config admin dictionary values.
*/
final static String WORDS = "words";
/**
* We store all configured words in a thread-safe data structure, because ConfigAdmin
* may invoke our updated method at any time.
*/
private CopyOnWriteArrayList<String> m_words = new CopyOnWriteArrayList<String>();
/**
* Our Dictionary language.
*/
private String m_lang;
protected void updated(Dictionary<String, ?> config) {
m_lang = (String) config.get(LANG);
m_words.clear();
String[] words = (String[]) config.get(WORDS);
for (String word : words) {
m_words.add(word);
}
}
...
}
|
Here, this is the same example as above, but using meta types:
@FactoryConfigurationAdapterService(
factoryPid="DictionaryServiceFactory",
propagate=true,
updated="updated",
heading="Dictionary Services",
description="Declare here some Dictionary instances, allowing to instantiates some DictionaryService services for a given dictionary language",
metadata={
@PropertyMetaData(
heading="Dictionary Language",
description="Declare here the language supported by this dictionary. " +
"This property will be propagated with the Dictionary Service properties.",
defaults={"en"},
id=DictionaryImpl.LANG,
cardinality=0),
@PropertyMetaData(
heading="Dictionary words",
description="Declare here the list of words supported by this dictionary. This properties starts with a Dot and won't be propagated with Dictionary OSGi service properties.",
defaults={"hello", "world"},
id=DictionaryImpl.WORDS,
cardinality=Integer.MAX_VALUE)
}
)
public class DictionaryImpl implements DictionaryService
{
/**
* The key of our config admin dictionary language.
*/
final static String LANG = "lang";
/**
* The key of our config admin dictionary values.
*/
final static String WORDS = "words";
/**
* We store all configured words in a thread-safe data structure, because ConfigAdmin
* may invoke our updated method at any time.
*/
private CopyOnWriteArrayList<String> m_words = new CopyOnWriteArrayList<String>();
/**
* Our Dictionary language.
*/
private String m_lang;
protected void updated(Dictionary<String, ?> config) {
m_lang = (String) config.get(LANG);
m_words.clear();
String[] words = (String[]) config.get(WORDS);
for (String word : words) {
m_words.add(word);
}
}
...
}
|