Get hands dirty with OSGi 🚀
We learned the concepts of OSGi services last time, here we are trying to create new OSGI services and run them in the WSO2 Carbon platform, which is an OSGi platform. If you don’t have any idea regarding what is OSGI you can refer to this link
Let's look at a simple scenario and try to implement that scenario using these OSGi services. The scenario is like this, imagine there is a player who plays a game where there are multiple champions, the champion for the player is generated automatically when the player requests a champion from the game.
Now we have to map the above scenario to the OSGi services, There is a module interaction in OSGi. Therefore, the player and champion generation are two different modules. Each module in a system defines a logical boundary. Also, a module itself is in a control of the classes which are fully encapsulated (hidden) and which are exposed to external use.
So the player module will have functions like requesting a champion, view the champion details, view player stats, etc. when the Champion generation module will have functions like generate a champion, champion stats update, champions add to the champion pool, nerf champion abilities, etc.
Okay now we need to code the two modules that need for the above scenario, but we need to write them as OSGi services. The most important task in building OSGi services is to write the manifest.mf file correctly. In most cases writing this file manually is not an easy task. So we need to use a tool to generate it, rather than writing it on our own. For this task, the Maven bundle plugin is a great choice.
Okay, let's create a maven project and start coding (Hope you have a basic understanding of the maven project management tool) Here we use Intellij IDEA as our IDE to create a maven project.
Create a maven project with the name game-manager. This is the main project and it contains both the player and champion pool components. we can create a player and champion pool in two different places, but doing this will make code less complex and easy to access.
groudId:org.wso2.carbon
artifactId:game-manager
version:1.0-SNAPSHOT
packaging:pom
name:WSO2 Carbon-Game Manager
After creating this parent module, in IntelliJ IDEA you can create sub-modules by right-clicking on the parent module and add new > module
so like that you need to create two submodules org.wso2.carbon.game.producer
and org.wso2.carbon.game.consumer
so after creating those two submodules your project directory will look like this.
Note: When adding submodules to this project, the parent project pom should be updated accordingly with <modules> and the submodules pom will have a <parent> tag. If you are using an IDE, it auto-generates them.
as you can see under the wso2-game-manager
parent project there are two submodules org.wso2.carbon.game.producer
and org.wso2.carbon.game.consumer
the final parent pom file will look like below,
Game Producer
First, let’s look at how to create org.wso2.carbon.game.producer
and we use the following stuff.
artifactId:org.wso2.carbon.game.producer
packaging:bundle
plugin:maven-bundle-plugin , maven-scr-plugin
dependency:org.apache.felix.scr.ds-annotations , org.eclipse.osgi.services ,org.eclipse.osgi
name:WSO2 Carbon-Game Producer
the final pom file looks like below.
As you can see in the pom file <packaging>bundle</packaging>
so this module is packaging as an OSGi bundle. In an OSGI bundle hardest part is to write the manifest.mf file. So in the above pom, you can see we use the maven-bundle plugin.
In the maven-bundle-plugin, we identify the Bundle-Symbolic-Name, Bundle-Name, Export-Package, Import-Package, Private-Package, and some other values sets for OSGi manifest header under configuration instructions.
We described that the org.wso2.carbon.game.producer.internal
the package needs to be private-package and also in the Export-package section we described that as below,
<Export-Package>
!org.wso2.carbon.game.producer.internal, org.wso2.carbon.game.producer.*
</Export-Package>
So this means export all the packages in org.wso2.carbon.game.producer
except the org.wso2.carbon.game.producer.internal
package.
Let's start the coding game-producer💻
First, create your files like this, so you need to have 4 files inside 2 directories.
Inside internal
directory create, GameProducerServiceComponent.java
class
Inside model
directory create, Player.java
class and crate GameProducer.java
interface and GameProducerImpl.java
the class which implements the GameProducer.java interface
Then register the created service of the game service producer in a registry to use it from other components.
That registering is done in the GameProducerServiceComponent.java
class.
bundleContext.registerService(GameProducer.class, GameProducerImpl.getInstance(), null);
Service registration is done in this line under activate method. Also, you can see that service registering is done under a try-catch block, that is because there can be some errors or exceptions that can happen when service is registering in OSGi framework due to server errors or some other errors to catch that and throw error we register services inside a try-catch block.
Game Consumer
Now, let’s look at how to create org.wso2.carbon.game.consumer
and we use the following stuff.
artifactId:org.wso2.carbon.game.consumer
packaging:bundle
plugin:maven-bundle-plugin , maven-scr-plugin
dependency:org.apache.felix.scr.ds-annotations , org.eclipse.osgi.services ,org.eclipse.osgi, org.wso2.carbon.game.producer
name:WSO2 Carbon-Game Consumer
the final pom file looks like below.
An important thing in this pom file is import packages. Since we are going to use the game producer in this consumer module, that producer needs to be imported as an imported package.
<Import-Package>
org.osgi.framework; version=”${osgi.framework.imp.pkg.version.range}”,
org.osgi.service.component; version=”${osgi.service.component.imp.pkg.version.range}”,
org.wso2.carbon.game.producer.*; version=”${project.version}”
</Import-Package>
Create your files like this, there need to have a internal
, the package consists of two java files. GameConsumerComponent.java
and GameConsumerDataHolder.java
and GameConsumer.java
file outside the internal
package.
Now we are going to write the GameConsumer.java
in such a way that the consumer requests to run addPlayer()
at its activation.
We use a data-holder class to hold the services that need to be used in the component, As you can see
@Reference( name = “org.wso2.carbon.game.producer”,
service = GameProducer.class,
cardinality = ReferenceCardinality.MANDATORY,
policy = ReferencePolicy.DYNAMIC, unbind = “unsetGameProducer” )
protected void setGameProducer(GameProducer gameProducer)
{
LOGGER.info(“Game producer is set to Game consumer bundle.”);
GameConsumerDataHolder.getInstance().
setGameProducer(gameProducer);
}
We are using GameProducer.class
and we user GameProducer
service and we are holding that service object inside the data holder class. So whenever we want it we can use that service by calling the data-holder class.
Now build your projects using the mvn clean install
command. Navigate to the wso2-game-manager folder. Then open a terminal and execute mvn clean install
.
If the build is succeeded, you could find,
org.wso2.carbon.game.producer-1.0-SNAPSHOT.jar inside wso2-game-manager/org.wso2.carbon.game.producer/target/
org.wso2.carbon.game.consumer-1.0-SNAPSHOT.jar inside wso2-game-manager/org.wso2.carbon.game.consumer/target/
to check the configurations that you added you can open the jar files and look at the manifest.mf file inside META-INF.
As an example, this is the org.wso2.carbon.game.consumer-1.0-SNAPSHOT.jar in the META-INF you can find the manifest.mf file.
and inside OSGI-INF you can find OSGi metadata such as registered services and stuff. Below you can see the org.wso2.carbon.game.producer service that we added that we want to use as a service in the consumer is in the metadata.
<?xml version=”1.0" encoding=”UTF-8"?>
<scr:component xmlns:scr=”http://www.osgi.org/xmlns/scr/v1.1.0" immediate=”true” name=”org.wso2.carbon.game.consumer” activate=”activate”>
<reference name=”org.wso2.carbon.game.producer” interface=”org.wso2.carbon.game.producer.GameProducer” cardinality=”1..1" policy=”dynamic” bind=”setGameProducer” unbind=”unsetGameProducer”/>
<implementation class=”org.wso2.carbon.game.consumer.internal.GameConsumerComponent”/>
</scr:component>
Now we are done with creating OSGi bundles. Let’s deploy and test.
Deploy and Test
We are going to run these OSGi services on the WSO2 platform.
- In your Web browser, go to http://wso2.com/identity-server.
- Click the Download button in the upper right-hand corner of the page to download the latest version. To download an older version, click the Previous Releases link and select the version that you want.
- Enter the required details in the form, and click Download.
- Extract the downloaded zip file. Hereafter I’ll use <IS_HOME> for that extracted folder.
- Navigate to
<IS_HOME>/repository/components/dropins
. - Copy org.wso2.carbon.game.producer-1.0-SNAPSHOT.jar and org.wso2.carbon.game.consumer-1.0-SNAPSHOT.jar to there.
- Then, go to the
<IS_HOME>/bin
folder and start the product as follows.
In Linux Environment
sh wso2server.sh -DosgiConsole
In Windows Environment
wso2server.bat -DosgiConsole
Since we have used the game-producer inside game-consumer when the consumer activates, That requested champion needs to be created before starts the server.
The below-highlighted logs could be seen if your bundles are activated and working properly.
Useful OSGi Commands
For you to know that the bundle is activated or if there are any issues OSGi console have few commands so by using those commands you can know bundle state, bundle details, etc.
ss
→ List down all bundles in the server with their bundle id, life cycle state and the name
ss <name>
→ Search the given name in the bundle's name and list out only relevant bundles.
To use this command press enter few times after the server is started then type the above command, As an example ss game
will show me the bundles which have the “game” sub-string as their bundle name.
right now my server consists of two bundles that have a “game” sub-string in their bundle name. So I can see my two services, and you can see the state of the bundle, right now it is in an “Active” state. So bundles are active and running. If the bundle is not active can use the below commands for further debugging. (Bundle life-cycle is explained here)
b <bundle-id>
→this will show bundle information
so you can see there is an “id” in the above screenshot of the terminal so “id” in there refers to the bundle.
So when using the b <bundle-id>
the command you can get all the information of a particular bundle, it will lists down the details of the selected bundle. Id, status, data root, registered services, services in use, exported packages, imported packages, etc. So, you can check whether the services are registered and relevant packages are imported and exported.
diag <bundle id>
→This shows any unsatisfied constraints of the bundle. So you can realize what are the reasons for not activating the bundle.
It shows any unsatisfied constraints of the bundle. So you can realize what are the reasons for not activating the bundle.
If it says,No resolution report for the bundle
it is good to proceed.
Few Other OSGi Commands
p <package_name>
→ Shows the bundles that export and import the specified package.
ls -c
→ Lists down details of all OSGI components.
ls -c <bundle_id> or comp <component_id>
→ Lists down details all components of the specified bundle, or details specified OSGi components.
services <service_name>
→Displays specified service details.
Below find the full code, 💻 🚀