Tuesday 25 September 2018

Hello OSGi

I found existing guides for OSGi to be a bit outdated. So, it is probably better to document the basic setup.

The plan is to first create a consumable bundle, i.e. a service bundle followed by a consumer bundle that uses the former.

I am also using maven for creating the projects so that the maven toolchain can be used for proper packaging.

For the consumable bundle, we start with defining an interface that exposes functionality of the bundle.

package main.java.interfaces;

public interface TestService {
    void hello();
}


We then implement it.

package main.java.impls;



import main.java.interfaces.TestService;



public class TestServiceImpl implements TestService {

    @Override

    public void hello() {

        System.out.println("Hello Duniya");
    }
}

We then add the activation logic for the bundle.

package main.java.provider;

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

import main.java.interfaces.TestService;
import main.java.impls.TestServiceImpl;

public class ServiceActivator implements BundleActivator {
    private ServiceRegistration registration;

    @Override
    public void start(BundleContext bundleContext) throws Exception {
        registration = bundleContext.registerService(
                TestService.class.getName(),
                new TestServiceImpl(),
                null);
        System.out.println("ServiceActivator started");
    }

    @Override
    public void stop(BundleContext bundleContext) throws Exception {
        registration.unregister();
    }
}



We complete this bundle by adding a POM for building it.




<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>felix_test</groupId>
    <artifactId>felix_test</artifactId>
    <version>1.0</version>
    <packaging>bundle</packaging>

    <dependencies>
        <dependency>
            <groupId>org.osgi</groupId>
            <artifactId>org.osgi.core</artifactId>
            <version>6.0.0</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.0.2</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <extensions>true</extensions>
                <configuration>
                    <instructions>
                        <Bundle-SymbolicName>felix_test</Bundle-SymbolicName>
                        <Export-Package>main.java.interfaces</Export-Package>
                        <Bundle-Activator>main.java.provider.ServiceActivator</Bundle-Activator>
                        <Bundle-Vendor>Amitav Mohanty</Bundle-Vendor>
                    </instructions>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>


The package of interfaces that are to be exposed are called out along with the activator so that the maven toolchain can build our bundle properly.

We move on to create the consumer bundle next. We start with the bundle activator.

package main.java.consumer;

import main.java.interfaces.TestService;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;

public class FelixTestActivator implements BundleActivator {
    @Override
    public void start(BundleContext bundleContext) throws Exception {
        ServiceReference reference = bundleContext.getServiceReference(TestService.class.getName());
        consumer = new TestConsumer((TestService)bundleContext.getService(reference));
        consumer.startTimer();
    }

    @Override
    public void stop(BundleContext bundleContext) throws Exception {
        consumer.stopTimer();
    }

    private TestConsumer consumer;
}

We then add the consumer.


package main.java.consumer;

import main.java.interfaces.TestService;

import javax.swing.Timer;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class TestConsumer implements ActionListener {
    @Override
    public void actionPerformed(ActionEvent e) {
        service.hello();
    }

    public TestConsumer(TestService service){
        super();
        this.service = service;
        timer = new Timer(1000, this);
    }

    public void startTimer(){
        timer.start();
    }

    public void stopTimer(){
        timer.stop();
    }

    private final TestService service;
    private final Timer timer;

}


The initiation of the consumer happens when the bundle is started.

The POM for building the consumer bundle is as follows:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>consumer</groupId>
    <artifactId>consumer</artifactId>
    <version>1.0</version>
    <packaging>bundle</packaging>

    <dependencies>
        <dependency>
            <groupId>org.osgi</groupId>
            <artifactId>org.osgi.core</artifactId>
            <version>6.0.0</version>
        </dependency>

        <dependency>
            <groupId>felix_test</groupId>
            <artifactId>felix_test</artifactId>
            <version>1.0</version>
            <scope>system</scope>
            <systemPath>${basedir}/../target/felix_test-1.0.jar</systemPath>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.0.2</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <extensions>true</extensions>
                <configuration>
                    <instructions>
                        <Bundle-SymbolicName>consumer</Bundle-SymbolicName>
                        <Bundle-Activator>main.java.consumer.FelixTestActivator</Bundle-Activator>
                        <Bundle-Vendor>Amitav Mohanty</Bundle-Vendor>
                    </instructions>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

The dependency on the other bundle is called out.

The "packaging" node in the POMs is essential for creating OSGi bundles.