MokaByte 83 - Marzo 2004 
Sviluppare applicazioni J2EE con Jakarta Struts
III parte: il controller
di
Alfredo Larotonda
Nei precedenti articoli della serie abbiamo introdotto il concetto di framework e descritto i componenti fondamentali e l'architettura di Struts. Ora iniziamo ad entrare nel dettaglio del framework descrivendo i componenti di Struts che ne costiuiscono il controller.

La ActionServlet
La org.apache.struts.action.ActionServlet è la servlet di controllo di Struts. Come già accennato nel precedente articolo è la servlet che gestisce tutte le richieste http che provengono dai client e indirizza il flusso applicativo in base alla configurazione presente nel file XML struts-config.xml.
Come è ovvio la ActionServlet estende la javax.servlet.http.HttpServlet; i suoi metodi doGet() e doPost() chiamano entrambi un metodo process() che esegue quindi l'elaborazione sia in caso di richieste di tipo GET che di tipo POST. Di seguito è riportato il metodo doGet(), il doPost() è identico:

public void doGet(HttpServletRequest request, HttpServletresponse response)
                 throws IOException,ServletException {
  process(request,response);
}

La ActionServlet esegue l'elaborazione che schematicamente comprende i seguenti step:

  1. I metodi doGet() e doPost() invocano il metodo process() della ActionServlet
  2. Nel metodo process() la ActionServlet ottiene l'istanza del RequestProcessor, configurato per l'applicazione nel tag <controller> dello struts.config.xml, e ne esegue il metodo process().
  3. Nel metodo process() del RequestProcessor viene eseguita l'elaborazione vera e propria, ed in output al metodo viene fornito un oggetto ActionForward che consente alla ActionServlet di inoltrare l'elaborazione in base alla configurazione presente nello struts-config.xml.

La ActionServlet viene configurata, come ogni servlet, nel web.xml. I parametri di inizializzazione sono molti e per un elenco completo si rimanda al sito ufficiale di Struts (http://jakarta.apache.org/struts/). Di seguito è riportato un blocco <servlet> di configurazione standard della ActionServlet nel quale è settato ad 1 il parametro <load-on-startup> in base al quale il container istanzia la ActionServlet allo start-up della web-application e ne invoca il metodo init(). Inoltre mediante il parametro config è specificata la posizione del file XML di configurazione dell'applicazione. Il parametro debug abilita il debugging dell'applicazione.
Mediante il blocco <servlet-mapping> si specifica che tutte le richieste con path terminante in .do vengono mappate sulla servlet di controllo di Struts:

<!-Configurazione standard della Action Servlet -->
<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>
<init-param>
<param-name>debug</param-name>
<param-value>2</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>


<!- Mapping della Action Servlet -->
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>

Con questa configurazione di base è già possibile utilizzare la ActionServlet come servlet di controllo della propia applicazione.

 

Il Request Processor
Come visto al punto 2) del precedente paragrafo, la org.apache.struts.action.RequestProcessor è la classe alla quale la ActionServlet delega l'elaborazione delle richieste.
Il RequestProcessor viene configurato mediante il tag <controller> dello struts-config.xml , ed è possibile utilizzarne uno proprio scrivendo una classe che estende la org.apache.struts.action.RequestProcessor e ne implementa i metodi. In particolare è di uso comune fare l'override del metodo processPreprocess() che viene eseguito dal RequestProcessor prima dell'elaborazione di ogni richiesta. Questo metodo è il punto ottimale per inserire controlli di validità della sessione, dell'utente o simili.

Il RequestProcessor esegue i seguenti step:

  1. Legge il file struts-config.xml per trovate un un elemento XML <action> corrispondente al path della richiesta.
  2. Una volta trovato l'elemento <action> corrispondente verifica se è presente l'attributo name che corrisponde al nome dell'ActionForm configurato per la richiesta in elaborazione. In tal caso provvede a reperire una istanza dell'ActionForm e a popolarne gli attributi con i valori presenti nella request http, facendo una corrispondenza tra nome parametro e nome attributo.
  3. Se nell'elemento <action> è presente l'attributo validate al valore true chiama il metodo validate() dell'ActionForm per il controllo dei dati forniti dalla request.
  4. Se il controllo è ok a questo punto il RequestProcessor chiama il metodo execute() dell'Action configurata nell'elemento <action> delegandole l'elaborazione della richiesta.
  5. Il metodo execute() dell'Action al termine dell'elaborazione restituisce un oggetto ActionForward che consente al RequestProcessor di inoltrare il flusso elaborativo.

Come visto quindi la ActionServlet delega l'elaborazione della richiesta al RequestProcessor che a sua volta , dopo aver popolato con i dati della request l'ActionForm configurato nell'elemento <action> corrispondente al path della richiesta, delega l'elaborazione della singola richiesta alla classe Action corrispondente. Un esempio di configurazione di una Action nel file struts-config.xml è il seguente:

<!-definizione del ActionForm -->
<form-beans>
<form-bean name="startForm" type="it.prove..MenuForm" />
</form-beans>

<action-mappings>
<!-definizione del Action -->
<action path="/start"
name="startForm"
scope="request"
type="it.prove.StartAction"
validate="true">
<forward name="ok" path="/pagina1.jsp"/>
<forward name="ko" path="/errorpage.jsp"/>
</action>
</action-mappings>

In questo esempio al path /start.do viene associato il form it.prove.StartForm e la Action it.prove.StartAction.
Ciò significa che quando il RequestProcessor riceverà una richiesta con path /start.do valorizzerà gli attributi di un oggetto della classe it.prove.StartForm con i parametri della request e dopo averne validato i valori la passerà al metodo execute() della classe it.prove.StartAction che esegue l'elaborazione prevista.

 

La classe Action
Dopo aver esaminato la ActionServlet ed il RequestProcessor finalmente arriviamo a parlare della classe org.apache.struts.action.Action. La classe Action è l'elemento fondamentale del controller di Struts in quanto per ogni funzione realizzata con Struts bisogna creare una propria classe che la estende e ne implementa il metodo execute() che è fatto come segue:

public ActionForward execute(ActionMapping mapping, ActionForm form,
                     HttpServletRequest request, HttpServletResponse response)
                     throws Exception{
  //codice di esempio
  //acquisizione form
  MyForm myForm = (MyForm)form;
  //acquisizione parametri dal form
  String param1 = myForm.getParam1();

  //business logic
  ...
  //fine business logic

  //inoltro dell'elaborazione
  return mapping.findForward("ok");
}

Il metodo execute() riceve in input request e response http, un'istanza dell'oggetto ActionForm prima descritto, e un oggetto ActionMapping che contiene le infornazioni configurate nell'elemento <action> tra le quali i forward, ovvero i percorsi a cui inoltrare in uscita l'elaborazione. Restituisce un oggetto ActionForward che contiene il path di inoltro dell'elaborazione.
E' nel metodo execute() della propria Action che lo sviluppatore inserisce il proprio codice di elaborazione della richiesta per la funzione specifica.
Bisogna subito sottolineare due aspetti fondamentali riguardo alle Action:

  1. Le Action vengono gestite esattamente come delle servlet. Ovvero il loro funzionamento è basato sulla stessa logica multithread delle servlet quindi sono soggette a tutti i problemi comunemente noti nelle servlet. Il codice scritto nelle Action deve essere thread-safe per un corretto funzionamento delle stesse.
  2. Le Action fanno parte del Controller e non del Model. La logica applicativa non deve essere scritta nella Action, ma questa deve delegare allo strato di Model l'elaborazione della business-logic.

In base a quanto detto una Action dovrebbe:

  1. Acquisire i dati della request dal form
  2. Delegare l'elaborazione della business-logic alle classi del Model
  3. Acquisire i risultati dell'elaborazione e prepararli per la vista da inviare all'utente mettendoli nello scope opportuno (se necessario).
  4. Inoltrare il flusso elaborativo in base alla logica applicativa.

Le Action costiuiscono quindi il 'ponte' applicativo tra lo strato di Controller e di Model di un aapplicazione scritta con Struts ed hanno un ruolo fondamentale perché sono le classi che lo sviluppatore scrive continuamente nello sviluppo di una applicazione Struts.

 

La classe ActionForm
Infine concludiamo parlando della classe org.apache.struts.action.ActionForm. Abbiamo già visto che i form (come sono chiamate nella terminologia Struts le classi che estendono la org.apache.struts.action.ActionForm) sono sostanzialmente dei bean contenenti dati, che il framework popola automaticamente con i dati della request svincolando lo sviluppatore dal doverlo fare con proprio codice applicativo. Associando ad un path un oggetto di una classe che estende la org.apache.struts.action.ActionForm, definito mediante il tag <form-bean>, è possibile fornire al metodo execute() della Action un oggetto contenente tutti i dati inseriti in un FORM HTML, con la possibilità di validarli prima che gli stessi giungano alla Action stessa come visto in precedenza. Ritorneremo comunque a parlare dei form quando analizzeremo lo strato di View di Struts.

 

Conclusioni
In questo articolo abbiamo esaminato gli elementi fondamentali del Controller di Struts per dare un'idea di come venga elaborata una richiesta dalle classi del framework. E' ovvio che le classi citate sono molto più complesse ed articolate di quanto detto quindi per un esame più approfondito del loro codice si consiglia di scaricare i sorgenti dal sito di Struts. Lo scopo dell'articolo era più che altro di illustrare gli step principali eseguiti dal controller di Struts ad ogni richiesta per consentire una migliore comprensione del suo funzionamento.

 

Bibliografia
[1] Chuck Cavaness - "Programming Jakarta Struts", O'Reilly, 2003
[2] Rod Johnson - "J2EE Design and Development", Wrox, 2002
[3] James Goodwill, Richard Hightower - Professional Jakarta Struts" - Wrox 2004


Alfredo Larotonda, laureato in Ingegneria Elettronica, lavora da diversi anni nel settore IT. Dal 1999 si occupa di Java ed in particolare dello sviluppo di applicazioni web J2EE. Dopo diverse esperienze di disegno e sviluppo ora si occupa in particolare di aspetti architetturali per progetti rivolti al mercato finanziario ed industriale. E' Web Component Developer certificato SUN per la piattaforma J2EE e Programmer per la piattaforma Java.

MokaByte® è un marchio registrato da MokaByte s.r.l. 
Java®, Jini® e tutti i nomi derivati sono marchi registrati da Sun Microsystems.
Tutti i diritti riservati. E' vietata la riproduzione anche parziale.
Per comunicazioni inviare una mail a info@mokabyte.it