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

Progetto: un web responder con Struts 2 e Hibernate

Tramite la costruzione di un semplice web responder passo passo avremo modo di configurare opportunamente un ambiente di lavoro java EE che utilizzi sia hibernate che struts 2 (e i relativi bean).
Tramite la costruzione di un semplice web responder passo passo avremo modo di configurare opportunamente un ambiente di lavoro java EE che utilizzi sia hibernate che struts 2 (e i relativi bean).
Link copiato negli appunti

L'obiettivo di questo articolo è la realizzazione di un progetto che utilizzi Struts 2 e Hibernate: implementeremo passo dopo passo un web responder, ovvero una applicazione web che consenta agli utente l'attivazione del proprio profilo utente.

L'applicazione web responder

L'applicazione presenta una pagina di login con informazioni tipo:

  1. Nome utente
  2. Cognome
  3. Codice Fiscale
  4. E-mail

L'applicazione quindi "raccoglie" queste informazioni e, se l'utente non è presente nel database di riferimento (nel nostro caso è il database Utenti già realizzato ed utilizzato per l'articolo "Hibernate per principianti", con in più il campo codice fiscale) invierà una e-mail con un link da cliccare che permetterà l'inserimento nell'utente nel database.

>> Leggi l'introduzione a Hibernate per principianti

L’applicazione è al solito volutamente semplice, però ci permetterà di affrontare diverse questioni molto interessanti tra cui:

  1. Alter della tabella Utenti inserendo il campo codice fiscale con nuovo reverse engineer
  2. Trattazione del framework struts 2
  3. Integrazione struts 2 - Hibernate

Di materiale da trattare quindi ce ne sarà parecchio.

Iniziamo subito aprendo MyEclipse, creiamo un nuovo web project e chiamiamolo WebResponder:

creazione del web project

creazione del web project

L'intento ora è di aggiungere al nostro database Utenti un campo Codice Fiscale e quindi nella nostra applicazione web dovremo aggiungere una pagina contenente una struts action che si occuperà dell'inserimento nel database.

Questa purtroppo non è la via migliore! Il problema è rappresentato dal fatto che è necessario effettuare una serie di operazioni (non problematiche attenzione) per integrare struts 2 ed hibernate quindi l’approccio migliore si chiama scaffolding.

Lo scaffolding è un termine che viene adottato per indicare una struttura di base o, per meglio dire, fondamenta sulle quali costruire il nostro software ad esempio supponiamo di voler creare una applicazione web che invia una email utilizzando una struttura software pre-esistente che invia e-mail in maniera generica, questa struttura è lo scaffolding sulla quale noi interveniamo apportando o modifiche oppure del codice customizzato.

Il termine in realtà è anche più ampio, quando ad esempio abbiamo aggiunto Hibernate Capabilities al nostro progetto abbiamo "fuso" la nostra applicazione con uno scaffolding (il codice Hibernate) di MyEclipse.

Vediamo adesso come utilizzare questa tecnica in maniera più esplicita all'interno della applicazione che stiamo realizzando.

Partiamo da una metodologia che è utile usare in fase di
progettazione: creare la struttura di comunicazione, lo scheletro funzionale ed a questo vi aggiungiamo di volta in volta le funzionalità business.

Preparazione del database

Ma procediamo con ordine, apriamo MySql Workbench e avviamo il server se necessario:

avvio di mysql workbench

avvio di mysql workbench

Nel nostro schema mioschema clicchiamo col tasto destro del mouse sulla tabella utenti e selezioniamo la voce Alter table:

alter table tramite mysqlworkbench

alter table tramite mysqlworkbench

Inseriamo quindi un nuovo field codiceFiscale, sempre di tipo varchar (scegliete la dimensione più appropriata) e selezionando la voce NN acronimo di Not Null a indicare che deve avere necessariamente un valore pena un errore che viene rilanciato a runtime dal database.

aggiunta campo codiceFiscale

aggiunta campo codiceFiscale

Aggiungiamo anche un ulteriore campo che chiamiamo visible di tipo boolean, se true indica che il record è effettivamente presente (vedremo poi a cosa ci servirà...)

Cliccando sul tasto Apply la modifica
verrà resa effettiva (ricordiamo che l'operazione è di tipo DDL acronimo di Data Description Language)

DDL alter table utenti

DDL alter table utenti

Ora andremo incontro ad una piccola inconsistenza: con il tasto destro sulla tabella selezionate Select Rows:

select rows per l'update dei valori sul campo aggiunto

select rows per l'update dei valori sul campo aggiunto

L'alter della tabella è andato a buon fine come possiamo notare dalla presenza del campo aggiunto codiceFiscale, però essendoci dei record pre-esistenti il campo risulta vuoto (nemmeno Null è un valore valido, pur essendo considerato tale in molti linguaggi) quindi bisogna capire come intervenire. Ovviamente se i record fossero stati centinaia avremmo avuto un problema molto serio, al momento per fortuna non è il nostro caso, e procediamo scegliendo tra:

  1. inserire manualmente un codice fiscale per ciascun record
  2. Effetuare una operazione di UPDATE con una operazione che calcoli un codice fiscale per ciascun record (molto più complesso)
  3. Cancellare i record esistenti

Scegliamo la prima opzione e procediamo (non dimentichiamoci di fare il commit dell'insert!).

Apriamo ora MyEclipse e creiamo un web project che chiamiamo WebResponder, riprendendo il concetto di scaffolding agiremo in questo modo:

  1. Aggiungiamo la Hibernate capabilities al nostro progetto riutilizzando la connessione esistente (vedi articolo Hibernate per principianti)
  2. Testeremo l'inserimento nel database utilizzando un form contenuto in una pagina Jsp

Apriamo la Palette ed inseriamo un Form nella pagina index.jsp che viene inizialmente caricata (viene specificata nel deployment descriptor web.xml nel tag xml <welcome-file>)

aggiunta del form dalla palette

aggiunta del form dalla palette

Diamo un nome al form, una action nothing che non ha nessun significato (la utilizziamo come Segnaposto) e
scegliamo come metodo POST invece di GET, questo perchè il form invierà delle informazioni al server ed è più logico usare post piuttosto che get, anche se quest'ultimo per un numero basso di
informazioni andava comunque bene.

form action

form action

Apriamo il tab HTML-BASIC nella Palette e trasciniamo l’elemento Table all’interno del form specificando i valori visualizzati in figura:

palette: aggiunta table

palette: aggiunta table

Ora cliccate su ogni casella a sinistra della tabella e inserite da tastiera il seguente contenuto:

completamento tabella

completamento tabella

Successivamente trascinate un textfield in ciascuna casella adiacente e date ad ognuno un nome corrisopndente associato ad esempio nel grafico successivo il textfield ha come nome nomeTextfield (quindi i restanti saranno cognomeTextfield, codicefiscaleTextfield e emailtextField):

palette: inserimento TextField

palette: inserimento TextField

Inseriamo allo stesso modo un pulsante di tipo Submit con label invia.

palette: aggiunta submit

palette: aggiunta submit

Ora creiamo una servlet Storer che verrà invocata da index.jsp e che riceverà i parametri del form stampandoli sulla console di Tomcat, questo per verificare la corretta interazione tra i due componenti, da notare che MyEclipse viene distribuito in Bundle con Apache Tomcat ma possiamo tranquillamente configurare uno o più server Server esterni.

Prima di procedere molti di voi si saranno chiesti del perchè non creare direttamente una servlet con dati fissi, il motivo è che dopo avremo necessità della pagina index.jsp e inoltre avremo collaudato l'interazione della stessa con altri componenti oltre che il corretto recupero dei parametri del form da parte di un altro componente (vedi servlet).

Usiamo il tasto destro sul nostro progetto e selezioniamo new/other/servlet:

creazione della servlet Storer

creazione della servlet Storer

Selezioniamo come mapping url il valore /Storer, invece che /servlet/Storer, come consigliato da
MyEclipse. Questo per un motivo di sicurezza: in questo modo per richiamare la servlet l'url nel browser dovrà essere:

http://:8080/WebResponder/servlet/Storer

Quindi sarà visibile quale tecnologia stiamo utilizzando mentre secondo la nostra scelta sarà:

http://:8080/WebResponder/Storer

il mapping scelto per la servlet Storer

il mapping scelto per la servlet Storer

Quindi modifichiamo il codice nella servlet in modo da fargli stampare un messaggio personalizzato.

modifiche all'html prodotto nella servlet Storer

modifiche all'html prodotto nella servlet Storer

Carichiamo in MyEclipse la pagina index.jsp cliccandoci due volte sopra quindi tasto destro del mouse all'interno della pagina che ne mostra il codice e selezioniamo format:

ri-formattazione del codice

ri-formattazione del codice

Aggiorniamo la pagina index.jsp in modo che richiami la servlet:

modifiche ad index.jsp per richiamare la servlet Storer

modifiche ad index.jsp per richiamare la servlet Storer

Con il tasto destro sul progetto selezioniamo Run As/MyEclipse server Application, da cui otteniamo la schermata iniziale e quella successiva premendo invia:

webresponder: primo run di prova della servlet

webresponder: primo run di prova della servlet

Possiamo quindi modificare il codice di Storer, per gestire le request in arrivo:

out.println("");
out.print("Salve sono la servlet storer! ");
String nomeTF = request.getParameter("nomeTextfield");
String cognomeTF = request.getParameter("cognomeTextField");
String codiceFiscaleTF = request.getParameter("codiceFiscaleTextfield");
String emailTF = request.getParameter("emailTextfield");
out.print("nome [" + nomeTF +"] ");
out.print("cognome [" + cognomeTF +"]");
out.print("codice fiscale [" + codiceFiscaleTF +"]");
out.print("email [" + emailTF +"]");
out.println("");

webresponder: test dell'invio dei dati

webresponder: test dell'invio dei dati

Per il textfield associato all’email ho incrementato la dimensione specificando l'attributo size col valore di 30:

<input type="text" name="emailTextfield" size="30">

Ora trasformiamo il nostro progetto in modo da basarlo su Struts 2. Abbiamo scelto la via più lunga, e il motivo è semplice: spesso ci si ritrova ad effettuare il porting di una web
application scritta con servlet e jsp in Struts.

Con il tasto destro sul progetto selezioniamo My eclipse/add struts capabilities, nella seguente schermata scegliamo struts 2.1 con url pattern *.action, questo significa che qualsiasi url diretto alla nostra applicazione che termina con questo postfisso verrà interpretata automaticamente dal motore di struts.

Clicchiamo due volte sul file struts.xml e selezioniamo il tab flow quindi trasciniamo l'icona package nell'area di lavoro:

aggiunta di struts

aggiunta di struts

Nella schermata successiva selezioniamo come nome webresponder e come package che estende struts-default:

creazione di uno struts package

creazione di uno struts package

Trasciniamo sull'area di lavoro all’interno del package l'icona della action:

webresponder: aggiunta action dalla palette

webresponder: aggiunta action dalla palette

E selezionaimo i seguenti parametri per la nostra struts action:

parametri per la struct action

parametri per la struct action

MyEclipse creerà una classe come questa:

package com.strutsActions;
public class StorerAction extends ActionSupport {
	public String execute() {
		return SUCCESS;
	}
}

E il file di configurazione struts.xml aggiornato con la configurazione della action:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC ".../dtds/struts-2.1.dtd">
<struts>
<package name="webresponder" extends="struts-default">
<action name="StrutsStorer" class="com.strutsActions.StorerAction">
</action>
</package>
</struts>

Ora velocemente aggiungiamo alla nostra action dei campi interni che seguono le specifiche dei javabeans con i nomi dei textfield presenti nella pagina index.jsp. Ad esempio nella index.jsp è definito il seguente textfiedl:

<input type="text" name="codiceFiscaleTextfield">

Quindi aggiungere alla nostra action i campi seguente con relativi metodo getter/setter:

public class StorerAction extends ActionSupport {
/...
private String codiceFiscaleTextfield;
private String nomeTextfield;
private String cognomeTextField;
private String codiceFiscaleTextfield;
private String emailTextfield;
// metodi getter/setter

Modificare quindi l'attributo action del form nella pagina index.jsp come segue:

modifiche alla action del form

modifiche alla action del form

Questo lo schema per capire il mapping url che consente di richiamare la action dal form:

mapping url per la action del form

mapping url per la action del form

Nel metodo execute della action inseriamo delle istruzioni di log:

public String execute() {
	System.out.println("OK STRUTS ACTION CALLED!!!");
	System.out.println("nomeTextfield:" + nomeTextfield);
	System.out.println("cognomeTextField:" + cognomeTextField);
	System.out.println("codiceFiscaleTextfield:" + codiceFiscaleTextfield);
	System.out.println("emailTextfield:" + emailTextfield);
	return SUCCESS;
}

Rideployando e lanciando l'applicazione:

redeploy dell'applicazione webresponder

redeploy dell'applicazione webresponder

L'output della console ci conferma che il binding dei paramteri del form con i corrispondenti campi della action è perfettamente riuscito!

output di webresponder

output di webresponder

Bisogna sistemare ancora alcune cose, innanzitutto nel browser compare la schermata Error 404- NotFound questo perchè dobbiamo mappare la stringa "success" restituita dal metodo execute della action ad una risorsa quindi creiamo una pagina success.jsp sotto la cartella WEB-INF e scriviamo il testo elaborazione riuscita!

result success per la action

result success per la action

Rideployando l’applicazione e premendo submit:

esito submit

esito submit

Dopo aver corretto la action nel caso di gestione positiva, la stessa procedura va applicata in caso di esito negativo, quindi se la action restituisce failure.

Integrazione con hibernate

Procediamo rifattorizzando il codice, utilizziamo cioè il binding per mappare i dati del form direttamente con il javabean Utenti creato nel refactoring di Hibernate:

binding dei dati con il javabean Utenti, generato da Hibernate

binding dei dati con il javabean Utenti, generato da Hibernate

Primo passo: utilizziamo i javabean generati da hibernate

La modifica è semplice basta utilizzare un javabean di classe Utenti al posto dei campi utilizzati, quindi la nostra action conterrà:

private Utenti utente;
// ...
System.out.println("nome:" + utente.getNome());
System.out.println("cognome:" + utente.getCognome());
System.out.println("codiceFiscaleTextfiel:" + utente.getCodiceFiscale());
System.out.println("email:" + utente.getEmail());

Infine modifichiamo la pagina index.jsp per implementare il binding dei textfield con i campi del javabean utenti inserendo per ciascun textfield un riferimento al corrispondente campo del javabean, ad esempio per il textfield cognome:

<input type="text" name="utente.cognome">

e analogamente per i restanti TextField

Rideployando la nostra applicazione si comporterà allo stesso modo:

re-deploy di webResponder

re-deploy di webResponder

Integrazione tra Struts 2 e Hibernate

Ora occupiamoci dell'integrazione tra struts 2 e hibernate.

Integrare Hibernate a Struts 2 non è intuitivo come sembra, il problema e che hibernate per effettuare operazioni sul database utilizza una propria Session mentre Struts ne utilizza una sua. Bisogna quindi da Struts
recuperare la Session di Hibernate.

Questa attività puo essere fatta in diversi modi, quello migliore prevede l'utilizzo di un plugin Hibernate.

Bisogna prima di tutto scaricare il seguente jar:

http://code.google.com/p/full-hibernate-plugin-for-struts2/

Scarichiamo il jar e inseriamolo sotto la directory WEB-INF/lib assicurandoci che è stato aggiunto al build path:

aggiunta delle librerie struts 2 ed hibernate al classpath

aggiunta delle librerie struts 2 ed hibernate al classpath

aggiunta delle librerie struts 2 ed hibernate al classpath

aggiunta delle librerie struts 2 ed hibernate al classpath

Configurazione del layer di persistenza con Hibernate

Ora creiamo una classe PersistenceLayer che si occuperà di recuperare la sessione di Hibernate:

package com.hibernate.layer.persistence;
public class PersistenceLayer {
	// va esplicitamente istanziata la classe DAO per gestire le operazioni sulla tabella del database.
	private UtentiDAO utentiDAO = new UtentiDAO();
	// Vanno dichiarate le variabili di sessione e transazioni nella classe in modo che il plugin installato possa 'istanziarle' via Inversion Of Control e renderle disponibili utilizzando le annotazioni.
	@SessionTarget
	private Session hibernateSession;
	@TransactionTarget
	private Transaction hibernateTransaction;
	public Session getHibernateSession() {
		return hibernateSession;
	}
	public void setHibernateSession(Session hibernateSession) {
		System.out.println("setHibernateSession() called!");
		this.hibernateSession = hibernateSession;
	}
	public Transaction getHibernateTransaction() {
		return hibernateTransaction;
	}
	public void setHibernateTransaction(Transaction hibernateTransaction) {
		System.out.println("setHibernateTransaction() called!");
		this.hibernateTransaction = hibernateTransaction;
	}
	public void addCliente(Utenti cliente) {
		utentiDAO.save(cliente);
	}
	public UtentiDAO getClienteDAO() {
		return utentiDAO;
	}
	public void setClienteDAO(UtentiDAO clienteDAO) {
		this.utentiDAO = clienteDAO;
	}
	public Utenti findUserById(Integer id) {
		return utentiDAO.findById(id);
	}
	public void updateCliente(Utenti cliente) {
		utentiDAO.merge(cliente);
	}
	public void deleteCliente(Utenti cliente) {
		utentiDAO.delete(cliente);
	}
}

Quindi mappiamo la nostra action nel file struts.xml, e facciamo un'altra importante modifica. Il package di riferimento della nostra action non deve estendere struts-default, ma hibernate-default, questo perché l'ultimo contiene degli interceptor per configurare le variabili di sessione e transazione nel persistence layer:

persistence layer: configurazione con hibernate-default

persistence layer: configurazione con hibernate-default

Aggiungiamo infine la seguente riga al file hibernate.cfg.xml:

<property name="hibernate.connection.autocommit">true</property>

In questo modo che hibernate effettua il commit quando richiamiamo utentiDAO.save(): questa riga aggiunta ci permetterà di non effettuare il commit in maniera manuale recuperando la sessione e la transazione hibernate, se non inserito dovremo occuparcene personalmente!

Un ultimo step consiste nel download di tre librerie:

aggiunta di librerie struts 2 e hibernate

aggiunta di librerie struts 2 e hibernate

Infine modificando il codice della action, in particolare il metodo execute:

public String execute() {
	System.out.println("nome:" + utente.getNome());
	System.out.println("cognome:" + utente.getCognome());
	System.out.println("codiceFiscaleTextfiel:" +
	utente.getCodiceFiscale());
	System.out.println("email:" + utente.getEmail());
	System.out.println("inserisco l'utente in sessione");
	utente.setPresente("n");
	PersistenceLayer pl = new PersistenceLayer();
	pl.addCliente(utente);
	// ...
	return success;
}

Possiamo quindi verificare a che punto siamo rifacendo il deploy, ed aprendo mysql qorkbench:

verifica su mysql workbench del re-deploy di webResponder

verifica su mysql workbench del re-deploy di webResponder

Il campo ‘presente’ può assumere due valori:

  • s: record presente nel database
  • n: record non presente nel database

Nel momento in cui l'utente clicca sul link contenuto nell'email verrà richiamata una nuova action InsertAction che imposta per l'utente il valore n invece di s.

Il link avrà reso possibile recuperare un parametro che rappresenta l'ID (Primary Key), che verrà impostato dalla action StorerAction.

Ricapitolando: StorerAction riceve i parametri dal form, costruisce Utente e lo inserisce nel database con il valore n nel campo presente. Quindi invia l'email all'utente con il link a InsertAction, agganciando all'url (URL rewriting) l'id del record, quindi InsertAction carica il record in funzione dell'ID, e cambia il valore del campo presente da n a s.

schema per InsertAction

schema per InsertAction

Il modulo per l'invio delle email all'utente

Occupiamoci ora dell’invio e-mail all’utente, utilizzeremo javamail disponile tra le librerie utilizzate quindi non dobbiamo scaricare nulla da internet, questa la nostra action con il codice in rosso che si occupa dell’invio della email:

package com.strutsActions;
public class StorerAction extends ActionSupport {
private Utenti utente;
static Properties properties = new Properties();
static {
	properties.put("mail.smtp.host", "smtp.gmail.com");
	properties.put("mail.smtp.socketFactory.port", "465");
	properties.put("mail.smtp.socketFactory.class","javax.net.ssl.SSLSocketFactory");
	properties.put("mail.smtp.auth", "true");
	properties.put("mail.smtp.port", "465");
}
private void sendEmail() {
	try {
		Session session = Session.getDefaultInstance(
			properties,
			new javax.mail.Authenticator() {
				protected PasswordAuthentication getPasswordAuthentication() {
					return new PasswordAuthentication("astaritagiuseppe@gmail.com", "asterix1111");
				}
			}
		);
		Message message = new MimeMessage(session);
		message.setFrom(new InternetAddress("astaritagiuseppe@gmail.com"));
		message.setRecipients(Message.RecipientType.TO, InternetAddress.parse("astarita_giuseppe@libero.it"));
		message.setSubject("messaggio");
		message.setText("clicca il link! http://192.168.1.140:8080/WebResponder/StrutsStorer.action ");
		Transport.send(message);
	} catch(Exception e) {
		System.out.println("ATTENZIONE: ERRORE NELL'INVIO DELL'EMAIL!");
		e.printStackTrace();
	}
	System.out.println("EMAIL INVIATA CONTROLLA!");
}
/
public String execute() {
// ...

Il codice per l'invio email non è tanto difficile da capire: in sostanza si "preparano" delle properties per l'invio, quindi si definiscono i valori del mittente (from), destinatario (to), l'oggetto dell'email (subject) e il
contenuto (text).

Nel nostro caso abbiamo utilizzato Gmail come Mail provider ma si possono utilizzare anche altri provider l’importante è fornire i parametri di connessione correttamente oltre ad un account valido, nel nostro caso abbiamo utilizzato il mio mascherando la password per ovvi motivi di privacy.

Infine da notare che il corpo dell'email contiene un link di prova, ma in realtà dovrà puntare alla action che si occuperà di inserire l’utente nel database recuperandolo dalla sessione (dove precedentemente è stato inserito).

Il metodo inviaEmail() è private e verrà richiamato dal metodo execute(), rideployando il tutto:

execute del metodo inviaMail()

execute del metodo inviaMail()

E nella mia casella email:

l'email ricevuta

l'email ricevuta

Verifichiamo il contenuto:

la mail contenente il link per l'attivazione

la mail contenente il link per l'attivazione

Questa la InsertAction:

un estratto del codice di InsertAction

un estratto del codice di InsertAction

Se rideployamo l'applicazione modificando la pagina success.jsp in modo da visualizzare un messaggio più comprensibile l'applicazione non funzionerà! Il motivo è che Hibernate mantiene in cache i risultati dell'update quindi bisogna chiamare in maniera esplicita il metodo flush() dal PersistenceLayer su UtentiDAO, quindi andiamo prima di tutto in UtentiDAO ed aggiungiamo il metodo:

public void flush() {
    getSession().flush();
}

Analogamente in PersistenceLayer:

public void flush() {
    gutentiDAO.flush();
}

In modo che la action insertAction possa richiamarlo:

un estratto di insertAction, con l'inserimento del flush() sul persistence layer

un estratto di insertAction, con l'inserimento del flush() sul persistence layer

Fatto questo ecco l’output:

riepilogo dell'output di webResponder

riepilogo dell'output di webResponder

Cliccando sul link:

esito del click nella finestra del browser

esito del click nella finestra del browser

Mentre sul nostro database:

esito del click sul database

esito del click sul database

Considerazioni finali

L'applicazione ovviamente è suscettibile di miglioramenti. Ad esempio:

  1. Possiamo aggiungere la validazione del form sia lato client con javascript che lato server sfruttando il metodo validate() della action
  2. Si poteva utilizzare una terminologia migliore per le nostre action ed integrare log4j
  3. Cè una questione di sicurezza da gestire, l'utente manda in chiaro una operazione su database
    attraverso un url
  4. Se l'utente clicca due volte sul link cosa succede? Eese lo fa in due sessioni distinte? Bisogna
    quindi gestire anche queste situazioni...

A voi il compito di implementare queste nuove funzionalirtà!

Ti consigliamo anche