Support free tutorials:











vogella training Training Books



OSGi Modularity - Tutorial

Lars Vogel

Version 4.8

10.06.2013

Revision History
Revision 0.1 03.09.2007 Lars
Vogel
Created
Revision 0.2 - 4.8 25.10.2008 - 10.06.2013 Lars
Vogel
bugfixes and enhancements

OSGi with Eclipse Equinox

This tutorial gives an overview of OSGi and its modularity layer. For this tutorial Eclipse 4.2 (Juno) is used.


Table of Contents

1. OSGi Overview
1.1. What is OSGi
1.2. Bundle vs. plug-in
1.3. Implementations
1.4. Eclipse Equinox
2. OSGi bundles and dependency management
2.1. OSGi bundles
2.2. Bundle-SymbolicName and Version
2.3. Semantic Versioning with OSGi
2.4. Field identifier in MANIFEST.MF
2.5. Bundle dependencies and public API
2.6. Provisional API and friends
2.7. Dynamic imports of packages
2.8. OSGi dependency management
2.9. Bundle life cycle
3. Find plug-in for a certain class
4. OSGi console
4.1. OSGi console
4.2. Required bundles
4.3. Telnet
4.4. Access to the Eclipse OSGi console
5. Download the Eclipse standard distribution
6. Exercise: Data model and service bundle
6.1. Target of the exercise
6.2. Naming convention: simple plug-in / bundle
6.3. Create plug-in
6.4. Create base class
6.5. Generate constructors
6.6. Generate getter and setter methods
6.7. Validation
6.8. Generate toString, hashCode and equals methods
6.9. Write a copy method
6.10. Create model interface
6.11. Export the model
6.12. Create data model provider plug-in (service plug-in)
6.13. Prepare service plug-in
6.14. Create factory and export package
7. Tutorial: Using the Activator and exporting your bundle
7.1. Create a new Bundle
7.2. Coding
7.3. Run
7.4. Export your bundle
8. Running a stand-alone OSGi server
9. Support free vogella tutorials
9.1. Thank you
9.2. Questions and Discussion
10. Links and Literature
10.1. Source Code
10.2. OSGi Resources
10.3. vogella Resources

1. OSGi Overview

1.1. What is OSGi

OSGi is a specification. The core of the OSGi specification defines a component and service model for Java. The components and services can be dynamically installed, activated, de-activated, updated and de-installed.

A software component is called bundle in OSGi.

A very practical advantage of OSGi is that every bundle must define its exported Java packages and its required dependencies. This way you can effectively control the provided API and the dependencies of your plug-ins.

1.2. Bundle vs. plug-in

In Eclipse the smallest unit of modularization is a plug-in. The terms plug-in and bundle are (almost) interchangeable. An Eclipse plug-in is also an OSGi bundle and vice versa.

1.3. Implementations

OSGi has several implementations, for example Equinox, Knopflerfish OSGi or Apache Felix.

1.4. Eclipse Equinox

Eclipse Equinox is the reference implementation of the base OSGi specification.

It is also the runtime environment on which Eclipse applications are based.

Eclipse extends the OSGi concept of bundles and services with the concept of extension points.

2. OSGi bundles and dependency management

2.1. OSGi bundles

The OSGi specification defines an OSGi bundle as the unit of modularization.

A bundle is a cohesive, self-contained unit, which explicitly defines its dependencies to other modules and services. It also explicitly defines its external API.

Technically OSGi bundles are .jar files with additional meta information. This meta information is stored in the META-INF/MANIFEST.MF file.

The MANIFEST.MF file is part of the standard Java specification. OSGi adds additional metadata. Any non-OSGi runtime will ignore the OSGi metadata. Therefore OSGi bundles can be used without restrictions in non-OSGi Java environments.

2.2. Bundle-SymbolicName and Version

Each bundle has a symbolic name which is defined via the Bundle-SymbolicName property. The name starts by convention with the reverse domain name of the bundle author, e.g. if you own the "example.com" domain then the symbolic name would start with "com.example".

Each bundle has also a version number in the Bundle-Version property.

The Bundle-Version and the Bundle-SymbolicName uniquely identifies a bundle in OSGi.

Both properties are defined in the MANIFEST.MF file.

2.3. Semantic Versioning with OSGi

OSGi recommends to use a <major>.<minor>.<patch> schema for the version number.

  • <patch> is increased if all changes are backwards compatible.

  • <minor> is increased if public API has changed but all changes are backwards compatible.

  • <major> is increased if changes are not backwards compatible.

2.4. Field identifier in MANIFEST.MF

The following is an example of a MANIFEST.MF file.

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Popup Plug-in
Bundle-SymbolicName: com.example.myosgi; singleton:=true
Bundle-Version: 1.0.0
Bundle-Activator: com.example.myosgi.Activator
Require-Bundle: org.eclipse.ui,
 org.eclipse.core.runtime
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-1.6 

The following table gives an explanation of the identifiers in the MANIFEST.MF file.

Table 1. OSGi identifiers in the MANIFEST.MF file

Identifier Description
Bundle-Name Short description of the bundle.
Bundle-SymbolicName The unique identifier of the bundle. If this bundle is using the extension point functionality of Eclipse, it must be marked as Singleton. You do this by adding the ; singleton:=true parameter after the Bundle-SymbolicName identifier.
Bundle-Version Defines the bundle version and must be incremented if a new version of the bundle is published.
Bundle-Activator Defines an optional activator class which implements the BundleActivator interface. An instance of this class is created when the bundle gets activated. Its start() and stop() methods are called whenever the bundle is started or stopped. An OSGi activator is typical used to configure the bundle during startup.
Bundle-RequiredExecutionEnvironment (BREE) Specify which Java version is required to run the bundle. If this requirement is not fulfilled, then the OSGi runtime does not load the bundle.
Bundle-ActivationPolicy Setting this to lazy will tell the OSGi runtime that this plug-in should only be activated if one of its components, i.e. classes and interfaces are used by other bundles. If not set, the OSGi runtime can , accordingly to the OSGi specification, activate this bundle by default. Equinox decides based on the config.ini setting if it automatically starts the bundle.
Bundle-ClassPath The Bundle-ClassPath specifies where to load classes from the bundle. The default is '.' which allows classes to be loaded from the root of the bundle. You can also add JAR files to it, these are called nested JAR files.

2.5. Bundle dependencies and public API

Via the MANIFEST.MF file a bundle can define its dependency to other bundles or packages. OSGi will throw a ClassNotFoundException if a class from a bundle tries to access a class without a defined dependency to it. The only exception are packages from the Java runtime environment from the standard Java virtual machine; these packages are always available. The available classes from the Java virtual machine can be configured via a JRE profile, but this configuration is not covered in this book.

In the MANIFEST.MF file a bundle also defines its API via the Export-Package Identifier. All packages which are not explicitly exported are not visible to other bundles.

All these restrictions are enforced via a specific OSGi classloader. Each bundle has its own classloader. Access to restricted classes is not possible.

Warning

Unfortunately OSGi can not prevent you from using Java reflection to access these classes. This is because OSGi is based on the Java runtime which does not yet support a modularity layer.

A bundle can define that it depends on a certain version (or a range) of another bundle, e.g. bundle A can define that it depends on bundle C in version 2.0, while bundle B defines that it depends on version 1.0 of bundle C.

2.6. Provisional API and friends

Equinox supports that an exported package is declared as provisional via the x-internal flag. The following screenshot shows how to set a package as x-internal in the MANIFEST.MF editor.

Setting the x-internal flag

This results in the following MANIFEST.MF file.

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Provider
Bundle-SymbolicName: de.vogella.osgi.xinternal.provider
Bundle-Version: 1.0.0.qualifier
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Export-Package: de.vogella.osgi.xinternal.provider;x-internal:=true 

Eclipse will show the usage of this API as an error, warning or ignore this, depending on the setting in WindowPreferencesJavaCompilerErrors/Warnings.

Settings in Eclipse for warnings for deprecated API usage

You can also release a package only for a specific plug-in via the x-friends directive. This flag is added if you add a plug-in to the Package Visibility section on the Runtime tab of the MANIFEST.MF editor.

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Provider
Bundle-SymbolicName: de.vogella.osgi.xinternal.provider
Bundle-Version: 1.0.0.qualifier
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Export-Package: de.vogella.osgi.xinternal.provider;x-friends:="another.bundle" 

x-friends is similar to x-internal, but the plug-in defined as friend can use the API without getting warnings.

Warning

Avoid using x-friends if possible, it is considered a bad practice as you create special cases in your modularity layer.

2.7. Dynamic imports of packages

For legacy reasons OSGi supports also a dynamic import of packages. See OSGi wiki for dynamic imports for details.

Warning

You should not use this feature, it is a symptom of a non-modular design.

2.8. OSGi dependency management

OSGi is responsible for the dependency management between the bundles.

OSGi reads the MANIFEST.MF file of a bundle during its installation. It ensures that all dependent bundles are also resolved and activated before the bundle is activated.

If the dependencies are not met, then the bundle is not resolved.

2.9. Bundle life cycle

With the installation of a bundle in the OSGi runtime the bundle is persisted in a local bundle cache. The OSGi runtime then tries to resolve all dependencies of the bundle.

If all required dependencies are resolved, the bundle is in the RESOLVED status otherwise it is in the INSTALLED status.

If several bundles exist which would satisfy the dependency, then the bundle with the highest version is used. If the versions are the same, then the bundle with the lowest install ID will be used (the bundle gets a unique identifier assigned by the framework during the installation). If the bundle is started, its status is STARTING. Afterwards it gets the ACTIVE status.

This life cyle is depicted in the following graphic.

OSGi life cycle

3. Find plug-in for a certain class

You frequently have to find the plug-in / bundle for a given class. The Eclipse IDE makes it easy to find the plug-in for a class. After making the Include all plug-ins from target into Java Search setting in the Eclipse IDE preferences you can use the Open Type dialog (Ctrl+Shift+T) to find the plug-in for a class. The JAR file is shown in this dialog and the prefix of the JAR file is typically the plug-in which contains this class.

Open Type Dialog

Plug-in for an Java class

4. OSGi console

4.1. OSGi console

The OSGi console is like a command-line shell. In this console you can type a command to perform certain OSGi actions. This can be useful to analyze problems on the OSGi layer of your application.

Use for example the command ss to get an overview of all bundles, their status and bundle-id. The following table is a reference of the most important OSGi commands.

Table 2. OSGi commands

Command Description
help Lists the available commands.
ss Lists the installed bundles and their status.
ss vogella Lists bundles and their status that have vogella within their name.
start id Starts the bundle with the id ID
stop id Stops the bundle with the id ID
diag <bundle-id> Diagnoses a particular bundle. It lists all missing dependencies.
install URL Installs a bundle from a URL
uninstall <bundle-id> Uninstalls the bundle with the <bundle-id> ID
bundle <bundle-id> Show information about the bundle with the <bundle-id> ID, including the registered and used services.
headers <bundle-id> Show the MANIFST.MF information for a bundle.
services filter Show all available services and their consumer. Filter is an optional LDAP filter,e.g. to see all services which provide a ManagedService implementation use the "services (objectclass=*ManagedService)" command.


4.2. Required bundles

You have to add the following bundles to your runtime configuration to use the OSGi console.

  • org.eclipse.equinox.console

  • org.apache.felix.gogo.command

  • org.apache.felix.gogo.runtime

  • org.apache.felix.gogo.shell

Equinox contains also a simple built-in console which can be activated with the -Dosgi.console.enable.builtin=true parameter.

4.3. Telnet

If you specify the -console parameter in your launch configuration Eclipse will allow you to interact with the OSGi console. An OSGi launch configuration created with the Eclipse IDE contains this parameter by default. Via -console 5555 you can open a port to which you can then telnet. If you telnet to the OSGi console tab completion and a history of the commands is supported similar to the Bash shell under Linux.

4.4. Access to the Eclipse OSGi console

You can also access the OSGi console of your running Eclipse IDE. In the Console View you find a menu entry with the tooltip Open Console. If you select Host OSGi Console, you will have access to your running OSGi instance.

Please note that interfering with your running Eclipse IDE via the OSGi console, may put the Eclipse IDE into a bad state.

5. Download the Eclipse standard distribution

The Eclipse IDE is provided in different flavors. While you can install the necessary tools in any Eclipse package, it is typically easier to download the Eclipse Standard edition which contains all necessary tools.

Browse to the Eclipse download site and download the Eclipse Standard package.

Download Eclipse Plug-in IDE

Extract the zip file in a folder of your selection.

Warning

Avoid having special characters or spaces in the path to your extract Eclipse.

6. Exercise: Data model and service bundle

6.1. Target of the exercise

In this exercise you create a bundle (== plug-in) for the definition of your data model. You also make this data model available to other plug-ins.

6.2. Naming convention: simple plug-in / bundle

A plug-in or bundle generated by the Eclipse new plug-in general wizard can be generated with several options. If you generate a plug-in with the following options, this book calls such a plug-in a simple plug-in or simple bundle.

  • No Activator

  • No contributions to the user interface

  • Not an 3.x rich client application

  • generated without a template

In the plug-in creation wizard the corresponding selection is depicted in the following screenshot. Press the Finish button on this page to avoid the usage of templates.

Creating a simple plug-in

6.3. Create plug-in

Create a simple plug-in project called com.example.e4.rcp.todo.model.

6.4. Create base class

Create the com.example.e4.rcp.todo.model package and the following model class.

package com.example.e4.rcp.todo.model;

import java.util.Date;

public class Todo {
  
  private long id;
  private String summary = "";
  private String description = "";
  private boolean done = false;
  private Date dueDate;

} 

6.5. Generate constructors

Select SourceGenerate Constructor using Fields... to generate a constructor using all fields. Use the same approach to another constructor using only the id field and create another constructor without any fields.

Warning

Ensure that you really created all three constructors because you need both in the following exercises.

6.6. Generate getter and setter methods

Use the SourceGenerate Getter and Setter... to create getters and setters for all fields except a setter for id. The id should not be changeable after the creation of an Todo object.

Getter and setter generation

The resulting class should look like the following listing.

package com.example.e4.rcp.todo.model;

import java.util.Date;

public class Todo {
  
  private long id;
  private String summary = "";
  private String description = "";
  private boolean done = false;
  private Date dueDate;

  public Todo() {
  }
  
  public Todo(long id) {
    this.id = id;
  }
  
  public Todo(long id, String summary, String description, boolean done,
      Date dueDate) {
    this.id = id;
    this.summary = summary;
    this.description = description;
    this.done = done;
    this.dueDate = dueDate;

  }

  public long getId() {
    return id;
  }

  public String getSummary() {
    return summary;
  }

  public void setSummary(String summary) {
    this.summary = summary;
  }

  public String getDescription() {
    return description;
  }

  public void setDescription(String description) {
    this.description = description;
  }

  public boolean isDone() {
    return done;
  }

  public void setDone(boolean done) {
    this.done = done;
  }

  public Date getDueDate() {
    return dueDate;
  }

  public void setDueDate(Date dueDate) {
    this.dueDate = dueDate;
  }

} 

Note

As the id field is later in this exercise used in the equals and hashCode() methods therefore it should not be mutable.

6.7. Validation

Warning

Ensure that you have generated all three constructors and that you have not generated a setter method for the id field.

6.8. Generate toString, hashCode and equals methods

Use Eclipse to generate a toString() method for the Todo class based on the id and summary field. This can be done via the Eclipse menu SourceGenerate toString()....

Also use Eclipse to generate a hashCode() and equals() method based on the id field. This can be done via the Eclipse menu SourceGenerate hashCode() and equals()....

6.9. Write a copy method

Also add the following copy() method to the class.

public Todo copy() {
  return new Todo(this.id, this.summary, 
      this.description, this.done,
      this.dueDate);
} 

6.10. Create model interface

Create the following ITodoService interface.

package com.example.e4.rcp.todo.model;

import java.util.List;

public interface ITodoService {

  Todo getTodo(long id);

  boolean saveTodo(Todo todo);

  boolean deleteTodo(long id);

  List<Todo> getTodos();
} 

6.11. Export the model

Export the com.example.e4.rcp.todo.model package. For this open the MANIFEST.MF file and select the Runtime tab. Add com.example.e4.rcp.todo.model to the exported packages.

Exported API

6.12. Create data model provider plug-in (service plug-in)

Create a new simple plug-in project called com.example.e4.rcp.todo.service. This plug-in is called todo service plug-in in the following description.

Tip

Mac OS users should use a different plug-in name as .service are treated special on a Mac OS.

6.13. Prepare service plug-in

Add the com.example.e4.rcp.todo.model plug-in as dependency to your service plug-in. To achieve this open the MANIFEST.MF file and select the Dependencies tab and add the com.example.e4.rcp.todo.model package to the Imported Packages.

Create the com.example.e4.rcp.todo.service.internal package in your service plug-in and create the following class.

package com.example.e4.rcp.todo.service.internal;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.example.e4.rcp.todo.model.ITodoService;
import com.example.e4.rcp.todo.model.Todo;

/** * Example service impl, data is not persisted * between application restarts * */
public class MyTodoServiceImpl implements ITodoService { private static int current = 1; private List<Todo> todos; public MyTodoServiceImpl() { todos = createInitialModel(); } // always return a new copy of the data @Override public List<Todo> getTodos() { List<Todo> list = new ArrayList<Todo>(); for (Todo todo : todos) { list.add(todo.copy()); } return list; } // create new or update existing Todo object @Override public synchronized boolean saveTodo(Todo newTodo) { Todo updateTodo = findById(newTodo.getId()); if (updateTodo == null) { updateTodo= new Todo(current++); todos.add(updateTodo); } updateTodo.setSummary(newTodo.getSummary()); updateTodo.setDescription(newTodo.getDescription()); updateTodo.setDone(newTodo.isDone()); updateTodo.setDueDate(newTodo.getDueDate()); return true; } @Override public Todo getTodo(long id) { Todo todo = findById(id); if (todo != null) { return todo.copy(); } return null; } @Override public boolean deleteTodo(long id) { Todo deleteTodo = findById(id); if (deleteTodo != null) { todos.remove(deleteTodo); return true; } return false; } // example data, change if you like private List<Todo> createInitialModel() { List<Todo> list = new ArrayList<Todo>(); list.add(createTodo("Application model", "Flexible and extensible")); list.add(createTodo("DI", "@Inject as programming mode")); list.add(createTodo("OSGi", "Services")); list.add(createTodo("SWT", "Widgets")); list.add(createTodo("JFace", "Especially Viewers!")); list.add(createTodo("CSS Styling","Style your application")); list.add(createTodo("Eclipse services","Selection, model, Part")); list.add(createTodo("Renderer","Different UI toolkit")); list.add(createTodo("Compatibility Layer", "Run Eclipse 3.x")); return list; } private Todo createTodo(String summary, String description) { return new Todo(current++, summary, description, false, new Date()); } private Todo findById(long id) { for (Todo todo : todos) { if (id == todo.getId()) { return todo; } } return null; } }

Note

This service implementation uses transient data storage, i.e. the data is not persisted between application restarts. To persist the data you could extend this class to store the data for example in a database or the file system. As this storage is not special for Eclipse RCP applications, it is not covered in this book.

6.14. Create factory and export package

You will now create a class in the com.example.e4.rcp.todo.service package. For this you might need the following tip.

Tip

In its default configuration the Eclipse IDE hides parent packages if they don't contain any classes. During the specification of your class you can define the correct package. This is depicted in the following screenshot.

Specify the package for the creation of a class

Create the following class in the com.example.e4.rcp.todo.service package.

package com.example.e4.rcp.todo.service;

import com.example.e4.rcp.todo.model.ITodoService;
import com.example.e4.rcp.todo.service.internal.MyTodoServiceImpl;

/** * Factory provides access to the todo service provider * */
public class TodoServiceFactory { private static ITodoService todoService = new MyTodoServiceImpl(); public static ITodoService getInstance() { return todoService; } }

This TodoServiceFactory provides access to the service provider of the ITodoService interface. This class can be considered to be a factory for the ITodoService interface. A factory hides the creation of the concrete instance of a certain interface.

Export the com.example.e4.rcp.todo.service package so that is available for other plug-ins.

Note

Please note that the Eclipse tooling does not support the export of empty packages. You have to first create at least one class in the package before being able to export it.

7. Tutorial: Using the Activator and exporting your bundle

In this exercise you create another bundle which uses an Activator. You also run it within Eclipse At the end of this chapter you will also export your bundle to use it later in a stand-alone OSGi server.

7.1. Create a new Bundle

Create a new simple plug-in project "com.vogella.osgi.firstbundle.internal" via FileNewOther...Plug-in DevelopmentPlug-in Project.

7.2. Coding

Create the following thread class.

package com.vogella.osgi.firstbundle.internal;

public class MyThread extends Thread {
  private volatile boolean active = true;

  public void run() {
    while (active) {
      System.out.println("Hello OSGi console");
      try {
        Thread.sleep(5000);
      } catch (Exception e) {
        System.out.println("Thread interrupted " + e.getMessage());
      }
    }
  }

  public void stopThread() {
    active = false;
  }
} 

Change the class Activator.java to the following.

package com.vogella.osgi.firstbundle;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

import de.vogella.osgi.firstbundle.internal.MyThread;

public class Activator implements BundleActivator {
  private MyThread myThread;

  public void start(BundleContext context) throws Exception {
    System.out.println("Starting com.vogella.osgi.firstbundle");
    myThread = new MyThread();
    myThread.start();
  }

  
  public void stop(BundleContext context) throws Exception {
    System.out.println("Stopping com.vogella.osgi.firstbundle");
    myThread.stopThread();
    myThread.join();
  }

} 

7.3. Run

Select your MANIFEST.MF file, right-click it and select Run AsRun Configuration . Create a OSGi Framework launch configuration. deselect all bundles except your de.vogella.osgi.firstbundle. Afterwards press the Add Required bundles . This adds the org.eclipse.osgi bundle to your run configuration.

Run this configuration. Every 5 second a new message is written to the console.

Tip

In case you are wondering in which folder OSGi starts you find it in ${workspace_loc}/.metadata/plugins/org.eclipse.pde.core/ <runconfig> . This folder list the installed bundles via the file "dev.properties" and set the Eclipse workspace as reference in the config.ini file via the osgi.bundles=reference\:file\ statement. This way you can update your bundles in the running OSGi environment directly without any deployment.

7.4. Export your bundle

Export your bundle. This will allow you to install it into a OSGi runtime. Select your bundle and choose File -> Export -> Plug-in Development -> "Deployable plug-ins and fragment".

Unflag the option to export the source.

8. Running a stand-alone OSGi server

This chapter will show how to run Equinox as a OSGi stand-alone runtime.

In your Eclipse installation directory identify the file org.eclipse.osgi*.jar. This file should be in the "plugin" folder. Copy this jar file to a new place, e.g. c:\temp\osgi-server. Rename the file to "org.eclipse.osgi.jar".

Start your OSGi server via the following command.

java -jar org.eclipse.osgi.jar -console 

You can use "install URL" to install a bundle from a certain URL. For example to install your bundle from "c:\temp\bundles" use:

install file:c:\temp\bundles\plugins\de.vogella.osgi.firstbundle_1.0.0.jar 

Tip

You probably need to correct the path and the bundle name on your system.

You can start then the bundle with start and the id.

Tip

You can remove all installed bundles with the -clean parameter.

9. Support free vogella tutorials

Maintaining high quality free online tutorials is a lot of work. Please support free tutorials by donating or by reporting typos and factual errors.

9.1. Thank you

Please consider a contribution if this article helped you.

Flattr this

9.2. Questions and Discussion

If you find errors in this tutorial, please notify me (see the top of the page). Please note that due to the high volume of feedback I receive, I cannot answer questions to your implementation. Ensure you have read the vogella FAQ as I don't respond to questions already answered there.

10. Links and Literature

10.1. Source Code

Source Code of Examples

10.3. vogella Resources

vogella Training Android and Eclipse Training from the vogella team

Android Tutorial Introduction to Android Programming

GWT Tutorial Program in Java, compile to JavaScript and HTML

Eclipse RCP Tutorial Create native applications in Java

JUnit Tutorial Test your application

Git Tutorial Put all your files in a distributed version control system