Tutorial 1: Developing a RESTful Web Application for OSGi Runtime Environment
Author: Hasan - clerezza.org
Date: October 9, 2008
Contributor: Reto BG, Tsuyoshi Ito - clerezza.org
Table of Contents
2. Initializing a Maven Project
3. Implementing the Functionality
4. Making a Class a JAX-RS Root Resource
5. Getting the Representation of a Resource
6. Configuring Packaging Type and Plugin for Bundling
7. Using OSGi Declarative Service
9. Deploying and Starting the Bundle in an OSGi Container
1. Objective
In this tutorial you will learn how to write a RESTful [ 1 ] Web application running in an OSGi [ 2 ] container. One key advantage of applying REST principles in developing a Web application is the simpler implementation of a service, since interactions are stateless. Statelessness also leads to a better server scalability (cf. Section 5.1.3 of [ 1 ]). The use of OSGi technology offers a modular implementation of an application through its component model. Each component (in the form of a bundle) can be dynamically deployed and replaced without the need to take the application down. A bundle is a tightly-coupled, dynamically loadable collection of classes, jars, and configuration files.
The example OSGi bundle to be developed allows for the retrieval of personal data by specifying an identification number (ID). Thus, the Web application provides for a personal data retrieval service. Depending on the ID in the URI of a Web request, the corresponding personal data are returned in the Web response. For the sake of simplicity, the personal data in this tutorial consist only of firstname and lastname. The time it takes to go through this tutorial is around 30 minutes. This tutorial is intended for Web application developers who are supposed to be familiar with the programming language Java and the build tool maven [ 3 ].
2. Initializing a Maven Project
First, a maven project with the groupId org.example.clerezza and the artifactId tutorial1 will be created by executing the following command in a shell:
$ mvn archetype:generate --batch-mode \
-DarchetypeGroupId=org.apache.maven.archetypes \
-DarchetypeArtifactId=maven-archetype-quickstart \
-DgroupId=org.example.clerezza \
-DartifactId=tutorial1 \
-Dversion=1.0-SNAPSHOT \
-Dpackage=org.example.clerezza.tutorial1
...
------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO]
------------------------------------------------------------------------
...
A new directory called tutorial1 is created containing a source directory src and a file called pom.xml used by maven to build the project. A program file called App.java is created and placed under the directory src/main/java/org/example/clerezza/tutorial1/. Since we are going to use annotations and generics, we need to configure maven-compiler-plugin in the pom.xml accordingly, i.e., to use javac version 1.5 or higher. In this tutorial we use version 1.6, thus, the modified pom.xml looks as follows:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example.clerezza</groupId>
<artifactId>tutorial1</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>tutorial1</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
3. Implementing the Functionality
To implement the required functionality, we need a Java class to represent persons and another Java class to manage them. We will modify App.java to implement the second class and write a new file called Person.java to implement the first class. As you can see, both classes are simply POJO. The class App manages an array of Person objects and provides for a method to retrieve a Person object given his ID. For the sake of simplicity, the ID of a person is represented by the respective array index.
The content of App.java is as follows:
package org.example.clerezza.tutorial1;
public class App {
Person[] persons;
public App() {
persons = new Person[3];
persons[0] = new Person("Jane", "Roe");
persons[1] = new Person("Richard", "Roe");
persons[2] = new Person("John", "Doe");
}
public Person getPerson(int id) {
return persons[id];
}
}
The class Person is defined in Person.java as follows:
package org.example.clerezza.tutorial1;
public class Person {
String firstName, lastName;
public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
}
4. Making a Class a JAX-RS Root Resource
JAX-RS [4] (a.k.a JSR 311) is the Java API for RESTful Web Services. Since we are going to use JAX-RS in this tutorial, we need to configure this dependency in the pom.xml. The modified pom.xml looks as follows:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
...
<dependencies>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId>
<version>1.0</version>
</dependency>
...
</dependencies>
...
</project>
The class App is a resource class (in JAX-RS terminology), i.e., a class that implements a Web resource. To denote a resource class, JAX-RS annotations [5] are used, as shown in the class App below. The JAX-RS annotation @Path in this class sets the path of the resource to “/contact”. The JAX-RS annotation @GET tells that the annotated method, getPerson, is responsible for processing GET requests. This method accepts a parameter, whose value is obtained from the GET request parameter called id. This is defined through the JAX-RS annotation @QueryParam.
package org.example.clerezza.tutorial1;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
@Path("/contact") // sets the path for this service
public class App {
Person[] persons;
public App() {
persons = new Person[3];
persons[0] = new Person("Jane", "Roe");
persons[1] = new Person("Richard", "Roe");
persons[2] = new Person("John", "Doe");
}
@GET // this method process GET requests
public Person getPerson(@QueryParam("id") int id) {
return persons[id];
}
}
5. Getting the Representation of a Resource
A resource can have multiple representations. For example, a Web page can be represented as html, pdf, plain text, or other representations. The HTTP defines a mechanism known as content negotiation to allow a client (e.g., a Web browser) to specify which representation it would like to get from the server. With JAX-RS we can define writers for various representations. In this tutorial we will add a writer, called PersonWriter, to produce an HTML representation of the resource Person. The content of PersonWriter.java is as follows:
package org.example.clerezza.tutorial1;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
@Provider
@Produces("text/html")
public class PersonWriter implements MessageBodyWriter<Person> {
@Override
public boolean isWriteable(Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType) {
return Person.class.isAssignableFrom(type);
}
@Override
public long getSize(Person t, Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType) {
return -1;
}
@Override
public void writeTo(Person t, Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, Object> httpHeaders,
OutputStream entityStream)
throws IOException, WebApplicationException {
PrintWriter writer = new PrintWriter(entityStream);
writer.println("<html>");
writer.println("<body>");
writer.println("Firstname: "+t.getFirstName());
writer.println("<br/>");
writer.println("Lastname: "+t.getLastName());
writer.println("</body>");
writer.println("</html>");
writer.flush();
}
}
The JAX-RS annotation @Provider tells that the annotated class implements a JAX-RS extension interface. The JAX-RS runtime is extended using application-supplied provider classes. In our case, the class PersonWriter is an entity provider, i.e., a provider that supplies a mapping service between representations and their associated Java types (classes). The PersonWriter maps a Java type to a representation. The JAX-RS annotation @Produces specifies a list of media types that a Java type or a method can produce.
To compile the sources, execute:
$ mvn compile
...
[INFO] [compiler:compile]
[INFO] Compiling 3 source files to .../tutorial1/target/classes
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
...
Congratulations! You have just written a JAX-RS application.
Fig. 1 depicts the class diagram of this simple Web application which comprises 3 classes and 1 interface class. An instance of the class Person (a Person object) represents a person identified by its first name and last name. PersonWriter provides a writeTo method to convert a Person object into a data stream containing the first name and the last name of the person in HTML. It implements the interface MessageBodyWriter defined by the JAX-RS specification.

Figure 1: Class Diagram of Web Application for Contacts Lookup
Since we are going to run the application in an OSGi container, let's dive into OSGi world now.
6. Configuring Packaging Type and Plugin for Bundling
We need to create a bundle from those classes defined above. To generate a bundle, we need to specify bundle as the packaging type and configure the build to use maven-bundle-plugin in the pom.xml as follows:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
...
<packaging>bundle</packaging>
...
<build>
<plugins>
...
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
</plugin>
...
</plugins>
</build>
</project>
7. Using OSGi Declarative Service
We use OSGi Declarative Service (OSGi DS) to wire together services across bundles. This wiring is specified declaratively in XML format, however, we will use maven-scr-plugin to automate the generation of this specification. This requires a plugin configuration in the pom.xml as follows:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
...
<build>
<plugins>
...
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-scr-plugin</artifactId>
<executions>
<execution>
<id>generate-scr-scrdescriptor</id>
<goals>
<goal>scr</goal>
</goals>
</execution>
</executions>
</plugin>
...
</plugins>
</build>
</project>
We use Triaxrs [6] developed by clerezza.org as the implementation of JAX-RS. It supports both injection of javax.ws.rs.core.Application instances as well as direct injection of Providers and root resources. For a direct injection, a component just needs to expose a service of type java.lang.Object and have the property "javax.ws.rs" set to true. Therefore, the next step is to add OSGi DS annotations to source code to be processed by the maven-scr-plugin.
The complete code of App.java looks now as follows:
package org.example.clerezza.tutorial1;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
/**
* Get Persons by id
*
* @scr.component
* @scr.service interface="java.lang.Object"
* @scr.property name="javax.ws.rs" type="Boolean" value="true"
*/
@Path("/contact") // sets the path for this service
public class App {
Person[] persons;
public App() {
persons = new Person[3];
persons[0] = new Person("Jane", "Roe");
persons[1] = new Person("Richard", "Roe");
persons[2] = new Person("John", "Doe");
}
@GET // this method process GET requests
public Person getPerson(@QueryParam("id") int id) {
return persons[id];
}
}
And the complete code of PersonWriter.java looks as follows:
package org.example.clerezza.tutorial1;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
/**
*
* @scr.component
* @scr.service interface="java.lang.Object"
* @scr.property name="javax.ws.rs" type="Boolean" value="true"
*/
@Provider
@Produces("text/html")
public class PersonWriter implements MessageBodyWriter<Person> {
@Override
public boolean isWriteable(Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType) {
return Person.class.isAssignableFrom(type);
}
@Override
public long getSize(Person t, Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType) {
return -1;
}
@Override
public void writeTo(Person t, Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, Object> httpHeaders,
OutputStream entityStream)
throws IOException, WebApplicationException {
PrintWriter writer = new PrintWriter(entityStream);
writer.println("<html>");
writer.println("<body>");
writer.println("Firstname: "+t.getFirstName());
writer.println("<br/>");
writer.println("Lastname: "+t.getLastName());
writer.println("</body>");
writer.println("</html>");
writer.flush();
}
}
Based on the @scr annotations, the maven-scr-plugin generates the configuration file serviceComponents.xml and places it in the directory target/scr-plugin-generated/OSGI-INF/ (see the next section). After executing "mvn compile" the file serviceComponents.xml is generated. The content of this file looks as follows:
<?xml version="1.0" encoding="UTF-8"?>
<components xmlns:scr="http://www.osgi.org/xmlns/scr/v1.0.0">
<scr:component enabled="true" name="org.example.clerezza.tutorial1.App">
<scr:implementation class="org.example.clerezza.tutorial1.App"/>
<scr:service servicefactory="false">
<scr:provide interface="java.lang.Object"/>
</scr:service>
<scr:property name="javax.ws.rs" type="Boolean" value="true"/>
<scr:property name="service.pid" value="org.example.clerezza.tutorial1.App"/>
</scr:component>
<scr:component enabled="true" name="org.example.clerezza.tutorial1.PersonWriter">
<scr:implementation class="org.example.clerezza.tutorial1.PersonWriter"/>
<scr:service servicefactory="false">
<scr:provide interface="java.lang.Object"/>
</scr:service>
<scr:property name="javax.ws.rs" type="Boolean" value="true"/>
<scr:property name="service.pid" value="org.example.clerezza.tutorial1.PersonWriter"/>
</scr:component>
</components>
The file serviceComponents.xml specifies that there are two OSGi components called org.example.clerezza.tutorial1.App and org.example.clerezza.tutorial1.PersonWriter which are to be enabled. A component needs to be enabled before its configuration can be activated. If you are interested in the detail description of a component life cycle, you can obtain this information from the OSGi Service Platform Service Compendium [7] Section 112.5. The serviceComponents.xml also specifies the class that is to be loaded when a component is activated. Based on this component description, the OSGi runtime can determine and invoke service instances referred to by Triaxrs.
8. Building Tutorial1 Bundle
Now we can build the tutorial1 bundle with the following command:
$ mvn clean install
...
[INFO] [clean:clean]
[INFO] [resources:resources]
...
[INFO] [compiler:compile]
[INFO] Compiling 3 source files to .../tutorial1/target/classes
[INFO] [scr:scr {execution: generate-scr-scrdescriptor}]
[INFO] Generating 4 MetaType Descriptors to .../tutorial1/target/scr-plugin-generated/OSGI-INF/metatype/metatype.xml
[INFO] Writing abstract service descriptor .../tutorial1/target/scr-plugin-generated/OSGI-INF/scr-plugin/scrinfo.xml with 2 entries.
[INFO] Generating 2 Service Component Descriptors to .../tutorial1/target/scr-plugin-generated/OSGI-INF/serviceComponents.xml
[INFO] [resources:testResources]
[INFO] Using default encoding to copy filtered resources.
[INFO] [compiler:testCompile]
[INFO] Compiling 1 source file to .../tutorial1/target/test-classes
[INFO] [surefire:test]
...
[INFO] [bundle:bundle]
[INFO] [install:install]
[INFO] Installing .../tutorial1/target/tutorial1-1.0-SNAPSHOT.jar to .../.m2/repository/org/example/clerezza/tutorial1/1.0-SNAPSHOT/tutorial1-1.0-SNAPSHOT.jar
[INFO] [bundle:install]
[INFO] Parsing file:/home/origami/.m2/repository/repository.xml
[INFO] Installing org/example/clerezza/tutorial1/1.0-SNAPSHOT/tutorial1-1.0-SNAPSHOT.jar
[INFO] Writing OBR metadata
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
...
If there is no error occurred during the build, maven will generate the package tutorial1-1.0-SNAPSHOT.jar, place it in the directory target and install it into the local repository. Besides serviceComponents.xml described above, the maven-scr-plugin also generates metatype.xml and scrinfo.xml within the OSGI-INF directory. The metatype.xml specifies the type of data that a service can use as arguments (cf. Section 105 of [7]), whereas the scrinfo.xml contains abstract service description. The maven-bundle-plugin generates the file MANIFEST.MF and places it in the directory classes/META-INF/. This file describes the generated bundle JAR as shown below. It specifies amongst others the packages that are to be exported and imported by the bundle, the location and the name of the component configuration file serviceComponents.xml. The information about the location and name of the component configuration file comes from the maven-scr-plugin.
Manifest-Version: 1.0
Export-Package: org.example.clerezza.tutorial1;uses:="javax.ws.rs,javax
.ws.rs.ext,javax.ws.rs.core"
Service-Component: OSGI-INF/serviceComponents.xml
Built-By: hasan
Tool: Bnd-0.0.255
Bundle-Name: Tutorial 1
Created-By: Apache Maven Bundle Plugin
Bundle-Version: 1.0.0.SNAPSHOT
Build-Jdk: 1.6.0_07
Bnd-LastModified: 1223461032518
Bundle-ManifestVersion: 2
Import-Package: javax.ws.rs,javax.ws.rs.core,javax.ws.rs.ext,org.examp
le.clerezza.tutorial1
Bundle-SymbolicName: org.example.clerezza.tutorial1
9. Deploying and Starting the Bundle in an OSGi Container
We will deploy tutorial1 bundle generated by maven and all its required bundles in Apache Felix, which is an OSGi container. Table 1 lists all bundles needed in this tutorial in addition to bundles that are already started by Apache Felix.
Table 1: Bundles Needed in Tutorial 1
| Bundle Name | Bundle Symbolic Name | Required By |
|---|---|---|
| Servlet Specification 2.5 API | org.mortbay.jetty.servlet-api-2.5 | Jetty Utilities |
| Jetty Utilities | org.mortbay.jetty.util | Jetty Server |
| Jetty Server | org.mortbay.jetty.server | WRHAPI Jetty |
| Apache Commons Logging Plug-in | org.apache.commons.logging | WRHAPI Jetty |
| WRHAPI | org.wymiwyg.wrhapi | WRHAPI Jetty, Clerezza - Triaxrs |
| WRHAPI Jetty | org.wymiwyg.wrhapi-jetty | - |
| jsr311-api | javax.ws.rs.jsr311-api | Clerezza - Triaxrs, Tutorial 1 |
| Sling - OSGi LogService Implementation | org.apache.sling.commons.log | Clerezza - Triaxrs |
| Clerezza - JAXRS Extensions | org.clerezza.jaxrs.extensions | Clerezza - Triaxrs |
| Clerezza - Triaxrs | org.clerezza.triaxrs | - |
| Tutorial 1 | org.example.clerezza.tutorial1 | - |
| Apache Felix Declarative Services | org.apache.felix.scr | - |
Apache Felix can be downloaded from http://felix.apache.org/site/downloads.cgi. The current version of Felix at the time of writing this tutorial is version 1.6.0. To install, you just need to untar the downloaded package felix-1.6.0.tar.gz:
$ tar xzvf felix-1.6.0.tar.gz
To run Felix, change the working directory to felix-1.6.0 and then execute the program felix.jar in the directory bin. You will be asked for a profile name which identifies a set of bundles to be installed or that was previously installed and associated with this profile name. Felix creates a directory for each profile and places it in the directory ~/.felix by default. In this tutorial we use tutorial_1 as the profile name. After you have entered the profile name, the Felix shell is started and the prompt “->” is shown.
$ cd felix-1.6.0
$ java -jar bin/felix.jar
Welcome to Felix.
=================
->
You can enter commands after the prompt of the Felix shell. Read the Apache Felix Usage Documentation [8] for a list of commands and their descriptions. Those bundles listed above can be installed and started as shown below. Note that not all bundles need to be started, e.g., API bundles such as javax.ws.rs need only be installed.
-> start http://mirrors.ibiblio.org/pub/mirrors/maven2/org/apache/felix/org.apache.felix.scr/1.0.6/org.apache.felix.scr-1.0.6.jar
-> install http://repo1.maven.org/maven2/org/mortbay/jetty/servlet-api-2.5/6.1.11/servlet-api-2.5-6.1.11.jar
-> install http://repo1.maven.org/maven2/org/mortbay/jetty/jetty-util/6.1.11/jetty-util-6.1.11.jar
-> install http://repo1.maven.org/maven2/org/mortbay/jetty/jetty/6.1.11/jetty-6.1.11.jar
-> start http://mirror.switch.ch/mirror/apache/dist/sling/org.apache.sling.commons.log-2.0.6.jar
-> start http://wymiwyg.berlios.de/maven2/org/wymiwyg/wrhapi/0.6/wrhapi-0.6.jar
-> start http://wymiwyg.berlios.de/maven2/org/wymiwyg/wrhapi-jetty/0.6/wrhapi-jetty-0.6.jar
-> install http://repo1.maven.org/maven2/javax/ws/rs/jsr311-api/1.0/jsr311-api-1.0.jar
-> start http://repo.trialox.org/release/org/clerezza/org.clerezza.jaxrs.extensions/0.1/org.clerezza.jaxrs.extensions-0.1.jar
-> start http://repo.trialox.org/release/org/clerezza/org.clerezza.triaxrs/0.6/org.clerezza.triaxrs-0.6.jar
-> start file:///home/.../tutorial1/target/tutorial1-1.0-SNAPSHOT.jar
10. Accessing the Web Service
Now, open a Web Browser window and access the service through the URL: http://localhost:8080/contact?id=2
As a response from the server, your Web Browser should show the following text in the window:
Firstname: John
Lastname: Doe
Surely you have noticed the use of the port number 8080 in the URL. This is due to the fact that the Activator bundle configures the Jetty Web server to listen on this port number.
11. References
[1] R.T. Fielding: Architectural Styles and the Design of Network-based Software Architectures; CHAPTER 5 Representational State Transfer (REST), 2000, http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm
[2] OSGi, http://www.osgi.org/Main/HomePage
[3] Maven, http://maven.apache.org/
[4] JAX-RS: The Java API for RESTful Web Services, http://jcp.org/en/jsr/detail?id=311
[5] JAX-RS Annotations, https://jsr311.dev.java.net/nonav/javadoc/javax/ws/rs/package-summary.html
[6] Triaxrs, http://trialox.org/projects/org.clerezza.triaxrs.parent/org.clerezza.triaxrs/index.html
[7] The OSGi Alliance: OSGi Service Platform Service Compendium; Release 4, Version 4.1, April 2007
[8] Apache Felix: Apache Felix Usage Documentation; http://felix.apache.org/site/apache-felix-usage-documentation.html
That's all folks for this time!
Copyright (c) 2008 trialox.org (trialox AG, Switzerland)