domenica 28 dicembre 2014

Inserire modificare cancellare righe su Datatable Primefaces

Vai all'indice

Nel post Primefaces esempio introduttivo vi ho presentato l'oggetto Datatable di Primefaces. Come abbiamo visto lo strumento DataTable espone i dati in forma tabellare ed in modo versatile e interattivo. Inoltre, come si può vedere dallo Showcase di Primefaces, è possibile implementare interessanti caratteristiche come: filtri, ordine e gruppi secondo colonne specifiche, tabelle annidate, paginazione e molto altro. In questo post dimostrerò come inserire, modificare e cancellare righe su un oggetto Datatable contenente i dati di una lista di utenti. Non riporterò i file di configurazione pom.xml, web.xml e faces-config.xml poichè sono gli stessi già visti nell'esempio precedente.

La pagina index.xhtml contiene un form necessario per l'inserimento dei dati relativi ai singoli utenti, ed un oggetto Datatable dove vengono gestiti i dati degli utenti una volta inseriti.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      >
    <h:head>
        <title>Gestione utenti</title>
    </h:head>
    <h:body>
        <h:form id="form1">
            <p:growl id="messages" showDetail="true"/>
            <p:panel header="Gestione utenti" style="width: 800px;"> 
                <p:dataTable value="#{utenti.utentiList}" var="o"  widgetVar="50" style="width: 760px;" editable="true">
                    <f:facet name="header">  
                        Lista utenti  
                    </f:facet>
                    <p:ajax event="rowEdit" listener="#{utenti.onEdit}" update=":form1:messages" />  
                    <p:ajax event="rowEditCancel" listener="#{utenti.onCancel}" update=":form1:messages" /> 
                   <p:column>
                        <f:facet name="header">  
                            <h:outputText value="Nome utente" />  
                        </f:facet>
                        <p:cellEditor>  
                            <f:facet name="output">  
                                <h:outputText value="#{o.nome}" />  
                            </f:facet>  
                            <f:facet name="input">  
                                <p:inputText value="#{o.nome}" style="width:100%"/>  
                            </f:facet>  
                        </p:cellEditor> 
                    </p:column>

                    <p:column>
                        <f:facet name="header">  
                            <h:outputText value="Ruolo" />  
                        </f:facet>
                        <p:cellEditor>  
                            <f:facet name="output">  
                                <h:outputText value="#{o.ruolo}" />  
                            </f:facet>  
                            <f:facet name="input">  
                                <p:inputText value="#{o.ruolo}" style="width:100%"/>  
                            </f:facet>  
                        </p:cellEditor>
                    </p:column>
                     <p:column>
                        <f:facet name="header">  
                            <h:outputText value="Data di nascita" />  
                        </f:facet>
                        <p:cellEditor>  
                            <f:facet name="output">  
                                <h:outputText value="#{o.data}" >  
                                <f:convertDateTime type="date" pattern="dd-MM-yyyy" timeZone="Europe/Berlin" />
                                </h:outputText>
                            </f:facet>  
                            <f:facet name="input">  
                                <p:inputText value="#{o.data}" style="width:100%"> 
                                  <f:convertDateTime type="date" pattern="dd-MM-yyyy" timeZone="Europe/Berlin" />
                                </p:inputText>
                            </f:facet>  
                        </p:cellEditor>
                    </p:column>  
                    <p:column>
                        <f:facet name="header">  
                            <h:outputText value="Via" />  
                        </f:facet>
                        <p:cellEditor>  
                            <f:facet name="output">  
                                <h:outputText value="#{o.via}" />  
                            </f:facet>  
                            <f:facet name="input">  
                                <p:inputText value="#{o.via}" style="width:100%"/>  
                            </f:facet>  
                        </p:cellEditor>
                    </p:column> 
                    <p:column>
                        <f:facet name="header">  
                            <h:outputText value="Città" />  
                        </f:facet>
                        <p:cellEditor>  
                            <f:facet name="output">  
                                <h:outputText value="#{o.citta}" />  
                            </f:facet>  
                            <f:facet name="input">  
                                <p:inputText value="#{o.citta}" style="width:100%"/>  
                            </f:facet>  
                        </p:cellEditor>
                    </p:column>
                    <p:column>
                        <f:facet name="header">  
                            <h:outputText value="Telefono" />  
                        </f:facet>
                        <p:cellEditor>  
                            <f:facet name="output">  
                                <h:outputText value="#{o.telefono}" />  
                            </f:facet>  
                            <f:facet name="input">  
                                <p:inputText value="#{o.telefono}" style="width:100%"/>  
                            </f:facet>  
                        </p:cellEditor>
                    </p:column>                           
                    <p:column headerText="Gest" style="width:50px">  
                        <p:rowEditor />  
                                        
                        <h:commandLink  styleClass="ui-icon ui-icon-trash" action="#{utenti.delete(o)}"/>
                    </p:column> 
                </p:dataTable>
                <p:spacer height="30px;"/>
                <p:panelGrid columns="2">
                   <f:facet name="header">  
                        Nuovo utente
                    </f:facet>
                    <h:outputLabel for="nome" value="Cognome Nome: " />
                    <p:inputText  value="#{utenti.nome}"/>
                    <h:outputLabel for="ruolo" value="Ruolo: " />
                    <p:inputText value="#{utenti.ruolo}"/>
                    <h:outputLabel for="data" value="Data nascita: " />
                    <p:calendar id="popup" value="#{utenti.data}" />
                    <h:outputLabel for="via" value="Via: " />
                    <p:inputText  value="#{utenti.via}"/>
                    <h:outputLabel for="citta" value="citta: " />
                    <p:inputText  value="#{utenti.citta}"/>
                    <h:outputLabel for="telefono" value="Telefono: " />
                    <p:inputMask value="#{utenti.telefono}" mask="999-9999999"/>      
                    <f:facet name="footer">
                        <h:commandButton value="Aggiungi utente" action="#{utenti.addAction}"/>
                    </f:facet>
                </p:panelGrid>                
            </p:panel>
        </h:form>
    </h:body>
</html>

Il Datatable presentato in questo esempio permette di modificare i dati direttamente "inline" tramite un comando Ajax. Cliccando l'icona a forma di matita in fondo alla riga i campi diventano editabili. L'icona "check" permette di consolidare le modifiche effettuate sui dati, mentre l'icona a forma di "x" consente di uscire dalla modalità di "editing" senza modificare i campi. E' possibile cancellare un singolo record cliccando l'icona "trash" in fondo alla riga. Il risultato è rappresentato dall'immagine seguente:









Il backing bean "utentiBean" contiene la definizione delle variabili ed i metodi per l'inserimento, cancellazione e modifica dei dati acquisiti dalla pagina index.

@ManagedBean(name = "utenti")
@SessionScoped
public class utentiBean implements Serializable {

    private static final long serialVersionUID = 1L;
    public String nome;
    public String ruolo;
    private Date data;
    public String via;
    public String citta;
    public String telefono;
    utenteBean utente;


    public String getNome() {
       return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    public String getRuolo() {
        return ruolo;
    }

    public void setRuolo(String ruolo) {
        this.ruolo = ruolo;
    }

    public Date getData() {
        return data;
    }

    public void setData(Date data) {
        this.data = data;
    }

    public String getVia() {
        return via;
    }

    public void setVia(String via) {
        this.via = via;
    }

    public String getCitta() {
        return citta;
    }

    public void setCitta(String citta) {
        this.citta = citta;
    }

    public String getTelefono() {
        return telefono;
    }

    public void setTelefono(String telefono) {
        this.telefono = telefono;
    }

    public utenteBean getUtente() {
        return utente;
    }

    public void setUtente(utenteBean utente) {
        this.utente = utente;
    }
    private static final ArrayList<utenteBean> utentiList = new ArrayList<utenteBean>();

    public ArrayList<utenteBean> getUtentiList() {
        return utentiList;
    }

    public String addAction() {
        utenteBean utentetmp = new utenteBean(this.nome, this.ruolo, this.data, this.via, this.citta, this.telefono);
        utentiList.add(utentetmp);

        nome = "";
        ruolo = "";
        String dateStr = "1970-01-01T00:00:00.000+01:00";
        SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSSZ"); 
        try  {
        data = sdf.parse(dateStr.replaceAll(":(?=..$)", "")); 
        } catch (ParseException e) { 
                System.out.println("Unparseable using " + sdf); 
        }
        via = "";
        citta = "";
        telefono = "";
        return null;
    }
    public void onEdit(RowEditEvent event) {  
        FacesMessage msg = new FacesMessage("Record modificato",((utenteBean) event.getObject()).getNome());  
        FacesContext.getCurrentInstance().addMessage(null, msg);  
    }  
       
    public void onCancel(RowEditEvent event) {  
        FacesMessage msg = new FacesMessage("Modifiche annullate");   
        FacesContext.getCurrentInstance().addMessage(null, msg); 
    }  
    public void delete(utenteBean std){
utentiList.remove(std);
FacesMessage msg = new FacesMessage("Record cancellato");   
        FacesContext.getCurrentInstance().addMessage(null, msg);
    }
}


I dati degli utenti sono salvati in un'istanza Arraylist della classe "UtenteBean" di cui riporto la struttura di seguito.

public class utenteBean implements Serializable {

    private static final long serialVersionUID = 1L;
    private String nome;
    private String ruolo;
    private Date data;
    private String via;
    private String citta;
    private String telefono;

  public utenteBean(String nome, String ruolo, Date data, String via, String citta, String telefono) {
        this.nome = nome;
        this.ruolo = ruolo;
        this.data = data;
        this.via = via;
        this.citta = citta;
        this.telefono = telefono;
 }

public String getNome() {
return nome;
}

public void setNome(String nome) {
this.nome = nome;
}

public String getRuolo() {
return ruolo;
}

public void setRuolo(String ruolo) {
this.ruolo = ruolo;
}

public Date getData() {
return data;
}

public void setData(Date data) {
this.data = data;
}

public String getVia() {
return via;
}

public void setVia(String via) {
this.via = via;
}

public String getCitta() {
return citta;
}

public void setCitta(String citta) {
this.citta = citta;
}

public String getTelefono() {
return telefono;
}

public void setTelefono(String telefono) {
this.telefono = telefono;
}    
    
}


Questo esempio ci dimostra la potenza dello strumento Datatable di Primefaces nella gestione di dati tabellari. Il prossimo passo sarà di utilizzare nel Datatable i dati provenienti da un database, inoltre grazie all'integrazione con Spring e Hibernate renderemo la nostra una vera e propria applicazione enterprise.

Anche questo esempio è scaricabile da GitHub.

Hai apprezzato questo post? Conferma le mie competenze o scrivi una segnalazione sul mio profilo Linkedin!

domenica 7 dicembre 2014

Autenticazione sicura con Primefaces

Vai all'indice

L'obiettivo di questo post è creare una pagina di autenticazione sicura con Primefaces. Per fare questo userò la libreria javax.servlet.Filter che protegge le risorse all'interno di un determinato percorso dell'applicazione.
La pagina login.xhtml acquisisce le chiavi di accesso, se le credenziali sono corrette si accede alle pagine home.xhtml ed esempio.xhtml. Se tentiamo di accedere senza autenticazione ad una di queste due pagine, il filtro ci rimanda alla pagina di login.

La costruzione della webapp, del file pom.xml e web.xml è la stessa già vista nel post Primefaces esempio introduttivo, mentre cambia la struttura del faces-config che riporto di seguito:

<?xml version='1.0' encoding='UTF-8'?>
<faces-config version="2.0"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">
    <navigation-rule>
        <from-view-id>/login.jsf</from-view-id>
        <navigation-case>
            <from-outcome>home</from-outcome>
            <to-view-id>/home.jsf</to-view-id>
        <redirect>     
        </redirect>
        </navigation-case>
 </navigation-rule>
</faces-config>

Di seguito riporto la pagina di accesso login.xhtml ed il bean associato.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"  
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui">
    <h:head>
        <title>Pagina di login</title>
    </h:head>
    <h:body>
        <h:form id="loginForm">          
            <p:growl id="msg" showDetail="true" life="3000" />
            <p:panel header="Login" style="width: 360px;">
                <h:panelGrid id="loginPanel" columns="2">
                    <h:outputText value="Username" />
                    <p:inputText id="username" value="#{loginBean.uname}" ></p:inputText>
                    <p:spacer></p:spacer> 
                    <p:message for="username" ></p:message>
                    <h:outputText value="Password" />
                    <p:password id="password" value="#{loginBean.password}"  feedback="false"></p:password>
                    <p:spacer></p:spacer>
                    <p:message for="password"></p:message>
                    <p:spacer></p:spacer>
                    <p:commandButton action="#{loginBean.loginProject}" value="Login" update="loginForm" ajax="true"></p:commandButton>
                </h:panelGrid> 
            </p:panel>
        </h:form>
    </h:body> 
</html>

package it.Beans;

import it.Dao.UserDAO;
import java.io.Serializable;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpSession;

@ManagedBean(name = "loginBean")
@SessionScoped

public class LoginBean implements Serializable {

    private static final long serialVersionUID = 1L;
    private String password;
    private String message, uname;

    public String loginProject() {
        boolean result = UserDAO.login(uname, password);
        if (result) {
            // Richiama la sessione Http e salva username
            HttpSession session = Util.getSession();
            session.setAttribute("username", uname);

            return "home";
        } else {

            FacesContext.getCurrentInstance().addMessage(
                    null,
                    new FacesMessage(FacesMessage.SEVERITY_WARN,
                    "Credenziali di accesso non valide!",
                    "Riprova di nuovo!"));

            // manda un messaggio di sessione non valida

            return "login";
        }
    }

    public String logout() {
      HttpSession session = Util.getSession();
      session.invalidate();
      return "login";
     }

     // metodi getter e setter
}


La classe LoginBean verifica che le credenziali inserite siano corrette, in tal caso richiama la sessione Http e salva lo username nella sessione corrente. In caso di credenziali errate visualizza un messaggio di errore.

Riporto di seguito le pagine visualizzate dopo la login e protette dal filtro.

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE HTML>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui">
    <h:head>
        <title>Homepage</title>
    </h:head>
    <h:body>
        <h:form>       
            <h1>Benvenuto, #{loginBean.uname}</h1>
            <p>questa è la home page del sito.</p>
            <p:link outcome="esempio" value="Vai alla pagina di esempio"></p:link><br/><br/>
        <p:commandButton action="#{loginBean.logout}" value="Logout"  ajax="false"></p:commandButton>
    </h:form>
    </h:body>
</html>


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE HTML>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui">
    <h:head>
        <title>Pagina di esempio</title>
    </h:head>
    <h:body>
        <h:form>       
            <h1>Benvenuto, #{loginBean.uname}</h1> 
            <p>questa è una pagina di esempio.</p>
            
            <p:link outcome="home" value="Vai alla homepage"></p:link><br/><br/>
        <p:commandButton action="#{loginBean.logout}" value="Logout"  ajax="false"></p:commandButton>
    </h:form>
    </h:body>
</html>


package it.Dao;
  
public class UserDAO {      
     public static boolean login(String user, String password) {

    if (user.equals("admin") && password.equals("test")) 
            {
               return true;
            }
            else {
                return false;
            }
    }
}

La classe UserDAO simula la chiamata al database per recuperare le chiavi di accesso.

package it.Beans;

import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

public class Util {
      public static HttpSession getSession() {
        return (HttpSession)
          FacesContext.
          getCurrentInstance().
          getExternalContext().
          getSession(false);
      }
       
      public static HttpServletRequest getRequest() {
       return (HttpServletRequest) FacesContext.
          getCurrentInstance().
          getExternalContext().getRequest();
      }
}

La classe Util definisce i metodi HttpSession e HttpServletRequest.
HttpServletRequest oltre a permettere l’accesso a tutte le informazioni relative allo header http, permette di ricavare i parametri passati insieme all’invocazione del client.
HttpSession permette di memorizzare le informazioni provenienti dal client all’interno di una sessione salvata sul server al quale viene affidato un identificativo di sessione.

package it.Filters;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@WebFilter(filterName = "AuthFilter", urlPatterns = {"*.jsf"})
public class AuthFilter implements Filter {

    public AuthFilter() {
    }
    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {   
        try {

            // verifica se la variabile sessione è impostata
            HttpServletRequest req = (HttpServletRequest) request;
            HttpServletResponse res = (HttpServletResponse) response;
            res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
            res.setHeader("Pragma", "no-cache"); // HTTP 1.0.
            res.setDateHeader("Expires", 0); // Proxies.
            HttpSession ses = req.getSession(false);
            //  permette all'utente di procedere se ci sono i presupposti
            String reqURI = req.getRequestURI();
            if (reqURI.indexOf("/login.jsf") >= 0 || (ses != null && ses.getAttribute("username") != null)
                    || reqURI.indexOf("/public/") >= 0 || reqURI.contains("javax.faces.resource")) {
                chain.doFilter(request, response);
            } else // utente non loggato ma tenta di accedere ad una pagina non permessa viene rimandato alla pagina di login
            {
                res.sendRedirect(req.getContextPath() + "/login.jsf");  // L'utente anonimo viene rimandato alla pagina di login
            }
        } catch (Throwable t) {
            System.out.println(t.getMessage());
        }    
    
    }
    
    @Override
    public void destroy() {
    }
}

La classe AuthFilter permette all'utente di procedere se ci sono i presupposti e se le credenziali sono corrette, se utente non loggato tenta di accedere alle pagine protette viene rimandato alla pagina di login.
I filtri sono delle classi di utilità molto importanti in quanto consentono di creare un flusso di operazioni particolari senza modificare la struttura principale del nostro software.
Come dimostrato dal nostro esempio anch'essi possono beneficiare delle annotation (vedi @WebFilter), liberandoci dalla necessità di configurare un apposito descrittore nel file web.xml.

Il progetto è ora completo. Lanciando l'applicazione verrà visualizzata la pagina di login:


Anche questo esempio è scaricabile da GitHub.

Hai apprezzato questo post? Conferma le mie competenze o scrivi una segnalazione sul mio profilo Linkedin!