Finora abbiamo visto come la sincronizzazione permette la comunicazione fra sessioni online e locali tramite scambio di messaggi costituiti da oggetti JavaScript.
Tuttavia le applicazioni utilizzano i documenti per le operazioni di trattamento dati, non semplici oggetti JavaScript: per questo motivo la sincronizzazione è stata integrata con il framework di gestione dei documenti.
Gli obiettivi di questa integrazione sono tre:
- Consentire alle sessioni locali di accedere ai documenti nel cloud come se fossero locali, al fine di semplificare al massimo l’architettura client-cloud.
- Consentire alle sessioni locali di sincronizzare automaticamente i documenti contenuti nel database offline del dispositivo.
- Consentire alle sessioni locali e online di reagire in tempo reale alle modifiche confermate di un documento.
In questo paragrafo verrà illustrato il funzionamento della Document Orientation remota, corrispondente al punto 1) dell’elenco precedente. Nel paragrafo successivo, i punti seguenti. Se non si ha familiarità con i concetti relativi alla Document Orientation, si consiglia di leggere il capitolo relativo.
Leggere documenti dal back end #
L’utilizzo della Document Orientation per gestire i documenti del cloud nelle sessioni locali richiede la configurazione a design time dei documenti e dei metodi di accesso.
A livello di progetto i documenti devono essere contenuti in un’apposita libreria così da poter essere condivisi sia dall’applicazione locale che da quella di back end. I documenti, inoltre, devono essere esplicitamente resi remotizzabili attivando la proprietà di design time Permetti chiamate remote.
A questo punto la sessione locale può utilizzare i metodi di caricamento e salvataggio del documento attivando l’opzione di remotizzazione. In questo caso l’operazione non verrà effettuata rispetto al database del dispositivo, ma inviando la richiesta alla sessione proxy tramite il canale di sincronizzazione, che a sua volta esegue il comando rispetto al database nel cloud.
La riga di codice seguente carica il documento Product con ID=1 dal cloud tramite il canale di sincronizzazione.
var p = yield App.NWBE.Product.loadByKey(app, 1, {remote : true});
L’unica differenza è costituita dall’opzione di remotizzazione che è stata attivata.
Le operazioni remotizzate richiedono che nella sessione locale la sincronizzazione sia stata configurata ed attivata. Se al momento dell’invocazione del comando la connessione non è ancora aperta, ne viene gestita l’apertura prima di procedere con l’esecuzione del comando. Se non è possibile aprire la connessione o essa viene rifiutata, il comando restituisce null, come nel caso in cui il documento non fosse presente nel database; inoltre viene visualizzato un messaggio di warning nel log della sessione.
Il diagramma precedente mostra i passaggi di esecuzione del comando di caricamento remotizzato:
- L’applicazione invia il comando di caricamento al documento Product.
- Il documento passa il comando al framework di gestione dei documenti.
- Essendo stata impostata l’opzione di remotizzazione, la query non può essere eseguita nel database locale, ma viene inviato un messaggio alla sessione proxy tramite il canale di sincronizzazione.
- La sessione proxy riceve il messaggio e lo gestisce richiedendo al framework DO di caricare il documento richiesto.
- Il risultato viene restituito alla sessione locale tramite il canale di sincronizzazione.
- Il framework DO della sessione locale riceve il risultato del comando e lo passa al codice della sessione, in modo trasparente rispetto alla modalità di esecuzione che questa volta è remota invece che locale.
Lo stesso schema di funzionamento avviene per le altre chiamate di caricamento come loadCollection, getRelated, collection.load eccetera. Si noti che gli eventi beforeLoad e AfterLoad vengono eseguiti sia nella sessione locale che nel back end.
Si segnala infine che i metodi di caricamento permettono anche la ricezione di documenti multi-livello impostando la proprietà childLevel del parametro options.
Per vedere un esempio funzionante di Document Orientation remota è possibile aprire il progetto sync-design-patterns e lanciarlo in anteprima FEBE. Per aprire la videata di test aprire il menu della sessione locale e scegliere la voce Remote Documents.
Scrivere documenti nel back end #
Come per le funzioni di caricamento, la remotizzazione delle funzioni di salvataggio di documenti e collection si attiva semplicemente attivando la proprietà remote nelle opzioni del metodo. L’esempio di codice seguente legge un documento dal cloud, cambia il valore di una proprietà e poi lo salva nel cloud.
var p = yield App.NWBE.Product.loadByKey(app, 1, {remote : true});
p.UnitsOnOrder++;
var b = yield p.save({remote : true});
Il principio di funzionamento del salvataggio è il seguente: in primo luogo il documento viene validato localmente, poi, se non ci sono errori, l’intera struttura viene inviata alla sessione proxy tramite la sincronizzazione e poi nuovamente validata e salvata nel cloud.
Se nella sessione proxy vengono apportate delle modifiche alle proprietà del documento, esse vengono restituite alla sessione locale che le applica all’istanza in memoria del documento di cui è stato richiesto il salvataggio.
Se, ad esempio, nell’evento onSave notificato nella sessione proxy viene calcolato un progressivo del documento, al termine dell’operazione di salvataggio questo valore è presente anche nell’istanza del documento della sessione locale.
In modo analogo, anche gli errori segnalati sul documento dalla sessione proxy verranno resi disponibili nell’istanza del documento della sessione locale.
Siccome il codice operativo del documento è lo stesso eseguito nella sessione locale e in quella proxy, è possibile che alcune operazioni vengano eseguite solo quando ci si trova lato cloud e non nel dispositivo. Per distinguere i due casi è possibile condizionare l’esecuzione del codice al valore restituito dal metodo app.runsLocally().
Si segnala infine che, per ragioni di performance e di coerenza dei dati, in caso di documenti multi-livello è preferibile remotizzare il salvataggio dell’intera struttura in un’unica operazione di salvataggio piuttosto che con diverse operazioni separate.
Chiamate remote #
Oltre che leggere o scrivere istanze di documenti, la Document Orientation Remota consente di configurare metodi di documento per essere invocati sia localmente che remotamente, in modo da renderne trasparente l’utilizzo.
Per far sì che un metodo sia remotizzabile, occorre attivare a design time l’opzione permetti chiamate remote. Per renderlo eseguibile sia in modalità locale che remota, bisogna inserire alcune righe di codice all’inizio del metodo, come mostrato nel template di codice seguente:
App.<library>.<document-class>.prototype.<method-name> =
function (<par1>, <par2>)
{
if (app.runsLocally()) {
return yield this.rfc("<method-name>", [<par1>, <par2>]);
}
//
... method code (online)...
}
Un metodo di istanza remotizzabile, quando viene chiamato localmente invoca l’esecuzione remota tramite this.rfc, che ammette tre parametri: il primo è il nome del metodo stesso, il secondo è un array di parametri (quelli passati al metodo) ed il terzo è un oggetto di opzioni, che può essere omesso.
Quando il metodo viene chiamato da una sessione locale, esso esegue solamente la chiamata rfc e ne restituisce il risultato. A sua volta, il metodo rfc chiamato sull’istanza di documento (this) utilizza il canale della sincronizzazione per inviare un messaggio alla sessione proxy per richiedere l’esecuzione del metodo su un’istanza di documento corrispondente a quella originaria.
Per questa ragione anche i dati del documento dovranno essere trasferiti alla sessione proxy, in modo che essa possa ricostruire in memoria l’istanza stessa per poi poterne chiamare il metodo, passando il valore dei parametri.
A questo punto verrà richiamato lo stesso codice del metodo, ma nella sessione proxy app.runsLocally è false, quindi verrà saltata la chiamata a rfc e verrà eseguito il codice vero e proprio del metodo. Il valore di ritorno viene poi comunicato dalla sessione proxy a quella locale tramite il canale di sincronizzazione ed infine tale valore viene restituito dal metodo locale al chiamante.
Se avvengono delle eccezioni all’interno della sessione proxy durante la chiamata del metodo, la medesima eccezione verrà generata dal metodo rfc chiamato nella sessione locale così che anche in questo caso il metodo si comporti allo stesso modo quando viene chiamato localmente o remotamente.
Si segnala che l’unica opzione supportata da rfc è la proprietà loadRemotely, attivabile passando { loadRemotely:true} come oggetto di opzioni. In questo caso non vengono passati tutti i dati dell’oggetto alla sessione proxy, ma solo la chiave primaria, in modo che lato cloud il documento venga istanziato caricandolo dal database invece che rigenerato dai dati inviati dalla sessione locale. Questo comportamento può essere utile in due casi:
- Se i dati del documento sono corposi e si preferisce ricaricarli invece che inviarli.
- Se si desidera operare sui dati aggiornati presenti nel database del cloud invece che su quelli provenienti dalla sessione locale.
Chiamate remote a metodi statici #
Oltre che poter chiamare remotamente i metodi di istanza, è possibile operare in modo analogo sui metodi statici. In questo caso cambia il template di codice da inserire per rendere trasparente la chiamata del metodo e il fatto che non c’è bisogno di comunicare al proxy i dati del documento, perché non si sta lavorando su un’istanza ma sulla classe stessa.
Per i metodi statici è possibile utilizzare il seguente template di codice. Come si può notare, il metodo rfc ha una versione statica che permette la remotizzazione delle chiamate di questo tipo.
App.<library>.<document-class>.<method-name> = function (<par1>, <par2>)
{
if (app.runsLocally()) {
return yield App.<library>.<document-class>.rfc(app,
"<method-name>", [<par1>, <par2>]);
}
//
... method code (online)...
}
Tipi gestibili tramite chiamate remote #
La chiamata remota avviene passando i parametri e ricevendo il risultato dal metodo tramite il sistema di sincronizzazione. Questo implica che i parametri e il risultato devono essere serializzabili in formato stringa in modo da poter essere passati via socket.
I tipi di parametri che ammettono questo comportamento sono: i tipi base (stringhe, numeri, date, oggetti JavaScript, ecc.), le istanze di documento, di collection e di datamap. Se si devono passare altri tipi di dati ad un metodo remotizzabile, si consiglia di convertirli in stringa o in oggetto per poterli ricostruire nella sessione proxy.
Test dei metodi remotizzati #
Il test delle chiamate ai metodi remoti avviene nello stesso modo delle altre funzioni della sincronizzazione, cominciando dalla modalità FEBE. Un esempio di chiamate remote è presente nel progetto sync-design-patterns, avviando l’anteprima FEBE e scegliendo la voce Remote Documents nel menu della sessione locale.
Remotizzazione dell’intera applicazione #
Quando si sviluppa un’applicazione omnichannel, un possibile percorso di implementazione consiste nel progettare l’applicazione inizialmente in modalità online, cioè funzionante tramite browser collegati direttamente al cloud.
Quando l’applicazione è pronta per essere installata nei dispositivi, si deve procedere ad attivare i metodi di sincronizzazione in modo che essa possa funzionare anche con sessioni locali e non solo via browser. È in questo momento che si inizia ad utilizzare la sincronizzazione.
La modalità di utilizzo della sincronizzazione dipende dalle specifiche dell’applicazione, cioè se essa deve funzionare anche in modalità offline, senza connessione ad internet, oppure solo quando la connessione è presente. Nel primo caso è necessario avere i dati in un database locale ed utilizzare la sincronizzazione per allineare i dati del cloud con quelli locali. Questo argomento verrà illustrato in un paragrafo successivo.
Se invece si prevede che la connessione sia sempre presente durante l’utilizzo dell’applicazione, non è necessario configurare l’applicazione per avere una replica locale dei dati, ma è sufficiente automatizzare la remotizzazione delle chiamate ai documenti quando l’applicazione è in esecuzione nei dispositivi.
I passaggi per ottenere questo risultato sono i seguenti:
- Configurare la sincronizzazione.
- Configurare la Document Orientation Remota.
- Rendere remoti i metodi chiamati nella sessione locale, aggiungendo il codice di remotizzazione.
- Non utilizzare direttamente query sul database se non nel codice online dei metodi remotizzabili. Per l’accesso ai dati usare sempre i metodi dei documenti.
- Automatizzare la remotizzazione delle chiamate ai documenti attivando la proprietà remote del framework quando la sessione è locale.
Impostando a true la proprietà App.Document.remote, si ottiene che tutte le chiamate di caricamento e salvataggio saranno automaticamente remotizzate, senza dover aggiungere l’opzione relativa nelle chiamate a metodo.
In questo modo è possibile centralizzare in un unico punto l’attivazione della remotizzazione dei documenti; di solito si utilizza il momento del login, dove si hanno disponibili le credenziali dell’utente. Il codice da usare è simile al seguente:
$btnLogin.onClick = function(event) {
if (app.runsLocally()) {
app.sync.topics = {user: $fldUser.value, pwd: $fldPwd.value}
app.sync.enabled = true;
App.Document.remote = true;
}
let u = App.BE.User.checkUser($fldUser.value, $fldPwd.value);
if (u) {
// ok, procediamo
}
else {
// utente non trovato
}
}
Dove BE.User.checkUser è un metodo remotizzabile che restituisce il documento utente date le credenziali dell’utente richieste dalla videata di login, oppure null se i dati non corrispondono.
Si segnala che la proprietà remote è disponibile anche per una specifica classe di documento oppure per una specifica istanza.
Gestire le eccezioni #
Quando una sessione locale utilizza metodi di documento remotizzati, viene aperta una comunicazione di tipo websocket tra il dispositivo e il back end nel cloud. Siccome i dispositivi utilizzano spesso la rete cellulare, non è detto che si riesca ad instaurare la comunicazione o che la connessione rimanga stabile nel tempo.
Per questa ragione, ogni chiamata remotizzata potrebbe generare eccezioni o restituire valori di errore anche se i dati del database sono corretti. Se un’applicazione utilizza sempre e solo chiamate remote, cioè presuppone che la connessione sia sempre disponibile durante il suo funzionamento, è possibile intercettare l’evento app.sync.onConnectionStatusChange dal lato della sessione locale per avvisare l’utente che l’applicazione è utilizzabile o meno a seconda della presenza della connessione.
Un meccanismo di avviso molto semplice ed efficace consiste nell’aprire un popup di tipo loading se la connessione non risulta attiva, e nascondere tale popup nel momento in cui essa ritorna disponibile. In questo modo l’interfaccia utente dell’applicazione non può essere utilizzata mentre la connessione non è attiva e, di conseguenza, l’utente non potrà fare azioni che richiederebbero l’accesso al server nel cloud.
Gestire la sicurezza #
L’utilizzo del sistema di Document Orientation Remota mette in comunicazione un dispositivo esterno al cloud con il back end. In questi casi è richiesta la verifica delle credenziali del dispositivo per controllare se può accedere e quali dati può vedere.
Per effettuare questa verifica è possibile utilizzare l’evento app.sync.onConnect nell’applicazione di back end. Ci sono diverse soluzioni, in funzione dei dati passati all’evento al momento della creazione della connessione. Quella migliore consiste nell’utilizzare le credenziali della sessione locale come topics della sincronizzazione, in modo che nell’evento onConnect si possa verificare se esse corrispondono ad un utente registrato o meno.
Se l’utente non viene riconosciuto, la connessione deve essere chiusa, altrimenti si può proseguire. Se si deve consentire la lettura solo di determinati dati in funzione dell’utente, durante l’evento onConnect è possibile memorizzare il documento che rappresenta l’utente in una proprietà della sessione, che potrà poi essere usata nelle query di caricamento dei documenti per limitare l’accesso.
Un esempio di questa strategia di controllo è visibile nel codice seguente:
app.sync.onConnect = function (options)
{
if (app.sync.topics) {
if (app.sync.topics.userId) {
app.account = yield App.TBBE.Account.loadByKey(app,
app.sync.topics.userId);
}
if (app.sync.topics.email && app.sync.topics.pwdhash) {
app.account = yield App.TBBE.Account.loadByKey(app,
{email:app.sync.topics.email, pwdhash:app.sync.topics.pwdhash);
}
}
//
if (!app.account) {
options.cancel = true;
options.cancelReason = “User not found”;
}
};
Per aumentare il grado di sicurezza del sistema è importante ridurre la superficie di attacco, attivando per la remotizzazione il numero minimo di documenti e di metodi. Una buona soluzione in questo senso consiste nella definizione di un documento aggiuntivo, non legato ad una tabella del database, che contiene i metodi necessari ai client per accedere ai dati. A questo punto è possibile rendere remotizzabile solo questo documento e i suoi metodi in modo da concentrare in un unico punto l’accesso esterno ai dati del back end.