by Eric Giguere
February 05, 2003
The Personal Basis Profile (PBP) - and by extension, the Personal Profile (PP) - defines a new application model called the Xlet model. One appealing feature is that Xlets running on the same device can communicate with each other using inter-Xlet communication, IXC for short.
IXC is similar to Remote Method Invocation (RMI), the Java interface that lets Java applications invoke methods of remote objects. IXC visibly reflects this similarity by reusing a number of RMI classes:
java.rmi.AccessException
java.rmi.AlreadyBoundException
java.rmi.NotBoundException
java.rmi.Remote
java.rmi.RemoteException
java.rmi.UnexpectedException
java.rmi.registry.Registry
Note, though, that the presence of these classes does not imply that PBP supports RMI. For full RMI support, J2ME applications need the RMI Optional Package. For more information about optional packages in general, see the article J2ME Optional Packages.
IXC is done using Java objects that implement a remote interface, an interface that extends java.rmi.Remote. Each method defined in the remote interface must follow certain rules. First, it must be declared to throw java.rmi.RemoteException, whether or not it throws any other exceptions. Second, all method parameters and return types must be declared as primitive types, serializable objects, or remote interfaces.
Before it can invoke any IXC operation, the Xlet must have access to the IXC registry, a singleton instance of the class javax.microedition.xlet.ixc.IxcRegistry. The factory method IxcRegistry.getRegistry() returns the singleton, and is called in or after the call to the Xlet's initXlet() method:
public void initXlet( XletContext context )
throws XletStateChangeException {
try {
IxcRegistry registry =
IxcRegistry.getRegistry( context );
// do something with the registry...
}
catch( RemoteException e ){
// Couldn't connect to the registry.
}
}
|
An Xlet that wishes to make an object available to other Xlets invokes the registry's bind() method - this operation is known as binding or exporting the object. Note that you bind the object through its remote interface, not its implementation class. Once bound, an object is available to other Xlets through the registry's lookup() method, which returns a stub class that acts as a proxy for the actual object. Departing from the RMI model, the system generates these stub classes automatically, as needed. It generates a stub class only if the object has a proper remote interface; if not, it throws a javax.microedition.xlet.ixc.StubException.
An Xlet can unbind any bound object by calling IxcRegistry.unbind(), or obtain the current list of exported objects from IxcRegistry.list().
Let's look at a simple example of IXC. RunOnceXlet uses IXC to ensure that only one instance of the Xlet runs at any given time. When an instance is started, it tries to bind a marker object. If the binding succeeds, the Xlet knows it is the first instance up and running, and continues. If the binding fails, the Xlet knows that it's redundant, and exits. Before it does, though, it looks up the exported marker and passes its own starting arguments to the marker's activateAgain() method, thereby notifying the first Xlet that another instance attempted to start.
package com.mypackage;
import java.rmi.*;
import javax.microedition.xlet.*;
import javax.microedition.xlet.ixc.*;
/**
* An Xlet that uses inter-Xlet communication (IXC)
* to ensure that only once instance of the Xlet is
* ever running.
*/
public class RunOnceXlet extends BasicXlet {
private static final String NAME =
"RunOnceXlet.activator";
private boolean removeBinding = false;
public RunOnceXlet(){
}
// Override the initXlet method so that the first
// thing the Xlet does is check to see if an
// instance of itself is already running.
public void initXlet( XletContext context )
throws XletStateChangeException {
try {
// Get the IXC registry and create an instance of
// our own remote interface in case we're the
// first instance running
IxcRegistry registry =
IxcRegistry.getRegistry( context );
RemoteInterface remote =
new RemoteInterfaceImpl();
while( true ){
try {
// First we try to bind our remote intf.
// to the registry. If two instances are
// started simultaneously, only one
// of them will win.
registry.bind( NAME, remote );
removeBinding = true;
break;
}
catch( AlreadyBoundException abe ){
try {
// Somebody else has bound, so
// call its activateAgain
// method before terminating.
remote =
(RemoteInterface) registry.lookup( NAME );
String[] args = (String[])
context.getXletProperty( XletContext.ARGS );
remote.activateAgain( args );
throw new
XletStateChangeException(
"Already running" );
}
catch( NotBoundException nbe ){
// Whoops, no object found, the other
// instance might have just terminated
// so try again...
}
}
}
}
catch( RemoteException e ){
// If we can't connect, just start up...
System.out.println( "Registry error:" );
e.printStackTrace();
}
super.initXlet( context );
}
// Override the exit method defined in BasicXlet to
// deregister the instance from the IXC registry. This
// will happen automatically when the Xlet is destroyed,
// but it's better to do it explicitly. Note that
// BasicXlet.destroyXlet calls this method as well --
// it's the perfect place to put all your cleanup code.
public void exit(){
if( removeBinding ){
try {
IxcRegistry registry =
IxcRegistry.getRegistry( getContext() );
registry.unbind( NAME );
}
catch( NotBoundException e ){
}
catch( RemoteException e ){
}
}
super.exit();
}
/**
* The remote interface instances of RunOnceXlet use to
* communicate with each other.
*/
public interface RemoteInterface extends Remote {
void activateAgain( String[] args )
throws RemoteException;
}
/**
* The implementation of the remote interface.
* One difference between IXC and regular RMI is that
* in RMI this class would extend UnicastRemoteObject
* and you would have to run rmic to generate
* the appropriate stub classes. With IXC the stubs are
* generated automatically.
*/
private class RemoteInterfaceImpl
implements RemoteInterface {
public RemoteInterfaceImpl() throws RemoteException {
}
// Our activation method. All it does is print a
// message, but it could do other things by invoking
// methods on its containing class.
public void activateAgain( String[] args )
throws RemoteException {
System.out.println(
"[RemoteInterfaceImpl] Activation request" );
}
}
}
|
This example depends on BasicXlet, a convenience class for building Xlets.
To run this example, you'll need to download the reference implementation (RI) of the Personal Basis Profile. The RI runs on x86-based Linux implementations, such as Red Hat 7.2. You run it with a command like:
<installdir>/bin/cvm com.sun.xlet.XletRunner -name
RunOnceXlet -path <classpath> -args Xlet1 -name RunOnceXlet -
path <classpath> -args Xlet2
|
You can also use Xlets with the Personal Profile RI, which also runs on x86-based Linux implementations. If you're running Microsoft Windows and you want to use either PBP or PP, you'll need to set up a separate Linux machine, or use a product like VMware to run Linux as a virtual machine.
About the Author: Eric Giguere is a software developer for iAnywhere Solutions, a subsidiary of Sybase, where he works on Java technologies for handheld and wireless computing. He holds BMath and MMath degrees in Computer Science from the University of Waterloo and has written extensively on computing topics.
Back To Top
|