MokaByte 83 - Marzo 2004 
Introduzione a Tapestry

di
Pietro Ferrara
Tra i tanti prodotti Jakarta disponibili, Tapestry è l'ultimo nato ed è sicuramente quello su cui si è detto e scritto meno. Andiamo quindi a vederne le linee generali, la filosofia che l'ha ispirato e quali sono le strutture portanti di questa neonata tecnologia.

Introduzione
Tapestry è un ambiente di sviluppo nato un paio di anni fa, e che dal maggio 2003 è entrato a far parte di Jakarta. Si tratta di uno strumento rivolto in particolar modo alla produzione di siti Internet particolarmente grandi e complessi, in cui la fase di sviluppo richieda la presenza di due team distinti (uno team di sviluppatori ed uno di grafici). Tuttavia non si limita solamente al Web, ma punta, grazie ad una estrema flessibilità, ad adattarsi ad altri linguaggi a tag già esistenti (ad esempio esistono delle librerie per lavorare con wml) o futuri.
Le applicazioni Tapestry richiedono la definizione, attraverso degli appositi documenti xml, delle pagine che fanno parte dell'applicazione e delle componenti presenti in ciascuna pagina. L'obiettivo di fondo è quello di ottenere dei template grafici estremamente puliti e che siano testabili anche a livello statico (ovvero senza la necessità di aver già sviluppato le parti dinamiche del sito, quali ad esempio le interrogazioni a database).

 

Tapestry: cos'è, quando nasce e perchè
Tapestry è costituito essenzialmente da alcune librerie all'interno del package net.sf.tapestry. All'interno di questo esistono diversi sottopackage, ciascuno dei quali svolge un preciso compito.
Tapestry non è un framework per lavorare solamente con documenti html. Si tratta anzi un approccio completamente generico e slegato da qualsiasi linguaggio a tag. A partire da questo approccio generico si sono sviluppati poi dei sottopackage specializzati in alcuni linguaggio a tag particolarmente diffusi. In particolare oggi esiste una libreria per la creazione e la gestione di documenti html e una per la gestione di documenti wml. In futuro però Tapestry potrà essere esteso a qualsiasi linguaggio a tag.
Tapestry non è un linguaggio di scripting; si tratta anzi di un progetto che vuole profondamente prendere le distanze da questi linguaggi (ed in particolar modo dalle JavaServer Pages) e dalla loro filosofia.
L'idea alla base di Tapestry è di dividere il lavoro dei disegnatori grafici di un sito da quello degli sviluppatori Java e di permettere ai due gruppi di procedere parallelamente con l'implementazione. Uno dei principali scopi di Tapestry è quindi quello di ridurre al minimo l'impatto del codice implementativo all'interno dei file html (mentre invece nei linguaggi di scripting parti statiche html e parti dinamiche sono continuamente confuse e inserite in uno stesso documento).

<%
String nome = (String)session.getAttribute("nome");
%>

<p>Benvenuto <%=nome%></p>

"Benvenuto utente" con JavaServer Pages

<p>Benvenuto <span jwcid="insertNome">Pietro Ferrara</span></p>

"Benvenuto utente" con Tapestry

Come si può vedere dall'esempio, mentre un linguaggio di scripting modifica sensibilmente la pagina html (che in questo modo è difficilmente editabile con un editor html) anche per una semplice operazione di lettura e scrittura di una variabile, l'impatto di Tapestry è minimo. Visualizzando la pagina di Tapestry con un editor html la si visualizzerà correttamente. Ma cosa accade quando questa pagina viene interpretata dal server?

 

Struttura di un'applicazione Tapestry
La chiave di Tapestry sono gli attributi jwcid (acronimo per Java Web Component ID). Questi attributi (che vengono ignorati dagli editor html) servono ad indicare che si tratta di parti dinamiche della pagine, e richiamano la componente con il nome riportato; a sua volta la componente è collegata ad una classe java. Il comportamento a livello dinamico può essere estremamente diverso in base al tipo di componente che viene richiamata (analizzeremo in seguito le diverse componenti esistenti e come crearne ex novo). Nel nostro caso, quanto scritto tra i due tag <span> viene rimosso e al suo posto viene inserito quanto restituito dalla componente insertNome.
Tutte le componenti devono essere definite in un apposito file .page. Questo file conterrà anche tutti i riferimenti a classi Java. Per poter interagire con le classi java Tapestry si appoggia sulla struttura dei JavaBeans. A ciascun file .page è associato un template html (nel caso in cui si stia lavorando con le librerie html) che serve a definire la struttura grafica della pagina e dove inserire le diverse componenti.
I file page poi per poter essere inseriti in una applicazione devono essere definiti all'interno di un file .application. Quando un utente si collegherà al sito, un'apposita servlet richiamerà il file .application. Questo verrà analizzato da apposite classi del package di Tapestry.


Figura 1 - Struttura di un'applicazione Tapestry
(clicca sull'immagine per ingrandire)

I JavaBeans
Apro qui una parentesi per spiegare l'architettura dei JavaBeans, elementi essenziali per lo sviluppo di siti internet con Tapestry e per permettere la comunicazioni tra le componenti e le classi java.
L'idea che sta alla base dei JavaBeans è di manipolare oggetti Java senza conoscerne a priori il tipo. Per poter accedere a questi dati è necessario rispettare le seguenti regole di naming nel definire i diversi metodi:

public type getName (){

}

public void setName (type valore){

}

In questo modo Tapestry potrà accedere ai valori contenuti nella classe tramite il metodo getName, mentre potrà settarne il valore tramite setName.
E' importante sottolineare che non è necessario che ad ogni coppia setName e getName corrisponda un attributo Name relativo; questi metodi infatti potrebbero effettuare una serie di operazioni su più attributi o valori presi dall'esterno (ad esempio da un database) prima di restituire un valore o settare uno o più attributi.

public Boolean isName (){

}

Esiste infine un metodo per lavorare con variabili booleane e che è tipicamente utilizzato da componenti che impostano il loro comportamento in base ad una condizione (ad esempio le componenti di tipo Conditional).

 

ApplicationServlet
In Tapestry le servlet sono state altamente automatizzate e la loro implementazione è estremamente ridotta. La navigazione tra le diverse pagine è gestita in automatico dalle librerie Tapestry, tramite una serie di parametri standard.
L'unico ruolo delle servlet è di richiamare un file application. In pratica quindi servono ad indicare dove risiede la definizione dell'applicazione all'interno dell'apposito metodo "protected String getApplicationSpecificationPath()"

import net.sf.tapestry.ApplicationServlet;
public class ProvaServlet extends ApplicationServlet{
  protected String getApplicationSpecificationPath() {
    return "Prova.application";
  }
}

Esempio di ApplicationServlet
Come si vede il codice all'interno della servlet è ridotto al minimo. La classe deve estendere ApplicationServlet (che analizzerà quanto contenuto nel file .application) contenuta all'interno del package net.sf.tapestry e non la classica HttpServlet contenuta in javax.servlet.http.
Come già detto, la definizione dell'applicazione è contenuta all'interno di un file application che serve a definire lo spazio dei nomi delle pagine. Ecco un esempio di file application

<application name="Prova" engine-class="net.sf.tapestry.engine.SimpleEngine">
<page name="Home" specification-path="Prova.page"/>
<page name="Menu" specification-path="Menu.page"/>
<page name="Principale" specification-path="Principale.page"/>
</application>

La struttura di un file application è di tipo xml. E' necessario definire un nome dell'applicazione (che corrisponderà al nome del file .application in cui vengono salvate queste informazioni) e associarci un "motore". Per l'applicazione che andremo a vedere è più che sufficiente utilizzare la classe SimpleEngine presente nel package net.sf.tapestry.engine (è l'unica classe motore che viene fornita insieme a Tapestry). Tuttavia è possibile definire dei motori propri estendono la classe AbstractEngine (e definendo i metodi protected void cleanupAfterRequest(IRequestCycle cycle) e Collection getActivePageNames() ).
Per ciascuna pagina è poi necessario legare un nome a ciascun file page (richiamato all'interno del parametro specification-path). E' importante che esista una pagina denominata Home, che sarà la pagina di partenza quando un utente verrà a visitare il sito. Non è quindi possibile accedere direttamente delle pagine interne all'applicazione ma è necessario entrare da tale pagina.

 

Definizione delle pagine (file page)
Come per l'applicazione, anche le pagine vengono definite in file xml. All'interno di questo file vengono definite diverse componenti che saranno richiamate all'interno dei template html. Ecco un esempio di definizione di una pagina

<page-specification class="net.sf.tapestry.html.BasePage">
<component id="componente1" type="…">
</component>
<component id="componente2" type="…">
</component>
………………………
<component id="componenten" type="…">
</component>
</page-specification>

A ciascuna pagina è necessario associare una classe che estenda net.sf.tapestry.AbstractPage. Nel nostro caso (ovvero per lavorare con template html) associamo la nostra pagina alla classe net.sf.tapestry.html.BasePage. Se ad esempio volessimo definire un file wml dovremo associare la nostra pagina alla classe net.sf.tapestry.wml.Deck. E' quindi in questa fase che Tapestry associa l'applicazione ad un particolare linguaggio a tag.
Spesso però non è sufficiente utilizzare la classe BasePage; per inserire parti dinamiche all'interno della pagina infatti è necessario estendere questa classe e definire diversi metodi per l'interazione tra le componenti e le classi java (la spiegazione di ciò viene rimandata al capitolo sulle componenti e sui binding dinamici).

 

Template (file html)
I template html sono pagine html che possono essere visualizzate da un normale editor e che contengono, lì dove vi è la necessità di inserire contenuti dinamici, tag con attributi jwcid (come visto in precedenza). Il comportamento a livello dinamico dipenderà poi dal tipo di componente definito: ad esempio, nel caso in cui venga richiamata una componente di tipo Insert, quanto contenuto tra i due tag verrà rimosso e sostituito con quanto trovato a livello dinamico.
A ciascun file page è associato un file html. Lo standard di Tapestry impone che il nome del file page sia lo stesso di quello del template. Anche si sta lavorando con file non html (ad esempio wml) il template dovranno essere comunque denominati con estensione html.
Una funzionalità estremamente interessante dei template html è la possibilità di settare il linguaggio in cui si vuole visualizzare il sito Internet. Questà possibilità è implementata a partire dalla classe java.util.Locale (da cui vengono prese le costanti per la definizione dei diversi linguaggi), lavorando poi sulla classe IPropertySelectionModel (che permette di inserire all'interno di una pagina tutti i linguaggi desiderati e di settare il linguaggio attualmente in uso).
Una volta settato il linguaggio, Tapestry cercherà ricorsivamente il template html in base ad una serie di regole standard di naming. Supponiamo ad esempio che si voglia visualizzare la pagina francese di un template HTML di index.html. Tapestry cercherà prima la pagina index_fr.html, e nel caso in cui non troverà tale pagina si sposterà ricorsivamente su index.html. E' possibile anche definire più sottolinguaggi, ad esempio una persona spagnola che parla brasiliano (nel cui caso verrà cercato prima index_es_BR.html, poi index_es.html, infine index.html).

 

Conclusioni
Tapestry è una tecnologia estremamente interessante. Si tratta infatti di un ambiente che cerca di aderire alle nuove tendenze (ed in particolar modo all'xml) e di essere il più possibile flessibile, sia rispetto alle tecnologie già esistenti, sia rispetto a quelle possibili in futuro. I risultati ottenuti a prima vista sono sicuramente stimolanti, anche se è necessario utilizzare più da vicino questo prodotto per rendersi conto dei suoi limiti e delle sue potenzialità. Certo è che, trattandosi di un prodotto ancora giovane, sarà necessario seguire da vicino la sua evoluzione per capire quali potranno essere i suoi sviluppi futuri.

 

Bibliografia
[1] Howard Lewis Ship - "Tapestry Tutorial", http://www.jakarta.org/tapestry
[2] Howard Lewis Ship - "Tapestry API", http://www.jakarta.org/tapestry
[3] Howard Lewis Ship - "Tapestry Developer's Guide ", http://www.jakarta.org/tapestry
[4] Howard Lewis Ship - "Tapestry Component Reference", http://www.jakarta.org/tapestry

MokaByte® è un marchio registrato da MokaByte s.r.l. 
Java®, Jini® e tutti i nomi derivati sono marchi registrati da Sun Microsystems.
Tutti i diritti riservati. E' vietata la riproduzione anche parziale.
Per comunicazioni inviare una mail a info@mokabyte.it