Pubblicato il 16/09/2023 da alnao nella categoria Java & Spring Boot

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
MENU