1. Introduzione a Struts
Andrea Colleoni
Applicazioni Web e Framework: MVC
Le applicazioni web, funzionano tutte seguendo le stesse regole di base e quindi si
assomigliano di più di quanto non differiscano. Per tale motivo è nata la necessità per gli
sviluppatori di tali applicazioni di dotarsi di infrastrutture che costituiscano l’impalcatura
generale dell’applicazione, le fondamenta del progetto: i framework.
Parallelamente gli ingegneri del software hanno individuato che, in linea generale, i tipi di
problemi che si presentano nell’informatica sono ricorrenti e non in numero elevato;
tipologie di problemi ricorrenti hanno tipologie di soluzione altrettanto ricorrenti: i design
pattern.
MVC (Model View Controller) è un pattern di progetto che si applica alle applicazioni multi
tier, come quelle web, e consiste nella separazione netta tra modello dei dati (il model),
controllo del flusso dell’applicazione (il controller) e presentazione dei risultati all’utente
(la view).
Struts è un framework che aiuta a perseguire la separazione indicata dal pattern MVC e
dalla sua evoluzione Model 2, fornendo un controller già funzionante e una base su cui
costruire la view.
Struts: quando, dove, come e perché; orientarsi nel sito
Struts è un progetto sviluppato negli anni dal 2000 al 2001 e ospitato nel grande
contenitore di progetti che è l’Apache Software Foundation; è reperibile all’indirizzo
http://struts.apache.org/.
L’apporto principale al suo sviluppo è venuto da Craig McClanahan già principale fautore
del progetto Apache Tomcat ed ora personaggio di spicco nella progettazione di
JavaServer Faces dello strato web delle specifiche J2EE.
Oggi il popolare framework è diviso in due parti: lo Struts Shale Framework e lo Struts
Action Framewok; noi ci occuperemo di quest’ultimo che è l’evoluzione del framework
originario.
Struts non è un’IDE, non ha un front-end grafico e non costruisce automaticamente
applicazioni web; è semplicemente una libreria di oggetti già costruiti che svolgono alcune
funzioni di base essenziali per il buon funzionamento di applicazioni web MVC.
Struts non è l’unico framework e non è necessariamente il più valido, ma è oggi uno dei
più utilizzati e diffusi. Ha molte estensioni tra cui alcune discretamente diffuse; Tiles, di cui
non parleremo, è una di queste e fornisce un comodo strumento per costruire la view.
Download (versione 1.2.8), contenuto del file ZIP e suo utilizzo
Sul sito all’indirizzo http://struts.apache.org/downloads.html sono disponibili per il
download sia le release con tutte le librerie compilate e pronte per l’uso (i binaries) sia i
programmi sorgenti (i sources). Attualmente la versione più aggiornata disponibile per il
download è la 1.2.8.
2. Nell’archivio struts-1.2.8-bin.zip (oppure tar.gz) contenente le librerie compilate, troviamo
le seguenti cartelle:
/lib: contiene tutti i file JAR necessari a far funzionare le varie parti del framework, più
tutte le DTD e le definizioni delle tag libraries utilizzabili
/contrib: contiene un’estensione delle tag libraries di Struts; non ci occuperemo di
questa parte
/webapps: contiene alcune applicazioni web di esempio che utilizzano Struts, più
un’applicazione web utilizzabile come base di partenza per costruire applicazioni
basate su Struts e la documentazione su Struts e Tiles
Per far funzionare le applicazioni di esempio, per consultare la documentazione e per
provare gli esempi di questo articolo avremo bisogno di un servlet container in cui
installare le applicazioni.
Essendo le specifiche Servlet e JSP uno standard, qualunque servlet container è adatto,
ma in quest’articolo ci riferiremo sempre al server Apache Tomcat versione 5.x, reperibile
all’indirizzo http://tomcat.apache.org/ e di cui sono disponibili gli installer per Windows o gli
archivi compressi, contenenti i file per l’installazione su tutti i sistemi operativi.
Nei nostri esempi il server è installato sul computer locale (localhost) e sulla porta http di
default (80) per cui gli indirizzi saranno nella forma http://localhost/struts-
examples/welcome.do.
L’installazione delle webapps fornite con Struts può avvenire copiando i file .WAR nella
directory webapps del servlet container, eseguendone un upload dal pannello di
amministrazione del servlet container oppure estraendone i file con un programma per la
gestione di file compressi tipo WinZip in una directory sotto webapps del servlet container.
Per gli scopi di quest’articolo è necessario installare le applicazioni struts-
documentation.war, struts-mailreader.war e struts-blank.war.
Uno sguardo alle applicazioni di esempio
Struts viene fornito con alcune applicazioni di esempio tra cui struts-mailreader.
Esplorando il contenuto all’indirizzo http://localhost/struts-mailreader/Welcome.do, si
accede ad una semplice applicazione di registrazione di informazioni; tramite un form è
possibile registrare un utente, per il quale possono poi essere definiti alcuni account di
accesso a server di posta. Una volta registrato, un utente, può rieseguire l’accesso e
visualizzare l’elenco degli account definiti. I dati vengono conservati in un database XML
nel file WEB-INF/database.xml.
Dando un’occhiata più in profondità, cerchiamo di capire come funziona e quale plus dà
Struts a questa applicazione. In primo luogo osserviamo il descrittore WEB-INF/web.xml:
Nella sezione relativa alla definizione dei servlet, viene definito il controller del nostro
framework MVC: l’ActionServlet.
web.xml (frammento)
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml, /WEB-INF/struts-config-registration.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
3. L’ActionServlet viene caricato all’avvio e gli vengono passati come parametri due file di
configurazione scritti in XML che il servlet caricherà all’avvio dell’applicazione e di cui
vedremo tra poco il contenuto.
Per essere invocato dal servlet container, l’ActionServlet, deve essere associato ad un
URL pattern:
web.xml (frammento)
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
Qualunque URL che termini con .do, invoca l’ActionServlet, quindi a dispetto di quanto
possa sembrare a prima vista, non ci sono file “.do” nella webapp.
Nella sezione delle Tag Libraries, vengono caricate le Tag Libraries di Struts che sono
presenti nella cartella WEB-INF.
Vediamo il contenuto di una semplice pagina JSP: /index.jsp
index.jsp
<%@ taglib uri="/tags/struts-logic" prefix="logic" %>
<logic:redirect action="/Welcome"/>
Questo frammento di codice consente di registrare l’uso della Tag library struts-logic
all’interno della pagina JSP e quindi di utilizzare il tag redirect di tale libreria per eseguire
un’azione di redirect. In particolare il browser viene rediretto verso una Action di nome
Welcome, la quale è definita nel file di configurazione di Struts.
Nella sezione action-mappings del file WEB-INF/struts-config.xml, viene definita tra le
altre l’azione Welcome:
struts-config.xml (frammento)
<action path="/Welcome"
type="org.apache.struts.webapp.example.WelcomeAction">
<forward name="failure" path="/Error.jsp" />
<forward name="success" path="/welcome.jsp" />
</action>
Questo frammento della configurazione, che viene chiamato ActionMapping, fa sì che
quando viene richiesto l’URL /Welcome.do (è case sensitive, quindi con la W maiuscola),
venga creato un oggetto di classe WelcomeAction, che è istanziato dal framework, ma
definito dal programmatore.
Il frammento contiene anche la definizione di due forward di nome failure e success. A
questi sono associate due pagine JSP, rispettivamente /Error.jsp e /welcome.jsp.
Vediamo cosa significano queste impostazioni e come funziona un’azione Struts.
Aprendo il file WEB-INF/src/org/apache/struts/webapp/example/WelcomeAction.java, a cui
l’action mapping visto sopra si riferisce, notiamo che l’azione estende una classe fornita
nell’esempio: BaseAction che a sua volta estende la classe del framework Action
introducendo rispetto a quest’ultima solo alcuni semplici metodi di utilità.
Una volta individuata la classe corrispondente all’azione, il framework la istanzia e ne
invoca il metodo execute() al quale vengono passati tutti gli oggetti per operare con il
contesto web e due oggetti Struts: l’ActionMapping che rappresenta il contenuto del file
di configurazione e l’ActionForm. Il metodo quindi termina restituendo al framework che
l’ha invocato, un oggetto di tipo ActionForward.
WelcomeAction.java (frammento)
public final class WelcomeAction extends BaseAction {
public ActionForward execute(
ActionMapping mapping,
ActionForm form,
4. HttpServletRequest request,
HttpServletResponse response)
throws Exception {
...
if (messages.size()>0) {
...
return findFailure(mapping);
}
return findSuccess(mapping);
}
}
Nel codice, a seconda del successo o meno di alcune istruzioni che non è nostro scopo
approfondire, possiamo decidere di “uscire con successo” o “uscire con insuccesso” dal
metodo.
I metodi findSuccess() e findFailure() sono definiti in BaseAction e facilitano il
rintracciamento dei tipici forward success e failure. Nei forward definiti nell’action
mapping, viene definito cosa fare in caso di successo e cosa in caso di insuccesso.
In caso di successo, nel nostro esempio, viene caricata nel browser la “view” /welcome.jsp
che è la pagina che si vede navigando l’URL http://localhost/struts-
mailreader/Welcome.do, che chiude gli elementi del paradigma MVC.
In definitiva uno dei compiti del controller è quello di governare il flusso dell’applicazione
web attraverso file di configurazione scritti in XML.
Il framework offre anche un altro strumento configurabile molto utile: l’ActionForm. Nel file
di configurazione, notiamo che c’è un’azione /Logon.do che fa semplicemente un forward
alla pagina /logon.jsp.
<action path="/Logon"
forward="/logon.jsp"/>
La pagina /logon.jsp, contiene un form costruito con i tag della libreria HTML di Struts:
logon.jsp (frammento)
<html:form action="/SubmitLogon" focus="username"
onsubmit="return validateLogonForm(this);">
...
<html:text property="username" size="16" maxlength="18"/>
...
<html:password property="password" size="16" maxlength="18"
redisplay="false"/>
...
<html:submit property="Submit" value="Submit"/>
...
<html:reset/>
...
</html:form>
5. Anche nel file di configurazione di Struts esiste una rappresentazione di questo form; si
trova nella sezione form-beans:
struts-config.xml (frammento)
<form-bean name="LogonForm"
type="org.apache.struts.validator.DynaValidatorForm">
<form-property name="username" type="java.lang.String"/>
<form-property name="password" type="java.lang.String"/>
</form-bean>
Il contenuto del form può essere quindi gestito dal framework (in questo caso viene
addirittura validato automaticamente dal framework attraverso l’uso del
DynaValidatorForm) e rappresentato al suo interno come un JavaBean. L’azione
/SubmitLogon a cui il form invia le informazioni è così definita:
struts-config.xml (frammento)
<action path="/SubmitLogon"
type="org.apache.struts.webapp.example.LogonAction"
name="LogonForm"
scope="request"
input="logon">
...
</action>
La classe LogonAction conterrà il codice per eseguire l’autenticazione utilizzando
username e password forniti dal form:
LogonAction.java (frammento)
public ActionForward execute(...) throws Exception {
...
String username = (String) PropertyUtils.getSimpleProperty(form,
USERNAME);
String password = (String) PropertyUtils.getSimpleProperty(form,
PASSWORD);
...
return (mapping.getInputForward());
...
return (findSuccess(mapping));
}
Vale la pena di osservare anche che il metodo execute() è in grado di ritornare alla pagina
di logon in caso di errori, con il metodo mapping.getInputForward() che restuisce il
forward definito nell’attributo input dell’action-mapping.
Uno sguardo alla "blank application"
La “blank-application” è contenuta nell’archivio struts-blank.war e contiene lo “scheletro” di
un’applicazione web che funziona con il controller Struts. Ha un web.xml che carica
l’ActionServlet con la configurazione definita nel file struts-config.xml, che inizialmente
contiene alcuni esempi ben commentati.
Per iniziare a costruire applicazione web con Struts la blank-application fornisce quindi
una buona base di partenza.
Per interagire con il file di configurazione di Struts, può essere comodo inizialmente
utilizzare uno strumento di supporto grafico che aiuti a ricordare la struttura del file e il suo
utilizzo. Un buono strumento in tal senso è la Struts console reperibile all’indirizzo
http://www.jamesholmes.com/struts/console/ di cui in questo esempio è stat usata la
versione 4.8.
Per iniziare con una nuova applicazione, quindi estrarremo il file struts-blank.war in
qualche directory nella quale verranno costruiti tutti i percorsi e i file di default, quindi
usando la console struts e un kit di sviluppo per Java procederemo con lo sviluppo di
azioni e pagine JSP.
6. Costruzione di un semplice esempio
Proviamo ora a costruire, partendo dalla blank.application, un’applicazione web che svolga
i seguenti compiti:
Da una pagina /text.jsp venga offerta la possibilità di inserire del testo
All’invio del form tale testo venga trattato da classi di back end e restituito
Servlet Container
WebApplication
Model
Browser text.jsp
Action
ActionServlet
success.jsp
struts-config.xml
Prima di tutto estraiamo con il programma di decompressione il file struts-blank.war in una
cartella, ad esempio c:esempistruts.
Poi con un editor Java creiamo secondo la struttura delle applicazioni web, le classi che
rappresentino il nostro model; costruiamo un JavaBean che rappresenti la nostra
informazione ed una classe adatta a trattarlo:
TextBean.java
public class TextBean {
private String text;
public String getText() {
return this.text;
}
public void setText(String text) {
this.text = text;
}
}
La classe TextBean è molto semplice: ha una proprietà di nome text di
tipo String accessibile sia in lettura che in scrittura.
TextManager.java
public class TextManager {
public TextManager() {
}
public void modifica (TextBean tb) {
tb.setText("***" + tb.getText() + "***");
}
}
TextManager ha solo un metodo che altera il contenuto della proprietà
text del bean TextBean
7. Creiamo quindi un semplice form usando la libreria HTML di Struts al posto dei normali tag
HTML; il form avrà come action l’azione /SubmitAction che andremo a definire
successivamente in struts-config.xml:
text.jsp
<%@ taglib uri="/tags/struts-html" prefix="html" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Form</title>
</head>
<body>
<html:form action="/SubmitAction">
Testo: <html:text property="textproperty" /><br />
<html:submit />
</html:form>
</body>
</html>
Usiamo la struts console per costruire il form e mappare l’azione; dal menù file apriamo il
file struts-config.xml che abbiamo estratto in c:esempistrutsWEB-INF ed eseguiamo i
seguenti passi:
Aggiungiamo un form bean di nome textform e di tipo DynaActionForm ed a questo
aggiungiamo una proprietà di nome textproperty di tipo String
Ora aggiungiamo un’action-mapping con URL /SubmitAction che usi il form bean che
abbiamo appena creato; il tipo di mapping deve essere SubmitAction che è il nome
della classe che andremo a creare fra poco per gestire l’azione:
8. Aggiungiamo nella configurazione del mapping anche un forward che chiameremo
success che punti al path /success.jsp:
Possiamo salvare lo struts-config.xml, chiudere la struts console e prosegure con
l’editor Java
Ora dobbiamo costruire la nostra classe SubmitAction che preso il valore inserito nel
form lo manipola con le classi del model e lo restituisce alla pagina /success.jsp:
SubmitAction.java
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionForm;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.beanutils.PropertyUtils;
public class SubmitAction extends Action {
public ActionForward execute(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws Exception {
ActionForward retValue;
TextBean tb = new TextBean();
TextManager tm = new TextManager();
tb.setText((String) PropertyUtils.getSimpleProperty(form, "textproperty"));
tm.modifica(tb);
request.setAttribute("risultato", tb);
return mapping.findForward("success"); }
9. }
Per rendere il risultato disponibile alla pagina view, una tecnica
utilizzata è quella di valorizzare un attributo dell’oggetto request,
il quale poi verrà letto con la libreria di tag di Struts bean.
Infine aggiungiamo la pagina success.jsp che deve presentare all’utente il risultato del
processo avvenuto nel back-end:
Success.jsp
<%@ taglib uri="/tags/struts-bean" prefix="bean" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JSP Page</title>
</head>
<body>
Il testo contenuto nel JavaBean processato è <bean:write name="risultato" property="text" />
</body>
</html>
Accediamo con il tag bean:write ad un bean che si chiami risultato e
quindi ne scriviamo la proprietà text. Da notare il fatto che accediamo
a text nel bean e non più textproperty del form.
Per installare ed eseguire l’esempio è necessario compilare il progetto e copiare tutti i file
dell’applicazione web contenuti nella cartella c:esempistruts nel servlet container.
Una volta copiati i file necessari navighiamo all’URL http://localhost/struts-blank/text.jsp.
Inseriamo del testo nella casella di testo
Inviamo il form ed avremo il risultato:
Conclusioni e bibliografia
Nell’esempio abbiamo toccato i punti essenziali dell’Action Framework che di fatto è
l’elemento architetturale che consente di scrivere applicazioni web secondo il paradigma
MVC.
Struts in effetti è un framework completo e diffuso e offre anche basi per sviluppare
organicamente altre parti delle applicazioni web, come il supporto
all’internazionalizzazione, alla validazione che in quest’articolo abbiamo solo sfiorato.
L’avvento di altri validi framework di successo ed il tentativo di standardizzare lo sviluppo
di applicazioni MVC condizionano lo sviluppo di Struts ed in effetti la divisione tra l’Action
Framework e Shale è conseguenza di questo fatto.