This page describes how works the composition mechanisms provided by iPOJO and how to use it. this feature allows you to design applications that will natively support the dynamism and the evolution.
- Why providing a composition layer?
- The different types of composition in the OSGi world
- The iPOJO composition theory for the dummies
- Let's start with a first composition
- Importing a service inside a composite
- Abstracting implementation... Composing services
- Publishing services
- Hierarchical composites
iPOJO aims to simplify the creation of OSGi (dynamic) applications. Creating such as applications is basically a two steps process:
- Developing services and components
- Assembling them together to create the application
To tackle the first step, iPOJO provides an easy-to-use development model. But, this does not help to design the application. To conduct the second step, iPOJO provides a composition language allowing to:
- Assemble components and services together
- Assert service isolation inside a composition
- Hide/Abstract implementations
Service or component composition has a stringent requirement. How to isolate components and services of an application to avoid side effects as an unexpected access to a used service ...iPOJO composites just provide an isolation mechanism. Services provided inside a composite ARE NOT published globally (inside the OSGi service registry). So, only the component instances living in the same composite can access to those services.
However, it is also possible to import a service from the parent/global service registry, as well as to export a service to the parent (i.e. superior) composite.
Basically there is two types of composition in OSGi:
- The intra-bundle composition
- The service-based composition
The intra-bundle composition is the policy followed by Spring-DM. Each bundle contains components. These components can require and provide services from the global (OSGi) registry. Moreover, these components can collaborate together inside the bundle without providing/requiring services. So the isolation scope of intra-bundle composition is the bundle. This type of composition allows providing coarse-grain services provided by the collaboration of several components. However, it is not possible to update one of those components, as the update unity is the bundle.
There are two types of service composition. The behavioral service composition is like BPEL for the web services. An orchestrator supervises the execution and the collaboration of services like:
- Call service A
- Call service B
The second type of service composition is the structural service composition. This aims to provide a kind of Architecture Description Language for service-based applications. iPOJO follows this trend. This way as several advantages:
- Avoids coupling a composition to specific implementation, and so it supports the dynamic substitution
- Allows the evolution of used services and components at runtime
This is also the way followed by SCA. However, SCA does not specify how the dynamism should be handled.
The distinction between component types and instances
When you declare a <component> (or a <composite>) in your metadata, you're declaring a component type. Then you declare instances from these component types. This mechanism is critic for the iPOJO composition. Indeed, inside a composite, you're able to create instances from any (public) component types (Refer to the Factory web page for further details).
On the top of this mechanism, we can define the concept of service implementation. A service implementation is just a component type where the instances provide a service. For example, if the instance from a component type provides the service S, the component type is a service implementation of S, and so can be used to create a provider of S.
The service context concept
The service context is the concept allowing service isolation. OSGi bundle context allows accessing both to bundle-based functionality (like loadClass) and to the service registry. However, OSGi defines only one service registry, accessible by any bundles. iPOJO splits these two interaction types. Instances receive an iPOJO Bundle Context delegating bundle-related methods to the "regular" bundle context, and service-related methods to a service context. This service context can access to the OSGi service registry or to a new one. Each composition instances have a service registry. Instances creates inside the composition used this service registry through received the iPOJO Bundle Context.
Download the archive. This archive contains:
- The different applications and component types implemented in this tutorial
- A preconfigured version of Felix
- Deployment scripts
Launch ant from the base directory of the unzipped archive to create every bundles. Then, you can test the different examples by copying and pasting commands from the script.txt file (in the felix folder) in the Felix shell.
In the iPOJO in 10 minutes tutorial, we created an application checking a sentence against dictionaries. We'll reuse this simple application in this tutorial.
So first, we need to implements several component types:
- The dictionary service implementation, containing words
- The Check service implementation, providing methods to check words against dictionaries
- And the client GUI, using a Check Service to indicate misspelled words.
We will reuse the same implementation than in the previous tutorial. The current application is designed as follow:
However, imagine that you want to isolate services provided and required by your application. For example, you want to isolate a stateful service or a critical (private) resource.
So, let's imagine a second version of our spellchecker application. In this application, the dictionary service, and the checker service are isolated inside a composite. So, those services will be accessible only from instances living in the composite. The GUI will also be instantiated inside the composite. The composition will be something like:
The descriptor of this application is:
First, a composite type is declared inside an iPOJO descriptor. A composite contain always a name attribute, which is the component type name. Inside the <composite></composite>, three instances are declared: the three instances used by our application. Remark that these instances are declared as 'regular' instances. The component attribute indicates the component type to use. Instances can be configured as regular iPOJO instances. Finally, an instance of our type is also declared.
To execute our composition, go in the felix directory and launch the following command:
This version of Felix starts with the iPOJO framework, the iPOJO Arch command and the composite support. So, we just need to install our component types and the composition.
In the Felix prompt, launch the following commands:
Those commands deploy the component types. Remark that no 'functional' (i.e. neither Check service, nor Dictionary service) services are provided. Deployed bundles provide only iPOJO Factory services:
Now, when can deploy our composition:
Once deployed and started, the fancy GUI appears:
Now, you can check that the functional services are not unavailable outside the composite:
Of course, if you stop a bundle providing a required service type, the application is stopped:
Then, the application also supports component type update. However the component type name must not change. We will see later how we can avoid this issue by abstracting implementations.
Let's imagine a second version of the checker service implementation (spell.checker-v2). This implementation removes the trace when wrong words are detected. Indeed, this implementation uses a log service to store such kind of errors.
If we use this implementation, we need to make a log service available inside the composite. Else, the checker will not be valid. To achieve this, use the following composite:
This composite just adds a subservice nested element. This subservice allows importing a service inside the composite. The action attribute specifies that we want to import the service from the parent scope (i.e. superior). The specification attribute indicates the required service.
Now, relaunch Felix and enter another profile name (composition2 for example). Once started, executes the following commands:
Those commands deploy required component type (note that we deploy spell.checker-v2) and an implementation of the OSGi Log Service. When you execute the last command, the fancy interface re-appears.
Try to enter a wrong word (as composite), and click on the check button. The trace does no more appear... the message is logged inside the log service.
Of course, such composite support dynamism. Try the following scenario
When the log service is stopped, the GUI disappears. In fact, the service can no more be imported, and so, the composition becomes invalid. When you stop a bundle containing a used component type, the same behavior occurs.
Like in the previous example, you can check that only the log service is globally available. Other services are isolated inside the composite.
In this case the parent scope is the OSGi service registry, but composite can also contain other composite. In such context, the import tracks services from the superior composite. An example of hierarchical composition is described later in this tutorial.
We saw in the first composition that we depend on specific component types. This can be avoided by specifying the composition in term of services instead of component types. So, every available service implementation can be used. Moreover, if the used one disappears, another one can be immediately used to replace the missing service. Let's illustrate this.
In the first composition, we create an instance of the English dictionary service implementation. We can remove this coupling to this specific implementation. To do this, we will target any implementation of the dictionary service regardless the language.
The previous composition instantiates a dictionary service. This means that the composite looks for an implementation of the Dictionary service and creates an instance of this implementation (i.e. component type) inside the composition.
If several implementations are available, the composite chooses one, and switches to another one if the used implementation becomes unavailable.
To execute this composition, launch Felix and execute the following command:
These commands deploy component types and the composition. Only one implementation of the dictionary service is available (English). You can check this by executing the service 8 command.
Note the component.providedServiceSpecifications property indicating provided services.
Now deploy another implementation of the dictionary service, such as the French dictionary service ☺
Write welcome in the GUI and then check. The word is correctly spelled. Then, stop the bundle providing the English dictionary.
Write welcome in the GUI, and check. The word is misspelled! Try to write bienvenue and check. The word is correctly spelled. This means that the composite has substitutes the previous English dictionary by the French one. This one will be use until it disappears. If you stop the bundle containing this implementation, the composite becomes invalid.
A composition can also provide services. iPOJO composites support two methods to provide services :
- The service export: re-export a service from the composite to the parent context
- The service implementation: the composite computes a delegation scheme to delegate every method of the provided service on internal entities (services and instances)
This section addresses the export. Exporting a service is the opposite of the service import. It tracks services from the composites to publish it in the parent (superior) context.
So, let's imagine a fourth version of our application. In this application, the GUI is externalized and lives in the global context (i.e. OSGi). So, the composition exports the spell checker service.
In the previous composition, the composite exports the spell checker service. Moreover, the GUI is also created but in the global context. At runtime, the result will be as following:
The composite published the spell checker service in the OSGi service registry. The GUI tracks this service in the OSGi service registry too.
To execute this composition, launch Felix and execute following the commands:
You can check that the composition exports the service with the following command:
So, now you can play with dynamism. Stop the bundle containing the Check service implementation. The GUI disappears. Restart it. The GUI reappears. Now, stop the bundle containing the GUI implementation. The checker service stills available. Indeed, the GUI is no more inside the composition, and so stills valid despite the unavailability of the GUI:
A composition can also contain others compositions. Let's imagine a variation of the latest application. In this case, we define a composite containing the GUI and the previous composite.
The composition5 contains an instance of the composition4 and of the GUI. So the spell checker service exported by the composition4 is published inside the service context of the composite 5 (the parent context). The GUI instance lives in this service context, and so can access to the exported Spell checker service.
To execute this composition, restart Felix and launch the following commands:
You can check that the composite does no more publish the spell checker service in the OSGi service registry.
This page has presented how to use iPOJO composition model. Several topics were not addressed and will be added shortly:
- Dynamic service implementation
- The dependency model
- Composable services and composition consistency