This usage idiom is based on a new feature, a ProvaService interface and default implementation ProvaServiceImpl, designed for both plain Java runtime and OSGi containers.
The host Java code creates and initializes a ProvaService, which can hold one or more Prova agents each running in their own Prova engine. In fact, Prova supports two ways of modularization in an OSGi environment: mapping one agent to a separate bundle or running multiple agents in one bundle. In a plain standalone non-OSGi Java process, the latter is the only option, and this Section shows how simple it is to embed multiple agents and have them communicate via messaging between themselves and with the embedding Java code--also considered to be an "embedding agent".
We reproduce below the source code for a standalone runner class ProvaSimpleService.java that demonstrates how to use ProvaService to create two cooperating agents. An alternative way to achieve that would have been to use a Mule ESB implementation although the example presented below has a net advantage in terms of simplicity and lack of massive numbers of dependencies required by Mule.
This runner code uses two rulebases below. This is a "receiver" agent in rules/service/receiver.prova.
This is a "sender" agent in rules/service/sender.prova.
When the runner code is initialized in the ProvaSimpleService constructor, it creates and initializes a ProvaService instance. It then creates two Prova agents using the ProvaService.instance() method and then uses the method consult to add the Prova rulebases to those agents. When the rulebases are consulted, they run their eval goals with the sender sending and the receiver receiving a message. For the agents to be "visible" to each other, they must communicate via the "osgi" protocol (the second argument in sendMsg and rcvMsg).
Using a global object count is only needed in this slightly contrived example: we use it for incrementing the count of expected messages (see $Count.incrementAndGet() in receiver.prova) and checking this count from the Java code.
The example also demonstrates that we can both send messages to the selected agents and receive messages back from the Java code. Beginning with Prova 3.0.4, there are now two ways for achieving this: (1) call service.register(AgentName,EPService) or (2) pass a chosen name of the containing "agent" to the ProvaService.send() method:
ProvaService registers the name "runner" against the callback object passed as the last parameter. Note that the ProvaSimpleService implements the EPService interface, allowing it the Prova agents to call back on the EPService.send() method when the need to send messages back to the containing agent. Observe the sendMsg with destination "runner" in receiver.prova.
Also, observe the use of Java Map for passing message payloads between all parties, including the embedding agent. This improves performance and yet allows for considerable flexibility (see Prova maps and messaging using slotted terms).
This is a typical output from running this program.
To summarize, we are able to exchange messages between all parties: the Java code representing an embedding agent and the embedded Prova agents. The messages can flow in any direction between all parties with only logical agent names required as destination.