Tic-tac-Jolie

V parte: Porte e importazione di file esternedi e

Last time on Tic-tac-Jolie

Nella precedente parte abbiamo:

  • commentato il servizio che implementa il giocatore;
  • approfondito lo strumento dei correlation sets con cui Jolie correla i messaggi ai rispettivi processi a cui sono logicamente destinati;
  • analizzato la strutturazione ad albero dei tipi di dati di Jolie e della valutazione dei loro percorsi di accesso in maniera dinamica.

È arrivato il momento di parlare di porte e importazione di file esterni.

 

Le porte

Le porte, in Jolie, sono viste come variabili e quindi posso modificare i loro campi (ad esempio, location) a tempo di esecuzione. In questo modo possiamo agganciare microservizi a tempo di esecuzione e non solo staticamente nel codice scritto.

È possibile realizzare quanto mostrato in figura 1.

Figura 1 – Rapporto tra dynamic binder e target service.

Figura 1 – Rapporto tra dynamic binder e target service.

 

Ciò è quanto inserito dentro l’operazione move() con la riga di codice:

User.location = circle_participant.location;

in cui cambiamo l’ubicazione in rete per poter rispondere prima adun giocatore e subito dopo a un altro giocatore.

 

Import di file esterni

Poiché le interfacce possono essere utilizzate in più servizi, è naturale che esse possano risiedere in un file a parte in modo da poter essere importate. Solitamente tali file hanno come estensione .iol.

Nei file .iol sono anche inclusi i servizi quali possono essere la Console, l’Accesso database, e così via, realizzati in Java e volti a fornire, nella notazione

<operazione>@<nome del file importato>

le cosiddette chiamate classiche a libreria.

 

Eccezioni interne al programma

È possibile, come nei linguaggi imperativi, dichiarare delle eccezioni e dei gestori di eccezione. Le eccezioni sono segnali identificati dai loro nomi. A tal fine si dichiara un oggetto scope (si veda nella parte VI che sarà pubblicata il prossimo mese, l’appendice A.8 per la sintassi).

Il fine di scope è di fungere da contenitore all’interno del quale intercettare e gestire i fault generati dal behaviour contenuto (come si vedrà in VI parte – Appendice, al punto A.9 per la sintassi fault handler).

Anche in Jolie vi è l’effetto bolla: l’eccezione non intercettatata all’interno di uno scope viene fatta risalire allo scope padre, che contiene lo scope figlio.

Nel caso di eccezioni che siano generate in un’operazione richiesta-risposta, se l’eccezione non è intercettata, viene restituita al chiamante; quindi, nella dichiarazione dell’operazione, dovremo esplicitare l’esistenza di questa possibile eccezione in ritorno alla nostra richiesta.

        scope( move ) {
            install( MoveNotAllowed => nullProcess );

installa un gestore di eccezioni per il messaggio MoveNotAllowed, a cui è associato un codice che nel caso specifico è l’operazione nullProcess, analoga all’operazione NOP in Assembler (nessuna operazione); l’eccezione non viene gestita e viene poi restituita al chiamante non essendoci uno scope di livello superiore a cui passarla.

Le eccezioni che siano restituite al chiamante devono essere dichiarate nell’interfaccia che contiene l’operazione chiamata

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

Le eccezioni possono essere accompagnate da un tipo di dato in risposta, nel nostro caso un messaggio.

Una nota sulle eccezioni inerenti i tipi accettati dalle operazioni

Nel caso di un chiamante che invochi un’operazione con tipi errati rispetto a quelli accettati, il sistema automaticamente restituisce al chiamante un’eccezione di tipo TypeMismatch. Il programmatore non deve assolutamente gestire tali situazioni.

 

Modalità di esecuzione del servizio

Jolie supporta tre tipi di modalità di esecuzione del servizio (precisamente del behaviour):

  • sequential
  • concurrent
  • single

Vediamole di seguito.

Single

Il servizio viene eseguito, riceve la chiamata a un’operazione oppure esegue una parte di codice e poi termina. Utile per creare degli script.

Sequential

Viene chiamata un’operazione e, fino al suo termine, non è possibile accedere a qualsiasi altra operazione.

Concurrent

All’esecuzione del behaviour individuato è associato ed eseguito un suo processo, rendendo il servizio immediatamente disponibile a servire ulteriori richieste. Nel nostro caso, startGame non inibisce il servizio ad accettare eventuali richieste di operazioni startGame, listOpenGames, move.

In figura 2 vi è una situazione desiderata.

Figura 2 – La situazione come desiderata.

Figura 2 – La situazione come desiderata.

 

Idealmente, vogliamo che giocatore 1 e giocatore 3 giochino assieme, e che possano farlo anche giocatore 2 e giocatore 4. La scelta del tipo di esecuzione è indicata nella sezione deployment:

execution { concurrent}

Nell’Appendice, che sarà pubblicata nel prossimo numero, la sintassi sarà illustrata al punto A.7.

Ciascuna istanza ha il suo ambiente (scope) ove sono conservate le variabili locali. Inoltre, all’avvio del servizio, è necessario eseguire un insieme di istruzioni di inizializzazione. Tale sequenza è racchiusa in un’apposita sezione denominata init.

init {
    behaviour
}

Nel nostro caso

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

va a recuperare — dalla porta esterna Runtime ove gira l’omonimo servizio, con l’operazione getLocalLocation — l’ubicazione in rete del servizio che andremo a memorizzare nella porta.

Sia nel caso sequential che concurrent, in main deve essere presente un input statement (nel nostro caso abbiamo un “input guard”).

Tali operazioni di ingresso sono dette “starting operation”. Esse devono far parte di una input guard, ossia sono mutualmente esclusive: mentre viene eseguito il corpo dell’operazione che ha ricevuto il messaggio, vengono disabilitate le altre operazioni, che saranno riabilitate al completamento del codice associato all’operazione appena eseguita.

La chiamata a tali operazioni darà luogo all’inizio di una sessione.

 

Conclusioni

Con questa serie abbiamo voluto mostrare la flessibilità del linguaggio Jolie, descrivendo un’applicazione che molti avrebbero implementato in maniera classica con un unico monolite ma che in questo caso viene implementata con un’architettura a microservizi.

Figura 3 – Ma come è poi finita la partita?

Figura 3 – Ma come è poi finita la partita?

 

Nel prossimo e ultimo articolo della serie, riporteremo un’appendice con la sintassi richiamata nel corso del testo. E ricordiamo ancora che è disponibile il sito ufficiale di Jolie [1] sia per la documentazione, sia per la possibilità di scaricare l’interprete.

 

Riferimenti

[1] Il linguaggio Jolie

http://www.jolie-lang.org/

 

[2] Un’introduzione ai microservizi

https://en.wikipedia.org/wiki/Microservices#Introduction

 

[3] Il codice nel repository

https://github.com/jolie/examples/blob/master/02_basics/5_sessions/tris/tris.ol

 

 

Condividi

Pubblicato nel numero
269 febbraio 2021
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…
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…
Articoli nella stessa serie
Ti potrebbe interessare anche