Nel capitolo Introduzione a Instant Developer Cloud abbiamo visto come un progetto sia costituito da applicazioni, data model e librerie. Abbiamo inoltre descritto i vari container in cui ogni applicazione può funzionare: il container per applicazioni web basato su Node.js, quello per applicazioni mobile basato su Apache Cordova ed infine le PWA.
In questo capitolo entreremo all’interno di un’applicazione per studiare l’architettura degli oggetti.
Per una migliore comprensione del testo seguente vi ricordiamo che la programmazione di Instant Developer Cloud è basata su JavaScript e che il modello di Object Orientation utilizzato è basato sull’uso di prototype. Per il lettore che non avesse familiarità con questi concetti si consiglia un approfondimento preventivo.
Applicazione e sessione #
L’oggetto base dell’architettura di un’applicazione Instant Developer Cloud è un oggetto messo a disposizione dal framework denominato App. Esso è un oggetto di infrastruttura, cioè contiene la definizione di tutte le classi di codice necessarie al funzionamento dell’applicazione. I tipi principali di classi contenuti in App sono i seguenti:
- Classi del framework: ad esempio App.View è la classe base di tutte le videate.
- Classi o videate specifiche dell’applicazione.
- Librerie definite nel progetto.
App è un oggetto definito dal framework al momento dell’attivazione di un processo worker del container in cui l’applicazione è in funzione. In particolare:
- Per il container di applicazioni web: all’avvio di un nuovo worker all’interno di uno specifico processo Node.js, vengono istanziati l’oggetto App e tutte le sue definizioni. Nel medesimo container possono essere presenti più oggetti App separati, uno per ogni processo worker del container.
- Per il container di applicazioni mobile e PWA: esso supporta un unico processo in cui è in esecuzione l’unica sessione applicativa del device o browser; in questo caso verrà creato un solo oggetto App per l’intero container.
App gestisce la lista delle sessioni applicative associate al processo in cui esso è presente. Ogni sessione applicativa è un’istanza della classe Session del framework ed è referenziabile in tutto il codice dell’applicazione tramite la variabile app. La sessione è un oggetto di lavoro, quindi conterrà le istanze delle classi che compongono l’applicazione, man mano che la sessione ne richiede l’utilizzo.
Sono presenti vari tipi di sessione; la più comune è quella collegata ad un browser di un utente che si collega al container e vuole iniziare a lavorare con l’applicazione web.
Ricapitolando: l’oggetto App (con la A maiuscola) rappresenta l’infrastruttura, quindi contiene la definizione delle classi dell’applicazione. L’oggetto app (con la a minuscola) rappresenta la sessione di lavoro, quindi contiene le istanze delle classi dell’applicazione che la sessione di lavoro sta utilizzando per svolgere i suoi compiti.
Nell’immagine sottostante è mostrato un progetto di base che esemplifica questi concetti.
Nell’applicazione chiamata MyApp è contenuta una videata chiamata Videata1, mostrata in anteprima sulla destra. Al centro vediamo il codice dell’evento onStart, evidenziato in rosso, e il codice dell’evento onClick del pulsante Click Me evidenziato in blu.
L’evento onStart viene chiamato dal framework al momento dell’attivazione di una nuova sessione e l’espressione usata per aprire un’istanza della videata è la seguente:
App.Videata1.show(app);
Quindi all’avvio di una sessione viene invocato il metodo statico show della classe Videata1 che, come indicato in precedenza, viene definita all’interno dell’oggetto App che rappresenta l’intera infrastruttura delle classi dell’applicazione MyApp.
Come avviene per tutte le invocazioni di metodi statici in Instant Developer Cloud, come primo parametro viene sempre passato app, cioè il riferimento alla sessione di lavoro per cui il metodo viene invocato. Ritornando all’esempio del metodo show, è solo in questo modo che è possibile per la classe che gestisce la videata apparire nel browser giusto, cioè quello collegato alla sessione di lavoro appena iniziata.
Il metodo show, invocato in modo statico sulla classe della videata, agisce come segue:
- Crea un’istanza della classe – cioè l’oggetto videata vero e proprio – a cui passa il riferimento alla sessione in cui la videata viene aperta.
- Memorizza l’oggetto videata appena creato in una lista di videate aperte all’interno della sessione (app).
- Chiama un metodo interno dell’oggetto videata che inizializza tutti gli elementi visuali contenuti in essa.
- L’oggetto videata comunica alla sessione di creare nel browser i corrispondenti elementi visuali utilizzando un metodo del motore di rendering grafico che è parte del framework.
Vediamo adesso il codice dell’evento onClick relativo al pulsante Click Me presente nella videata. Il codice apre una message box nel browser mostrando il testo “Hello world”. Per ottenere questo risultato l’espressione utilizzata è la seguente:
app.alert(“Hello World”);
In questo caso viene invocato il metodo alert sull’oggetto app che rappresenta la sessione collegata al browser in cui è stato cliccato il pulsante. Il metodo alert è naturalmente presente nell’oggetto app (sessione) in quanto il suo scopo è proprio quello di mostrare un messaggio nel browser collegato alla sessione.
Tipi di sessione #
Il container per applicazioni web supporta i seguenti tipi di sessione:
- web: normali sessioni web che vengono istanziate tramite browser.
- rest: sessioni che gestiscono un comando in modalità rest, solitamente proveniente da un sistema esterno che deve essere integrato.
- webapi: sessioni che gestiscono una webapi di tipo OData generata con Instant Developer Cloud.
- proxy: sessioni che permettono ai dispositivi mobili di collegarsi con il backend del cloud per condividere dati o sincronizzare database.
- server: sessioni batch che svolgono attività periodiche in modalità non presieduta.
Il container per applicazioni mobile e PWA supporta solo una singola sessione di tipo web.
Avvertenza #
Nel container per applicazioni web, tramite App è possibile condividere informazioni tra sessioni o addirittura è possibile per una sessione operare all’interno di un’altra. Questo comportamento è sconsigliato perché può causare effetti collaterali difficili da identificare. In ogni caso non consentirebbe di raggiungere l’intero insieme delle sessioni in esecuzione, che viene suddiviso fra più processi Node.js e quindi in oggetti App non presenti nella medesima virtual machine JavaScript.
Per ottenere questo scopo è invece possibile utilizzare il framework di sincronizzazione, in grado di comunicare in modo sicuro a tutte le sessioni in esecuzione in un determinato container per applicazioni web.
Ciclo di vita di una sessione #
Le sessioni iniziano e terminano in modo diverso a seconda del loro tipo, e a seconda di questi diversi modi, notificano eventi diversi.
Sessioni web #
La sessione nasce quando un browser contatta il server Node.js e, dopo l’attivazione del framework lato client, viene attivata la connessione websocket. In questo momento nel codice dell’applicazione viene notificato l’evento onStart in cui è possibile leggere la richiesta originale del browser.
A differenza di un’applicazione web tradizionale, le applicazioni sviluppate con Instant Developer Cloud non hanno tempo di attesa prestabilito per una sessione inattiva (solitamente 20 minuti). La sessione rimane attiva fino a che il browser viene chiuso, a meno di non impostare la proprietà app.sessionTimeout che permette di terminare la sessione dopo un determinato periodo di inattività.
Quando il browser viene chiuso, la sessione viene terminata immediatamente in quanto il framework client comunica subito al server che è in fase di chiusura. In alcune situazioni la connessione al server si interrompe senza poter dare notizia della chiusura del browser; in questo caso, dopo un periodo di tempo pari a 3 secondi, la sessione viene terminata lato server. Quando la sessione viene terminata, viene notificato l’evento onTerminate a livello di sessione; una sessione può essere terminata anche dal codice dell’applicazione chiamando il metodo app.terminate().
Se il browser entra in background, notifica all’applicazione l’evento onPause. In questo stato, quando ritorna in primo piano, verrà notificato l’evento onResume.
Le sessioni web, infine, notificano l’evento onHistoryPop quando l’utente clicca sul pulsante back del browser, fino alla prima pagina di ingresso nell’applicazione.
Sessioni REST (webapi) #
Le sessioni REST vengono attivate quando un agente (browser o altro) contatta il server inserendo il parametro mode=rest nella query string. In questo caso la sessione ha un ciclo di vita completamente diverso dal precedente, infatti al posto dell’evento onStart viene notificato l’evento onCommand, nel quale è possibile leggere tutti i parametri della richiesta compresi eventuali file allegati alla medesima. I file vengono gestiti dal framework prima di attivare l’evento onCommand, cioè vengono salvati sul file system dell’applicazione nella cartella uploaded.
La sessione REST può rispondere all’agente chiamante tramite il metodo app.sendResponse. Dopo questa chiamata la sessione viene terminata, quindi la risposta deve essere inviata in una volta sola. Il timeout di risposta è impostato a 60 secondi, ed è modificabile tramite la proprietà app.sessionTimeout. Se la risposta non viene data in tempo, la sessione viene terminata con errore.
Esistono due casi particolari di sessioni che si attivano in modalità REST pur non avendo la stringa mode=rest nella query string. Il primo riguarda una chiamata ad una webapi di tipo OData generata con Instant Developer Cloud. Per distinguere questo caso dalle normali chiamate REST è possibile utilizzare il metodo app.isWebApiRequest() che restituisce true se la chiamata è di tipo webapi OData.
Il secondo caso si verifica quando la chiamata all’applicazione avviene da parte di un crawler bot, come ad esempio quello di Google. In questo caso la chiamata viene comunque attivata in modalità REST, così da poter restituire al motore di ricerca una stringa HTML che rappresenta il contenuto della pagina richiesta. Diventa così possibile indicizzare nei motori di ricerca le applicazioni sviluppate con Instant Developer Cloud in modalità SPA (single page application), particolarmente difficili da far indicizzare a tali motori.
Esempio di indicizzazione sui motori di ricerca #
L’indicizzazione sui motori di ricerca di una SPA (single page application) è particolarmente difficile perché i motori di ricerca considerano i siti web come una serie di documenti testuali, separati in varie pagine. Una SPA invece è costituita da una sola pagina, quindi i bot dei motori di ricerca non sono in grado di navigare tali pagine come i siti web tradizionali.
Normalmente un’applicazione non necessita di indicizzare ogni pagina, ma solo i contenuti pubblici, cioè quelli che devono essere trovati da chi utilizza i motori di ricerca. Se, ad esempio, pensiamo ad Amazon, nei motori di ricerca troviamo le pagine di dettaglio dei prodotti venduti.
Per ottenere un comportamento analogo, occorre predisporre due template di pagina da gestire per ogni categoria di contenuto pubblico. Se, ad esempio, vogliamo indicizzare un elenco di case in vendita, potremo gestire i seguenti tipi di query string:
- https://myapp.com/?cat=case
- https://myapp.com/?cat=case&cid=<id della casa>
All’interno dell’evento onStart dell’applicazione che viene notificato da un vero browser, nel primo caso occorre mostrare la pagina di ricerca delle case, mentre nel secondo caso la pagina di dettaglio della casa identificata dal suo id.
Per ottenere l’indicizzazione occorre gestire anche l’evento onCommand, che viene notificato per le medesime query string da un crawler di un motore di ricerca. Nel primo caso occorre restituire una stringa HTML composta dalla lista di case contenute nel database, eventualmente suddividendola in più pagine se la lista contiene migliaia di referenze. Per ogni riga della lista occorre inserire un link del secondo tipo, in modo che il motore di ricerca entri in ogni pagina di dettaglio. Se l’evento onCommand viene chiamato con un link del secondo tipo, sarà sufficiente restituire una stringa HTML che rappresenta il contenuto semantico delle informazioni riguardanti il dettaglio della casa.
In questo modo si otterrà l’indicizzazione della lista e di ogni dettaglio. Quando un browser cliccherà sul link di un motore di ricerca, verrà richiamata la nostra applicazione in modo da creare una sessione web in cui l’evento onStart si preoccupa di visualizzare subito le informazioni trovate tramite il motore di ricerca.
Sessioni offline #
Una sessione offline nasce quando un utente attiva l’applicazione sul proprio device o workstation. Le applicazioni offline vengono installate come app scaricabili da app store per gli smartphone e tablet, oppure come PWA multi-channel. Il termine offline non identifica la mancanza di connessione internet, ma il fatto che l’intera applicazione è in funzione localmente nel dispositivo e continuerà a funzionare anche in mancanza di connessione internet.
La sessione offline si comporta a tutti gli effetti come una sessione web. Occorre però tenere in considerazione le differenze nell’ambiente operativo in cui essa è in esecuzione, fra le quali:
- La sessione web è in funzione in un server Node.js, la sessione offline in una virtual machine di un device o di un browser (PWA). In questo caso la virtual machine dell’applicazione (framework server) è la stessa di quella del motore di rendering (framework client).
- Il database utilizzabile da una sessione web è di tipo Postgres, o comunque è un database cloud. Una sessione offline può utilizzare SQLite; alcuni tipi di PWA non hanno accesso a nessun tipo di database offline. Per maggiori informazioni sulla gestione dei database, si consiglia di leggere il capitolo relativo.
- Il file system dell’applicazione nel cloud ha caratteristiche diverse da quello offline anche se l’interfaccia di programmazione è la stessa. Le PWA possono accedere ai file offline solo in alcuni casi.
- Una sessione offline può accedere ai plugin nativi del device; una sessione web o PWA presenta limitazioni maggiori.
Per distinguere se la sessione è di tipo web o di tipo offline è possibile utilizzare il metodo app.runsLocally() che restituisce true se la sessione è di tipo offline.
Sessioni proxy #
Una sessione proxy rappresenta la controparte cloud di una sessione offline quando l’applicazione utilizza il framework di sincronizzazione incluso in Instant Developer Cloud. Tale framework permette di implementare efficacemente un’architettura di tipo client-cloud senza dover modificare il codice dell’applicazione scritto per la versione web della medesima.
La sessione proxy nasce quando una sessione offline attiva il sistema di sincronizzazione ed in questo momento viene notificato alla sessione proxy l’evento onConnect, che permette di controllare l’accesso e gestire l’invio dei dati al database offline. Quando la sessione offline si disconnette, alla sessione proxy viene notificato l’evento onDisconnect e poi la sessione viene terminata.
Per maggiori informazioni sulle sessioni proxy si consiglia di leggere il capitolo relativo al sistema di sincronizzazione.
Sessioni server #
Una sessione server è in esecuzione nel server Node.js senza avere una controparte browser; di solito viene utilizzata per eseguire processi in modalità batch. Una sessione server funziona a tutti gli effetti come una sessione web, ma senza visualizzare l’interfaccia utente della medesima.
La sessione server nasce automaticamente al momento dell’installazione dell’applicazione su un server se per questa applicazione è stato attivato il flag Attiva server session all’interno della console.
Solitamente nell’evento onStart viene utilizzato il metodo app.isServerSession() per aprire videate specifiche per la gestione dei processi batch della server session. Anche se le videate non vengono visualizzate su un browser utente, il loro comportamento applicativo è identico al caso delle sessioni web.
Per una più semplice gestione delle attività batch, si consiglia di dedicare una videata all’esecuzione di questi processi nella quale sarà presente un timer che permette di eseguire operazioni periodiche.