Introduction
OSGI implements a dynamic component model for the Java platform. OSGI components or bundles defines its own context for packages and classes. This article describes the OSGI basics. Furthermore my intention is to clarify why OSGI is compared with SOA concepts and why the dynamic mechanism brings more flexibility in software architectures.
OSGI – Basic Concepts
Classloading – Difference between imported, exported and private packages
Before you starts developing OSGI you have to undestand the Java classloading concept. OSGI uses excessively the Java classloader mechanism. Mostly Java classloading is hierarchicaly used (see Java Classloader Delegation Model). OSGI realizes a network based classloader structure. Every OSGI bundle has it's own classloader. At this OSGI bundles seperate between imported, exported and private bundle packages. Classes of exported packages could be accessed from other running Bundles. Classes, which are declared in a bundle, but are required (e.g. referenced by import package.class), have to be imported.
Service registration
BundleActivator class
Before you develop a new Bundle you have to implement the org.osgi.framework.BundleActivator interface. At runtime the BundleActivator enables access to the BundleContext object out of your Bundle. The BundleContext contains all registered service references. For this you have to implement the lifecycle methods start(BundleContext ctx) and stop(BundleContext ctx).
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
public final class Activator
implements BundleActivator {
public void start(BundleContext context)
throws Exception {
//access to the bundle context on starting bundle
}
public void stop(BundleContext context)
throws Exception {
//clean up
}
}
MANIFEST.MF - File
This file includes all possible OSGI instructions and will be interpreted by the OSGI runtime. For example Import-Package, Export-Package, declaration of Bundle-Activator and so on.
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: FirstService Bundle-SymbolicName: org.developers.blog.example.Service Bundle-Version: 1.0.0 Bundle-Vendor: Rafael Sobek Export-Package: org.developers.blog.sample.service Import-Package: org.osgi.framework;version="1.3.0"
How to provide and consume services
OSGI bundles act as service providers and service consumers. The exchange of OSGI services is achieved by accessing BundleContext at your BundleActivator.
If you want to consume a service outside of your bundle, you have to get the ServiceReference by calling the BundleContext.getServiceReference(String serviceName) method. After that you can get the service object by calling BundleContext.getService(ServiceReference serviceReference) method.
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import org.osgi.framework.ServiceReference;
public final class Activator
implements BundleActivator {
private ServiceRegistration myServiceRegistration;
public void start(BundleContext context)
throws Exception {
ServiceReference referenceOfExternalServiceObject =
context.getServiceReference(ExternalService.class.getName());
//if ExternalService object already registered in OSGI runtime
if (referenceOfExternalServiceObject != null) {
ExternalService externalService =
(ExternalService) context.getService(referenceOfExternalServiceObject);
MyService myServiceObject = new MyService();
//adds the bundle service object to the bundle context
this.myServiceRegistration = context.registerService(MyService.class.getName(), myServiceObject, null);
}
}
public void stop(BundleContext context)
throws Exception {
//remove bundles service object from bundle context
this.myServiceRegistration.unregister();
}
}
OSGI example
This example doesn't use any dependency resolution mechanism how Spring DM,
Declarative Services or IPojo. It contains two bundles. The persistence-service-bundle registers a PersistenceService with an embedded Derby database to the BundleContext. If the persistence-service-bundle is started in OSGI container, the BundleActivator will instance Derby DB. The customer-web-overview-bundle uses the PersistenceService to get the list of customers in database and generates a HTML table. Additionaly it's implementing a Servlet. The BundleActivator of this bundle uses the Whiteboard pattern to add this Servlet event oriented to the HTTPService.
Used concepts:
- Service providing by Whiteboard Pattern
- Service consuming by standard OSGI Activator
- Pax Runner Provisioning System
- Felix OSGI Container
Download and Deployment
You can download full example here. To run this Maven project in OSGI container you have to execute mvn clean install pax:provision. After all bundles was started you can show the result at your browser. Check out the following URL http://localhost:8080/customerlist.
Persistence-Service-Bundle
DerbyManager - Manages Connection to Database
package org.developers.blog.osgi.basic.persistence.internal;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
/**
*
* @author Rafael Sobek
*/
public class DerbyManager {
private final static String DERBY_DRIVER =
"org.apache.derby.jdbc.EmbeddedDriver";
private final static String DATABASE_NAME = "persistenceDB";
private Connection connection = null;
public DerbyManager() throws Exception {
init();
}
public void init() throws Exception {
Class.forName(DERBY_DRIVER).newInstance();
String property = "java.io.tmpdir";
String tempDir = System.getProperty(property);
String jdbcURL = "jdbc:derby:" + tempDir + "/" + DATABASE_NAME + ";create=true";
System.out.println(jdbcURL);
connection = DriverManager.getConnection(jdbcURL);
generateDummyCustomers();
}
public Connection getConnection() {
return this.connection;
}
public void destroy() throws Exception {
connection.close();
DriverManager.getConnection("jdbc:derby:;shutdown=true");
}
public void generateDummyCustomers() throws Exception {
Statement stmt = null;
try {
stmt = connection.createStatement();
stmt.executeUpdate("DROP TABLE customer");
stmt.close();
} catch (Exception ex) {}
String createTable = "CREATE TABLE customer (" +
"id INT NOT NULL GENERATED ALWAYS AS IDENTITY," +
"firstName VARCHAR(128)," +
"secondName VARCHAR(128)," +
"address VARCHAR(128)," +
"city VARCHAR(128)," +
"country VARCHAR(128)," +
"email VARCHAR(128))";
stmt = connection.createStatement();
stmt.executeUpdate(createTable);
stmt.close();
String insert = "INSERT INTO customer " +
"(firstName, secondName, address, city, country, email) VALUES " +
"('Rafael','Sobek','Kaiseralle','Karlsruhe','Germany','info@developers-blog.org')";
stmt = connection.createStatement();
stmt.executeUpdate(insert);
stmt.close();
}
}
Persistence Service - Reads Database and Generates Customer Object List
package org.developers.blog.osgi.basic.persistence;
import org.developers.blog.osgi.basic.persistence.vo.Customer;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import org.developers.blog.osgi.basic.persistence.internal.DerbyManager;
public class PersistenceService
{
private DerbyManager derbyManager;
public PersistenceService() throws Exception {
this.derbyManager = new DerbyManager();
}
public List getCustomers() throws PersistenceException {
Statement stmt = null;
ResultSet rs = null;
List customers = new ArrayList();
try {
Connection connection = derbyManager.getConnection();
stmt = connection.createStatement();
rs = stmt.executeQuery("SELECT * FROM CUSTOMER");
while (rs.next()) {
customers.add(
new Customer(
rs.getString("firstName"),
rs.getString("secondName"),
rs.getString("address"),
rs.getString("city"),
rs.getString("country"),
rs.getString("email")));
}
} catch (Exception ex) {
throw new PersistenceException("DB query failed", ex);
} finally {
try {
rs.close();
} catch (SQLException ex) {
throw new PersistenceException("DB closing failed", ex);
}
try {
stmt.close();
} catch (SQLException ex) {
throw new PersistenceException("DB closing failed", ex);
}
}
return customers;
}
}
Activator - Adds PersistenceService object to BundleContext
package org.developers.blog.osgi.basic.persistence;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
public class Activator implements BundleActivator {
private ServiceRegistration serviceRegistration;
public void start(BundleContext bundleContext) throws Exception {
PersistenceService persistenceService =
new PersistenceService();
serviceRegistration =
bundleContext.registerService(
PersistenceService.class.getName(), persistenceService, null
);
}
public void stop(BundleContext bundleContext) throws Exception {
serviceRegistration.unregister();
}
}
Customer-Web-Overview-Bundle
CustomerTableServlet - List all customers in HTML table
It uses for listing customer the PersistenceService object of the Persistence-Serivce-Bundle. Additionaly this Servlet contains helper methods to build up a HTML table.
package org.developers.blog.osgi.basic.web.whiteboard;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import java.io.IOException;
import java.io.Writer;
import org.developers.blog.osgi.basic.persistence.PersistenceException;
import org.developers.blog.osgi.basic.persistence.PersistenceService;
import org.developers.blog.osgi.basic.persistence.vo.Customer;
public class CustomerTableServlet
extends HttpServlet {
private PersistenceService persistenceService;
public void setPersistenceService(PersistenceService persistenceService) {
this.persistenceService = persistenceService;
}
@Override
public void init(ServletConfig config)
throws ServletException {
doLog("Init config [" + config + "]");
super.init(config);
}
@Override
public void destroy() {
doLog("Destroyed servlet");
super.destroy();
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html");
Writer writer = resp.getWriter();
writeTop(writer);
String header = wrapRow(
wrapCell("First Name")
+ wrapCell("Second Name")
+ wrapCell("Address")
+ wrapCell("City")
+ wrapCell("Country")
+ wrapCell("Email"));
try {
String content = null;
for (Customer customer : persistenceService.getCustomers()) {
content = wrapRow(
wrapCell(customer.getFirstName())
+ wrapCell(customer.getSecondName())
+ wrapCell(customer.getAddress())
+ wrapCell(customer.getCity())
+ wrapCell(customer.getCountry())
+ wrapCell(customer.getEmail()));
}
String table = wrapTable(header + content);
writer.write(table);
} catch (PersistenceException ex) {
ex.printStackTrace();
throw new ServletException(ex);
}
writeBottom(writer);
}
private void writeTop(Writer writer) throws IOException {
writer.write(
"<html>"
+ " <head>"
+ " <title>OSGI Basic Web Example</title>"
+ " </head>"
+ " <body>");
}
private void writeBottom(Writer writer) throws IOException {
writer.write(
" </body>"
+ "</html>");
}
private String wrapTable(String value) throws IOException {
return "<table border='1'>" + value + "</table>";
}
private String wrapCell(String value) throws IOException {
return "<td>" + value + "</td>";
}
private String wrapRow(String value) throws IOException {
return "<tr>" + value + "</tr>";
}
private void doLog(String message) {
System.out.println("[" + this.getClass().getName() + "] " + message);
}
}
Activator - Adds the CustomerTableServlet to BundleContext
package org.developers.blog.osgi.basic.web.whiteboard;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import javax.servlet.Servlet;
import java.util.Hashtable;
import org.developers.blog.osgi.basic.persistence.PersistenceService;
import org.osgi.framework.ServiceReference;
public final class Activator
implements BundleActivator {
private ServiceRegistration serviceRegistration;
public void start(BundleContext context)
throws Exception {
ServiceReference sRefPersistenceService =
context.getServiceReference(PersistenceService.class.getName());
//if PersistenceService exists in OSGI runtime
if (sRefPersistenceService != null) {
PersistenceService persistenceService =
(PersistenceService) context.getService(sRefPersistenceService);
CustomerTableServlet tableServlet = new CustomerTableServlet();
tableServlet.setPersistenceService(persistenceService);
//use whiteboard pattern to register servlets
Hashtable props = new Hashtable();
props.put("alias", "/customerlist");
this.serviceRegistration = context.registerService(Servlet.class.getName(), tableServlet, props);
}
}
public void stop(BundleContext context)
throws Exception {
this.serviceRegistration.unregister();
}
}
Regards
Rafael Sobek
Technorati Tags: OSGI Tutorial OSGI

Nice Article.
You could show how to set things up in the Servlet Container in order to have an OSGi application running.
Thank you