I have a scenario where a want to refresh a resource, but I also want to be able to terminate the refresh.
I have the following interfaces:
interface terminate{
OneWay: terminate(void)
}
interface refreshAll {
RequestResponse: refreshAll(void)(void)
}
And the resource:
include "interface.iol"
include "console.iol"
inputPort dummyInput {
Location: "socket://localhost:8002"
Protocol: sodep
Interfaces: refreshAll
}
init{
registerForInput@Console()()
}
main
{
refreshAll( number )( result ) {
println@Console("refresh")();
in(req);
result = void
}
}
And the service I run if I want to terminate:
include "interface.iol"
outputPort term {
Location: "socket://localhost:8000"
Protocol: sodep
Interfaces: terminate
}
main
{
terminate@term()
}
And the program coordinating everything:
include "interface.iol"
include "console.iol"
inputPort terminate {
Location: "socket://localhost:8000"
Protocol: sodep
Interfaces: terminate
}
outputPort resource {
Location: "socket://localhost:8002"
Protocol: sodep
Interfaces: refreshAll
}
main
{
scope(hej){
install(
hello => {
println@Console("terminate")()
}
);
{
refreshAll@resource()()
}|
{
terminate();
throw(hello)
}
}
}
Why is the exception not thrown directly when terminate
is received?
That is, in the coordination program the exception handler is not called when terminate
is recived. The exception handler is first invoked after the refreshAll@resource()()
has finished.
How can I write so the refreshAll
is terminated getting a terminate
?
In Jolie, a fault (what you're triggering with the throw primitive) does not interrupt a pending solicit-response call (your refreshAll@resource()()
): if it hasn't started yet, it's not started at all, but if the request has been sent to the intended receiver (resource
here), then Jolie is going to wait for the response (or a timeout) before propagating the fault to the enclosing scope (hej
here). That's because the result of the solicit response might be important for the fault management logic.
If you don't care about the result of the solicit-response in your fault handler (and here you don't), then you can just make a little adapter to handle the solicit-response call in two steps, effectively making it asynchronous at the application level (at the implementation level, most Jolie stuff is asynchronous anyway, but here you want to explicitly see that the communication happens in two steps in your program logic).
I modified your coordinator program as follows and then everything worked as you wanted:
include "interface.iol"
include "console.iol"
inputPort terminate {
Location: "socket://localhost:8000"
Protocol: sodep
Interfaces: terminate
}
outputPort resource {
Location: "socket://localhost:8002"
Protocol: sodep
Interfaces: refreshAll
}
// New stuff from here
interface RefreshAsyncAdapterIface {
OneWay:
refreshAllAsync(void)
}
interface RefreshAsyncClientIface {
OneWay:
refreshCompleted(void)
}
inputPort CoordinatorInput {
Location: "local://Coordinator"
Interfaces: RefreshAsyncClientIface
}
outputPort Coordinator {
Location: "local://Coordinator"
Interfaces: RefreshAsyncClientIface
}
// Adapter service to split the solicit-response in two steps
service RefreshAsyncAdapter {
Interfaces: RefreshAsyncAdapterIface
main {
refreshAllAsync();
refreshAll@resource()();
refreshCompleted@Coordinator()
}
}
main
{
scope(hej){
install(
hello => {
println@Console("terminate")()
}
);
{
// Split the call in send and receive steps
refreshAllAsync@RefreshAsyncAdapter();
refreshCompleted()
}|
{
terminate();
throw(hello)
}
}
}
This patterns appears quite often, so we'll probably make it even easier in the future.
References: