OSGi

OSGi Hello World


We start with an interface, which describes our 'Talker' service. Our mini service only provides a String over the getText()-Method:
 package com.example.talkersys;
 public interface Talker {
	public String getText();
 }
A service implementation returns numbered "Hello World!" greetings:
 package com.example.talkersys;
 public class TalkerImpl implements Talker {
	int n = 0;	
	public String getText() {
		return "Hello World! " + ++n;
	}
 }
An Activator instantiates the TalkerImpl and registers it as a service:
 package com.example.talkersys;
 import org.osgi.framework.BundleActivator; 
 import org.osgi.framework.BundleContext; 
 import java.util.Hashtable;
 public class TalkerActivator implements BundleActivator { 
	public void start(BundleContext bc) { 
		Talker t = new TalkerImpl();
		bc.registerService(Talker.class.getName(), t, new Hashtable<String, Object>());
	} 
	
	public void stop(BundleContext bc) { 
	} 
 }
Finally, our service gets the following META-INF/MANIFEST.MF:
 Manifest-Version: 1.0 
 Bundle-ManifestVersion: 2 
 Bundle-SymbolicName: com.example.talkersys 
 Bundle-Version: 1 
 Bundle-Activator: com.example.talkersys.TalkerActivator 
 Import-Package: org.osgi.framework
 Export-Package: com.example.talkersys
 Bundle-ClassPath: .
The three class-files are packaged together with MANIFEST.MF to form a OSGi-Bundle in a JAR-file. To test or service, we'll create a second bundle, consisting of only a Activator class:
 package com.example;
 import org.osgi.framework.BundleActivator; 
 import org.osgi.framework.BundleContext; 
 import org.osgi.framework.ServiceReference; 
 import com.example.talkersys.Talker;
 import java.util.Timer;
 import java.util.TimerTask;
 public class ClockActivator implements BundleActivator { 
	private static volatile Timer timer;
	
	public void start(BundleContext bc) { 
		ServiceReference ref = bc.getServiceReference(Talker.class.getName());
		final Talker talker = (Talker) bc.getService(ref);
		
		timer = new Timer();
		TimerTask tt = new TimerTask() {
			public void run() {
				System.out.println("Clock: " + talker.getText());
			}
		};
		timer.schedule(tt, 0, 5000);
	} 
	
	public void stop(BundleContext bc) {
		System.out.println("Stopping clock.");	
		if(timer != null) timer.cancel();
	} 
 }
The framework will not concurrently call a Activator ( 1). The start()-Method looks up the Talker service and starts a TimerTask, so that every 5 seconds a text provided by that service is printed. That bundle gets its manifest, too:
 Manifest-Version: 1.0 
 Bundle-ManifestVersion: 2 
 Bundle-SymbolicName: com.example.clocksys 
 Bundle-Version: 1 
 Bundle-Activator: com.example.ClockActivator 
 Import-Package: com.example.talkersys,org.osgi.framework
 Bundle-ClassPath: .
The Import-Package field corresponds with the Export-Package field of our Talker bundle.
If you build the bundles with maven ("mvn package", with the META-INF/MANIFEST.MF under src/main/resources), you'll need to declare a dependency on the artifact org.osgi.core and command the package plugin to use your manifest:
    <dependency>
	<groupId>org.osgi</groupId>
	<artifactId>org.osgi.core</artifactId>
	<version>5.0.0</version>
    </dependency>
    ...
      <plugin>
        <artifactId>maven-jar-plugin</artifactId>
        <configuration>
          <archive>
            <manifestFile>src/main/resources/META-INF/MANIFEST.MF</manifestFile>
          </archive>
        </configuration>
      </plugin>
This can be done in a parent POM of service and client.
The bundles will be ran inside a instance of the Equinox implementation of the OSGi framework. I just took the JAR files whose name started with 'org.eclipse.osgi' from the "plugins" directory of an Eclipse installation and ran
 java -jar org.eclipse.osgi_3.6.1.R36x_v20100806.jar -console
to get a console connected to a new OSGi instance. We can now enter commands at the console prompt.
For newer versions (since 3.8?) the console doesn't appear. You'll need a file configuration/config.ini in your working directory that contains:
    osgi.bundles=\
    org.eclipse.equinox.console,\
    org.apache.felix.gogo.runtime,\
    org.apache.felix.gogo.shell 
as well as the corrensponding JAR files of the listed bundles (named <SymbolicName>_<Version>.jar, for example org.apache.felix.gogo.shell_0.8.0.v201110170705.jar).
Let's load our bundles (adjust for your path- and file-name):
 osgi> install file:///wherever/talker.jar 
 Bundle id is 1
 osgi> install file:///wherever/clock.jar
 Bundle id is 2
Using the provided bundle IDs, we can now start (and stop) our bundles:
 osgi> start 1
 osgi> start 2
 Clock: Hello World! 1
 Clock: Hello World! 2
 Clock: Hello World! 3
 Clock: Hello World! 4
 Clock: Hello World! 5
 osgi> stop 2
 Stopping clock.

Getting Status Information

The "ss" command prints a short status:
 osgi> ss
 Framework is launched.
 id	State       Bundle
 0	ACTIVE      org.eclipse.osgi_3.6.1.R36x_v20100806
 1	ACTIVE      com.example.talkersys_1.0.0
 2	RESOLVED    com.example.clocksys_1.0.0
The "bundle" command shows bundle information, including provided services.
 osgi> bundle 1
 com.example.talkersys_1.0.0 [1]
  Id=1, Status=ACTIVE      Data Root=/wherever/configuration/org.eclipse.osgi/bundles/1/data
  Registered Services
    {com.example.talkersys.Talker}={service.id=28}
  No services in use.
  Exported packages
    com.example.talkersys; version="0.0.0"[exported]
  Imported packages
    org.osgi.framework; version="1.5.0"<org.eclipse.osgi_3.6.1.R36x_v20100806 [0]>
  No fragment bundles
  Named class space
    com.example.talkersys; bundle-version="1.0.0"[provided]
  No required bundles
That command may also provide insight in case anything refuses to work.

EditContents