In questo articolo parliamo dei 4 componenti fondamentali che compongono il Controller Struts:
- la classe ActionServlet
- la classe Action
- i Plugin
- il RequestProcesser
La classe ActionServlet
La classe org.apache.struts.ActionServlet rappresenta la pietra miliare di tutte le applicazioni Struts. Si tratta del componente controller principale che gestisce le richieste client e determina quale org.apache.struts.action.Action processerà la richiesta arrivata.
Funziona come un Action Factory, creando istanze di classi Action in base alle richieste ricevute.
La ActionServlet è una servlet che, come tutte le altre servlet, estende la classe javax.servlet.http.HttpServlet e quindi implementa tutti i metodi di lifecycle, incluso init()
, doGet()
, doPost()
e destroy
.
I due punti di ingresso per l'ActionServlet sono essenzialmente gli stessi delle altre servlet: doGet()
e doPost()
.
Listato 1. Sorgenti per doGet() e doPost()
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
process (request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
process (request, response);
}
Da notare che l'implementazione di questi metodi fa esattamente la stessa cosa, ossia chiamare il metodo process()
.
Quando l'ActionServlet riceve una richiesta, completa i seguenti step:
- I metodo
doPost()
odoGet()
ricevono una richiesta e invocano il metodoprocess()
. - Il metodo
process()
ottiene il corrente RequestProcessor e ne invoca poi il metodoprocess()
. Se si intende estendere l'ActionServlet, il posto per la personalizzazione è l'oggetto RequestProcessor. Contiene la logica che il controller Struts esegue su ciascuna richiesta. - Il metodo
RequestProcessor.process()
è il luogo dove la richiesta viene effettivamente servita. Questo metodo reperisce, dal file struts-config.xml, l'elemento <action> che è in match con il path submitted della richiesta. - Quando il metodo
RequestProcessor.process()
trova un <action> in match, va alla ricerca dell'entry <form-bean> referenziato dall'elemento <action>. - Quando il metodo
RequestProcessor.process()
conosce il fully qualified name del FormBean, crea o reperisce un'istanza dell'ActionForm nominata dall'elemento <form-bean> e popola i membri di istanza con i valori che arrivano dalla richiesta. - Dopo che i dati dell'Action Form sono stati caricati, il metodo
RequestProcessor.process()
chiama il metodoActionForm.validate()
, che controlla la validità dei valori passati. - Il metodo
RequestProcessor.process()
conosce tutto ciò che gli serve e può servire la richiesta. Reperisce il fully qualified name della classe Action e chiama il metodoexecute()
. - Quando la classe azione ritorna dal suo processing, il suo metodo
execute()
ritorna un oggetto ActionForward che viene utilizzato per determinare il target della transazione. Il metodoRequestProcessor.process()
riacquisisce il controllo e la richiesta viene indirizzata al target. - A questo punto l'ActionServlet ha completato il suo processing per questa richiesta ed è pronto per servirne delle altre.
Configurazione dell'ActionServlet
L'ActionServlet è configurata utilizzando il deployment descriptor web.xml attraverso l'elemento <servlet>.
Una pratica comune consiste nell'utilizzare l'elemento <load-on-startup> per essere certi che l'ActionServlet venga avviata quando il container avvia l'applicazione Web.
Listato 2. un esempio di <servlet> entry che descrive ActionServlet
<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>
<param-name>debug</param-name>
<param-value>4</param-value>
<init-param></init-param>
<load-on-startup>1</load-on-startup>
</servlet>
Utilizzo dei moduli di Struts
Struts introduce il concetto di modulo che permette ai team di sviluppatori di segmentare logicamente le loro applicazioni Struts.
Configurazione dei moduli di Struts
È estremamente semplice configurare i moduli di Struts. In questo esempio, segmentiamo un pezzo amministrativo di un'applicazione Struts:
- Creare una directory che descrive il nuovo modulo. Per questo esempio, creare una directory nominata admin che verrà utilizzata per le funzionalità amministrative dell'applicazione.
- Copiare tutte le JSP relative e i componenti visti in questa directory.
- Creare un nuovo file struts-config.xml che descrive i componenti Struts di questo nuovo segmento e copiarlo nella WEB-INF directory della nostra applicazione (es: struts-config-admin.xml).
- Nel nuovo file di struts creato, aggiungere il prefisso del nostro Modulo a tutte le risorse. Se per esempio si referenzia una JSP come userAdmin.jsp, è necessario modificare il reference in /admin/userAdmin.jsp.
- Aggiungere un nuovo servlet <init-param> al file web.xml che descriva il nuovo modulo; ecco di seguito il codice:
Listato 3. servlet <init-param> da aggiungere al file web.xml
<init-param>
<param-name>config/admin</param-name>
<param-value>/WEB-INF/struts-config-admin.xml</param-value>
</init-param>
La classe Action
Il componente più comune di un Controller Struts è la classe org.apache.struts.action.Action che rappresenta una funzione dell'applicazione.
Per sviluppare le proprie classi Action, è necessario completare gli step seguenti:
- Creare una classe che estende org.apache.struts.action.Action;
- Implementare il metodo
execute()
appropriato e aggiungere la specifica logica di business; - Compilare la nuova Action e spostarla nel classpath dell'applicazione Web;
- Aggiungere un elemento
<action>
al file struts-config.xml all'applicazione che descrive la nuova azione.
Estensione della classe Action
Listato 4. Esempio di dichiarazione
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
public class EsempioAction
extends Action {
// Di seguito implementiamo la nostra Action
}
Il metodo execute()
rappresenta l'inizio della logica dell'applicazione. È il metodo su cui bisogna effettuare l'override quando si definiscono le azioni Struts. Il metodo execute
ha 2 funzioni:
- Esegue la logica di business user-defined dell'applicazione;
- Dice al Framework il percorso successivo della richiesta.
Il Framework di Struts definisce due metodi execute()
. Il primo viene utilizzato quando si definiscono azioni custom che non sono specificatamente HTTP.
Questa implementazione del metodo è analoga alla classe javax.http.servlet.GenericServlet; e la sua signature è la seguente:
public ActionForward execute(
ActionMapping mapping,
ActionForm form,
ServletRequest request,
ServletResponse response
) throws IOException, ServletException
L'implementazione del secondo metodo viene utilizzato quando si stanno definendo azioni HTTP specifiche. Ecco la sua signature:
public ActionForward execute(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response
) throws IOException, ServletException
- ActionMapping: contiene tutte le informazioni di deployment per un particolare Action bean;
- ActionForm: rappresenta gli input del Form contenente i parametri della richiesta;
- HttpServletRequest: è un riferimento all'oggetto HTTP Request;
- HttpServletResponse: è un riferimento all'oggetto HTTP Response
Dopo aver esaminato i parametri passati al metodo execute()
, bisogna dare un'occhiata al suo tipo di ritorno.
Questo oggetto viene utilizzato dal RequestProcessor per determinare dove la richiesta andrà poi, se è una JSP o un'altra azione.
Listato 5. esempio di execute()
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
public class EsempioAction extends Action {
if (form!=null) {
// 1. Creazione del form
SkeletonForm actionForm = (SkeletonForm) form;
// 2. Aggiunta del Business Logic
// 3. return con un appropriato ActionForward
return mapping.findForward("success");
}
}
Dopo aver costruito gli oggetti Action, notiamo che fanno sempre più o meno gli stessi step:
- Effettuare un cast dell'ActionForm referenziato;
- Aggiungere la specifica logica di business;
- Utilizzare il metodo
ActionMapping.findForward()
per trovare l'oggetto ActionForward che effettua un match con il sottoelemento <forward> nella definizione di <action>; - Restituire l'oggetto ActionForward reperito.
Configurazione della classe Action
La classe Action è un oggetto specifico di Struts e quindi bisogna mettere mano al file struts-config.xml.
L'elemento che viene utilizzato per descrivere un'azione Struts è un elemento <action>.
La classe che definisce gli attributi dell'elemento <action> è org.apache.struts.action.ActionMapping. Vedremo in seguito come potrà essere estesa per definire attributi <action> addizionali.
Plugin
I Plugin di Struts sono estensioni modulari del controller di Struts. Introdotti con Struts 1.1, sono definiti dall'interfaccia org.apache.struts.action.Plugin.
I Plugin di Struts sono utili quando si allocano risorse o si preparano connessioni a database o su risorse JNDI.
Questa interfaccia definisce due metodi che sono init()
e destroy()
che sono i metodi di lifecycle di un Plugin di Struts.
init()
Il metodo init()
di un Plugin di Struts viene chiamato quando il container di JSP/Servlet avvia l'applicazione Web contenente il Plugin. Ha la firma seguente:
public void init(ActionServlet servlet, ApplicationConfig applicationConfig)
throws javax.servlet.ServletException;
Questo metodo viene utilizzato per caricare e inizializzare risorse che sono richieste dal Plugin. Da notare che il metodo init()
riceve un riferimento all'ActionServlet e all'ApplicationConfig quando viene invocato.
Il riferimento ad ActionServlet permette di referenziare qualunque informazione del Controller mentre l'oggetto ApplicationConfig fornisce l'accesso alle informazioni di configurazione che descrivono l'applicazione Struts.
Questo metodo rappresenta l'inizio della vita del plugin.
destroy()
Il metodo destroy()
di un Plugin di Struts viene chiamato ogni volta che il container di JSP/Servlet arresta l'applicazione Web contenente il Plugin. Ha la seguente signature:
public void destroy();
Questo metodo è utile quando si stanno reclamando o chiudendo risorse che sono state allocate dal metodo Plugin.init()
. Questo metodo termina la vita del Plugin.
Creazione di un Plugin
Ecco gli step di creazione di un Plugin riassunti:
- Creazione di una classe che rappresenta l'interfaccia org.apache.struts.action.Plugin
- Aggiunta di un costruttore di default vuoto
- Implementazione dei metodi
init()
edestroy()
- Compilazione del Plugin e spostamento nel classpath dell'applicazione Web
- Aggiunta dell'elemento <plug-in> al file struts-config.xml
Listato 6. Implementazione d'esempio di un plugin (Vedi codice completo)
..//
public void init(
ActionServlet servlet,
ModuleConfig applicationConfig
) throws javax.servlet.ServletException {
System.out.println("---->The Plugin is starting<----");
Properties properties = new Properties();
try {
File file = new File("c:/info/startup.info");
// Creazione dell' input stream
FileInputStream fis = new FileInputStream(file);
// load della properties
properties.load(fis);
ServletContext context = servlet.getServletContext();
..//
L'obiettivo di questo Plugin è quello di rendere disponibili un set di properties allo startup dell'applicazione.
Gli step sono:
- Compilare e spostare la classe del Plugin nella cartella esempio;
- Aggiungere l'elemento <plug-in> al file di configurazione struts-config.xml;
- Riavvio dell'applicazione.
Ecco tutto il file struts-config.xml:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
<struts-config>
<form-beans>
<form-bean name="lookupForm" type="esempio.LookupForm" />
</form-beans>
<action-mappings>
<action path="/Lookup" type="esempio.LookupAction" name="lookupForm" >
<forward name="success" path="/quote.jsp" />
<forward name="failure" path="/index.jsp" />
</action>
</action-mappings>
<plug-in className="esempio.EsempioPlugin" />
</struts-config>
Il RequestProcessor
Il RequestProcessor è la classe da riscrivere quando si vuole personalizzare il processing dell'ActionServlet.
Essa contiene un entry point predefinito che viene invocato dal controller di Struts con ciascuna richiesta. Questo entry point si chiama processPreprocess()
.
Se si desidera aggiungere il proprio processing specializzato al Controller è necessario implementare il metodo processPreprocess()
, aggiungendo la specifica logica e restituendo true per continuare con il processing normale. Se si desidera terminare il processing normale, restituire false per dire al controller che la richiesta corrente è completa.
Listato 7. Implementazione di default processPreprocess()
protected boolean processPreprocess(HttpServletRequest request, HttpServletResponse response) {
return true;
}
Per creare il proprio RequestProcessor è necessario seguire gli step seguenti:
- Creare una classe che estende org.apache.struts.action.RequestProcessor;
- Aggiungere un costruttore di default vuoto;
- Implementare il metodo
processPreprocess()
Ecco di seguito il codice:
Listato 8. Codice per creare il RequestProcessor
import javax.servlet.http.*;
import javax.servlet.*;
import java.io.*;
import java.util.*;
import org.apache.struts.action.RequestProcessor;
public class EsempioRequestProcessor
extends RequestProcessor {
public EsempioRequestProcessor() {
}
public boolean processPreprocess(HttpServletRequest request,
HttpServletResponse response) {
System.out.println("-------------processPreprocess Logging-------------");
System.out.println("RequestedURI = " + request.getRequestURI());
System.out.println("Protocol = " + request.getProtocol());
System.out.println("Remote Address = " + request.getRemoteAddr());
System.out.println("Remote Host = " + request.getRemoteHost());
System.out.println("Remote User = " + request.getRemoteUser());
System.out.println("Requested Session Id = " + request.getRequestedSessionId());
return true;
}
}
Nel metodo processPreprocess()
reperiamo le informazioni memorizzate in request ed effettuiamo un log.
Una volta che il log è completato, il metodo processPreprocess
ritorna il valore boolean true e il processing normale prosegue.
Se il metodo processPreprocess avesse restituito false, il Controller avrebbe terminato il normale processing e l'azione non sarebbe mai stata eseguita.
Per effettuare il deploy del nostro RequestProcessor dobbiamo:
- Compilare il nostro RequestProcessor e metterlo nel classpath dell'applicazione.
- Aggiungere l'elemento <controller> al file di configurazione dell'applicazione struts-config.xml.
Ecco il contenuto totale del file struts-config.xml:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
<struts-config>
<form-beans>
<form-bean name="lookupForm" type="esempio.LookupForm" />
</form-beans>
<action-mappings>
<action path="/Lookup" type="fulvios.LookupAction" name="lookupForm" >
<forward name="success" path="/quote.jsp" />
<forward name="failure" path="/index.jsp" />
</action>
</action-mappings>
<controller processorClass="esempio.EsempioRequestProcessor" />
<plug-in className="esempio.EsempioPlugin" />
</struts-config>