|
L'uomo è il computer più straordinario di tutti
|
|
| Home Page |
|
| Articoli |
|
| News |
|
| Forum |
|
| Classi |
|
|
|
|
|
STRIPES ///
By
Ivan De Martino
27 aprile 2009
|
 |
|
Lo scopo di questo articolo è mostrare i punti di forza di Stripes e, sviluppando una semplice applicazione di autenticazione, essere subito operativi con questo strumento. Stripes è un presentation framework open source, giunto alla versione 1.5. Sebbene siano già disponibili numerosi altri prodotti che agevolano lo sviluppo di applicazioni, Stripes merita una particolare menzione, in quanto si pone l'ambizioso obiettivo di semplificare uno dei punti più critici dei framework: la configurazione. Perché la la configurazione è un aspetto così complesso nei framework? Perché è spesso distribuita su diversi files (il più delle volte xml), richiede tempi lunghi in termini di apprendimento, ha una sintassi articolata e complessa e rende difficile lo sviluppo in team. Con Stripes invece è possibile partire “from scartch” in pochissimo tempo, in quanto la sua configurazione è veramente minimale. Inoltre, l'uso delle Annotation rende questo framework semplice ed intuitivo, senza sottovalutare il vantaggio di poterlo estendere con facilità. Le principali caratteristiche di Stripes sono: - configurazione basata su Annotation;
- convention over Configuration;
- auto-discovery degli action beans e dei form beans;
- sviluppo modulare ed indipendente delle viste e dei modelli;
- installazione e configurazione semplice e rapida;
- facilità di estensione;
- ricca disponibilità di taglibs;
- supporto integrato e trasparente per il file uploading.
Il flusso tipico di Stripes può essere rappresentato come segue:  Per analizzare le caratteristiche di Stripes, costruiamo una piccola applicazione (download codice di esempio). Partiremo con una welcome page che ci permetterà di accedere ad una form di autenticazione ed una form di registrazione. La form di autenticazione verrà utilizzata per implementare una semplice validazione dei dati, mentre gli ActionBean ci mostreranno la possibilità di poter intercettare in uno stesso ActionBean diversi eventi. Con il nostro esempio avremo modo di: - verificare la possibilità di gestire la validazione dei dati in modo semplice e rapido senza dover utilizzare configurazioni dedicate, ma solo le annotazioni;
- utilizzare i java bean per effettuare il binding dei dati;
- implementare eventi diversi all'interno dello stesso ActionBean handler;
- focalizzare l'attenzione sulle tre componenti principali di Stripes: configurazione, view, ActionBean.
|
|
|
Configurazione
|
top
|
Per essere immediatamente funzionante Stipes richiede la configurazione di uno StripesFilter e di una StripesDispatchServlet all'interno del file web.xml.
web.xml ... <filter> <filter-name>StripesFilter</filter-name> <filter-class>net.sourceforge.stripes.controller.StripesFilter</filter-class> <init-param> <param-name>ActionResolver.Packages</param-name> <param-value>it.javaportal.stripes.action</param-value> </init-param> </filter>
<servlet> <servlet-name>DispatcherServlet</servlet-name> <servlet-class>net.sourceforge.stripes.controller.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet>
<filter-mapping> <filter-name>StripesFilter</filter-name> <servlet-name>DispatcherServlet</servlet-name> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> </filter-mapping>
<servlet-mapping> <servlet-name>DispatcherServlet</servlet-name> <url-pattern>*.htm</url-pattern> </servlet-mapping>
<welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> ... Lo StripesFilter assicura che tutte le request verso Stripes siano intercettate nello stesso modo, anche le request multipart/form saranno trattate dagli ActionBean al pari i una qualsiasi request. E' Importante notare il parametro di inizializzazione ActionResolver.Packages. E' utilizzato da Stripes per effettuare l'autodiscovery degli ActionBean all'interno del classpath, indica all'interno di quali packages sono presenti i nostri ActionBean. Questo è l'unico parametro obbligatorio richiesto dallo StripesFilter. Come altri framework, esiste un unico controller, la DispatcherServet, che intercetta tutte le request “.htm” e le instrada verso gli appropriati ActionBean.
Un altro file richiesto da Stripes è il file StripesResources.properties. Utilizzato per effettuare il lookup dei messaggi in fase di validazione, sia per le validazioni disponibili di default all'interno del framework, sia per quelle che saranno implementate in fase di sviluppo. Il file StripesResource.properties deve essere disponibile all'interno del classpath. Di seguito una porzione del file StripesResources.properties:
#STRIPES VALIDATION MESSAGES validation.required.valueNotPresent={0} is a required field validation.minlength.valueTooShort={0} must be at least {2} characters long validation.maxlength.valueTooLong={0} must be no more than {2} characters long ..... ...... validation.file.postBodyTooBig=Total upload size of {3} KB exceeds the maximum size of {2} KB # CUSTOM VALIDATION MESSAGES custom.validation.error.valueNotValid={0} value {1} is not a possible value
L'ultimo messaggio riportato sarà utilizzato nella validazione custom all'interno del nostro ActionBean di esempio.
|
|
VIEW
|
top
|
Stripes di default supporta l'utilizzo delle JSP come tecnologia standard per le views. E' possibile integrare con estrema facilità altre tecnologie come FreeMarker, noto componente java basato su template per la generazione di pagine html . Inoltre sono a disposizione una serie di taglibs simili ai corrispettivi html tags per agevolare e semplificare lo sviluppo delle pagine di view. La prima pagina che visualizziamo e la jsp di la welcome-page:

Il codice della pagina index.jsp è il seguente <%@ taglib prefix="stripes" uri="http://stripes.sourceforge.net/stripes.tld" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <title>Stripes Sample</title> </head> <body> <h1>Welcome to stripes sample</h1> <stripes:link event="login" beanclass="it.javaportal.stripes.action.HomeActionBean"> Login </stripes:link><br/><br/> <stripes:link event="register" beanclass="it.javaportal.stripes.action.HomeActionBean"> Register </stripes:link> </body> </html Attraverso le taglib, messe a disposizione da Stripes, abbiamo creato due link che effettuano una richiesta verso l' url associata all' ActionBean it.javaportal.stripes.action.HomeActionBean, comunicando anche l'evento definito all'interno dell'attributo event. l'html di output sarà: <a href="/Home.htm?login=">Login</a> <a href="/Home.htm?register=">Register</a> Il framework, in fase di runtime, converte l'attributo beanClass nell' url a cui è associato. A questo punto una domanda sorge spontanea “ma dove è definita questa url?” La risposta la troveremo di seguito, in una annotation all'interno della definizione della classe che ci consentirà di associare l'url all'ActionBean. Da notare che i due link puntano alla stessa url, li differenzia un parametro che è il valore immesso nell'attributo event. Questo valore ci consentirà di invocare l'handler corretto all'interno dell'ActionBean.
La pagina di registrazione è html puro e non utilizza nulla che debba essere commentato (il codice sorgente di register.jsp è presente all'interno dell'esempio allegato). L'ultima pagina del nostra applicazione è una form di login con due text input e due tasti di submit. I due tasti di submit consento di generare rispettivamente l'evento “login” ed un altro generico evento da noi chiamato “other”. Come nella welcome-page, per dimostrare la possibilità di avere più di un handler nell'ActionBean, abbiamo usato l'evento “other” per reindirizzare l'utente alla form di registrazione. Inoltre per agevolare la comprensione, il campo password è stato definito come text.
Il codice della nostra pagina login.jsp è il seguente: <%@ taglib prefix="stripes" uri="http://stripes.sourceforge.net/stripes.tld" %> <%@ taglib prefix="dyna" uri="http://stripes.sourceforge.net/stripes-dynattr.tld" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <title>FIRTS STRIPES SAMPLE</title> <link rel="stylesheet" type="text/css" href="css/style.css"> </head> <body> <div id="main"> <stripes:errors/> <stripes:form beanclass="it.javaportal.stripes.action.LoginActionBean"> <table align="center"> <tr> <td>login:</td> <td><stripes:text name="credentials.userName"/></td> </tr> <tr> <td>password: </td> <td><dyna:text name="credentials.password" extra="extraAttr"/></td> </tr> <tr> <td colspan="2"> <stripes:submit name="submit" value="login"/> <stripes:submit name="other" value="other"/> </td> </tr> </table> FAIL è un valore non valido per il campo login </stripes:form> </div> </body> </html>
Stripes ha una serie di jstl che rendono intuitiva la scrittura delle view. Le jstl offrono un supporto immediato per:
- la gestione degli errori di validazione;
- la corretta action da impostare nei tag form;
- la creazione di link (vedi index.jsp);
- la creazione dei tag html.
In breve: il tag <stripes:error> fornisce un supporto di default per mostrare gli errori di validazione. E' possibile modificare la visualizzazione o gestire diversamente la visualizzazione degli stessi.
Il tag <stripes:form> crea una html form che sarà utilizzato per l'invio della request all'aActionBean indicato nell'attributo beanclass.
Il tag <stripes:text...> crea un html type input. Nel nostro esempio abbiamo due input di tipo text. Il loro valore è rispettivamente quello delle proprietà userName e password dell'oggetto Credential esposto dall'ActionBean.
Il tag <stripes:submit ...> crea un html button. Nel nostro esempio sono i due bottoni per gli eventi “login” ed “other” definiti nell'ActionBean.
Esistono due tipologie di taglibs:
- le standard: non consento l'utilizzo di attributi non standard, cioè non definiti nel Tag Library Definition
- le Dynamic Attributes: accettano anche attributi arbitrari, non standard, per esempio le librerie Ajax.
Nella nostra applicazione, il campo di immissione password è generato utilizzando la taglib dynattr,ed è stato aggiunto un attributo non standard al campo.
La form mostrata all'utente sarà

Nella form, i campi login e password sono obbligatori. Il campo login genera un errore di validazione qualora contenga il valore “FAIL”. Se tentiamo di effettuare la login senza aver immesso i dati all'interno della form, all'utente saranno notificati gli errori di validazione secondo le regole definite all'interno dell'ActionBean:

Viene notificato un errore anche quando i campi sono valorizzati ma il campo login contiene il valore FAIL:

In caso di dati corretti, si visualizza una pagina di benvenuto con il valore immesso nel campo login.

|
|
ActionBean
|
top
|
Un ActionBean è un controller che, a fronte di una submit, riceve la request dell'utente e ne processa i dati. Per permettere allo StripesDispatcher di iniettare l'oggetto ActionBeanContext, ogni ActionBean deve necessariamente implementare l'interfaccia Stripes ActionBean o estendere una classe che la implementi. Questa implementazione rende possibile accedere agli oggetti HttpServletRequest, HttpServletResponse e ServletContext.
Il ciclo di vita di una request associata ad un ActionBean può essere così descritto:
- in base alla url richiesta si rende disponibile un ActionBean e si iniettata l'istanza di ActionBeanContext;
- si risolve il metodo handler che si occupa di elaborare la richiesta per quel determinato evento;
- si effettua un bind dei valori dalla HttpServletRequest all'ActionBean, se prevista è eseguita la validazione dei dati;
- si invoca un qualsiasi altro metodo di validazione custom;
- si invoca il metodo handler precedentemente risolto;
- si esegue l'oggetto Resolution restituito dall'handler.
Analizziamo in dettaglio gli ActionBean.
Il primo è l'ActionBean HomeActionBean:
@UrlBinding("/Home.htm") public class HomeActionBean implements ActionBean {
ActionBeanContext actionBeanContext; public void setContext(ActionBeanContext actionBeanContext) { this.actionBeanContext = actionBeanContext; }
public ActionBeanContext getContext() { return this.actionBeanContext; } @HandlesEvent(value = "login") public Resolution view() { return new RedirectResolution("/Login.htm"); }
@HandlesEvent(value = "register") public Resolution register(){ return new RedirectResolution("/Register.htm"); } }
La senconda è la LoginActionBean:
@UrlBinding("/Login.htm") public class LoginActionBean implements ActionBean {
@ValidateNestedProperties({ @Validate(field = "userName",required = true,on = { "submit" }) , @Validate(field = "password",required = true,on = { "submit" }) }) private Credentials credentials;
private ActionBeanContext context;
public Credentials getCredentials() { return credentials; }
public void setCredentials(Credentials credentials) { this.credentials = credentials; }
public void setContext(ActionBeanContext actionBeanContext) { this.context = actionBeanContext; }
public ActionBeanContext getContext() { return context; }
@DefaultHandler @DontValidate public Resolution init(){ return new ForwardResolution("WEB-INF/jsp/login.jsp"); }
@HandlesEvent(value = "submit") public Resolution login() { return new ForwardResolution("WEB-INF/jsp/hello.jsp"); }
@ValidationMethod(on = "submit") public void extraValidation(ValidationErrors errors) { if(credentials.getUserName().equals("FAIL")){ ValidationError error = new LocalizableError("custom.validation.error.valueNotValid",new Object[]{credentials.getUserName()}); errors.add("credentials.userName",error); } }
@HandlesEvent( value = "other") public Resolution otherHandler() { return new RedirectResolution("/Register.htm"); } }
Si può notare la presenza nel codice di esempio di diverse annotation.
L'annotazione @UrlBinding esegue un bind del path specificato, ogni qual volta il client richiede questo specifico path l'ActionBean a cui è associato viene invocato.
L'annotazione @ValidateNestedProperties è utilizzata per la validazione di oggetti complessi. Nell'esempio riportato sono validate le proprietà userName e password dell'oggetto Credential, ma solo quando è generato l'evento submit. Qualora l'utente immetta dati che non rispettano le regole di validazione, viene effettuato un ridirezionamento sulla pagina sorgente ed uno o più messaggi di errore sono visualizzati. I messaggi di errore sono basati sul tipo di validazione eseguita e sul campo validato.
L'annotazione @DefaultHandler marca il metodo a cui è applicata come handler di default, qualora non sia specificato nessun evento all'interno della request o sia specificato un evento che non è possibile associare a nessun altro handler.
L'annotazione @HandlesEvent marca il metodo a cui è applicata come handler dell'evento specificato. Ad esempio nella LoginActionBean @HandlesEvent( value = "other") indica che quando viene generato l'evento “other” il metodo otherHandler si occupa di gestire la richiesta.
L'annotazione @ValidationMethod determina l'esecuzione del metodo a cui è applicata ed è utilizzata per definire validazioni custom. L' esecuzione avverrà prima di eseguire l'handling dell'evento dichiarato nell'attributo on (on = "submit").
Analizzando l'esecuzione dell'evento sumbit, nella nostra form di login possiamo sintetizzare i seguenti step:
- l'utente effettua la submit dei dati;
- Il framework imposta i valori ed effettua la validazione definita nella annotazione ValidateNestedProperties;
- in caso di errori di validazione viene ridirezionata la pagina sorgente;
- in caso di nessun errore, viene richiesta l'esecuzione dell'handler associato all'evento specificato, nel nostro caso all'evento sumbit è associato il metodo login();
- poiché l'evento submit è marcato come da validare nell'annotation ValidationMethod, viene eseguito il metodo extraValidation;
- in caso di errori di validazione viene ridirezionata la pagina sorgente;
- in caso di nessun errore viene eseguito il metodo handler.
L'annotazione @DontValidate dichiara che nessuna validazione deve essere eseguita quando viene generato quello specifico evento, o meglio quando viene invocato il metodo associato a quello specifico evento.
|
|
Conclusioni
|
top
|
Lontano dall'essere una completa panoramica di Stripes, questo articolo fornisce le basi per iniziare a lavorare con questo interessante framework. Aiutandoci con il codice di esempio della nostra applicazione di autenticazione, abbiamo messo in evidenza la semplicità e immediatezza della configurazione. Oltre alla configurazione, abbiamo analizzato anche la validazione. Infatti in Stripes, la validazione è molto veloce, ricca di controlli di default già disponibili ed è facilmente customizzabile.
Ritengo che una caratteristica importante di Stripes è la comodità di poter avere in un unico ActionBean diversi handler, senza dover ricorrere a workarounds quali: campi hidden, specifici parametri della request o if/else all'interno delle Action. Questa caratteristica di Stripes permette di implementare tutte le operazioni di C.R.U.D. associate ad un'entità in un unico ActionBean.
Sul sito ufficiale di Stripes è possibile consultare un wiki che fornisce un'esaustiva panoramica di tutte le funzionalità di questo giovane e promettente framework.
|
|
|

Eccetto dove diversamente specificato, i contenuti di questo sito sono rilasciati sotto licenza Creative Commons
|
|
|