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.