Apache Struts è un framework open source per lo sviluppo di applicazioni studiato come estensione/evoluzione delle servlet dei progetti J2EE. Permette di creare applicazioni Web di grandi dimensioni, agevola la parallelizazione degli sviluppi e offre delle potentissime tag-lib, la validazione dei form e la gestione della localizzazione/l’internazionalizzazione. In questo sito si fa riferimento sempre alla mitica versione 1.3 del framework, la versione 2 non mi è mai piaciuta ma una guida è disponile vecchia versione del sito. La base del framework si basa su alcuni concetti:
- classi Action gestiscono la logica di business e la logica di forward tramite metodi specifici, di fatto sono estensioni evolute delle classi Servelet
- classi Form gestiscono i dati inseriti dall’utente nelle pagine definendo le regole di validazione dei dati
- le pagine jsp non devono aver nessuna logica ma hanno solo il compito di visualizzare i dati
Un utente esperto avrà notato che queste tre componenti rispettano l’architettura MVC dove il Model sono le classi Form, le view sono le classi jsp e il controtroller sono le classi Action. Il framework per funzionare correttamente ha bisogno che tutte le classi Form e le classi action vengano censite in file di configurazione che storicamente viene sempre chiamato:
struts-config.xml
che deve essere censito nel web.xml. Per creare un semplice esempio di progetto con questo framework si parte da un progetto webapp standard con maven:
mvn archetype:generate -DgroupId=it.alnao -DartifactId=06Struts -DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false
E bisogna importare le dipendenze del core e della tablib:
<dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> </dependency> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts-core</artifactId> <version>1.3.10</version> </dependency> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts-taglib</artifactId> <version>1.3.10</version> </dependency>
Nel web.xml si vede censire una (e una sola) servlet dove si indica la posizione del file di configurazione xml previsto dal framework:
<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</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>*.nao</url-pattern> </servlet-mapping>
Nel file di configurazione si deve procedere al censimento di due blocchi: il primo riguarda i form-beans, cioè tutte le classi Form del progetto, il secondo definisce l’elenco di tutte le action con il corrispettivo form, path, classe e tutte le classi jsp sulle quali le classi Action potranno fare il forward:
<struts-config> <form-beans> <form-bean name="loginForm" type="it.alnao.mavenExamples.PrimoForm" /> </form-beans> <action-mappings> <action name="loginForm" path="/login" type="it.alnao.mavenExamples.PrimaAction" scope="request" input="/index.jsp"> <forward name="failure" path="/index.jsp" redirect="true" /> <forward name="success" path="/success.jsp" redirect="true" /> </action> </action-mappings> </struts-config>
La classe Action estende un tipo previsto dall’architettura e deve definire obbligatoriamente un metodo execute
che permette di definire logiche di business (come per esempio la validazione di un username-password) con conseguenti logiche di business (per esempio quale pagina visualizzare a seconda se le credenziali sono valide):
public class PrimaAction extends Action { @Override public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { PrimoForm loginForm = (PrimoForm) form; if (loginForm.getUserName() == null || loginForm.getPassword() == null || ! loginForm.getUserName().equalsIgnoreCase("alnao") || ! loginForm.getPassword().equals("bellissimo")) { return mapping.findForward("failure"); } else{ return mapping.findForward("success"); } } }
La classe Form oltre a definire tutti i campi come un semplice Bean/model definiscono due metodi: reset e validate, il primo serve ad inizializzare i dati di un form, il secondo serve a validarli. Qualora una classe action viene eseguita e il suo form non sia valido, la servlet non viene eseguita ma viene eseguito un forward automatico nella pagina definita come input nel file xml di configurazione:
public class PrimoForm extends ActionForm { private String userName = null; //TODO Setter & Getter private String password = null; @Override public void reset(ActionMapping mapping, HttpServletRequest request) { this.password = null; } @Override public ActionErrors validate(ActionMapping mapping, HttpServletRequest request){ // TODO Auto-generated method stub return super.validate(mapping, request); } }
Il sistema di validazione dei dati viene attivato solo se nella pagina jsp viene essere usato un tag form previsto dalla libreria standard struts e gli input previsti dalla libreria standard del framework. Nel metodo presente nella classe ActionForm è possibile creare logiche di validazione come nell’esempio. Il form della pagina che invia dati:
<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html"%> ... <html:form action="/login" focus="userName"> <p>Username : <html:text property="userName" /></p> <p>Password : <html:password property="password" /></p> <p><html:submit value="login" /></p> </html:form>
La tag-lib di struts prevede tre grandi tag: html, bean e logic che verranno descritti in un articolo dedicato.
Il tag più usato è il bean:message per la visualizzazione delle label, questo viene usato perché il framework gestisce nativamente il concento di localizzazione e multilingua: i messaggi vengono censiti in file di proprietà, per esempio il file di default di solito si chiama Application.properties
e deve essere posizionato in un package dell’applicazione:
success.message=Benvenuto
E’ possibile creare un secondo file per una specifica lingua, per esempio un file dedicato ai messaggi in lingua inglese Application_en.properties
che deve essere posizionato nello stesso package del generale:
success.message=Welcome
Il bundle (cioè il gruppo di file di tipo properties) deve essere censito nel file di xml di configurazione con l’indicazione del package e del nome:
<message-resources parameter="it.alnao.Application" key="ApplicationBundle" />
Nella pagina jsp, per visualizzare i messaggi, bisogna prima importare la libreria e poi visualizzare i messaggi indicando il nome del bundle e il nome del messaggio:
<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %> ... <p><bean:message key="success.message" bundle="ApplicationBundle"/></p>
Per attivare la gestione del multi-lingua nel framework bisogna impostare il “locale” nella prima action chiamata:
request.getSession().setAttribute(Globals.LOCALE_KEY, request.getLocale());
con questa istruzione il framework riuscirà a recuperare la lingua del browser (della request) e selezionerà il messaggio corrispondente al file indicato nel file di configurazione. L’esempio completo funzionante può essere trovato al solito repository:
https://github.com/alnao/JavaExamples/tree/master/GenericJava/06Struts