Dopo aver visto come connettere le varie sessioni al sistema, illustriamo il comportamento di base della sincronizzazione: lo scambio di messaggi tra sessioni.
Per mostrare i vari passaggi, utilizzeremo il seguente caso: si hanno un’applicazione che ammette una chiamata REST per acquisire lo stato del segnale di allarme di una centralina, e delle sessioni di visualizzazione che mostrano lo stato di una specifica centralina o di un insieme di centraline collocate nel medesimo sito.
In questo caso, la sessione che gestisce la chiamata REST deve avvisare del cambiamento le sessioni di visualizzazione. Questo deve avvenire solo per le sessioni che visualizzano lo stato della centralina in cui è avvenuto l’evento.
Inviare e ricevere messaggi #
Per inviare un messaggio è sufficiente utilizzare il metodo sendMessage dell’oggetto app.sync. Ad esempio la sessione REST che riceve i dati dell’allarme potrebbe inviarli alle altre sessioni tramite questa riga di codice:
app.sync.sendMessage({
topics:”1212”,
body:{sensor:”W1”, status:”ON”},
ttl:0
});
Il primo parametro sono i topics del messaggio, che spiegheremo nel paragrafo successivo, il secondo è il contenuto (body) del messaggio e l’ultimo (ttl) è il tempo di vita del messaggio.
Come contenuto del messaggio possiamo usare un qualunque oggetto JavaScript, mentre il tempo di vita è un numero intero che rappresenta il numero di secondi per cui il messaggio deve essere memorizzato nel caso non sia possibile consegnarlo subito alle sessioni offline. Se è pari a 0 o viene omesso, il messaggio viene inviato solo alle sessioni attualmente collegate.
Il messaggio viene notificato tramite l’evento app.sync.onMessage, che ne contiene i dati. Per intercettare il messaggio ed aggiornare l’interfaccia utente si può usare la seguente riga di codice:
app.sync.onMessage = function (message)
{
App.Pages.postMessage(app, message);
};
Come abbiamo visto nel capitolo relativo al framework IonicUI, il metodo postMessage invia un messaggio alla videata attiva del page controller. A questo punto la videata può intercettare l’evento onMessage ed aggiornare gli elementi visuali di conseguenza. Si ricorda che impostando message.bc = true, il messaggio verrà consegnato a tutte le videate aperte e non solo a quella attiva.
I topics #
Il sistema di sincronizzazione non deve inviare tutti i messaggi ad ogni sessione collegata, ma occorre effettuare una selezione. Prima di poter scambiare i messaggi, è necessario comprendere come avviene il filtro del sistema di sincronizzazione, in modo da poter inviare ad ogni sessione solo i messaggi che la riguardano.
Per poter effettuare questa selezione, è necessario familiarizzare con il concetto di topics di un messaggio e di topics della sessione.
I topics di un messaggio rappresentano i suoi argomenti e sono rappresentati come oggetto JavaScript. I topics della sessione sono gli argomenti a cui una determinata sessione dichiara di essere interessata, anch’essi espressi come oggetto JavaScript.
In questo modo un messaggio viene inviato ad una sessione solo se i topics del messaggio sono compatibili con i topics della sessione.
Siccome i topics sono oggetti JavaScript, essi possono essere composti a più livelli, sia tramite array, che con valori singoli.
Nell’esempio delle centraline, i topics del messaggio di cambio stato dell’allarme devono contenere almeno il codice della centralina. Ogni sessione può dichiarare a quale centralina è interessata usando il suo codice come topics della sessione di sincronizzazione. Se quindi i topics del messaggio sono “1212”, i topics delle sessioni che intercettano il messaggio possono essere definiti come segue:
app.sync.topics = “1212”;
Siccome le centraline sono raggruppate in diversi siti, una sessione può visualizzare lo stato di tutte le centraline di un sito. Per farlo essa può impostare come topics della sessione un array che contiene tutti i codici delle centraline del sito, come mostrato nella riga di codice seguente:
app.sync.topics = [“1212”, “1225”, “1201”, ...];
Una soluzione alternativa è quella di usare come topics del messaggio sia il codice della centralina che quello del sito, ad esempio:
app.sync.sendMessage({topics:{box:”1212”, site:”east”}, ...});
In questo modo le sessioni di visualizzazione potrebbero “sottoscrivere” uno o più “canali” di interesse: alcune potrebbero essere interessate ai box, altre ai siti. In questo caso una sessione deve definire a cosa è interessata, inserendo un asterisco nelle proprietà rimanenti. Vediamo alcuni esempi:
// Mi interessa solo una centralina 1212, senza considerare il sito
app.sync.topics = {box:”1212”, site:”*”};
// Mi interessano tutte le centraline di un sito
app.sync.topics = {box:”*”, site:”east”};
// Mi interessano varie centraline (anche di siti diversi)
app.sync.topics = {box:[”1212”,“2519”], site:”*”};
// Mi interessano varie centraline, di un unico sito
app.sync.topics = {box:[”1212”,“1201”], site:”east”};
// Mi interessano più siti
app.sync.topics = {box:”*”, site:[”east”, “west”]};
È possibile che una sessione sia interessata a tutti i messaggi; in tal caso può usare come topics il carattere asterisco. In generale questo non deve avvenire perché inviare tutti i messaggi a tutte le sessioni può peggiorare le performance e minare la privacy del sistema.
Topics come canali di interesse #
L’uso di un oggetto JavaScript con diverse proprietà come topics dei messaggi e delle sessioni può essere interpretato come la dichiarazione dei possibili “canali” a cui le varie sessioni possono dichiarare di essere interessate.
In un sistema informativo complesso, infatti, si potrebbero scambiare messaggi relativi a diverse entità, anche completamente indipendenti. Ad esempio, oltre al sistema degli allarmi, nello stesso sistema informativo può essere presente lo scambio di messaggi relativi al decollo o all’atterraggio di aerei negli aeroporti.
In questo caso si possono definire due canali di interesse: le centraline e i voli. Per quanto riguarda le centraline, i dati importanti sono il codice e il sito, mentre per i voli il codice dell’aereo e quello dell’aeroporto.
Se si invia un messaggio relativo ad una centralina verranno usati i seguenti topics:
app.sync.sendMessage({topics:{alarm:{box:”1212”, site:”east”}, ...});
Mentre per un volo:
app.sync.sendMessage({topics:{flight:{plane:”UK1234”, airport:”BLQ”},...});
Le sessioni interessate agli allarmi sottoscriveranno solo il canale allarmi. Ad esempio:
app.sync.topics = {alarm:{box:”*”, site:[”east”, “west”]}};
Mentre quelle interessate agli aeroporti sottoscriveranno solo il canale voli. Ad esempio:
app.sync.topics = {flight:{plane:”*”, airport:”BLQ”}};
Se una sessione fosse interessata sia ad aeroporti che ad allarmi potrebbe avere come topics quanto segue:
app.sync.topics = {flight:{plane:”*”, airport:”BLQ”},
alarm:{box:”*”, site:”east”}};
Topics delle sessioni locali #
Le sessioni locali definiscono i topics come tutte le sessioni nel cloud, tuttavia esse entrano nel sistema di sincronizzazione per mezzo della sessione proxy ad esse collegata. Quando una sessione locale apre la connessione, essa passa i suoi topics alla sessione proxy, che può cambiarli a suo piacimento. La sessione locale quindi riceverà i messaggi relativi ai topics della sessione proxy e non ai suoi.
Questo funzionamento serve come validazione dei topics della sessione locale rispetto al sistema cloud. Se ad esempio viene aperta una connessione che utilizza come topics l’ID dell’utente della sessione locale, può essere utile modificare i topics della sessione proxy in funzione dei dati che tale utente deve vedere. Nell’evento onConnect della sessione proxy, dopo aver verificato che l’utente sia valido, si possono modificare i topics in modo da inviare alla sessione locale non solo i messaggi che riguardano direttamente l’utente, ma anche quelli che riguardano altre entità che l’utente può vedere.
Immaginiamo, ad esempio di voler gestire un sistema di messaggistica in cui è possibile inviare messaggi fra due utenti oppure agli utenti di un gruppo, come avviene ad esempio in WhatsApp.
Il messaggio tra due utenti può essere inviato con i seguenti topics:
app.sync.sendMessage({topics:{sender:”ute0”, receiver:”ute1”}, ...});
Mentre il messaggio relativo inviato in gruppo potrebbe avere i seguenti topics:
app.sync.sendMessage({topics:{sender:”ute0”, receiver:”grp3”}, ...});
Quando una sessione locale apre la connessione, essa si può presentare con i seguenti topics:
app.sync.topics = “ute0”;
A questo punto, in funzione dei gruppi a cui l’utente è iscritto, la sessione proxy può modificare i topics come segue:
app.sync.topics = [
{sender:“ute0”, receiver:”*”}, // messaggi inviati da me
{sender:“*”, receiver:”ute0”}, // messaggi inviati a me
{sender:“*”, receiver:”grp3”}, // messaggi inviati al gruppo 3
{sender:“*”, receiver:”grp555”}, // messaggi inviati al gruppo 555
…
};
In questo modo la sessione locale riceve i messaggi inviati dall’utente, quelli inviati all’utente e quelli inviati a tutti i gruppi a cui l’utente è iscritto.
Variazione dei topics durante una sessione #
Abbiamo visto che i topics di una sessione locale vengono inviati alla corrispondente proxy all’apertura in modo che l’evento onConnect possa gestirli.
È possibile che, in seguito, la sessione locale voglia modificare i suoi topics senza dover chiudere e riaprire la connessione.
Per ottenere questo risultato la sessione locale deve modificare i suoi topics e poi usare il metodo app.sync.notifyTopicsChanged che invia i topics alla sessione proxy senza resettare la connessione. Nella sessione proxy viene notificato l’evento onTopicsChanged in modo che essa possa gestire la variazione.
I messaggi permanenti #
Abbiamo visto che il terzo parametro della funzione sendMessage rappresenta il tempo, misurato in secondi, per cui il messaggio rimane ricevibile da una sessione locale che si collega dopo che il messaggio è stato inviato.
Al contrario, le sessioni collegate direttamente al server nel cloud (sessioni online) non ricevono i messaggi inviati prima dell’inizio della sessione.
Questa differenza nasce dal fatto che una sessione online all’avvio può analizzare l’intero stato del sistema leggendo direttamente il database nel cloud. Inoltre, essendo in esecuzione nel server, per definizione essa rimane connessa alla sincronizzazione fino a che non termina. Ecco perché nelle sessioni online sono importanti solo i messaggi che arrivano dopo l’avvio.
Invece per una sessione locale è più difficile leggere l’intero stato del sistema perché essa non è connessa al database nel cloud. Inoltre, mentre rimane attiva, la sessione locale può connettersi e disconnettersi dalla sincronizzazione varie volte a causa dello stato della rete cellulare. Per le sessioni locali è quindi essenziale ricevere i messaggi inviati anche quando erano disconnesse, altrimenti potrebbero perderli senza venirne a conoscenza. Da qui la necessità dei cosiddetti messaggi permanenti.
Gestione dei messaggi permanenti #
I messaggi permanenti vengono memorizzati in una tabella del database indicato al sistema di sincronizzazione tramite la proprietà app.sync.dataStore. La tabella, di nome z_sync, viene creata e gestita automaticamente dal sistema.
La tabella z_sync viene creata sia nei device che nel database nel cloud. Nei device essa serve per memorizzare i messaggi inviati tramite il dispositivo mentre questo è offline. Essi verranno consegnati all’apertura della connessione e poi cancellati dal database.
Nel cloud, la tabella z_sync memorizza i messaggi permanenti di tutte le sessioni non appena essi vengono consegnati, cancellandoli solo quando diventano più vecchi del timeout. In questo modo, quando una sessione locale si connette, la tabella z_sync nel cloud viene scansionata per trovare i messaggi a cui la sessione è interessata che devono ancora essere consegnati.
Messaggi permanenti: quando utilizzarli? #
Finora abbiamo visto come configurare ed inviare messaggi di base tramite il sistema di sincronizzazione. Nei paragrafi seguenti vedremo come applicare la sincronizzazione alla gestione dei documenti, in cui i messaggi scambiati non sono più semplici oggetti JavaScript, ma documenti completi.
Non è quindi consigliabile utilizzare la sincronizzazione di base per impostare il proprio sistema di scambio messaggi; il metodo migliore è sempre quello di utilizzare la sincronizzazione applicata ai documenti come illustrato nel paragrafo Document Orientation Remota.
Per questa ragione la gestione dei messaggi permanenti viene disabilitata per default al livello dei messaggi di base. Se si desidera utilizzare i messaggi permanenti di base, è necessario impostare options.skip = false nell’evento onConnect.
Test della sincronizzazione #
Una volta completate la configurazione e le funzioni di scambio messaggi, il sistema di sincronizzazione deve essere testato. L’IDE possiede una modalità di esecuzione delle applicazioni chiamata Anteprima Back End – Front End, abbreviata come FEBE, in grado di eseguire contemporaneamente due sessioni: una in modalità locale e la seconda in modalità online.
La modalità FEBE si attiva tramite la freccia posta sulla destra del pulsante di anteprima. Una volta attivata, vengono mostrate le opzioni di configurazione, come mostrato nell’immagine seguente:
In questa videata è possibile scegliere l’applicazione del progetto che deve essere usata come front end (sessione locale) e come back end (sessione online). È possibile usare anche la stessa applicazione in entrambi i casi.
È inoltre possibile scegliere se le sessioni devono essere avviate nel browser o in un dispositivo connesso all’IDE tramite l’applicazione InstaLauncher, ed infine se deve essere attivata la modalità di debug full trace o meno.
Confermando le opzioni, le sessioni verranno attivate. Se si desidera modificare le opzioni, occorre disattivare e riattivare la modalità FEBE.
Quando l’applicazione viene lanciata in FEBE, la proprietà app.sync.serverURL della sessione locale viene preimpostata dal framework in modo che si possa connettere alla sessione online corrispondente. Per questa ragione, tale proprietà dovrebbe essere impostata da codice solo se viene trovata vuota.
Si noti inoltre che la sessione online funge sia da sessione proxy per la sessione locale che da sessione browser per la visualizzazione dei dati del cloud. Per tale sessione, quindi, vengono notificati sia gli eventi onStart che onConnect.
Il vantaggio di questa soluzione consiste nel fatto che la sessione browser/proxy può mostrare a video più facilmente gli effetti delle operazioni di sincronizzazione che avvengono tramite la sessione locale.
Occorre tenere presente, tuttavia, le possibili interazioni nell’avvio dei due tipi di sessioni. Ad esempio, se la sessione browser imposta una proprietà di sessione come app.loggedUser, e tale proprietà viene usata nell’evento onConnect senza impostarla anche in quel caso, è possibile che l’applicazione funzioni correttamente in modalità FEBE ma fallisca quando viene installata.
Quando l’applicazione è in funzione in modalità FEBE, possiamo notare che la barra della console dell’IDE mostra contemporaneamente i messaggi delle due sessioni. Quella locale (front end) sulla sinistra, quella online (back end) spostata a destra. Nell’immagine seguente il messaggio “connecting” proviene dal back end, mentre gli altri dal front end.
Come esempio del sistema di invio e ricezione messaggi è possibile aprire il progetto sync-design-patterns e lanciarlo in anteprima FEBE. Per aprire la videata di test del sistema di scambio messaggi, aprire il menu della sessione locale e scegliere la voce Sending Messages.
A questo punto è possibile selezionare i topics del messaggio sulla destra e quelli della sessione di sincronizzazione sulla sinistra. Poi occorre premere il pulsante Subscribe Topics per aggiornare la sessione di sincronizzazione online ed infine provare ad inviare il messaggio dalla sessione locale a quella online. Se il messaggio viene ricevuto, verrà visualizzato nel campo apposito della sessione online, evidenziato tramite un’animazione.
Si possono fare vari esperimenti provando a variare i topics del messaggio e quelli della sessione di sincronizzazione.
Test con applicazione in produzione #
Dopo aver testato la sincronizzazione tramite FEBE, è importante provare il funzionamento nell’ambiente di produzione. La modalità di test consigliata è la seguente:
- Attivare il log strutturato delle sessioni impostando la proprietà app.sessionName negli eventi onStart, onConnect e onCommand.
- Installare in un server di produzione l’applicazione di back end attivando l’opzione di debug a runtime durante la configurazione dell’installazione.
- Eseguire in anteprima offline nell’IDE l’applicazione di front end. Questa modalità crea una sessione locale nel browser che si connette al back end di sincronizzazione installato nell’ambiente di produzione.
Se vengono evidenziati problemi nel back end non rilevati in modalità FEBE, si consiglia di aprire il debug a runtime della sessione proxy a cui la sessione locale in anteprima nell’IDE si è connessa. Tale sessione viene listata nella pagina log strutturato dell’installazione nel server di produzione. Nell’immagine seguente viene evidenziato il pulsante per accedere alla pagina log strutturato di un’installazione tramite la console di Instant Developer Cloud.
SI ricorda che per attivare il log strutturato di un’installazione occorre dare un nome alle sessioni che si desidera tracciare impostando da codice la proprietà app.sessionName. Inoltre occorre attivare la raccolta dei dati di log tramite gli appositi comandi nella pagina del log strutturato della propria installazione.
L’ultima fase di test della sincronizzazione consiste nell’utilizzare il back end nell’ambiente di produzione e l’applicazione di front end installata su un dispositivo, anche tramite InstaLauncher. Se in questa fase si verificano anomalie non rilevate dalle fasi di test precedenti, si consiglia di utilizzare il debug a runtime della sessione proxy insieme alla raccolta dati di analitica o inviando messaggi di log dal device di front end alla sessione proxy tramite il servizio di log illustrato nel paragrafo successivo.
Usare il sistema di log della sincronizzazione #
Il sistema di sincronizzazione di Instant Developer Cloud include alcuni servizi aggiuntivi fra i quali un servizio che permette di inviare messaggi di log da una sessione locale alla corrispondente proxy. Il servizio viene utilizzato nella sessione locale tramite i seguenti metodi:
app.sync.log.log(...);
app.sync.log.warn(...);
app.sync.log.error(...);
Nella sessione proxy viene notificato l’evento onClientMessage in cui è possibile gestire il messaggio, ad esempio inserendolo in un database o inviandolo ad una applicazione di visualizzazione. Se questo evento non viene gestito, il messaggio appare nel log della sessione proxy ed è quindi leggibile tramite il log strutturato o tramite i log del server.