Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Classi Action avanzate

Proseguiamo con i componenti Controller del framework di Struts analizzando le classi azione built-in che possono facilitare il design dell'applicazione
Proseguiamo con i componenti Controller del framework di Struts analizzando le classi azione built-in che possono facilitare il design dell'applicazione
Link copiato negli appunti

Solitamente è una cattiva idea inserire, all'interno di una pagina JSP, dei link diretti ad altre pagine JSP; infatti, il file di configurazione struts-config.xml, che fa parte del Controller, dovrebbe contenere l'intero flusso dell'applicazione.

Nell'architettura Model-View-Controller è compito del controller selezionare la prossima vista. Se si utilizza un link diretto, a un'altra pagina JSP, si stanno violando i confini dell'architettura Model 2 (ossia dell'MVC per applicazioni Web).

A volte, comunque, quello che si vuole è un plain link, ossia non si desidera l'esecuzione di un azione. In questo caso, l'approccio migliore è utilizzare ForwardAction.

ForwardAction agisce come bridge tra la vista corrente JSP e la pagina alla quale si collega. Utilizza il RequestDispatcher per effettuare un forward alla risorsa Web specificata. È il collante che permette di collegarsi a un'azione invece che direttamente a una pagina JSP.

Per utilizzare la ForwardAction, seguire gli step seguenti:

  • Utilizzare html:link con l'attributo action, aggiungere un link alla pagina JSP che punta all'azione.
  • Creazione di un action mapping nel file di configurazione di Struts che utilizza ForwardAction con l'attributo parameter che specifica il percorso alla JSP.

Supponiamo di avere una pagina JSP, coded.jsp, che ha un link diretto su un'altra pagina JSP:

Listato 1. Pagina JSP con link a un altra pagina JSP

<%@ page language="java" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<html>
    <body>
        <html:link page="/index.jsp">Home</html:link>
    </body>
</html>

Ci siamo di recente convertiti alla religione dell'architettura MVC/Model2 e vogliamo cambiare html:link tag su un'azione. È sufficiente produrre notcoded.jsp:

Listato 2. Pagina JSP con link a Home

<%@ page language="java" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<html>
    <body>
        <html:link action="home">Home</html:link>
    </body>
</html>

Infine bisogna aggiungere un action mapping all'azione home referenziata dal tag html:link:

Listato 3. Action Mapping all'azione

<action
  path="/home" type="org.apache.struts.actions.ForwardAction"
  parameter="/index.jsp">
</action>

Da una prospettiva di design, c'è un'ulteriore alternativa all'utilizzo dell'azione ForwardAction, anziché inserire un link a un azione o a una pagina, ci si potrebbe collegare a un global forward:

Listato 4. Pagina JSP collegata a un global forward

<%@ page language="java" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<html>
    <body>
        <html:link forward="home">Home</html:link>
    </body>
</html>

Per permettere ora al codice di funzionare dobbiamo aggiungere un home forward al file di configurazione di Struts:

Listato 5. Global Forward nel file di configurazione di Struts

<global-forwards>
  <forward name="home" path="/index.jsp" />
</global-forwards>

È possibile anche modificare il global forward in modo che punti a un action mapping:

Listato 6. Global forward che punta a un action mapping

<global-forwards>
  <forward name="home" path="/home.do" />
</global-forwards>

Questa, solitamente, è la pratica migliore, infatti, è più naturale collegarsi a forward che ad azioni.

Forward Attribute vs. ForwardAction

Struts permette le due dichiarazioni seguenti nel file struts-config.xml, ma la seconda è sicuramente più snella:

Listato 7. ForwardAction

<action
  path="/home"
  type="org.apache.struts.actions.ForwardAction"
  parameter="/index.jsp"
/>

Listato 8. Forward Attribute

<action
  path="/home"
  forward="/index.jsp"
/>

Forward per Accesso Legacy

Supponiamo di avere una risorsa Web legacy che si vuole utilizzare con la validazione form di Struts, purtroppo, la risorsa legacy
fa parte di un modello MVC elaborato che è stato creato prima dell'implementazione di Struts.

Per ragioni di semplicità la nostra risorsa legacy sarà una servlet. Essenzialmente si vuole fare in modo che il metodo doGet di una
servlet venga chiamato solo se l'ActionForm valida i dati con successo. Ecco la nostra servlet d'esempio:

Listato 9. Servlet di esempio

import javax.servlet.*;
import javax.servlet.http.*;

import org.apache.struts.action.*;
import org.apache.struts.*;

import java.io.*;

public class LegacyServlet
    extends HttpServlet {
    public void doGet (HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        ActionMapping mapping = (ActionMapping) request.getAttribute(Globals.MAPPING_KEY);
        UserForm form = (UserForm)request.getAttribute(mapping.getName());
        response.getWriter().println("User name: " + form.getUsername() + " Password: " + form.getPassword());
    }
}

e questo è lo UserForm.java

Listato 10. UserForm che passa UserName e password

import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import javax.servlet.http.HttpServletRequest;

public class UserForm extends ActionForm {

  private String username = null;
  private String password = null;

  public String getUsername() {
    return username;
  }

  public void setUsername(String username) {
    this.username = username;    
  }

  public String getPassword() {
    return password;
  }

  public void setPassword(String password) {
    this.password = password;    
  }

  public void reset(ActionMapping mapping, HttpServletRequest request) {
    this.username = null;
    this.password = null;
  }
}

Notiamo che la servlet può accedere al contesto che il Framework di Struts ha mappato su request.

Listato 11. File web.xml (Guarda il codice completo)

..//
    <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>
    
..//

Quindi, un post su /legacy/roar causerebbe l'esecuzione del metodo doGet della servlet.

Ora, per mappare questa servlet su un'azione che agisce come form handler, è necessario mettere mano a struts-config.xml:

Listato 12. Modifiche al 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="userForm" type="simone.UserForm" />
    </form-beans>

    <action-mappings>
        <action
          path="/legacy"
          forward="/legacy/roar"
          input="/userForm.jsp"
          name="userForm"
          parameter="/legacy/roar"
          validate="true"
          scope="request">
        </action>
    </action-mappings>  
</struts-config>

E infine ecco la JSP:

Listato 13. JSP con il form

<%@ page language="java" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<html>
<body>
<html: form action="/legacy">
  username: <html:text property="username"/><br/>
  password: <html:password property="password"/><br/>
  <html:submit/><html:cancel />
</html:form>
</body>
</html>

Il RequestDispatcher effettua la chiamata al metodo doGet() della Servlet solo se il metodo execute() della ForwardAction viene chiamato.

Avendo impostato il metodo validate su true nell'action mapping per la servlet, il metodo execute() di ForwardAction viene chiamato solo se ActionForm (UserForm) validates restituisce nessun oggetto ActionError.

IncludeAction

È simile a ForwardAction, la differenza sta nel fatto che si ha bisogno di utilizzare IncludeAction solo se l'azione dovrà essere inclusa in un'altra azione o JSP.

Listato 14. Un esempio di pagina JSP, incap.jsp

<%@ page language="java" %>
<html>
<body>
    Prova di inclusione di contenuto
    <jsp:include page="/NOTcoded.jsp" />  
</body>
</htnk>

Sistemiamo ora il file struts-config.xml con il nuovo elemento <action>

DispatchAction

Spesso le azioni sembrano essere troppo numerose e troppo piccole, sarebbe utile raggruppare azioni correlate in una classe facilitando il riutilizzo.

La classe org.apache.struts.actions.DispatchAction viene utilizzata per raggruppare azioni correlate in una classe. DispatchAction è una classe astratta che estende la classe Action.

Anziché che avere un singolo metodo execute, si ha un metodo per ciascuna azione logica. DispatchAction effettua un dispatch a una delle azioni logiche rappresentate dai metodi, scegliendo il metodo da invocare in base al valore di un parametro in ingresso.

Per utilizzare DispatchAction, eseguire gli step seguenti:

  • Creare una classe action handler che sottoclassi DispatchAction;
  • Creare un metodo che rappresenta ciascuna azione logica;
  • Creare un action-mapping per questo action handler.

Listato 15. Un esempio di DispatchAction

import javax.servlet.http.*;
import javax.servlet.*;
import org.apache.struts.actions.*;
import org.apache.struts.action.*;

public class UserDispatchAction
    extends DispatchAction {
    
    public ActionForward remove(
        ActionMapping mapping,
        ActionForm form,
        HttpServletRequest request,
        HttpServletResponse response)
        throws Exception {
        
        System.out.println("REMOVE USER!!!!");
        
        return mapping.findForward("success");
    }
    
    public ActionForward save(
        ActionMapping mapping,
        ActionForm form,
        HttpServletRequest request,
        HttpServletResponse response)
        throws Exception {
        
        System.out.println("SAVE USER!!!!");
        
        return mapping.findForward("success");
    }
}

Da notare che questi metodi hanno la stessa firma del metodo Action.execute().

Produciamo il bean che conterrà il valore di method, unica proprietà del form inviante:

Listato 16. Bean che contiene il valore di method

import javax.servlet.http.*;
import org.apache.struts.action.*;

public class UserForm1
  extends ActionForm {

  private String method = null;

  public String getMethod() {
    return method;  
  }

  public void setMethod(String method) {
    this.method = method;
  }

  public void reset(ActionMapping mapping, HttpServletRequest request) {
    this.method = method;
  }
}

Il terzo step è quello per creare un action mapping per questo action handler e aggiungendo un elemento <form-bean> nel struts-config.xml:

Listato 17. {descrizione}

<form-beans>
    <form-bean name="userForm1" type="fulvios.UserForm1" />
</form-beans>

<action
  path="/dispatchUserSubmit"
  type="fulvios.UserDispatchAction"
  parameter="method"
  input="/userForm1.jsp"
  name="userForm1"
  scope="request"
  validate="false">
    <forward name="success" path="/success.jsp" />
</action>

Ecco quindi lo step finale: creare la userForm1.jsp

Listato 18. Form JSP

<%@ page language="java" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>

<html>
<body>
<html:form action="/dispatchUserSubmit">
action:<html:select property="method" size="2">
  <html:option value="save">Save</html:option>
  <html:option value="remove">Remove</html:option>
</html:select><br/>
  
<html:submit/><html:cancel/>
</html:form>

Listato 19. Risultato finale

<%@ page language="java" %>
<html>
<body>
    success!!
</body>
</html>

LookupDispatchAction

La classe org.apache.struts.actions.LookupDispatchAction non è come la DispatchAction (che sottoclassa), infatti, effettua un reverse lookup sul resource boundle. È necessario implementare un metodo speciale che effettui un mapping delle chiavi delle risorse
ai metodi che si vogliono invocare.

Per utilizzare LookupDispatchAction è necessario eseguire i seguenti step:

  • Creare un action handler che sottoclassi LookupDispatchAction;
  • Creare un metodo che rappresenta ciascuna azione logica;
  • Implementare il metodo getKeyMethodMap per mappare le chiavi del file di risorsa ai nomi dei metodi;
  • Creare un action mapping per questo action handler utilizzando l'attributo parameter per specificare il parametro richiesta
    che contiene il nome del metodo che si vuole invocare;
  • Impostare i messaggi nel resource boundle per le etichette e i valori per i pulsanti;
  • Utilizzare bean:message per mostrare le etichette sul pulsante.

Il primo step è creare una classe action handler che sottoclassi LookupDispatchAction:

Listato 20. Handler che sottoclassa LookupDispatchAction (Guarda il codice completo)

..//
public class UserLookupDispatchAction
    extends LookupDispatchAction {
    
    // È necessario implementare questo metodo che mappi
    // le chiavi del file di risorse ai nomi dei metodi
    protected Map getKeyMethodMap() {
    
        Map map = new HashMap();
        map.put("userForm.remove", "remove");
        map.put("userForm.save", "save");
        
        return map;
    }
..//

Produciamo il bean che conterrà il valore di method, unica proprietà del form inviante:

Listato 21. Bean che contiene il valore di method

import javax.servlet.http.*;
import org.apache.struts.action.*;

public class UserForm2
    extends ActionForm {
    
    private String method = null;
    
    public String getMethod() {
        return method;  
    }
    
    public void setMethod(String method) {
        this.method = method;
    }
    
    public void reset(ActionMapping mapping, HttpServletRequest request) {
        this.method = method;
    }
}

Creiamo un action mapping (struts-config.xml):

Listato 22. Action Mapping

<?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="userForm2" type="simone.UserForm2" />
    </form-beans>
<action-mappings>
    <action
      path="/lookupDispatchUserSubmit"
      type="simone.UserLookupDispatchAction"
      input="/userForm2.jsp"
      name="userForm2"
      parameter="method"
      scope="request"
      validate="true">
        <forward name="success" path="/success.jsp" />
    </action>
</action-mappings>  
</struts-config>

Listato 23. Form JSP

<%@ page language="java" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>

<html>
<body>
<html:form action="/lookupDispatchUserSubmit">
  <html:submit property="method">
  <bean:message key="userForm.remove" />
  </html:submit>
  
  <html:submit property="method">
  <bean:message key="userForm.save" />
  </html:submit>

  <html:cancel>
  </html:cancel>
</html:form>
</body>
</html>

Listato 24. JSP con il risultato del form

<%@ page language="java" %>
<html>
<body>
  success!!
</body>
</html>

Infine il file di risorse:

Listato 25. Imposta le etichette e i valori per i pulsanti

ApplicationResources_it_IT.properties
userForm.save=Salva
userForm.remove=Rimuovi

ApplicationResources_en_US.properties
userForm.save=Save
userForm.remove=Remove

È importante mettere mano anche al file web.xml a causa dei file di risorse:

Listato 26. Modifica al file web.xml

<init-param>
    <param-name>application</param-name>
    <param-value>ApplicationResources</param-value>
</init-param>

Ti consigliamo anche