Saturday 3 January 2015

Simple example of modular application development using OSGI Framework.

The OSGi (Open Service Gateway initiative) specification describes a modular system and a service platform for the Java programming language that implements a complete and dynamic component model.
A service-oriented architecture that decouples components, and enables these components to dynamically discover each other for collaboration. Service provider component must register services after that consumer component will use this.
The framework is conceptually divided into the following areas:
    Bundles (JAR file): Bundles are normal jar components with manifest headers. Bundles provide (export) and reuse (import) services via the OSGI framework. It defines encapsulation and declaration of dependencies (how a bundle can import and export Java packages and required component service packages). It implements specified interface (services) and these services will registered using Service Registry.
    Services: The services layer connects bundles in a dynamic way by offering a publish-find-bind model.
    Life-Cycle: OSGi provide an API for managing bundles life-cycle states (Install[1]/Resolve[32]/Start[2]/Stop[4]/Update[8]/Uninstall[16]) ,so OSGi framework RI will control bundle lifecycle.
    Security: The layer that handles the security aspects by limiting bundle functionality to pre-defined capabilities.
Following image explains the service-oriented architecture of OSGI.
This blog instruction will help us to implement simple example of service-oriented architecture of OSGI.
Please check Apache Felix specification for detailed decription.
Please check OSGI version 5speciication for detailed description.

  • Pre-requsite
  • Instructions assumes that jdk, Apache Felix 4.4.1 is installed without any error.
    Following image will guide us to start Apache Felix 4.4.1.

  • Implemenation instructions
    1. Create a Simple spring project name "sample-osgi". Following image will guide us. Add the required libraries in classpath.
    2. - osgi.cmpn-5.0.0.jar
      - osgi.core-5.0.0.jar
      - osgi.enterprise-5.0.0.jar

    3. Create following packages.
      - service.provider
      - service.consumer
    4. Create build.xml and build.properties and following snippet.
      - content of build.xml.
      <?xml version="1.0" encoding="UTF-8"?>
      <project name="sample-osgi" default="deploy" basedir=".">
       <!-- Import machine-specificsettings -->
       <property file="build.properties" />
       <!-- Setup build paths -->
       <property name="build.dir" value="${basedir}/build" />
       <property name="build.lib" value="${basedir}/lib" />
       <property name="line.separator" value="," />
       <property name="build.resource" value="${basedir}/resources" />
       <property name="build.classes.dir" value="${build.dir}/classes" />
       <property name="build.bundles.dir" value="${build.dir}/bundles" />
       <property name="build.test.dir" value="${build.dir}/tests" />
      
       <!-- Load the bnd custom task -->
       <taskdef resource="aQute/bnd/ant/taskdef.properties" classpath="${bnd.path}" />
      
       <!-- Set aclasspath for the OSGi libraries -->
       <path id="osgilibs">
        <fileset dir="${osgi.path}" includes="*osgi*.jar" excludes="*resource*"/>
        <fileset file="${bnd.path}" />
       </path>
      
       <!-- TARGET : clean ; cleans all build o u t p u t s -->
       <target name="clean" description="Clean all build outputs">
        <delete dir="${build.dir}" />
       </target>
      
       <!-- TARGET : compile; compiles Java sources -->
       <target name="compile" depends="clean" description="Compile Java sources">
        <pathconvert pathsep="${line.separator}" refid="osgilibs" property="build.classpath.list" />
        <echo message="*****ClassPath Entries*****" />
        <echo message="${build.classpath.list}" />
        <mkdir dir="${build.classes.dir}" />
        <javac optimize="on" includeantruntime="on" includejavaruntime="on" srcdir="src" destdir="${build.classes.dir}" debug="true" classpathref="osgilibs" />
        <mkdir dir="${build.test.dir}" />
       </target>
      
       <!-- TARGET : bundle ; g e n e r a t e s bundle JARs using bnd -->
       <target name="bundle" depends="compile" description="Build bundles">
        <mkdir dir="${build.bundles.dir}" />
        <!-- Convertan ANT file sett a flat list of files -->
        <pathconvert property="bnd.files" pathsep=",">
         <fileset dir="${build.resource}">
          <include name="*.bnd" />
         </fileset>
        </pathconvert>
        <bnd classpath="${build.classes.dir}" failok="false" output="${build.bundles.dir}" exceptions="true" files="${bnd.files}" trace="true" />
       </target>
      
       <!-- TARGET : deploy ; clean felix-cache and start felix server -->
       <target name="deploy" depends="bundle" description="Build bundles">
        <delete dir="${felix.home}/felix-cache" />
        <copy todir="${felix.home}/bundle" overwrite="true">
         <fileset dir="${build.bundles.dir}">
          <include name="*.jar" />
         </fileset>
        </copy>
        <tstamp>
         <format property="current.time" pattern="MM-dd-yyyy hh:mm:ss aa Z" />
        </tstamp>
        <echo message="build finished at ${current.time}" />
       </target>
      </project>

      - content of build.properties. Please these value will changed in your case.
      junit.path=F:/dev/test/junit-4.12.jar
      bnd.path=F:/dev/felix 4.4.1/biz.aQute.bnd-latest.jar
      felix.home=F:/dev/felix 4.4.1/felix-framework-4.4.1/
      osgi.path=F:/dev/felix 4.4.1/osgi
    5. Create example service interface "ExampleServiceInterface" in service.provider package and add following snippet.
    6. /**
       *
       * Copyright © Kaustuv Maji , 2015
       * Repos - https://github.com/kaustuvmaji
       * Blog -  http://kaustuvmaji.blogspot.in
       */
      package service.provider;
      
      /**
       * Example service interface.
       *
       * @author kmaji
       *
       */
      public interface ExampleServiceInterface {
      
       /**
        * Printing message.
        *
        * @param message
        */
       public void message(String message);
      }
    7. Create example implementation class ExampleServiceImpl which will implement service interface "ExampleServiceInterface" in service.provider and add following snippet.
    8. /**
       *
       * Copyright © Kaustuv Maji , 2015
       * Repos - https://github.com/kaustuvmaji
       * Blog -  http://kaustuvmaji.blogspot.in
       */
      package service.provider;
      
      import org.osgi.service.log.LogService;
      
      /**
       * Example Implementation class for service interface.
       *
       * @author KMaji
       *
       */
      public class ExampleServiceImpl implements ExampleServiceInterface {
      
       private LogService logservice;
      
       /**
        * @param logservice
        */
       public ExampleServiceImpl(LogService logservice) {
        this.logservice = logservice;
       }
      
       @Override
       public void message(String message) {
        logservice.log(LogService.LOG_INFO, "service provider printing message. -> [ " + message + " ]");
       }
      }

    9. Create class ProducerActivator in service.provider and add following snippet.
    10. /**
      *
      * Copyright © Kaustuv Maji , 2015
      * Repos - https://github.com/kaustuvmaji
      * Blog -  http://kaustuvmaji.blogspot.in
      */
      package service.provider;
      
      import org.osgi.framework.BundleActivator;
      import org.osgi.framework.BundleContext;
      import org.osgi.framework.BundleEvent;
      import org.osgi.framework.BundleListener;
      import org.osgi.framework.ServiceReference;
      import org.osgi.framework.ServiceRegistration;
      import org.osgi.service.log.LogService;
      
      /**
       * @author KMaji
       */
      public class ProducerActivator implements BundleActivator, BundleListener {
      
       protected ServiceRegistration<?> registration;
      
       private ServiceReference<?> ref;
      
       private LogService logservice;
      
       @Override
       public void start(BundleContext context) throws Exception {
        context.addBundleListener(this);
        logservice = log(context);
        registration = context.registerService(ExampleServiceInterface.class.getName(), new ExampleServiceImpl(logservice), null);
        logservice.log(LogService.LOG_INFO, context.getBundle().getSymbolicName() + " started.");
       }
      
       @Override
       public void stop(BundleContext context) throws Exception {
        context.removeBundleListener(this);
        logservice.log(LogService.LOG_INFO, context.getBundle().getSymbolicName() + " stoped.");
       }
      
       @Override
       public void bundleChanged(BundleEvent event) {
      
       }
      
       private LogService log(BundleContext context) {
      
        this.ref = context.getServiceReference(LogService.class.getName());
      
        if (ref != null) {
         logservice = (LogService) context.getService(ref);
        }
      
        logservice.log(LogService.LOG_INFO, " Starting log service for sample osgi example app.");
        return logservice;
       }
      }
    11. Create bnd file for producer app "sample-osgi-provider.bnd" in resource folder.
    12. Bundle-Version: 0.0.0
      Bundle-Copyright: © kaustuv maji
      Bundle-Vendor: Knowledge
      Bundle-DocURL: http://kaustuvmaji.blogspot.in
      Import-Package: org.osgi.*,\
       org.osgi.framework.*,\
       org.osgi.service.*,\
       org.osgi.util.*,\
       *
      Export-Package: service.provider
      Service-Component: *
      Bundle-Activator: service.provider.ProducerActivator

      now we will run build.xml. ant target "deploy" will create example service provider application first and then it will copy application jar to auto-deploy folder of felix.
    13. Create class ExampleConsumer in package service.consumer and add following snippet. This class will consume the example service provided by service provider app.
    14. /**
       *
       * Copyright © Kaustuv Maji , 2015
       * Repos - https://github.com/kaustuvmaji
       * Blog -  http://kaustuvmaji.blogspot.in
       */
      package service.consumer;
      
      import java.awt.event.ActionEvent;
      import java.awt.event.ActionListener;
      
      import javax.swing.Timer;
      
      import service.provider.ExampleServiceInterface;
      
      /**
       * @author KMaji
       *
       */
      public class ExampleConsumer implements ActionListener {
      
       protected ExampleServiceInterface service;
      
       protected Timer timer;
      
       public ExampleConsumer(ExampleServiceInterface service) {
        super();
        this.service = service;
        timer = new Timer(1000, this);
       }
      
       public void startTimer() {
        timer.start();
       }
      
       public void stopTimer() {
        timer.stop();
       }
       /*
        * (non-Javadoc)
        *
        * @see
        * java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
        */
       @Override
       public void actionPerformed(ActionEvent e) {
        service.message("from consumer 1");
       }
      }
    15. Create class consumer activator class in service.consumer.
    16. /**
       *
       * Copyright © Kaustuv Maji , 2015
       * Repos - https://github.com/kaustuvmaji
       * Blog -  http://kaustuvmaji.blogspot.in
       */
      package service.consumer;
      
      import org.osgi.framework.BundleActivator;
      import org.osgi.framework.BundleContext;
      import org.osgi.framework.ServiceReference;
      import org.osgi.service.log.LogService;
      
      import service.provider.ExampleServiceInterface;
      
      public class ConsumerActivator implements BundleActivator {
      
       protected ExampleConsumer consumer;
      
       protected ServiceReference<?> ref;
      
       private LogService logservice;
      
       @Override
       public void start(BundleContext context) throws Exception {
        LogService logservice = log(context);
        logservice.log(LogService.LOG_INFO, "Starting " + context.getBundle() + "!");
        logservice.log(LogService.LOG_INFO, "Log service found for " + context.getBundle().getSymbolicName() + "!");
        ref = context.getServiceReference(service.provider.ExampleServiceInterface.class.getName());
        if (ref != null) {
         logservice.log(LogService.LOG_INFO, "Found service reference and now trying to consume provided service!");
         consumer = new ExampleConsumer((ExampleServiceInterface) context.getService(ref));
         if (consumer == null) {
          logservice.log(LogService.LOG_INFO, "consumer didn't found provider service :(");
         }
         consumer.startTimer();
        }
        logservice.log(LogService.LOG_INFO, "Started " + context.getBundle() + "!");
       }
      
       @Override
       public void stop(BundleContext context) throws Exception {
        logservice.log(LogService.LOG_INFO, "Stopping " + context.getBundle() + "!");
        if (consumer != null) {
         consumer.stopTimer();
        }
        logservice.log(LogService.LOG_INFO, "Stopped " + context.getBundle() + "!");
       }
      
       private LogService log(BundleContext context) {
      
        this.ref = context.getServiceReference(LogService.class.getName());
      
        if (ref != null) {
         logservice = (LogService) context.getService(ref);
        }
      
        logservice.log(LogService.LOG_INFO, " Starting log service for sample osgi example app.");
        return logservice;
       }
      }
    17. Create bnd file for consumer app "sample-osgi-consumer.bnd" in resource folder.
    18. Bundle-Version: 0.0.0
      Bundle-Copyright: © kaustuv maji
      Bundle-DocURL: http://kaustuvmaji.blogspot.in
      Bundle-Vendor: Knowledge
      Service-Component: *
      Import-Package: *
      Export-Package: service.consumer
      Bundle-Activator: service.consumer.ConsumerActivator

    19. now we will run build.xml. ant target "deploy" will create example consumer application first and then it will copy application jar to auto-deploy folder of felix.
    20. Open command prompt and go to felix.home and execute java -jar bin/felix.jar. This will start felix. Please check following image that will be the output.
    21. kmaji /cygdrive/f/dev/felix 4.4.1/felix-framework-4.4.1 $ java -jar bin/felix.jar
      [INFO] Started bridged http service
      [INFO] Started bridged http service
      ____________________________
      Welcome to Apache Felix Gogo
      
      g! 2015-01-03 19:58:20.319:INFO:oejs.Server:jetty-8.1.14.v20131031
      2015-01-03 19:58:20.459:INFO:oejs.AbstractConnector:Started SelectChannelConnector@0.0.0.0:8080
      [INFO] Started Jetty 8.1.14.v20131031 at port(s) HTTP:8080 on context path /
      [Bundle  id { 20 }  ( name resources.kaustuv-osgi-log.0.0.0)]     :3:     | BundleEvent STARTED
      [Bundle  id { 22 }  ( name resources.sample-osgi-provider.0.0.0)]       :3:     | BundleEvent RESOLVED
      [Bundle  id { 21 }  ( name resources.sample-osgi-consumer.0.0.0)]       :3:     | BundleEvent RESOLVED
      [Bundle  id { 21 }  ( name resources.sample-osgi-consumer.0.0.0)]       :3:     |  Starting log service for sample osgi example app.
      [Bundle  id { 21 }  ( name resources.sample-osgi-consumer.0.0.0)]       :3:     | Starting resources.sample-osgi-consumer [21]!
      [Bundle  id { 21 }  ( name resources.sample-osgi-consumer.0.0.0)]       :3:     | Log service found for resources.sample-osgi-consumer!
      [Bundle  id { 21 }  ( name resources.sample-osgi-consumer.0.0.0)]       :3:     | Started resources.sample-osgi-consumer [21]!
      [Bundle  id { 21 }  ( name resources.sample-osgi-consumer.0.0.0)]       :3:     | BundleEvent STARTED
      [Bundle  id { 22 }  ( name resources.sample-osgi-provider.0.0.0)]       :3:     |  Starting log service for sample osgi example app.
      [Bundle  id { 22 }  ( name resources.sample-osgi-provider.0.0.0)]       :3:     | ServiceEvent REGISTERED
      [Bundle  id { 22 }  ( name resources.sample-osgi-provider.0.0.0)]       :3:     | resources.sample-osgi-provider started.
      [Bundle  id { 22 }  ( name resources.sample-osgi-provider.0.0.0)]       :3:     | BundleEvent STARTED
      [Bundle  id { 0 }   ( name org.apache.felix.framework.4.4.1)]      :3:     | BundleEvent STARTED
      [Bundle  id { 0 }   ( name org.apache.felix.framework.4.4.1)]      :3:     | FrameworkEvent STARTED
      [Bundle  id { 22 }  ( name resources.sample-osgi-provider.0.0.0)]       :3:     | service provider printing message. -> [ from consumer 1 ]
      [Bundle  id { 22 }  ( name resources.sample-osgi-provider.0.0.0)]       :3:     | service provider printing message. -> [ from consumer 1 ]
      [Bundle  id { 22 }  ( name resources.sample-osgi-provider.0.0.0)]       :3:     | service provider printing message. -> [ from consumer 1 ]
      [Bundle  id { 22 }  ( name resources.sample-osgi-provider.0.0.0)]       :3:     | service provider printing message. -> [ from consumer 1 ]
      [Bundle  id { 22 }  ( name resources.sample-osgi-provider.0.0.0)]       :3:     | service provider printing message. -> [ from consumer 1 ]
      [Bundle  id { 22 }  ( name resources.sample-osgi-provider.0.0.0)]       :3:     | service provider printing message. -> [ from consumer 1 ]
      [Bundle  id { 22 }  ( name resources.sample-osgi-provider.0.0.0)]       :3:     | service provider printing message. -> [ from consumer 1 ]
      [Bundle  id { 22 }  ( name resources.sample-osgi-provider.0.0.0)]       :3:     | service provider printing message. -> [ from consumer 1 ]
      [Bundle  id { 22 }  ( name resources.sample-osgi-provider.0.0.0)]       :3:     | service provider printing message. -> [ from consumer 1 ]
      [Bundle  id { 22 }  ( name resources.sample-osgi-provider.0.0.0)]       :3:     | service provider printing message. -> [ from consumer 1 ]
      [Bundle  id { 22 }  ( name resources.sample-osgi-provider.0.0.0)]       :3:     | service provider printing message. -> [ from consumer 1 ]
      Now from console log snippet we can clearly figure out that service provider application already registered service interface and this service interface prints the message which is sent from consumer application. So if we refer to Figure no 1 Bundle B is our service provider bundle and Bundle A is our consumer bundle. Bundle B export and register service in osgi framework and Bundle A consumes it.
      Apache felix web-console
    Apache felix also provide web console to perform administrative functionalities like deployment/un-deployment of bundles. At first we need to download web-console binary from Apache felix distribution link. After downloading we have copy paste binary file in felix.home/bundle folder. After this we got to start felix.
    Web-console url is http://localhost:8080/system/console. Default username password is admin/admin.
    • Bundles management: url -> http://localhost:8080/system/console/bundles using this we can life-cycle of bundles. We can also install and un-install bundles.

    • Log Service: url -> http://localhost:8080/system/console/logs using this we can check all the logs messages.

    • Service management : url -> http://localhost:8080/system/console/services using this we can check all the available services the consumer app can consume.

    Click here to download source code of above example

    Source Code


    European Union laws require you to give European Union visitors information about cookies used on your blog. In many cases, these laws also require you to obtain consent.

    As a courtesy, we have added a notice on your blog to explain Google's use of certain Blogger and Google cookies, including use of Google Analytics and AdSense cookies.