La versione 2 di Struts risulta profondamente rinnovata rispetto alla precedente. In realtà il progetto è nato con il nome di WebWork, che non ha nessun legame particolare con la versione 1 di Struts, per poi passare, dopo una prima fase di analisi e sviluppo, al nome Struts 2. Per questo motivo la migrazione dalla versione 1 alla versione 2 non è semplice.
In questo articolo analizziamo i concetti principali della nuova versione del progetto, proponendo la struttura base di un applicazione java basata sul nuovo framework che semplifica ulteriormente la vita dello sviluppatore, sia in fase di configurazione delle proprie applicazioni, sia in fase di sviluppo.
Utilizzando il nuovo framework è possibile scrivere software facilmente manutenibile, solido e aderente al pattern MVC. Naturalmente, gli strumenti a nostra disposizione sono molteplici e non possono essere analizzati tutti in questo articolo. Ci limiteremo a fornire un semplicissimo esempio d'uso per prendere confidenza con il nuovo framework.
Nel momento in cui questo articolo viene redatto la versione attualmente stabile è la 2.0.14. Il sito ufficiale del progetto è struts.apache.org, dal quale è possibile scaricare sia le librerie, che la documentazione.
Prima di partire con l'esempio pratico, è giusto mettere in evidenza le differenze principali rispetto alla versione precedente.
- web.xml
- Struts: Il controllo viene affidato ad una Servlet. Di default alla ActionServlet ma è possibile definire una Action personalizzata
- Struts 2: Il controllo viene affidato ad un Filter. Di default al FilterDispatcher ma è possibile definire un Filtro personalizzato
- URI pattern
- Struts: Di default viene utilizzato il pattern *.do per identificare una richiesta che la ActionServlet prenderà in carico
- Struts 2: Di default viene utilizzato il pattern *.action per identificare una richiesta che il FiltroDispatcher prenderà in carico
- File di configurazione
- Struts: Di default il nome del file di configurazione è struts-config.xml che va posizionato allo stesso livello del file web.xml
- Struts 2: Di default il nome del file di configurazione è struts.xml che va posizionato in una directory del classpath
- Mapping delle Action
- Struts: Il mapping di una Action viene definito nel file di configurazione mediante il tag action-mapping
- Struts 2: Il mapping di una Action viene automaticamente generato dalla concatenazione "package-nome della action", entrambi definiti nel file di configurazione
- Proprietà di una Action
- Struts: Le proprietà necessarie ad una Action vengono definite in una classe ActionForm, nella quale occorre definire anche i metodi get e set
- Struts 2: Le proprietà necessarie vengono definite direttamente nella Action nella quale occorre definire anche i metodi get e set
- Action
- Struts: Una Action deve estendere la classe org.apache.struts.action.Action
- Struts 2: Una Action deve implementare l'interfaccia com.opensymphony.xwork2.Action oppure estendere la classe com.opensymphony.xwork2.ActionSupport che, a sua volta, è un'implementazione di Action
- Metodo execute
- Struts: Il metodo execute di una Action restituisce un ActionForward
- Struts 2: Il metodo execute di una Action restituisce una stringa. Il Controller stabilisce, in base alla stringa restituita, qual è la vista da richiamare
- TagLibrary
- Struts: Sono disponibili diverse taglibrary, suddivise per tipo di argomento trattato (logic, bean, HTML)
- Struts 2: È disponibile un'unica taglibrary che mette a disposizione sia operazioni di logica che di rendering HTML
Applicazione di esempio
Svilupperemo un'applicazione che, in base all'orario di sistema, visualizzerà una pagina specifica. In pratica, se l'orario è incluso tra le 5 e le 16 verrà visualizzata una pagina con lo sfondo azzurro (giorno), altrimenti verrà visualizzata una pagina con lo sfondo blu (notte).
Prima di tutto aggiungiamo le seguenti librerie al nostro progetto, incluse nello zip completo scaricabile dal sito ufficiale del progetto:
- commons-logging-1.0.4.jar
- freemarker-2.3.8.jar
- ognl-2.6.11.jar
- struts2-core-2.0.12.jar
- xwork-2.0.6.jar
Come tutte le Web application scritte in java, occorre creare il file web.xml, nel quale, oltre alle usuali dichiarazioni, è necessario definire un filtro al quale vengono affidate tutte le richieste. Questo filtro rappresenta il Controller dell'applicazione. Naturalmente è necessario indicare anche l'URL al quale esso risponde. Nel nostro esempio, utilizziamo la classe di default messa a disposizione dal framework, org.apache.struts2.dispatcher.FilterDispatcher
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<filter>
<filter-name>struts</filter-name>
<filter-class>
org.apache.struts2.dispatcher.FilterDispatcher
</filter-class>
</filter>
<filter-mapping>
<filter-name>struts</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
Nel nostro esempio il filtro viene invocato su tutte le URL (/*) che hanno come suffisso l'estensione .action (default). È possibile comunque configurare diversamente il filtro, indicando un pattern diverso da quello di default.
Ogni richiesta intercettata dal Controller viene inoltrata ad una Action, una classe Java che implementa l'interfaccia com.opensymphony.xwork2.Action. L'unico metodo da dover implementare è execute()
, che viene invocato automaticamente dal framerwork nel momento in cui il Controller inoltra la richiesta alla Action. È possibile anche estendere la classe com.opensymphony.xwork.ActionSupport, un'implementazione di Action, dalla quale è possibile ereditare un insieme di metodi e attributi di grande utilità nello sviluppo, quali gestione degli errori, dei messaggi e così via.
Anche la versione 2 di Struts, così come il suo predecessore, prevede un file di configurazione. Il file deve essere posizionato nel classpath della webapp. Il posto più appropriato ove inserirlo è in /WEB-INF/classes/struts.xml. Tale file contiene la definizione di tutte le Action disponibili nel sistema. Ciascuna Action è identificata da un package, da un nome univoco e da uno o più destinazioni (result). Il result verrà utilizzato dal Controller per decidere qual è la vista appropriata da richiamare.
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <package name="html" extends="struts-default"> <action name="HtmlAction" class="it.html.action.HtmlAction"> <result name="giorno">/jsp/giorno.jsp</result> <result name="notte">/jsp/notte.jsp</result> </action> </package> </struts>
Il nostro file struts.xml
contiene soltanto la Action HtmlAction
, il cui package è HTML
. La Action prevede due possibili comandi di uscita (result
): giorno e notte. Se il metodo execute restituisce la stringa "giorno", verrà caricata la pagina giorno.jsp, se il metodo execute restituisce la stringa "notte", verrà caricata la pagina notte.jsp. Sarà possibile richiamare la Action mediante il seguente URL: html/HtmlAction, che corrisponde alla concatenazione package-nome.
package it.html.action; import java.util.Calendar; import com.opensymphony.xwork2.ActionSupport; public class HtmlAction extends ActionSupport { private static final long serialVersionUID = -4383906363325947941L; public String execute() throws Exception { Calendar cal = Calendar.getInstance(); int ora = cal.get(Calendar.HOUR_OF_DAY); if (ora > 16 || ora < 5) return "notte"; else return "giorno"; } }
Il codice della nostra Action è semplicissimo e ci serve soltanto come esempio. Il metodo execute deve restituire una stringa: se l'orario è superiore alle ore 16 ed inferiore a 5, restituisce notte, altrimenti restituisce giorno.
Uno dei concetti nuovi di Struts 2 è rappresentato dagli Interceptor, classi stateless (che non mantengono uno stato tra invocazioni successive) che possono essere invocate prima e dopo una Action, o un insieme qualuque di Action.
Gli interceptors svolgono lo stesso compito dei filtri, infatti, entrambi implementano il pattern "interceptor". Usando questi interceptors è possibile, per esempio, validare l'input, intercettare eccezioni e così via. Il framework dispone di numerosi interceptor predefiniti, ciascuno dei quali può essere inserito in uno stack. L'ordine di esecuzione dei diversi interceptors è dato dal loro ordine di dichiarazione nello stack. La filosofia utilizzata è quella FIFO, chi è dicharato per primo, viene eseguito per primo.