8 Native Java Application Interface (CEJAVA)
The CEJAVA interface is built upon the CE interface, using the Java
Native Interface (JNI). This allows a user to tap the power of the
Ensemble messaging system from Java. Performance of this interface is
similar to the native ML and C interfaces.
Not surprisingly, the API is similar to the CE API. Here, we walk
through an overview of the interface, and then point out the major
differences with respect to CE. Method and constructor documentation
can be found either in the code itself, or through the javadoc
generated HTML files.
8.1 Overview
The basic concept is that of a Group. A group is constructed by:
public Group(Callbacks cb);
A group has to define the following set of callbacks:
public class Callbacks {
public abstract void install(View view);
public abstract void exit();
public abstract void recv_cast(int origin, byte[] msg);
public abstract void recv_send(int origin, byte[] msg);
public abstract void flow_block(int rank, boolean onoff);
public abstract void block();
public abstract void heartbeat(double time);
}
The callbacks define behavior when events, such as message receipt or
view changes, occur.
Within the context of a group the user can perform eleven actions:
public void join(JoinOps jops);
public void leave();
public void cast(byte[] msg);
public void send(int[] dests, byte[] msg) ;
public void send1(int dest, byte[] msg);
public void prompt();
public void suspect(int[] suspects);
public void xferDone();
public void rekey();
public void changeProtocol(String protocol_name);
public void changeProperties(String properties);
Before starting to use the package, the initialization function must
be called:
static public void init(String args []);
Any command line argument to CE and Ensemble should be passed here.
To run a Java program that uses CEJAVA, the classpath must point to
the ensemble.jar java-archive file, and the library path should
point to the matching native library libcejava.so. For example,
to run java program prog.java, assuming the jar file is in the
local directory, and that the native library is under lib use:
java -Djava.class.path=;:ensemble.jar -Djava.library.path=lib prog
A simpler option is to set the CLASSPATH environment variable to
include ensemble.jar and set the dynamic library path to include
libcejava.so. On Unix systems this means setting then
LD_LIBRARY_PATH and on win32 setting the PATH.
8.2 Notes
There are two major difference with respect to CE: zero-copy, and
synchronization.
CEJAVA is not ``zero-copy'', the receive callbacks copy message data
from C to Java, and the send actions copy data from Java to C. This
costs extra copying but allows the Java application to do whatever it
likes with message data, without being bound by underlying reference
counting and memory management.
Synchronization is very different between C and Java because the Java
has language support for locking. A group can be in one of six phases:
public static final int PRE = 0;
public static final int JOINING = 1;
public static final int NORMAL = 2;
public static final int BLOCKED = 3;
public static final int LEAVING = 4;
public static final int LEFT = 5;
-
PRE:
- preliminary phase, the group has not been fully
constructed yet.
- JOINING:
- this endpoint is joining the group.
- NORMAL:
- the group is in stable state.
- BLOCKED:
- group is blocked prior to a pending view change.
- LEAVING:
- this member is leaving the group.
- LEFT:
- member has left the group.
The only phase in which actions are allowed is the NORMAL state.
Multiple threads can perform actions on the same group, furthermore,
the Ensemble main-loop executing in a separate thread invokes group
callbacks when events arrive from the network. The group object is a
synchronization point for these threads of execution. The
implementation must ensure that group-state does not change during an
action. To this end, whenever an action is performed on group g: (1) the
group object is locked (2) status is checked (3) if it is NORMAL, the
action is performed. For example, the code for send looks like this:
public void send(int[] dests, byte[] msg) {
synchronized(this) {
check_normal();
natSend(nat_env, dests, msg);
}
}
An application can have critical sections in which it must ensure
group state is NORMAL. It can also lock group state, and ensure it
does not change using a similar technique. A getStatus
call is provided that returns the group state for group g. An example
of coding a critical section is:
synchronized(group) {
int stat = group.getStatus ();
if (stat == NORMAL) {
/* Perform critical code here */
}
}
To maximize performance, the send/recv callbacks can also invoke
Ensemble actions. The callbacks enjoy the best latency since they do
not incur a thread-switch.