Secondo articolo su Wicket, un Java framework open source a componenti per applicazioni web. In questa seconda parte verranno illustrati i principali componenti del core del framework.
Nella prima parte di questa serie è stata descritta l’architettura del framework Wicket. In questa seconda parte, con l’ausilio di una applicazione di esempio, verranno mostrati i principali component del core del framework.
La versione a cui faremo riferimento anche in questo secondo articolo è la 1.4-m2. Ricordo ancora che, a partire dalla versione 1.4, Wicket richiede necessariamente Java 1.5. o release successiva.
L’applicazione Comics Convention Registration
Il modo migliore per illustrare i principali componenti di Wicket è ricorrere a una applicazione di esempio. L’applicazione in questione è una semplice applicazione per la registrazione dei partecipanti ad una convention fumettistica. Dopo un login iniziale l’applicazione consente di registrare nuovi partecipanti o di modificare i dati di partecipanti già registrati. È possibile inoltre associare una foto ad ogni partecipante.
La struttura dell’applicazione è analoga a quella dell’esempio dell’articolo precedente (https://www.mokabyte.it/cms/article.run?articleId=XIU-464-LXV-CM2_7f000001_11994537_f9c9053a). Le considerazioni fatte in tale articolo valgono anche per l’applicazione descritta in questo articolo.
Dettagli implementativi
Anche in questo caso definiamo la WebApplication per la nostra applicazione:
WicketExamples2Application extends WebApplication
Come home page dell’applicazione indichiamo la login page:
public Class getHomePage() { return LoginPage.class; }
La LoginPage estende la classe org.apache.wicket.markup.html.WebPage.
Nella LoginPage, così come nelle altre WebPage dell’applicazione, come prima cosa aggiungiamo due Panel, l’HeaderPanel e il FeedBackPanel. L’HeaderPanel è l’header comune a tutte le pagine dell’applicazione in esame. La classe HeaderPanel estende la classe org.apache.wicket.markup.html.Panel. Un Panel è un componente riusabile che contiene markup e altri componenti. In tutti i casi in cui si ha necessità di includere un gruppo di component in più page è buona norma ricorrere all’utilizzo di Panel, raggruppando i componenti comuni in un unico Panel e includendo quest’ultimo nelle pagine interessate, piuttosto che ripetere lo stesso codice in tutte le page. L’header della nostra applicazione contiene un logo con il link alla LoginPage e il titolo. Il codice della classe Java è il seguente:
public class HeaderPanel extends Panel { public HeaderPanel(String id) { super(id); init(); } public HeaderPanel(String id, IModel model) { super(id, model); init(); } private void init() { add(new Label("headerLabel", "Comics convention registration")); } }
Così come per le WebPage, anche per i Panel il codice HTML è separato da quello Java. Quindi va creato un template HTML con lo stesso nome della classe e avente lo stesso percorso all’interno del classpath dell’applicazione. Il codice del template HeaderPanel.html è il seguente:
"http://www.w3.org/TR/html4/loose.dtd">![]()
My label
Un FeedbackPanel è invece un component di Wicket (org.apache.wicket.markup.html.panel.FeedbackPanel) che consente di visualizzare messaggi generati da componenti appartenenti alla page a cui il panel è stato associato.
Tutte le pagine dell’applicazione di esempio avranno quindi le seguenti righe di codice Java:
headerPanel = new HeaderPanel("headerPanel"); add(headerPanel); feedbackPanel = new FeedbackPanel("feedbackPanel"); add(feedbackPanel);
e HTML:
Feedback messages will be here
La LoginPage contiene un Form con due field in cui inserire lo username e la password e i button di login e clear dei field. Il form del login estende il form base di Wicket (classe org.apache.wicket.markup.html.form.Form), in modo da poter associare all’evento di onSubmit (facendo l’override del metodo omonimo della classe ancestor) la nostra logica:
private class LoginForm extends Form { public LoginForm(String id) { super(id); } public LoginForm(String id, IModel model) { super(id, model); } @Override public void onSubmit() { if(usernameTextField.getModelObjectAsString().equals("") || passwordTextField.getModelObjectAsString().equals("")) { feedbackPanel.error("Missing username and/or password!"); } else { setResponsePage(new ParticipantListPage()); } } }
Ad ogni click sul button di Login, viene eseguito un semplice controllo per verificare che l’utente abbia immesso sia username che password. In caso affermativo viene eseguito il redirect verso la pagina che mostra la lista dei partecipanti già registrati, altrimenti viene generato un messaggio di errore che viene mostrato nel FeedbackPanel della pagina stessa.
Il form contiene due field. Il primo, usernameTextField, è di tipo TextField (org.apache.wicket.markup.html.form.TextField), mentre il secondo, passwordTextField, è di tipo PasswordTextField (org.apache.wicket.markup.html.form.PasswordTextField). La differenza tra un TextField e un PasswordTextField è che quest’ultimo mostra il contenuto non in chiaro, ma sotto forma di asterischi.
Al form non sono stati associati due button custom: utilizziamo quelli di default per il submit e per il clear. Quindi non c’è alcun riferimento ai button nel codice Java, ma solo nel template HTML.
A runtime, la pagina di login ha il seguente aspetto:
Figura 1 – Login page
La ParticipantListPage a cui viene redirezionato l’utente in caso di login effettuato con successo include un HeaderPanel e un FeedbackPanel (come le altre pagine dell’applicazione).
Ad essa sono stati aggiunti un link per raggiungere la pagina di registrazione di nuovi partecipanti e una ListView che mostra un elenco dei partecipanti già registrati. L’implementazione di un hyperlink in Wicket è la classe org.apache.wicket.markup.html.link.Link . Codice Java:
add(new Link("newParticipantLink") { public void onClick() { setResponsePage(new RegistrationPage()); } } );
e HTML:
Register a new participant
Una ListView (org.apache.wicket.markup.html.list.ListView) è un component che consente di visualizzare in maniera molto semplice il contenuto di java.util.List . Il model per i partecipanti è il seguente POJO:
public class Participant implements Serializable { private long id; private String firstName; private String secondName; private String country; private List talks; private String pictureName; public Participant() { talks = new ArrayList(); } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getSecondName() { return secondName; } public void setSecondName(String secondName) { this.secondName = secondName; } public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } public List getTalk() { return talks; } public void setTalk(List talks) { this.talks = talks; } public String getPictureName() { return pictureName; } public void setPictureName(String pictureName) { this.pictureName = pictureName; } }
La lista dei partecipanti sarà quindi una List. Nella ParticipantListPage, per ogni Participant presente nella lista verranno mostrati il first name e il second name. Inoltre a ciascun item verrà aggiunto un link per poterlo modificare. Nella classe ParticipantListPage.java avremo:
List participants = getParticipants(); add(participantListView = new ListView("participants", participants) { public void populateItem(final ListItem listItem) { final Participant participant = (Participant)listItem.getModelObject(); listItem.add(new Label("secondNameLabel", "Second Name: ")); listItem.add(new Label("secondName", participant.getSecondName())); listItem.add(new Label("firstNameLabel", "First Name: ")); listItem.add(new Label("firstName", participant.getFirstName())); listItem.add(new Link("editParticipantLink") { public void onClick() { PageParameters registrationPageParameters = new PageParameters(); registrationPageParameters.put( Constants.C_PARTICIPANT_TO_EDIT_PARAM, participant); setResponsePage( new RegistrationPage(registrationPageParameters)); } }); } });
Mentre nel template ParticipantListPage.html il codice corrispondente sarà:
Second name label goes here
Second name goes here
First name label goes here
First name goes here
A runtime la pagina di login ha il seguente aspetto:
Figura 2 – Participant list page
La RegistrationPage è la pagina in cui è possibile registrare nuovi partecipanti o modificare gli attributi di quelli già registrati.
Anche la ParticipantListPage include un HeaderPanel e un FeedbackPanel.
Alla page viene aggiunto un Form che estende quello base di Wicket:
private class RegistrationForm extends Form { public RegistrationForm(String id) { super(id); } public RegistrationForm(String id, IModel model) { super(id, model); } @Override public void onSubmit() { Participant currentParticipant = getParticipantData(); if(!uploadPanel.getFilename().equals("")) { currentParticipant.setPictureName(uploadPanel.getFilename()); } WicketExamples2Application application = (WicketExamples2Application) (getRequestCycle().getApplication()); currentParticipant.setId( application.getParticipantManager().getParticipants().size()); application.getParticipantManager().getParticipants().add(currentParticipant); feedbackPanel.info("Participant succesfully saved!"); } }
Al form viene assegnato come modello un Participant:
registrationForm = new RegistrationForm("registrationForm", new Model(participant)); add(registrationForm);
La page si apre in modalità di new o di update in base alla valorizzazione del Model del form.
Al field country (paese di appartenenza di un participant) viene associata una org.apache.wicket.markup.html.form.DropDownChoice.
È il component Wicket per la selezione di un singolo valore (appartenente ad un set) da associare ad un field. Codice Java:
countryChoice = new DropDownChoice("countryChoice", new Model(participant.getCountry()), getCountries());
e HTML:
Al field talks (talks a cui un participant intende assistere), viene associata invece una org.apache.wicket.markup.html.form.ListMultipleChoice (poiche’ in questo caso è possibile scegliere più di un valore). Codice Java:
talkListChoice = new ListMultipleChoice("talkListChoice", new Model(participant.getCountry()), getTalks());
e HTML:
Dalla RegistrationPage è possibile associare anche una foto a un participant. Per l’upload di foto è stato implementato un Panel ad hoc, UploadPanel, all’interno del quale viene utilizzato il component org.apache.wicket.markup.html.form.upload.FileUploadField , il component per l’upload di file. L’UploadPanel viene incluso nella RegistrationPage.
Figura 3 – Registration page
Nell’applicazione appena illustrata la logica di persistenza è stata sostituita da una classe dummy. Questo sia perchè il focus dell’articolo è incentrato sull’uso vero e proprio di Wicket, cioè per l’implementazione del layer di presentation di una applicazione web, e sia perchè l’integrazione di Wicket con framework di persistenza sarà argomento di uno dei prossimi articoli (e merita necessariamente una trattazione a parte).
Il codice dell’applicazione di esempio può essere scaricato come allegato dal menu in alto a sinistra. L’archivio non comprende le librerie necessarie per la compilazione e/o il runtime. Le dipendenze sono le seguenti:
- wicket-1.4-m2.jar (compilazione e runtime)
- slf4j-api-1.5.0.jar (runtime)
- slf4j-jcl-1.5.0.jar (runtime)
Conclusioni
In questo secondo articolo sono stati illustrati i principali component del core del framework. Nel prossimo articolo faremo invece una panoramica sui component extension. La trattazione di tali component è stata separata da quella dei core perchè la comprensione di questi ultimi, fondamentale per poter acquisire la completa padronanza di Wicket ed essere in grado di implementare applicazioni complesse, non sarebbe risultata chiara a chi si avvicina per la prima volta al framework.
Riferimenti
[1] Karthik Gurumurthy, “Pro Wicket”, Apress, 2006
[2] Sito ufficiale di Wicket presso Apache
http://wicket.apache.org/
Guglielmo Iozzia si è Laureato nel 1999 in Ingegneria Elettronica (indirizzo Biomedico) presso l‘Università di Bologna. Ha progettato e realizzato un software diagnostico per la predizione dell‘andamento della pressione intracranica in pazienti in terapia intensiva neurochirurgica. Frequenta il mondo Java dall‘inizio del 2000. Dopo numerose esperienze presso un‘azienda di Bologna del settore IT (fino all‘aprile del 2006), e per qualche mese in una analoga società di Roma, ha scelto la libera professione, lavorando per RAI Net fino ai primi mesi del 2008. In seguito è diventato Senior IT Consultant per la FAO (Food and Agriculture Organization of the United Nations). In ambito FAO ha dedicato quasi tutto il 2012 al progetto GRMS (Global Resources Management System) in qualità di "Web Services and cross-environmental integration specialist". Da luglio 2013 si è trasferito a Dublino, dove ricopre il ruolo di SVT Automation Engineer per IBM Ireland.