- In an internet driven environment, it is imperative for a company to keep its product or business to the forefront of potential customers' minds. The ...
- A successful enterprise is all about constantly reinventing ways to work more efficiently. In today’s techno age, this translates to testing new too...
- The IT industry plays a pivotal role in providing application development solutions and custom software development to a wide range of industries, i...
- Royal Victorian Eye and Ear Hospital, 2010 Atcomm has been contracted to implement a complex IOP glaucoma management tool which will be distribut...
- We are pleased to announce another implementation of a CMS system for a large Melbourne based fitness center - Star Plate Studio. Atcomm has depl...
Managed Smart Clients
-
font size
decrease font size
increase font size
Managed Smart Clients
4.1 Container-Managed Applications
In the field of software engineering, the term container refers to specialized software that runs other software. For example,
-
The MIDP Application Management Software (AMS) is a container that installs, starts, pauses, stops, updates, and deletes MIDlet applications. In the CDC Personal Basis Profile, the Xlet programming model also features container-managed life-cycle methods.
-
A Java servlet engine is a container that invokes servlets and provides access to the HTTP context.
-
The Java Virtual Machine (JVM) itself is a container. It monitors Java applications for proper memory usage (garbage collector) and security.
In the next two sections, we will discuss the features and benefits of mobile containers.
4.1.1 Container Features
As mobile enterprise applications become mainstream, the complexity of smart clients grows. For example, fully commercial applications often require features such as user login, logging, transaction, and transparent data access. Without proper tools for code and service reuse, mobile developers have to duplicate those functionalities for every smart client. Wasting time reinventing the wheel is not only inefficient but also causes error-prone code.
Hence, it makes sense to make those common features available as services in software containers that run on mobile devices. An advanced container usually provides the following functionalities.
-
Self-contained applications: Applications run inside the container are self-contained with portable code and necessary configuration files. The interdependence of applications and library components could be managed by the container. Examples are the WAR files for servlet containers and EAR files for EJB containers.
-
Life-cycle management: By calling the life-cycle methods defined in the container framework and implemented by all applications, the container can install, start, stop, update, and delete any application programmatically or through an interactive console.
-
Application services: The container provides services that are common to all applications. For example, an authentication module in the container could allow all applications to authenticate against a single password database.
-
Custom services: The container should also allow its applications to offer services to each other. That encourages code reuse and prompts architectures for layered and modularized applications.
In this book, we use the term container rather loosely. Our containers do not impose arbitrary boundaries for API usages. Applications installed inside the container can transparently access any Java or native API available on the device. These containers are often known as frameworks. The container architecture on J2ME mobile devices is illustrated in Figure 4.1
4.1.2 Benefits of Containers
The above container features translate to real benefits in mobile development projects:
-
Reduced code redundancy: Since the common services are not repeatedly implemented, we can reduce overall footprint and potential number of errors while improving the developer productivity.
-
Managed update: When we fix a bug or add a new feature in a service, all applications that use it automatically get the update. Some containers support service versioning for more refined controls.
-
Support for multitiered application models: Services in a container offer natural separations between application tiers (e.g., the presentation and business layers).
-
Simplified application provisioning: Self-contained applications can be easily deployed to any container. That enhances Java's value proposition of "write once, run anywhere."
Given these benefits, containers or frameworks are widely used in mobile Java application development. In the next section, we introduce a standard container specification for lightweight mobile devices: the OSGi specification.
4.2 OSGi Containers
The OSGi Alliance is an industry consortium that creates open standard specifications for network-delivered services. Founded in March 1999, OSGi is a nonprofit organization with open membership. Its board of directors includes Acunia, BMW, Deutsche Telekom, Echelon, Gatespace, IBM, Motorola, Oracle, Philips, ProSyst, Samsung, Sun and Telcordia. The OSGi specification defines the mobile container framework and standard container services as Java APIs that span from J2ME to J2SE to J2EE.
The OSGi Service Platform Release 2 specification was released in October 2001. It has been widely adopted by vendors and has many implementations. The OSGi Service Platform Release 3 specification was made available in March 2003. The IBM Services Management Framework (SMF) v3.5 is targeted to be OSGi R3 compatible. It is available for free evaluation from the IBM Web site (see "Resources"). We discuss both Release 2 and 3 in this chapter. However, all examples are written for and tested on Release 2 containers.
Note
Despite the term Gateway in its name, the OSGi specification does not define any particular kind of gateway servers. It defines a framework for service components delivery and execution. The OSGi container provides the runtime environment for those services. Gateway server is only one of OSGi's application areas.
4.2.1 Bundles
OSGi applications are packaged as bundles, which are just standard JAR files. The OSGi bundle is completely self-contained with all the necessary metadata in its manifest file. The OSGi container completely manages the bundle life cycle:
-
Install, update, and uninstall the bundle.
-
Start and stop the bundle.
-
Register, unregister, and track services in the bundle.
The bundle management interfaces are defined in the org.osgi.framework package. Since the bundles can be deployed to the container dynamically without restarting the container, the OSGi platform is an ideal choice for mobile application provisioning clients. It allows applications to be managed, tracked, and updated throughout its lifetime.
4.2.2 Standard Services
The OSGi container provides common crosscutting services such as device drivers, user preferences, and logging to all its bundles. Table 4.1 lists the OSGi services defined in OSGi Service Platform Release 2 specification. The new OSGi Service Platform Release 3 specification defines more services, some of which are of great importance to mobile applications. Those new services are listed in Table 4.2.

4.2.3 Bundle Interaction and Custom Services
The OSGi framework provides powerful ways for bundles to interact with each other. This encourages code reuse and makes it easier to architect complex multilayer applications. For example, the OSGi container on a stock trader's PDA might be provisioned with services bundles from major exchanges. Each bundle knows how to run real-time queries and execute trades in a specific exchange market, and it makes those functionalities available to other bundles. The trader can then deploy the actual trading client bundle, which provides a user interface, supports custom trade logic, and executes the query/trade through the individual service bundles. The possible interactions among bundles are as follows.
-
Static sharing: The OSGi container runs on a single JVM instance but has a different classloader for each bundle. That means bundle namespaces are separate. We cannot directly access objects or classes in another bundle by default. However, a bundle can explicitly export some of its Java packages through the Export-Package attribute in its manifest file. It can also import Java packages exported by others using the Import-Package manifest attribute. The export and import features allow direct sharing of Java packages.
-
Dynamic services: In addition to standard services provided by the container, any bundle can consume and provide services from/to other bundles at the same time:
-
A bundle can dynamically register (or unregister) services with the container. The bundle needs to register the service interface with a concrete implementation class. Any change to the service (register, modify, unregister) will result in framework events that could be captured and processed.
-
Another bundle finds the service reference through a lookup API in the framework. It calls a framework method to obtain the service implementation object from the service provider bundle. The service object is now ready to use.
-
The interacting bundles allow us to deliver reusable services to any OSGi node, from the high function grid to pervasive devices.
4.2.4 OSGi Runtime Requirements
Currently, different OSGi vendors have different requirements for their products. The required execution environments range from PersonalJava v1.1 to J2SE. This has created considerable confusion in the developer community. In an effort to standardize the runtime requirements, the OSGi Service Platform Release 3 specification formally defines the following runtime environments:
-
The Java 2 Micro Edition: All OSGi implementations should run under the CDC v1.0 plus Foundation Profile v1.0 runtime environment.
-
The OSGi minimum execution environment: The specification also defines a subset of CDC/FP APIs, which allows devices not powerful enough for the CDC/FP (e.g., Palm PDAs) to run the OSGi framework. The OSGi minimum execution environment is defined to be a proper subset of CDC/FP and J2SE.
The standard execution environments make it easier for developers, especially resource-conscious mobile developers, to choose the right OSGi product.
4.3 A Simple Echo Service Example
In this section, we first introduce a J2ME-compatible OSGi implementation from IBM. Using a simple echo example, we demonstrate how to implement bundles and share services among them.
4.3.1 The IBM Service Management Framework
The IBM SMF is a readily available OSGi implementation. It has a memory footprint of 3 MB and runs on both execution environments defined in the OSGi Service Platform Release 3 specification. IBM supports the J2ME environments through WME (WebSphere Micro Environment JVM) and the minimum execution environment through WCE (WebSphere Custom Environment JVM) products. It can be tightly integrated into IBM's WebSphere Studio Device Developer IDE. The SMF product versions we cover in this book are v3.1 for OSGi R2 and v3.5 for OSGi R3.
The SMF installation process varies among devices. It generally involves the following steps.
-
Download and unpack the SMF toolkit from IBM.
-
Copy the following directories and files to the target device (or to a local execution directory, if you want to run SMF on a desktop computer). For my PocketPC device, I put all four items under the device root directory.
-
The jarbundles directory contains installed bundles.
-
The smf.jar file provides implementation classes for the OSGi specification.
-
The smfconsole.jar file provides a command-line management console for the container.
-
The smf.properties file specifies the runtime configuration.
-
-
Make sure that the com.ibm.osg.smf.bundledir property in the smf.properties file points to the correct bundle directory. For example,
com.ibm.osg.smf.bundledir=jarbundles
-
Now we can start the SMF console using the following command (in one line) or its equivalent on the device platform.
java -classpath "smf.jar:smfconsole.jar" com.ibm.osg.smf.SMFLauncher -console "launch"
For my PocketPC device with IBM WebSphere Micro Environment preinstalled, I use the following command (in one line). Please refer to the Appendix B for the steps to install the IBM J2ME runtimes on PDA devices.
"\WSDD\j9.exe" -jcl:foun "-Xbootclasspath:\WSDD\lib\jclFoundation\classes.zip; \smf.jar;\smfconsole.jar" "com.ibm.osg.smf.SMFLauncher" -console "Launch"
After the SMF console is started, it loads all currently installed bundles into the container and presents the user a command-line interface for management tasks. For a complete list of management commands, please refer to the SMF manual. Table 4.3 lists some of the most frequently used commands.

In the next two sections, we describe how to create and deploy two OSGi bundles: The EchoService bundle exposes an echo service; the EchoUIConsumer bundle presents a simple GUI client and uses the EchoService in the container to echo user input. Figures 4.3 and 4.4 show the two bundles in action in J2SE and J2ME OSGi containers. Figure 4.5 shows user interactive OSGi bundles.

4.3.2 The EchoService Bundle
The EchoService bundle demonstrates how to implement and register a service in an OSGi bundle. The service itself is extremely simple: It only defines one method that does nothing more than echo a string input. The steps to create the bundle are as follows.
Define the service interface as a Java interface (Interface EchoService, Listing 4.1).
-
Create a BundleActivator implementation as the entry point to the bundle (Class EchoUIConsumer, Listing 4.5).
-
In the EchoUIConsumer.start() method, create and open a ServiceTracker object to track the echo service we started.
-
Create the UI frame class EchoFrame (Listing 4.6) and pass the ServiceTracker object and the current bundle (i.e., the echo consumer bundle) to EchoFrame.
-
The EchoFrame object obtains the EchoService object from the ServiceTracker and uses the EchoService to echo any user input. When we hit the Exit button in the UI frame, the AWT event handler calls the bundle's stop() method and triggers the container to invoke the EchoUIConsumer.stop() method. For more details, refer to method actionPerformed() in Listing 4.6.
-
In the EchoUIConsumer.stop() method, dispose the UI frame and close the ServiceTracker (Listing 4.5).
-
Create the manifest file (Listing 4.7). We import the package containing the EchoService interface here.
-
Package and deploy the JAR bundle.
Listing 4.1. The EchoService interface
package com.enterprisej2me.osgi.echoservice;
public interface EchoService {
public String echo (String s);
}
Listing 4.2. The EchoServiceImpI class
package com.enterprisej2me.osgi.echoserviceimpl;
import com.enterprisej2me.osgi.echoservice.*;
public class EchoServiceImpl implements EchoService {
EchoServiceImpl () { }
public String echo (String s) {
return s;
}
}
Listing 4.3. The EchoActivator class
package com.enterprisej2me.osgi.echoserviceimpl;
import org.osgi.framework.*;
import com.enterprisej2me.osgi.echoservice.*;
public class EchoActivator implements BundleActivator {
private ServiceRegistration reg;
public EchoActivator () { }
public void start (BundleContext context) throws Exception {
EchoServiceImpl impl = new EchoServiceImpl ();
reg = context.registerService (
EchoService.class.getName(), impl, null);
}
public void stop (BundleContext context) throws Exception {
reg.unregister ();
}
}
Listing 4.4. The JAR manifest for the echo service bundle
Manifest-Version: 1.0 Bundle-Name: Echo service Bundle-Description: Echo the input Bundle-Activator: com.enterprisej2me.osgi.echoserviceimpl.EchoActivator Import-Package: org.osgi.framework; specification-version=1.1 Export-Package: com.enterprisej2me.osgi.echoservice Export-Service: com.enterprisej2me.osgi.echoservice.EchoSerivce
Now, we can install and start the package in our SMF console.
4.3.3 The EchoUIConsumer Bundle
The EchoUIConsumer bundle is created to demonstrate how to use the echo service through the framework:
Tracking the ServicesIn the service consumer bundles, we could manually look up the service objects from the framework. Then, we would have to register event listener and callback methods to handle situations when other bundles or the container itself changes those services (e.g., removes the service). This could be a tedious task. Instead, we take a shortcut and use a pair of ServiceTracker objects to automatically track those services. The ServiceTracker object tracks a list of services meeting certain criteria passed to it in the constructor. It provides default event handlers for the services it tracks. The ServiceTracker object can be instantiated with a ServiceTrackerCustomizer object. When a service in the tracker is added, modified, or deleted, the appropriate method in its associated ServiceTrackerCustomizer is called. For more usage examples of the ServiceTracker class, please refer to Section 4.4. |
Listing 4.5. The EchoUIConsumer class
package com.enterprisej2me.osgi.echouiconsumer;
import org.osgi.framework.*;
import org.osgi.util.tracker.*;
import com.enterprisej2me.osgi.echoservice.*;
public class EchoUIConsumer implements BundleActivator {
ServiceTracker echoTracker;
EchoFrame frame;
public EchoUIConsumer () { }
public void start (BundleContext context) {
echoTracker = new ServiceTracker (context,
EchoService.class.getName(), null );
echoTracker.open ();
frame = new EchoFrame(250, 250, echoTracker, context.getBundle());
}
public void stop (BundleContext context) {
frame.dispose ();
echoTracker.close();
}
}
Listing 4.6. The EchoFrame class
package com.enterprisej2me.osgi.echouiconsumer;
import java.awt.*;
import java.awt.event.*;
import org.osgi.framework.*;
import org.osgi.util.tracker.*;
import com.enterprisej2me.osgi.echoservice.*;
public class EchoFrame extends Frame
implements WindowListener, ActionListener {
private TextField entryText;
private Label echoedText;
private Button submit;
private Button exit;
private Panel content, top, bottom, middle;
private ServiceTracker echoTracker;
private Bundle echoUIConsumerBundle;
public EchoFrame (int width, int height,
ServiceTracker t, Bundle b) {
super ("Echo UI");
setBounds(0, 0, width, height);
echoTracker = t;
echoUIConsumerBundle = b;
entryText = new TextField (20);
echoedText = new Label (" ");
submit = new Button ("Echo");
exit = new Button ("Exit");
submit.addActionListener (this);
exit.addActionListener (this);
top = new Panel ();
middle = new Panel ();
bottom = new Panel ();
top.setLayout(new FlowLayout(FlowLayout.LEFT));
top.add(new Label("Echo: "));
top.add(echoedText);
middle.setLayout(new FlowLayout(FlowLayout.LEFT));
middle.add(new Label("Input: "));
middle.add(entryText);
bottom.setLayout(new FlowLayout(FlowLayout.CENTER));
bottom.add(submit);
bottom.add(exit);
content = new Panel ();
content.setLayout(new GridLayout(3, 1));
content.add(top);
content.add(middle);
content.add(bottom);
add (content);
addWindowListener(this);
pack ();
setVisible (true);
}
public void actionPerformed (ActionEvent e) {
if ( e.getSource() == submit ) {
top.remove (echoedText);
// Obtain the echo service object
EchoService echoObj = (EchoService) echoTracker.getService();
// Use the echo service to echo a string
echoedText = new Label ( echoObj.echo(entryText.getText()) );
top.add (echoedText);
entryText.setText("");
setVisible (true);
} else if ( e.getSource() == exit ) {
// see note
// echoUIConsumerBundle.stop();
dispose ();
}
}
public void windowClosing(WindowEvent e) {}
public void windowOpened(WindowEvent e) {}
public void windowClosed(WindowEvent e) {}
public void windowIconified(WindowEvent e) {}
public void windowDeiconified(WindowEvent e) {}
public void windowActivated(WindowEvent e) {}
public void windowDeactivated(WindowEvent e) {}
}
Listing 4.7. The manifest file for the echo consumer bundle
Manifest-Version: 1.0 Bundle-Name: Echo UI consumer Bundle-Description: Consume the echo service Bundle-Activator: com.enterprisej2me.osgi.echouiconsumer.EchoUIConsumer Import-Package: com.enterprisej2me.osgi.echoservice, org.osgi.framework; specification-version=1.1, org.osgi.util.tracker; specification-version=1.1 Import-Service: com.enterprisej2me.osgi.echoservice.EchoSerivce
4.4 Smart Client with HTTP Front End
The managed GUI bundle uses only a fraction of the power provided by the OSGi container. Through its services, the OSGi container supports external applications and devices over the network. To use a separate program to render the UI, we can more effectively separate the business and presentation layers.
The "pizza order" example application distributed with the SMF illustrates the use of HTTP services in the OSGi framework. After starting the bundle from the SMF console, we can launch the device's built-in HTML browser (Internet Explorer for PocketPC or the Opera browser for Embedded Linux) and point the URL to http://localhost/pizza. An HTML page of a dummy pizza store appears. We can fill out the pizza order form, submit the form, and get response from a servlet running inside the bundle.
This design allows us to build a simple UI very quickly, using HTML without messaging with complex event handlers in AWT code. It also allows the vast majority of serverside Java developers to transfer their skills and make use of their familiar patterns, such as the Model-Viewer-Controller pattern. The overall architecture of the smart client with HTTP front end is illustrated in Figure 4.7.
4.4.1 The Pizza Order Bundle
The PizzaBundle class (Listing 4.8) implements the BundleActivator interface. This bundle does not register or provide any new services. It customizes the container HTTP service to serve pizza order HTML content at a specified URL. It also uses the container logging services to record activities inside the bundle. If no logging service has been registered for this bundle, it logs to the standard output.
-
The bundle start() method instantiates trackers for LogService and HttpService. The bundle then invokes their open() methods.
-
When the ServiceTracker for the HttpService is opened, it obtains all registered HttpService references from the container, adds them into the tracker, and invokes the corresponding ServiceTrackerCustomizer's addingService() method for each added reference.
-
The addingService() method obtains the HTTP service object from the container and customizes it with the pizza order servlet and other resources.
-
When the bundle stops, its stop() method calls the close() methods of the two ServiceTrackers. The HttpService tracker in turn calls the removedService() method, which unplugs the servlet from the HTTP service.
Listing 4.8. The PizzaBundle class
public class PizzaBundle implements
BundleActivator, ServiceTrackerCustomizer {
/** BundleContext for this bundle */
protected BundleContext context;
/** Log Service wrapper object */
protected LogTracker log;
/** Http Service tracker object */
protected ServiceTracker tracker;
/** HttpContext for HTTP registrations */
protected HttpContext httpContext;
// ... ...
public PizzaBundle() { }
// Methods in BundleActivator
public void start(BundleContext context) throws Exception {
this.context = context;
httpContext = new HttpContext() { ... ... };
log = new LogTracker(context, System.err);
tracker = new ServiceTracker(context,
HttpService.class.getName(), this);
tracker.open();
}
public void stop(BundleContext context) throws Exception {
tracker.close();
log.close();
}
// Methods for ServiceTrackerCustomizer
public Object addingService(ServiceReference reference) {
HttpService http = (HttpService)context.getService(reference);
if (http != null) {
try {
http.registerServlet(servletURI,
new Pizza(), null, httpContext);
http.registerResources(servletURI+imagesURI,
imagesURI, httpContext);
log.log(log.LOG_INFO, "Pizza Servlet registered");
} catch (Exception e) {
// handle the exception
}
}
return http;
}
public void modifiedService(ServiceReference
reference, Object service) {
}
public void removedService(ServiceReference
reference, Object service) {
HttpService http = (HttpService) service;
http.unregister(servletURI);
http.unregister(servletURI+imagesURI);
context.ungetService(reference);
log.log(log.LOG_INFO, "Pizza Servlet unregistered");
}
}
4.4.2 The Pizza Order Servlet
In the addingService() method, we use a servlet Pizza (Listing 4.9) to provide the custom HTTP service (the application logic). This is just a standard Java servlet that reads from HttpRequest and writes HTML data to HttpResponse objects. Those HTTP context objects are provided by the container.
Listing 4.9. The Pizza servlet
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
public class Pizza extends HttpServlet {
protected void doGet(HttpServletRequest req,
HttpServletResponse res)
throws ServletException, IOException {
// Generate some output
res.setContentType("text/html;" + "charset=iso-8859-1");
PrintWriter out = res.getWriter();
out.print(" ... ... ");
// Get query parameters
String queryString = req.getQueryString();
// any pizza order logic
// ... ...
out.println("