Il framework Apache Struts
Transcript
Il framework Apache Struts
LEZIONE 9 - 18/02/2014 © G. Malnati, C. Barberis, 2012-13 L'approccio MVC è così rilevante che la comunità internazionale ha sviluppato un botto di roba che supporta il paradigma. Apache Struts è uno dei framework che supporta MVC. Il framework Apache Struts A.A. 2012-13 Il motivo per cui è interessante è proprio perchè supporta le diverse fasi in cui passa un'applicazione. Il problema più grande dell'MVC è il modello, che dipende solo da noi. Di conseguenza dobbiamo fare leva su logiche scritte da noi e testarle opportunamente. Per questo MVC si adatta molto bene in fase di test. Introduzione • L’architettura MVC consente di separare la logica dalla presentazione • Facilità di © G. Malnati, C. Barberis, 2012-13 ◦ ◦ ◦ ◦ Le turbe che ci facciamo sulla presentazione sono minime perchè bene o male va tutto bene. Sviluppo Test Manutenzione dell’applicazione Estensione Anche la manutenzione è più facile, perchè sostanzialmente si è fatto un bel divide et impera: quando separo le parti riesco a identificare prima il problema. Buona anche la manutenzione perfettiva. 2 In Struts 2 ci si è concentrati su MVC. Di base si è partiti da un prodotto un po' commerciale, poi donato alla comunità. Lo citiamo perchè in alcuni namespace che ci verranno tra le mani ci saranno questi nomi. Introduzione • Il framework open source Struts 2 è stato introdotto nel 2005 E' il successore di Struts 1, poco estensibile e che non supportava AJAX e tutto un altro insieme di cose belle. ◦ Implementa il pattern MVC ◦ È basato sul framework OpenSymphony WebWork 2.2 © G. Malnati, C. Barberis, 2012-13 ◦ http://struts.apache.org/ • Aggiunge delle funzionalità mancanti nella versione 1 • • • • • • Estensibilità Supporto ad AJAX Internazionalizzazione e localizzazione Gestione della navigazione nelle pagine Validazione dei campi di input Layout consistente 3 Struts 2: Caratteristiche generali • Il modello, le viste e il controllore sono implementati dalle interfacce e classi • Il controllore determina l’azione da invocare in base all’URL contenuta nella richiesta ed al file struts.xml • Alla ricezione di una richiesta, viene istanziato l’oggetto Action corrispondente ◦ Action, Result e FilterDispatcher © G. Malnati, C. Barberis, 2012-13 ◦ Che definisce le corrispondenze richiesta/azione ◦ Ed eseguito il metodo execute() ◦ Questo restituisce un stringa che sarà utilizzata per determinare quale vista selezionare • Le viste possono essere implementate utilizzando differenti tecnologie ◦ JSP, Velocity Template, Freemaker, … 4 --------------------------------------------------------------------------------------------In generale i principi che ci interessano sono i seguenti: - abbiamo modelli, viste e controllori. Il controllore, elementare, lo fa Struts previa configurazione di un filtro nel WebContainer. Il filtro va configurato sull'URL /* (qualunque cosa che viene chiesta la prende lui) - la prima cosa che fa il controllore è capire che azione viene richiesta: siccome Struts intercetta tutto, decide cosa fare in base alla URL e ha delle sue regole che permettono di mappare un qualcosa sulla URL a un qualche componente che gestisce il tutto. Il componente che gestisce la richiesta prende il nome di Action: in Struts è definita un'interfaccia Action e una classe ActionSupport. Quando noi vogliamo implementare i comportamenti relativi alle azioni, creiamo una classe che implementa l'interfaccia Action o estende ActionSupport. L'interfaccia Action ha un metodo solo, execute: è senza parametri e ritorna una stringa - FilterDispatcher: riceve tutte le richieste, decide in base alla URL a quale Action è da girare la richiesta e invoca l'oggetto Action, utilizzando execute. In base alla stringa di risposta, il FilterDispatcher decide quale vista attivare. Ci sono già delle stringhe predefinite, come SUCCESS, INPUT, ERROR... e possiamo definire qualunque tipo di risposta. Il filtro mappa le risposte sulle viste corrispondenti, tramite un insieme di regole (se l'azione A1 ritorna la stringa S2 faccio vedere la vista V5). Se non viene detto niente di meglio, la vista V5 è raggiungibile a V5.jsp. E' il motore iniziale che controlla tutto quello che succede. Struts 2: Caratteristiche generali Il FilterDispatcher, prima di prendere la richiesta che ha ricevuto e passarla all'execute fa attraversare la richiesta a un insieme di moduli intermedi, detti intercettori. framework opera grazie alla presenza di un filter dispatcher, che ha il compito di © G. Malnati, C. Barberis, 2012-13 • Il Ognuno di questi moduli si occupa di un aspetto (intercettore dei parametri, intercettore dell'internazionalizzazione...). Hanno il potere di stoppare la richiesta, non invocando nemmeno Action. Altrimenti arrivo a eseguire e mi viene restituita la mia bella stringa, torno indietro ripercorrendo tutta la catena e gli intercettori hanno ancora il potere di dire la loro. ◦ Esaminare le richieste ◦ Inoltrarle ai controllori ◦ Selezionare la vista da mostrare • Un insieme di componenti, chiamati intercettatori, supportano tali operazioni automatizzando vari compiti 5 Struts 2: Caratteristiche generali • Le URI contenute nelle richieste vengono mappate sulle singole azioni usando le informazioni contenute nel file struts.xml ◦ O ricavate in base ad una convenzione, se nel framework viene incluso il file Se l'XML viene modificato runtime cambiando il mapping (aggiungo azioni senza aggiungere nuove classi) non c'è bisogno di ricompilare l'applicazione: al ricevimento della richiesta si fa quello che si deve. © G. Malnati, C. Barberis, 2012-13 • struts2-convention-plugin.jar ◦ Eventuali cambiamenti non comportano la dell’applicazione ricompilazione dell applicazione • Se la richiesta contiene dei parametri, questi sono memorizzati dal framework nell ’Action nell’Action relativa, prima dell dell’invocazione del metodo execute() Attraverso l'uso di un apposito interceptor, se la richiesta contiene dei parametri, questi vengono salvati nell'oggetto action. ◦ Utilizzando i metodi setter da essa esposti, che devono avere il nome corrispondente al parametro 6 Struts 2: Caratteristiche generali • Si possono validare i valori in ingresso ad un’azione e rimandare l’utente sul campo del form sul quale è fallito il controllo © G. Malnati, C. Barberis, 2012-13 ◦ Attraverso annotazioni o un file xml per la validazione • ---------------------------------------------------------------------------------------------Le URI delle richieste che ricevo vengono mappate su azioni differenti (ricordati il modello FSM: l'azione serve per sapere dove devo andare, sotto ben precise condizioni). Si noti che il mapping viene creato in modo normalmente esplicito andando a creare un file creato struts.xml: le richieste a stringa vuota le mappo ad esempio sulla homeAction, le richieste che vanno su list le mappo su listAction e così via... Possiamo poi installare un plugin: struts2-convention-plugin.jar (installabile solo se ho il core), che usa la convenzione qualcosa -> qualcosaAction() e evita di fare l'xml. Il dispatcher, dopo aver esaminato il risultato dell’azione, può intraprendere diverse azioni ◦ Mostrare una pagina jsp, generare un pdf, mandare un messaggio di errore, generare pagine d'attesa (validazione carte di credito), ... *** ESEMPIO DI DINAMICA *** 1) Arriva una Request 2) Il filtro la prende 3) Qual è la URI? Cerco la determinata Something 4) Creo un'istanza di SomethingAction, vuota (= costruttore senza parametri) 5) Una volta creato viene passato agli intercettori: ce ne sono già di precablati, possiamo aggiungerli o toglierli al bisogno 6) L'intercettore di parametri vede se ci sono parametri HTTP, li estrae e vede se l'oggetto Action selezionato ha il metodo setParametro: se c'è lo invoca, se c'è bisogno di fare parsing si fa e gli si passa il parametro 7) Sull'oggetto Action ora popolato si invoca execute 8) Quando l'azione è terminata ritorno una stringa per sapere com'è andata l'esecuzione: se execute ha prodotto dei dati, li ha salvati da qualche parte (tipicamente dentro una mappa che viene passata) 9) Il filtro guarda il valore ritornato e sceglie la vista che potrà prendere i valori nella mappa per riempire il template con dei dati significativi --------------------------------------------------------------------------------------------Tra i vari interceptor c'è validate che verifica se "i dati sono buoni" attraverso una serie di strumenti per imporre vincoli sui singoli parametri o sui parametri nel loro insieme. 7 © G. Malnati, C. Barberis, 2012-13 Quando arriva una richiesta attraversa una sequenza di filtri: 1) ActionContextCleanUp, non obbligatorio: se messo permette di garantire che l'elaborazione sia "fresca" 2) Filtri personali 3) FilterDispatcher Quando il FilterDispatcher prende in mano la cosa interroga l'ActionMapper, oggetto a cui dice: "Mi è arrivata una richiesta, chi è il deputato per questa URL?". Mi viene istanziato un oggetto con l'opportuna Action e glielo passo al FilterDispatcher. Il FilterDispatcher passa all'ActionProxy che ne governa l'invocazione. Si fa tutta la catena degli intercettori: se uno stoppa gestisco il tutto, altrimenti l'azione costruita pian piano è pronta. Si arriverà a un risultato e al suo relativo template. Si ripassa tutto agli intercettori che possono ancora aggiungere pezzi. Il risultato viene messo dentro la risposta. 8 Struts 2: Flusso di funzionamento Framework ActionInvocation - invoke() Si © G. Malnati, C. Barberis, 2012-13 L’elaborazione della richiesta può continuare? Lo stack contiene un nuovo interceptor? No Si No Chiamata dell’intercettatore Chiamata dell’azione Generazione del risultato Render del risultato Post elaborazione 9 Struts 2: Flusso di funzionamento • © G. Malnati, C. Barberis, 2012-13 • La catena degli intercettori viene eseguita attraverso il metodo invoke(). Il controllore riceve la richiesta dell’utente e determina l’azione da invocare Il framework crea un’istanza dell’azione richiesta e seleziona gli intercettatori corrispondenti ◦ Essi validano i parametri ricevuti, effettuano le conversioni di tipo eventualmente necessarie, popolano l’oggetto Action • Ogni volta che viene chiamato il metodo invoke(), l’istanza di ActionInvocation consulta il proprio stato ed esegue il prossimo intercettatore, attraverso il metodo intercept() 10 Struts 2: Flusso di funzionamento • © G. Malnati, C. Barberis, 2012-13 • Se all'intercettore "piace" la richiesta richiama invoke... e così via fino ad arrivare all'azione. L’intercettatore, a sua volta, richiama il metodo invoke() dell’istanza di ActionInvocation Dopo aver chiamato tutti gli intercettatori, viene invocata l’azione e generato il risultato ◦ Gli intercettatori possono essere chiamati anche dopo la generazione del risultato, per rielaborarne il contenuto • Infine, avviene la presentazione dei risultati 11 LA PRIMA COSA CHE DOBBIAMO FARE è dire che vogliamo questo filtro. Filter Dispatcher •È il cuore del controllore del framework © G. Malnati, C. Barberis, 2012-13 ◦ Verifica l’URI in ingresso e determina quale azione invocare e quale classe azione Java instanziare • Quello di default ha il seguente nome ◦ org.apache.struts2.dispatcher.ng.filter. StrutsPrepareAndExecuteFilter 12 Filter Dispatcher essere registrato nel descrittore di sviluppo dell’applicazione (web.xml) © G. Malnati, C. Barberis, 2012-13 • Deve <filter> <filter-name>struts2</filter-name> <filter-class> org.apache.struts2.dispatcher.ng.filter. StrutsPrepareAndExecuteFilter </filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> 13 Nel file struts.xml si specifica come mappare le URL ricevute su determinate azioni. Scrivendo che si estende struts-default si va a prendere una catena di intercettori già fatti all'interno. Gli intercettatori • Alcuni compiti del controllore vengono affidatati a sotto-componenti, gli intercettatori © G. Malnati, C. Barberis, 2012-13 ◦ Possono essere aggiunti o rimossi semplicemente modificando il file di configurazione di struts ◦ Non occorre ricompilare l’applicazione 14 Ne possiamo aggiungere / modificare degli altri. Gli intercettatori • Alias ad esempio permette di trasformare il nome di un parametro nel nome dell'attributo corrispondente di una nostra Action. Utile se per qualche motivo dobbiamo interfacciarci con qualcosa di cui non abbiamo il codice completo. Di seguito vengono elencati e descritti gli intercettatori più diffusi © G. Malnati, C. Barberis, 2012-13 ◦ I nomi tra parentesi sono utilizzati per registrare gli stessi nel file di configurazione • Chaining: permettiamo di avere visibili, oltre alla lavagna corrente in cui ci si scrive cose relative alla richiesta corrente, anche cose della richiesta precedente. Utile per verificare bontà parametri. Alias (alias) ◦ Converte parametri simili che possono avere nomi differenti tra le richieste • Chaining (chain) ◦ Se il risultato è di tipo “Chain”, vengono resi disponili le proprietà della precedente azione a quella corrente 15 Checkbox: gestione decente delle checkbox in HTML. Gli intercettatori • Cookie: permette di aggiungere un cookie all'azione corrente Checkbox (checkbox) Conversion Error: se un parametro non viene accettato perchè il suo tipo non era buono viene visualizzato un messaggio di errore coerente. © G. Malnati, C. Barberis, 2012-13 ◦ Gestisce le checkbox all’interno di un form, in modo tale che quelle non spuntate possano essere rilevate • Cookie (cookie) • Conversion Error (conversionError) ◦ Aggiunge un cookie all’azione corrente Create Session: crea una sessione, se già non esiste. Occhio che per default il FilterDispatcher NON crea l'oggetto Session, questo per evitare di caricare il container con contenuti che magari non vengono utilizzati. ◦ Aggiunge gli errori legati alla conversione dei parametri all’elenco degli errori presenti nei campi dell’azione • Create Session (createSession) ◦ Crea un oggetto HttpSession se questo non esiste già per l’utente corrente 16 Execute and Wait: quando lato server devo fare un'operazione lunga permette di generare una pagina d'attesa, elaborare nel frattempo e quando il risultato è disponibile si sostituisce la pagina. Gli intercettatori • Debugging (debugging) Exception: permette di mappare eccezioni che possono venire da una qualche azione in una pagina di risultato ◦ Supporto al debugging © G. Malnati, C. Barberis, 2012-13 • Execute and Wait (execAndWait) File Upload: permette la gestione di POST in cui il Content-Type non è x-form-www-url-encoded, ma mime-multipart (tipico dell'upload del file). ◦ Esegue un’azione che richiede un’elaborazione di tempo lunga in background e nel frattempo manda all’utente una pagina intermedia di attesa • Exception (exception) ◦ Mappa le eccezioni a un risultato • File Upload (fileUpload) ◦ Supporto all’upload di file 17 i18n: per l'internazionalizzazione. Dà la possibilità di sostituire delle parole chiave con la versione tradotta nelle diverse lingue, a patto di fornire le corrispondenze. Gli intercettatori • I18n (i18n) Message store: permette di accedere, all'interno della sessione, ad una serie di messaggi relativi alle azioni precedenti. ◦ Supporto all’internazionalizzazione e alla localizzazione © G. Malnati, C. Barberis, 2012-13 • Logger (logger) ◦ Fornisce in uscita il nome dell’azione • Message store (store) ◦ Salva e recupera dalla sessione i messaggi o gli errori delle azioni o gli errori dei campi, per gli oggetti Action che implementano l’interfaccia ValidationAware 18 Parameters: se la richiesta contiene parametri, va a cercare il metodo set opportuno e salva quello che deve salvare Gli intercettatori • Scope: permette di salvare lo stato dell'azione nella sessione o nell' applicazione. Model Driven (modelDriven) ◦ Supporto per il pattern model driven per tutte le classi azione che implementano ModelDriven © G. Malnati, C. Barberis, 2012-13 • Parameters (params) ◦ Popola le proprietà dell’azione con i parametri della richiesta • Scope (scope) ◦ Fornisce un meccanismo per salvare lo stato dell’azione nella sessione o nel contesto dell’applicazione 19 Servlet Config: permette di accedere agli oggetti richiesta e risposta, che altrimenti sarebbero nascosti. Gli intercettatori • Servlet Config (servletConfig) ◦ Fornisce l’accesso alle mappe che rappresentano gli oggetti di tipo HttpServletRequest e HttpServletResponse © G. Malnati, C. Barberis, 2012-13 • Static Parameters: permette di accedere alle proprietà contenute in struts.xml Static Parameters (staticParams) Roles: permette di definire ruoli. ◦ Mappa le proprietà definite all’interno del file struts.xml sulle proprietà dell’azione Timer: logga quanto dura l'esecuzione di un'azione. • Roles (roles) • Timer (timer) ◦ Supporta azioni basate su ruoli ◦ Fornisce in uscita il tempo che è stato richiesto per l’esecuzione dell’azione 20 Token e Token Session: permette di validare la sessione corrente, guardando il SessionID come un token autorizzativo. Gli intercettatori • Validation: estende Parameter. Permette di andare a vedere se c'è un file action-validation.xml nella cartella dove c'è la nostra classe per decidere il range dei nostri parametri in ingresso. Token (token) - Token Session (tokenSession) ◦ Verifica che sia presente un token valido nella richiesta per evitare l’invio ripetuto delle stesse informazioni • Validation (validation) ◦ Supporto alla validazione dei campi di input (specificati tramite il file actionvalidation.xml) © G. Malnati, C. Barberis, 2012-13 • Workflow: chiama validate() presente nell'azione e se fallisce restituisce INPUT. Workflow (workflow) ◦ Chiama il metodo validate() all’interno della classe azione e restituisce INPUT in caso di errore • Parameter Filter (n/a) • Profiling (profiling) • Prepare (prepare) ◦ Rimuove i parametri dalla lista di quelli disponibili per l’azione ◦ Supporta la profilatura dell’azione ◦ Se l’azione implementa l’interfaccia Preparable, invoca il metodo prepare() prima di invocare il metodo di esecuzione vero e proprio 21 Per default questa è la sequenza base. Ci interessa che vengono chiamati params (setta i parametri), validation (verifica i vincoli) e workflow (se la verifica è fallita genera INPUT e ricarica la pagina precedente). Lo stack di default • Struts2 definisce il seguente stack di default © G. Malnati, C. Barberis, 2012-13 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. exception servletConfig prepare checkbox multiselect actionMappingParams params conversionError validation workflow 22 Le azioni sono classi che hanno il metodo execute. Possono avere una serie di setter e getter, utilizzabili per ricevere parametri o informazioni ulteriori o mettere a disposizione informazioni alla vista. Le azioni • • Le azioni permettono di eseguire operazioni all’interno dell’applicazione Sono costituite da classi java dotate di metodi e proprietà, secondo le seguenti regole © G. Malnati, C. Barberis, 2012-13 ◦ Una proprietà deve avere i metodi get e set ◦ Una proprietà può essere di qualunque tipo ◦ La conversione tra tipi String e i tipi elementari avviene automaticamente ◦ Deve avere un costruttore senza argomenti ◦ Deve avere almeno un metodo Tutti i getter sono accessibili alle viste. Tutti i setter agli intercettori. Il costruttore dev'essere senza argomenti. Ci dev'essere almeno un metodo. Occhio che siccome ogni volta che c'è una richiesta viene costruito un oggetto, gli attributi sono volatili. Se dobbiamo salvare delle informazioni dobbiamo appoggiarci su un model, salvandolo nel contesto. In base all'URL della richiesta si crea un oggetto istanza della Action. Per default non si crea la sessione HTTP. ◦ Ad essa si possono associare azioni multiple ◦ Viene creata un’istanza di azione ad ogni richiesta HTTP ◦ Non viene creato un oggetto HttpSession 23 Ha un senso estendere ActionSupport, nonostante un qualche execute venga lanciato comunque, perchè così abbiamo tutta una serie di benefici. Le azioni classe azione può implementare l’interfaccia © G. Malnati, C. Barberis, 2012-13 • La ◦ com.opensymphony.xwork2.Action ◦ Direttamente o estendendo la classe ActionSupport •È lecito dichiarare azioni che dispongano di un metodo execute() ma che non implementino formalmente l’interfaccia 24 L'azione trovandosi i parametri salvati li può utilizzare e salva nei propri attributi informazioni visibili alle viste successive. Oltre ai parametri della richiesta veri e propri, Action può analizzare il contesto più generale del ServletContainer. Le azioni • Attraverso il metodo statico ActionContext.getContext(), si può accedere a vari tipi di informazione © G. Malnati, C. Barberis, 2012-13 ◦ ◦ ◦ ◦ ◦ ◦ La lavagnetta temporanea in cui scrivere dei dati che vanno a beneficio delle viste successive è l'OGNL value stack, in cui troviamo coppie (chiave, valore). I parametri della richiesta Gli attributi della richiesta Gli attributi della sessione Gli attributi della applicazione Gli errori rilevati Lo stack dei valori OGNL 25 Validare i parametri (1) • Un'azione può verificare se i parametri ricevuti sono accettabili per i propri scopi o chiedere all'utente di inserire valori compatibili attraverso il metodo validate() Non ci sono parametri. Se va tutto bene non fa niente, se viceversa scopre qualcosa che non funziona ha la possibilità di invocare il metodo addFieldError per dire "guarda che user mi è piaciuto, password un po' meno". Il dato viene sbattuto dentro lo stack: un'eventuale vista attivata potrà accedere all'informazione e visualizzarla vicino al tag opportuno. © G. Malnati, C. Barberis, 2012-13 ◦ L'intercettore "parameter" è responsabile della sua invocazione automatica ◦ Se, durante la sua esecuzione, vengono chiamati i metodi addFieldError(<fieldName>,<ErrorMsg>) addActionError(<ErrorMsg>) i parametri forniti sono considerati errati e l'azione si interrompe restituendo il valore "input" ◦ Altrimenti si prosegue normalmente, invocando il metodo execute() e restituendo il valore relativo 26 L'esecuzione degli intercettori, in tal caso, si interrompe. Non arrivo alla execute e mi viene ritornato INPUT, che di solito viene mappato in struts.xml in un qualcosa del tipo "torna a dove hai immesso questi parametri". Se validate passa liscio si passa agli intercettori successivi. Tipica configurazione per validare i parametri. Questa roba va ovviamente in struts.xml Validare i parametri (2) © G. Malnati, C. Barberis, 2012-13 LEZIONE 10 - 24/03/2014 C'è un intercettore particolare che si chiama parameter. Invoca sulla action corrente, se presente, il metodo validate. Di base si è detto che la nostra richiesta può avere dei parametri. Dopo che l'interceptor parameter ha chiamato tutti i setter, guarda se c'è un metodo validate. Se c'è si invoca. <struts> <package name="n" extends="struts-default"> <action name="Login" class="ns.Login"> <result name="input">/login.jsp</result> <result name="success">/success.jsp</result> </action> </package> </struts> 27 E questo è il metodo validate. In alto abbiamo i getter e i setter. ... Validare i parametri (3) © G. Malnati, C. Barberis, 2012-13 class Login extends ActionSupport { private String user,pass; public String getUser() {return user;} public String getPass() {return pass;} public void setUser(String u) {user=u;} public void setPass(String p) {pass=p;} public void validate() { if (user==null|| user.length()==0) addFieldError("user", "User required"); if (pass==null|| pass.length()==0) addFieldError("pass", "Pass required"); if (!checkPw(user,pass)) addActionError("reject"); } public String execute() {return SUCCESS;} } 28 Nel file struts.xml, all'interno di ogni blocco action ci sono una serie di sottoblocchi result che hanno un type e un name. Il name è la stringa ritornata dalla action, il type è cosa farsene. I risultati © G. Malnati, C. Barberis, 2012-13 • Un metodo dell’azione ritorna una stringa Per default mi ritrovo success come nome e dispatcher come tipo. Dispatcher: quanto contenuto tra <result> e </result> è il nome di che determina quale risultato eseguire una pagina a cui andare. • Un risultato deve avere un nome e un tipo ◦ Il nome di default è “success” ◦ Il tipo di default è “dispatcher”, che ridirige il browser a una pagina jsp ◦ Se si usa il convention-plugin, i risultati sono cercati nella cartella WEB-INF/content 29 ... I risultati • Di seguito, vengono elencati e descritti i tipi di risultati • • Chain (chain) © G. Malnati, C. Barberis, 2012-13 • • Consente di effettuare il forward verso una pagina JSP FreeMarker (freemarker) • • Permette di concatenare i risultati precedenti con quelli attuali Dispatcher (dispatcher) • • I nomi tra parentesi sono utilizzati per registrare gli stessi nel file di configurazione Utilizzato per l’integrazione con FreeMarker HttpHeader (httpheader) • Utilizzato per rispondere al browser con header http 30 I risultati • Redirect Action(redirect-action) ◦ Utilizzato per redirigere il risultato ad un’altra azione • Stream (stream) • Velocity (velocity) • XSLT (xslt) • PlainText(plaintext) © G. Malnati, C. Barberis, 2012-13 ◦ Utilizzato per mandare un InputStream al browser ◦ Utilizzato per l’integrazione con Velocity ◦ Utilizzato per l’integrazione per XML/XSLT ◦ Utilizzato per mandare del testo in chiaro, generalmente per mostrare il sorgente di una pagina JSP 31 Per poter creare un'applicazione struts dobbiamo partire da un progetto (meglio se Maven), scaricare le dipendenze e creare nella cartella src/main/resources il file struts.xml che ci permette di impostare l'elenco delle azioni che ci interessano. File di configurazione • Il file struts.xml definisce tutti gli aspetti dell’applicazione Tipicamente questo file include una serie di definizioni come ad esempio lo stack di default che conviene usare. Un file importante che si può mettere è default.properties, in cui posso aggiungere comportamenti particolari di struts. ◦ Include le azioni, gli intercettatori e i possibili risultati ◦ Può ereditare le configurazioni di default del framework © G. Malnati, C. Barberis, 2012-13 • Sono incluse nel file del framework struts2-core-VERSION.jar • Il file default.properties contiene le impostazioni che vengono utilizzate in tutte le applicazioni struts ◦ Anch’esso incluso nel package struts2-core-VERSION.jar ◦ File opzionale, nel caso in cui vanno bene le proprietà di default 32 Lorem ipsum dolor sit amet. Il file struts.xml • • L’elemento root è il tag <struts> Le azioni possono essere raggruppate in package ◦ Quest’ultimo deve avere un attributo name e un namespace © G. Malnati, C. Barberis, 2012-13 • Il namespace, se non presente, è quello di default “/” • Per invocare un’azione di un package non presente in un namespace non di default, si deve specificare il namespace nell’URI • /context/namespace/actionName.action <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <package name="package-1" namespace="namespace-1" extends="struts-default"> <action name="..."/> <action name="..."/> </package> </struts> 33 Il file struts.xml • I package possono essere suddivisi in file distinti ◦ Si utilizza il tag <include> ◦ Ogni tag deve avere lo stesso elemento DOCTYPE e un elemento root struts • Un’azione è posizionata all’interno di un elemento package © G. Malnati, C. Barberis, 2012-13 ◦ Deve avere un attributo "name" ◦ Se non si specifica la classe di azione verrà istanziata la classe di azione di default ◦ Si può specificare anche il metodo da eseguire quando l’azione viene invocata ◦ Se non specificato, il metodo di default è execute ◦ <action name="Address_save" class="app.Address" method="save"> 34 Il file struts.xml • L’elemento <result> è un sotto elemento di <action> ◦ Corrisponde al valore di ritorno di un metodo di un’azione © G. Malnati, C. Barberis, 2012-13 ◦ Un’azione può ritornare differenti risultati, quindi avere differenti elementi <result> ◦ Se il risultato restituito da un’azione non è presente nel file di configurazione • Viene controllato se il esso è presente nell’elemento global-results, quest’ultimo contenuto all’interno del package • In caso contrario, viene lanciata un eccezione ◦ L’attributo name, se omesso, assume il valore “success” ◦ L’attributo type specifica il tipo di risultato di ritorno, se omesso assume il valore “Dispacher” <action name="Product_save" class="app.Product" method="save"> <result name="success" type="dispatcher”>/jsp/Confirm.jsp </result> <result name="input" type="dispatcher"> /jsp/Product.jsp </result> </action> 35 Il file struts.xml • Ci sono 5 elementi relativi agli intercettatori che possono comparire nel file struts.xml ◦ Interceptors, interceptor, interceptor-ref, interceptor-stack, default-interceptorref © G. Malnati, C. Barberis, 2012-13 • Gli intercettatori definiti in un package possono essere usati da tutte le azioni dello stesso ◦ Per applicare un intercettatore a un’azione si utilizza il tag interceptor-ref <package name="main" extends="struts-default"> <interceptors> <interceptor name="alias" class="..."/> </interceptors> <action name="Product_delete" class="..."> <interceptor-ref name="alias"/> <result>/jsp/main.jsp</result> </action> </package> 36 Lorem ipsum dolor sit amet. Il file struts.xml • L’elemento param è utilizzato per passare un valore a un oggetto ◦ Può essere utilizzato all’interno di: action, result-type, interceptor © G. Malnati, C. Barberis, 2012-13 ◦ Ha l’attributo obbligatorio name <action name="customer" class="..."> <param name="siteId">california01</param> </action> • L’elemento constant permette di sovrascrivere un’impostazione del file default.properties ◦ Ha gli attributi name e value 37 Associare URL e azioni (1) • Se il progetto include tra le proprie librerie il file struts2-convention-plugin.jar, il framework crea un insieme implicito di corrispondenze URL/azioni © G. Malnati, C. Barberis, 2012-13 ◦ Vengono cercati, tra le classi del progetto, quelle appartenenti a package il cui nome contenga le stringhe "struts", "struts2", "action", "actions" ◦ Tutte le classi in tali package (o nei loro sottopackage) che implementano l'interfaccia Action o il cui nome termina per "Action" sono considerate azioni 38 Associare URL e azioni (2) © G. Malnati, C. Barberis, 2012-13 • Una classe identificata come azione viene mappata su una URL in base al proprio nome ed al sotto-package di appartenenza ◦ Il nome viene dapprima privato del suffisso "Action", se presente ◦ Se ciò che resta del nome usa la convenzione "CamelCase", viene trasformato in una sequenza di parole separate da "-" • "ResetForm" ð "reset-form" • L'eventuale sotto-package determina la presenza di sottocartelle nel path ◦ myproject.actions.resetAction ð /reset ◦ myproject.actions.cart.InsertItem ð /cart/insert-item 39 Legare le azioni ai risultati convention plugin, per default, cerca i risultati nella cartella WEB-INF/content © G. Malnati, C. Barberis, 2012-13 • Il ◦ Secondo il pattern <actionURL>-<resultCode>.<extension> ◦ Ad esempio, se l'azione "alfa" restituisce il valore "success", la pagina caricata sarà WEB-INF/content/alfa-success.jsp • Le estensioni supportate sono ◦ jsp, ftl, vm, htm, html 40 Lorem ipsum dolor sit amet. Usare le annotazioni © G. Malnati, C. Barberis, 2012-13 • I singoli metodi presenti all'interno di una classe possono essere annotati con @Action("/UrlToBeMapped") ◦ Questo fa aggiungere una corrispondenza tra la URL indicata ed il metodo annotato • Nell'annotazione è anche possibile indicare quali intercettori debbano essere attivati ◦ Se l'informazione è omessa, viene attivato lo stack di default 41 Il file struts.properties • Può essere creato per sovrascrivere le impostazioni di default presenti in default.proprerties © G. Malnati, C. Barberis, 2012-13 ◦ Deve essere situato nel classpath o in WEB-INF/classes ◦ In alternativa, si può utilizzare l’elemento constant ◦ Oppure l’elemento init-param all’interno della dichiarazione del filter dispatcher <filter> <init-param> <param-name>struts.devMode</param-name> <param-value>true</param-value> </init-param> </filter> 42 OGNL © G. Malnati, C. Barberis, 2012-13 • Nel pattern MVC la vista ha il compito di rendere accessibile il modello ed altri oggetti (richiesta, parametri, sessione, ...) agli utenti ◦ Per accedere ad essi da una pagina JSP si può utilizzare OGNL (Object-Graph Navigation Language) ◦ Linguaggio ereditato da WebWork • OGNL permette di ◦ Associare elementi della GUI agli oggetti del modello e convertire valore da un tipo all’altro ◦ Associare tag generici con gli oggetti del modello ◦ Creare liste e mappe al volo, che possono essere usate dagli elementi della GUI ◦ Invocare qualsiasi metodo 43 OGNL - Value Stack © G. Malnati, C. Barberis, 2012-13 • Per ogni invocazione di azione, viene creato l’oggetto Value Stack ◦ Prima dell’esecuzione del metodo execute() ◦ È utilizzato per salvare l’azione e altri oggetti ◦ Viene consultato durante l’elaborazione dagli intercettatori e anche dalla vista per mostrare i risultati e altre informazioni ◦ Per potervi accedere, Struts conserva questo oggetto come un attributo della richiesta chiamato struts.valueStack 44 Lorem ipsum dolor sit amet. OGNL – Value Stack • Il Value Stack al suo interno contiene due tipologie di elementi ◦ Object Stack • Vengono salvate le azioni e le relative proprietà © G. Malnati, C. Barberis, 2012-13 ◦ Context Map • Vengono salvate tutte le mappe del contesto dell’applicazione Object 0 Object 1 request attr parameters session application 45 OGNL – Value Stack • Le seguenti mappe vengono salvate nell’oggetto Context Map ◦ parameters • Contiene i parametri di richiesta per la richiesta corrente © G. Malnati, C. Barberis, 2012-13 ◦ request • Contiene tutti gli attributi della richiesta per la richiesta corrente ◦ session • Contiene gli attributi di sessione per l’utente corrente ◦ application • Contiene gli attributi di ServletContext per l’applicazione corrente ◦ attr • Ricerca gli attributi nel seguente ordine: request, session e application 46 OGNL – Accesso ai dati • È possibile accedere ai dati di Context Map e Object Stack • La ricerca è basata sugli indici ◦ Nel primo caso l’espressione deve iniziare con il carattere ‘#’ © G. Malnati, C. Barberis, 2012-13 ◦ Se una determinata proprietà non viene trovata su un preciso oggetto, la ricerca continuerà sul prossimo oggetto e così via • Si possono utilizzare le seguenti forme ◦ object.propertyName ◦ object.[‘propertyName’] ◦ object[“propertyName”] ◦ [0].propertyName ◦ #session.code ◦ #request[“customer”][“contactName”] 47 OGNL – Accesso a dati statici e metodi • Nella vista si possono invocare campi e metodi statici ◦ Non necessariamente presenti nello Stack Value ◦ Per accedervi si usa rispettivamente la seguente sintassi © G. Malnati, C. Barberis, 2012-13 • @fullyQualifiedClassName@fieldName • @fullyQualifiedClassName@fieldName ◦ Ad esempio: • @java.util.Calendar@DECEMBER • @app04a.Util@now() • Se invece si vuole accedere a un metodo dello Stack Value, si utilizza la seguente sintassi ◦ object.methodName(argumentList) 48 Lorem ipsum dolor sit amet. I tag di Struts • Tag dell’interfaccia utente messi a disposizione da Struts per interagire con i form HTML e i suoi elementi © G. Malnati, C. Barberis, 2012-13 ◦ All’inizio della propria pagina jsp bisogna includere la seguente direttiva • <%@ taglib prefix="s" uri="/struts-tags" %> ◦ I tag struts sono definiti nel package org.apache.struts2.components e derivano dalla classe UIBean 49 Struts tag - attributi • Ad un attributo è possibile assegnare un valore statico o un’espressione OGNL • Attributi più comuni ◦ Rispettivamente racchiudendo l’attributo tra “value” e %{value} © G. Malnati, C. Barberis, 2012-13 ◦ cssClass, cssStyle, title, disabled, label*, labelPosition*, key, requiredposition*, name, required*, tabIndex, value ◦ L’attributo name, in un tag di input, mappa l’elemento alla rispettiva proprietà dell’azione ◦ L’attributo value mantiene il valore dato dall’utente ◦ L’attributo label definisce l’etichetta dell’elemento ◦ L’attributo key è una scorciatoia per indicare il name e il value 50 Struts tag - attributi Gli attributi secondari si possono suddividere nelle seguenti categorie • Template • © G. Malnati, C. Barberis, 2012-13 ◦ templateDir, theme, template • Javascript ◦ onclick, ondbclick, onmousedown, onmouseup, onmouseover, onmouseout, onfocus, onblur, onkeypress, onkeyup, on keydown, onselect, onchange • Tooltip ◦ Tooltip, tooltiplconPath, tooltipDelay 51 Configurare un’applicazione Struts2 in Eclipse 1/2 © G. Malnati, C. Barberis, 2012-13 • Inserire nella cartella WebContent/WEB-INF/lib almeno i seguenti file ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ asm-3.3.jar asm-commons-3.3.jar asm-tree-3.3.jar commons-fileupload-1.2.2.jar commons-io-2.0.1.jar commons-lang3-3.1.jar freemaker-2.3.19.jar javassist-3.11.0.GA,jar ognl-3.0.6.jar struts2-core-2.3.12.jar xwork-core-2.3.12.jar 59 Lorem ipsum dolor sit amet. Configurare un’applicazione Struts2 in Eclipse 2/2 Aggiungere al classpath le cartelle così inserite • Selezionare la cartella “Java Resources” con il tasto destro e creare una nuova cartella sorgente chiamata “resources” © G. Malnati, C. Barberis, 2012-13 • ◦ Al suo interno creare il file struts.xml ◦ All’atto dell’esecuzione verrà posto all’interno della cartella WebContent/ WEB-INF/classes 60 © G. Malnati, C. Barberis, 2012-13 Il file WEB-INF/web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app …> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <filter> <filter-name>struts2</filter-name> <filter-class> org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter </filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app> 61