Tutorial: Create a Role

This page describes how to create a role.

Contents


Role definition

According to the CRIO metamodel a role is an expected behaviour (a set of role tasks ordered by a plan) and a set of rights and obligations in the organisation context. The goal of each Role is to contribute to the fulfilment of (a part of) the requirements of the organisation within which it is defined. A role can be instantiated either as a Common Role or Boundary Role. A Common Role is a role located inside the designed system and interacting with either Common or Boundary Roles. A Boundary Role is a role located at the boundary between the system and its outside and it is responsible for interactions happening at this border (i.e. GUI, Database, etc).

Role's basic structure and lifecycle

Each Role in Janus is implemented as first-class entity using a dedicated java class. The lifecycle of a role is decomposed in three fundamental activities, each one managed by a specific method:

  • Activate : it corresponds to the initialization phase of the role. The activate method is automatically called by the Janus kernel when an agent has obtained the right to access this role.
  • Live : it corresponds to the main part of the role's lifecycle. The live method will be called in the behavior of the agent playing this role.
    Each call to this method should run one step of the role behavior.
    Caution: Do not use infinite loop in live method of a role. A such practice will block the execution of your agent. The agent behavior will enter in this role and never exit.
  • End : It is the termination activity. All the resources used by the role may be free in the end method.

This lifecycle is common with the agent class in Janus, it is defined by the Activable Interface.

The code below, describes the typical architecture of a role in Janus.

// A simple annotation used to specify the datatype of the
// parameters required for the initialization of the role
// These parameters corresponds to the one provided by an
// agent when he requests a role.
// These parameters are automatically passed to the 
// activate method, i.e. fixedParameters={Integer.class}
@RoleActivationPrototype(
		fixedParameters={}
)
public class MyRole extends Role {
	@Override
	public Status activate(Object... parameters) {
		//Initialize the role according to the parameters
                //coming from the agent which plays this role.
 
                //return a default ok status
		return StatusFactory.ok(this);
	}
 
	@Override
	public Status live() {
		The core behavioral method of the role.
		return StatusFactory.ok(this);
	}
 
 
	@Override
	public Status end() {
		// Role finalization: free the resources....
		// If this method is usless in your application, do
                // not override it and everything will work fine
		return StatusFactory.ok(this);
	}
 
}


Adopting the CRIO metamodel, we usually specify the role behavior using a state-chart. A state-chart may be translated in java using a switch-based implementation. Such an implementation is presented below:

@RoleActivationPrototype(
		fixedParameters={}
)
public class MyRole extends Role {
 
	//Attribute storing the current state of the role behavior
 
	private State state;
 
 
	@Override
	public Status activate(Object... parameters) {
		this.state = State.STATE1;
 
		return StatusFactory.ok(this);
	}
 
	@Override
	public Status live() {
		// This method delegates the behavior execution
                // in to a thrid-party method:
		// Run(), that correpsonds to the direct implementation
                // of the role statechart.
		this.state = Run();
		return StatusFactory.ok(this);
	}
 
	private State Run() {
		switch (this.state) {	
		case STATE1: //do something;
			     return STATE2;
 
		case STATE2: //do something;
			     return STATE3;
 
		case STATE3: //do something;
			     return STATE1;
 
		default:
			return this.state;
		}			
	}
 
	/** This inner class defines the various possible 
         * state of the PBroker role.
	 * It is used in the Run() method to specify the
         * behavior of this role.
         */
	public static enum State {
		STATE1,
		STATE2,
		STATE3;
	}
 
}


Communication in Organizational Context: Message

In Janus, various communication mechanism are available. The first one is based on the organizational approach and enables the communication between role in the same group.

In the organizational perspective, a role can only communicates with other roles defined in his group.


How to send message to other roles within a group

A role can only communicate with other roles defined within the same group. Different kinds of communication are available in Janus:

  • One-to-One Communication
    If a role just wants to send a message to another specific role within the same group, use this method:
    public final AgentAddress sendMessage(Class<? extends Role> role, Message message)

    This methods sends the specified message to one agent playing the specified role if it exists.

    If a role wants to send a message to a specific agent playing a given role. This situation usually appends when a role has already started a communication with an agent and it doesn't want to change his partner and continue the previously started task. In this case, the developer may used the following methods:

    public final void sendMessage(Class<? extends Role> role, AgentAddress receiver, Message message)

    This methods sends the specified message to the agent having the specified address and playing the specified role.

    If the specified agent doesn't play the specified role, the message sending fails.
  • One-to-Many Communication
    If a role wants to inform all agents playing a specific role in its group, use this method:
    public final void broadcastMessage(Class<? extends Role> role, Message message)
    This method broadcasts the specified message to all agents playing the specified role.


How to manage message reception

In Janus, each agent and each role is associated to a personal mailbox. This agent's mailbox is composed by the collection of the mailboxes of the roles which is currently playing and one specific mailbox to manage agent-to-agent direct communication.

If you want to access to the mailbox of a role, just call getMailbox() method from the role context to get the mailbox and process messages using dedicated methods. But a mailbox also implements iterable on message, so you may also directly use this method to process all waiting messages. Please find below a typical java code to process messages of a role:

for(Message msg : getMailbox()) {
	if (msg instanceof SomeMessageClass1) {
		//do something
	} else if (msg instanceof SomeMessageClass2) {
		//do something
	} else {
		//do something
	}
}

Several additional functions permits to quickly access to the mail boxes:

  • getMessage(): consume the first message in the mail box;
  • peekMessage(): get but do not consume the first message in the mail box;
  • getMessages(): reply all the messages in the mail box as an iterator;
  • peekMessages(): reply but do not consume all the messages in the mail box as an iterator;
  • hasMessage(): indicate if a message is currently sotred in the mail box.

Communication in Agent Context: Signals

At a given moment, an agent may play various role in various groups. These roles may require to communicate together or exchange information with their owner agent. To enable this kind of communication Janus provides an event-based communication system called Signals. For a role, these signals may used to communicate with its owner agents and with other roles played by its owner agent. The processing and dispatching mechanisms of this event-based communication is ensured by the agent.


How to fire signals to other roles played by the same agent

To fire a signal, a role has just call the following method:

public void fireSignal(Signal signal);

Typical java code to fire a signal:

fireSignal(new MySignal(...));

Agent will then automatically dispatch the signal to all registered listeners.


How to receive signals from other roles played by the same agent

The implementation of signal reception mechanism is based on the adapter design pattern. To receive signals, agents and roles have to create or implement a SignalListener. Janus already provides two major kinds of SignalListeners:

  • LastSignalAdapter : in this implementation, only the last signal is stored, if the previous haven't not been processsed, it is lost.
  • QueuedSignalAdapter : using this implementation, incoming signals are stored in queue until their processing by the role or the agent. Caution: if signals are not consumed, the memory footprint of this adapter may increased and throw a out-of-memory exception.

Each SignalListener provides specific methods to process its signals. Please find below, a sample of code to illsutrate the use of LastSignalAdapter.

public class MySignal extends Signal {
	public MySignal(Object source,) {
		super(source);
	}
}
 
public class MyRole extends Role {
	private final LastSignalAdapter<MySignal> signalListener
                      = new LastSignalAdapter<MySignal>(MySignal.class);
 
	@Override
	public Status activate(Object... parameters) {
		addSignalListener(this.signalListener);
		return StatusFactory.ok(this);
	}
 
	@Override
	public Status live() {
	        // ...
	        MySignal influence = this.signalListener.getLastReceivedSignal();
	        if (influence!=null) {
	        	//do something
	        }
	        // ...
	        return StatusFactory.ok(this);
	}
}


Role and Group Obtain/Leave Conditions

In Janus, roles and groups may restrict their access, they are considered as ConditionnedObject

  • To enter in a group, an agent have to satisfy group obtain conditions.
  • To obtain a given role, an agent have to satisfy role and group obtain conditions.
  • To leave a group, an agent have to satifisfy group leave conditions.

To leave a role, an agent have to satifisfy role and group leave conditions.

Obtain and leave conditions are empty by default.

Each role and each group may restrict its access using a set of pre-defined (in the constructor of the role) or dynamic (in the behavior of the role) conditions. These conditions are defined thanks to RoleCondition and GroupCondition interfaces. The developer may define its own conditions implementing these interfaces to protect access to its roles and groups, but Janus already provides a set of predefined conditions.

Two basic kinds of role/group obtain conditions already exists in Janus to restrict access to a role or a group:

  • SatisfyRoleDependenciesCondition A role may require that its player play another specific role to authorize its access. It is usually used when two roles have to be played by the same agent to ensure information exchange between two distinct groups. Respectively, a group may restrict its access to agents playing particular roles.
    public class MyRole extends Role {
    	public MyRole() {
    		addObtainCondition(new SatisfyRoleDependenciesCondition(
                                       Arrays.asList(MyRole1.class, MyRole2.class)));
    	}
    }
  • HasAllRequiredCapacitiesCondition According to CRIO, a role may require a set of capacities to define its behavior. An agent have thus to provide an implementation to these required capacities to access to the role. Respectively, a group may restrict its access to agents having particular capacities.
    public class MyRole extends Role {
    	public MyRole() {
    		addObtainCondition(new HasAllRequiredCapacitiesCondition(
                                       Arrays.asList(MyCapacity1.class, MyCapacity2.class)));
    	}
    }



Agent Memory: how to easily exchange information between an agent and its roles

In various applications, it is often required to adapt the behavior of a role according to dynamic characteristics of its player agent. It is also useful to model a kinds of memory storing data that can be modified by various roles of the same agent at the same time. The concept of agent memory was introduced to solve such issues. It considered as a data container embedded inside the agent. This memory is private to the agent context, it may be modified by the agent itself or its roles. It may thus be considered as a way to exchange information between roles played by an agent. This concept of memory is implemented in a fully generic way in Janus. The developer may define its own implementation. However, Janus already provides two basics implementations of memory that are briefly described below :

  • BlackBoardMemory : This is the default implementation of memory. It is a simple blackboard that can modified by the agent and its various roles, ie. each data on the blackboard is identified by a key. This implementation doesn't manage concurrent access.
  • JavaReflectionMemory : Implementation of an agent memory using a direct access to the getter and setter methods of the agent class.

Please find a below a typical java code that describes how to use the concept of memory to enable the communication between a agent and one of its role.

public class MyAgent extends Agent {
//.....
	public MyAgent() {
		Memory memory = getMemory();
		assert(memory!=null);
		memory.putMemorizedData(MyRole.WORLD_HEIGHT, 50);
		memory.putMemorizedData(MyRole.WORLD_WIDTH, 40);
	}
 
 
	public Status activate(Object... parameters) {
		GroupAddress group = getOrCreateGroup(organization(MyOrganization.class));
		requestRole(MyRole.class, group, RoleInitData);
		return StatusFactory.ok(this);
	}
//.....
}
 
 
public class MyRole extends Role {
//.....
	public static final String WORLD_HEIGHT = "WORLD_HEIGHT";
	public static final String WORLD_WIDTH = "WORLD_WIDTH";
 
	public Status activate(Object... parameters) {
		Memory memory = getMemory();
		this.width = memory.getMemorizedData(WORLD_WIDTH, Integer.class);
		this.height = memory.getMemorizedData(WORLD_HEIGHT, Integer.class);
		return StatusFactory.ok(this);
	}
//.....
	public Status live() {
		Memory memory = getMemory();
		Map<AgentAddress,WorldState> locations = (Map<AgentAddress,WorldState>)memory.getMemorizedData(POSITIONS);
		memory.putMemorizedData(IS_PREY_CATCHED, Boolean.TRUE);
 
		return StatusFactory.ok(this);
	}
 
}


Related Pages

2012-01-31Roadmap of Janus is public.
This page was last modified on 16 February 2011, at 08:44. This page has been accessed 1,286 times.
Copyright 2010-2012 © Janus Core Developers - Privacy policy