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.