 | Level: Intermediate Xing Xing Li (lixx@cn.ibm.com), Software Engineer, IBM Yu Peng (pengyu@cn.ibm.com), Staff Software Engineer, IBM Jian Lin (jianlin@cn.ibm.com), Manager, IBM
13 May 2008 Learn how to fill the gap between the IBM® Rational® Functional Tester and the
console of Eclipse-based products by supporting the OSGi commands install, ss, start,
stop, headers, active, update, and uninstall. The solution offers an effective approach
for automation test-case support when the manifest of an Eclipse-AutoStart header has
been upgraded to Eclipse-LazyStart. This article presents test scenarios to verify that
the bundle-management mechanisms work well.
Soon after our test team upgraded to Eclipse V3.2, we discovered that Eclipse no
longer supported the AutoStart header in our test cases. The Eclipse Foundation
had replaced AutoStart with LazyStart, an adoption of the lazy-activation policy in the
OSGi R4.1 specification. A downside of this change is that we
found it difficult to trigger the automated objective bundles in LazyStart.
Legacy automation test cases need to expose a resource to be loaded by a trigger
bundle. Since updating all of our legacy test cases would be clumsy and
time-consuming, we decided to take a different approach.
One possibility was the Eclipse console, a powerful tool to manipulate a bundle's
life-cycle management. However, Rational Functional Tester does not recognize the
Eclipse console. Our solution was to design and implement the GUI Console.
A gap in the automated testing
Eclipse is a popular IDE for developing applications and, thanks to the Rich Client
Platform (RCP), Eclipse is a runtime platform for a growing list of applications,
including IBM Notes® Client, Sametime®, and Expeditor. (See Resources if you are new to Eclipse and need background information
on Eclipse's capabilities.) During an automated test-case practice, however, testers
confront a serious problem trying to leverage automation tools, such as IBM Rational
Functional Tester, to develop automation test cases for Eclipse RCP-based products. (As
for Rational Functional Tester's object-recognition details, download "Grabbing GUI objects with IBM Rational Functional Tester.")
IBM Rational Functional Tester can't recognize the console of Eclipse or Eclipse-based
products. When automation testers try to grab the console object of Eclipse by IBM
Rational Functional Tester, it fails to recognize the console. Consequently, automation
testers can't continue with subsequent tasks. Figure 1 is a screenshot of IBM Rational
Functional Tester failing to recognize the console of Eclipse. The expected action of
the recognition of the console by Rational Functional Tester is a red rectangle around
the console in the Figure 1. We would see something like this when Rational Functional
Tester recognizes other widgets in Eclipse, such as the task window, menu bar, and combo list.
Figure 1. Inability of Rational Functional
Tester to recognize the Eclipse console
Consequently, the Rational Functional Tester's failure to recognize the Eclipse console
is fatal because automated tools need the console to perform the following two tasks.
-
Bundle operations
- For obvious reasons, we need to check and track the status of target bundles in
Eclipse RCP-based products. But without the console's help, automation testers must
develop and deploy new manual tests. Testers must translate the old automated tests
into manual test cases. The test scenarios must install, uninstall, start, stop, and
target bundles in RCP applications. This can be accomplished with the console's
support. Commonly, usage scenarios invoke these commands in the console, which offers a
mechanism for retrieving echoed results.
-
Collect diagnostic information
- When Eclipse runs into an error, it should print exceptions and errors to the console.
Such information is critical in diagnosing the root cause of a problem. Developers
always want testers to provide such info for each software problem report, but Rational
Functional Tester fails to collect them during the automated test. It means the
tester needs to rerun the test manually to collect this information. This is virtually
impossible for long-running and stress tests.
To fix the gap, we proposed a solution we call the GUI Console.
Requirement identification
Bundle life-cycle management: AutoStart vs. LazyStart
In the MANIFEST.MF headers, AutoStart and LazyStart may be considered synonyms for
the same bundle-manifest header, except that the former is deprecated and the latter is
preferred. During the fixing of the 537 bugs in Eclipse V3.1.2, which brought us to
Eclipse Europa, AutoStart fell to the wayside. Today, it's all about OSGi V3.2
compliance and LazyStart (see Resources to learn more about how
a bundle carries descriptive information about itself in the MANIFEST.MF file).
In Eclipse V3.2, the LazyStart header defines whether a bundle will be started
automatically before its class or if a resource is accessed by other bundles. By
setting the value to true, we can activate a bundle lazily — in other
words, automatically — the first time someone tries to load a class or resource.
As we described, even though it's logical to migrate all legacy test cases to
LazyStart, doing so would be a huge, clumsy effort. Updating all test cases to
support AutoStart would expose one resource to them and load them by a trigger
bundle. Besides, starting a bundle does not cover all requirements of our automation
test cases. Bundling state management — in other words "bundling life-cycle
manipulating" — involves the verification points in our test cases.
Bundle life-cycle management:
Bundle state
A bundle, including bundles in our automation test cases, can be in only one of the
following six states at a time.
-
INSTALLED
- A bundle can enter the INSTALLED state when it's successfully installed.
-
RESOLVED
- A bundle can enter the RESOLVED state when its needed Java classes are available. In
other words, this state indicates that the framework has successfully resolved the
bundle's dependencies as described in the manifest. The RESOLVED state comes from the
INSTALLED or ACTIVE state and leads to ACTIVE.
-
STARTING
- A bundle can enter the STARTING state when it's being started (when the
BundleActivator.start() method has been called, but has not yet returned).
-
ACTIVE
- A bundle can enter the ACTIVE state when the bundle has been launched and is serving.
-
STOPPING
- A bundle can enter the STOPPING state when the bundle is being stopped (when the
BundleActivator.stop() method has been called, but has not yet
returned).
-
UNINSTALLED
- A bundle can enter the UNINSTALLED state when the bundle has been uninstalled. It
cannot move into another state.
The Bundle interface defines a getState() method for returning a bundle's state.
Figure 2 demonstrates all states of a bundle in its life cycle, as well as its transition paths.
Figure 2. OSGi bundle states
Retrieving manifest headers
The headers in MANIFEST.MF are another part of the GUI Console foundation. Eclipse
expects bundle developers to provide descriptive information about a bundle in a
manifest file named MANIFEST.MF. This is the OSGi file that defines manifest headers,
such as Export-Package and Bundle-Classpath descriptions. Table 1 lists the most useful
headers in an OSGi bundle.
Table 1. An OSGi bundle's headers
| OSGi bundle header | Description |
|---|
| Bundle-Activator | Specifies the name of the class used to start and stop the bundle. |
|---|
| Bundle-Classpath | Specifies the JAR file or directories containing classes and resources. The
period ('.'), the default value, specifies the root directory of the bundle's JAR. |
|---|
| Bundle-ContactAddress | Contains the contact address of the vendor. |
|---|
| Bundle-Copyright | Contains the copyright specification for this bundle. |
|---|
| Bundle-DocURL | Specifies a URL pointing to documentation about this bundle. |
|---|
| Bundle-Localization | Specifies the location of the bundle's localization files, whose default value is OSGI-INF/l10n/bundle. |
|---|
| Bundle-ManifestVersion | Specifies that the bundle follows the rules of OSGi specification V3 or OSGi
specification V4. |
|---|
| Bundle-Name | Specifies the bundle's readable name with no spaces. |
|---|
| Bundle-SymbolicName | A mandatory header to specify a unique name for this bundle. |
|---|
| Bundle-Vendor | Contains a readable name of the bundle vendor. |
|---|
| Bundle-Version | Specifies the bundle's version, whose default value is 0.0.0. |
|---|
| Export-Package | Specifies exported packages from this bundle. |
|---|
| Fragment-Host | Defines the host bundle for this fragment. |
|---|
| Import-Package | Declares the imported packages for this bundle. |
|---|
| Require-Bundle | Specifies the required exports from another bundle. |
|---|
| Import-Service | Deprecated |
|---|
| Export-Service | Deprecated |
|---|
Requirement summary
Based on the above requirement analysis, the GUI Console solution is asked to provide
the following commands to support OSGi bundle management for our automation test cases.
Table 2. OSGi commands supported by the GUI Console
| Command | Description |
|---|
| install+bundle URL | Adds a bundle with the given URL into the current platform. |
|---|
| uninstall+bundle ID | Remove a specified bundle from the current platform. |
|---|
| ss | Lists a short status of all bundles registered in the current platform. |
|---|
start+bundle ID; start+bundle name | Launches a bundle with the given bundle ID or symbolic name. |
|---|
stop+bundle ID; stop+bundle name | Terminates a bundle with the given bundle ID or symbolic name. |
|---|
| headers | Lists manifest headers for a bundle given an ID or symbolic name. |
|---|
| active | Lists all active bundles in the current platform. |
|---|
| update | Updates a bundle for the current instance. |
|---|
Design and implementation
In this section, we will show why the BundleContext object and the bundle object will
be selected as the pivotal figures in our GUI Console solution, and we will discuss bundle management in OSGi.
The BundleContext
BundleContext is the bridge to connect the Eclipse framework and the installed
bundles in it. A BundleContext object represents the execution context of a bundle
within the OSGi platform and acts as a proxy to the underlying framework.
When a bundle is started, a BundleContext object will be created by the framework and
provided as an argument to the start(BundleContext) method
of the bundle's Bundle-Activator. The bundle can use this private BundleContext object
for:
- Installing new bundles into the OSGi environment.
- Interrogating other installed bundles in the OSGi environment.
- Obtaining a persistent storage area.
- Retrieving service objects of registered services.
- Registering services in the framework service.
- Subscribing or unsubscribing to events broadcast by the framework.
Each bundle has its own BundleContext object, and they should not be passed between
bundles. Why? The the BundleContext object is related to the security and
resource-allocation aspects of a bundle. When the Bundle-Activator's stop(BundleContext) method is returned, the BundleContext object is out of service.
The most useful point of BundleContext is that it defines methods to retrieve
information about bundles installed in the OSGi Service Platform:
-
getBundle()
- Returns the single bundle object associated with the BundleContext object.
-
getBundles()
- Returns an array of bundles currently installed in the framework.
-
getBundle(long)
- Returns the bundle object specified by the unique identifier, or null if no matching bundle is found.
Because there are no restrictions for bundle access, any bundle can enumerate the set
of installed bundles. This allows us to conduct and manipulate our automation test
case's bundle life-cycle management operations and bundle information-retrieval
operations. The GUI Console will retrieve all active bundle information using the code
in Listing 1. It functions the same as the Eclipse console's active command.
Listing 1. Active command implementation code
public static void doActive() throws Exception {
b = bContext.getBundles();
for (int i = 0; i > b.length; i++) {
if (b[i].getState() == b[i].ACTIVE) {
result = result + b[i].getLocation()+" " + "["+b[i].getBundleId() +"]"
+System.getProperty ("line.separator");
}
}
result=result+p+" active bundle(s).";
}
|
As for the ever-popular ss command, the GUI Console can
implement it based on the above code only if we accept all bundles with respective
states, including INSTALLED, RESOLVED, and ACTIVE. See the sample code for details.
Bundle object
To manage a bundle's life cycle in the OSGi platform with Eclipse-based products, we
must leverage an associated bundle object for our target bundle. The BundleContext
interface provides the following methods for installing a bundle.
-
installBundle(String)
- Installs a bundle from the specified location string (a URL).
-
installBundle(String,InputStream)
- Installs a bundle from the specified InputStream object.
When a bundle is installed successfully, a bundle object will be generated for it, and
all operations for life-cycle management must be performed with this object, such as
start, stop, and uninstall.
As we see in Listing 2, we can install a bundle with its location
string provided. If the bundle is installed successfully in the platform, it will return the symbolic name of the bundle.
Listing 2. Active command implementation
public static String doInstall(String location) throws Exception{
...
Bundle iBundle = bContext.installBundle(location);
if(iBundle!=null){
iBundle.update();
return iBundle.getSymbolicName();
}
return null;
}
|
Starting bundles
The start() method is defined by the bundle interface to
start a bundle and takes a bundle from the RESOLVED state to the ACTIVE state. The
prerequisite is that the bundle must have been resolved successfully; otherwise, a
BundleException will be thrown.
To execute a bundle's start() method, we need to inform the
OSGi environment of the class name of the Bundle-Activator by the Bundle-Activator
header in the bundle's manifest file. The OSGi environment will instantiate a new
object of this class and cast it to a Bundle-Activator instance. Then it will call the
BundleActivator.start() method to start the bundle.
As a Bundle-Activator, the class in the bundle must implement the Bundle-Activator
interface, with it declared as public and a public default constructor. However, it's
optional to provide a Bundle-Activator in each bundle. For example, a library bundle
exporting a small number of packages does not need to define a Bundle-Activator.
Our GUI Console application invokes the following code to start a bundle when the bundle ID or symbolic name is provided.
Listing 3. Start command implementation
public static void doStart(int bID) throws Exception {
if(bContext.getBundle(bID)!=null&&bContext.getBundle(bID).state==
bContext.getBundle(bID).RESOLVED){
bContext.getBundle(bID).start();
}
}
public static void doStart(String s,String matchS) throws Exception {
b = bContext.getBundles();
...
for (int i = 0; i < b.length; i++) {
if (b[i].getSymbolicName().indexOf(s) >-1 && (b[i].getState() ==
b[i].RESOLVED) {
boolean isFragment=false;
Enumeration eKey = b[i].getHeaders().keys();
Dictionary dValue = b[i].getHeaders();
Enumeration eValue = dValue.elements();
while (eKey.hasMoreElements() && eValue.hasMoreElements()) {
String sKey = eKey.nextElement().toString();
if (sKey.equalsIgnoreCase("Fragment-Host")) {
isFragment=true;
}
}
if (isFragment==false){
b[i].start();
}
}
}
|
Stopping bundles
The stop() method is defined by the bundle interface to stop
a bundle, resulting in the RESOLVED state. All threads associated with the stopping
bundle should be stopped immediately.
Uninstalling bundles
The uninstall() method is provided by the bundle interface
to uninstall a bundle from the framework. The framework will inform other bundles that
the target bundle is being uninstalled, remove any resources related to the bundle, and
set the target bundle's state to UNINSTALLED.
Packages of uninstalled bundles must not be used by newly installed bundles. However,
if the uninstalled bundle has ever exported any packages used by other bundles, the
framework will continue to make these packages available until the framework is
restarted or the org.osgi.service.packageadmin.PackageAdmin.refreshPackages() method has been called.
The code below demonstrates how the GUI Console uninstalls a bundle from the platform
with a bundle ID or symbolic name provided.
Listing 4. Uninstall command implementation
public static void doUninstall(String s, String matchS) throws Exception{
b = bContext.getBundles();
...
if (b[i].getSymbolicName().indexOf(s) >-1) {
....
b[i].uninstall();
}
...
}
public static void doUninstall(int bID) throws Exception{
if (bContext.getBundle(bID)!=null){
bContext.getBundle(bID).uninstall();
}
}
|
Retrieving manifest headers
Two methods are provided by the bundle interface to return manifest header information:
-
getHeaders()
- Returns a dictionary object containing the bundle's manifest headers and values (key-value pairs).
-
getHeaders(String)
- Returns a dictionary object containing the bundle's manifest headers and values(key-value pairs).
Even when a bundle enters the UNINSTALLED state, the getHeaders method can continue to provide the manifest header information.
Listing 5. Headers command implementation
Enumeration eKey = b[i].getHeaders().keys();
Dictionary dValue = b[i].getHeaders();
Enumeration eValue = dValue.elements();
while (eKey.hasMoreElements() && eValue.hasMoreElements()) {
String sKey = eKey.nextElement().toString();
String sValue = eValue.nextElement().toString();
headers.append(sKey+" = ");
headers.append(sValue+"\n");
}
|
Execution
We execute GUI Console test scenarios by using IBM Expeditor, an Eclipse-based product
(see Resources). It require us to install
the GUI Console into Expeditor or any other Eclipse-based product before our
verification scenarios. After that, as shown in Figure 3, we use a sample bundle named
PascalTriangle as the manipulated bundle during our execution. With its JAR file
exported in the file system, we will explore the bundle-management actions on the GUI
Console, including install, start, stop, uninstall, and others.
One advantage of the GUI Console is that the bundle's symbolic name and bundle ID will
be automatically supported during command interpretation and execution. When a bundle
formatted as an exported JAR file is installed, the symbolic name and the bundle ID are
retrieved programmatically. What's more, to provide the maximum amount of flexibility
for automation test execution, you can search for a symbolic name using simple regex
techniques, including start with, contains, and perfect match.
Figure 3. Manipulated bundle: Pascal
bundle JAR file exported for testing
To demonstrate the function to support OSGi's install command, we will import the
manipulated bundle into Expeditor, or any other Eclipse-based product, already equipped
with the GUI Console application. In the GUI Console application, press Browse,
navigate to where the PascalTriangle bundle JAR file resides in
the file system and press OK. After you have verified the location address of
your target bundle in the text field, press Install. If your PascalTriangle
bundle can be loaded and resolved successfully by the GUI Console and the OSGi
platform, you will see a bundle ID designated.
Figure 4. Install command scenario
Check to see if the bundle's status is resolved using the ss
command (press the ss button), as shown below.
Figure 5. ss command scenario
To launch the bundle, press Start after the target bundle is
installed and its symbolic name is retrieved by the text field automatically. The
execution result of starting PascalTriangle bundle can be verified as shown below.
Figure 6. Start command scenario
To see the headers' details in a bundle's MANIFEST file, press the headers
button. Consequently, you can view all available headers and their values in your
target bundle. Figure 7 gives a demonstration for the headers command for the PascalTriangle bundle.
Figure 7. Headers command scenario
Sometimes you need to list all bundles of ACTIVE status on your platform. For an
overview, press Active. See Figure 8 for details.
Figure 8. Active command scenario
As shown below, we find that the GUI Console bundle and the PascalTriangle bundle are
listed at the end of the display, with their bundle IDs provided.
Figure 9. Active command scenario
Figure 10 also demonstrates the Update command when you
substitute the target bundle's JAR file with an updated one. We can find the updated
PascalTriangle printed after we update it.
Figure 10. Update command scenario
The action of uninstalling a bundle is shown below. The result is verified by the ss command to search the PascalTriangle bundle by its symbolic name.
As we can see, no more PascalTriangle bundle exists in the OSGi platform after we uninstall it.
Figure 11. Uninstall command scenario
Summary
IBM Rational Functional Tester does not recognize the Eclipse console. Starting with
V3.2, Eclipse no longer supports the Eclipse-AutoStart header in legacy test cases. To
fix the gap, we demonstrated a solution we call the GUI Console that works with the new Eclipse-LazyStart.
Download | Description | Name | Size | Download method |
|---|
| Sample code | os-eclipse-bundlemgmt_source.zip | 34KB | HTTP |
|---|
Resources Learn
-
The Eclipse Foundation article "PDE Does
Plug-ins" offers a description of how to develop applications in Eclipse's PDE.
-
"Explore
Eclipse's OSGi console" introduces the basic elements of the console in Eclipse.
-
The OSGi Web site is the portal to access OSGi fundamental and advanced technologies.
-
OSGi Service
introduction describes the OSGi service-related topics.
-
Read "Learn how to
create robust, reusable automated functional tests with Rational Functional Tester"
for an introduction to IBM Rational Functional Tester.
-
Read "Brand your
Eclipse RCP applications" to explore the powerful
production configuration feature in Eclipse V3.1 to brand and publish your RCP application easily.
-
If you have questions or interest in IBM Expeditor, visit
the InfoCenter
for IBM Expeditor V6.1.x. IBM Lotus Expeditor software is a service-oriented architecture-based, server-managed client platform that helps improve responsiveness and increase productivity by extending composite applications to laptops, desktops, kiosks, and mobile devices.
-
Check out the "Recommended Eclipse reading list."
-
Browse all the Eclipse content on developerWorks.
-
New to Eclipse? Read the developerWorks article "Get started with Eclipse Platform" to learn its origin and architecture, and how to extend Eclipse with plug-ins.
-
Expand your Eclipse skills by checking out IBM developerWorks' Eclipse project resources.
-
To listen to interesting interviews and discussions for software developers, check out check out developerWorks podcasts.
-
Stay current with developerWorks' Technical events and webcasts.
-
Watch and learn about IBM and open source technologies and product functions with the no-cost developerWorks On demand demos.
-
Check out upcoming conferences, trade shows, webcasts, and other Events around the world that are of interest to IBM open source developers.
-
Visit the developerWorks Open source zone for extensive how-to information, tools, and project updates to help you develop with open source technologies and use them with IBM's products.
Get products and technologies
Discuss
-
The Eclipse Platform newsgroups should be your first stop to discuss questions regarding Eclipse. (Selecting this will launch your default Usenet news reader application and open eclipse.platform.)
-
The Eclipse newsgroups has many resources for people interested in using and extending Eclipse.
-
Participate in developerWorks blogs and get involved in the developerWorks community.
About the authors  | 
|  | Xing Xing Li joined the IBM China Software Development Lab in Beijing China in 2004 as a software engineer for IBM Lotus. He focuses on software development and testing on Eclipse platform and Java technologies. He is also interested in user interface design, design pattern and algorithmic research. He received his master's degree in computer science from Chongqing University. |
 | 
|  | Peng Yu joined the IBM Lotus branch of the China Software Development Lab in
Bejing in 2004 as a software engineer. He focuses on software testing on the
Eclipse platform and Java technologies. He is also interested in user-interface
design, design pattern and algorithmic research. He received his master's degree
in computer science from NanKai University. |
 | 
|  | Jian Lin is the Manager of Pervasive Computing Device Software Development and Services, IBM China Software Development Lab. He can be reached at jianlin@cn.ibm.com. |
Rate this page
|  |