Login
Cerca all'interno di JavaPortal
Help
Home Page Documentazione Forum Progetti Partner Pubblica!
Documentazione > Tutorial > Struts
Hide
Best Practices
EJB
Frameworks
Howto
J2EE
J2ME and Wireless
J2SE
JSP e Servlet
Java Application Server
Java IDE/Tools
Java Media
Java Security
Java Sys Admin
Java e XML
Java e SQL
OpenSource Java
Patterns
Repository
Tesi
UML
Web Services
Slide
White Paper di jws.it
project management
Eventi
Groovy

Hai una tesi in Java?
Tesine preparate
per esami?
Pubblica tutto su
JavaPortal!

Scrivi al nostro staff


IntelliJ IDEA Free e Open Source


Aristotele
Lo scopo del lavoro è quello di guadagnarsi il tempo libero


GRASP : Pattern Low Coupling


Rss Feed
Home Page
Articoli
News
Forum
Classi

  Visualizza Commenti (0) Aggiungi Commento    
Add to Shortcuts
 
Vota l'articolo
Struts
By Simone Dionisi
10 giugno 2005
Valutazione Acquisita: 60

  Struts
Program Prima Parte
Program Seconda Parte
Program Terza Parte
Program Quarta Parte



Prima Parte top

Con questa serie di articoli andremo ad analizzare uno degli strumenti sicuramente più utilizzati nella costruzione di web application: Struts.

In questo primo articolo andremo ad analizzare il framework spiegandone il funzionamento, l’utilizzo all’interno della nostra applicazione e descrivendo uno dei suoi punti cardine lo struts-config.xml.

Struts é un framework open-source facente parte del Jakarta project (http://jakarta.apache.org/struts) compatibile con la piattaforma J2EE della SUN (basato principalmente sulle tecnologie Servlet e JSP) sviluppato per incoraggiare l'utilizzo di un tipo di architettura che si basa sul pattern MVC(model-view-control). In questo modo si cercano di fornire delle solide basi su cui poi poter creare con tranquillità e sicurezza un’ applicazione, con la convinzione di non dover ritoccare la struttura poiché creata seguendo un pattern raccomandato dalla SUN per la costruzione di web application.

Fig. 1  Struts -prima parte-

 

Logicamente l’utilizzo di questo framework comporta tutti i vantaggi derivanti dall’utilizzo del pattern MVC, inoltre ci permette di utilizzare una controller servlet ben testata, delle utili tag libraries, una facile integrazione dell’ internazionalizzazione dell’applicazione e la possibilità di utilizzare un prodotto open-source creato da programmatori che conoscono le reali problematiche, avendo inoltre a disposizione i codici sorgente modificabili secondo le proprie necessità (e se si vuole anche con la possibilità di partecipare attivamente alla costruzione del framework stesso).

Per prima cosa vediamo cosa bisogna installare sul proprio computer per utilizzare Struts :

- Java Development Kit -
(versione 1.2 o superiori) scaricabile dal sito della SUN all'indirizzo http://java.sun.com/j2se.

- Servlet Container -
(compatibile con le Servlet API Specification, versione 2.2 o superiori, e le JavaServer Pages (JSP) Specification, versione 1.1 o superiori). Una delle possibili scelte é Tomcat (versione 3.1 o superiori, la versione 3.2 o superiori é raccomandata) scaricabile dal link http://jakarta.apache.org/tomcat , ma vi sono anche altre scelte commerciali e non : WebSphere, Weblogic Application Server, Resin, SilverStream, Jetty, JRun, iPlanet Application Server etc.

- XML Parser -
Struts richiede la presenza di un parser XML compatibile con le Java API for XML Parsing (JAXP) specification, 1.1 o superiori.

- Servlet API Classes -
Per poter compilare Struts o le applicazioni che usano Struts avremo bisogno del servlet.jar file. Molti servlet containers includono questi file JAR: se si sta utilizzando Tomcat questa libreria la troverete in %TOMCAT_HOME%\common\lib .

Una volta accertato che si possiede tutto l’occorrente per poter utilizzare Struts possiamo procedere scaricando il codice binario dal seguente sito http://jakarta.apache.org/builds/jakarta-struts/release/ scegliendo la versione più recente (in questo articolo parleremo della versione 1.0.2 introducendo anche la 1.1 che nel momento in cui scriviamo è ancora in versione beta).

Una volta spacchettato il file .zip troveremo al suo interno :

- lib/jdbc2_0-stdext.jar -
Le classi JDBC 2.0 Optional Package API. Questo file, se stiamo utilizzando come servlet container Tomcat, lo troveremo già contenuto nella directory %TOMCAT_HOME%\common\lib.

- lib/struts.jar -
Questo JAR file contiene tutte le classi facenti parte del progetto Struts.

- lib/struts-*.tld -
Questi sono i files "tag library descriptor" che descrivono i custom tags nelle Struts tag libraries.

- lib/*.dtd –
I files descrittori per il web.xml (secondo le specifiche 2.2 e 2.3 delle Servlet API) e per lo struts_config.xml (struts-config_1_0.dtd nel caso della versione 1.02 di Struts, anche lo struts-config_1_1.dtd nel caso della 1.1).

- webapps/*.war –
Questi files WAR sono delle applicazioni di esempio oppure delle mini-guide sui taglibs etc.

Nella versione 1.1 oltre ai files sopra elencati troveremo anche altri files aggiuntivi:

- lib/commons-*.jar -
Questi files JAR contengono tutti i pacchetti del Jakarta Commons project che sono utilizzati all'interno del framework Tiles ora integrato con Struts; , se stiamo utilizzando come servlet container Tomcat, li troveremo già contenuti nella directory %TOMCAT_HOME%\common\lib.

- lib/tiles.jar –
Contiene tutte le classi del framework Tiles (che possiede le seguenti caratteristiche : utilizzo dei templates, supporto per il multi-linguaggio, caricamento e costruzione dinamica delle pagine, impostazione del layout etc.).

- lib/validator-rules.xml –
Questo file contiene delle regole di validazione di default da integrare nella propria applicazione. Spiegheremo più avanti come utilizzarlo.

Per utilizzare Struts nella nostra applicazione web bisogna seguire i seguenti passi:

- copiamo tutti i files JAR nella directory WEB-INF/lib della nostra applicazione.

- copiamo tutti i files *.tld nella directory WEB-INF della nostra applicazione oppure in un’altra sottodirectory che potremmo chiamare per esempio tld.

- modifichiamo il file web.xml della nostra applicazione web includendo un elemento <servlet> che definisce la nostra nuova servlet con la funzione di controller, che in questo caso sarà quella che ci viene fornita da Struts(ActionServlet) oppure un’altra che la estende, ed un elemento <servlet-mapping> che stabilisce quali URI sono mappati con questa servlet.

Creiamo come parametri della servlet i due seguenti files :

· un file WEB-INF/struts-config.xml che definisce il mappaggio delle azioni(action mappings) e tutte le altre caratteristiche della nostra applicazione.

· un file di properties da utilizzare per internazionalizzare la propria applicazione. Al suo interno ci sarà qualunque messaggio testuale che vogliamo sia tradotto in più lingue.

Inoltre aggiungiamo gli elementi <taglib> con la dichiarazione delle tag libraries di Struts che utilizzeremo.

Vediamo qui un esempio di file web.xml:

<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app>
<servlet>
<servlet-name>Nome della servlet</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>

<!—
questa servlet ha la funzione di controller ed una volta chiamata ha il compito di leggere il file struts-config.xml che specifica il mappaggio delle azioni; quindi utilizza questo mappaggio per stabilire dove mandare le richiesteHTTP e quali modelli utilizzare per i dati che facciamo viaggiare
-->

<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>

<!--
il parametro debug serve per stabilire il livello di debug che si vuole per l’applicazione. Il valore di default nelcaso in cui non sia specificato nessun valore è 0.
-->

<init-param>
<param-name>debug</param-name>
<param-value>3</param-value>
</init-param>

<!--
il parametro validating è un booleano che specifica se si vuole che al file di configurazione di Struts venga fatto il parsing per vedere se si tratta di un file valido. Il valore di default è true.
-->

<init-param>
<param-name>validating</param-name>
<param-value>false</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Nome della servlet</servlet-name>

<!-- il nome deve essere uguale al servlet-name nel tag servlet -->

<url-pattern>*.do</url-pattern>

<!--
estensione che fa partire la servlet di Struts, in questo modo si può decidere di integrare il framework con una vecchia applicazione specificando al container di utilizzare la servlet di Struts solo quando si trova di fronte ad un url con questa estensione
-->

</servlet-mapping>

<!--
Qui di seguito dichiariamo le librerie per i custom tags di Struts se le vogliamo utilizzare nell'applicazione, noi le abbiamo inserite in una cartella tld all’interno della cartella WEB-INF
-->

<taglib>
<taglib-uri>/WEB-INF/tld/struts-bean.tld</taglib-uri>
<taglib-location>/WEB-INF/tld/struts-bean.tld</taglib-location>
</taglib>

<taglib>
<taglib-uri>/WEB-INF/tld/struts-html.tld</taglib-uri>
<taglib-location>/WEB-INF/tld/struts-html.tld</taglib-location>
</taglib>

<taglib>
<taglib-uri>/WEB-INF/tld/struts-logic.tld</taglib-uri>
<taglib-location>/WEB-INF/tld/struts-logic.tld</taglib-location>
</taglib>

<taglib>
<taglib-uri>/WEB-INF/tld/struts-template.tld</taglib-uri>
<taglib-location>/WEB-INF/tld/struts-template.tld</taglib-location>
</taglib>

<!-- nella versione 1.1 ci sono anche i files struts-nested.tld e tiles.tld -->

</web-app>

Il cuore dell'intero framework é sicuramente il file struts-config.xml, infatti mentre il file web.xml specifica dove una richiesta deve andare, quest'ultimo invece determina esattamente cosa accadrà ad essa. Quindi il controller utilizza questo file per determinare quali oggetti deve chiamare. Il file viene letto in fase di startup e le relazioni sono immagazzinate nella memoria per ottenere migliori performance.

Nella versione 1.1 tutte le informazioni contenute all’interno di questo file vengono tenute in memoria all’interno di JavaBeans contenuti nel package org.apache.struts.config. Ognuna di queste classi contiene le informazioni di un singolo settore del file (ExceptionConfig, ForwardConfig, FormBeanConfig etc.). All’interno di questo package troviamo anche due classi particolarmente importanti :

ConfigRuleSet : è una classe con tutte le regole di parsing del file di configurazione; si occupa anche di istanziare le classi che contengono i dati del file di configurazione quando l’applicazione viene fatta partire.

ApplicationConfig : è la classe centrale del package e contiene tutte le informazioni che descrivono l’applicazione.

Nella versione 1.1 viene anche fornita la possibilità di creare più files di configurazione, suddividendo l’applicazione in tante sotto-applicazioni; ciò si è reso necessario perché nei grandi progetti con numerosi programmatori si creava non poca difficoltà nel condividere questo file vitale per l’applicazione e che deve essere modificato continuamente.

Per configurare questi files all’interno dell’applicazione basta aggiungerli al file web.xml come parametri iniziali:
<init-param>
<param-name>config/sottoapplicazione1</param-name>
<param-value>/WEB-INF/struts-sottoapplicazione1-config.xml</param-value>
</init-param>

in questo modo specifichiamo al controller di caricare la sotto-applicazione utilizzando il file di configurazione specifico per questa.

Andiamo adesso ad esaminare come va scritto un file di configurazione per Struts.

Una volta inserito il tag root <struts-config> passiamo ad inserire gli altri tag per il settaggio dei datasource, delle forms e delle actions che specificano come associare ad un url un dato evento o meglio action (utilizzando un termine di Struts).

<struts-config>
<!-- tag per il settaggio dei datasources -->
<datasources>
<datasource>

<!--
nel tag datasource é possibile inserire le seguenti proprietà utilizzando il tag set-property :
description la descrizione del datasource
autoCommit un booleano che specifica se impostare l’autocommit dopo ogni operazione sul database
driverClass il driver da utilizzare
password e username
url per specificare il percorso al database
-->

<set-property property=”autoCommit” value=”false” />
<set-property property=”url” value=”jdbc:postgresql://localhost/mydatabase” />
<set-property property=”driverClass” value=”org.postgresql.Driver” />
</datasource>
</datasources>

<!--
questo tag é stato introdotto nella versione 1.1 e serve per impostare delle eccezioni globali rilanciate da più actions
-->

<global-exceptions>


<!--
nel tag exception troviamo i seguenti attributi:

handler : il nome della classe che si occupa di processare le eccezioni (la classe di default è org.apache.struts.action.ExceptionHandler)
key : la chiave che ci permette di determinare il messaggio di errore associato con questa eccezione
path : il percorso alla classe o pagina JSP a cui indirizzarsi nel caso avvenga questa eccezione
scope : specifica se la classe ActionError contenente gli errori generati venga messa nella request o nella session
type : la classe che rappresenta l’eccezione catturata
-->

<exception key=”chiave.messaggio.errore” path=”/paginaDiErrore.jsp”type=”java.io.IOException”/>
</global-exceptions>

<!--
tag per il settaggio dei form beans, cioè dei beans che rappresentano dei dati di una form
-->

<form-beans>

<!--
nel tag form-bean che rappresenta un singolo form bean possiamo trovare i seguenti attributi:

type : il tipo della classe con tutto il package
name : il nome che rappresenta la chiave che identifica il bean all’interno del file struts-config

Nella versione di Struts 1.1 troviamo anche l’attribute dynamic, un booleano che indica se il form bean ha delle proprietà di tipo dinamico, un’importante funzionalità introdotta con la nuova versione per costruire dei beans che possano essere utilizzati su più forms e non per un form specifico (comunque spiegheremo meglio questa parte più in là). Oltre ala presenza di questo attributo è stato aggiunto anche un elemento form-property che ci permette di elencare le proprietà di quel bean dinamico; in esso sono presenti gli attributi che indicano il nome del parametro(name), il tipo(type) ed anche l’eventuale valore iniziale(initial).
-->

<form-bean name=”nomeBean” type=”package.del.bean.NomeBean” />
</form-beans>

<!--
tag per il settaggio di forwards globali visibili a tutte le actions; questo tag viene utilizzato nel caso si abbiano delle forward comuni tra le varie actions
-->

<global-forwards>

<!--
il tag forward rappresenta un singolo forward globale e contiene i seguenti attributi

name : il nome del forward utilizzato per referenziarlo all’interno delle actions
path : il percorso alla risorsa
redirect : è un booleano valorizzato a true se si vuole utilizzare il metodo sendRedirect per arrivare alla risorsa, valorizzato a false se si intende utilizzare il metodo forward della classe RequestDispatcher

Nella versione 1.1 si trova anche l’attributo contextRelative, un boolean che se impostato a true indica che il path deve essere considerato relativo all’intera applicazione
-->

<forward name="success" path="/paginaJSP.jsp" />
</global-forwards>

<!-- tag per il mapping delle actions -->

<action-mappings>

<!--
nel tag action possiamo trovare i seguenti attributi:

type : la classe Action utilizzata; in alternativa a questo attributo si possono utilizzare o forward (che contiene il path della Servlet o della JSP che processerà la request) o include
name : nome del form bean definito nel tag form-beans che questa action utilizzerà
path : l'URI associato con quella determinata action (può essere settato a true se quella determinata action sarà quella di default per tutta l'applicazione, in modo che gestisca tutte le richieste non gestite dalle altre action. Solo una action può essere settata come quella di default)
validate : é settato a true se si vogliono validare i parametri passati nel form bean
input : il percorso al form che e’ stato invocato; viene utilizzato nel caso si presenti un errore di validazione dei dati, così che l’applicazione sappia da chi è stata fatta la richiesta e possa tornare alla pagina di partenza elencando gli errori trovati che l’utente può così correggere
parameter : un attributo utilizzato per passare eventualmente un parametro; nel caso si stia invocando una action che estende la classe DispatchAction questo attributo si rileverà molto utile per specificare il nome del parametro inserito nella request il cui valore non sarà nient’altro che il nome del metodo da invocare
-->

<action path="/Path" type="percorso.della.classe.NomeAction">
<!-- il tag forward reindirizza il tutto alla fine dell'Action ad un'altra Action o ad una JSP -->
<forward name="success" path="/AltraAction.do" />
<forward name="failure" path="/jspDiFailure.jsp" />
</action>
<action path=”/Path2” type=”percorso.della.classe.NomeAction2”>

<!-- nel caso si abbia come risultato “success” la action reinderizzerà il tutto al path indicato nel forward impostato nel tag global-forwards -->

<forward name=”failure” path=”/jspFailure2.jsp” />
</action-mappings>

<!--
il tag controller è stato introdotto nella versione 1.1; possiamo notare che molti degli attributi di questo tag non sono altro che valori che inseriamo nel file web.xml. Ma allora perché questa ripetizione? Perché nella nuova versione di Struts molte delle funzionalità che prima si trovavano nella ActionServlet ora sono state spostate nella classe RequestProcessor che si occupa quindi di gestire le richieste ricevute dalla ActionServlet.

Troviamo quindi i seguenti attributi:

contentType : il valore di default è text/html
debug : il livello di debug così come viene impostato nel file web.xml
locale : un booleano che indica se vogliamo che sia messo in sessione la classe Locale(che indica il paese di appartenenza) preferita dall’utente
maxFileSize : nel caso di upload specifica le dimensioni massime per accettare un file
multipartClass : il nome della classe da utilizzare nel caso di download/upload (la classe di default è org.apache.struts.upload.DiskMultipartRequestHandler)
nocache : un booleano che se impostato a true inserisce in ogni response l’header che specifica che la pagina non deve essere presa dalla cache
processorClass : la classe che processa le request (il valore di default è la classe org.apache.struts.action.RequestProcessor)
-->

<controller contextType=”text/html” nocche=”false” />

<!-- nel tag plug-in vengono specificati i nomi di quelle classi, con i loro eventuali parametri di configurazione, che vengono create o distrutte quando viene fatta partire l’applicazione o viene fermata.-->

<plug-in className=”nomeDellaClasse”>
<set-property property=”debug” value=”true” />
</plug-in>
</struts-config>

Abbiamo quindi dato uno sguardo molto generico al progetto Struts, studiando un pò più da vicino come configurarlo e come costruire lo scheletro dell’applicazione grazie allo struts-config; nei prossimi articoli andremo a vedere nel dettaglio le varie componenti di Struts.



Seconda Parte top

Nel primo articolo abbiamo parlato del framework in generale (quando utilizzarlo, come installarlo) e di uno dei suoi componenti principali : lo struts-config.xml. In questo secondo articolo analizzeremo altri componenti facenti parte di Struts, sempre confrontando la versione 1.0.2 con la nuova versione, che però essendo una beta release potrebbe essere diversa da come viene descritta qui.

Andiamo quindi a vedere queste classi principali : alcune verranno utilizzate direttamente così come sono, quindi non richiederanno modifiche da parte nostra, altre verranno da noi estese per integrarle con la nostra applicazione.

Queste classi sono: ActionServlet, Action, ActionForm, ActionError, ActionForward, ActionMapping.

La classe org.apache.struts.ActionServlet

La ActionServlet é sicuramente la classe principale che ha la funzione di controller dell'applicazione; questa ha il compito di associare un evento con una classe e per fare cio’ utilizza lo struts-config (già analizzato nell’articolo precedente).

L’utilizzo di questo file ha molti vantaggi :

  • Essendo un file impostato in maniera gerarchica risulta più facile da consultare e da capire, rendendo più chiaro l’intero flusso dell’applicazione anche a chi non conosce Java.
  • Quando bisogna modificare qualcosa del flusso dell’applicazione non bisogna ricompilare tutto.

Nella versione 1.1 la servlet principale e’ stata affiancata da una nuova classe org.apache.struts.action.RequestProcessor che si occupa di processare le richieste. In questo modo, separando le due azioni di ricevimento e di processo l’utente non e’ più vincolato al processore interno della ActionServlet, ma può utilizzarne uno scritto da lui.

Per poter utilizzare la ActionServlet dobbiamo configurarla nel file web.xml oppure inserire una classe che la estenda, magari con delle funzioni aggiuntive specifiche per la nostra applicazione.

In un progetto già avviato in cui si vuole utilizzare Struts solo in alcune parti ancora da sviluppare, in modo da non dover modificare il codice già scritto, si può tranquillamente specificare nel file web.xml quando la ActionServlet deve essere utilizzata e quando no; solitamente viene stabilito quando l'URL termina con una specifica estensione (nell’esempio di web.xml dell’articolo precedente *.do).

Vediamo cosa succede quando la ActionServlet viene inizializzata, cosa che avviene o all’avvio del container, nel caso in cui abbiamo inserito il tag <load-on-startup>, oppure quando questa viene invocata per la prima volta. Qui metteremo a confronto le due versioni di Struts, sempre ricordando che la versione 1.1 è in beta e che potrebbe subire dei cambiamenti.

Versione 1.0.2

 

Fig. 2 Seconda Parte

Al momento dell’inizializzazione della servlet il container invocherà il metodo init() (questo vale per tutte le servlets) che si occuperà di effettuare i seguenti passi:

1. Per prima cosa ripulirà la servlet dalle classi Action precedentemente inizializzate (initActions());

2. Inizializza la classe MessageResource, utilizzata per far ritornare messaggi specifici a seconda della lingua impostata nell’applicazione (initInternal());

3. Inizializza il debug, prendendo il valore che è stato eventualmente impostato nel file web.xml (initDebug());

4. Imposta il file di properties che bisogna utilizzare per i messaggi all’interno dell’applicazione. Questo file si chiama solitamente ApplicationResource e viene anch’esso settato all’interno del file web.xml (initApplication());

5. Inizializza alcune informazioni di mappaggio della servlet come il livello di dettaglio del debug, se il file xml va validato e cosa più importante effettua il parsing del file struts-config.xml. Per effettuare il parsing utilizza la classe org.apache.struts.digester.Digester, che vedremo più avanti (initMapping());

6. Inizializza tutti i parametri utili in fase di upload, come la grandezza massima di un file, le dimensioni del buffer, la creazione di una cartella temporanea etc. (initUpload());

7. Inizializza tutti i datasources utilizzati all’interno dell’applicazione settati all’interno dello struts-config.xml (initDataSources());

8. Imposta altri parametri non trattati prima(locale, nocache, content) (initOther());

9. Preleva il valore impostato nel file web.xml con cui la servlet verrà invocata (questo valore verrà utilizzato nel tag html:form per impostare l’url esatto a cui inviare i dati inseriti all’interno del form) ed inoltre registra tutti i files DTDs che ci possono servire per la validazione (initServlet()).

Versione 1.1

Fig. 3 Seconda Parte

Nel caso della versione 1.1 quando viene invocato dal container il metodo init() vengono effettuate le seguenti operazioni:

 

1. Invoca il metodo initInternal() che esegue le stesse operazioni della precedente versione.

2. Imposta alcuni parametri utilizzati all’interno dell’applicazione (lo struts-config.xml, il livello di debug, la validazione dei files XML) invocando il metodo initOther().

3. Invoca il metodo initServlet() che esegue le stesse operazioni della precedente versione. Da notare che la classe Digester che esegue il parsing non fa più parte del framework di Struts ma è entrata a far parte del Commons Project, quindi non la troveremo all’interno dello struts.jar ma nel file commons-digester.jar.

4. Chiama il metodo initApplicationConfig() che inizializza alcuni dati di configurazione del framework per l’applicazione di default, effettuando il parsing del file struts-config.xml impostato nel web.xml con il parametro config. Crea quindi un oggetto di tipo ApplicationConfig, con all’interno tutte le informazioni che ci interessano(actions, formbeans, forwards, exceptions etc. ), che verrà immagazzinato nel ServletContext.

5. Viene invocato il metodo initApplicationMessageResources() che carica ed inizializza ogni MessageResource impostato nello struts-config.xml per l’applicazione di default. Una volta caricato viene immagazzinato anche questo nel ServletContext con la chiave indicata all’interno del tag <message-resource>.

6. Inizializza i datasources impostati all’interno dello struts-config.xml per l’applicazione di default (initApplicationDataSources()). Questo metodo sostituisce il metodo initDataSources() che e’ stato lasciato solo per compatibilità con le vecchie versioni ed in futuro verrà eliminato.

7. Inizializza i plug-in specificati nello struts-config.xml per l’applicazione di default.(initApplicationPlugIns())

8. (ciclo while)Dopo aver inizializzato l’applicazione di default la servlet vedrà se esistono delle sotto-applicazioni e provvederà ad inizializzarle una ad una ripetendo i procedimenti visti prima.

Questo avviene quando la servlet viene inizializzata, ma cosa avviene quando viene invocata?

In poche parole la ActionServlet creerà ed utilizzerà delle classi di helper (Actions) che si occuperanno di eseguire le operazioni di business (connessioni, invocazioni di EJB etc.), riempiendo o prelevando i dati da dei contenitori (ActionForm) e restituendo un risultato delle operazioni eseguite (ActionForward).

Ma andiamo a vedere più nel dettaglio come ciò avviene all’interno della ActionServlet.

Versione 1.0.2

Fig. 4 Seconda Parte


- Viene invocato il metodo processPath() che identifica il percorso che verrà utilizzato per selezionare un
oggetto ActionMapping che contiene tutte le informazioni riguardanti la classe Action da utilizzare, il form bean etc..

- Seleziona la località dell’utente(processLocale()).

- Imposta l’header con il content-type e il no-caching se richiesto(processContent() , processNoCache()).

- Chiama il metodo processMapping() passandogli il percorso ottenuto prima, per trovare la classe ActionMapping associata a quel percorso.

- Ritorna dall’ActionMapping ottenuto la classe ActionForm, cioè il bean, impostata nello struts-config.xml ed associata alla Action invocata. (processActionForm())

- Una volta ottenuto il bean lo popola(processPopulate()) con i dati passati nella request (se ne vengono passati).

- Invoca il metodo validate() del bean se è stato impostato il parametro validate=”true” nel tag <action> (processValidate()).

- Esegue il processo di un forward o di una include a seconda di quello che abbiamo impostato nel mapping(processForward() o processInclude())

- Crea l’istanza della Action che questa richiesta dovrà processare (processActionCreate())

- Chiama il metodo perform() della Action istanziata, questo metodo eseguirà la parte di business impostata nella Action. (processActionPerform())

- Se dal metodo perform() della Action e’ tornato un ActionForward lo processa e rimanda il tutto alla destinazione specificata, che puo’ essere un’altra Action oppure una pagina di view(processActionForward())

Versione 1.1

Dal momento in cui la ActionServlet riceve una richiesta HTTP viene chiamato il metodo process() da entrambi i metodi doGet() e doPost(). Come abbiamo già detto prima nella nuova versione di Struts tutto il codice che esegue le operazioni viste prima è stato trasferito in un’altra classe (RequestProcessor), quindi in questo metodo verrà soltanto invocato il metodo process() di quest’ultima non prima di aver invocato però il metodo statico selectApplication() della classe org.apache.struts.util.RequestUtils. Il compito di questo metodo è di selezionare la sotto-applicazione da gestire in seguito alla richiesta ricevuta, confrontando il percorso ricevuto con quelli impostati nel file web.xml.

La classe org.apache.struts.digester.Digester (versione 1.0.2) o org.apache.commons.digester.Digester (versione 1.1)

La classe Digester venne creata in principio con l’unica intenzione di processare i contenuti all’interno dello struts-config.xml, il tutto in maniera molto semplice, immediata e facile da estendere; in un secondo momento si è pensato di utilizzare queste sue proprietà anche al di fuori del framework di Struts ed è per questo che è stata inserita nel progetto Jakarta Commons.

Questa classe permette al programmatore di specificare delle azioni associate a dei pattern che il parser incontra durante la lettura del file XML; il progetto Digester già contiene al suo interno alcune “rules” (così vengono definite all’interno del progetto perchè estendono tutte l’interfaccia Rule) che vengono comunemente utilizzate per la creazione di beans, l’impostazioni di alcuni parametri etc., ma permette anche di specificarne di nuove.

Vediamo come funziona questa classe, fermo restando che se si sta utilizzando la classe Digester solo per leggere lo struts-config.xml non dovremo occuparci assolutamente dell’implementazione di nuove regole.

Il primo passo da effettuare è quello di fissare le regole da utilizzare nel Digester (che poi non sono nient’altro che i tag annidati che la classe può trovare all’interno del documento XML durante il parsing) come fa ad esempio nel metodo initDigester() della ActionServlet :

…..
digester.addObjectCreate("struts-config/data-sources/data-source",
"org.apache.struts.util.GenericDataSource","type");
digester.addSetProperties("struts-config/data-sources/data-source");
digester.addRule("struts-config/data-sources/data-source",new AddDataSourceRule(digester));
digester.addSetProperty("struts-config/data-sources/data-source/set-property",
"property", "value");
…..

Una volta impostate le regole non resta che invocare il metodo parse() che si preoccuperà di confrontare i tag letti sul documento XML con le regole impostate. Come risultato quindi della chiamata al metodo parse() avremo che tutte le informazioni inserite nello struts-config.xml ci ritorneranno come collezioni di oggetti che verranno immagazzinati all’interno della ActionServlet che potrà quindi utilizzarli con maggiore facilità.

Un’altra importante proprietà della classe Digester è quella di poter registrare una copia locale di un DTD referenziato nel DOCTYPE. Ad esempio nel metodo initServlet() della ActionServlet(versione 1.0.2) registra tutti i DTDs che poi verranno utilizzati da Struts nella loro versione locale. Ciò permette l’utilizzo di Struts anche in quelle applicazioni che non sono connesse ad internet ed inoltre aumenta le performance dei siti su internet, evitando di doversi addentrare nella rete.

La classe org.apache.struts.action.RequestProcessor (versione 1.1)

Come abbiamo già detto prima questa classe è stata aggiunta nella nuova versione di Struts soprattuto per permettere agli sviluppatori di poter gestire il comportamento di un’applicazione secondo i propri bisogni quando questa riceve delle richieste. Anche con la vecchia versione ciò era possibile (bastava estendere la classe ActionServlet), ma con l’introduzione nella nuova versione delle sotto-applicazioni ci si e’ trovati di fronte alla gestione di comportamenti diversi a seconda dell’applicazione da cui si ricevevano le richieste. Creare dei processori personalizzati è molto semplice, basta aggiungere all’interno dello struts-config.xml il tag <controller> specificando il percorso della classe da invocare più altri parametri che hanno altre funzionalità che non staremo qui ad esporre perchè già viste nel precedente articolo.

Ma adesso vediamo cosa avviene invece nel processore di default quando dalla ActionServlet viene invocato il suo metodo process() :

1. Viene invocato il metodo processMultipart() che controlla se il contentType della richiesta inizia con “multipart/form-data” ed in tal caso viene utilizzata una classe di Struts (org.apache.struts.upload.MultipartRequestWrapper), creata proprio nel caso in cui l’applicazione debba gestire degli uploads, invece dell’HttpServletRequest method

2. Come nella versione precedente viene chiamato il metodo processPath() che si occupa di determinare il percorso che verrà utilizzato per identificare la Action da utilizzare.

3. Il metodo processLocale() determina la localizzazione dell’utente e immagazzina l’oggetto Locale generato nella sessione.

4. Chiamando i metodi processContent() e processNoCache() determina il content type e se l’attributo noCache è impostato.

5. Esiste inoltre un metodo processPreprocess(), ripreso anche questo dalla versione precedente (ma che non abbiamo spiegato prima), che non fa assolutamente nulla. Questo metodo viene invocato prima della chiamata alla Action ed è stato messo proprio per permettere allo sviluppatore per effettuare dei possibili controlli (di validazione, di sessione etc.) prima di entrare nella parte di business.

6. Invoca il metodo processMapping() che determina la classe ActionMapping da utilizzare.

7. Controlla se l’utente che ha fatto la richiesta è in possesso dei permessi (processRoles()).

8. Come nella versione precedente invoca i metodi che determinano la ActionForm, cioè il bean che si deve occupare del trasporto dei dati, lo popolano, invocano la action etc. etc.

Come abbiamo potuto vedere non ci sono grandi trasformazioni rispetto alla versione precedente se non per lo spostamento di tutti i metodi in un’altra classe e l’implementazione di un nuovo metodo per la sicurezza, che controlla se l’utente che ha chiamato quella Action gode realmente dei permessi per effettuare quell’operazione.



Terza Parte top

Continuiamo il nostro viaggio all’interno del framework di Struts e delle classi che lo costituiscono.

Mentre nel secondo articolo siamo andati ad analizzare il funzionamento di alcune classi di Struts che generalmente non verranno modificate dal programmatore, in questo articolo andremo a vedere delle classi a cui bisogna fare riferimento per sviluppare la propria applicazione. Quindi le classi che presenteremo qui (o almeno alcune) verranno estese dallo sviluppatore ed adattate per i propri scopi utilizzandone le proprietà ed i metodi già forniti dal framework.

 

La classe org.apache.struts.action.ActionMapping

 

Fig. 5 Terza Parte

Incominciamo con una di quelle classi che non verranno estese dal programmatore (o almeno di solito, molti preferiscono estendere anche questa classe per aggiungere dei parametri) : la classe ActionMapping.

La ActionMapping contiene le informazioni con cui determinare a quale Action è associato un particolare evento.

Queste informazioni vengono fornite in fase di startup, prelevandole dallo struts-config.xml, e trasformate in un set di ActionMapping inserite all’interno di un container (la classe ActionMappings : nel framework le classi che finiscono con una s sono dei contenitori) da cui verranno riprese quando bisognerà avere accesso ad ogni tipo di parametro di configurazione inserito nel tag <action>.

Riprendendo quindi una parte di codice dallo struts-config.xml possiamo vedere che all’elemento qui sotto corrisponde una classe ActionMapping che mappa il percorso “/Path” alla Action “percorso.della.classe.NomeAction”.

....

<action path="/Path" type="percorso.della.classe.NomeAction">

                           <forward name="success" path="/AltraAction.do" />

                           <forward name="failure" path="/jspDiFailure.jsp" />

</action>

….

 

Quindi quando il controller riceverà una richiesta con il percorso contenente la stringa “/Path” invocherà il metodo perform() della Action “percorso.della.classe.NomeAction”, inoltre a seconda dell’esito reinderizzerà il tutto o ad un’altra Action associata al percorso “/AltraAction” o ad una pagina di errore “/jspDiFailure.jsp”.

A partire dalla versione 1.1 questa classe estende org.apache.struts.config.ActionConfig, che avrebbe dovuto prendere il suo posto rendendola deprecata, ma per problemi di compatibilità con le precedenti versioni di Struts questa classe viene ancora utilizzata.

Come abbiamo detto prima, alcuni sviluppatori hanno la necessità di estendere la classe ActionMapping per aggiungere dei parametri o degli attributi da utilizzare in seguito nelle classi Actions.

Ma quali sono i passi da compiere per estendere le funzionalità di questa classe?

Per prima cosa bisogna creare una classe che estenda ActionMapping: anche nella nuova versione di Struts non bisogna estendere ActionConfig perché la ActionMapping è ancora ampiamente utilizzata nel framework (per esempio viene passata come parametro nel metodo execute() della Action).

A questo punto creata la classe che chiameremo qui “mia.classe.MiaActionMapping” la vado ad aggiungere nello struts-config.xml :

….

<action-mappings>

<!-- con l’attributo className specifico al framework che quando processa questa richiesta deve utilizzare la mia classe MiaActionMapping e non quella di default -->

             <action className=”mia.classe.MiaActionMapping” path=..... type=.... >

             <!-- uso il tag set-property se devo inizializzare delle proprietà all’interno della classe -->

             <set-property property=”miaproprietà” value=”valore” />

             .....

            </action>

             .....

</action-mappings>

.....

 

Inoltre poichè stiamo utilizzando una classe da noi implementata ma nei metodi perform() e execute() delle Actions ricevo una classe di tipo ActionMapping, per vedere le mie nuove proprietà dovrò ricordarmi di fare il casting all’oggetto passato.

La classe org.apache.struts.action.Action

La classe Action può avere compiti diversi a seconda del tipo di applicazioni in cui viene utilizzata. Nelle applicazioni più semplici si occupa di gestire la parte di business logic associata con una richiesta, mentre in altri casi la classe Action invocherà un altro oggetto (EJBs, oggetti CORBA, classi DAO) che si occuperà lui della parte di business logic; compito della Action sarà allora quello di gestire gli errori e di interpretare i dati nelle HttpServletRequest per passarli come variabili Java alle classi che si occupano delle varie operazioni.

Quindi la Action non viene utilizzata propriamente per gestire la business logic, ma si occupa di altre funzioni, come il logging, il controllo della sessione, l’autenticazione ed autorizzazione di una richiesta, demandando gli altri servizi a delle classi esterne al framework. In questo modo possiamo riutilizzare con maggiore semplicità queste classi esterne anche in altre applicazioni ed inoltre, nel caso venga modificato qualcosa nel servizio, la classe Action non dovrà essere re-implementata. Un esempio sostanziale potrebbe essere quello delle chiamate al Database; se scriviamo il codice che tira su la connessione e chiama le query all’interno della classe Action, allora dovremo riscriverlo per ogni Action e non potremmo riutilizzarlo all’interno di un’altra applicazione. Invece se ad occuparsi delle connessioni c’è un’altra classe invocata dalla Action allora potremmo riutilizzarla anche in altri parti ed in caso di modifiche (non so utilizzo di un DataSource al posto di una connessione singola etc.) la classe Action rimarrà tale e quale.

Come abbiamo visto nel precedente articolo nel metodo process() della ActionServlet della versione 1.0.2 e del RequestProcessor della versione 1.1, venivano create e gestite queste classi Actions il cui unico compito è quello di essere il ponte tra le richieste dei clients e la parte di business.

Quindi nel metodo processActionCreate() della classe di controller si verificherà se esiste già una istanza della classe Action trovata nell’ActionMapping, cercando se è già contenuta dentro un HashMap, dove le chiavi sono i nomi delle Actions e i valori sono le loro istanze; se non troverà nulla allora creerà una nuova istanza e la inserirà all’interno dell’HashMap creando quindi una sola istanza per ogni Action del framework. Per garantire che un solo thread creerà l’istanza da utilizzare nell’applicazione questa parte di codice sarà sincronizzata (utilizzando quindi synchronized (actions) {…. } ).

Lo sviluppatore dovrà quindi fare molta attenzione quando dovrà implementare le sue classi Actions, stando molto accorto a gestire nella maniera più appropriata possibile un ambiente multi-threading.

Tutte le richieste che avvengono da parte dei clients faranno riferimento all’unica istanza creata dal framework, così come già fanno riferimento ad un’unica istanza della ActionServlet, quindi fate bene attenzione a non utilizzare all’interno della Action variabili globali che rappresentino lo stato di un singolo client.

Una volta istanziata la classe da utilizzare, oppure recuperata la sua istanza dall’HashMap, viene chiamato il metodo processActionPerform() che non fa altro che invocare il metodo contenuto nella vostra classe. Eh si perché da questo momento entrate in gioco voi; le classi di Action vengono implementate completamente da voi secondo le vostre esigenze. Quindi non vi resta che creare una classe che estenda la classe Action ed implementare al suo interno nel caso si stia utilizzando la versione 1.0.2 il metodo perform(), mentre nella versione 1.1 execute().

Entrambi i metodi ricevono gli stessi parametri in ingresso e ritornano un oggetto di tipo ActionForward, ma allora perché si è sentito il bisogno di crearne uno nuovo?

Semplicemente perchè il metodo perform() della classe Action nella versione 1.0.2 gestiva soltanto delle eccezioni di tipo IOException e ServletException, mentre il metodo execute() gestisce una eccezione più generica Exception; la decisione di scrivere un altro metodo piuttosto che riscrivere quello precedente è stata quindi un passo obbligato per mantenere la compatibilità con le versioni precedenti. Il bisogno di rimanere compatibili con i vecchi Struts comporterà altre variazioni di questo tipo ed il “salvataggio” di alcune classi che altrimenti sarebbero sparite.

Uno degli errori più comuni quando si sviluppa le prime volte con Struts e non si usano degli IDE che ci aiutino nella creazione delle classi Action è proprio durante l’ override del metodo perform() (o execute(), che poi non fa altro che chiamare il metodo perform()). Infatti molto spesso si sbaglia la signature del metodo, errore che non verrà segnalato né durante la compilazione delle classi (perché il metodo può essere non implementato) né in runtime (perché viene invocato il metodo di default che ritorna null). Estendendo la classe Action ne ereditiamo i metodi, tra cui quello perform(), che però non essendo astratto e ritornando un valore null può non essere implementato.

Una volta scritta la nostra classe e la business logic all’interno del metodo perform(), non ci resta che specificare nello struts-config.xml quando questa Action deve essere chiamata in causa.

….

<action path="/Path" type="percorso.della.classe.NomeAction">

            <forward name="success" path="/AltraAction.do" />

           <forward name="failure" path="/jspDiFailure.jsp" />

</action>

….

Oltre ai metodi perform() ed execute() visti finora, la classe Action fornisce ulteriori funzionalità utili per migliorare la gestione della nostra applicazione : gestione degli errori, gestione dei messaggi etc.

Utilizzando quindi i metodi saveErrors() e saveMessages() (introdotto nella versione 1.1) è possibile salvare sia dei messaggi di errore che dei semplici messaggi all’interno della richiesta, associando ognuno di questi ad una chiave specifica. In questo modo potremo visualizzare degli errori o dei messaggi di warnings all’interno delle pagine di view tramite i tags <html:errors> e <html:messages>.

Sicuramente una delle funzionalità più interessanti è la gestione del flusso dell’applicazione, che nel framework assume il nome di token, definizione presa da “Core Pattern” di SUN. In pratica si controlla che l’utente non possa entrare nel flusso e digitare il tasto di invio di un form per due volte, dopo essere tornato indietro con il tasto Back del browser.

Vediamo come effettuare questo tipo di controllo:

  1. nella action che invocherà la pagina di view, con il form da non “postare” per due volte con il “trucchetto” del tasto Indietro, salvo il token all’interno della sessione(saveToken());
  2. quando la Action effettua il forward verso la pagina di view con il form questo token verrà salvato nella pagina attraverso un campo “hidden”;
  3. quando invio i dati del form compilato faccio partire un’altra Action dove controllo se il token che ho in sessione corrisponde a quello che ho in sessione (isTokenValid()). Se non sono uguali torno un errore, altrimenti continuo le mie operazioni, non prima però di aver eliminato il token dalla sessione (resetToken()) rendendo irrealizzabile il trucco visto prima.

Quindi gli “attori” che ci aiutano in questa funzionalità sono :

  • protected boolean isTokenValid(HttpServletRequest request, boolean reset) , effettua un controllo sulla   sessione per vedere   se ad una transazione è associato un token. Può tornare “false” se nessuna sessione è   associata alla richiesta, se nessun token   è stato salvato nella sessione o nella richiesta, se il token che si   trova nella sessione non è uguale a quello della transazione   corrente;
  • protected void resetToken(HttpServletRequest request) , ripulisce la sessione dal token salvato, che non sarà   più necessario   nella prossima richiesta;
  • protected String generateToken(HttpServletRequest request) , per permettere solo una richiesta singola in   quella transazione   genera un token, una stringa esadecimale costituita da un id di sessione e dal tempo   corrente in millisecondi, codificata  utilizzando la classe MessageDigest(questo metodo viene invocato   all’interno del metodo saveToken());
  • protected void saveToken(HttpServletRequest request) , salva il token all’interno della sessione.

 

La classe org.apache.struts.actions.DispathAction
  

Fig. 6 Terza Parte

Abbiamo visto che la classe Action si occupa di gestire una singola operazione all’interno del metodo perform(), quindi per gestire n operazioni dovremo allora creare n classi Actions. Questo tipo di approccio può risultare molte volte svantaggioso sotto alcuni punti di vista : eccessiva produzione di classi in applicazioni grandi, scrittura di codice che porta via molto tempo etc. Per venire incontro a questo tipo di problematica è stata creata la classe DispatchAction, il cui unico obiettivo è quello di poter comprendere all’interno di un’unica classe più operazioni che abbiano un collegamento logico (per esempio in un carrello della spesa l’operazione di aggiunta e l’operazione di eliminazione di un componente).

Vediamo ora cosa dobbiamo fare per utilizzare correttamente questa classe.

Per prima cosa creiamo la nostra classe “mia.classe.MiaDispatchAction” che estende la classe org.apache.struts.actions.DispatchAction ed aggiungiamo un metodo per ogni funzionalità da gestire al suo interno (tenendo sempre presente che ci deve essere un collegamento logico, non perché poi la classe non funziona, ma per un più corretto sviluppo che permetta una migliore manutenzione e comprensione). I metodi creati devono ricevere in ingresso gli stessi parametri che di solito passiamo nel metodo perform() della classe Action (ActionMapping, ActionForm, HttpServletRequest, HttpServletResponse) e ritornare un oggetto di tipo ActionForward, questo perché logicamente agiscono come dei metodi execute() raggruppati all’interno di un’unica Action, quindi con una signature diversa soltanto nel nome.

package mia.classe;

// import da inserire

 

public class MiaDispatchAction extends org.apache.struts.actions.DispatchAction {

 

        // i metodi creati all’interno della classe che devono essere chiamati a seguito della richiesta, devono essere            public altrimenti non sono visibili al metodo perform() della DispatchAction che li invoca

         public ActionForward metodoUno(ActionMapping mapping, ActionForm form,

                   HttpServletRequest request, HttpServletResponse response) throws Exception {

 

                 // implementazione del metodo uno

        }

 

        public ActionForward metodoDue(ActionMapping mapping, ActionForm form,

                   HttpServletRequest request, HttpServletResponse response) throws Exception {

 

                 // implementazione del metodo due

        }

}

Come potete vedere all’interno della nostra classe non abbiamo inserito il metodo perform() e non certo per una dimenticanza. Infatti a differenza delle classi che estendono Action qui il metodo perform() è già implementato all’interno della classe DispatchAction e sarà proprio lui che si occuperà di volta in volta di chiamare il metodo associato ad una determinata funzionalità (vedremo più avanti come farà).

Scritta la nostra classe non ci resta altro da fare che inserire la sua mappatura all’interno dello struts-config.xml.

….

<action path="/miaDispatch" parameter="nomeParametro" scope="request"

         type="mia.classe.MiaDispatchAction" validate="false">

                    <forward name="Success" path="/paginaDiView.jsp" />

</action>

.....

Rispetto al mapping di una normale Action vediamo che è stato soltanto aggiunto l’attributo “parameter”. In questo attributo noi specifichiamo il nome del parametro con cui salveremo all’interno della request il nome del metodo da invocare.

Quindi nel nostro caso dovremo trovare nella request il parametro “nomeParametro” che avrà come valore la stringa “metodoUno” o “metodoDue”, a seconda di quale metodo vogliamo invocare (per esempio inserito all’interno dell’url “http://localhost:8080/miaapplicazione/miaDispatch.do?nomeParametro=metodoUno” ;attenzione state molto attenti che il nome del parametro sia perfettamente uguale al nome del metodo da chiamare anche nelle maiuscole).

Così tramite l’utilizzo della reflection è possibile chiamare un metodo data la stringa che contiene il suo nome esatto.

Vediamo qui un estratto del codice che si occupa di farsi ritornare il nome del metodo e di chiamarlo:

…

// ritorna il valore impostato nell’attributo parameter cioè “nomeParametro”

String parametro = mapping.getParameter();

// ritorna il nome del metodo da invocare, per esempio “metodoUno”

String nomeMetodo = request.getParameter(parametro);

// ritorna il metodo da invocare, per esempio metodoUno

Method metodo = this.getClass().getMethod(nomeMetodo, tipi);

// invoco il metodo passandogli i parametri di ingresso (mapping, form, request, response) sotto forma di array di Object e ritorno un ActionForward

ActionForward forward = (ActionForward)metodo.invoke(this, parametriDiIngresso);

....

Abbiamo visto come si implementa una classe che estende DispatchAction, creando i metodi “metodoUno” e “metodoDue”, ma ricordiamo che la stessa cosa si poteva fare costruendo due Actions separate ognuna per ogni metodo.

La classe org.apache.struts.actions.SwitchAction (versione 1.1)

Con l’introduzione nella nuova versione di Struts della possibilità di creare più sotto-applicazioni, in modo da poter gestire in maniera più consona il lavoro in team, si è venuta anche a creare il bisogno di gestire una nuova situazione, cioè quella di supportare il passaggio da una sotto-applicazione all’altra. Così nasce la classe SwitchAction.

Quando si decide di invocare questa Action lo sviluppatore deve assolutamente ricordarsi di passare nella request i seguenti parametri, se non vuole ottenere una pagina di errore:

  • "prefix” , il prefisso dell’applicazione a cui si deve passare. Questo parametro deve essere preceduto dal     carattere “/” oppure può essere uguale ad una stringa vuota “” nel caso si voglia passare all’applicazione di     default;
  • “page” , la pagina a cui reinderizzarsi una volta che si è passati nella sotto-applicazione indicata prima.

La classe org.apache.struts.actions.ForwardAction

In molte occasioni quando ci spostiamo da una pagina di view all’altra non abbiamo bisogno di passare attraverso una Action per effettuare delle operazioni di business, quindi non facciamo nient’altro che collegare le due pagine con un link. Questa non è una soluzione molto corretta, perché in questo modo andiamo a violare un passaggio del pattern MVC, eliminando la parte di controller. Ciò potrebbe creare dei problemi nella visualizzazione dei messaggi nelle pagine di view, anche perché è proprio il controller che si occupa di gestire la richiesta e di inserire all’interno di questa le classi ApplicationConfig e MessageResources.

Per cercare di evitare questo tipo di problema, senza nello stesso tempo costringere lo sviluppatore a costruire una classe Action che abbia come unico scopo il forward verso una pagina di view, si è creata la classe ForwardAction con l’unico scopo di collegare due pagine di view passando per il controller.

A differenza delle altre Actions il percorso a cui indirizzare l’applicazione dopo l’esecuzione del metodo perform() non viene indicato nel tag <forward> annidato nel tag <action>, ma nell’attributo “parameter”di quest’ultimo.

La classe org.apache.struts.action.ActionForward

Abbiamo visto prima che i metodi execute() o perform() delle classi Actions ritornano un oggetto di tipo ActionForward.

Ma cosa rappresenta questa classe?

Nient’altro che una destinazione a cui deve puntare la ActionServlet una volta terminate le operazioni all’interno delle classi Actions.

E’ andato tutto bene quindi possiamo tranquillamente invocare una pagina di view o un’altra Action, oppure ci sono state delle eccezioni e quindi dobbiamo puntare ad una pagina di errore? La ActionForward contiene le informazioni che ci servono per proseguire il flusso dell’applicazione. Queste informazioni sono impostate nelle sue proprietà:

  • name – Il nome logico con cui bisognerebbe cercare l’istanza di questa classe all’interno dell’ActionMapping fra   quelle disponibili.
  • path – Il percorso relativo al contesto (o anche al modulo nella versione 1.1) a cui il controllo deve essere   reinderizzato.
  • redirect – Un valore booleano che se impostato a “true” indica alla controller servlet di chiamare   HttpServletResponse.sendRedirect() sul percorso indicato, altrimenti il metodo RequestDispatcher.forward().
  • Dalla versione 1.1 questa classe estende org.apache.struts.config.ForwardConfig e ne eredita il parametro :
  • contextRelative – Un booleano che serve per specificare se il percorso è relativo al contesto.

Anche questa classe come la ActionMapping vista prima sarebbe dovuta essere deprecata e sostituita con la classe ForwardConfig, ma anche per questa ci sono state delle complicazioni dovute alla compatibilità con le vecchie versioni di Struts di cui questa classe era una delle API fondamentali.



Quarta Parte top

Abbiamo finora visto nei precedenti articoli le classi di controller del framework e tutte le classi di utilità ad esseassociate: ora andremo ad analizzare le classi che nel pattern MVC si occupano di gestire la parte del model.

La classe org.apache.struts.action.ActionForm

La classe ActionForm è stata creata non soltanto con lo scopo di essere il contenitore dei dati passati attraverso le richieste, ma anche per risolvere tutti quei passaggi obbligatori per un programmatore quando si sviluppa una web application.

Questa infatti viene utilizzata per catturare i dati contenuti all’interno di un form HTML, per validarli ed una volta riscontrato che non ci sono errori passarli alla Action. Quindi questa classe avrà la funzionalità di “firewall”, non permettendo a dei dati non validi di arrivare nel business tier e quindi gestendo i possibili errori all’inizio del processo e non alla fine. Se i dati passati non risulteranno corretti allora si occuperà di ritornare alla pagina di view di partenza mostrando i dati inseriti ed i messaggi di errore rilevati durante la validazione.

Inoltre grazie all’utilizzo della classe BeanUtils (facente parte del Jakarta Commons Project) non siamo più obbligati a farci ritornare tutti i valori dalla request quando li acquisiamo (request.getParameter(“valore1”) etc.) ed ad inserirli nella response quando li inviamo alla pagina di view, perché si occuperà di tutto il framework (a condizione che il nome del campo del form sia uguale al nome dell’attributo nell’ActionForm). Ricordatevi però che il bean è riempito dai parametri del request e non dagli attributi, i due sono delle risorse separate e quindi quello che verrà salvato nella request come attributo non andrà a popolare direttamente l’ActionForm.

L’ActionForm riempita viene poi passata al metodo perform() della Action ed è proprio qui che lo sviluppatore effettua i primi errori architetturali. Infatti molti decidono di passare la ActionForm direttamente alle classi del business tier.

Questa soluzione non è la più corretta anche perché dobbiamo cercare di rendere il più riutilizzabili possibile i componenti di business, cosa non possibile in questo modo perché :

  1. in questo modo creiamo un vincolo difficilmente slegabile tra la parte di view e quella di business;
  2. la classe ActionForm è una classe facente parte di Struts e passandola alle classi del business tier si vincola l’utilizzo di quest’ultime solo con il framework stesso;
  3. la classe ActionForm importa alcuni packages dalle Servlet API, quindi si vincolano i metodi del business tier a queste classi.

Quindi per mantenere i due strati dell’applicazione separati non ci resta che creare altri beans a cui verranno passati i dati prelevati dal form (per effettuare questo passaggio ci può venire incontro sempre il progetto Jakarta Commons BeanUtil).

Vediamo adesso come creare una classe di tipo ActionForm e configurarla all’interno della nostra applicazione.

Per prima cosa creiamo una classe che estenda ActionForm con al suo interno degli attributi ed i corrispettivi metodi set e get per impostare o far ritornare i loro valori.

public class ActionFormEsempio extends ActionForm { 
           private String nome;
private String cognome;
public void setNome(String nome) {
                this.nome = nome;
}
public void setCognome(String cognome) { 
                this.cognome = cognome;
}
public String getNome() { 
                return nome;
}
public String getCognome() { 
                return cognome;
}
}

Naturalmente per poter essere popolati da un form i due attributi del bean devono avere dei campi chiamati “nome” e “cognome” nella pagina di view.

Ora non ci resta che configurare la classe appena creata all’interno del file struts-config.xml, ricordandoci però che i nomi dei form beans sono case sensitive e che l’attributo “type” deve contenere il nome della classe che estende ActionForm con tutto il percorso per trovarla.

....
<form-beans>
<form-bean name="esempioForm" type="package.della.classe.ActionFormEsempio"/>
</form-beans>
....

A questo punto non ci resta che associare questo bean con una o più Actions.

....
<action path="/Path" type="percorso.della.classe.NomeAction"                                  scope="request" 
 name="esempioForm" validate="true" 
             input="/percorso/paginaForm.jsp" > 
   <forward name="Success" path="/AltraAction.do" /> 
   <forward name="Failure" path="/jspDiFailure.jsp" />
</action>
....

Dando un’occhiata al tag <action> possiamo notare la presenza di alcuni attributi:

Dando un’occhiata al tag possiamo notare la presenza di alcuni attributi:

  • scope – questo attributo serve per specificare se vogliamo salvare il bean all’interno della request o della session a seconda se i dati devono essere passati attraverso più pagine di view. Se questo parametro non viene impostato il valore di default è “session”.
  • validate – impostare questo attributo a “true” vuol dire indicare all’applicazione che vogliamo validare i dati del bean implementando all’interno della classe che abbiamo creato prima il metodo validate(). Questo metodo normalmente ritorna un null quindi può anche non venire implementato, mentre se trova degli errori tra i dati ritorna un oggetto di tipo ActionErrors (che vedremo più avanti). In questo punto quindi verranno esaminati i valori forniti prima di essere passati nel metodo perform() della classe Action, ricordandoci però che le validazioni effettuate sono a livello presentation e non di business (i controlli su questo livello verranno eventualmente affrontati nelle classi Action).
  • input – con questo parametro viene specificato al framework, in caso di rilevazione di errori all’interno del metodo validate(), a quale pagina di view rimandare i dati, così che possano venir utilizzati per ripopolare i campi del form.

Oltre al metodo validate() la classe ActionForm ci fornisce il metodo reset() che viene invocato ogni volta che c’è una richiesta. Questo è stato creato per facilitare l’impostazione ai valori di default delle proprietà del bean (booleani a true o false, numeri ad un determinato valore etc.); viene invocato ogni volta che viene chiamato un ActionForm da utilizzare all’interno di una Action. Naturalmente se lo stesso bean viene utilizzato su più pagine dovremo stare attenti a come implementarlo altrimenti perderemmo tutti i valori acquisiti fino a quel momento. Un’alternativa a questo metodo sarebbe quello di creare dei metodi reset() per ogni campo da ridefinire, invocabili all’interno della classe Action.

Logicamente pur utilizzando il framework Struts non è obbligatorio catturare i dati che ci vengono dal form HTML utilizzando la classe ActionForm; infatti lo sviluppatore può decidere liberamente di non associare nessun bean ad una Action e quindi di gestire manualmente tutte le operazioni che questa ci fornisce : trasferimento dei dati dalla request al bean, gestire il processo di validazione, gestire gli errori all’interno della Action (ma chi se la sentirebbe di rinunciare a questi vantaggi per implementarsi tutto da solo?).

La classe org.apache.struts.action.DynaActionForm (versione 1.1)

Utilizzare la classe ActionForm può però causare una serie di problemi, soprattutto in progetti molto grandi. Infatti uno dei problemi maggiori è la creazione di un numero eccessivo di classi (molti infatti creano una classe per ogni form HTML), che rende difficoltosa la manutenzione e la gestione dell’applicazione. Una soluzione potrebbe essere quella di associare una classe per più di un form, che però si va a scontrare con un altro problema, la condivisione dei files. Infatti andando a creare un punto comune a molte parti dell’applicazione si rischia di creare anche una contesa tra gli sviluppatori sull’utilizzo di quella classe, con tutti i problemi che ciò comporta : troppe persone toccano quella classe rendendo spesso il codice illeggibile, tempi di attesa oppure modifiche effettuate da altri che influiscono anche sulla vostra parte di codice etc..

Un altro problema (come se non bastassero già questi) è che qualunque modifica al bean comporta che la classe modificata sia ricompilata.

Per cercare di risolvere tutto ciò si è deciso di aggiungere nella versione 1.1 di Struts la classe org.apache.struts.action.DynaActionForm, che estende la classe ActionForm e che differisce da quest’ultima nel modo di definire le sue proprietà e nei metodi validate() e reset().

Le proprietà del bean in questo caso vengono specificate all’interno dello struts-config.xml, evitando in questo modo di modificare delle classi Java che dovrebbero essere ricompilate, e sono proprio loro che rendono questo bean dinamico perché vengono inserite al suo interno con i corrispondenti metodi get e set in fase di runtime.

Il metodo reset() invece viene invocato al momento in cui si processa la richiesta, mentre nell’ActionForm questo veniva invocato al momento della creazione o quando viene ripresa una sua vecchia istanza. Inoltre non abbiamo un controllo diretto sul metodo (che infatti non deve essere implementato) a meno che non decidessimo di creare una classe che estenda DynaActionForm e che faccia l’overriding del metodo.

Per quanto riguarda il metodo validate() merita un discorso a parte che affronteremo più avanti, vi basti soltanto sapere che la validazione dei dati del form avviene in maniera un po’ più complicata e non soltanto tramite l’implementazione di un metodo.

Vediamo adesso come possiamo utilizzare questa classe.

Per prima cosa, come in tutte le altre classi, va configurata all’interno del nostro struts-config.xml semplicemente aggiungendo un tag <form-bean> all’interno del tag <form-beans> (d’altronde questa classe altro non è che un ActionForm e quindi va impostata nella stessa maniera):

….
<form-beans> 
  <form-bean name="provaDynaForm" dynamic="true" type="org.apache.struts.action.DynaActionForm">    
<form-property  name="campoStringa" type="java.lang.String "/> 
    <form-property  initial=”10” name="campoIntero" type="java.lang.Integer"/> 
  </form-bean>
<form-beans>
.....

Notiamo che a differenza del tag <form-bean> del paragrafo precedente c’è un attributo “dynamic” impostato a “true”. E’ proprio questo che specifica al framework di gestire questa ActionForm in maniera dinamica. Inoltre possiamo vedere che le proprietà del bean sono impostate grazie al tag <form-property> : in questo caso abbiamo inserito nella classe soltanto due proprietà che verranno caricate dalla classe DynaActionForm all’interno di un java.util.Map ponendo come chiave l’attributo “name” e come valore quello di default o quello da noi impostato nell’attributo “initial”.

L’attributo “initial” quindi serve per impostare i valori delle proprietà quando l’applicazione verrà avviata, ma anche quando verrà invocato il metodo reset() del bean che li risistemerà nel loro stato originale.

Nell’attributo “type” invece andremo a specificare il tipo del parametro (cioè se si tratta di una Stringa, di un intero etc.) ricordandoci di utilizzare sempre le classi di wrapper per i tipi primitivi (java.lang.Integer per gli interi, java.lang.Boolean per i booleani etc.) anche perché come abbiamo detto prima questi parametri andranno inseriti all’interno di un Map che accetta solo oggetti.

Come per il metodo reset() anche per il metodo validate() non abbiamo nessun controllo e non siamo in grado di poterlo implementare, quindi come effettuare la validazione dei nostri dati? Semplice, utilizzando come in altre parti del framework un progetto facente parte del Jakarta Commons Project : lo Struts Validator.

Questo progetto dipende da altri già visti in precedenza (Commons BeansUtils, Commons Logging, Commons Collections e Digester) ed inoltre nella versione utilizzata da Struts 1.1 anche da Jakarta ORO.

Quindi per poterlo utilizzare all’interno della nostra applicazione basterà inserire i files commons-validator.jar e jakarta-oro.jar all’interno della sua directory WEB-INF/lib.

Ma quali sono i vantaggi di questo Validator?

Per prima cosa con il Validator siamo in grado di risolvere i due problemi principali che si hanno con l’implementazione del metodo validate() :

  1. un primo problema può essere quello di scrivere codice ridondante, infatti in molti forms i controlli sui campi saranno gli stessi e questi dovranno essere ripetuti per ogni form senza la possibilità di riutilizzare il codice già scritto.
  2. un secondo problema è che quando modifichiamo qualche controllo dovremo ricompilare la classe cambiata.

Tutto viene risolto con l’utilizzo di files XML esterni alle ActionForms, spostando quindi la logica al di fuori dei beans e permettendo di effettuare delle modifiche senza ricompilare tutta l’applicazione.

Il primo di questi files è validation-rules.xml. Questo file è già inserito all’interno del binary di Struts scaricato dal sito Jakarta e contiene delle regole di validazione generali che vengono solitamente utilizzate all’interno di form.

Nel caso in cui le regole contenute non soddisfino tutte le nostre richieste possiamo decidere di estendere questo file con le regole da noi scritte: la cosa migliore da fare è inserirle all’interno di un altro file XML di modo che se ci saranno delle modifiche al framework Validator non saremo costretti a riscrivere tutto nel nuovo file validator-rules.xml.

Sia che volessimo creare un nostro file o estendere quello già esistente come dobbiamo agire?

Leggendo il file DTD fornitoci con Struts (validator-rules_1_1.dtd deprecato in favore del file validator_1_0.dtd) possiamo facilmente capire come inserire le nostre regole all’interno del Validator.

......
<validator  name="minlength" classname="org.apache.struts.validator.FieldChecks" method="validateMinLength"
methodParams="java.lang.Object, org.apache.commons.validator.ValidatorAction,         
org.apache.commons.validator.Field,
org.apache.struts.action.ActionErrors, javax.servlet.http.HttpServletRequest"
depends=”required” msg="errors.minlength">
<!-- il tag validator va inserito all’interno dei tags form-validation e global e rappresenta una regola di validazione con i seguenti attributi:
name : assegna un nome in maniera univoca alla regola di validazione. Questo valore verrà utilizzato sia all’interno di questo file per referenziare le dipendenze tra le varie regole sia all’interno dell’altro file di configurazione del Validator (che vedremo più avanti);
classname : contiene il nome della classe che si occupa della validazione;
method: il metodo della classe che deve essere invocato per effettuare questa validazione;
methodParams : i tipi dei parametri con i relativi percorsi da passare al metodo separati da virgola;
msg : indica la chiave all’interno del nostro file di properties da cui prendere il messaggio di errore nel caso la validazione non vada a buon fine. Nel caso in cui stessimo utilizzando delle regole già presenti all’interno del validator-rules.xml dovremo o modificare il nome all’interno di quest’ultimo con uno presente nell’ApplicationResources.properties (cosa sconsigliabile perché incontreremmo grosse difficoltà in caso di aggiornamento della versione del file XML del Validator) o inserire queste chiavi all’interno del file di properties della nostra applicazione o utilizzando l’attributo msg all’interno dell’altro file di configurazione;
depends : specifica le regole a cui deve dipendere (ad esempio un campo può avere solo determinati valori, ma prima di tutto bisogna vedere se è stato valorizzato). Quindi prima di invocare il metodo di questa validazione verrà invocato quello delle regole da cui dipende, separate da virgola (naturalmente se una di queste non va a buon fine le altre non verranno eseguite). --> 
    <javascript>
< !--
Questo tag contiene il codice javascript che può essere utilizzato se si vuole eseguire una validazione clent-site
---> 
          <![CDATA[ ........... ]]> 
    </javascript>
</validator>
.....

Come possiamo vedere nell’attributo “classname” del tag <validator> è stata inserita la classe org.apache.struts.validator.FieldChecks, una classe di utility specifica per il framework Struts (creata come alternativa alla classe GenericValidator contenuta all’interno del Validator) con all’interno i metodi invocati nel validator-rules.xml. Quando questi vengono chiamati e la validazione non va a buon fine viene creato un ActionError inserito nel request e questo con il suo messaggio di errore viene reso visibile nella parte di view.

Come possiamo vedere nell’attributo del tag è stata inserita la classe , una classe di utility specifica per il framework Struts (creata come alternativa alla classe contenuta all’interno del Validator) con all’interno i metodi invocati nel . Quando questi vengono chiamati e la validazione non va a buon fine viene creato un ActionError inserito nel request e questo con il suo messaggio di errore viene reso visibile nella parte di view.

Come possiamo vedere nell’attributo del tag è stata inserita la classe , una classe di utility specifica per il framework Struts (creata come alternativa alla classe contenuta all’interno del Validator) con all’interno i metodi invocati nel . Quando questi vengono chiamati e la validazione non va a buon fine viene creato un ActionError inserito nel request e questo con il suo messaggio di errore viene reso visibile nella parte di view.

Come possiamo vedere nell’attributo del tag è stata inserita la classe , una classe di utility specifica per il framework Struts (creata come alternativa alla classe contenuta all’interno del Validator) con all’interno i metodi invocati nel . Quando questi vengono chiamati e la validazione non va a buon fine viene creato un ActionError inserito nel request e questo con il suo messaggio di errore viene reso visibile nella parte di view.

Il secondo file di configurazione richiesto dal framework Validator è validation.xml. Questo file associa le regole di validazione contenute all’interno del validation-rules.xml alle varie classi ActionForms.

Andiamo ad analizzare la struttura di questo file, che dovremo creare completamente noi seguendo le indicazioni forniteci dal file validation_1_1.dtd presente anch’esso all’interno del binary di Struts (ed anch’esso deprecato in favore del file validator_1_0.dtd), dichiarando una regola di validazione per ogni proprietà del form che ne abbia bisogno.

<form-validation>
<!-- E’ la root del file validation.xml --> 
 <global>
<!-- Questo tag può essere presente 0 o più volte e permette di configurare delle costanti globali (assegnadone il nome ed il valore) da utilizzare all’interno di tutto il file quindi disponibili anche all’interno del tag
formset ---> 
  <constant> 
      <constant-name>nomeCostanteGlobale</constant-name> 
      <constant-value>valoreCostante</constant-value> 
  </constant>                           
 </global> 
 <formset>  
<!-- Questo tag deve essere presente almeno una volta e contiene un set di form associate ad una localizzazione.
Possiede gli attributi language e country che sono utili per l’internazionalizzazione : nel caso in cui non vengano impostati l’applicazione utilizzerà il linguaggio di default. All’interno di questo tag si possono definire delle costanti global come nel tag visto prima.
--> 
  <form name="nomeForm">
<!-- Il tag form corrisponde ad una particolare ActionForm della nostra applicazione e possiede delle proprietà che devono essere validate. L'attributo name deve combaciare con l'attributo name del tag  form-bean all'interno dello struts-config.xml -->
   <field  property="proprietàForm" depends="nomeRegola(esempio minlength)">
<!-- Questo tag corrisponde alla proprietà presente all’interno del form che deve essere validata e possiede i seguenti attributi:
property : il nome della proprietà all’interno della classe ActionForm;
depends : le regole (possono essere più di una separate da virgola) di validazione che il campo deve rispettare.
--> 
     <msg name=”minlength” key=”chiaveNelFile” />
<!-- Il tag msg specifica un messaggio associato al campo; questo va a sostituire il valore impostato nell’attributo msg all’interno della regola nel validate-rules.xml.
Oltre a questo ci sono anche i tags arg0, arg1, arg2 e  arg3 utilizzati per inserire degli eventuali valori all’interno del messaggio di errore.
--> 
     <var>
<!-- Il tag var viene utilizzato per passare degli eventuali parametri al metodo di validazione --> 
       <var-name>nomeParametro</var-name> 
       <var-value>valoreParametro</var-value> 
     </var>                   
   </field>    
  </form>            
 </formset>  
</form-validation>

Una volta creati i due files di configurazione del framework Validator questi andranno impostati all’interno della nostra applicazione e come al solito, dopo averli inseriti all’interno della directory WEB-INF, andremo a modificare lo struts-config.xml.

...
<plug-in className="org.apache.struts.validator.ValidatorPlugIn">
  <set-property  property="pathnames"  value="/WEB-INF/validator-rules.xml,/WEB-INF/validator.xml"/>
</plug-in>
...

In questo modo specifichiamo al framework che quando l’applicazione verrà avviata nel metodo initApplicationPlugIns() della classe RequestProcessor verrà invocato il metodo init() della classe ValidatorPlugIn che permetterà di caricare in memoria i files di configurazione del Validator (questi sono impostati nell’attributo “value” separati da virgola e con nome dell’attributo “property” uguale a “pathnames”).

Ma una volta caricati i files di configurazione del Validator come faccio a specificare all’applicazione quali sono i form beans che lo vogliono utilizzare per la validazione?

Basterà solo inserire l’attributo “validate” impostato a “true” nel tag <form-bean> come nel caso delle classi ActionForms?

Per l’utilizzo del framework Validator all’interno di Struts sono state disegnate delle sottoclassi sia della classe DynaActionForm sia della classe ActionForm (nel caso lo volessimo utilizzare al posto del metodo validate() del bean).

Entrambi possono essere estese da delle sottoclassi diverse a seconda che lo sviluppatore voglia associare la validazione con il tag <form bean> o con il tag <action>; ma perché questa distinzione? Per il semplice fatto che se ci troviamo di fronte ad un bean che possiede differenti regole di validazione noi abbiamo in questo modo la possibilità di associarne solo alcune alla Action che viene invocata. Nella figura qui sotto sono esposte le varie classi disponibili con le loro relazioni, notiamo che le classi associate con le Actions hanno al loro interno la parola Action per distinguerle. 

Fig. 7 Quarta Parte 

La classe org.apache.struts.action.ActionMessage (versione 1.1)

 .

Fig. 8 Quarta Parte

Nella nuova versione di Struts è stata introdotta la classe ActionMessage.

Questa classe ha il solo compito di contenere dei messaggi che verranno in seguito visualizzati nelle pagine di view.

Ogni istanza della classe ActionMessage con il messaggio da mostrare all’utente verrà inserita all’interno di un contenitore rappresentato dalla classe ActionMessages.  

Questo contenitore verrà salvato all’interno della request (tramite il metodo saveMessages() della classe Action) e visualizzato con l’utilizzo del tag <html:messages/>.

La classe org.apache.struts.action.ActionError

Se i messaggi che vogliamo visualizzare nelle pagine di view sono dei messaggi particolari (messaggi di errore), allora non ci resta che utilizzare una classe che estenda la classe ActionMessage (o almeno lo fa nella versione 1.1 di Struts) e che specifichi all’utente la gravità dell’informazione ricevuta. Infatti sicuramente un messaggio di errore è bloccante rispetto ad un warning che serve solo per dare un’informazione all’utente su una possibile anomalia da correggere, ma che non vincola il regolare svolgimento dell’applicazione.

Come abbiamo potuto vedere il metodo validate() della ActionForm ritorna un oggetto di tipo ActionErrors, che non è nient’altro che un contenitore di istanze della classe ActionError.

Se all’interno del metodo viene trovato che qualche dato non è corretto verrà istanziato un oggetto di tipo ActionError. Il suo costruttore riceverà come parametro una stringa che rappresenta la chiave con cui recuperare il messaggio di errore scritto su un file di properties(ApplicationResources.properties) e degli eventuali altri parametri per una costruzione dinamica del messaggio.

Ad esempio se scriverò :

....
ActionError error = new ActionError("errore.generico", “generico” );
.....

allora dovrò avere all’interno del file di properties

….
errore.generico=Questo è un messaggio di errore di tipo {0}
…..

che darà come risultato

Questo è un messaggio di errore di tipo generico

Naturalmente possiamo inserire più valori.

Una volta istanziato un oggetto di tipo ActionError dobbiamo crearne uno anche di tipo ActionErrors in cui aggiungere l’istanza del precedente.

Quest’ultimo verrà salvato all’interno della request che lo renderà visibile all’utente tramite dei custom tags nelle pagine JSP : uno di questi è <html:errors/>.



Username:
Password:
To sign up for an account, click register... Register
Hide





Powered By



Campagna Anti-IF


Skin


PARTNER
Zio Budda
HostingJava


LICENZA



Eccetto dove diversamente specificato, i contenuti di questo sito sono rilasciati sotto licenza Creative Commons

Sitemap  © 2002-2004 Copyright Information. Privacy . Today is domenica 1 agosto 2010