This page presents how to use the iPOJO runtime and its associated service component model. The concepts of the service component model are introduced, followed by a simple example that demonstrates the features of iPOJO.
Introduction
iPOJO aims to simplify service-oriented programming on OSGi frameworks; the name iPOJO is an abbreviation for injected POJO. iPOJO provides a new way to develop OSGi service components with the main goal being to simplify service component implementation by transparently managing the dynamics of the environment as well as other non-functional requirements. The iPOJO framework allows developers to more clearly separate functional code (i.e., the POJO) from the non-functional code (i.e., dependecy management, service provision, configuration, etc.). iPOJO combines the functional and non-functional aspects at run time. To achieve this, iPOJO provides a simple and extensible service component model based on POJOs.
The POJO concept
POJO is an acronym for Plain Old Java Object, but it embodies a concept that the simpler and less intrusive the design of a given framework, the better. The name is used to emphasize that a given object is not somehow special, but is an ordinary Java Object. Martin Fowler, Rebecca Parsons and Josh MacKenzie coined the term POJO in September 2000: "We wondered why people were so against using regular objects in their systems and concluded that it was because simple objects lacked a fancy name. So we gave them one, and it's caught on very nicely." From the developer's perspective, the iPOJO framework strives to only require POJOs in as much as it is possible.
iPOJO service component overview
A service component is able to provide and/or require services, where a service is an object that implements a given service interface embodied as a Java interface. Creating components the provide and/or require services comprises the core features of the iPOJO service component model. In addition, iPOJO introduces a callback concept to notify a component about various state changes.
The component is the central concept in iPOJO. In the core IPOJO model, a component describes service dependencies, provided services, and callbacks; this information is recorded in the component's metadata. Then, the second important concept in iPOJO is component instances. A component instances is a special version of the component. By merging component metadata and instance configuration, the iPOJO runtime is able to manage the component, i.e., manage its life cycle, inject required services, publish provided services, discover needed services.
A simple example
The following is a simple example illustrating how to use the core iPOJO concepts. The example is comprised of two components, one providing a Hello service and one requiring any number of Hello services. The components are packaged into two different bundles using Maven. The Hello service bundle contains the service interface and a component implementing the service. The Hello service consumer bundle contains the consumer component only.
You can download the provider
and the consumer
projects.
Preparing Maven
The first step is to download and installMaven; the example was created using Maven 2.0.4. Once Maven is installed, then download and compile Felix trunk (help here
). iPOJO is a part of the Felix project, by compiling Felix, you compile iPOJO too.
Be aware that Maven outputs a lot of information while it executes and often downloads a lot of required JAR files into its local repository.
Hello Service Provider
The first thing to do is to create a skeleton Maven project for the Hello service provider. To do this, type the following command:
mvn archetype:create -DarchetypeGroupId=org.apache.felix \
-DarchetypeArtifactId=maven-ipojo-plugin -DarchetypeVersion=0.7.5-SNAPSHOT \
-DgroupId=ipojo.example -DartifactId=hello.impl \
-DpackageName=ipojo.example.hello.impl
This will create a project directory called "hello.impl" in the current directory. The project directory will contain a "pom.xml" file and a "src" directory. The "pom.xml" is Maven's Project Object Model (POM), used by Maven to describe the project.
Create the file "src/main/java/ipojo/example/hello/Hello.java" for the following Hello service interface:
package ipojo.example.hello; public interface Hello { /** * Return a message like: "Hello $user_name" * @param name: the name of the user * @return the hello message **/ public String sayHello(String name); }
The component implementation of the service is a simple Java class implementing the Helloservice interface. Create the file "src/main/java/ipojo/example/hello/impl/HelloImpl.java" for the following service implementation:
package ipojo.example.hello.impl; import ipojo.example.hello.Hello; /** * Component implementing the Hello service. **/ public class HelloImpl implements Hello { public String sayHello(String name) { return "hello " + name; } }
To manage the component, iPOJO needs some metadata to understand that the component provides the Helloservice. Edit the skeleton iPOJO metadata file in "src/main/resources/metadata.xml" to match the following metadata (Note: iPOJO also supports a JAR manifest-based syntax):
<?xml version="1.0" encoding="UTF-8"?>
<iPOJO>
<component className="ipojo.example.hello.impl.HelloImpl">
<provides/>
</component>
<instance component="ipojo.example.hello.impl.HelloImpl" name="HelloService"/>
</iPOJO>
The text highlighted in red above indicates the information that needs to be edited. In the above XML-based metadata, the component element has a mandatory className attribute. This attribute tells iPOJO the implementation class of the component. Since the component in this example provides a service, the component element also specifies a child provides element. The provides element informs iPOJO that it must manage the publishing of a service. When the provides element does not contain an interface attribute, as is the case in this example, iPOJO will expose all implemented interfaces of the component as a service; it is also possible to specify the precise service interface. The instance element ask iPOJO to create an instance of your component when the component is started.
Finally, edit the skeleton "pom.xml" file in the project directory to reflect the following information about the Hello service provider project:
<project>
<modelVersion>4.0.0</modelVersion>
<packaging>bundle</packaging>
<groupId>ipojo.example</groupId>
<artifactId>hello.impl</artifactId>
<version>0.0.1</version>
<name>Hello Service</name>
<pluginRepositories>
<pluginRepository>
<id>apache.snapshots</id>
<name>snapshot plugins</name>
<url>
http://people.apache.org/repo/m2-snapshot-repository![]()
</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>
<Private-Package>ipojo.example.hello.impl</Private-Package>
<Export-Package>ipojo.example.hello</Export-Package>
</instructions>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-ipojo-plugin</artifactId>
<version>0.7.5-SNAPSHOT</version>
<executions>
<execution>
<goals>
<goal>ipojo-bundle</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
The text highlighted in red above indicates the information that needs to be edited in the POM file. The first part of the POM file indicates that the packaging format is an iPOJO bundle and also includes some information about the project (name, groupId, and artifactId). This information is not used by iPOJO, but is used by Maven. The rest of the POM file contains the bundle configuration. In the osgiManifest element, you need to enter the bundle name, the bundle description, and the exported packages. The service provider bundle exports the package of Hello interface.
After the skeleton "pom.xml" file is modified, the project is ready to be built issuing the following Maven command inside the project directory:
mvn clean install
Maven should report that the build was a success; if an error was reported then verify the previous steps. Upon success the Helloservice component JAR file is installed into the local Maven repository. A copy of the bundle JAR file will also be present in the "target" directory inside the project directory.
Hello Service Client
The first thing to do is to create a skeleton Maven project for the Hello service client. To do this, type the following command:
mvn archetype:create -DarchetypeGroupId=org.apache.felix \ -DarchetypeArtifactId=maven-ipojo-plugin -DarchetypeVersion=0.7.5-SNAPSHOT \ -DgroupId=ipojo.example -DartifactId=hello.client \ -DpackageName=ipojo.example.hello.client
This will create a project directory called "hello.client" in the current directory. The project directory will contain a "pom.xml" file and a "src" directory.
Create the file "src/main/java/ipojo/example/hello/client/HelloClient.java" for the following Hello service client:
package ipojo.example.hello.client; import ipojo.example.hello.Hello; public class HelloClient implements Runnable { private Hello[] m_hello; // Service Dependency private final static int DELAY=10000; private boolean end; public void run() { while (!end) { try { invokeHelloServices(); Thread.sleep(DELAY); } catch (InterruptedException ie) { } /* will recheck end */ } } public void invokeHelloServices() { for (int i = 0; i < m_hello.length; i++) { System.out.println(m_hello[i].sayHello("Clement")); } } public void starting() { Thread T = new Thread(this); end = false; T.start(); } public void stopping() { end = true; } }
The Hello service client creates a thread that periodically invokes the available Hello services. The thread starts when at least one Hello service provider is present using iPOJO's call back mechanism. In the client code, to use the hello the component implementation simply declares a field of the type of the service and then simply uses it directly in its code. In this example, it is the m_hello field is declared as the service field; notice that the field is an array of Hello. In iPOJO an array of services represents an aggregate or multiple cardinality dependency, whereas if a scalar value represents a singular or unary cardinality dependency. In other words, for a singular dependency simply remove the array brackets from the example (e.g., HelloService m_hello[]. After declaring a field for the service, the rest of the component code can simply assume that the service field will be initialized, e.g., m_hello[i].sayHello("Clement").
Notive that iPOJO manages service synchronisation too. So, the service invocations does not require synchronization blocks.
The component provides two callback methods for its activation and deactivation, starting() and stopping(), respectively. Callbacks are used when the component needs to be informed about a component state change. In iPOJO, the component state is either INVALID (i.e., not all of the component's constraints are satisfied) or VALID (i.e., all of the component's constraints are satisfied). In this example, the starting() callback method creates and starts a thread; the stopping()callback method stops the thread. The component metadata will instruct iPOJO to invoke these methods when the component's state changes to VALID or INVALID respectively.
Edit the skeleton iPOJO metadata file in "src/main/resource/metadata.xml" to match the following metadata:
<?xml version="1.0" encoding="UTF-8"?>
<iPOJO>
<component className="ipojo.example.hello.client.HelloClient">
<requires field="m_hello"/>
<callback transition="validate" method="starting"/>
<callback transition="invalidate" method="stopping"/>
</component>
<instance component="ipojo.example.hello.client.HelloClient" name="HelloClient"/>
</iPOJO>
The text highlighted in red above indicates the information that needs to be edited. The component element again has the className attribute that refers to the component implementation class. The dependency element describes the Hello service dependency by simply specifying its associated component field. The callback elements describe which method to invoke when the component's state changes from the specified initial state to the specified final state, one for each call back method. Then the instance element ask iPOJO to create an instance of the component.
Finally, edit the skeleton "pom.xml" file in the project directory to reflect the following information about the Hello service client project:
<project>
<modelVersion>4.0.0</modelVersion>
<packaging>bundle</packaging>
<groupId>ipojo.example</groupId>
<artifactId>hello.client</artifactId>
<version>0.0.1</version>
<name>Hello Client</name>
<dependencies>
<dependency>
<groupId>ipojo.example</groupId>
<artifactId>hello.impl</artifactId>
<version>0.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
<pluginRepositories>
<pluginRepository>
<id>apache.snapshots</id>
<name>snapshot plugins</name>
<url>
http://people.apache.org/repo/m2-snapshot-repository![]()
</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>
<Private-Package>ipojo.example.hello.client</Private-Package>
<Import-Package>*</Import-Package>
</instructions>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-ipojo-plugin</artifactId>
<version>0.7.5-SNAPSHOT</version>
<executions>
<execution>
<goals>
<goal>ipojo-bundle</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
The text highlighted in red above indicates the information that needs to be edited. The dependencies element tells Maven that the client bundle has a compilation dependency on the service provider bundle. In this case, the client bundle needs the Hello service interface to compile. After building the service provider bundle JAR file, Maven installs it into a local repository on your machine. To resolve compilation dependencies, Maven looks in the local repository to find required JAR files.
After the skeleton "pom.xml" file is modified, the project is ready to be built issuing the following Maven command inside the project directory:
mvn clean install
Maven should report that the build was a success; if an error was reported then verify the previous steps. Upon success the Helloservice component JAR file is installed into the local Maven repository. A copy of the bundle JAR file will also be present in the "target" directory inside the project directory.
Running the example
To run the example, start Felix:
java -jar bin/felix.jar
At the Felix command shell prompt, install the iPOJO runtime bundle. You can download the iPOJO runtime bundle on (take the last version) : http://people.apache.org/maven-snapshot-repository/org/apache/felix/org.apache.felix.ipojo/0.7.5-incubator-SNAPSHOT/
.
Note : If you have compiled the Felix trunk, you can install iPOJO from your local maven repository too : $mvn_repo/org/apache/felix/org.apache.felix.ipojo/0.7.5-incubator-SNAPSHOT/org.apache.felix.ipojo-0.7.5-incubator-SNAPSHOT.jar
start file:/<ipojo-directory>/org.apache.felix.ipojo-0.7.5-incubator-SNAPSHOT.jar
Install both the Hello service provider and client bundles that were created above:
start file:/<hello-service-project-directory>/target/hello.impl-0.0.1.jar start file:/<hello-client-project-directory>/target/hello.client-0.0.1.jar
Start the client bundle. Start the Hello service provider bundle and the client component will automatically be activated. Stop the provider and the client will automatically be deactivated since its dependency is no longer valid. If multiple Hello services are deployed, the client will connect to all of them.
Note: if you compile iPOJO and projects, with Maven you can directly use the following script :
cd file:/<your-local-maven-repository> start org\apache\felix\org.apache.felix.ipojo\0.7.5-SNAPSHOT\org.apache.felix.ipojo-0.7.5-SNAPSHOT.jar start ipojo\example\hello.impl\0.0.1\hello.impl-0.0.1.jar start ipojo\example\hello.client\0.0.1\hello.client-0.0.1.jar
Then to check the bind and unbind. Try to stop the hello implementation bundle ('stop 5'). The hello client stops as no more hello services are available. Then, restart it ('start 5'), the hello client restarts.
Conclusion
We saw how to use easily iPOJO to build service-oriented component. If you have questions or remarks, feel free to send me an email at : clement.escoffier@imag.fr
.

