Preparazione della vie filee manipolare le proprietà del Model ... una certa classe è una action...
Transcript of Preparazione della vie filee manipolare le proprietà del Model ... una certa classe è una action...
1
Preparazione della view● Il lavoro di scrittura della view è molto
semplificato se usiamo la libreria di tag Struts UI
● Collezione di tag JSP per creare oggetti HTML e manipolare le proprietà del Model
● Direttiva JSP:• <%@ taglib prefix="s" uri="/struts-tags" %>
Richiami di JSP
● Direttive● <%@ page ... %>, <%@ include ... %>,
<%@ taglib ... %>
● Azioni● Azioni Standard:
<jsp:useBean>, <jsp:getProperty>, <jsp:setProperty>, <jsp:forward>,● Azioni custom (estensioni)
● Scripting (porzioni di codice Java)– <% out.println(“Ciao”); %> scriptlet– <%=“Ciao”%> expression– <%! String ciao=“Ciao”; %> declaration– <%-- Questo è un commento --%> commenti
3
Tag Struts UI● Textfield
● <s:textfield>
– name: – label:
● Password
● <s:password>
– Name: – Label:
● Property
● <s:property>
– Value:
4
Attributi dei tag● Name
● Il nome dell'oggetto corrispondente nel ValueStack● È una espressione OGNL
● Label● Etichetta che comparirà accanto alla casella di
testo
5
Configurazione via Annotations● Abbiamo visto come configurare le action e i
corrispondenti result via XML● Possono essere configurate anche tramite Java
Annotations● La configurazione è più immediata, ma è
necessario fare qualche modifica● Ed accettare qualche compromesso di
convenzione
6
Modifica web.xml● Bisogna aggiungere in web.xml (quello
dell'applicazione, non quello di Struts) parametri di inizializzazione per il nostro FilterDispatcher
● Per fargli sapere dove sono (in quale package) le action
7
Interface Action● Le action devono implementare l'interface
Action● Struts, tramite Java Reflection, stabilisce che
una certa classe è una action valida● Questo meccanismo è usato da Struts anche in
altri ambiti (vedi SessionAware)
8
Nuovi nomi delle Action● In mancanza della configurazione XML, Struts
genera i nomi delle action (nelle URL) a partire dai nomi delle classi
● La classe HelloWorld genera● /helloWorld.action
● La convenzione di generazione è CamelCase con la prima lettera minuscola
9
Nuovi nomi delle Action● Presi dai nomi delle classi
10
Esempio1annot● Vedi esempio
11
Struts Package● Abbiamo visto come un Package è un
contenitore di Action che condividono uno stesso namespace (parte della URL) e quindi gestiscano aspetti omogenei
● I package contengono anche la configurazione di interceptor e altri componenti
● Per evitare di riscrivere tutto a mano, abbiamo fatto in modo che il nostro package estendesse il package struts-default, ereditandone le definizioni
12
Componenti della URL
13
Struts Package● Nei nostri package possiamo anche scegliere di
ridefinire alcuni aspetti di configurazione● Per esempio aggiungendo interceptor
opzionali, oppure scritti da noi
14
Interceptor Stack● Struts esegue, prima di ogni Action, un insieme
di interceptor● Sono disposti a pila (stack), ovvero viene
eseguito il primo, poi l'esecuzione passa al secondo, e così via
● Dopo lo stack degli interceptor viene eseguita la Action
● Alla fine viene rieseguito lo stack degli interceptor, al contrario● Prima l'ultimo, poi il penultimo, e così via
15
Interceptor Stack
16
Interceptor Stack● Stack predefinito in struts-default.xml
17
Interceptor Stack● Lo stack di default (defaultStack) è definito
nel package struts-default● Per utilizzarlo non dobbiamo scrivere alcuna
configurazione
18
Interceptor e Action● La nostra business logic sta nella Action● Eppure ci sono degli aspetti condivisi da più
Action, che troviamo implementati negli Interceptor● Ad esempio, se dobbiamo validare una password, si
tratta di business logic; però l'aspetto di “validazione” è una cosa comune a più Action
● Molti Interceptor, per essere attivati, hanno bisogno di un oggetto Action implementato in un certo modo
19
Interceptor e Action (2)● Abbiamo visto nell'Esempio1 (senza saperlo) il
comportamento dell'interceptor Params● Params acquisisce i parametri della HTTP
Request e li trasferisce all'interno della Action● Può farlo solo se trova i metodi get/set!● Ovvero Params entra in azione solo se la Action
è implementata in un certo modo● Se esistono, cioè, i metodi get e set per ogni
parametro HTTP Request
20
ActionSupport● Gli interceptor sono molti, ognuno ha le sue
necessità● ActionSupport è una classe che implementa
Action e un certo numero di altre interface necessarie per alcuni interceptor
● Può essere utile usarla per semplificare lo sviluppo di una action
● Per esempio fornisce il necessario per la validazione dell'input e la traduzione dei testi
21
Interceptor: Workflow● La validazione dell'input può essere suddivisa
in diverse fasi● Conversione di tipo● Validazione logica
● Vedremo l'implementazione della seconda, sfruttando ActionSupport e l'interceptor Workflow
22
Esempio2: Workflow
23
Esempio2: funzione validate()● ActionSupport fornisce al nostro interceptor
Workflow una funzione vuota validate(), che viene richiamata dallo stesso
● Per implementare la logica, ne facciamo l'override
● L'interceptor Workflow viene eseguito dopo Params, perciò avremo a disposizione gli oggetti username, password già pronti
● Validate() non restituisce niente● Workflow controlla se è stato chiamato il metodo
addFieldError()
24
Esempio2: addFieldError()● La funzione addFieldError() permette di
definire un errore di validazione in un certo campo
● Permette allo stesso tempo di definire il messaggio di errore per l'utente
● Possono essere definiti più errori per uno stesso campo
25
Esempio2: result INPUT● Se viene eseguita almento una addFieldError(),
Workflow procede così:● Imposta il Result (la view corrispondente) a
“input”● Ferma l'esecuzione della request, ovvero non
saranno eseguiti né gli interceptor successivi né la Action vera e propria
● Saranno ri-eseguiti, in verso contrario, gli interceptor eseguiti prima
26
Stringhe e localizzazione● Nell'Esempio2 i nostri messaggi di errore erano
definiti dentro la funzione di validazione● In gergo, sono “hardcoded”● Questo genera qualche problema
● Se vogliamo modificare il contenuto dobbiamo andare a cercare il punto esatto di definizione
● Non possiamo affidare la correzione ortografica/grammaticale a qualcuno che non è uno sviluppatore
● Non possiamo modificarli dinamicamente, ad esempio per la traduzione
27
Resource Bundles● Per risolvere questi problemi si usano i
Resource Bundles● Sono dei file che contengono properties
(coppie chiave=valore)● Si possono usare per definire, in modo
separato dal codice, tutte le stringhe della nostra applicazione
● L'interface TextProvider (implementata da ActionSupport) cerca un file con lo stesso nome della Action, ma che termina in .properties
28
Resource Bundles: i18n● I18n sta per Internationalization● Dato che abbiamo definito dei file esterni con
le stringhe, possiamo creare un file per ogni lingua
● La convenzione è● NomeClasse_lingua.properties● La lingua è di due caratteri, secondo lo standard
ISO 639– Register_it.properties
29
Resource Bundles: i18n (2)● ActionSupport implementa l'interface
LocaleProvider, che, tramite il metodo getLocale(), permette di settare la lingua corrente
● L'implementazione di default analizza la request del browser per capire la lingua
● Possiamo però reimplementarlo per forzare un particolare Locale, per esempio dalle preferenze utente
30
Esempio3: i18n● Vedi
31
Trasferimento dati al Model● Abbiamo visto come alcuni interceptor
riempiano per noi le variabili membro della Action, a partire dai parametri ricevuti in input dall'utente
● Il nostro Model però può essere più complicato di così● Può essere composto da oggetti complessi che
interagiscono tra loro
32
Trasferimento dati al Model● Possiamo prendere i valori delle variabili della
Action e passarle agli oggetti del Model, una alla volta● Come abbiamo fatto finora
● Oppure possiamo “esporre”, nella nostra action, direttamente gli oggetti del Model in modo che gli interceptor riempiano quelli
33
Trasferimento dati al Model● Ci sono due modalità:
● Via Javabeans● Via ModelDriven
34
Esempio4: Javabeans● Vedi
35
Esempio4: Javabeans● Definiamo una variabile membro “user”, di
tipo User (che fa parte del nostro Domain Model)
● Ricordiamo che tutte le variabili membro della Action, se hanno i metodi get/set, sono esposte● Cioè sono raggiungibili via OGNL
● La variabile user viene inizializzata da struts!
36
Esempio4: Javabeans● Eliminazione dei getters/setters diretti per
username e password
37
Esempio4: Javabeans● Se User è a sua volta un Javabean, potremo
accedere anche alle sue variabili membro via OGNL
● Modifichiamo quindi i tag della view con i nuovi name
● Es: “user.username” è una espressione OGNL che visualizza/imposta la variabile username dentro l'oggetto user
38
Esempio5: ModelDriven● Vedi
39
Esempio5: ModelDriven● Con la modalità ModelDriven decidiamo di
esporre uno e un solo oggetto, che rappresenta il Model
● Finora, scrivendo in OGNL:● “username”● Ci riferivamo alla variabile della Action
● Impostando un oggetto come Model ci riferiremo alla variabile di quell'oggetto● Per esempio, se l'oggetto è User, scrivendo
“username” intendiamo “user.username”
40
Esempio5: ModelDriven● La nostra Action deve implementare l'interface
ModelDriven● ModelDriven richiede la scrittura del metodo
getModel(), che nel nostro caso restituisce user
● L'inizializzazione di user, stavolta dobbiamo farla noi
41
Esempio5: ModelDriven● ModelDriven quindi si occupa di esporre,
all'input utente e all'output, un solo oggetto che rappresenta il Model per quell'azione
● Questo oggetto può anche essere solo un Data Transfer Object separato dalla Action● Ovvero riempiamo questo oggetto e poi lo
passiamo a tutti gli altri● Dato che questo metodo espone totalmente un
oggetto, può essere pericoloso usare un vero oggetto del Model, perché un utente malizioso potrebbe accedere a parametri “privati”
42
Interceptors● Abbiamo già visto un paio di interceptor in
azione● Struts ne contiene molti
● Alcuni sono predefiniti● Altri sono opzionali
● Gli interceptor, ripetiamo, lavorano sui cross-cutting concerns● Cioè sugli aspetti comuni a più Action
● In pratica si tratta di operazioni di preprocessing e postprocessing
43
Esecuzione degli Interceptors● Il framework Struts non chiama direttamente il
metodo execute() della action● Crea un oggetto di tipo ActionInvocation che si
occupa di chiamare tutti gli interceptor dello stack e infine il metodo execute() della action
● Gli interceptor, agendo sull'oggetto ActionInvocation, possono modificare il processing della request● Abbiamo già visto l'interceptor Workflow che
interrompe tutto se trova un errore di validazione
44
Interceptors aggiuntivi● Buona parte delle operazioni necessarie sono
già implementate negli interceptor interni di Struts2
● E' raro dover scrivere un interceptor da zero● Una eccezione è la gestione
dell'autenticazione
45
ActionInvocation● ActionInvocation ha un metodo invoke() che
richiama il prossimo interceptor della lista● Ogni volta che viene richiamato invoke() si
passa al prossimo ● È una chiamata ricorsiva
● Ogni interceptor quindi esegue:● Preprocessing ● Invoke() (passa al prossimo interceptor)● Postprocessing
46
Fasi di ogni Interceptor● Esempio con due interceptor, lo stack è
costituito da (Interc1, Interc2, Action):● Primo invoke();
● Interc1.preprocessing()● Secondo invoke()
– Interc2.preprocessing()– Terzo invoke()
● Action.execute()
– Interc2.postprocessing()
● Interc1.postprocessing()
47
Interceptors Utili● Timer● Logger● Params (default)● Workflow (default)● Validation (default)● Prepare (default)● Exception (default)● Servlet-config (default)
48
Interceptor: Timer● Non è nello stack di default● Cronometra l'esecuzione della request,
mandando in output il tempo impiegato
49
Interceptor: Logger● Non è nello stack di default● Scrive nei log l'esecuzione delle Action● Scrive una riga all'inizio dell'esecuzione, e poi
alla fine
50
Interceptor: Servlet-Config● Serve ad iniettare all'interno della nostra
action gli oggetti tipici di configurazione della servlet
● Es. la sessione, la Servlet Context e così via● La nostra action può implementare una delle
interface di supporto di servlet-config● Ad esempio, implements SessionAware
● Se lo fa, questo interceptor userà i metodi lì definiti per settare in modo corretto un oggetto che contiene le variabili di sessione
51
Interceptor: Exception● In caso si verifichi un'eccezione nel codice,
questa verrà mostrata secondo una view predefinita
● Le exception in realtà possono essere gestite redirezionando l'output su un result particolare
● Questo result deve essere “globale”, ovvero non appartenere ad alcuna azione● <global-results><result … /></global-result>● <global-exception-mapping><exception-mapping
exception=... result=... /></g-e-m>
52
Aggiungere interceptor non-default● Per aggiungere un interceptor che non è
presente nello stack di default dobbiamo definirlo nella configurazione di una action
● Oppure possiamo creare un nuovo stack di default, che eredita gli interceptor predefiniti e vi aggiunge i nuovi
53
Esempio 6: aggiunta interceptor● Vedi
54
Esempio 6: aggiunta interceptor● Per aggiungere un interceptor ad una action,
basta aggiungere il tag interceptor-ref nel file XML di configurazione
55
Autenticazione● L'autenticazione, ovvero il processo tramite il
quale un sistema riconosce la corretta identità di un utente, è uno dei classici cross-cutting concerns
● La maggior parte del codice si può raccogliere in una parte che si occupa del login (inserimento credenziali)
● Però ogni parte dell'applicazione dove l'accesso non è “pubblico” deve controllare che l'autenticazione sia andata a buon fine
56
Autenticazione tramite Interceptor● Quindi l'autenticazione può essere realizzata
con un interceptor● Ogni applicazione ha le sue metodologie e
schemi per effettuare l'autenticazione● In base al nome utente● In base all'email● Usando un database/servizio esterno● Usando dei ruoli
● Dovremo implementare una nostra procedura di autenticazione
57
Interface Interceptor● Un interceptor deve implementare l'interface
Interceptor, che definisce tre metodi:● destroy()● init()● intercept()
● Il metodo fondamentale è intercept(), che riceve un oggetto ActionInvocation
● Ricordiamo che i passi sono: preprocessing, invoke(), postprocessing
58
Esempio 7: Authentication● Vedi●
59
Esempio 7: Authentication● L'autenticazione vera e propria è svolta dalla
Action Login● Riceve username e password, effettua dei
controlli (dovrebbe effettuare, ad esempio, query su un db)
● Infine salva l'oggetto User nella sessione● La sessione è contenuta nella variabile session,
grazie all'interceptor servlet-config● Interceptor che, ricordiamo, entra in azione
quando una action implementa SessionAware
60
Esempio 7: Authentication● L'interceptor servlet-config (nello stack di
default) controlla se la action implementa SessionAware
● Se si, usa il metodo setSession() della stessa per impostare un oggetto di tipo Map (coppia chiave/valore) contenente le variabili di sessione
● In pratica esegue una injection della sessione nella action
61
Esempio 7: Authentication● Il nostro interceptor acquisisce la sessione
tramite l'oggetto ActionInvocation● Controlla se dentro la sessione c'è un oggetto
user● Se si, imposta questo oggetto User all'interno della
action e poi chiama invoke() [passando l'esecuzione agli altri interceptor dello stack]
● Se no, restituisce una stringa “LOGIN” che interrompe l'esecuzione e chiama la result corrispondente
62
Esempio 7: Authentication● UserAware è un'interface scritta da noi, che
opera similmente a SessonAware● Ovvero, permette di iniettare un oggetto User
all'interno delle nostre azioni● Il meccanismo è lo stesso● Se la action implementa UserAware, viene
usato il suo metodo setUser() per impostare l'oggetto utente
63
Esempio 7: Authentication● getInvocationContext() restituisce l'intero
oggetto ActionContext, con tutto il ValueStack● (ricordate l'ActionContext?)
● Nella business logic è meglio non accedere direttamente all'ActionContext, usando invece OGNL
● Negli interceptor possiamo anche accedervi direttamente, perché si possono considerare oggetti “di sistema”