Communication Primitives
Communication primitives are strictly related to the operations declared in the interfaces and the ports defined into the service. Communication primitives can be divided in two categories:
- input primitives
- output primitives
Input primitives are triggered by a message reception, whereas the output primitives enable a message sending.
Input primitives
Input primitives can be divided in two types which also correspond to those used into the interface declaration:
- one-way: a message can be received from an external caller. It must correspond to a OneWay operation declared into an interface.
- request-response: a message can be received from an external caller, and a synchronous reply can be sent back. It must correspond to a RequestResponse operation declared into an interface.
In order to program a one-way operation inside the behaviour of a service, it is sufficient to declare the name of the OneWay operation published into an inputPort of the service followed by the name of the variable between brackets where the received message will be stored.
operation_name( request )
On the other hand, a request-response operation requires the name of a RequestResponse operation defined into an interface followed by two variables: the former is in charge to store the receiving message whereas the latter is in charge to store the replying message. Both the variables must be defined within brackets. Since a request-response primitive is a synchronous primitive, between the request and the response message some code can be executed. The caller will wait for the termination of that code before receiving for the reply.
operation_name( request )( response ){
// code block
}
As an example let us consider the following service which has two operations defined. The former is a one-way operation and the latter a request-response one.
from console import Console
interface MyInterface {
OneWay:
myOW( string )
RequestResponse:
myRR( string )( string )
}
service MyService {
execution: concurrent
embed Console as Console
inputPort myPort {
location: "socket://localhost:8000"
protocol: sodep
interfaces: MyInterface
}
main {
[ myOW( request ) ]{ println@Console("OW:" + request )() }
[ myRR( request )( response ) {
println@Console("RR:" + request )();
response = "received " + request
}]
}
}
Output primitives
Output primitives allow for sending messages to some input operations defined on another service. Also the output primitives can be divided into two categories:
- notification: a message can be sent to a receiving one-way operation.
- solicit-response: a message can be sent to a receiving request-response operation. The solicit-response is blocked until the reply message is received.
The syntax of notification and solicit-response resembles those of one-way and request-response with the exception that the operation name is followed by the token @
and the name of the outputPort to be used for sending the message. Here in the following, we report the syntax of the notification where OutputPort_Name is the name of the outputPort to be used and request is the variable where the sending message is stored.
operation_name@OutputPort_Name( request )
Analogously, in order to program a solicit-response it is necessary to indicate the port used to send the message. Differently from the one-way primitive, in the solicit-response one the first variable contains the message to be sent and the second one contains the variable where the reply message will be stored. No code block is associated with a solicit-response primitive because it simply sends a message and waits until it receives a response from the requested service.
operation_name@OutputPort_Name( request )( response )
In the following we report a possible client of the service above which is able to call the operations myOW and myRR in sequence:
from console import Console
execution: concurrent
interface MyInterface {
OneWay:
myOW( string )
RequestResponse:
myRR( string )( string )
}
service MyService {
embed Console as Console
execution: single
outputPort myOutputPort {
location: "socket://localhost:8000"
protocol: sodep
interfaces: MyInterface
}
main {
myOW@myOutputPort( "hello world, I am the notification" );
myRR@myOutputPort( "hello world, I am the solicit-response" )( response );
println@Console( response )()
}
}
Solicit-Response timeout
It is possible to set the response timeout of a solicit-response by specifying the engine argument responseTimeout
when running Jolie. Details can be found at page Basics/Engine Argument.
Example
Here we discuss a simple example where both OneWay/Notification and RequestResponse/SolicitResponse primitives are used. The complete code can be checked and downloaded at this link.
The example's architecture is reported below.
A newspaper service collects news sent by authors, users can get all the registered news into the newspaper. The interface of the newspaper service defines two operations:
- sendNews which is a OneWay operation used by authors for sending news to the newspaper service
- getNews which is a RequestResponse operation used by users for getting the list of the registered news
type News: void {
.category: string
.title: string
.text: string
.author: string
}
type GetNewsResponse: void {
.news*: News
}
type SendNewsRequest: News
interface NewsPaperInterface {
RequestResponse:
getNews( void )( GetNewsResponse )
OneWay:
sendNews( SendNewsRequest )
}
The implementation of the two operations is very simple; we exploit a global variable for storing all the incoming news. When the getNews is invoked, we just return the list of the stored news. Details about the global variables can be found in section Processes.
from NewsPaperInterface import NewsPaperInterface
service NewsPaper {
execution: concurrent
inputPort NewsPaperPort {
location:"auto:ini:/Locations/NewsPaperPort:file:locations.ini"
protocol: sodep
interfaces: NewsPaperInterface
}
main {
[ getNews( request )( response ) {
response.news -> global.news
}]
[ sendNews( request ) ] { global.news[ #global.news ] << request }
}
}
The author and the user can invoke the NewsPaper by exploiting two jolie scripts, author.ol and user.ol respectively. The two scripts can be run in a separate shell with respect to the newspaper one. In the following we report the code of the twi scripts:
//author.ol
from NewsPaperInterface import NewsPaperInterface
from console import Console
from console import ConsoleInputInterface
service AuthorClient {
embed Console as Console
inputPort ConsoleInput {
location: "local"
Interfaces: ConsoleInputInterface
}
outputPort NewsPaper {
location: "socket://localhost:9000"
protocol: sodep
interfaces: NewsPaperInterface
}
main {
/* in order to get parameters from the console we need to register the service to the console one
by using the operatio registerForInput. After this, we are enabled to receive messages from the console
on input operation in (defined in console.iol)*/
registerForInput@Console()();
print@Console("Insert category:")(); in( request.category );
print@Console("Insert title:")(); in( request.title );
print@Console("Insert news text:")(); in( request.text );
print@Console("Insert your name:")(); in( request.author );
sendNews@NewsPaper( request );
println@Console("The news has been sent to the newspaper")()
}
}
//user.ol
from NewsPaperInterface import NewsPaperInterface
from console import Console
service UserClient {
embed Console as Console
outputPort NewsPaper {
location: "socket://localhost:9000"
protocol: sodep
interfaces: NewsPaperInterface
}
main {
getNews@NewsPaper()( response );
for( i = 0, i < #response.news, i++ ) {
println@Console( "CATEGORY: " + response.news[ i ].category )();
println@Console( "TITLE: " + response.news[ i ].title )();
println@Console( "TEXT: " + response.news[ i ].text )();
println@Console( "AUTHOR: " + response.news[ i ].author )();
println@Console("------------------------------------------")()
}
}
}