Dopo aver pubblicato articoli sul framework Wicket e sui componenti Java Web Beans, affrontiamo in questo articolo l‘argomento Wicket Web Beans, un toolkit che combina insieme le potenzialità di Wicket con quelle dei Java Web Beans. In questa prima parte ne verranno illustrate le caratteristiche principali.
Nei mesi scorsi, su queste stesse pagine, sono state pubblicate una serie di articoli sul framework Wicket [2] e una sulle specifiche Web Beans [3]. Per la comprensione del presente articolo è necessario avere una conoscenza di base di entrambi gli argomenti. In caso contrario si consiglia la lettura preliminare degli articoli sopra citati. La versione di Wicket Web Beans a cui faremo riferimento in questa serie è la 1.1.
Che cosa è Wicket Web Beans?
Wicket Web Beans (WWB) è un toolkit per Wicket per la visualizzazione e l’editing di POJOs (Plain Old Java Objects) conformi alle specifiche Sun sui JavaBeans [4]. Con questo toolkit le WebPage vengono generate automaticamente in base alle proprietà dei Bean e a determinate convenzioni. Il layout, la modalità di editing e le action associate alle WebPage possono essere comunque customizzate, qualora ce ne sia bisogno: WWB consente sempre l’override dei propri comportamenti di default. WWB mette a disposizione diverse feature che consentono di focalizzare il design e lo sviluppo sul modello: sono a carico del toolkit tutti gli aspetti relativi alle gestione delle interfacce utente.
Il componente di WWB di più alto livello, il BeanForm, comprende funzionalità AJAX tipiche delle rich client applications. Tale form component può essere incluso in qualsiasi pagina web: in questo modo è quindi garantita la piena customizzazione per quanto riguarda il design delle pagine. È possibile inoltre includere più di un BeanForm in una sola pagina. È comunque sempre possibile utilizzare altri component di WWB di più basso livello rispetto al BeanForm: WWB non costringe a implementare una applicazione seguendo un unico schema. L’utilizzo dei BeanForm comunque rimane la maniera migliore per risparmiare un sacco di lavoro. In questo modo ci si può focalizzare sulla definizione del modello, mentre la gestione delle user interface rimane a carico del framework. I valori dei field all’interno di un form vengono automaticamente inviati al bean server-side nello stesso istante in cui essi cambiano, senza la necessità di dover gestire un ciclo di submit. Quindi un’applicazione WWB si comporta più come una rich client application che come una applicazione di tipo form-based standard.
Il sito ufficiale del progetto è [5]. Da qui è possibile scaricarne sia i JAR che i sorgenti. Il checkout dei sorgenti del progetto può essere fatto con un qualsiasi client SubVersion (SVN) via HTTP dall’URL riportato in [6]. Una volta scaricati, il build va eseguito con Maven 2 [7]. Basta posizionarsi nella directory principale del trunk principale e, da shell, invocare Maven con la seguente sintassi:
mvn clean install
oppure, se si desidera fare il build senza eseguire i test:
mvn clean install -Dmaven.test.skip=true
WWB 1.1 necessita di Java 1.5 o release superiore, Wicket 1.3 o release successiva e di Apache Common BeanUtils [8].
Un primo esempio pratico
Vediamo in dettaglio un esempio completo di applicazione WWB based. Implementiamo il seguente Bean:
public class TestBean implements Serializable { private static final long serialVersionUID = -8500883418534059147L; private String firstName; private String lastName; private Integer number; private BigDecimal operand1; private BigDecimal operand2; private PropertyChangeSupport listeners = new PropertyChangeSupport(this); public TestBean() { } /** * JavaBeans compliant method to add a PropertyChangeListener. */ public void addPropertyChangeListener(PropertyChangeListener listener) { listeners.addPropertyChangeListener(listener); } /** * JavaBeans compliant method to remove a PropertyChangeListener. */ public void removePropertyChangeListener(PropertyChangeListener listener) { listeners.removePropertyChangeListener(listener); } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = (firstName == null ? null : firstName.toUpperCase()); } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public Integer getNumber() { return number; } public void setNumber(Integer number) { this.number = number; } public BigDecimal getOperand1() { return operand1; } public void setOperand1(BigDecimal operand1) { this.operand1 = operand1; fireResultChange(); } public BigDecimal getOperand2() { return operand2; } public void setOperand2(BigDecimal operand2) { this.operand2 = operand2; fireResultChange(); } private void fireResultChange() { listeners.firePropertyChange("result", null, getResult()); } public BigDecimal getResult() { if (getOperand1() == null || getOperand2() == null) { return null; } return getOperand1().add( getOperand2() ); } }
Si tratta di un POJO conforme alle specifiche JavaBeans. Esso infatti comprende i metodi getter e setter per ogni sua proprietà e un costruttore pubblico senza argomenti in ingresso. TestBean inoltre implementa l’interfaccia java.io.Serializable, come da convenzione Wicket. TestBean implementa anche java.beans.PropertyChangeListener. In questo modo net.sourceforge.wicketwebbeans.containers.BeanForm si registrerà come listener per TestBean. In questo esempio utilizziamo l’evento di property change per notificare a BeanForm quando determinate proprietà cambiano di valore.
Vediamo ora come è fatta la pagina che contiene TestBean. Essa è una classica pagina web Wicket: quindi avremo nello stesso package una classe Java:
public class SimpleBeanPage extends WebPage { public SimpleBeanPage() { TestBean bean = new TestBean(); BeanMetaData meta = new BeanMetaData(bean.getClass(), null, this, null, false); add( new BeanForm("beanForm", bean, meta) ); } }
e la corrispondente pagina HTML SimpleBeanPage.html:
La classe SimpleBeanPage, che estende org.apache.wicket.markup.html.WebPage, contiene solo la dichiarazione e la crezione dell’istanza di TestBean e l’add di quest’ultimo alla pagina stessa. La figura 1 mostra l’aspetto della pagina a runtime.
Figura 1 – La pagina di esempio
Il campo Result non è editabile poiche’ nella classe TestBean non ne è stato implementato il metodo setter. Il suo valore però cambia automaticamente non appena viene editato un nuovo valore di Operand1 o Operand2: il calcolo viene eseguito del metodo getResult() di TestBean e il PropertyChangeListener notifica a WWB che il valore del field va aggiornato.
La classe net.sourceforge.wicketwebbeans.model.BeanMetaData rappresenta i metadati relativi alle proprietà di un bean e alle actions. Di default i metadati vengono originati secondo le seguenti convenzioni:
- i nomi delle label relative alle proprietà vengono derivati dai nomi delle proprietà stesse;
- i Field components relativi a tipi Java primitivi o wrapper, i tipi enum, java.util.Date e java.util.Calendar e le loro sottoclassi, e java.util.List sono già pre-configurati;
- tutte le proprietà JavaBean vengono visulizzate;
- tutti i field sono editabili, a meno che l’attributo viewOnly non sia false: in tal caso i field sono tutti read-only;
- una proprietà marcata come read-only viene sempre visualizzata read-only;
- tutti i field vengono visualizzati in ordine alfabetico crescente secondo i corrispondenti property name;
- tutti i field vengono visualizzati in un unica pagina.
I tipi dei field vengono dedotti da quelli delle corrispondenti proprietà Java. Il mapping fra i tipi Java e quelli net.sourceforge.wicketwebbeans.fields.Field viene automaticamente eseguito dalla classe net.sourceforge.wicketwebbeans.model.ComponentRegistry. È possibile aggiungere a ComponentRegistry il mapping verso nuovi tipi o fare per una proprietà di un Bean l’override di un field type built-in.
In WWB è possibile aggiungere delle actions ad un form. Rimanendo al nostro esempio, SimpleBeanPage diventa così dopo l’aggiunta di alcune actions:
public class SimpleBeanPage extends WebPage { public SimpleBeanPage() { TestBean bean = new TestBean(); BeanMetaData meta = new BeanMetaData(bean.getClass(), null, this, null, false); add( new BeanForm("beanForm", bean, meta) ); } public void save(AjaxRequestTarget target, Form form, TestBean bean) { info("Saved - thank you"); } public void cancel(AjaxRequestTarget target, Form form, TestBean bean) { info("Canceled - thank you"); } public void clearLastName(AjaxRequestTarget target, Form form, TestBean bean) { bean.setLastName(""); } }
I tre metodi aggiunti alla classe (save, cancel e clearLastName) definiscono le azioni disponibili per la pagina. La label di una action, per default, viene ricavata dal nome del metodo corrispondente. In generale, un metodo action deve sempre avere una firma di questo tipo:
public void actionName(AjaxRequestTarget target, Form form, bean) { ... }
L’aspetto della pagina a runtime diventa quello mostrato in figura 2.
Figura 2 – La pagina di esempio dopo l’implementazione delle actions.
Conclusioni
In questo primo articolo è stata fatta una introduzione al framework Wicket Web Beans e presentato un semplice esempio di applicazione web soffermandoci sulle caratteristiche ereditate da Wicket. Nel prossimo articolo ci addentreremo invece nella descrizione delle caratteristiche più spiccatamente Web Beans.
Riferimenti
[1] Karthik Gurumurthy, “Pro Wicket”, Apress, 2006
[2] Guglielmo Iozzia, “Wicket: Java framework per applicazioni web – I parte: Introduzione e primi esempi”, MokaByte 131, Luglio/Agosto 2008, e successivi
https://www.mokabyte.it/cms/article.run?articleId=XIU-464-LXV-CM2_7f000001_11994537_f9c9053a
[3] Guglielmo Iozzia, “Web Beans – I parte: Introduzione e specifiche”, MokaByte 138, Marzo 2009, e successivi
https://www.mokabyte.it/cms/article.run?articleId=2U2-E3P-NNC-6VT_7f000001_10911033_0a4a63e2
[4] Specifiche Sun JavaBeans
http://java.sun.com/javase/technologies/desktop/javabeans/docs/spec.html
[5] Sito ufficiale di Wicket Web Beans presso Google
http://code.google.com/p/wicket-web-beans/
[6] Wicket Web Beans SVN repository
http://wicket-web-beans.googlecode.com/svn/trunk/
[7] Apache Maven 2
[8] Apache Common BeanUtils
http://commons.apache.org/beanutils/