Example from "Guess Who's Coming: Runtime Inclusion of Participants in Choreographies"

Description

We report below the code of the AIOCJ choreography where a rule introduces a new role (logger) in the adapted scope.

Choreography

preamble {
 starter: client
}

aioc {
  product@client = getInput( "Insert product name" ); 
  request: client( product ) -> seller ( order ); 
  scope @seller{
    // process the order and compute result, here XXX
    result@seller = "XXX";
    response: seller( result ) -> client( result )
  }
}

Adaptation Rule

rule {
  include log from "socket://localhost:8002" with "jsonrpc"
  include getTime from "socket://localhost:8003" with "jsonrpc"
  newRoles: logger
  on { N.apply != false } // applicability conditions, irrelevant in this example 
  do{
    log: seller( order ) -> logger( entry );
    { 
      time@logger = getTime();
      _@logger = log( time + ": " + entry ) 
    } | {
      result@seller = "XXX";
      response: seller( result ) -> client ( result ) 
    }
  }
}

Projection

For completeness, we report excerpts of the projection as Jolie programs.

Code of the Seller

Comments added manually
// scope adaptation synchronisation procedure
var5.sid = "3dd7e775-7006-4a96-b07f-cfa920183575";
var5.rolesNum = 2;
var5.hasAck = true;
Leader.location = "socket://localhost:10501/";
initStartProcedure@Leader( var5 );
undef( var5.rolesNum );
undef( var5.hasAck );
startSR.name = "execute";
acquire@SemaphoreUtils( startSR )(  );
joinStart@Leader( var5 )(  );
aReq.client = "socket://localhost:10501/";
aReq.ports.seller.address = "socket://localhost:10501/";
aReq.ports.client.address = "socket://localhost:10500/";
// adaptation request
checkForUpdate@AdaptationManager( aReq )( aRes );
// if adaptation is required
if ( is_defined( aRes ) ) {
  for (   c = 0, c < #aRes.roles.seller.code, c++ ){
    embed_scope@ActivityManager( aRes.roles.seller.code[ c ] )(  )
  };
  var4.sid =     adaptRequest.main_key = aRes.main_key;
  foreach ( r : aRes.roles ){
    var4.rolesNum++
  };
  var4.hasAck = true;
  initStartProcedure@Leader( var4 );
  var4.sid = "3dd7e775-7006-4a96-b07f-cfa920183575_adapt";
  undef( var4.hasAck );
  initStartProcedure@Leader( var4 );
  undef( var4.rolesNum );
  undef( aRes.roles.seller );
  // distribute the adaptation code to all participants (roles) ...
  foreach ( roleName : aRes.roles ){
    if ( is_defined( aRes.roles.( roleName ).cookie ) ) {
      adaptRequest.cookie = aRes.roles.( roleName ).cookie;
      LedRole.location = aRes.roles.( roleName ).location;
      op0.msgID = adaptRequest.cookie;
      op0.content.sid = "3dd7e775-7006-4a96-b07f-cfa920183575_adapt";
      op0.content.leader = Leader.location;
      coord@LedRole( op0 )(  )
    } else {
      adaptRequest.cookie = "3dd7e775-7006-4a96-b07f-cfa920183575";
      LedRole.location = aRes.roles.( roleName ).location + "!/Activity/3dd7e775-7006-4a96-b07f-cfa920183575"
    };
    adaptRequest.code << aRes.roles.( roleName ).code;
    adapt@LedRole( adaptRequest )(  );
    undef( adaptRequest.code )
  };
  joinStart@Leader( var4 )(  );
  // ... and execute your own
  run@ActivityManager( aRes.main_key )(  )
} else {
  // no adaptation, execute original code
  eReq.cookie = "3dd7e775-7006-4a96-b07f-cfa920183575";
  noAdapt@client( eReq );
  var0 = "XXX";
  var1.value = var0;
  var1 = "result";
  set@State( var1 )(  );
  get@State( "result" )( var3 );
  _tmp = var3;
  var2.content = _tmp;
  var2.msgID = "973caa7c-0bc9-4f5e-8455-eb3047ced1bd";
  response@client( var2 )(  )
};
// scope execution terminated, returning to the parent choreography
var6.sid = "3dd7e775-7006-4a96-b07f-cfa920183575";
joinAck@Leader( var6 )(  );
startActivity@ActivityManager( "3dd7e775-7006-4a96-b07f-cfa920183575" );
startSR.name = "done";
release@SemaphoreUtils( startSR )(  )

Code of the Logger (adaptation rule)

Comments added manually
// gets the entry log from the seller
var0.msgID = "9751af6e-277a-47ad-b272-f6b6f904ed2e";
get_log@MH( var0 )( var0 );
var1 = var0.content;
var2.value = var1;
var2 = "entry";
set@State( var2 )(  );
{
  // contacts the getTime service
  getTime@Port1( var3 )( var4 );
  var5.value = var4;
  var5 = "time";
  set@State( var5 )(  );
  // prepare the message to the logger service
  get@State( "entry" )( var6 );
  get@State( "time" )( var7 );
  var8.p[0] = var7 + ": " + var6;
  // send the message to the logger service
  log@Port0( var8 )( var9 );
  var10.value = var9;
  var10 = "_";
  set@State( var10 )(  )
}