vai al contenuto principale

Tipi di Handler e modellazione

Tipi di Handler #

Come accennato in Ciclo di Sviluppo, gli Handler Livebase si dividono in due gruppi: i modellabili (o model-dependent), che dipendono dalle classi e da altri elementi definiti in un modello, e i default, che invece esistono a prescindere dal modello usato.

Handler

Gli Handler modellabili sono a loro volta divisi in due famiglie: Persistency Handlers e Interaction Handlers:

  • Persistency Handlers: include il SaveActionHandler, il ValidationHandler e i tre DatabaseHandler (DatabaseInsert, DatabaseUpdate, DatabaseDelete). Consentono di gestire le diverse fasi del processo di salvataggio/modifica di un oggetto e vengono richiamati nell’ordine in cui li abbiamo elencati. Il SaveActionHandler consente di gestire i campi di una form in scrittura, il ValidationHandler di eseguire una validazione custom dell’istanza di una classe, infine i DatabaseHandler si collocano nella fase di commit della transazione sul database.
  • Interaction Handlers: include solo i FormActionHandler, che vengono richiamati a partire da eventi custom definiti in uno o più Application Schema; generalmente questi eventi si invocano tramite opportune query GraphQL.

Gli Handler di default sono l’AuthenticationHandler, il LogoutHandler e lo ScheduledTask).

Modellare gli Handler #

Per aggiungere un Handler a una classe del modello occorre, dal Database Schema , aprire il Class menu (facendo click destro sull’header della classe) e selezionare la voce New handler corrispondente tra quelle disponibili, come mostrato in figura:

Des classMenu handlers

Una volta scelto il tipo di Handler dovremo:

  • Digitare un nome, che dovrà essere univoco nel namespace della classe.
    Una buona pratica è usare un nome rappresentativo dell’azione che vogliamo far compiere, ad esempio sendEmail se l’Handler invierà una mail.
  • Per alcuni Handler (tra cui il FormActionHandler) è necessaria una configurazione ulteriore nell’Application Schema .
  • Per alcuni Handler sono inoltre disponibili opzioni extra nel menù contestuale Service Handler menu, accessibile facendo click destro sull’Handler dal Database Schema .

Handler e spazio dei nomi #

Gli Handler definiti su una classe condividono un namespace separato da quello degli attributi. Nel diagramma sono elencati in un’altra lista sotto quella degli attributi; ad esempio, in figura sono presenti tre diversi Handler su Class1.

Des handlers example

Handler e viste applicative #

Analogamente agli altri elementi del diagramma – classi, attributi, relazioni – è possibile abilitare/disabilitare un Handler per una specifica vista applicativa: per farlo è sufficiente cliccare sull’Handler dall’Application Schema per fare toggle del suo stato, così come illustrato nel tutorial (Definisci viste applicative e profili utente). Ciò consente di scegliere in quali viste applicative consentire l’uso di un certo plugin su quella classe.

Des handlers example app schema

Persistency Handler #

SaveActionHandler #

Viene richiamato ogniqualvolta si invoca il servizio GraphQL save.

La action verrà eseguita prima che i dati vengano validati e salvati sul database; questo consente di gestire in scrittura i campi compilati dall’utente ed eventualmente modificarne il contenuto (ad esempio, formattare gli attributi nativi o aggiungere informazioni sul Database e collegarle all’oggetto creato inizializzando relazioni di default).

Lo sviluppatore può specificare le operazioni da far eseguire all’handler implementando il metodo doAction() della relativa classe, e interagendo con il contesto (SaveActionHandlerContext) che ne costituisce il parametro.

SaveActionHandlerContext #

L’entity può essere acceduta sia nella versione precedente alla modifica che in quella attuale, ed in quest’ultimo caso possono essere scritti sia i campi interni dell’entity che quelli di eventuali oggetti correlati.

L’handler può unicamente restituire un risultato vuoto tramite la chiamata none(), effettuata sull’oggetto result offerto dal contesto.

ValidationHandler #

Viene richiamato dopo il SaveActionHandler (se presente sulla stessa classe) e prima del commit di una transazione sul Database relativa a una delle operazioni (insert o update) sulle istanze di una specifica classe. Questo Handler consente di implementare una logica di validazione personalizzata dell’istanza della classe, con la quale poter controllare regole non esprimibili nel modello (ad esempio, tramite restrizioni sul dominio degli attributi o Class warning, oppure regole la cui specificità rende l’approccio low-code troppo oneroso – ad esempio, eseguire una chiamata a un servizio esterno per validare dei campi.

Lo sviluppatore definisce il processo di validazione implementando il metodo validate() della classe dell’handler, e interagendo con il contesto (ValidationHandlerContext) che ne costituisce il parametro.

ValidationHandlerContext #

L’entity coinvolta può essere acceduta nella versione precedente e in quella attuale, ma unicamente in lettura.

L’handler deve restituire un risultato che indichi il successo o il fallimento della validazione personalizzata. Per fare questo, una volta ottenuto il result, si deve effettuare una delle seguenti chiamate:

  • success() : Per segnalare l’esito positivo;
  • failure(msg) : Per segnalare l’esito negativo. A questa chiamata va passata una stringa che rappresenta un messaggio di errore, il quale verrà restituito in una risposta GraphQL.

È offerto anche l’accesso all’insieme dei valori localizzati per la Cloudlet, come sono stati definiti all’interno del Designer, con l’invocazione del metodo getCloudletLocales().

ValidationHandler bloccanti #

Di default, la validazione aggiuntiva eseguita da un ValidationHandler è non bloccante: se l’invocazione di save non viene validata con successo, l’API restituisce un messaggio di tipo warning, in modo del tutto analogo a quanto accade per i Class warning non bloccanti.

Possiamo modificare questo comportamento agendo sul flag Blocking dal menù contestuale accessibile facendo click destro sul ValidationHandler: spuntando questa opzione la validazione sarà bloccante, e in caso di validazione negativa il framework rifiuterà la save.

Il metodo failure(msg) del context ha un parametro booleano opzionale, che permette di sovrascrivere in determinati casi il comportamento specificato nel Designer: un valore di true renderà la validazione bloccante, mentre un valore di false la renderà non bloccante.

DatabaseHandler #

Per ogni transazione esiste una variante specifica di questo Handler: DatabaseInsertHandler, DatabaseUpdateHandler e DatabaseDeleteHandler. Ciascuno viene richiamato prima e dopo il commit di una transazione sul Database relativa a una delle operazioni (insert, update, delete) sulle istanze di una specifica classe. Nel caso delle prime due, ciò avviene sempre dopo il ValidationHandler (se è presente sulla stessa classe). Questi Handler consentono di eseguire azioni che dipendono dal contenuto di una transazione – ad esempio, eseguire una query SQL custom.

È possibile cambiare il tipo di transazione che richiama il DatabaseHandler dalla voce Change type dal menù contestuale dell’Handler.

Ciascun DatabaseHandler richiede l’implementazione di due metodi da parte dello sviluppatore: beforeCommit(), per specificare le operazioni da eseguire prima del commit della transazione, e afterCommit(), per le operazioni successive allo stesso commit. Nel primo caso, il contesto offerto è un TransactionalContext, mentre nel secondo caso un Context.

Contesti di esecuzione #

Sono consentiti accessi alla Entity unicamente in lettura, ed in funzione del fatto che si stia operando prima o dopo il commit, si può avere visibilità della versione attuale (new) e/o precedente (old):

Tipo handlerMetodo implementatoAccessi
InsertbeforeCommitnew
InsertafterCommitnew
UpdatebeforeCommitnew/old
UpdateafterCommitnew
DeletebeforeCommitold
DeleteafterCommitNO

L’unico risultato restituibile dai metodi di un DatabaseHandler è una conclusione positiva, ottenibile invocando sul result il metodo none().

DatabaseHandler bloccanti #

Di default, i DatabaseHandler non sollevano eccezioni quando non sono implementati. Dal Service handler menu è possibile spuntare l’opzione Implementation required e forzare l’applicazione a richiedere l’esecuzione di un Plugin che implementi l’Handler corrispondente, bloccando il persist sul DB se il Plugin non è presente nella Cloudlet o se è inattivo.

Des service handler menu handlers

Interaction handler #

FormActionHandler #

Viene richiamato da GraphQL.

A differenza dei Persistency Handlers, che si attivano in una sequenza ben precisa, i FormActionHandler possono eseguire la propria sequenza di azioni in ogni momento. Queste azioni vanno specificate implementando il metodo doAction() della classe relativa e interagendo con il FormActionContext che ne costituisce il parametro.

FormActionContext #

L’Entity a cui si fa riferimento è accessibile nella sola versione in corso di modifica, sia in lettura che in scrittura. É inoltre possibile accedere alla sessione con il database, nella sua versione mutabile, con il metodo getEntitySession() offerto dal contesto.

Ulteriori funzionalità offerte dal contesto sono:

  • La possibilità di ottenere informazioni sull’evento che ha attivato l’handler, tramite il metodo getEventSource(). Queste informazioni includono la tipologia di evento (clic sul pulsante o onChange) e il campo della form a cui esso è collegato;
  • L’accesso al file storage della Cloudlet, tramite il metodo getFileService();
  • L’accesso a un JSON di parametri passato da un client, tramite il metodo getHandlerCustomParams().

I risultati restituibili da un FormActionHandler sono:

  • none() : Un risultato positivo che non esegue ulteriori azioni;
  • withMessage(msg) : Inserisce il messaggio passato come parametro all’interno della risposta GraphQL. A questa chiamata ne deve seguire una ulteriore che specifica il livello di severità del messaggio:
    • info() : Semplice messaggio informativo, senza valenza di errore;
    • warning() : Messaggio di avvertimento o di errore generalmente non bloccante;
    • error() : Messaggio di errore bloccante.

I seguenti tipi di risultato sono ancora in fase sperimentale, quindi non pienamente supportati dallo schema GraphQL:

  • download(file, fileName) : Ordina il download di un file. Il parametro fileName è opzionale e specifica il nome con il quale salvare quel file sul filesystem di destinazione;
  • preview(file, fileName) : Apre una nuova scheda nel browser di esecuzione della Cloudlet, contenente l’anteprima di un file. Da questa scheda si potrà decidere se scaricare o meno il file, ed anche in questo caso si prevede il parametro opzionale fileName per specificare il nome con il quale salvarlo;
  • withPayload(json) : Restituisce un JSON, specificato come stringa e passato come parametro, a un client che stia accedendo alla Cloudlet.

FormActionHandler e Form class #

Il Service handler menu del FormActionHandler offre il comando Set form..., che consente di agganciare una Form class alla classe sui cui è definito l’Handler.

Des handlers serviceHandlerMenu setFormClass

In questo modo l’Handler dentro Class1 avrà a disposizione anche il contesto di myForm (tutti i campi compilati con i rispettivi valori): questo si ottiene chiamando getHandlerParams() sul contesto del FormActionHandler.

Una Form Class, al pari delle normali classi, può avere dei FormActionHandler collegabili ai suoi pulsanti o a eventi onChange(), in questo caso è possibile accedere ai campi compilati tramite il metodo getEntity() e i possibili risultati sono gli stessi di un handler definito sulle altre classi non-Form.

Default Handlers #

AuthenticationHandler #

Viene richiamato al momento dell’autenticazione di un membro della Cloudlet. Lo username e la password vengono passate all’AuthenticationHandler, che può eseguire un’autenticazione con regole personalizzate – ad esempio, eseguire controlli aggiuntivi sul Database oppure coordinarsi con un servizio esterno.

Lo sviluppatore deve implementare il metodo authenticate() dell’interfaccia SpiCloudletAuthenticationHandler, specificando le azioni da eseguire al momento del login. Lo stesso metodo offre un parametro di tipo AuthenticationData, che contiene le informazioni riguardanti la tipologia di autenticazione che è stata eseguita.

LogoutHandler #

Viene richiamato nel momento in cui un utente effettua il logout dalla Cloudlet e consente di eseguire un’azione personalizzata in questo punto dell’esecuzione.

Lo sviluppatore deve implementare il metodo logout() dell’interfaccia SpiCloudletLogoutHandler, con la sequenza di azioni da eseguire al momento del logout. Il contesto che ne costituisce il parametro è un LogoutHandlerContext.

LogoutHandlerContext #

Offre i seguenti metodi di utilità:

  • getAuthenticationData() : consente di ottenere la struttura AuthenticationData che è stata accennata nella sezione precedente, la quale contiene le informazioni relative all’autenticazione;
  • get__CloudletCurrentUser() : consente di ottenere le informazioni sull’utente della Cloudlet che si è disconnesso.

Il LogoutHandler deve restituire un risultato che specifica se è necessario o meno un redirect a un altro URL. Nel primo caso, è necessario invocare il metodo none() sulla struttura ottenuta dalla chiamata a result(); nel secondo caso, va invocato il metodo sendRedirect(url) con la specifica dell’URL al quale reindirizzare.

ScheduledTask #

Gli ScheduledTask possono essere sfruttati per programmare routine indipendentemente dagli eventi che si verificano sull’applicazione; per ciascun task possiamo configurare la frequenza mediante un’espressione CRON (la cui sintassi è consultabile qui).

Lo sviluppatore deve implementare il metodo run() dell’interfaccia SpiCloudletScheduledTask, e decorare la classe appositamente definita con l’annotazione @CloudletScheduledTask. Nell’annotazione va specificata l’espressione CRON che ne definisce la frequenza di esecuzione. Il metodo run() equivale a una procedura, quindi non deve restituire alcun risultato.