This section discusses bundle writing. The OSGi Service Platform Core Specification defines the main structural elements that a bundle must contain. This document introduces you to the main steps of how to create your own bundles.
Note: It is assumed that you have basic knowledge of the Java programming language and you are acquainted with the OSGi Framework Specification.
Contents:
The paragraphs below discuss the steps that a bundle developer can follow to create a bundle for an OSGi framework. Note that this is only a short guide for developing and running OSGi bundles.
A bundle can provide some functionality to other bundles in the form of services. In such case you go through all steps below.
If your bundle contains Java code, for example for starting and stopping threads, but this code is not intended to be used by other bundles as a service, you can ignore steps 1 and 2.
A bundle can simply export a library to other bundles in the framework - it does not need a Bundle Activator nor should it register services. Hence, you may skip steps 1, 2 and 3.
|
The figure that follows illustrates the stages of the bundle development process.
org.osgi.framework.BundleActivator to define starting and stopping
operations on the bundle.Bundle developers register and access a service in a bundle through the service interface. The service interface defines the methods that the consumers of the service can call.
To clarify, consider the service
interface. Test declares a single method,
Test showHello. The service interface, in particular its Java package,
is exported to other bundles through the Export-Package header in the
bundle manifest file.
package bundles.test.serv; |
You must provide an implementation of the service interface. Later, you should provide an instance of the implementation class when registering the service in the framework.
The interface implementation is the
Test TestImpl class. The showHello method simply
prints a string in the system output. Since the Test interface
is small, there are no additional classes in this implementation.
package bundles.test.serv; |
The Bundle Activator object implements the org.osgi.framework.BundleActivator
interface. In the start/stop method of the Bundle
Activator, the programmer encloses the operations that the bundle executes
when started/stopped. Such operations are:
start is called by the Framework.
stop .
If the operation mode of your bundle does not require any special settings at startup, such as service registration or consumption, it is not necessary to have a Bundle Activator.
After you provide the service interface and its implementation, in the body
of the BundleActivator.start() method you can register the service
with the framework. For this purpose, you use the org.osgi.framework.BundleContext
object passed by the framework. Of course, you can register the service in another
bundle class.
You register a service under the class name of the interface. You pair this class name with an instance of the interface implementation (the so-called service object).
Listing 3.1. contains the TestActivator class that registers a
service under the Test interface class name. TestActivator
provides a service object, implementing the Test interface (a TestImpl
instance).
package bundles.test.serv; |
You can send a specific service object to each requesting bundle by means of
a Service Factory. A Service Factory must implement the org.osgi.framework.ServiceFactory
interface. This interface defines the getService and ungetService
methods. The getService method is invoked by the framework the
first time the specified bundle requests a service object by using the BundleContext.getService(ServiceReference)
method. The ungetService method is invoked by the framework when
a service has been released by a bundle.
The example in this guide defines a Service Factory called TestFactory
. The getService method extracts the ID of the requesting bundle
and prints it to the system output. At each invocation of the service, a new
service object is created and subsequently passed to the requesting bundle.
The ungetService method has an empty implementation for conciseness.
package bundles.test.serv; |
To activate a Service Factory, you pair the service interface with the Service Factory object at service registration instead of with the service interface implementation.
In the example below, the TestActivator class registers a service
using TestFactory.
. . . |
If you need to consume services found in other bundles running in the framework,
you can refer them with the BundleContext object assigned
to your bundle. First, you should call the BundleContext.getServiceReference()
method to obtain org.osgi.framework.ServiceReference for the target
service. Next, you can retrieve the service object with BundleContext.getService
passing the ServiceReference object as the argument. To release
a service object, use BundleContext.ungetService.
You can get the class name(s) under which a service is registered using ServiceReference.getProperty
method with the "objectClass" key.
The AnotherActivator class below is a Bundle Activator that uses
the BundleContext objects in its start and stop
methods to retrieve and free the Test service discussed
in this document. It is assumed that the Test service is in a bundle,
which is running in the framework.
import org.osgi.framework.BundleActivator; |
Troubleshooting:
IllegalAccessException at bundle startup. The framework
throws this exception because it tries to instantiate this class but it cannot
access it.stop method of a thread is
called. In this case, you should explicitly quit the threads of the bundle
in BundleActivator.stop().To produce class files, which the target Java virtual machine will launch, you should naturally compile the bundle's java source files. You can include in the classpath the lib/frameworklib.jar file, which holds the framework, service and utility APIs, related to the mBS Framework Professional Edition Package .
Assuming that the java files are placed in the bundles/test/serv directory, the compilation command using the JDK compiler can be:
javac -classpath .;../../../lib/frameworklib.jar bundles/test/serv/*.java
The bundle developer decides what properties should be put in the manifest file. Bundle properties are reviewed in the OSGi Service Platform Core Specification document. Consider these bundle properties and their values:
Manifest-Version: 1.0 |
Tip: For more information about bundle manifest headers, refer to the OSGi Service Platform Core Specification. You can get more help about manifest files in JARs from Sun Microsystems.
Note: It is best to include all interfaces defined in your bundle and export those packages.
Troubleshooting:
org.osgi.framework.BundleActivator, the framework will
throw a BundleException at bundle startup.You can generate the bundle JAR file using the jar command of
the JDK. Assuming that the class files and the manifest file are located in
bundles/test/serv, the command can be:
jar cmf bundles/test/serv/manifest.mf testbundle.jar bundles/test/serv/*.class
Troubleshooting: If the bundle JAR does not
include the Bundle Activator specified in the manifest, the framework will throw
a ClassNotFoundException at bundle startup.
To activate the bundle, you must install and start it on the OSGi framework, for example by using the Text Console (see "Text Console" and "Framework Commands") or the mConsole (see "OSGi Framework Administration through mConsole" ).
After running the bundle on the ProSyst mBS, you can manage the bundle life cycle by means of console or visual administration.
Troubleshooting: Usually, defining a synchronized method improperly may bring a deadlock situation and cause the server framework to hang. To achieve greater efficiency, most of the framework job is done within the same thread as the requester that caused the deadlock. You can decrease deadlocks if you do not refer the framework in synchronized methods.
This section describes how to invoke native methods in bundles. It assumes basic knowledge on the OSGi Framework Specification and Java Native Interface (JNI).
Basically, using JNI in OSGi bundles is the same as in standalone applications. The only difference is the additional information that needs to be added to the bundle manifest file.
We'll use the source code of the Native Demo to illustrate the steps, described further in this section.
Bundles can use native methods in the conventional way, defined for Java applications.
Basically, before invoking a native method, you should declare it with the native
keyword and statically load the appropriate library with System.loadLibrary.
package demo.nat; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; public class NativeDemoActivator implements BundleActivator{
static {
try{
System.loadLibrary("demonative");
} catch (Throwable t) {
t.printStackTrace();
}
}
private native int multiply (int i, int j); public void start(BundleContext bc) throws Exception {
System.out.println("Native call 6x7: " + multiply(6,7));
}
public void stop (BundleContext bc) {
//does nothing
}
} |
For bundles this step is again the same as with standalone applications. Once
you have compiled the bundle class file(s), run the javah JDK tool
on it. As a result, a header file (with the .h extension) will be produced.
For example:
javah -jni demo.nat.NativeDemoActivator
Write the implementation, usually in the C language, of used native methods according to the signatures, generated in the header file.
#include <jni.h> #include "demo_nat_NativeDemoActivator.h" JNIEXPORT jint JNICALL Java_demo_nat_NativeDemoActivator_multiply (JNIEnv * env, |
Note: Make sure the method signatures in the implementation match the ones from the generated header file.
Through a C compiler, build a native shared library out of the header and implementation source code.
For example:
Using the LCC compiler for Windows:
lc -A -IC:\jdk1.3.1_10\include -IC:\jdk1.3.1_10\include\win32 demo_nat_NativeDemoActivator.c
-dll -o libdemonative.dll *.obj
Using the GCC compiler for Linux:
gcc -I/opt/java/jdk1.3.1/include -I/opt/java/jdk1.3.1/include/linux -I.
-o libdemonative.so --shared demo_nat_NativeDemoActivator.c
In order for the bundle to be able to load and use the native library, the Bundle-NativeCode header needs to be added to the bundle manifest. The header syntax is as follows:
Bundle-NativeCode: <native_clause>[ , <native_clause>] *
<native_clause> ::= <native_path>[ ; native_path; ...] <env_param>;
[env_param; ...]
where <native_path> is the full path to the native library within the bundle JAR, and <env_param> can be one of the following environment properties - osname, osversion, processor, or language.
For example:
Bundle-NativeCode: demonative.dll;
osname=WindowsXP;
osname=Windows2000;
osname=Windows95;
osname=Windows98;
osname=WindowsNT;
processor=x86 ,
libdemonative.so;
osname=Linux;
processor=x86
Tip: For more information on Bundle-NativeCode clauses, check the OSGi Service Platform Core Specification.
The framework applies the following algorithm to load the appropriate library for the environment it resides in:
|
The framework uses the OS and processor names, included in the bundle's Bundle-NativeCode manifest header, for loading the right native libraries for the runtime environment. It is possible that some Java virtual machines return OS and processor names (through standard Java system properties), different from the commercial ones, for example "Windows CE" instead of "WindowsCE". Such non-conventional names are called aliases. In such case, it may happen that a bundle's Bundle-NativeCode native clause, containing commercial names, does not match the environment properties, although they are compatible. As a result the framework won't locate and load the corresponding native code library. For this reason, the OSGi Framework Specification defines that the framework should try to include both aliases and commercial names when determining the native code library, appropriate for the currect execution environment.
It is recommended that Bundle-NativeCode clauses contain commercial names, as they have strict and unified values, while the values returned by different VMs for the characteristics of the same OS and processor may differ.
In case the used virtual machine returns an alias instead of a commercial name,
for the mBS framework you can set the os.aliases and
processor.aliases system properties (see System
Properties) to the OS and processor commercial names, respectively.
The OSGi Framework Specification recommends the following values for OS and processor names and aliases:
You should pack the ready Java class files, shared libraries and manifest in a bundle JAR as described in Step 6. Generate the Bundle JAR File.
Note: Make sure that you have added the native library with the path, defined in the Bundle-NativeCode clause.
Finally, you can install and start the bundle (see Step 7. Install and Start the Bundle).
Note: If the used Java virtual machine supports
the JDK 1.1 storage model, make sure the directory, where native code library
will be extracted from the bundle JAR (given with the mbs.storage.native
system property), is added to the LD_LIBRARY_PATH (for Linux and
similar) or PATH (for Windows) environment variable.