4.3 A Simple Echo Service ExampleIn 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 FrameworkThe 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. Figure 4.2 shows the command-line console on desktop and PocketPC devices. Table 4.3 lists some of the most frequently used commands. Figure 4.2. The SMF console on desktop and PocketPC devices.
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. Figure 4.3. The echo bundles in action in J2SE.Figure 4.4. The echo bundles in action in J2ME.Figure 4.5. OSGi bundles with UIs.4.3.2 The EchoService BundleThe 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 an implementation class for the interface (Class EchoserviceImpI, Listing 4.2). Create a BundleActivator, class that implements the required OSGi life-cycle methods and registers the service with the container upon startup (Class EchoActivator, Listing 4.3). Create a manifest file that specifies the BundleActivator class for this bundle and exports the service interface package (Listing 4.4). The OSGi container uses the manifest to find the entry point of the bundle and collect necessary configuration data. Package the compiled classes and manifest file into a standard Jar file.
Listing 4.1. The EchoService interfacepackage com.enterprisej2me.osgi.echoservice; public interface EchoService { public String echo (String s); } Listing 4.2. The EchoServiceImpI classpackage 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 classpackage 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 bundleManifest-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 BundleThe EchoUIConsumer bundle is created to demonstrate how to use the echo service through the framework: 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.5. The EchoUIConsumer classpackage 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 classpackage 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 bundleManifest-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
|