JEgg Developer's Guide


NOTE: This guide currently refers to JEgg versions prior to 0.3.0. It is being re-written for 0.3.0.


JEgg development should be simple - if it becomes too complicated, it's defeating its purpose, to simplify the development of multithreaded apps in Java and increase their robustness.  To that end, this guide will be short and sweet.

Introduction

JEgg aims to provide a lightweight framework for implementing "eggs", which are Java objects that communicate with one another by sending messages, which are just ordinary Java objects.  These objects are called "eggs" because their external aspect, or public interface, is relatively featureless, hiding the internal details (like an egg!).

When a message is sent to an egg, the framework queues the message, by priority, for later delivery to the egg.  Subsequently, the JEgg message dispatcher assigned to the egg takes the message off the egg's message queue and passes it as an argument to the appropriate handler method on the egg.  The egg's handler method, implemented by you, the application developer, acts on the message in whatever way the application logic requires.

The net effect of the message dispatch mechanism is:
Race conditions between three or more Eggs can occur, but they are completely explicit, not hidden in the subtleties of  program code.  A common race condition that can occur is the lookup by one Egg of another Egg's message port using the PortRegistry.  However, the race condition will manifests itself in the worst case by a lookup failure message delivered to the requesting Egg.

The JEgg Base Class

All Eggs must extend the framework class jegg.Egg.  The minimal requirements for an Egg implementation are the following:
public void handle (Object message)

That's it.  The handle(Object) method is where your Egg decides what to do with messages it doesn't recognize.  If you extend jegg.Egg and have only a single handle(Object) method, your Egg will be compatible with the JEgg framework, but it obviously won't be very useful.

To implement the application logic that your new Egg is intended for, add additional handle(..) method, with public visibility, to your derived class.  Each handle method should take a single argument, which declares the type of the message that the handle method will recognize.  For instance:

public class MyEgg extends jegg.Egg {
    public MyEgg (Object id) {super(id);}  // more on this later
    public MyEgg (Object id, Dispatcher d) {super(id,d);} // more on this later

    public void handle (Object o) { /* 'default' handler */ }
    public void handle (Bacon strip) { ... }
    public void handle (Toast slice) { ... }
    public void handle (Coffee cup) { ... }
}

A simple (and obvious) way to view an Egg is as a Finite State Machine (FSM) with a single state.  The messages delivered to the Egg consitute events that trigger self-transitions (transitions from the state back to itself), and the handle methods are the transitions.

The Egg base class has two constructors, at least one of which your egg will explicitly invoke:
The first constructor assigns the argument as an identifier for the egg and assigns the egg to the default message dispatcher.  The second form of the constructor assigns the egg to the passed-in dispatcher. 

Any other methods in your derived Egg should be directly related to its implementation with visibility no higher than protected.

The JEgg Message Dispatcher

The JEgg framework message dispatcher is implemented by the jegg.Dispatcher class.   The message dispatcher delivers incoming messages to each of the eggs assigned to it, round-robin style.  Each message is allowed be handled to completion by the egg it was delivered to before the dispatcher delivers the next message to the next egg.  Messages can be assigned different priorities, although in the current version, priority is only taken into account between messages in the same queue (egg).

The framework automatically creates a default dispatcher that an egg is assigned to if it is not explicitly constructed with a dispatcher.

You should use the default dispatcher as much as possible, especially for eggs that spend most of their time doing very little.  However, you may need to partition your eggs among different dispatchers, either because you have too many to assign to the default dispatcher or because some of them may block the dispatcher for long intervals as they handle messages.  Actually, any egg that can potentially take a long time to process a message should probably be assigned to its own individual dispatcher, unless the other eggs assigned to that dispatcher are tolerant. 

How many Eggs are too many to assign to a single dispatcher?  I don't know, but a future version of the dispatcher will track its utilization and emit runtime warnings if it appears to be over committed.  A well designed application will allow its eggs to be assigned to dispatchers through the application configuration., which will help you to tweak the application performance without rewriting any code.

The Egg Port

Each egg has a single message port that can be used by other eggs to send messages to it.  An egg's message port can be obtained by calling the following method (one of the few public methods on the egg):

jegg.Port jegg.Egg.getPort(void)

The Port class has a method for sending a message to the Egg it is associated with:

void jegg.Port.send(jegg.Message msg)

The send method takes an object of type jegg.Message which encloses the actual object to be delivered to the target egg.  The object to send to the target should be wrapped in a jegg.Message object using the jegg.Egg.createMessage(Object) convenience method from the Egg base class:

// In an egg method ...
Object msg =  new SomeApplicationSpecificMessageClass();

Message eggMessage = createMessage(msg);
Port toEgg = targetEgg.getPort();
toEgg.send(eggMessage);


Often, an egg will need to communicate with another egg that it doesn't have a reference to so that it can't directly retrieve the target egg's message port.  If the target egg has published its message port in the JEgg framework port registry, under a well-known name, then any other egg can lookup the port in the registry and use it to send messages to the target egg.  The JEgg port registry is described next.

The Port Registry

The Egg base class provides another convenience method that the derived egg can use to publish its message port in the JEgg framework port registry:

void jegg.Egg.publishPort (String name);

This method will register the egg's message port in the port registry under the specified name.  Often, an egg will invoke this method in its constructor.

An egg can also use a Egg base class method to lookup the port of another egg published in the port registry:

void jegg.Egg.requestPort (String name);

If a port is registered in the port registry under the specified name, then the port will be sent to the requesting egg wrapped in a jegg.registry.LocatePortResponse message.  For instance:

public MyEgg extends Egg {
    public MyEgg() {
        super(MyEgg.class.getName());
         requestPort("some-egg-id");
    }

    public void handle(LocatePortResponse r) {
        Port p = r.getPort();
        p.send(createMessage("SPAM!"));
    }
}

If the target egg hasn't yet published it's port when the port registry receives the lookup request, then the request is saved until a port  is published under a name matching the name specified in the request.

Sending Messages

Previous examples have already shown how to send simple point-to-point messages at the "default" priority.  This section describes how to "broadcast" messages and send messages with higher or lower priorities.

Message Broadcast

Some eggs will be designed to emit messages for delivery to all interested eggs.  JEgg accomodates this by allowing an egg to "bind" to the port of another egg in such a way that any message broadcast by the latter will be delivered to all eggs that have explicitly bound to that port.

An egg can bind to the port of another egg using the base class method jegg.Egg.bindToPort(jegg.Port).  For instance,

public MyEgg extends Egg {
    public MyEgg() {
        super(MyEgg.class.getName() /*why not..*/);
         requestPort("emitter-egg");
    }

    public void handle(LocatePortResponse r) {
        Port p = r.getPort();
        bindToPort(p);
    }

    public void handle(SomeBroadcastMessage m) {
        // Handle message broadcast from other egg.
    }
}

In the preceding, the SomeBroadcastMessage was broadcast by the egg that the MyEgg instance had bound to.  How did that egg broadcast the message?

public EmitterEgg extends Egg {
    public EmitterEgg() {
        super(EmitterEgg.class.getName());
         publishPort("emitter-egg");
    }

    public void handle(SomeTriggerEvent e) {
        // This event (message) causes me to
        // emit another message
        send(new SomeBroadcastMessage());
    }
}

Notice that the broadcasting egg used another base class method: 

void jegg.Egg.send(Object)

Notice also that the argument to this method doesn't have to be an instance of jegg.Message.  You just pass the "raw" message object that you want to be delivered to any listener eggs.

Responding to Messages

Often, the implementation of a handle() method will require the ability to send a response message back to the egg that sent the message being handled at that instant.  The Egg base class offers convenience methods to messages to be responded to as they are being handled, at a later time, and with varying priorities. 

Responding to the current message

To respond to the message being currently handled  invoke:

void jegg.Egg.respond (Object)

When invoked during the handling of a message from another egg, either point-to-point or broadcast, this method will send the object back to the egg that sent the current message.  Notice that the response object doesn't have to be wrapped in a jegg.Message instance;  consequently, the messages is sent at the default priority. 

For instance:

public MyEgg extends Egg {
    public MyEgg() {
        super(MyEgg.class.getName() /*why not..*/);
         requestPort("emitter-egg");
    }

    public void handle(LocatePortResponse r) {
        Port p = r.getPort();
        bindToPort(p);
    }

    public void handle(SomeBroadcastMessage m) {
        respond ("Message received - over and out!");
    }
}

To send at a different priority, use the other form of the jegg.Egg.respond method that also takes a jegg.Priority argument in addition to the response object.

Responding to a message later

During the handling of a message, an egg may determine that it should respond, but can't immediately.  In this situation, the handler can defer the response (during the handling of a different message, say) by saving the port of the sending egg and/or the current message being handled.  The base class methods that can be used for this are:

jegg.Port  jegg.Egg.getFromPort()
jegg.Message jegg.Egg.getCurrentMessage()

Both of these message can only be called during the handling of a message, which is pretty much any time, right? (Since the egg only executes when its handling a message).  To simplify matters, the jegg.Message class provides access to the port of the sending egg, so if you need both port and message, you only need to save the return value of the getCurrentMessage() method.

To respond to a message that was previously delivered and saved (as just described), use the form of the response methd that allows explicit specification of  the target egg's port:

void jegg.Egg.respond (jegg.Port, Object)

This method is really no different from using the jegg.Port.send method directly except that the respond method will take care of wrapping the response object in a jegg.Message instance before writing it to the port.

Timers

Since eggs communicate asynchronously, it's important to be able to detect when an important message is late.  A timer that can deliver a timeout message to an egg fits in well with the JEgg messaging scheme.

The jegg.Egg base class offers two types of timers, repeating and single-shot:

jegg.timer.Timer createRepeatingTimer(long interval, long delay)
jegg.timer.Timer createSingleShotTimer(long delay)


A repeating timer will continue to deliver jegg.timer.Timeout messages to the egg until it's cancelled by invoking jegg.timer.Timer.cancel().  A single-shot timer is automatically cancelled after it delivers its single timeout message, unless it's cancelled by the egg before it expires.

Using single-shot timers

Single-shot timers are useful for putting an upper limit on an anticipated message that must be received before further action can be taken.  A typical usage is to start a timer after a port lookup request has been issued:

public MyEgg extends Egg {
    private Timer timer;
    public MyEgg() {
        super(MyEgg.class.getName());
        requestPort("emitter");
        timer = createSingleShotTimer(30000/*30 secs*/);
    }

    public void handle (Timeout t)
    {
        System.err.println("Help!  Can't find 'emitter'!");
    }

    public void handle(LocatePortResponse r) {
        timer.cancel();
        Port p = r.getPort();
        bindToPort(p);
    }

    public void handle(SomeBroadcastMessage m) {
        respond ("Message received - over and out!");
    }
}

Using repeating timers

Repeating timers are useful for performing periodic actions (really!).  A consequence of the queue-based delivery of messages, though, is that if a handle method takes a long time to respond to a timeout, the other timeouts may expire, be queued in the egg's message queue, and be delivered to the egg in quick succession. 

A More Complex Egg


Tips and Tricks