Chapter 10. Events

Jadex is an event-based system, that is, nothing happens inside a Jadex agent unless it is triggered by some event. For example the ping plan gets triggered when a ping request message arrives. Nevertheless, Jadex agents are not purely reactive, because Jadex not only supports external events (e.g., messages), but also different types of internal events. For example the adoption of a new goal will generate a goal event, leading to plans being executed to achieve the goal. When an event occurs in Jadex and no plan is found to handle this event a warning message is generated, which can be printed to the console depending on the logging settings (see Chapter 13, Properties).

The Jadex events XML schema part

Figure 10.1. The Jadex events XML schema part

Three types of events are supported in Jadex: Message events, goal events, and internal events, as well as references to these types, as shown in Figure 10.1, “The Jadex events XML schema part”. Generally, all event types are parameter elements meaning that any number of parameter(set)s can be specified for a user-defined kind of event. A parameter itself can be used for passing values from the source to the consumer of the event and possibly back. For making explicit in which direction the data flow should occur the parameter's direction attribute can be used. The possible values are "in", "out" or "inout" similar to parameters of procedure calls known from conventional programming languages. In addition all kinds of events share the common attributes: "posttoall", "metalevelreasoning" and "randomselection". The "posttoall" flag determines if the event should be dispatched to a single receiver or to all applicable plans (note, that for post-to-all goals no retry is performed). The "metalevelreasoning" flag can be used to turn off the process of reasoning about plan candidates if more than one candidate is applicable for a given event. Finally, the "randomselection" flag can be used to turn off the importance of the declaration order of plans for the plan selection process. Nevertheless, the priority of a plan is still respected.

Table 10.1. Event Flags

FlagsDefault Value
posttoallinternal event=true, otherwise false
metalevelreasoningtrue
randomselectionfalse

At runtime, e.g., when accessed from plans, instances of the elements are represented by the jadex.runtime.IMessageEvent, jadex.runtime.IGoalEvent, and jadex.runtime.IInternalEvent interfaces. The following sections will describe these different types of events in more detail.

10.1. Goal Events

Goal events are used only internally for the processing of goals. This means that the agent will create a goal event in response to an active goal it wants to perform (process event) and for a goal that finished its processing (info event). A user never has to define custom goal events, but instead can concentrate on goal modelling. To explicitly decide which kind of event has happened (as commonly necessary inside mobile plans) the IGoalEvent.isInfo() method can be used.

10.2. Internal Events

Internal events are the typical way in Jadex for explicit one-way communication of an interesting occurrence inside the agent. The usage of internal events is characterized by the fact that an information should be passed from a source to some consumers (similar to the object oriented observer pattern). Hence, if an internal event occurs within the system, e.g., because some plan dispatches one, it is distributed to all plans that have declared their interest in this kind of event by using a corresponding trigger or by waiting for this kind of internal event. The internal event can transport arbitrary information to the consumers if custom parameter(set)s are defined in the type for that purpose. A typical use case for resorting to internal events is, e.g., updating a GUI.

<!-- ADF snippet showing the internal event declaration. -->
...
<events>
        <!-- Specifies an internal event for updating the gui.-->
        <internalevent name="gui_update">
            <parameter name="content" class="String"/>
        </internalevent>
</events>
...
// Plan snippet showing the creation and dispatching of the internal event.
...
public void body() {
    String update_info;
    ...
    // "gui_update" internal event type must be defined in the ADF
    IInternalEvent event = createInternalEvent("gui_update");
    // Setting the content parameter to the update info
    event.getParameter("content").setValue(update_info);
    dispatchInternalEvent(event);
    ...
}

Figure 10.2. Dispatching an internal event example

In addition to user-defined internal events there are also some already predefined internal events used by the Jadex system itself. In Table 10.2, “Predefined Internal Event Types” these types are explained shortly. They should not be of much importance for most agent developers. Only, if you intend to write mobile plans you may need to know them.

Table 10.2. Predefined Internal Event Types

Predefined TypesDescription
jadex.model.IMEventbase.TYPE_TIMEOUTA timeout has occurred (e.g., while waiting for some other event)
jadex.model.IMEventbase.TYPE_EXECUTEPLANA plan should be executed (e.g., initial or conditional plan)
jadex.model.IMEventbase.TYPE_CONDITION_TRIGGEREDA condition has been triggered

10.3. Message Events

All message types an agent wants to send or receive need to be specified within the ADF. The message event (class jadex.runtime.IMessageEvent) denotes the arrival or sending of a message. The direction of the message (arrived or to be sent) can be checked with the isIncoming() method. Note, that only incoming messages are handled by the event dispatching mechanism, while outgoing messages are just sent. The native underlying message object (which is platform dependent) can be retrieved using the getMessage() method. In addition, the message content, which may be a String or some content object, can be retrieved using the getContent() method.

Templates for message events are defined in the ADF in the <events> section. The direction attribute can be used to declare whether the agent wants to receive, send or do both (default) for a given event. Possible values for that attribute are "send", "receive" and "send_receive" respectively. The type of the message constrains the available parameters of a message. Currently, the only available type is "fipa" which automatically creates parameter(set)s according to the FIPA message specification (e.g., parameters for the receivers, content, sender, etc. are introduced). Through this message typing Jadex does not require that only FIPA messages are being sent, as other options may be added in future. In the following Table 10.3, “Reserved FIPA message event parameters”, all available parameter(set)s are itemized. For details about the meaning of the FIPA parameters, see the FIPA specifications available at http://www.fipa.org. In addition to the FIPA parameters, Jadex introduces the content-start, content-class, and action-class parameters. These are explained below.

Table 10.3. Reserved FIPA message event parameters

NameClass
performativeString
senderjadex.adapter.fipa.AgentIdentifier
reply-tojadex.adapter.fipa.AgentIdentifier
contentObject
languageString
encodingString
ontologyString
protocolString
reply-withString
in-reply-toString
conversation-idString
reply-byjava.util.Date
receiversjadex.runtime.adapter.AgentIdentifier
content-startString
content-classClass
action-classClass

10.3.1. Receiving Messages

Typically in the ADF of an agent a number of message event types for sending and receiving message events are declared for the application domain. Examples for such user-defined message event types might be "inform_time", "request_vision", etc. As those message types are defined for each agent separately there are consequently no global message types. So how does an agent know the message type of a newly received message? For this purpose a simple matching process is used. This means that all locally known message types of an agent and its subcapabilities (with direction "receive" or "send_receive") are matched against the newly received message and the best fitting is selected. For the matching process the parameter values of a message type are checked against the values in the received message. For this purpose only parameters with direction="fixed" are considered important, as they represent fixed type information. A message event type matches an incoming message if all fixed parameter values are the same in the received message.

There are several reasons why an agent may fail to correctly process an incoming message. These are indicated by different logging outputs at different logging levels (see Table 10.4, “Possible problems when matching messages”). In the first case, if more than one message event type has a match with the incoming event the most specific match will be used. The number of fixed parameters is used as indicator for the specificity. As this is a common case, it is only logged at level INFO. When a message is received, which does not match any of the declared message events of the agent, a WARNING is generated, indicating that this message is ignored by the agent. Finally, when there are two or more message events, which all match an incoming message to the same degree (i.e., all have the same number of fixed parameters) the system cannot decide which message event to use, and has to choose one arbitrarily. As this probably indicates a programming error in the ADF, a SEVERE ouput is produced.

Table 10.4. Possible problems when matching messages

LevelOutput
INFOMultiple events matching message, using message event with highest specialization degree
WARNING...cannot process message, no message event matches
SEVERECannot decide which event matches message, using first

The content-start, content-class, and action-class parameters (if present) are treated specially in the matching process. The content-start parameter matches to all messages with a content starting with the given string value. The content-class parameter is matched against the class of the object sent as message content (see below). Finally, the action-class parameter is useful for messages encoded in FIPA SL. The parameter is matched against the action expression contained in the message.

10.3.2. Sending Messages

The super class of both plan types (jadex.runtime.AbstractPlan) provides several convenience methods to create message events. To send a message, a message event has to be created using the createMessageEvent(Strint type) method supplying the declared message event type name as parameter. The receivers of fipa messages are specified by agent identifiers (class jadex.adapter.fipa.AgentIdentifier). The message content can be supplied as String or as Object with setContent(Object content). If the content is provided as Object it must be ensured that the agent can encode it into a transmissable representation. Currently the only language supported for all adapters is the jadex.runtime.adapter.SFipa.JAVA_XML language which employs the JDK built-in Java bean mechanism to encode and decode the content. Using this language requires that Java bean information about the content object can be found or inferred by the Java bean introspector. Please have a look at the Beanynizer tool (available from the Jadex homepage) if you are interested in converting an ontology to Java beans including the necessary bean infos.

Note

If you are using the Jadex adapter for the JADE platform you can rely on the JADE specific codecs for content transmission. In this case the content object must be instance of a jade.content.ContentElement, which will be automatically filled into the message using JADEs content manager mechanism. To use this mechanism you have to ensure several things.

  • The language and ontology need to be known by the agent. Therefore the following predefined properties "jade.language.xxx" and "jade.ontology.yyy" can be used to supply the JADE codec and the ontology instance (replacing "xxx"/"yyy" with a suitable name of your choice).

  • The language and ontology names need to be set in the message, so that JADE can determine at runtime which codec to use for the given message event.

To actually send the message event it is sufficient to call the sendMessage(IMessageEvent me) method with the prepared message event as parameter. It is also possible to send a message and directly wait for a reply with an optional timeout by using the sendMessageAndWait(IMessageEvent me [, timeout]) method. In this case, make sure you have supplied a conversation-id or a reply-with parameter, because otherwise the reply cannot be related to the original message. The reply-with resp. conversation-id parameter need to be filled with an appropriate unique id. For this purpose you may consider using the helper method SFipa.createUniqueId() as shown in Figure 10.3, “Sending a message example”. When using a timout and the message is not received before the timeout has elapsed, a jadex.runtime.TimeoutException is thrown (see also Section 9.2.1, “Plan Success or Failure and BDI Exceptions”).

<!-- ADF snippet showing the message declaration. -->
...
<events>
    <messageevent name="request_carry" type="fipa" direction="send">
        <parameter name="performative" class="String" direction="fixed">
            <value>SFipa.REQUEST</value>
        </parameter>
        <parameter name="language" class="String" direction="fixed">
            <value>SFipa.JAVA_XML</value>
        </parameter>
        <parameter name="ontology" class="String" direction="fixed">
            <value>MarsOntology.ONTOLOGY_NAME</value>
        </parameter>
        <parameter name="reply-with" class="String">
            <value>SFipa.createUniqueId($scope.getAgentName())</value>
        </parameter>
    </messageevent>
    ...
</events>
... 
// Plan snippet showing the creation and sending of the message.
public void body() {
    ...
    jadex.adapter.fipa.AgentIdentifier receiver;
    CarryRequest action;  // Onotology Java bean.
    ...
    // "request_carry" message event type must be defined in the ADF
    IMessageEvent me = createMessageEvent("request_carry");
    me.getParameterSet(jadex.adapter.fipa.SFipa.RECEIVERS).addValue(receiver);
    me.setContent(action);
    IMessageEvent reply = sendMessageAndWait(event);
    ...
}

Figure 10.3. Sending a message example

On the other hand, if you have received a message event and want to reply to the sender you don't have to create a new message event from scratch but can directly create a reply. This ensures that all important information such as the conversation id also appears in the answer. A reply can be created by calling createReply(String type [, Object content]) method directly on the received message event. This method takes the message event type for the reply as parameter. Note that the message type with which you are replying still has to be present in your ADF.