Getting Started with WSO2 Carbon
By Isuru Suriarachchi
- 29 Dec, 2008
Table of Contents
The Carbon platform is a collection of totally independent components which can be added or removed from a solution dynamically. This behavior is achieved through the use of Open Services Gateway Initiative(OSGi) framework. In OSGi, independent components are called bundles. Therefore, Carbon can also be referred to as a collection of highly dynamic bundles. A product like WSAS is really constructed adding product specific bundles to the base Carbon platform. Even a user could write his own bundle to add in functionality and have it included in a solution without affecting other components.
This article describes the basics of OSGi needed to write your own Carbon components. In addition, we will also show you how to plug that new component back into the system and how to maintain it.
A Carbon Component
As introduced above, WSO2 Carbon is a collection of totally independent components. Those components are called Carbon components. A Carbon component can be added or removed from the Carbon framework any time the system is up and running. These components can use the functionalities and resources exported from other components, and export their resources to the framework.
Carbon components inherit this dynamic behavior through the use of OSGi technology that lies beneath the Carbon framework. Basically a Carbon component is an OSGi bundle. A bundle is a standard jar file which includes OSGi specific meta data in a MANIFEST.MF file which should be located in the META-INF folder of the jar file. Just as normal jar files, it can contains .class files, images, html files etc. Additionally, it can embed other jar files at the root level. Additionally, an OSGi bundle can contain OSGi-INF and OSGi-OPT folders with meta data. Those are optional and rarely used.
Therefore, in simple terms, a Carbon component can be constructed by making a normal jar file containing your logic in .java files, and adding OSGi specific meta data in to the MANIFEST.MF file.
Manifest Headers to be Included in MANIFEST.MF
MANIFEST.MF file contains a set of manifest headers that indicate OSGi meta data. All these headers are defined in the section 3.2.1 of the OSGi specification. Most important headers which are frequently used are described below:
Note : Almost all the Manifest Headers defined in the specification are optional. If some header is mandatory, it is mentioned under the particular header description below.
Bundle-Activator – This header specifies the class used to start and stop the bundle within the OSGi framework. This activator class must implement the BundleActivator interface which has a start method and a stop method. The start method is called by the OSGi framework when the bundle starts after installing it in an environment. If you have any particular logic to be executed at start time, you can include it in this start method. Similarly, stop method is executed when the bundle is uninstalled from the OSGi environment.
It is important to remember that a bundle Activator class is optional for an OSGi bundle. If you don't have any specific logic to be executed on bundle startup or shutdown, you don't have to write an activator class and omit the Bundle-Activator header.
Example - Bundle-Activator: org.wso2.carbon.example.Activator
Bundle-Classpath – This is a comma separated list of directories and jar file path names which exist within the bundle itself. Bundle class loader can see the resources located in these specified locations. The period ('.') is the default value which indicates the entire root directory of the bundle and is also the default value.
Example – Bundle-Classpath: org.wso2.cabon.example, example.jar
Bundle-ManifestVersion – This header indicates the OSGi specification version implemented in this bundle. The default value is 1 and indicates version 3. Value 2 indicates version 4 and higher and it is the version followed by all Carbon related bundles.
Example – Bundle-ManifestVersion: 2
Bundle-SymbolicName – This is the unique name give for the bundle. This is a mandatory header that must be set by the bundle author. Name should be based on the reverse domain name convention.
Example – Bundle-SymbolicName: org.wso2.carbon.example
Bundle-Version – The Bundle-Version header specifies the version of this bundle. The default value is 0.0.0 . Using versions is important when different versions of the same bundle exist in the system. OSGi specification defines a set of version rules under sections 3.2.4 and 3.2.5.
Example – Bundle-Version: 1.0.0
Export-Package – This header lists all the packages exported to the OSGi environment for this bundle. These packages either exist within bundle packages or inside some other jar file which is embedded within this bundle. Once this bundle is activated within the OSGi environment (In our case the Carbon framework), other bundles can use these exported packages by importing them. Each package can be exported with a version (optional) which is specific to that particular package. This version should be specified in front of the package and a semi colon (';') is used to separate the package and the version. Using versions is important when different versions of the same package exist in the system at the same time. All exported packages should be given as a comma separated list.
Example – Export-Package: org.wso2.carbon.foo;version="1.4.0", org.wso2.carbon.bar;version="1.2.1"
Import-Package – This is also a comma separated list of packages with versions (optional). This is the list of packages which are needed by this bundle from other bundles of the OSGi environment. Those packages should be exported from some other bundle before it can be imported. If all the imported packages are not found, this particular bundle will not be activated properly once it is dropped into the OSGi environment.
Example – Import-Package: org.wso2.carbon.foo;version="1.1.0", org.wso2.carbon.bar;version="1.1.6"
Although this is the default behavior, you can make the resolution of a particular package optional. In that case, even if that package is not found in the OSGi environment, bundle will be running.
Example – Import-Package: org.wso2.carbon.foo; resolution:=optional; version="1.1.0"
DynamicImport-Package – This header contains a comma-separated list of package names that should be dynamically imported when needed while the bundle is active within the OSGi environment. These packages are not needed to start the bundle. But at run time these are loaded when needed.
Example – DynamicImport-Package: org.wso2.carbon.example
It is a best practice to use this header for all dynamically needed packages (indicated by '*') when you are writing complex bundles as you may forget to import some packages using Import-Package header.
Example – DynamicImport-Package: *
Private-Package – This header also contains a comma separated list of packages which are not exported to the outside. Those packages are private to the bundle.
Example – Private-Package: org.wso2.carbon.example.internal
Fragment-Host – A bundle can be included into the OSGi environment as an extension to an already existing bundle. In this case, the existing bundle is called the host bundle and the attached bundle is called the fragment bundle. So if your bundle should be a fragment to some other bundle, you have to specify the host bundle under this Fragment-Host header. Resources in the fragment bundle are added to the host bundle class path.
Example – Fragment-Host: org.wso2.carbon.ui
Require-Bundle – If your bundle needs all exports made by some other bundle, you can specify that bundle under this header. If this header is set, your bundle can't be started if the other bundle doesn't exist in the system. It's not a good practice to use this header and best method is to import the needed packages using Import-Package header or DynamicImport-Package.
Example – Require-Bundle: org.wso2.carbon.example
Embed-Dependency – If you want to embed some other jar within your bundle jar, this header should be set. This is a pipe ('|') separated list of artifact ids of the jars to embedded.
Example – Embed-Dependency: org.apache.foo | org.wso2.bar
Manifest headers described above are the most commonly used headers in Carbon components. But there are some other rarely used headers which are defined in the OSGi specification.
Writing the MANIFEST.MF file is a complex task when the bundle is complex. For example, if you have many import and export packages, you have to list all those one by one in the MANIFEST.MF file. WSO2 Carbon uses Maven2 as the build system. Therefore, Maven bundle plugin is used to generate MANIFEST.MF file for all the Carbon components.
Users can write the MANIFEST.MF file manually or using a tool. It will work within the Carbon environment as far as it is written correct. But the easiest way to generate a correct MANIFEST.MF file is to use the Maven bundle plugin.
Using Maven Bundle Plugin
When constructing an OSGi bundle using Maven2, the first step is to write the normal pom.xml with all the needed dependencies and Maven attributes. Refer to Maven2 help guide if you are not familiar with Maven2. After getting the project to build correctly, you can use the Maven bundle plugin under plugins section of the pom.xml to generate the OSGi meta data. You can provide all above explained meta data easily as in the example shown in the figure below. After including those meta data into the pom.xml, all you have to do is to build the project again to build the bundle jar file which includes the generated MANIFEST.MF file.
Some Important Points about Maven Bundle Plugin
The Star ('*') character can be used to indicate all packages under a particular parent package. Bundle plugin will write all matching packages into the MANIFEST.MF file.
Exported packages are removed from the imported list using '!' character to avoid cyclic dependencies as a best practice.
Even though you haven't specified any import packages, bundle plugin will automatically write all external packages used within this bundle, under the Import-Package section of the MANIFEST.MF file.
Plugging Your Carbon Component into WSO2 Carbon
First of all you have to have a WSO2 Carbon binary distribution. The easiest way to have Carbon is to download one of the WSO2 products which runs on Carbon. So lets download WSO2 WSAS. Extract the zip file and copy your bundle into WSAS_HOME/webapps/ROOT/WEB-INF/plugins directory. Now move into bin directory and run the server with -DosgiConsole parameter (refer to README files in the distribution for more details).
Example : sh wso2server.sh -DosgiConsole
This command will start the server and it will provide you with an OSGi console to monitor bundles in the system. Once the server starts, use 'ss' command to list all bundles. It will list all bundles with their statuses as shown in the image below. Please read Chapter 4 of the OSGi specification for details on bundle life cycle.
Now you can check whether your bundle is ACTIVE (or RESOLVED if it is a fragment) by tracking it using the symbolic name you've provided.
Some OSGi Commands to Maintain Carbon Components
There are some OSGi commands useful in troubleshooting. You can use those commands on the OSGi console just like we used 'ss' above. (Ex: osgi> ss)
start <bundle-id> (Ex: osgi> start 18)
This command can be used to start a particular bundle. If there is any trouble starting, reasons will be printed on the console.
b <bundle-id> (Ex: osgi> b 18)
This can be used to print all meta-data related to this bundle. That includes imported packages, exported packages, host bundle, required bundles etc.
diag <bundle-id> (Ex: osgi> diag 18)
This can be used to diagnose a particular bundle. It will show you the list of missing imported packages.
packages <package-name> (Ex: osgi> packages org.wso2.foo)
This can be used to list all bundles which use a give package.
install file:<file-path> (Ex: osgi> install file:/home/temp/bundle1.jar)
This can be used to install a bundle into a running OSGi environment. You can use this command to install your bundle, instead of copying it into WSAS_HOME/webapps/ROOT/WEB-INF/plugins directory prior to starting the server. After installing, use 'start' command to activate the bundle.
uninstall <bundle-id> (Ex: osgi> uninstall 18)
This can be used to remove a bundle from the OSGi environment.
refresh (Ex: osgi> refresh)
This can be used to refresh the system. All package resolutions are refreshed when this is executed. Always refresh the system after uninstalling a bundle as a best practice.
WSO2 Carbon framework is highly dynamic and flexible. This is achieved using OSGi technology. As a user, it is really important to know how to extend a Carbon based product like WSAS in order to add new features. It can be done simply by writing a new Carbon component which is also called an OSGi bundle. This article looked at the basics on writing a bundle and plugging it back to WSO2 Carbon.
Isuru Suriarachchi, Software Engineer, WSO2.Inc, isuru AT wso2 DOT com