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.