APAM/Primitive

From Wiki Adele Team
< APAM
Revision as of 10:37, 2 October 2012 by Mehdi (talk | contribs) (Properties)

Jump to: navigation, search

Apam Metamodel

The Apam description metamodel is the following

Figure 1 : APAM Description Metamodel

Building in Maven

In APAM, specifications and implementations are developed as traditional Java classes (POJO); and the additional properties, as well as instance declarations are specified in a component descriptor. The following describes the syntax and semantics of these descriptors, which are xml files that must be included in the bundle containing the associated entity. If using Maven for building, you should include the plugin "ApamMavenPlugin" after the iPOJO plugin "maven-ipojo-plugin" in the following way:

<plugin>
   <groupId>fr.imag.adele.apam</groupId>
   <artifactId>ApamMavenPlugin</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <executions>
      <execution>
         <goals>
            <goal>apam-ipojo-bundle</goal>
         </goals>
      </execution>
   </executions>
</plugin>

For the "s1Impl" implementation below, by default the associated xml file should be called "s1Impl.xml" and be located in the "src/main/resource" directory of the corresponding Eclipse project.

Example

For illustration purpose, let us assume the following java class containing the implementation "S1Impl":

package example.s1Impl;

import example.s1.S1;
import example.s2.S2_1;
import example.s2.S2_2;
import example.s4.S4;
import example.Type1 ;
import example.Type2 ;
import example.Type3 ;
import fr.imag.adele.apam.AbstractProducer ;
import fr.imag.adele.apam.AbstractConsumer ;

public class S1Impl implements S1 {
    private S2_1 s2-1;
    private S2_2 s2-2;
    private S2_1[] s2 ;
    private List<S4> s4s;
    private AbstractConsumer<Type1> consumerType1 ;
    private AbstractProducer<Type2> producerType2 ;
    private String aString ;

    public void callS1(String s) {
        System.out.println("S1 called " + s);
        s2-1.callS2_1("from S1Impl");
        Type2 t2 = producerType2.getMessage() ;
	   consumerType1.sendMessage ((Type1)f(t1)) ;
    }

    public void getMessageType3 (Type3 t3) { 
        ...
    }
}


Specification description

APAM distinguishes two kinds of resource :

- Interfaces : in the Java sense, i.e. fonctionalities provided or required
- Messages : an object produced or consumed.

A specification is an abstraction that factors out the common visible properties and characteristics of a set of implementations; mostly its provided resources and its dependencies toward resources and specification, as seen below:

<apam xmlns="fr.imag.adele.apam">
   <specification name="S1" interfaces="example.s1.S1" messages="example.Type1">
         <dependency specification="S2" />	
         <dependency interface="example.S4"/>
         <dependency message="example.DataType2" />
   </specification>
</apam>

In this example, specification "S1" provides the interface "example.s1.S1" and produces messages of type "example.Type1". It requires the specification "S2", the interface "S4", and will consume messages of type "example.DataType2".
For syntactic conveniance, the same dependency can describe various targets like "<messages="{dataType1, DataType2}" interfaces="{i1, i2}"/>. For specifications, both syntax mean the same.

A specification can also indicate the constraints its dependencies must satisfy (in a similar way as implementations, see below). A specification describe the properties common to all the implementations and the definition of the properties that can be associated to its implementations, as seen below:

<apam xmlns="fr.imag.adele.apam">
   <specification name="S1" interfaces="example.s1.S1" messages="example.Type1">
      <property name="constantA" type="String" value="a"/>
      <definition name="b" type="boolean" value="true"/>
      .....
    </specification>
</apam>

In this example, all implementations implementing "S1" will share the property "constantA="a""; they will have a boolean attribute "b" with "true" as default value, and a property "c". Properties "b" and "c" may have different values in different implementations.

Implementation description

An implementation always implements a single specification, and has (inherits) all the characteritics and properties of that specification. In order to support legacy code, an implementation definition may omit its specification, as in the following example :

<apam xmlns="fr.imag.adele.apam">
	<implementation name="S1Impl" classname="example.s1Impl.S1Impl"/>
</apam >

In this example, the implementation "S1Impl" provides by default all the interfaces implemented (in the java sense) by it class "classname", and does not require anything. Apam will anyway create an associated specification (called "S1Impl_spec") making the hypothesis that this specification provides all the interfaces implemented by "classname" (which may be a wrong assumption).
It is nevertheless strongly advised to explicitly specify the APAM specification provided by an implementation in its definition.

Properties

<apam xmlns="fr.imag.adele.apam">
   <implementation name="S1Impl" classname="example.s1Impl.S1Impl" specification="S1" shared="false">
      <property name="b" value="false"/>
      <definition name="c" value="myC" field="aString"/>
      <definition name="d" type="string" value="defaultD"/>
      <definition name="e" type="string"  />
   </implementation>
</apam >

In this example, implementation "S1Impl" declares that it implements specification "S1" defined above, "S1Impl" "inherit" all the properties and characteristics defined in "S1". Therefore "S1Impl" will have three properties: "constantA="a"", inherited from "S1", "b="false"" and "c="myC"".
Note that "b" and "c" must have been declared in "S1", and default values can be overwritten; furthermore, the value of "c" will be injected into the variable "aString".

A number of properties are predefined by APAM like the "shared" property ( see protection ). In the similar way as specifications, implementations can define the attributes that can be instantiated on their instances; here "S1Impl" instances will have properties "shared="false"", "a", "b" and "c" inherited from "S1Impl", and their own properties "d" and "e"; "d" having a default value "defaultD".

Required resources

The above description of "S1Impl" is incomplete; indeed implementations must make more precise the dependencies inherited from the specification, and may have additional provided and required resources. A correct description of "S1Impl" can be as follows:

<apam xmlns="fr.imag.adele.apam">
   <implementation name="S1Impl" classname="example.s1Impl.S1Impl" specification="S1" 
                messages="example.Type1" message-fields="consumerType1">
  	 <dependency field="s4s"/>
	 <dependency field="producerType3" multiple="false"/>
	 <dependency specification="S2" id="s2-1_s2-2"> 
            <interface field="s2-1" name="example.s2.S2_1"/>
            <interface field="s2-2" />
	 </dependency>
	 <dependency specification="S2" multiple="true">
 	    <interface field="s2" />
	    <message name="example.DataType2" field="producerType2" />
	    <message method="getMessageType3" />
	 </dependency>

   </implementation >
</apam >

Fields refer to the variables in the source code that will be injected (initialised) at execution time. The simplest way to indicate a dependency is to provide only the field name (e.g. "<interface field="s4s"/>"); the other information will be deduced from the source code. An equivalent complete definition would say "<interface field="s4s" name="example.s4.S4" multiple="true" id="s4s"/>". Here attribute "name" provides the name of the interface, attribute "multiple" indicates that the field is either an array, a List or a Set. In this example the field "s4s" will be bound to all the instances implementing (at least) the interface "S4". A dependency always has an id which is unique for an implementation; by default it is the type of the resource (e.g. "S2" for "<specification="S2""), but any another symbolic name can be explicitly provided using attribute "id". If more than one dependency reference the same resource, an "id" must be provided. It is the case for "<specification name="S2" id="s2-1_s2-2">".

A specification defining a set of resources, a dependency toward a specification allows different fields, of different types, to be resolved toward the same service instance, implementing that specification. Fields "s2-1" and "s2-2" will be bound to the same service instance implementing specification "S2". Similarly, fields s2 and messages of type "Type2" and "Type3" will come from the same instance. If both the field and the type are indicated (as in "name=”example.DataType2" field="producerType2""), it is checked that the field is realy of the indicated type, otherwise the type is deduced from the source code. All the fields in a complex dependency must have the same multiplicity, and they will all have the same set of instances.

Message producers

The header "<implementation>" make precise the messages provided by this implementation. It says that the field "consumerType1" in the source code will be injected such that calling "consumerType1.sendMessage (t)" the value of "t" (the message) will be routed to all consumers currently connected to this producer (if any). An abstract consummer is defined by "fr.imag.adele.apam.AbstractConsumer" interface

Public interface AbstractConsumer<T>{
	Public void sendMessage(T t) ;
}

Message consumers

Apam supports two types of message consumers. If the dependency indicates "<message method="getMsgType3">", the method "getMsgType3" will be called each time the target instances of this dependency produces a message of type "Type3".
If the dependency indicates "<message field="consType2">", the field will be injected toward an abstract producer. Calling "consType2.getData()" returns the next available data of type "Type2", or null if none is available. An abstract producer is defined by "fr.imag.adele.apam.AbstractProducer" interface

Public interface AbstractProducer<T>{
	Public T getMessage() ;
}

Dependency cardinality

The number of service references that will be bound to given a dependency is determined by the type of the field used for injection. If the field is declared using an array or some type of Collection, APAM will consider that the cardinality is multiple, and will inject at runtime all the available service references found when the field is used for the first time. Otherwise the dependency is considered as single cardinality and will be bound to a single service. For the previous example the dependency s4s has multiple cardinality. For messages, the cardinality cannot be infered, "multiple="true" indicates a multiple cardinality.

Advanced : filtering dependencies and multiple handlers

An implementation may declare the constraints and preferences that Apam must use when resolving a dependency. In the exemple, when the variables "s2-1" or "s2-2" will be used for the first time, Apam will look (1) for an implementation of "S2" satisfying the filter "(apam-composite=true)"; (2) if none is found, the resolution fails; if more than one implementation satisfy the filter, Apam will select arbitrarily one that satisfy "(x<=10)" is existing, any one if none satisfy. Say "S2Compo" is selected. (3) Apam will try to find an instance satisfying "(SCOPE=GLOBAL)"; if none, Apam instantiate "S2Compo", if possible. If more than one implementation satisfy the filter, Apam will select arbitrarily one that satisfy "(b=false)" is existing, any one if none satisfy.

<implementation name="S1Impl" classname="example.s1.S1Impl" specification="S1"	 
    messages="Type1" message-fields="consumerType1">

      <dependency specification="S2" id="s2-1_s2-2" >
            <interface field="s2-1" />
            <interface field="s2-2" />
         <constraints>
            <implementation filter="(apam-composite=true)"/> 
            <instance filter="(SCOPE=GLOBAL)"/> 
         </constraints>
	 <preferences>
	    <implementation filter="(x<=10)"/> 
            <instance filter="(b=false)"/> 
         </preferences >
      </dependency>
      ....

   <ipojo:requires field="s2" />
   ....
</implementation>
</apam >

The constraints declared in an implementation are said to be "intrinsic" and will have to be satisfied by all clients of that implementation; those declared in composites are said to be "contextual" and are to be satisfied only by clients pertaining to that composite. This improves the re-usability of primitive implementations as their dependency management can be customized in the context of a given application (composite).

NOTE : iPojo also proposes a service dependency specification. The iPojo and APAM mechanisms can coexist. The developer can decide which dependencies are managed by iPojo and which are managed by APAM. The main difference is that the dependency resolution policy is usualy not specified in the implementation itself but in the composite(s) that use this implementation. In the following, field "s2" will be managed by ipojo, the others by APAM.

Instance definition

Apam is in charge to dynamicaly select the existing instances, or to instantiate them if needed. Nevetheless it is possible to, staticaly, create instances of the defined implementations. In the following, when the bundle containing "S1Impl" will be started, an instance of "S1Impl", named "firstS1Impl" will be created. It is also possible to give its initial values.

<apam xmlns="fr.imag.adele.apam">	
<implementation name="S1Impl" className="example.s1.S1Impl" specification="S1"	 
    messages="Type1" message-fields="consumerType1" shared="false">
      <property name="b" value="false"/>
      <definition name="c" value="myC" field="aString"/>
      <definition name="d" type="string" value="defaultD"/>
      <definition name="e" type="string"  />
   <dependency >
    ...
   </dependency>
</implementation>

<instance implementation="S1Impl" name="firstS1Impl">
      <property name="d" value="myD" />
      <property name="e" value="example"/>
      <dependency specification="S2" id="s2-1_s2-2">
         <constraints>
            <implementation filter="(x<=5)"/> 
            <instance filter="(scope=local)"/> 
         </constraints>
	 <preferences>
            <instance filter="(b=true)"/> 
         </preferences >
      </dependency >
      ....
</instance>
</apam>

In this example, instance "firstS1Impl" (name is optional) will be created with the properties "constantA="a"" (from "S1"), "shared="false"", "b="false"" and "c="myC"" (from "S1Impl"), and its own properties "d="myD"" and "e="example"". An instance can set different dependencies constraints and preferences. If so the constraints and preferences defined in the associated specification and implementation will be ignored, and only the provided contraints and preferences will used. If the dependency type is ambiguous (two dependencies are related to "S2"), the dependency "id" must be provided. If an instance must be executed immediately when created, it must implement the interface ApamComponent.

public class S1Impl implements S1, ApamComponent {
    public void apamStart(Instance inst) { ... } ;
    public void apamStop() { ... } ;
    public void apamRelease() { ... } ;
    ...

Upon instanciation, Apam will call the method "apamStart", with the instance object as parameter "inst". From "inst" it is possible navigate and to get the complete state of the current execution i.e. the "inst" attributes, its implementation, composite, dependencies and so on. See the APAM API.

Dependency management

Introduction

Apam is a platform designed for the support of a number of independent applications using and sharing services and devices in a context at least partially unpredictable. The platform does its best to simplify the design and development of such applications by automating the selection and connection between services, by handling the dynamic devices and services, by managing the application coordination and conflicts. To a large extent, the platform automatically adapts the applications to the current context, within the range of admissible behavior as expressed in the metadata.
In the following we detail how the platform manages the Apam dependencies; and how to change the default behavior.

Lazy and Dynamic bindings

To illustrate, take again our example :

public class S1Impl implements S1 {
(1)  private S2_1 s2-1;
(2)  private List<S4> s4s;
	
(3)   s2-1.callS2_1("from S1Impl");
(4)   for (S4 s4 : s4s) {  }

The variables mentioned in the “field” attributes of the meta data (s2-1 and s4s in our example) are called “instrumented variable”; it means that the value of these variables will be completely managed, transparently by Apam. In the example above, s2-1 is an instrumented variable, it is not initialized in the code, but line 3 will work because Apam will transparently set the address of a S2_1 object in the variable. Most of the following are variations around the moment and the way instrumented variables are managed.

By default, Apam is lazy and dynamic. Apam is lazy because it is only when executing line (3) for the first time that the dependency is resolved. If the resolution fails, s2-1 will be null. If the S2_1 object disappears during execution, variable s2-1 will become null, and another resolution will be attempted at each access to the s2-1 variable. If the new resolution succeeds, the s2-1 variable changes transparently during execution. It is therefore strongly advised not to copy variable s2-1 into other variables or to give it as parameter. Setting variable s2-1 manually in the program is ignored (for example, writing “s2-1= null;” in the program does nothing).
Similarly, the s4s set will be resolved only when executing line (4) for the first time; it will contain all the S4 objects satisfying the constraints at time of the resolution, or the empty set of none are found. Then, if a new S4 instance appears, or disappears, Apam creates a new set containing the current set of S4 instances, and changes the variable s4s toward that new set. Therefore, line (4) is risky if the set changes during the loop. It is advised, if these dependencies can be dynamic, to write our example as follows:

(3)   if (s2-1!= null) s2-1.callS2_1("from S1Impl");
(4)   Set<S4>tempLoop = new HashSet<S4>(s4s);
(5)   for (S4 temp : s4s) {  }

Note that, even with these precautions, still some abnormal behavior may appear, if an instrumented variable is changed by Apam while a thread is using that variable. If a new S4 instance appears, all the multiple dependencies toward S4 are updated.

Missing dependency

A resolution is said to be missing if, when the dependency is used for the first time, Apam could not find nor deploy any service provider. In this case, by default, Apam returns null for a single dependency, and the empty set for multiple dependencies. A dependency may also be missing, if after being resolved toward service "s", "s" disappears (deleted by a third party, or a device that is no longer accessible). When an instance disappears, all the wires (the dependencies) leading to that instance are removed. Then the system behaves exactly as if that dependency had never been resolved before. Therefore, a new tentative resolution will be executed the next time the dependency will be called, if successful, the client will transparently use another provider; if it fails, by default null (or emty set) is returned, and the client has to test the pointer value in order to avoid a “null pointer exception”. The programmer has to test the variable and know what to do in this case; this is why we say the default Apam behaviour is "Optional". However it can be made explicit writing:

	<specification name=”S2” missing="optional" >

Property wait

When a resolution fails, the dependency is said to be “frozen” and the current thread and all subsequent thread using this dependency will be halted and queued. When an instance that satisfies the dependency appears, the dependency is resolved and the blocked threads are resumed. Note that the dependency only is frozen, any thread entering the component object without using the frozen dependency will perform as usual. An implementation with a wait (and delete) dependency is visible for a client only if all its wait dependencies can be resolved for that client.

	<specification name=”S2” missing="wait" />

If all the temp dependencies are removed (no thermometer is available), the system will halt all threads making use of the Thermometer dependency, and will resume them after at least one Thermometer dependency has been established (at least one thermometer is available). The energyControler cannot be instantiated if no thermometers are available.

Property delete

When a dependency disappears (the provider disappeared, and no alternative provider could be found; for example, the last thermometer disappeared), the client instance must be deleted. All the wires toward the deleted instance are removed . Deleting an instance x is interpreted by the system as “instance x disappeared”. Note that Apam turns to “null” or the empty set all the variables related to the deleted dependencies (variable heater in our example), but if the program contains copies of these variables, the Java VM will not remove the corresponding Java objects, and errors may occur if using these variables.
If a thread was inside instance x at the time it is deleted, the thread continues its execution, until it leaves x normally, or it makes an exception. No other thread can enter a deleted object since wires have been removed. An implementation with a delete dependency D is invisible from a client C if dependency D cannot be satisfied for C .

	<specification name=”S2” missing="delete" />

The energyControl instance has to be deleted if the heaterControl dependency disappears. As long as heaterControl cannot be resolved, no energyControl instance can be created.

Property mandatory

By default, a resolution is lazy, and may fail, leaving the variable with a “null” value or an empty Collection which requires the programmer to test the variable before any use. Many components can perform their job only if some dependencies are available; it makes no sense starting a component without these dependencies. We call such a dependency mandatory; a component with a mandatory dependency is called a coupled component, and the provider of a mandatory dependency is called a coupled provider.
The life cycle of a coupled component is completely linked (coupled) with the life cycle of its coupled providers. A coupled component is created with its coupled providers, and deleted if at least one of its mandatory dependencies cannot be satisfied (at least one of its coupled providers disappeared and could not be replaced). Note that a chain of coupled components is created atomically, but chains are not statically defined, since each mandatory dependency is resolved independently; the selected component is not statically known (in general); it depends on the current context and can be itself a coupled component or not. A mandatory dependency is a delete dependency. Therefore, if a coupled provider disappears, Apam tries to satisfy the mandatory dependency with another service, but if no alternative solution is found, the coupled client is deleted. The coupled provider is not visible as long as at least one of its delete dependencies cannot be satisfied. This algorithm ensures that the resolution process will not loop, and will be optimized. If the deleted client is itself a coupled provider, the same process is repeated one “client backward”, until an alternative resolution is found or there are no more mandatory dependencies. Therefore coupled chains are deleted only after having tried all the alternative solutions at each level of the chain.
The resolution mechanism is such that the coupled component instances that cannot be created (some coupled providers are missing) are simply ignored . For example, a component with a mandatory dependency toward a device will be ignored as long as this device is missing, and automatically becomes selectable as soon as the coupled device appears.

	<specification name=”S2” missing="mandatory” />

It means that the component which declared that dependency can be started only if its dependency toward S2 can be immediately satisfied, i.e. if at least an S2 instance is available, and will be deleted as soon as all S2 instances disappear.