Skip to content

jade-oo: Exposing the hidden side-effects of the action() method

March 28, 2010

Background

In JADE, an Agent has a set of Behaviours, and each Behaviour has an action() method. The Agent calls the action() method of each Behaviour in turn, and so the overall operation of the Agent is performed by each of its individual behaviours.

By default, an Agent will continue to call each of the action() methods of its behaviours, in turn, without pausing. Rather than looping forever at 100% CPU, Behaviours can call one of two block() methods, provided by the Behaviour base class. Calling block() from within the action() method tells the Agent to only call action() again if a message is received; and calling block(long timeout) tells the Agent to only call action() again if a message is received or timeout milliseconds elapses, whichever is sooner.

The problem

What happens if, within your action() method, you call block() then block(50)? What happens if you call block() right at the beginning of your action() method? How can you unit test that a certain set of of inputs will prompt a Behaviour to block()?

Hiding calls to block() within the action() method results in hidden side-effects that are difficult to reason about, and difficult to capture and test.

The jade-oo way

Rather than having a void action() method, jade-oo behaviours have a Block action() method. The behaviour then returns a Block object, which the platform then uses to determine whether to keep the behaviour active, or to leave the behaviour until a new message is received/the timeout has elapsed.

Here’s my Block class:

package jade.oo.core.behaviours.blocking;

import jade.core.behaviours.Behaviour;

public abstract class Block {
        public static Block NULL = new DontBlock();
        public static Block UNTIL_WOKEN = new BlockUntilWoken();
        public static Block UNTIL_WOKEN_OR_TIMEOUT(long timeout) {
                return new BlockWithTimeout(timeout);
        }
        public void doBlock(Behaviour behaviour) {
        }
}

Here’s one of the three implementations (note that the behaviour being passed in is the standard JADE Behaviour):

package jade.oo.core.behaviours.blocking;

import jade.core.behaviours.Behaviour;

class BlockUntilWoken extends Block {

        @Override
        public void doBlock(Behaviour behaviour) {
                behaviour.block();
        }

}

Here’s the modified method in the standard JADE Behaviour class (note that the original is abstract, and so any existing implementations will simply override this and retain their existing behaviour):

          /**
         * Runs the behaviour. This abstract method must be implemented by
         * <code>Behaviour</code>subclasses to perform ordinary behaviour duty. An
         * agent schedules its behaviours calling their <code>action()</code>
         * method; since all the behaviours belonging to the same agent are
         * scheduled cooperatively, this method <b>must not</b> enter in an endless
         * loop and should return as soon as possible to preserve agent
         * responsiveness. To split a long and slow task into smaller section,
         * recursive behaviour aggregation may be used.
         *
         * @see jade.core.behaviours.CompositeBehaviour
         */
        public void action() {
                Block blockAction = delegate.action();
                blockAction.doBlock(this);
        }

And here’s an example of its use, taken from my new TickerBehaviour (my only changes here are on the two return lines at the end):

        @Override
        public final Block action() {
                // Someone else may have stopped us in the meanwhile
                if (!finished) {
                        long blockTime = wakeupTime - System.currentTimeMillis();
                        if (blockTime <= 0) {
                                // Timeout is expired --> execute the user defined action and
                                // re-initialize wakeupTime
                                tickCount++;
                                onTick();
                                wakeupTime = System.currentTimeMillis() + period;
                                blockTime = period;
                        }
                        // Maybe this behaviour has been removed within the onTick() method
                        if (!finished) {
                                return Block.UNTIL_WOKEN_OR_TIMEOUT(blockTime);
                        }
                }
                return Block.NULL;
        }

Further enhancements

  • I don’t like Block; it sounds like the Behaviour is stopping something else from processing, when really it’s doing the opposite, and freeing up CPU cycles for other behaviours to use.
  • I’m not sure about having the static methods within the interface itself. I should probably have a Block interface with a doBlock(Behaviour behaviour) method, and a BlockFactory with the three static methods. Or something else?
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: