Message sending is an integral part of the Prova design. It is an operation that is a dual to message receiving (see Using reaction rules for receiving messages). Message sending is initiated when a Prova engine processes a literal in a rule body that is either of the two special builtins: sendMsg or sendMsgSync. The duality with message receiving is clearly visible in the following structure of mandatory position-based arguments to these primitives.
Position-based arguments
- XID - conversation id of the message;
- Protocol - name of the message passing protocol;
- Destination - a logical endpoint;
- Performative - the message type broadly characterising the meaning of the message;
- Payload - a Prova object (normally, a Prova map or a list) containing the actual content of the message.
Here are some examples of message sending:
Conversation id XID
Broadly speaking, conversation id is used for message correlation. However, the accent here goes on the word "conversation", rather than "correlation". It is because the conversation identity is supposed to be preserved over relatively large, logically distinct conversations that typically achieve some goals in the participating agents. This is in contrast with correlation which simply guarantees the match between the request and response.
If this parameter is a free variable at the time sendMsg (or sendMsgSync) is executed, it is set by the engine to a unique value representing a brand new conversation id and as a result this variable is bound to this value, which can be used immediately for further messaging. If the parameter is a constant value, the sent message is supposed to be a follow-up for an ongoing conversation (so that the particular conversation-id had been obtained from an earlier received message). It may also be the case that the sender agent is confident that the constant it chooses will be unique, which most often happens for demonstration or test examples.
Here is an example of a typical follow-up pattern:
The above rule expects a message using rcvMsg and then follows on the same XID with a message sent using sendMsg to another destination on the specified protocol esb.
Protocol
The message exchange protocol is currently taken from a hard-coded set of protocols that are built into Prova.
There are three internal protocols: self, task, async, and swing. The swing protocol cannot be currently used for message sending, only for receiving notifications from Swing components (see Reactive messaging for Java Swing). The other three protocols are discussed in Concurrent reactive messaging. In the main, if you want to ensure fully sequential processing of received messages, you might want to use the self protocol. The task protocol is an execution thread pool that makes no guarantees on the order in which the received messages will be processed, so that two messages for the same conversation-id might end up executing concurrently or even in a reverse order. This pool is used for running tasks achieving maximum throughput. The async protocol is quite different from the typical Actor languages in that it makes good use of the available conversation id to pin the processing to a unique thread as calculated from the conversation id. This is the preferred way to handle long-going conversations, to achieve correctness, maximise data locality, and minimise the need for synchronisation.
The osgi protocol was mainly designed for use in OSGi environments (see Running Prova inside an OSGi container) when the standard way of creating Prova agents is to embed them inside an OSGi bundle (you can have more than one of such bundles as well). The way you go around creating these embedded agents is documented in ProvaService--a simpler way to embed Prova agents. It is important to note that the osgi protocol does not actually require running Prova in an OSGi container, it simply facilitates this integration by allowing each embedded agent to exchange messages with the embedding module. In the case of an OSGi bundle, the latter can then use a complementary mechanism to exchange messages between OSGi components. In the plain Java process case, the agents merely gain the ability to send messages to each other and to the containing process.
The esb protocol (named after ESB, Enterprise Service Bus) is used for designating the containing agent as the forwarder for dispatching the message. In the mule-prova-agents project, the Mule ESB anbled components are used as a container for Prova agents so that with the Protocol set to esb and Destination set to the logical endpoint on the Mule ESB, the message is delivered by Mule over any of its dozens of actual message transports to the agent that may be located locally or anywhere in the world.
In the Mule Pova agents project, when a Mule components receives a message from elsewhere, we swap the inbound protocol name from esb to async in order to guarantee that the conversations are processed on the async protocol. The Prova agent then is assumed to understand that although it receives messages on a nominally internal protocol async, in actual fact, it must use the esb protocol for responding (see ESB example).
Destination
For internal intra-agent messaging, the destination must be either a constant 0 or the actual name of the running agent. The latter is obtainable by using the builtin iam:
The variable Me above is instantiated by iam to be the name of the current agent.
In the case of inter-agent distributed message sending, Destination is a logical name of the destination endpoint. In particular, if Mule ESB is used for message routing, it is a logical name of the endpoint as configured via the Mule configuration file. For example:
This isolates Prova code from unnecessary details related to the use message transports with Mule perfectly able to route the messages as required, including the use of interceptors or specialised routing, including sending the message to more than one actual destination.
Performative
Performatives (that we routinely call, message types) is a concept derived from M. Searle's Speech Act theory and used in the FIPA Agent Control Language (see for example, http://jade.tilab.com/papers/AIIA-jvp-fb.pdf) for specifying widely recognised message types that agents can reason upon with actually inspecting the message payload. In FIPA ACL, they resemble to formalised typical questions and answers humans would use in normal conversation, for example, ask-if or inform.
Prova does not impose any particular discipline on using performatives but allows agent authors to easily encode performative based reasoning. A number of examples included in the Prova distribution make use of performatives to great effect. This is taken from reloaded/test023.prova:
Processing messages with the queryref performative typically involve local derivation of the query included as the message payload and sending back of the answerset facts as individual messages:
Payload
Payload is the main information content of a message (or event). Prova before version 3.0.0 required the content to be a ProvaList. In the latest Prova versions, the content can be a ProvaConstant (wrapping any Java object), a ProvaList, or a ProvaMap. It must be noted, however, that each element in a ProvaList or ProvaMap must be derived from a class ProvaObject, which includes Prova constants, variables, or Prova lists or maps. If the message is sent from a Prova agent, all of this is satisfied automatically.
In this example, the payload login(user2,'30.30.30.30') automatically becomes a Prova list (note that in Prova, a(b,c) is the same as [a,b,c]). The code below uses a Prova map.
If a pure Java code is used for sending messages to a Prova agent via ProvaCommunicator, you can use the following method to wrap the Java Object[] array automatically.
Alternatively, ProvaCommunicator offers two variants of the addMsg() method to send a Prova message to a Prova agent from Java. The first one takes a pre-constructed ProvaList that includes all mandatory sendMsg parameters. For example, the following sends a raw object msg wrapped in a ProvaConstant inside a ProvaList.
However, the best practice we recommend currently is to use the second addMsg method. In the following example, payload is a plain Java Map<String, Object>. The addMsg below automatically converts this payload into a Prova map, such that the map values are wrapped inside ProvaConstantImpl instances.
If the osgi protocol is used for sending messages from the Prova rulebase as Prova maps, the sendMsg implementation unwraps the ProvaMap into a plain Java map.
The following example shows how the data can be extracted from a Prova map in a reaction guard so that the reaction can pattern match the inbound message.
If the message payload is passed as a ProvaList, the reaction rcvMsg can pattern match the payload login(User,IP2) against the pattern including local variables, constants or embedded lists as well as use reaction guards if necessary:
Synchronized message sending with sendMsgSync
As the primitive sendMsg is executed on the current thread, the message is sent immediately to the specified destination. Consider the following code:
The idea here is to "jump threads" so that message sending occurs on an automatically selected thread from the task thread pool (see Concurrent reactive messaging). What actually happens is that once the switch message is sent, the following rcvMsg inside switch_thread makes sure there is an inline reaction waiting for a reply on the task protocol. So while rcvMsg itself is executed on the main thread, the reaction it creates should be ready to intercept the expected message on another task thread. We have a race condition, the reaction may not be ready before the message arrives.
The primitive sendMsgSync ensures that the current rule runs exhaustively until all solutions or failure is encountered, which means, among other things, that all subsequent rcvMsg are all executed, before the message is actually sent. So the correct code will be as above but with sendMsgSync.
This message sending primitive must typically be used whenever there are send+receive pairs in the code and there is a possibility that the execution thread will be changed. The protocol async is specially designed for maintaining coherence through the whole message exchange in a conversation, in which case, a conversation is always mapped to one thread. The same is true for the default self protocol that runs everything in a single thread.