Both Java and OSGi originally focused on small hardware devices. Java's attraction came from its promise "write once, run anywhere" which appealed to hardware vendors but, it was soon found that in Java applications dependencies were difficult to sort out, especially in regards to the classpath. Often it was found that different versions of the same jar were needed by different parts of the application. In short, jars are not a complete solution for providing modularity for Java applications.
OSGi is designed as a universal integration platform for the interoperability of applications and services. OSGi makes it possible for different components of an application to depend on different versions of a jar.
The use case of an application consuming services provided through OSGi requires both the consumer and the provider publish manifests. The manifest required is, in most ways, a standard jar manifest stored in the jar as META-INF/MANIFEST.MF. The jar which provides the service will export the package(s) in which the Java classes are found with a statement in the jar's manifest such as Export-Package: com.company.search. This statement only effects the Java components registered in the OSGi registry. It exposes all the public classes in the package.
The manifest in the jar file of the consumer will have an import like Import-Package: com.company.search. This statement imports all the public classes in the package.
Below I have created a MessagePrinter function that consumes messages produced by the HelloGoodbyeProducer. There are three modules, the activator, the producer and the consumer; each module is a separate project created in its own folder.
package com.activator; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceRegistration; import com.producer.HelloGoodbyeProducer; import com.producer.impl.HelloGoodbyeImpl; import com.consumer.MessagePrinterService; import com.consumer.impl.MessagePrinterServiceImpl; public class HelloWorldActivator implements BundleActivator { public void start(BundleContext context) throws Exception { HelloGoodbyeProducer producer = getHelloGoodbyeProducer(context); MessagePrinterService printer = getMessagePrinter(context); printer.printMessage(producer.getHelloMessage()); } public void stop(BundleContext context) throws Exception { HelloGoodbyeProducer producer = getHelloGoodbyeProducer(context); MessagePrinterService printer = getMessagePrinter(context); printer.printMessage(producer.getGoodbyeMessage()); } private HelloGoodbyeProducer getHelloGoodbyeProducer(BundleContext context) { ServiceRegistration reg = context.registerService(HelloGoodbyeProducer.class.getName(), new HelloGoodbyeImpl(), null); HelloGoodbyeProducer producer = (HelloGoodbyeProducer) context.getService(reg.getReference()); return producer; } private MessagePrinterService getMessagePrinter(BundleContext context) { ServiceRegistration reg = context.registerService(MessagePrinterService.class.getName(), new MessagePrinterServiceImpl(), null); MessagePrinterService printer = (MessagePrinterService) context.getService(reg.getReference()); return printer; } }
src/META-INF/MANIFEST.MF
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: HelloActivator Bundle-SymbolicName: com.activator.HelloWorldActivator Bundle-Version: 1.0.3 Bundle-Activator: com.activator.HelloWorldActivator Import-Package: com.producer, com.producer.impl, com.consumer, com.consumer.impl, org.osgi.framework
ANT build.xml for the Activator
<project name="activator" default="jar" basedir="."> <property name="src.dir" location="src"/> <property name="build.dir" location="bin"/> <property name="target.dir" location="target"/> <property name="osgi.dir" location="${user.home}/dev/bin/equinox-sdk-3"/> <property name="consumer.jar" value="../consumer/target/consumer.jar"/> <property name="producer.jar" value="../producer/target/producer.jar"/> <property name="manifest" location="${build.dir}/META-INF/MANIFEST.MF"/> <path id="classpath"> <pathelement location="${build.dir}"/> <pathelement location="${osgi.dir}/plugins/org.eclipse.osgi_3.5.2.R35x_v20100126.jar"/> <pathelement location="${consumer.jar}"/> <pathelement location="${producer.jar}"/> </path> <target name="clean"> <delete dir="${build.dir}"/> <delete dir="${target.dir}"/> </target> <target name="init" depends="clean"> <mkdir dir="${build.dir}"/> <mkdir dir="${target.dir}"/> </target> <target name="compile" depends="init"> <javac srcdir="${src.dir}" destdir="${build.dir}" classpathref="classpath"/> <copy file="src/META-INF/MANIFEST.MF" todir="${build.dir}/META-INF"/> </target> <target name="jar" depends="compile"> <jar jarfile="${target.dir}/${ant.project.name}.jar" basedir="${build.dir}" manifest="${manifest}"/> </target> </project>
Java Code for the Message Producer
package com.producer; public interface HelloGoodbyeProducer { String getHelloMessage(); String getGoodbyeMessage(); }
package com.producer.impl; import com.producer.HelloGoodbyeProducer; public class HelloGoodbyeImpl implements HelloGoodbyeProducer { public String getHelloMessage() { return "Hello World!"; } public String getGoodbyeMessage() { return "Goodbye World!"; } }
src/META-INF/MANIFEST.MF
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: HelloGoodbye Bundle-SymbolicName: com.producer.HelloGoodbyeProducer Bundle-Version: 1.0.3 Bundle-Activator: com.activator.HelloWorldActivator Export-Package: com.producer, com.producer.impl
ANT build.xml for the Producer
<project name="producer" default="jar" basedir="."> <property name="src.dir" location="src"/> <property name="build.dir" location="bin"/> <property name="target.dir" location="target"/> <property name="manifest" location="${build.dir}/META-INF/MANIFEST.MF"/> <path id="classpath"> <pathelement location="${build.dir}"/> </path> <target name="clean"> <delete dir="${build.dir}"/> <delete dir="${target.dir}"/> </target> <target name="init" depends="clean"> <mkdir dir="${build.dir}"/> <mkdir dir="${target.dir}"/> </target> <target name="compile" depends="init"> <javac srcdir="${src.dir}" destdir="${build.dir}" classpathref="classpath"/> <copy file="src/META-INF/MANIFEST.MF" todir="${build.dir}/META-INF"/> </target> <target name="jar" depends="compile"> <jar jarfile="${target.dir}/${ant.project.name}.jar" basedir="${build.dir}" manifest="${manifest}"/> </target> </project>
Java Code for the Consumer
package com.consumer; public interface MessagePrinterService { public void printMessage(String message); }
package com.consumer.impl; import com.consumer.MessagePrinterService; public class MessagePrinterServiceImpl implements MessagePrinterService { public void printMessage(String message) { System.out.println(message); } }
src/META-INF/MANIFEST.MF
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: HelloGoodbye Bundle-SymbolicName: com.producer.HelloGoodbyeProducer Bundle-Version: 1.0.3 Bundle-Activator: com.activator.HelloWorldActivator Export-Package: com.producer, com.producer.impl
build.xml for the Consumer
<project name="consumer" default="jar" basedir="."> <property name="src.dir" location="src"/> <property name="build.dir" location="bin"/> <property name="target.dir" location="target"/> <property name="manifest" location="${build.dir}/META-INF/MANIFEST.MF"/> <path id="classpath"> <pathelement location="${build.dir}"/> </path> <target name="clean"> <delete dir="${build.dir}"/> <delete dir="${target.dir}"/> </target> <target name="init" depends="clean"> <mkdir dir="${build.dir}"/> <mkdir dir="${target.dir}"/> </target> <target name="compile" depends="init"> <javac srcdir="${src.dir}" destdir="${build.dir}" classpathref="classpath"/> <copy file="src/META-INF/MANIFEST.MF" todir="${build.dir}/META-INF"/> </target> <target name="jar" depends="compile"> <jar jarfile="${target.dir}/${ant.project.name}.jar" basedir="${build.dir}" manifest="${manifest}"/> </target> </project>
It's Showtime!
[greg:equinox-SDK-3] java -jar plugins/org.eclipse.osgi_3.5.2.R35x_v20100126.jar -console osgi> ss Framework is launched. id State Bundle 0 ACTIVE org.eclipse.osgi_3.5.2.R35x_v20100126 osgi> install file:producer.jar Bundle id is 4 osgi> install file:consumer.jar Bundle id is 5 osgi> install file:activator.jar Bundle id is 6 osgi> ss Framework is launched. id State Bundle 0 ACTIVE org.eclipse.osgi_3.5.2.R35x_v20100126 4 RESOLVED com.producer.HelloGoodbyeProducer_1.0.3 5 RESOLVED com.consumer.MessagePrinterService_1.0.3 6 RESOLVED com.activator.HelloWorldActivator_1.0.3 osgi> start 6 Hello World! osgi> stop 6 Goodbye World! osgi>