felix-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From conflue...@apache.org
Subject [CONF] Apache Felix > Apache Felix Dependency Manager - Using Annotations - Lifecycle
Date Tue, 24 Aug 2010 20:16:00 GMT
<html>
<head>
    <base href="https://cwiki.apache.org/confluence">
            <link rel="stylesheet" href="/confluence/s/1810/9/1/_/styles/combined.css?spaceKey=FELIX&amp;forWysiwyg=true"
type="text/css">
    </head>
<body style="background: white;" bgcolor="white" class="email-body">
<div id="pageContent">
<div id="notificationFormat">
<div class="wiki-content">
<div class="email">
    <h2><a href="https://cwiki.apache.org/confluence/display/FELIX/Apache+Felix+Dependency+Manager+-+Using+Annotations+-+Lifecycle">Apache
Felix Dependency Manager - Using Annotations - Lifecycle</a></h2>
    <h4>Page <b>edited</b> by             <a href="https://cwiki.apache.org/confluence/display/~pderop">Pierre
De Rop</a>
    </h4>
        <br/>
                         <h4>Changes (1)</h4>
                                 
    
<div id="page-diffs">
            <table class="diff" cellpadding="0" cellspacing="0">
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >We have seen that a component may
declare some dependencies and is  started when all required dependencies are available. But
there are some  cases when you may need to define some dependencies filters  dynamically,
possibly from data picked up from other  dependencies (like a configuration dependency for
instance). <br> <br></td></tr>
            <tr><td class="diff-changed-lines" >So, all this is possible using
*named* dependencies: When you assign a name to a dependency; for instance <span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;">_@ServiceDependency(name=&quot;MyDependency&quot;)_,</span>
<span class="diff-added-words"style="background-color: #dfd;">_@ServiceDependency(name=&quot;foo&quot;)_,</span>
then this has an impact on how the dependency is handled. Indeed, all named dependencies are
calculated *after* the @Init method returns. So from your @Init method, you can then  configure
your named dependencies, using data provided by  already injected dependencies. <br></td></tr>
            <tr><td class="diff-unchanged" > <br>To do so, your @Init method
is allowed to return a Map containing  the filters and required flags for each named dependencies.
For a given  named dependency, the corresponding filter and required flag must be  stored
in the Map, using the &quot;*filter*&quot; and &quot;*required*&quot; keys,
prefixed with the name of the dependency. <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
        </table>
</div>                            <h4>Full Content</h4>
                    <div class="notificationGreySide">
        <h1><a name="ApacheFelixDependencyManager-UsingAnnotations-Lifecycle-AnnotationsLifecycle"></a>Annotations
- Lifecycle</h1>




<p>A component has a lifecycle that controls when it is started or stopped. A bundle
must be started before the DM Runtime can process its components. When the bundle is started,
the DM Runtime will parse a specific <em>DependencyManager-Component</em> manifest
headers, which points to a list of descriptors defining all annotated components. Such descriptors
are actually generated at compilation time, and annotation are not reflectively parsed at
runtime. Only the descriptor is used to process the components. For each component, the DM
Runtime first ensures that all dependencies are satisfied before activating it. Likewise,
the component is deactivated when some of the required dependencies are not available anymore
or when the bundle is stopped. Unless the bundle is stopped, components may be deactivated
and reactivated, dependending on the departure and arrival of required dependencies. The manager
which is in charge of maintaining the state of components is implemented in the DM Runtime
bundle (org.apache.felix.dm.runtime bundle).</p>

<h2><a name="ApacheFelixDependencyManager-UsingAnnotations-Lifecycle-LifecycleCallbacks"></a>Lifecycle
Callbacks</h2>

<h3><a name="ApacheFelixDependencyManager-UsingAnnotations-Lifecycle-ComponentActivation"></a>Component
Activation</h3>



<p>Activating a component consists of the following steps:</p>
<ol>
	<li>Wait for all required dependencies to be available. When all required dependencies
are available:
	<ol>
		<li>Instantiate the component.</li>
		<li>Inject all required dependencies (on class fields using reflection, or by invoking
callback methods).</li>
		<li>Inject all optional dependencies defined on class fields, possibly with a NullObject
if the dependency is not available.</li>
		<li>Call the component init method (annotated with <b>@Init</b>). In the
 Init method, you are yet allowed to add some additional dependencies  (but using the API).
Alternatively, you can also configure some  dependencies dynamically (explained later, in
<a href="#ApacheFelixDependencyManager-UsingAnnotations-Lifecycle-DynamicDependencyConfiguration">Dynamic
Dependency Configuration</a>).</li>
	</ol>
	</li>
	<li>Wait for extra dependencies eventually configured from the init() method.</li>
	<li>If the component is not using the @<b>LifecycleController</b> annotation
(detailed in the <a href="#ApacheFelixDependencyManager-UsingAnnotations-Lifecycle-ControllingtheLifecycle">Controlling
the Lifecycle</a> section), then:
	<ol>
		<li>Invoke the component start method (annotated with <b>@Start</b>).</li>
		<li>Publish eventual OSGi services (if the component provides some services).</li>
		<li>Start tracking optional dependencies applied on method callbacks (useful for the
whiteboard pattern).</li>
	</ol>
	</li>
	<li>Else do nothing because&nbsp; the component will trigger itself the startup
using the lifecycle controller.</li>
</ol>


<h3><a name="ApacheFelixDependencyManager-UsingAnnotations-Lifecycle-ComponentDeactivation"></a>Component
Deactivation</h3>

<p>Deactivating a component consists of the following steps:</p>
<ol>
	<li>If the bundle is stopped or if some required dependencies are unavailable, Invoke
the stop method (annotated wit <b>@Stop</b>),  and unregister eventual OSGi services
(if the components provides some  services). Any optional dependency unavailability does not
trigger the  component deactivation, and the <em>removed</em> callbacks are just
invoked, if declared in the annotation.</li>
	<li>invoke destroy method (annotated with <b>@Destroy</b>)</li>
	<li>invoke <em>removed</em> dependency callbacks, if any</li>
</ol>


<h3><a name="ApacheFelixDependencyManager-UsingAnnotations-Lifecycle-Example"></a>Example</h3>

<p>The following example show a basic service, which uses the @Start, @Stop, annotation:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
/**
 * A Service Using lifecyce callbacks
 */
@Service
class X <span class="code-keyword">implements</span> Y {
    @ServiceDependency
    void bindOtherService(OtherService other) {
       <span class="code-comment">// Will be injected before we are started (because
it's a required dependency).
</span>    }

    @Start
    void publishing() {
        <span class="code-comment">// All required dependencies are injected: initialize.
Once we <span class="code-keyword">return</span>, the service will be published
</span>        <span class="code-comment">// in the OSGi registry.
</span>    }

    @Stop
    void unpublished() {
       <span class="code-comment">// We are not registered anymore in the OSGi registry.
</span>    }
}
</pre>
</div></div>

<h3><a name="ApacheFelixDependencyManager-UsingAnnotations-Lifecycle-"></a></h3>


<h3><a name="ApacheFelixDependencyManager-UsingAnnotations-Lifecycle-DynamicDependencyConfiguration"></a>Dynamic
Dependency Configuration</h3>


<h4><a name="ApacheFelixDependencyManager-UsingAnnotations-Lifecycle-Rationale"></a>Rationale</h4>

<p>We have seen that a component may declare some dependencies and is  started when
all required dependencies are available. But there are some  cases when you may need to define
some dependencies filters  dynamically, possibly from data picked up from other  dependencies
(like a configuration dependency for instance).</p>

<p>So, all this is possible using <b>named</b> dependencies: When you assign
a name to a dependency; for instance <em>@ServiceDependency(name="foo")</em>,
then this has an impact on how the dependency is handled. Indeed, all named dependencies are
calculated <b>after</b> the @Init method returns. So from your @Init method, you
can then  configure your named dependencies, using data provided by  already injected dependencies.</p>

<p>To do so, your @Init method is allowed to return a Map containing  the filters and
required flags for each named dependencies. For a given  named dependency, the corresponding
filter and required flag must be  stored in the Map, using the "<b>filter</b>"
and "<b>required</b>" keys, prefixed with the name of the dependency.</p>

<p>For instance, if you define a Dependency like this:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
@ServiceDependency(name=<span class="code-quote">"foo"</span>)
FooService fooService;
</pre>
</div></div>
<p>Then you can return this map from your @Init method:</p>


<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
@Init
Map init() {
    <span class="code-keyword">return</span> <span class="code-keyword">new</span>
HashMap() {{
        put(<span class="code-quote">"foo.filter"</span>, <span class="code-quote">"(foo=bar)"</span>);
        put(<span class="code-quote">"foo.required"</span>, <span class="code-quote">"<span
class="code-keyword">false</span>"</span>);
    }};
}
</pre>
</div></div>
<p>So, after the init method returns, the map will be used to configure  the dependency
named "foo", which will then be evaluated. And once the  dependency is available, then your
@Start callback will be invoked.</p>

<h4><a name="ApacheFelixDependencyManager-UsingAnnotations-Lifecycle-Usageexample%3A"></a>Usage
example:</h4>

<p>This is an example of a service X whose dependency "foo" filter is  configured from
ConfigAdmin. First, we defined a ConfigurationDependency  in order to get injected with our
configuration. Next, we define a  dependency on the <em>FooService</em>, but this
time, we declare the annotation  like this: <em>@ServiceDependency(</em><b><em>name="foo"</em></b><em>)</em>.
As explained  above, The  ConfigurationDependency will be injected before the @Init method,
but  the named dependency ("foo") will be calculated after the @Init method  returns. So,
from our Init method, we just return a map which contains  the filter and required flag for
the "foo" dependency, and we actually  use the configuration which has already been injected:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
/**
 * A Service whose FooService dependency filter is configured from ConfigAdmin
 */
@Service
class X {
    <span class="code-keyword">private</span> Dictionary m_config;

    /**
     * Initialize our service from config ... and store the config <span class="code-keyword">for</span>
later usage (from our init method)
     */
    @ConfigurationDependency(pid=<span class="code-quote">"MyPid"</span>)
    void configure(Dictionary conf) {
         m_config = config;
    }

    /**
     * All unnamed dependencies are injected: we can now configure other named
     * dependencies, using the already injected configuration.
     * The returned Map will be used to configure our <span class="code-quote">"foo"</span>
Dependency (see below)
     */
    @Init
    Map init() {
        <span class="code-keyword">return</span> <span class="code-keyword">new</span>
HashMap() {{
            put(<span class="code-quote">"foo.filter"</span>, m_config.get(<span
class="code-quote">"filter"</span>));
            put(<span class="code-quote">"foo.required"</span>, m_config.get(<span
class="code-quote">"required"</span>));
        }};
    }

    /**
     * This named dependency filter/required flag will be configured by our init method (see
above).
     */
    @ServiceDependency(name=<span class="code-quote">"foo"</span>)
    FooService fooService;

    /**
     * All dependencies are injected and our service is now ready to be published.
     */
    @Start
    void start() {
    }
}
</pre>
</div></div>



<h2><a name="ApacheFelixDependencyManager-UsingAnnotations-Lifecycle-"></a></h2>


<h2><a name="ApacheFelixDependencyManager-UsingAnnotations-Lifecycle-ControllingtheLifecycle"></a>Controlling
the Lifecycle</h2>

<p>As explained in the <em>Component Activation</em> section, a  component
which provides a service is automatically registered into the  OSGi registry, after the @Start
method returns. But it is sometimes  required to control when the service is really started/published
or  unpublished/stopped.</p>

<p>This can be done using the @LifecycleController annotation. This  annotation injects
a Runnable object that can be invoked once you want  to trigger your service startup and publication.</p>

<p>For instance, imagine that your component publishes an OSGi service,  but before,
it needs to register into a DHT (Distributed Hash Table),  whose API is asynchronous: that
is: the DHT API will callback you once  you are inserted into a node in the DHT. In this case,
what you would  like to do is to publish your OSGi service, but only after you are  inserted
into the DHT (when the DHT callabacks you) ... Such a case  is supported using the @LifecyceController
annotation, which gives you  full control of when your component is started/stopped.</p>

<p>Let's illustrate this use case with a concrete example: First here is the DHT asynchronous
API:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
/**
 * This is an element which can be inserted into the distributed hash table.
 */
<span class="code-keyword">public</span> <span class="code-keyword">interface</span>
DHTElement {
   void inserted(); <span class="code-comment">// callback used to notify that the element
is inserted into the DHT
</span>}

/**
 * This is the DHTService, which registers a DHTElement asynchronously.
 */
<span class="code-keyword">public</span> <span class="code-keyword">interface</span>
DHTService {
   void insert(DHTElement element); <span class="code-comment">// will callback element.inserted()
later, once registered into the DHT.
</span>}
</pre>
</div></div>
<p>Next, here is our service, which uses the @LifecycleController in  order to take
control of when the service is published into the OSGi  registry:</p>


<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
@Service(provides={MyService.class})
<span class="code-keyword">public</span> class MyServiceImpl <span class="code-keyword">implements</span>
MyService, DHTElement {
    @ServiceDependency
    DHTService dht;

    @LifecycleController
    <span class="code-object">Runnable</span> trigger; <span class="code-comment">//
will fire component startup, once invoked.
</span>
    @Init
    void init() {
        dht.insert(<span class="code-keyword">this</span>); <span class="code-comment">//
asynchronous, will callback us in our inserted method once registered into the DHT
</span>    }

    void inserted() {
        <span class="code-comment">// We are inserted into the DHT: we can now trigger
our component startup.
</span>        <span class="code-comment">// We just invoke the runnable injected
by our @LifecycleController annotation, which will trigger our
</span>        <span class="code-comment">// service publication (we'll be called
in our @Start method before)
</span>        trigger.run();
    }

    @Start
    void start() {
        <span class="code-comment">// method called only once we invoke our trigger
<span class="code-object">Runnable</span> (see inserted method).
</span>        <span class="code-comment">// Our Service will be published once
<span class="code-keyword">this</span> method returns.
</span>    }
}
</pre>
</div></div>

<h2><a name="ApacheFelixDependencyManager-UsingAnnotations-Lifecycle-"></a></h2>


<h2><a name="ApacheFelixDependencyManager-UsingAnnotations-Lifecycle-DynamicServiceProperties"></a>Dynamic
Service Properties</h2>

<p>When a component provides an OSGi Service, the service properties are calculated
as the following:</p>
<ul>
	<li>Any properties specified in the @Service annotation are used to provide the OSGi
Service</li>
	<li>Any properties provided by a FactorySet are also inserted in the published service</li>
	<li>Any Dependency whose <em>propagate</em> attribute is set to true will
also insert its properties to the published service</li>
</ul>


<p>But there is also a use case where your own component might want to  dynamically
specify the list of properties which should be also inserted  in the published service, and
you can do this by returning an optional  Map from your @Start callback method. For instance:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
@Service(properties={@Property(name=<span class="code-quote">"foo"</span>, value=<span
class="code-quote">"bar"</span>)})<span class="code-keyword">public</span>
class MyServiceImpl <span class="code-keyword">implements</span> MyService {
    @ConfigurationDependency(pid=<span class="code-quote">"MyPid"</span>, propagate=<span
class="code-keyword">true</span>)
    void updated(Dictionary conf) {
       <span class="code-comment">// <span class="code-quote">"conf"</span>
contains foo2=bar2, <span class="code-keyword">for</span> example, and since we
have set the <span class="code-quote">"propagate"</span> attribute to <span
class="code-keyword">true</span>, then
</span>       <span class="code-comment">// the property will be propagated to
our published service ...
</span>    }

    @Start
    Map start() {
        <span class="code-comment">// Return some extra properties to be inserted along
with our published properties. This map takes
</span>        <span class="code-comment">// precedence, and may override some
properties specified in our @Service annotation, or some properties
</span>        <span class="code-comment">// propagated from our @ConfigurationDependency
dependency ...
</span>        <span class="code-keyword">return</span> <span class="code-keyword">new</span>
HashMap() {{ put(<span class="code-quote">"foo3"</span>, <span class="code-quote">"bar3"</span>);
}};
    }
}
</pre>
</div></div>
<p>Here, the service MyService will be published into the OSGi registry along with the
following service properties:</p>
<ul>
	<li>foo=bar (specified in our @Service annotation)</li>
	<li>foo2=bar2 (propagated by our ConfigurationDependency dependency)</li>
	<li>foo3=bar3 (specified dynamically in the map returned by our start method)</li>
</ul>


<p>Notice that properties returned by the Map takes precedence over other properties,
and may override some of them.</p>
    </div>
        <div id="commentsSection" class="wiki-content pageSection">
        <div style="float: right;">
            <a href="https://cwiki.apache.org/confluence/users/viewnotifications.action"
class="grey">Change Notification Preferences</a>
        </div>
        <a href="https://cwiki.apache.org/confluence/display/FELIX/Apache+Felix+Dependency+Manager+-+Using+Annotations+-+Lifecycle">View
Online</a>
        |
        <a href="https://cwiki.apache.org/confluence/pages/diffpagesbyversion.action?pageId=23335756&revisedVersion=5&originalVersion=4">View
Changes</a>
                |
        <a href="https://cwiki.apache.org/confluence/display/FELIX/Apache+Felix+Dependency+Manager+-+Using+Annotations+-+Lifecycle?showComments=true&amp;showCommentArea=true#addcomment">Add
Comment</a>
            </div>
</div>
</div>
</div>
</div>
</body>
</html>

Mime
View raw message