felix-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From pde...@apache.org
Subject svn commit: r1732044 - /felix/site/trunk/content/documentation/subprojects/apache-felix-dependency-manager/reference/dependency-configuration.mdtext
Date Wed, 24 Feb 2016 09:26:47 GMT
Author: pderop
Date: Wed Feb 24 09:26:47 2016
New Revision: 1732044

URL: http://svn.apache.org/viewvc?rev=1732044&view=rev
Log:
updated dm doc for configuration types

Modified:
    felix/site/trunk/content/documentation/subprojects/apache-felix-dependency-manager/reference/dependency-configuration.mdtext

Modified: felix/site/trunk/content/documentation/subprojects/apache-felix-dependency-manager/reference/dependency-configuration.mdtext
URL: http://svn.apache.org/viewvc/felix/site/trunk/content/documentation/subprojects/apache-felix-dependency-manager/reference/dependency-configuration.mdtext?rev=1732044&r1=1732043&r2=1732044&view=diff
==============================================================================
--- felix/site/trunk/content/documentation/subprojects/apache-felix-dependency-manager/reference/dependency-configuration.mdtext
(original)
+++ felix/site/trunk/content/documentation/subprojects/apache-felix-dependency-manager/reference/dependency-configuration.mdtext
Wed Feb 24 09:26:47 2016
@@ -2,24 +2,100 @@ Title: Dependency Manager - Configuratio
 
 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.
 
-## @ConfigurationDependency
+The dependency injects by default the configuration in an "updated" callback which can accept
the following parameters:
+
+- updated(Dictionary)
+- updated(Component, Dictionary)
+- updated(ConfigurationType)
+- updated(Component, ConfigurationType)
+
+If you only specify a pid, by default the callback method name is assumed to be "updated".
+
+Configuration types are a new feature that allows you to specify an interface that is implemented
by DM and such interface is then injected to your callback instead of the actual Dictionary.
Using such configuration interface provides a way for creating type-safe configurations from
a actual Dictionary that is normally injected by Dependency Manager. The callback accepts
in argument an interface that you have to provide, and DM will inject a proxy that converts
method calls from your configuration-type to lookups in the actual map or dictionary. The
results of these lookups are then converted to the expected return type of the invoked configuration
method.
+As proxies are injected, no implementations of the desired configuration-type are necessary!
+
+The lookups performed are based on the name of the method called on the configuration type.
The method names are "mangled" to the following form: [lower case letter] [any valid character]*.
Method names starting with get or is (JavaBean convention) are stripped from these prefixes.
For example: given a dictionary with the key "foo" can be accessed from a configuration-type
using the following method names: foo(), getFoo() and isFoo().
+
+The return values supported are: primitive types (or their object wrappers), strings, enums,
arrays of primitives/strings, Collection types, Map types, Classes and interfaces. When an
interface is returned, it is treated equally to a configuration type, that is, it is returned
as a proxy.
+
+Arrays can be represented either as comma-separated values, optionally enclosed in square
brackets. For example: [ a, b, c ] and a, b,c are both considered an array of length 3 with
the values "a", "b" and "c". Alternatively, you can append the array index to the key in the
dictionary to obtain the same: a dictionary with "arr.0" => "a", "arr.1" => "b", "arr.2"
=> "c" would result in the same array as the earlier examples.
+
+Maps can be represented as single string values similarly as arrays, each value consisting
of both the key and value separated by a dot. Optionally, the value can be enclosed in curly
brackets. Similar to array, you can use the same dot notation using the keys. For example,
a dictionary with
+
+ "map" => "{key1.value1, key2.value2}"
+
+and a dictionary with
+
+ "map.key1" => "value1", "map2.key2" => "value2"
+
+result in the same map being returned. Instead of a map, you could also define an interface
with the methods getKey1() and getKey2 and use that interface as return type instead of a
Map.
 
-A configuration dependency is always required, and allows you to depend on the availability
of a valid configuration for your component. This dependency requires the OSGi Configuration
Admin Service.
+In case a lookup does not yield a value from the underlying map or dictionary, the following
rules are applied:
 
+- primitive types yield their default value, as defined by the Java Specification;
+- string, Classes and enum values yield null;
+- for arrays, collections and maps, an empty array/collection/map is returned;
+- for other interface types that are treated as configuration type a null-object is returned.

+
+Usage example where a component depends on a configuration:
+
+    :::java
+    public class ServiceImpl {
+        void modified(Dictionary<String, Object> cnf) {
+            if (cnf != null) {
+                String addr = (String) cnf.get("address");
+                int port = Integer.valueOf(cnf.get("port");
+                ...
+            }
+        }
+    }
+
+    public class Activator extends DependencyActivatorBase {
+        @Override
+        public void init(BundleContext ctx, DependencyManager dm) throws Exception {
+            dm.add(createComponent()
+              .setImplementation(ServiceImpl.class)            
+              .add(createConfigurationDependency().setPid(ServiceImpl.class.getName()));
+        }
+    }
+
+Here is the same example, but a custom configuration type interface is used (by default,
the fqdn of the configuration type is assumed to be the configuration pid):
+
+    :::java
+    public interface MyConfig {
+        String getAddress();
+        int getPort();
+    }
+
+    public class ServiceImpl {
+        void modified(MyConfig cnf) {
+            if (cnf != null) {
+                String addr = cnf.getAddress();
+                int port = cnf.getPort();
+                ...
+            }
+        }
+    }
+
+    public class Activator extends DependencyActivatorBase {
+        @Override
+        public void init(BundleContext ctx, DependencyManager dm) throws Exception {
+            dm.add(createComponent()
+              .setImplementation(ServiceImpl.class)            
+              .add(createConfigurationDependency().setCallback("updated", MyConfig.class);
+        }
+    }
+
+
+## @ConfigurationDependency
+
+Configuration dependencies can be defined usnig the @ConfigurationDependency.
 Annotation attributes:
 
 * *pid*: Returns the pid for a given service (by default, the pid is the service class name).
-* *pidClass*: Will the the name of the specified class as the the pid for a given service
(by default, the pid is the service class name).
 * *propagate*: Returns true if the configuration properties must be published along with
the service. Any additional service properties specified directly are merged with these.
-* *name*: The name for this configuration dependency. When you give a name a dependency,
it won't be evaluated immediately, but after the component's init method has been called,

-and from the init method, you can then return a map in order to dynamically configure the

-configuration dependency (the map has to contain a "pid" and/or "propagate" flag, prefixed

-with the dependency name). Then the dependency will be evaluated after the component init

-method, and will be injected before the start method. 
-
-## Usage Examples
 
-In the following example, the "Printer" component depends on a configuration whose PID name
is "org.apache.felix.sample.Printer". This service will initialize its ip/port number from
the provided configuration:
+In the following example, the "Printer" component depends on a configuration with "org.apache.felix.sample.Printer"
PID.
 
     :::java
     package org.apache.felix.sample;
@@ -37,7 +113,7 @@ WebConsole GUI. Using these meta data, y
 configurations data, some descriptions, the cardinality of configuration values, etc ...
 (we use here standard bnd metatype annotations, [see bnd metatype documentation here](http://www.aqute.biz/Bnd/MetaType).
 
- First, we define the configuration metadata, using standard bndtools metatatype annotations:
+First, we define our PrinterConfiguration interface annotated with standard bndtools metatatype
annotations:
 
      :::java
      package sample;
@@ -53,7 +129,9 @@ configurations data, some descriptions,
          int portNumber();
      }
      
- Next, we define our Printer service which instantiates the PrinterConfiguration using the
*Configurable" bndlib helper:
+ Next, we define our Printer service with an `updated` method which is injected with the
+PrinterConfiguration type that is implemented by DM (all interface methods will lookup in
the 
+actual configuration dictionary).
 
      :::java
      package sample;
@@ -61,60 +139,15 @@ configurations data, some descriptions,
 
      @Component
      public class Printer {
-         @ConfigurationDependency(pidClass = PrinterConfiguration.class) // Will use pid
"sample.PrinterConfiguration"
-         void updated(Dictionary props) {
-             // load configuration from the provided dictionary, or throw an exception of
any configuration error.
-             PrinterConfig cnf = Configurable.createConfigurable(PrinterConfig.class, props);
-             String ip = cnf.ipAddress();
-             int port = cnf.portNumber();
-             ...
+         @ConfigurationDependency // Will use pid "sample.PrinterConfiguration"
+         void updated(PrinterConfiguration cnf) {
+             if (cnf != null) {
+                 // load configuration from the provided dictionary, or throw an exception
of any configuration error.
+                 String ip = cnf.ipAddress();
+                 int port = cnf.portNumber();
+                 ...
+             }
          }
      }
 
-Finally, the last example shows how to dynamically configure a configuration dependency pid
from the init method.
-The following component first depends on a "sample.MyComponent" configuration pid. Then the
init method gets from that configuration 
-another pid for a second "global" configuration:
-
-
-    :::java
-    package sample;
-    
-    /**
-      * A Service that dynamically defines an extra dynamic configuration dependency from
its init method. 
-      */
-    @Component
-    class MyComponent {
-        private Dictionary m_config;
-        
-        // Inject initial Configuration (injected before any other required dependencies)
-        @ConfigurationDependency
-        void componentConfiguration(Dictionary config) {
-            // you must throw an exception if the configuration is not valid
-            m_config = config;
-        }
-      
-        /**
-         * All unnamed dependencies are injected: we can now configure our dynamic configuration
whose dependency name is "global".
-         */
-        @Init
-        Map init() {
-            Map properties = new HashMap();
-    	    properties.put("global.pid", m_config.get("globalConfig.pid"));
-    	    properties.put("global.propagate", m_config.get("globalConfig.propagate"));
-    	    return properties;
-        } 
-     
-        // Injected after init, and dynamically configured by the init method.
-        @ConfigurationDependency(name="global")
-        void globalConfiguration(Dictionary globalConfig) {
-    	    // you must throw an exception if the configuration is not valid
-        }
-    
-        /**
-         * All dependencies are injected and our service is now ready to be published.
-         */
-        @Start
-        void start() {
-        }
-    }
 



Mime
View raw message