martedì 24 marzo 2015

Programmazione PL-SQL

Vai all'indice

Nel precedente articolo ho introdotto PL/SQL il linguaggio di programmazione proprietario di Oracle. Eseguire operazioni pesanti direttamente nel database tramite PL/SQL rende l'elaborazione dell'applicazione più leggera e stabile, tuttavia, nel caso le operazioni da eseguire nel DB non siano particolarmente onerose, conviene mantenere le query all'interno dell'applicazione, per facilità di manutenzione e per la portabilità verso altri datasource. Nel precedente articolo vi ho mostrato il funzionamento di una semplice stored procedure, tuttavia esistono altri costrutti utili ed interessanti.

Le function

Le function sono dei blocchi di codice che restituiscono un valore in output, di seguito un semplice esempio:

-- Nota: mai creare i propri oggetti nel SYS schema.
  
CREATE OR REPLACE FUNCTION UtentiTotali
RETURN number IS
   totali number(2) := 0;
BEGIN
   SELECT count(*) into totali
   FROM Utente;
   
   RETURN totali;
END;
/


La funzione UtentiTotali esegue un conteggio dei record all'interno della tabella Utente e lo memorizza nella variabile numerica totali, la quale viene restituita come risultato della funzione. Eseguo la funzione in SQLPLUS.
Definisco una variabile itot di tipo numerico,
SQL>var itot number;

inserisco il codice all'interno di un file e genero la function
SQL>@D:path/file.sql;

Eseguo la function assegnando il risultato alla variabile itot appena definita.
SQL>exec :itot := utentiTotali; 

Infine stampo a video il risultato.
SQL>print itot; 

I trigger

I trigger sono programmi che vengono eseguiti al verificarsi di un evento


  
CREATE OR REPLACE TRIGGER display_ruolo_changes
BEFORE DELETE OR INSERT OR UPDATE ON myuser.myUtente
FOR EACH ROW
WHEN (NEW.ID > 0)
DECLARE
BEGIN
   dbms_output.put_line('Old ruolo: ' || :OLD.ruolo);
   dbms_output.put_line('New ruolo: ' || :NEW.ruolo);
   dbms_output.put_line('il ruolo è stato modificato');
END;
/


Il Trigger esegue il blocco di codice definito all'interno delle istruzioni begin..end prima dell'esecuzione di operazioni di insert, delete o update nella tabella Utente. La clausula "for each row" indica che viene eseguito una volta per ogni riga interessata dall'evento. I riferimenti OLD e NEW indicano il valore del campo prima e dopo l'esecuzione dell'operazione di DML. Per testare il trigger eseguo l'istruzione:
SQL>@D:path/trigger.sql

L'operazione genera un messaggio di errore.
ERROR at line 1:
ORA-04089: cannot create triggers on objects owned by SYSERROR at line 1:

Nello specifico Oracle impedisce di generare i trigger tra gli oggetti di sistema. In realtà è buona norma definire un proprio schema in cui generare tutti i propri oggetti.

In quest'ottica genero un tablespace personale:
SQL> create tablespace mytabspace
2 datafile 'mytabspace.dat'
3 size 10m autoextend on;


Creo un'utenza personale a cui associo il tablespace appena creato.
SQL> create user myuser
2 identified by passwd
3 default tablespace mytabspace;


Associo all'utenza i permessi per la creazione di sessioni, tabelle, e trigger
SQL> grant create session to myuser;
SQL> grant create table to myuser;
SQL> grant create trigger to myuser;
SQL> grant unlimited tablespace to myuser;


Accedo all'utenza appena creata e genero gli oggetti di cui ho bisogno compreso il trigger che voglio testare.
SQL> conn myuser;
SQL> CREATE TABLE myUtente("ID" NUMBER(*,0) NOT NULL ENABLE, "NOME" VARCHAR2(30), "COGNOME" VARCHAR2(30), "RUOLO" VARCHAR2(30), PRIMARY KEY ("ID"));

SQL> insert into myUtente (ID, NOME, COGNOME, RUOLO) VALUES (1, 'FRANCO', 'NERI', 'CAPOUFFICIO');
SQL> insert into myUtente (ID, NOME, COGNOME, RUOLO) VALUES (2, 'MARIO', 'BIANCHI', 'IMPIEGATO');
SQL> insert into myUtente (ID, NOME, COGNOME, RUOLO) VALUES (3, 'MARCO', 'ROSSI', 'APPRENDISTA');


Rigenero il trigger dopo avere corretto il riferimento dalla tabella Utente a myUtente
SQL>@D:path/trigger.sql

Eseguo una Update sul ruolo di un singolo record ed ottengo il risultato atteso.
SQL> set serveroutput on;
SQL> update myUtente set ruolo = 'Impieganto' where id = 3;

Old ruolo: Apprendista
New ruolo: Impieganto

il ruolo è stato modificato.


I pacchage

I package raggruppano oggetti PL/SQL logicamente correlati come variabili e sottoprogrammi.
Un pacchetto ha due parti obbligatorie:
  • Package Specification
  • Package Body
Il Package Specification è la l'interfaccia del package. Essa dichiara i tipi, variabili, costanti, eccezioni, cursori e sottoprogrammi che possono essere referenziate al di fuori del pacchetto. Il altre parole contiene tutte le informazioni sul contenuto del pacchetto, ma esclude il codice per i sottoprogrammi.
Il Package Body contiene il codice dei vari metodi dichiarati nello specifico pacchetto e altre dichiarazioni private che non vengono mostrate all'esterno al pacchetto.

Riporto di seguito un esempio pratico.
Genero il Package Specification.
  
CREATE OR REPLACE PACKAGE c_package AS
   -- Aggiunta nuovo utente
   PROCEDURE addUtente(c_id   myutente.id%type,
   c_nome  myutente.nome%type,
   c_cognome  myutente.cognome%type,
   c_ruolo myutente.ruolo%type);
  
   -- Elimina utente
   PROCEDURE delUtente(c_id  myutente.id%TYPE);
   --Lista utenti
   PROCEDURE listaUtenti;

END c_package;
/


Genero il Package Body.
  
CREATE OR REPLACE PACKAGE BODY c_package AS
   PROCEDURE addUtente(c_id  myUtente.id%type,
      c_nome myUtente.nome%type,
      c_cognome  myUtente.cognome%type,
      c_ruolo  myUtente.ruolo%type)
   IS
   BEGIN
      INSERT INTO myUtente (id,nome,cognome,ruolo)
         VALUES(c_id, c_nome, c_cognome, c_ruolo);
   END addUtente;
  
   PROCEDURE delUtente(c_id   myUtente.id%type) IS
   BEGIN
       DELETE FROM myUtente
         WHERE id = c_id;
   END delUtente;

   PROCEDURE listaUtenti IS
   CURSOR c_utente is
      SELECT  nome FROM myUtente;
   TYPE c_list is TABLE OF myUtente.nome%type;
   name_list c_list := c_list();
   counter integer :=0;
   BEGIN
      FOR n IN c_utente LOOP
      counter := counter +1;
      name_list.extend;
      name_list(counter)  := n.nome;
      dbms_output.put_line('Utente(' ||counter|| ')'||name_list(counter));
      END LOOP;
   END listaUtenti;
END c_package;
/


Il blocco di codice che riporto di seguito utilizza i metodi dichiarati e definiti nel pacchetto c_package.
  
DECLARE
   code myUtente.id%type:= 8;
BEGIN
      c_package.addUtente(7, 'Maria', 'Viola', 'Impiegata');
      c_package.addUtente(8, 'Gianni', 'Verdi', 'Apprendita');
      c_package.listaUtenti;
      c_package.delUtente(code);
      c_package.listaUtenti;
END;
/


Per testare i programmi appena creati su SQLPLUS è sufficiente inserirli su dei file .sql e mandarli in esecuzione con le stesse modalità già viste in precedenza.

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

giovedì 19 marzo 2015

Oracle e PL-SQL

Vai all'indice

Finora negli esempi proposti ho utilizzato per comodità JavaDB (una versione di Apache Derby) un database leggero presente all’interno della JDK. Tuttavia sviluppando applicazioni "Enterprise" è opportuno utilizzare una base di dati più performante e completa. In quest'ottica Oracle può essere una delle scelte migliori. Dal sito della Oracle è possibile scaricare la versione 11g Express, l'edizione gratuita e più leggera del prodotto. Una volta eseguita l'istallazione è possibile dialogare con Oracle tramite la console operativa SQLPLUS semplicemente accedendo al prompt e digitando il comando:
sqlplus "/ as sysdba".
Uno degli aspetti più interessanti di Oracle è la possibilità di scrivere veri e propri programmi utilizzando PL/SQL un linguaggio di programmazione procedurale che mette a disposizione, oltre a tutti i costrutti dell'SQL, anche i controlli condizionali, i cicli iterativi, la gestione delle variabili e delle costanti, la gestione delle eccezioni, delle procedure, delle funzioni, ecc.
Un concetto fondamentale del PL/SQL è quello di blocco al cui interno è possibile combinare logicamente i vari comandi SQL nonché i vari costrutti "accessori" messi a disposizione dal PL/SQL. Di seguito riporto un esempio pratico di programma PL/SQL.

       
CREATE OR REPLACE PROCEDURE cursore
IS
BEGIN
DECLARE
   c_id utente.id%type;
   c_nome utente.nome%type;
   c_cognome utente.cognome%type;
   CURSOR c_utente is
      SELECT id, nome, cognome FROM utente;
BEGIN
   OPEN c_utente;
   LOOP
      FETCH c_utente into c_id, c_nome, c_cognome;
      EXIT WHEN c_utente%notfound;
      dbms_output.put_line(c_id || ' ' || c_nome || ' ' || c_cognome);
   END LOOP;
   CLOSE c_utente;
EXCEPTION
   WHEN no_data_found THEN
      dbms_output.put_line('Nessun utente trovato!');
   WHEN others THEN
      dbms_output.put_line('Errore generico!');
END;
END;
/


Il blocco di codice definisce un cursore ovvero un costrutto che permette di gestire le tuple restituite dall'esecuzione di una query. Il risultato finale è la stampa a video dei campi Id, Nome e Cognome della tabella Utente.
Nella sezione DECLARE definisco il cursore e le variabili con lo stesso tipo del campo definito in tabella Utente. Nel corpo della procedura apro il cursore e definisco un ciclo che estrae i campi direttamente nelle variabili definite in precedenza nella declare, quindi terminato il ciclo chiudo il cursore. Nella sezione Exception inserisco 2 codici predefiniti che intercettano e stampano a video un messaggio in caso di errori generici o di utente non trovato.
Per eseguire il programma appena descritto riapro SQLPLUS e creo la tabella Utente con la stessa struttura già vista diverse volte in questo blog.

  
-- Nota: mai creare i propri oggetti nel SYS schema.
SQL> CREATE TABLE Utente("ID" NUMBER(*,0) NOT NULL ENABLE, "NOME" VARCHAR2(30), "COGNOME" VARCHAR2(30), "RUOLO" VARCHAR2(30), PRIMARY KEY ("ID"));


Inserisco il codice del programma PL/SQL in un file di testo che chiamo cursore.sql. Col seguente comando eseguo la creazione della Stored Procedure.
SQL> @C:/pathdelfile/cursore.sql;
Se la creazione è andata a buon fine il processo restituisce il messaggio "Procedure created." , quindi eseguo il programma:
SQL> exec cursore();
Se il processo termina correttamente vedremo a video la lista degli utenti.
Hai apprezzato questo post? Conferma le mie competenze o scrivi una segnalazione sul mio profilo Linkedin!

martedì 10 marzo 2015

Gestire la transazionalità con Spring

Vai all'indice

In questo post approfondirò un aspetto di cui finora ho solo accennato, ma che ha una grande importanza nelle applicazioni J2EE, ovvero la gestione della transazionalità con Spring.
Una transazione è una sequenza di azioni svolte in un database che vengono trattate come una singola unità di lavoro . Queste azioni dovrebbero o essere interamente completate o non prevedere alcun effetto nel caso qualcosa vada storto. La gestione delle transazioni è una parte importante di un RDBMS orientato alle applicazioni enterprise per assicurare l’integrità e la coerenza dei dati . Il concetto di transazionalità può essere definito dalle seguenti quattro proprietà descritte dall’acronimo inglese ACID:


  • Atomicity: Una transazione deve essere trattata come una singola unità di lavoro quindi se una sola operazione dell'intera sequenza di operazioni della transazione non avrà esito positivo, allora tutta la transazione non andrà a buon fine. 
  • Consistency: Rappresenta la consistenza dell'integrità referenziale del database, chiavi primarie univoche nelle tabelle.
  • Isolation: Ci possono essere molte transazioni che elaborano gli stessi dati nello stesso momento, ogni transazione dove essere isolata dalle altre per prevenire la corruzione dei dati.
  • Durability: Una volta che una transazione è stata completata, i risultati di tale operazione devono essere resi permanenti e non possono essere cancellati dal database a causa di una anomalia.

Un database RDBMS deve garantire tutte le quattro proprietà per ogni transazione . Da un punto di vista pratico al termine di una transazione dovrà essere eseguita la commit per consolidare le operazioni, in caso contrario sarà eseguito il rollback su tutte le operazioni.
Il framework Spring fornisce un livello astratto al di sopra delle sottostanti API per la gestione delle transazioni.
 

Transazioni locali e transazioni globali


Le transazioni locali sono specifiche di una singola risorsa come ad esempio una connessione JDBC, mentre le operazioni globali possono occupare più risorse transazionali come ad esempio una transazione in un sistema distribuito .
La gestione delle transazioni locali può essere utile in un ambiente informatico centralizzato in cui le risorse delle applicazioni si trovano in un unico sito, e la gestione delle transazioni comporta solo un gestore di dati locali in esecuzione su una singola macchina.
La gestione globale delle transazioni è necessaria dove risorse sono distribuite su più sistemi. In tal caso, la gestione delle transazioni deve essere fatto sia a livello locale e globale. Pertanto una transazione globale distribuita viene eseguita su più sistemi, e la sua esecuzione richiede un coordinamento tra il sistema di gestione delle transazioni globali e tutti i gestori locali di dati di tutti i sistemi coinvolti.

Transaction management programmatica o dichiarativa.


Spring supporta due tipi di transaction management.
  1. Transaction management programmatica: significa gestire la transazione tramite la programmazione. Questo dà una estrema flessibilità, ma è difficile da mantenere.
  2. Transaction management dichiarativa: è l’approccio preferibile in quanto separa il transaction management dal codice. Questa metodologia è più semplice da gestire poiché con qualche configurazione XML e l’uso delle annotazioni si applica una funzionalità comune in tutto il codice.
Per utilizzare la gestione delle transazioni tramite le annotazioni è sufficiente aggiungere 3 bean nel file di configurazione xml :

  • <context:annotation-config/>: indica al framework Spring di collegare le annotazioni con le classi e con i metodi.
  • <tx:annotation-driven/>: Aggiunge automaticamente il supporto alle transazioni al fine includere il codice in ambito transazionale.
  • Inizializzare il bean DataSourceTransactionManager con i parametri di connessione.

Di seguito sono riportati i tag xml da inserire nel file di configurazione di Spring per abilitare l’annotazione @Transactional. 

<context:annotation-config/>
<!-- Questo tag abilita le annotations transactions -->
<tx:annotation-driven  transaction-manager="transactionManager"/>
<bean id="transactionManager"
  class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
  <property name="driverClassName" value="org.apache.derby.jdbc.ClientDriver"></property>
  <property name="url" value="jdbc:derby://localhost:1527/derby/derbyDB"></property>
  <property name="username" value="test"></property>
  <property name="password" value="test"></property>
</bean>


Uso dell’annotazione @Transactional
L’annotazione @Transactional a livello di classe include tutti i metodi di quella classe nell’ambito transazionale. L’annotazione @Transactional possiede proprietà come: readOnly, isolation, propagation, rollbackFor, noRollbackFor, utilizzate per controllare la transazione o comunicare con altre transazioni in corso. La proprietà readonly=true è utilizzata per indicare che un metodo verrà utilizzato esclusivamente per eseguire query di selezione, mentre impostando readonly=false potremo eseguire anche operazioni come: update, insert e delete. Di default readonly è impostato a false. Altra proprietà interessante è rollbackFor e noRollbackFor.Di default Spring esegue il RollBack di una transazione per le eccezioni di tipo RuntimeException o di eccezioni non controllate.
Altre proprietà interessanti sono Isolation, che indica la misura in cui una transazione è isolata dal lavoro di altre transazioni, e Propagation che specifica il comportamento nel caso in cui un metodo transazionale viene eseguito quando esiste già un contesto di transazione.


Conclusioni
La transazionalità è un tema molto importante e grazie a Spring è possibile gestirla con un alto livello di astrazione. Questo post dà una panoramica generale su questo tema, tuttavia gli aspetti da approfondire sono molteplici. La documentazione dettagliata è disponibile qui.

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

mercoledì 25 febbraio 2015

Implementazione di una relazione uno-a-molti

Vai all'indice

Il prossimo passo nella costruzione della webapp Prototype è la gestione degli Uffici e l'integrazione con la classe Utente. La struttura delle entità è la stessa proposta nel post Hibernate relazione uno a molti, nel quale si esemplifica la relazione uno a molti tra due tabelle di un database. Nella specifica relazione utenti/uffici ogni utente appartiene ad un solo ufficio, mentre ad ogni ufficio possono appartenere molti utenti.
Per la gestione delle operazioni di inserimento, cancellazione e modifica creo una nuova pagina ufficio.xhtml a cui associo il backing bean ufficiBean. La logica di sviluppo è del tutto simile a quella già descritta per la gestione degli utenti, ma molto più semplice, in quanto l'elaborazione è limitata alla gestione di due campi. Per questo motivo non mi soffermo oltre su questo aspetto, tuttavia chi è interessato può trovare tutto il codice su GitHub.



       <h:selectOneMenu id="ufficio" value="#{utenti.seluff}" effect="fold">
             <f:selectItems value="#{utenti.ufficio1Value}"/>
       </h:selectOneMenu>

Il managed bean utentiBean subisce sostanziali modifiche per la gestione del nuovo campo. Innanzitutto definisco un oggetto Map a cui assegno il nome ufficio1List e nel quale carico i dati da inserire nell'oggetto selectOneMenu. Nel codice seguente riporto il metodo per il caricamento della mappa partendo dall'estrazione della tabella Ufficio:

 public Map ufficio1List = new HashMap();
     public Map getUfficio1Value() {
        int indi=0;
        for (Ufficio uff : uffix) {
             ufficio1List.put(uff.getNomeUfficio(), Integer.toString(uff.getUfficioId()));
             indi = indi + 1;
        }
     return ufficio1List;
  }

Nei metodi addAction e onEdit, che rispondono rispettivamente agli eventi di caricamento e modifica dei dati degli utenti, definisco il collegamento tra gli oggetti Utente e Ufficio.

Ufficio uff = ufficioDao.getUfficio(Integer.parseInt(seluff));

Nell'entità di appoggio utenteBean, che raggruppa i dati estratti dalle tabelle Utente e DettaglioUtente per il caricamento dell'oggetto datatable, definisco il nuovo campo nomeUff in cui salvo la denominazione dell'ufficio da esporre come output al momento del caricamento.

La WebApp aggiornata è scaricabile da GitHub.

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

martedì 3 febbraio 2015

Approfondimenti sulla validazione degli utenti

Vai all'indice

Nel post precedente vi ho esposto come ho definito il controllo accessi per l'applicazione "prototype". Nei prossimi paragrafi vi fornirò alcuni dettagli sulle modifiche all'applicazione utili alla gestione degli utenti loggati. A conclusione dell'articolo mi soffermerò sulle regole di navigazione in JSF.
In testa alla pagina index inserisco un nuovo oggetto toolbar in cui riporto lo username dell'utente loggato e i due pulsanti di logoff e cambio password.







<p:toolbar>
        <f:facet name="left"> 
           <h:outputText value="Utente attivo: #{loginBean.uname}" style="color:#2779BD;"/>
        </f:facet>
        <f:facet name="right">
           <p:commandButton action="#{utenti.changePass}" value="Cambio password"  ajax="false">    
           </p:commandButton>
           <p:commandButton action="#{loginBean.logout}" value="Logout"  ajax="false">     
           </p:commandButton>  
        </f:facet>
</p:toolbar>

La pagina changepass riverifica le credenziali di accesso e richiede un doppio inserimento della nuova password. Una funzione di controllo verifica che i due valori  inseriti coincidano ed esegue un test sulla sicurezza della nuova password.



<p:toolbar>
            <h:form id="loginForm">          
            <p:growl id="msg" showDetail="true" life="3000" />
            <p:panel header="Cambio password" style="width: 360px;">
                <h:panelGrid id="loginPanel" columns="2">
                    <h:outputText value="Username" />
                    <p:inputText id="username" value="#{cambioBean.uname}" ></p:inputText>
                    <h:outputText value="Password" />
                    <p:password id="password" value="#{cambioBean.password}" feedback="false">
                    </p:password>
                    <p:spacer></p:spacer>
                    <p:spacer></p:spacer> 
                   
                    <h:outputLabel for="pwd1" value="Nuova password: " />
                    <p:password id="pwd1" value="#{cambioBean.password1}" match="pwd2" label="Password 1" feedback="true" required="true" />

                    <h:outputLabel for="pwd2" value="Riperti password: " />
                    <p:password id="pwd2" value="#{cambioBean.password2}" label="Password 2" feedback="true" required="true" />
                    <p:spacer></p:spacer>
                    <p:spacer></p:spacer>                   
                    <p:commandButton action="#{cambioBean.modiPass}" value="Cambio password" update="loginForm" ajax="true"></p:commandButton>
                </h:panelGrid> 
            </p:panel>
        </h:form>


Nel backing-bean cambioPass.java inserisco il controllo delle credenziali di accesso, la codifica e l'aggiornamento del valore della nuova password nel database.

    public String modiPass() {
        
        boolean trovato = utenteDao.login(uname, password);
        if (trovato) {
        Utente result = utenteDao.estrlogin(uname, password);
            String hashed = "";
            try {hashed = hspass.createHash(password2);}
            catch(Exception ex) {System.out.println("Anomalia conversione password");}
        result.setPassword(hashed);
            utenteDao.aggUte(result);
            return "index";
        } else {
            FacesContext.getCurrentInstance().addMessage(
                    null,
                    new FacesMessage(FacesMessage.SEVERITY_WARN,
                    "Credenziali di accesso non valide!",
                    "Riprova di nuovo!"));

            return "changepass";
        }
    }


Approfondimento sulle regole di navigazione in JSF.


Il framework JSF fornisce le navigation-rules che permettono di definire la navigazione da una vista all'altra in un'applicazione web. Le regole di navigazione sono definite nel file di configurazione solitamente denominato faces-config.xml, tuttavia è possibile assegnare un altro nome specificando le direttive nel file web.xml.

 <navigation-rule>
        <from-view-id>/login.jsf</from-view-id>
        <navigation-case>
            <from-outcome>index</from-outcome>
            <to-view-id>/index.jsf</to-view-id>
        <redirect>     
        </redirect>
        </navigation-case>
 </navigation-rule>


Il codice specifica che la pagina login verrà redirezionata verso la pagina index.jsf

  <navigation-rule> 
    <from-view-id>/login.jsp</from-view-id> 
    <navigation-case> 
            <from-outcome>index</from-outcome>
            <to-view-id>/index.jsf</to-view-id>
    </navigation-case> 
    <navigation-case> 
            <from-outcome>goodbye</from-outcome>
            <to-view-id>/goodbye.jsf</to-view-id>
    </navigation-case> 
  </navigation-rule> 


Il codice specidica che la pagina login.jsf ha due output associati a due pagine specifiche.

Il modello di navigazione JSF ci permette di utilizzare dei pattern ovvero dei percorsi che terminano con un "*", in questo caso si vuole applicare una regola a tutte le pagine contenute nella cartella pages.

  <navigation-rule> 
    <from-view-id>/pages/*</from-view-id> 
    <navigation-case> 
            <from-outcome>index</from-outcome>
            <to-view-id>/index.jsf</to-view-id>
    </navigation-case> 
    <navigation-case> 
            <from-outcome>goodbye</from-outcome>
            <to-view-id>/goodbye.jsf</to-view-id>
    </navigation-case> 
  </navigation-rule>   


Questa regola di navigazione verrà applicata per ogni pagina contenuta nel percorso /pages/.

Di seguito un esempio di modello di navigazione JSF più articolato.

  <navigation-rule> 
    <from-view-id>/pages/*</from-view-id> 
    <navigation-case> 
            <from-outcome>goodbye</from-outcome>
            <to-view-id>/goodbye.jsf</to-view-id>
    </navigation-case> 
  </navigation-rule> 
  <navigation-rule> 
    <from-view-id>/pages/login.jsf</from-view-id> 
    <navigation-case> 
            <from-outcome>index</from-outcome>
            <to-view-id>/index.jsf</to-view-id>
    </navigation-case>  
  </navigation-rule> 


In questo caso abbiamo due regole di navigazione: la prima generica per tutte le pagine contenute nel percorso /pages/, la seconda diversa per la pagina login.jsf. In questo caso la regola specifica ha la precedenza sulla regola riferita al path generico.

La WebApp aggiornata è scaricabile da GitHub.

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

mercoledì 28 gennaio 2015

Autenticazione di accesso alla WebApp

Vai all'indice

In questo post vi illustrerò come ho inserito l'autenticazione degli accessi all'applicazione presentata nell'articolo precedente. La logica proposta è la stessa già vista nell'esempio: Autenticazione sicura con Primefaces.

Gli utenti abilitati all'ingresso sono gli stessi gestiti nell'applicazione, quindi utilizzerò la tabella Utente per salvare le credenziali di accesso per la login. Nello specifico ricaverò lo username concatenando i campi Cognome e Nome, mentre per la gestione della password inserirò un nuovo campo nella tabella Utente.

Riporto di seguito l'istruzione SQL per la modifica della tabella utente.

ALTER TABLE utente add COLUMN password VARCHAR(255);


Inoltre l'istruzione seguente inizializza il campo password col valore cifrato 'testtest00'.

update utente set password='1000:c5cca4907214bb9fb4d9e978bb970f63875350f3456ea492:49685e1f51d4cdebb3e3c8127ae05264fac87b50d473ea2b';

Per la protezione del campo password utilizzerò un meccanismo di cifratura che che mi permetterà di "cammuffare" l'informazione. Questi metodi sono detti funzioni hash o cifratura a senso unico e sono utilizzati per la cifratura delle password. E' buona norma non memorizzare le password in chiaro sul sistema, ma soltanto la loro cifratura. Quando un utente vuole accedere al sistema, il meccanismo di autenticazione richiede la password, la cifra con l'algoritmo di hashing considerato e confronta la password cifrata con quella memorizzata sul sistema per l'utente in questione. Se le due password cifrate coincidono, all'utente è permesso accedere al sistema.

Dall'esempio Autenticazione sicura con Primefaces importo i file necessari per l'implementazione del controllo accessi.

  • Il file login.xhtml contiene il form per l'autenticazione e viene impostato come welcome-page dell'applicazione, 
  • al bean associato loginBean aggiungo l'interrogazione delle chiavi di accesso nel database e la crittografia per rendere sicure le password degli utenti, 
  • la classe util inserita nel pacchetto it.prototype.utils contiene i metodi HttpSession e HttpServletRequest, 
  • la classe AuthFilter contiene il filtro di autenticazione e va inserito nel pacchetto it.Filters. 

Modifico il file web.xml indicando login.xhtml come nuova welcome-page, mentre nel file faces-config.xml inserisco il tag navigation-rule che definisce le regole di navigazione.
Aggiungo la nuova classe hashpass che contiene l'algoritmo di hashing per la crittografia della password.



Nel prossimo post vedremo come implementare la gestione degli uffici a cui appartengono gli utenti e delle applicazioni a cui essi sono abilitati.

La WebApp aggiornata con il controllo accessi è scaricabile da GitHub.

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

giovedì 22 gennaio 2015

WebApp con Spring Hibernate e Primefaces

Vai all'indice

L'obiettivo di questo post è la realizzazione di un'architettura enterprise e multidatasource costruita integrando i framework Spring, Hibernate e Primefaces. La web application proposta in questo esempio gestisce i dati contenuti nel database Apache Derby già utilizzato in precedenza di cui riporto di seguito la struttura.



Per fare questo riprenderò i concetti visti nei post Introduzione a Spring con esempio pratico e Inserire, cancellare modificare righe su Datatable Primefaces. Nel primo esempio vi ho mostrato come mappare il modello dati e costruire le classi per la gestione delle informazioni provenienti dal database grazie all'utilizzo di Spring e Hibernate, mentre nel secondo abbiamo visto la gestione grafica dell'interfaccia utente con Primefaces.

La costruzione della WebApp


Per prima cosa creo una nuova WebApp mantenendo l'interfaccia già vista nell' esempio precedente, pertanto, dopo avere definito un nuovo progetto Maven, importo la pagina index.xhtml, i bean associati, i file web.xml e faces-context.xml di configurazione.

Dall'esempio Introduzione a Spring con esempio pratico importo i pacchetti contenenti le entità e le classi DAO. Inoltre inserisco il file applicationContext.xml di Spring nel percorso WebContent > WEB-INF.

A questo punto, dopo l'importazione dei moduli di nostro interesse dai due progetti, è necessario integrare la parte di gestione dati con l'interfaccia grafica, riporto di seguito le modifiche salienti ai singoli moduli.
Come prima cosa aggiungo nel web.xml i listener di Spring.

  <context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>
      /WEB-INF/applicationContext.xml
      </param-value>
  </context-param>
  <listener>
  <listener-class>
   org.springframework.web.context.ContextLoaderListener
  </listener-class>
   </listener>
   <listener>
  <listener-class>
   org.springframework.web.context.request.RequestContextListener
  </listener-class>
   </listener>

La classe ContextLoaderListener è un ServletContextListener, che viene chiamato all’avvio del Tomcat, per inizializzare e caricare il context di Spring nell'applicazione, mentre RequestContextListener serve a Spring per dichiarare lo scope associato al bean.
Grazie al parametro contextConfigLocation è possibile indicare il percorso del file di configurazione del contesto di Spring.

   <application>
     <el-resolver>
      org.springframework.web.jsf.el.SpringBeanFacesELResolver
     </el-resolver>
   </application>

Sia JSF che Spring gestiscono un proprio IoC Container, per cui è necessario trovare un modo per farli comunicare, ed è Spring che se ne occupa. Inserendo nel faces-context.xml di JSF la definizione della classe el-resolver che delega a Spring tutte le espressioni EL che JSF non riesce a risolvere, tra cui gli stessi bean definiti nel container di Spring.

  <dependency>
     <groupId>log4j</groupId>
     <artifactId>log4j</artifactId>
     <version>1.2.16</version>
  </dependency>
  <dependency>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-api</artifactId>
     <version>1.5.6</version>
  </dependency>
  <dependency>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-log4j12</artifactId>
     <version>1.5.6</version>
  </dependency>


Nel pom.xml inserisco tutte le dipendenze di Spring, Hibernate e Primefaces già viste nei due esempi sopra citati. Inoltre ho aggiunto la dipendenza c3p0 per la gestione del pool di connessione e le dipendenze slf4j e log4j per inserire nel progetto la gestione di un sistema di logging.

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

    private static final long serialVersionUID = 1L;
    public int userid;
    public String nome;
    public String cognome;
    public String ruolo;
    private Date data;
    public String via;
    public String citta;
    public String telefono;
    public List<Utente> users;
utenteBean utente;
    
    @ManagedProperty(value="#{utenteDao}")
    UtenteDao utenteDao;
    
    // Definizione Getter e Setter

    private static final ArrayList<utenteBean> utentiList = new ArrayList<utenteBean>();

    public ArrayList<utenteBean> getUtentiList() {
        return utentiList;
    }
    
    @PostConstruct
    public void init() {
   users = utenteDao.getAll();
   inizializza();
   initdata();
}

    public String addAction() {
   
     // Istanzio e salvo gli oggetti utente e dettaglioutente
     Dettaglioutente dettUte = new Dettaglioutente(this.data, this.via, this.citta, this.telefono);
     Utente ute = new Utente(this.nome, this.cognome, this.ruolo);
     utenteDao.saveDetUte(ute, dettUte);
   
     inizializza();

     nome = "";
     cognome = "";
     ruolo = "";
     via = "";
     citta = "";
     telefono = "";
     initdata();
     return null;
    }
    
    public void onEdit(RowEditEvent event) { 
        Dettaglioutente dettUte = new Dettaglioutente(((utenteBean) event.getObject()).getUserid(), ((utenteBean) event.getObject()).getData(), ((utenteBean) event.getObject()).getVia(), ((utenteBean) event.getObject()).getCitta(), ((utenteBean) event.getObject()).getTelefono());
        Utente ute = new Utente(((utenteBean) event.getObject()).getUserid(), ((utenteBean) event.getObject()).getNome(), ((utenteBean) event.getObject()).getCognome(), ((utenteBean) event.getObject()).getRuolo());  
        // Aggiorno l'utente
        utenteDao.aggUte(ute, dettUte);
        inizializza();
        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){ 
// Elimina l'utente
        utenteDao.deleteUtente(std.getUserid());
        inizializza();
FacesMessage msg = new FacesMessage("Record cancellato");   
        FacesContext.getCurrentInstance().addMessage(null, msg);
}

public void inizializza(){
        utentiList.clear();
   List<Utente> users = utenteDao.getAll();
for (Utente user : users) {
Dettaglioutente dettuser = utenteDao.getDettaglioutente(user.getuserId());
utenteBean utentetmp = new utenteBean(user.getuserId(), user.getNome(), user.getCognome(), user.getRuolo(), dettuser.getDataNascita(), dettuser.getVia(), dettuser.getCitta(), dettuser.getTelefono());
       utentiList.add(utentetmp);
}
}

public void initdata(){
    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("Anomalia parser data " + sdf); 
    }
}



UtentiBean è la classe di gestione della pagina index.xhtml ed è il modulo chiave su cui agire per collegare la parte di presentazione con la gestione dei dati.
L'annotazione @managedbean è usata per configurare l’iniezione di dipendenza in Spring ed è associata alla classe UtenteDao che contiene i metodi di interfaccia per l'accesso alle tabelle utente.
@PostContruct è l'annotazione applicata al metodo init() per indicare che deve essere chiamato dopo che tutte le dependency injection sono state completate. Nel nostro caso specifico è utilizzata per caricare i campi del Datatable coi dati estratti dal database.
Le chiamate ai metodi della gestione dei dati vengono attivate tramite gli eventi di inserimento, cancellazione e modifica dei dati presenti nel Datatable di Primefaces.
A questo punto la stesura del codice è terminata ed è possibile testare l'applicazione.



L'interfaccia grafica gestisce i dati relativi agli utenti ed è formata da un'unica pagina web divisa in due schede. La scheda inferiore contiene il modulo di inserimento nuovo utente, mentre la scheda superiore contiene un oggetto Datatable che permette la visualizzazione, l'aggiornamento e la cancellazione dei dati inseriti. Poichè a livello di Database i dati relativi agli utenti sono divisi in 2 tabelle (utente e dettaglioutente tra cui esiste una relazione uno a uno) ma l'oggetto datatable è unico, per comodità ho mappato i dati nel POJO UtenteBean prima di caricarli nel Datatable.

Nei prossimi post vedremo come implementare e completare il progetto appena iniziato.

Anche questo esempio è scaricabile da GitHub.

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