Mokabyte

Dal 1996, architetture, metodologie, sviluppo software

  • Argomenti
    • Programmazione & Linguaggi
      • Java
      • DataBase & elaborazione dei dati
      • Frameworks & Tools
      • Processi di sviluppo
    • Architetture dei sistemi
      • Sicurezza informatica
      • DevOps
    • Project Management
      • Organizzazione aziendale
      • HR
      • Soft skills
    • Lean/Agile
      • Scrum
      • Teoria della complessità
      • Apprendimento & Serious Gaming
    • Internet & Digital
      • Cultura & Società
      • Conferenze & Reportage
      • Marketing & eCommerce
    • Hardware & Tecnologia
      • Intelligenza artificiale
      • UX design & Grafica
  • Ultimo numero
  • Archivio
    • Archivio dal 2006 ad oggi
    • Il primo sito web – 1996-2005
  • Chi siamo
  • Ventennale
  • Libri
  • Contatti
Menu
  • Argomenti
    • Programmazione & Linguaggi
      • Java
      • DataBase & elaborazione dei dati
      • Frameworks & Tools
      • Processi di sviluppo
    • Architetture dei sistemi
      • Sicurezza informatica
      • DevOps
    • Project Management
      • Organizzazione aziendale
      • HR
      • Soft skills
    • Lean/Agile
      • Scrum
      • Teoria della complessità
      • Apprendimento & Serious Gaming
    • Internet & Digital
      • Cultura & Società
      • Conferenze & Reportage
      • Marketing & eCommerce
    • Hardware & Tecnologia
      • Intelligenza artificiale
      • UX design & Grafica
  • Ultimo numero
  • Archivio
    • Archivio dal 2006 ad oggi
    • Il primo sito web – 1996-2005
  • Chi siamo
  • Ventennale
  • Libri
  • Contatti
Cerca
Chiudi

Nel numero:

266 novembre
, anno 2020

Tic-tac-Jolie

II parte: Dati, porte e “scacchiera”

Mauro Sgarzi e Claudio Guidi

Mauro Sgarzi

Ha cominciato con l'informatica da autodidatta, nel lontano 1982 — anni ruggenti — con il mitico Lemon II (clone di Apple II) e registratore a cassette. Ha poi conseguito una laurea vecchio ordinamento in ingegneria elettronica indirizzo informatica presso l’Università degli Studi di Bologna.
Negli anni, ha spaziato in diversi settori — assicurazione qualità, pianificazione produzione, logistica — mantenendo sempre un occhio sulla parte informatica, settore in cui ha poi rifocalizzato i suoi interessi. Lavora attualmente presso Imola Informatica, con il ruolo di software engineer.

Mauro Sgarzi e Claudio Guidi

Claudio Guidi

Claudio Guidi è un ricercatore ed un imprenditore nell‘area dei microservices. Co-Leader del progetto Jolie (http://www.jolie-lang,org), ha conseguito il suo Ph.D. in computer science presso l‘Università di Bologna con una tesi sulla formalizzazione dei linguaggi per il Service Oriented Computing. Insieme a Fabrizio Montesi, l‘altro creatore di Jolie, ha fondato italianaSoftware che già oggi vende soluzioni legate alla system integration orientate ai microservizi.

Tic Tac Jolie

Tic-tac-Jolie

II parte: Dati, porte e “scacchiera”

Mauro Sgarzi e Claudio Guidi

Mauro Sgarzi e Claudio Guidi

  • Questo articolo parla di: Architetture dei sistemi, Programmazione & Linguaggi

Last time on Tic-tac-Jolie

Nella precedente parte abbiamo:

  • introdotto il concetto e la filosofia delle architettura a microservizi;
  • presentato un’introduzione a Jolie;
  • analizzato l’impostazione a microservizi della gestione del Tris;
  • introdotto il concetto di interfaccia ed elencato quelle utilizzate;
  • in ultimo abbiamo riformulato lo schema a servizi del gioco utilizzando le operazioni esposte.

In questa seconda parte della serie, vediamo alcuni aspetti importanti — quali i tipi di dati e le porte che consentono il dialogo con l’esterno — e cominciamo a definire la “scacchiera” su cui il gioco si svolgerà.

 

I tipi di dati

I dati gestiti in Jolie sono di due tipi:

  • elementari (basiliari): int, string, bool, raw, long, double, void;
  • tipi personalizzati (structured data tree): la struttura è un albero — simile a quello di XML o JSON — con una radice a cui diamo un nome. In particolare, il nodo radice può avere a sua volta un suo valore (void se non ha alcun valore) e ha dei nodi figli per i quali si può ripetere ricorsivamente la stessa struttura.

Nella appendice A4 e A5, che pubblicheremo al termine della serie, sarà riportata la sintassi formale.

Analisi dei dati usati nel gioco

Veniamo adesso ad analizzare i dati usati nel gioco, alternando il listato con la spiegazione.

type StartGameRequest: void {
    .game?: string
    .user_location: string
}

Definiamo un tipo StartGameRequest che nella radice non contiene alcun valore e ha un nodo game (opzionale, indicato dal carattere “?”) di tipo stringa, e un altro nodo user_location (obbligatorio) di tipo stringa.

StartGameRequest.user_location

Fa riferimento alla collocazione dell’utente, vale a dire, dove è presente l’utente sulla rete.

StartGameRequest.game

È il nome della scacchiera a cui vogliamo sederci e iniziare a giocare.

type StartGameResponse: void {
    .game_token: string
    .role_token: string
    .role_type: string
}

Qui abbiamo un tipo con tre nodi obbligatori: game_token, role_token, role_type.

  • game_token identificherà il giocatore durante il gioco (valore univoco);
  • role_token è il token che identifica il giocatore;
  • role_type identifica il simbolo di cerchio (“circle“) o croce (“cross“) con cui un giocatore fa le sue mosse sulla scacchiera.
type ListOpenGamesRequest: void

ListOpenGamesRequest è un alias per void, quindi la chiamata all’operazione avverrà senza invio di dati.

type ListOpenGamesResponse: void {
    .game_token*: string
}

L’asterisco * definisce una cardinalità potenzialmente infinita tipo game_token, cioè definiamo un array di lunghezza arbitraria.

Dati per l’operazione interna

C’è anche un tipo di dato per l’operazione interna.

type InitiateGameRequest: void {
    .game_token: string
    .circle_participant: string {
        .location: string
    }
    .cross_participant: string {
        .location: string
    }
}

type InitiateGameRequest: void {   è il messaggio passato all’operazione interna; game_token rappresenta il nome della schacchiera/partita; circle_participant indica l’ubicazione sulla rete del giocatore rappresentato dal cerchio e cross_participant, analogamente, è l’ubicazione sulla rete del giocatore con la croce.

 

Le porte: accedere al mondo reale e dialogare con esso

Abbiamo visto in precedenza il paradigma linguistico: tipi, messaggi e interfacce. Il paradigma linguistico cerca di catturare l’essenza del servizio (linguaggio con sintassi e semantica adatte a implementare i vari aspetti del servizio).

Il punto di vista operativo (deployment) considera il servizio come una scatola nera di solito messa in opera dentro un suo container ed eseguita. Dal suo punto di vista, un’architettura a servizi non è altro che una rete di microservizi connessi tra loro tramite porte.

In Jolie i due aspetti hanno trovato una sintesi. Mentre con le interfacce descriviamo l’insieme delle operazioni che il servizio espone logicamente e i messaggi in esse scambiati (contract first), con le porte andiamo a descrivere come nella realtà comunichiamo con il resto del mondo. Questo è il punto di “accoppiamento”.

La sintassi delle porte sarà formalmente descritta nell’appendice A6.

Porte del gestore della scacchiera

Ecco il listato di dichiarazione delle porte:

outputPort User {
  Protocol: sodep
  Interfaces: UserInterface
}

outputPort MySelf {
  Interfaces: InternalInterface
}

inputPort Local {
  Location: "local"
  Protocol: sodep
  Interfaces: InternalInterface
}

inputPort Tris {
  Location: "socket://localhost:9000"
  Protocol: sodep
  Interfaces: TrisGameInterface
}

Come si evince abbastanza facilmente, abbiamo sia porte in uscita (outputPort) sia in ingresso (inputPort). Ecco di seguito i vari brani del codice con il relativo commento.

outputPort User {
  Protocol: sodep
  Interfaces: UserInterface
}

Porta in uscita per comunicare con l’utente che utilizza  come messaggi quelli esposti dall’interfaccia UserInterface. Sodep è un protocollo sviluppato per Jolie.

outputPort MySelf {
  Interfaces: InternalInterface
}

Porta utilizzata per chiamare l’operazione interna.

inputPort Local {
  Location: "local"
  Protocol: sodep
  Interfaces: InternalInterface
}

Anche nel caso di un’operazione interna devo dichiarare la corrispettiva porta. A tale scopo pongo Location = local per indicare una porta locale.

inputPort Tris {
  Location: "socket://localhost:9000"
  Protocol: sodep
  Interfaces: TrisGameInterface
}

Porta d’ingresso del servizio su cui arriveranno le richieste dei giocatori.

Porte del gestore del giocatore

outputPort TrisGame {
  Location: "socket://localhost:9000"
  Protocol: sodep
  Interfaces: TrisGameInterface
}

inputPort User {
  Location: UserLocation
  Protocol: sodep
  Interfaces: UserInterface
}

Vediamo anche qui i commenti alle varie porzioni di codice.

outputPort TrisGame {
  Location: "socket://localhost:9000"
  Protocol: sodep
  Interfaces: TrisGameInterface
}

Porta verso il gestore della scacchiera. Il gestore è sulla stessa macchina su cui si esegue il giocatore.

inputPort User {
  Location: UserLocation
  Protocol: sodep
  Interfaces: UserInterface
}

Porta su cui si riceveranno gli aggiornamenti della scacchiera e lo stato del gioco, a seguito dell’operazione move(.)(.).

Il codice di gestione della scacchiera (Behaviour)

Abbiamo illustrato la parte contract first (interfacce + porte) che definiamo deployment. Ora andiamo a illustrare il codice implementativo del gestore delle scacchiere (Behaviour), il cui commento sarà concluso nella prossima parte della serie.

Ecco intanto il listato implementativo:

include "TrisGameInterface.iol"
include "UserInterface.iol"
include "runtime.iol"
include "console.iol"

cset {
    token: MoveRequest.game_token
}

execution{ concurrent }

type InitiateGameRequest: void {
    .game_token: string
    .circle_participant: string {
        .location: string
    }
    .cross_participant: string {
        .location: string
    }
}

interface InternalInterface {
  OneWay:
    initiateGame( InitiateGameRequest )
}

outputPort User {
  Protocol: sodep
  Interfaces: UserInterface
}

outputPort MySelf {
  Interfaces: InternalInterface
}

inputPort Local {
  Location: "local"
  Protocol: sodep
  Interfaces: InternalInterface
}

inputPort Tris {
  Location: "socket://localhost:9000"
  Protocol: sodep
  Interfaces: TrisGameInterface
}

define checkVictory {
    /* check the rows */
    for ( r = 0, r < 3, r++ ) {
        if ( (places[0+(3*r)] + places[1+(3*r)] + places[2+(3*r)]) == 3 ) { circle_wins = true };
        if ( (places[0+(3*r)] + places[1+(3*r)] + places[2+(3*r)]) == -3 ) { cross_wins = true }
    };
    /* check the columns */
    for( c = 0, c < 3, c++ ) {
        if ( (places[c]+places[c+3]+places[c+6]) == 3 ) { circle_wins = true };
        if ( (places[c]+places[c+3]+places[c+6]) == -3 ) { cross_wins = true }
    };
    /* check diagonal */
    if ( (places[0]+places[4]+places[8]) == 3 ) { circle_wins = true };
    if ( (places[0]+places[4]+places[8]) == -3 ) { cross_wins = true };
    if ( (places[2]+places[4]+places[6]) == 3 ) { circle_wins = true };
    if ( (places[2]+places[4]+places[6]) == -3 ) { cross_wins = true };

    /* send final messages */
    if ( !circle_wins && !cross_wins ) {
        circle_message = cross_message = "Nobody wins"
    } else {
        if ( circle_wins ) {
          circle_message = "You win!";
          cross_message = "You loose!"
                  } else {
          cross_message = "You win!";
          circle_message = "You loose!"
        }
        ;
        usr.places -> places;
        usr.status_game = "end";
        User.location = circle_participant.location;
        usr.message = circle_message;
        syncPlaces@User( usr );
        User.location = cross_participant.location;
        usr.message = cross_message;
        syncPlaces@User( usr )
    }
}

init {
    getLocalLocation@Runtime()( MySelf.location )
}

main {
  [ initiateGame( request ) ] {
      csets.token = request.game_token;
      circle_participant = request.circle_participant;
      circle_participant.location = request.circle_participant.location;
      cross_participant = request.cross_participant;
      cross_participant.location = request.cross_participant.location;
      for( i = 0, i < 9, i++ ) { places[i] = 0 };

      /* send start messages */
      User.location = cross_participant.location;
      usr.places -> places; usr.message = "Wait for a move from circle player";
      usr.status_game = "stay";
      syncPlaces@User( usr );
      User.location = circle_participant.location;
      usr.status_game = "play";
      usr.message = "It is your turn to play";
      syncPlaces@User( usr );

      /* start game */
      moves = 0; circle_wins = false; cross_wins = false;
      while( moves < 9 && !circle_wins && !cross_wins ) {
          /* waiting for a move */
          scope( move ) {
              install( MoveNotAllowed => nullProcess );
              move( mv_request )() {
                  /* check if the place is empty */
                  if ( places[ mv_request.place ] != 0 ) { throw( MoveNotAllowed, "The place is already occupied")};

                  /* check the turn */
                  if ( (moves%2) == 0 ) {
                      /* circle move */
                      if ( mv_request.participant_token != circle_participant ) {
                          throw( MoveNotAllowed, "It is not your turn" )
                      } else {
                          places[ mv_request.place ] = 1;
                          User.location = circle_participant.location;
                          usr.places -> places; usr.message = "Wait for a move from cross player";
                          usr.status_game = "stay";
                          syncPlaces@User( usr );
                          User.location = cross_participant.location;
                          usr.message = "It is your turn to play";
                          usr.status_game = "play";
                          syncPlaces@User( usr )
                      }
                  } else {
                      /* cross move */
                      if ( mv_request.participant_token != cross_participant ) {
                          throw( MoveNotAllowed, "It is not your turn" )
                      } else {
                          places[ mv_request.place ] = -1;
                          User.location = cross_participant.location;
                          usr.places -> places; usr.message = "Wait for a move from circle player";
                          usr.status_game = "stay";
                          syncPlaces@User( usr );
                          User.location = circle_participant.location;
                          usr.status_game = "play";
                          usr.message = "It is your turn to play";
                          syncPlaces@User( usr )
                      }
                  }
              }
              ;
              moves++;
              checkVictory
          }
      }
      ;
      if ( !circle_wins && ! cross_wins ) {
          usr.places -> places;
          usr.status_game = "end";
          User.location = circle_participant.location;
          usr.message = "";
          syncPlaces@User( usr );
          User.location = cross_participant.location;
          usr.message = "";
          syncPlaces@User( usr )
      }
  }

  [ listOpenGames( request )( response ) {
      count = 0;
      foreach( g : global.games ) {
          response.game_token[ count ] = g;
          count++
      }
  }]

  [ startGame( request )( response ) {
      new_game = false;
      if ( !is_defined( global.games.( request.game ) ) ) {
          new_game = true;
          token = new;
          global.games.( token ) = true;
          global.games.( token ).circle_participant = new;
          global.games.( token ).circle_participant.location = request.user_location;
          global.games.( token ).cross_participant = new;
          response.game_token = token;
          response.role_token = global.games.( token ).circle_participant;
          response.role_type = "circle"
      } else {
          response.game_token = request.game;
          response.role_token = global.games.( request.game ).cross_participant;
          global.games.( request.game ).cross_participant.location = request.user_location;
          response.role_type = "cross"
      }
  }] {
      if ( !new_game ) {
          with( initiate_request ) {
              .game_token = request.game;
              .circle_participant = global.games.( request.game ).circle_participant;
              .circle_participant.location = global.games.( request.game ).circle_participant.location;
              .cross_participant = global.games.( request.game ).cross_participant;
              .cross_participant.location = global.games.( request.game ).cross_participant.location
          };
          initiateGame@MySelf( initiate_request );
          undef( global.games.( request.game ) )
      }
  }
}

Per completezza esplicitiamo le interfacce importate:

TrisGameInterface.iol

type ListOpenGamesRequest: void
type ListOpenGamesResponse: void {
    .game_token*: string
}

type MoveRequest: void {
    .game_token: string
    .participant_token: string
    .place: int
}

type StartGameRequest: void {
    .game?: string
    .user_location: string
}
type StartGameResponse: void {
    .game_token: string
    .role_token: string
    .role_type: string
}

interface TrisGameInterface {
  RequestResponse:
    listOpenGames( ListOpenGamesRequest )( ListOpenGamesResponse ),
    move( MoveRequest )( void ) throws MoveNotAllowed( string ),
    startGame( StartGameRequest )( StartGameResponse )

UserInterfaces.iol

type SyncPlacesRequest: void {
    .places[9,9]: int
    .message: string
    .status_game: string // play | stay | end
}

interface UserInterface {
    OneWay:
      syncPlaces( SyncPlacesRequest )
}

 

Conclusioni

In questa parte abbiamo affrontato (in prima battuta) la definizione di dati (tipi) in Jolie; poi, abbiamo elencato i principali tipi di dati utilizzati nelle interfacce; successivamente abbiamo introdotto il concetto di porta e la sua giustificazione teorica ed elencato e commentato le porte utilizzate. Infine abbiamo introdotto il listato completo dei servizio gestione scacchiera. Nel prossimo numero ripartiremo da qui.

 

 

Facebook
Twitter
LinkedIn
Mauro Sgarzi e Claudio Guidi

Mauro Sgarzi

Ha cominciato con l'informatica da autodidatta, nel lontano 1982 — anni ruggenti — con il mitico Lemon II (clone di Apple II) e registratore a cassette. Ha poi conseguito una laurea vecchio ordinamento in ingegneria elettronica indirizzo informatica presso l’Università degli Studi di Bologna.
Negli anni, ha spaziato in diversi settori — assicurazione qualità, pianificazione produzione, logistica — mantenendo sempre un occhio sulla parte informatica, settore in cui ha poi rifocalizzato i suoi interessi. Lavora attualmente presso Imola Informatica, con il ruolo di software engineer.

Mauro Sgarzi e Claudio Guidi

Claudio Guidi

Claudio Guidi è un ricercatore ed un imprenditore nell‘area dei microservices. Co-Leader del progetto Jolie (http://www.jolie-lang,org), ha conseguito il suo Ph.D. in computer science presso l‘Università di Bologna con una tesi sulla formalizzazione dei linguaggi per il Service Oriented Computing. Insieme a Fabrizio Montesi, l‘altro creatore di Jolie, ha fondato italianaSoftware che già oggi vende soluzioni legate alla system integration orientate ai microservizi.

Mauro Sgarzi e Claudio Guidi

Mauro Sgarzi e Claudio Guidi

Tutti gli articoli
Nello stesso numero
Loading...

Blast from the past

I parte: Che cosa è il cloud computing?

Vita da Scrum Master

IV parte: Supporto all’organizzazione

Business Model in quattro atti

IV parte: Personal Busines Model e vista d’insieme

Nella stessa serie
Loading...

Tic-tac-Jolie

VI parte: Appendice

Tic-tac-Jolie

V parte: Porte e importazione di file esterne

Tic-tac-Jolie

IV parte: Il microservizio “giocatore”

Tic-tac-Jolie

III parte: listOpenGames, startGames, initiateGame

Tic-tac-Jolie

I parte: Un linguaggio per microservizi si mette in gioco

Mokabyte

MokaByte è una rivista online nata nel 1996, dedicata alla comunità degli sviluppatori java.
La rivista tratta di vari argomenti, tra cui architetture enterprise e integrazione, metodologie di sviluppo lean/agile e aspetti sociali e culturali del web.

Imola Informatica

MokaByte è un marchio registrato da:
Imola Informatica S.P.A.
Via Selice 66/a 40026 Imola (BO)
C.F. e Iscriz. Registro imprese BO 03351570373
P.I. 00614381200
Cap. Soc. euro 100.000,00 i.v.

Privacy | Cookie Policy

Contatti

Contattaci tramite la nostra pagina contatti, oppure scrivendo a redazione@mokabyte.it

Seguici sui social

Facebook Linkedin Rss
Imola Informatica
Mokabyte