Velocity è un Template Engine che consente di eseguire il rendering di dati da applicazioni java, mantenendo separato il codice di rendering dal modello dei dati. Tale caratteristica lo rende particolarmente adatta allo sviluppo di applicazione web basate sul pattern MVC, ma la sua flessibilità gli consente di adattarsi a molti altri scopi, come la generazione di codice, la generazione e trasformazione di file XML e l’elaborazione dei flussi di testo. Questo articolo vuole introdurre sinteticamente Velocity Template Language (VTL) e fornire esempi su come utilizzare il framework.
Perché Utilizzarlo
Progettato come uno strumento per la creazione di template di facile utilizzo, Velocity è utile in qualsiasi area di applicazione Java che richiede la formattazione e la presentazione dei dati. Alcuni motivi per utilizzarlo sono:
- Si adatta a molte aree di applicazione con minimo sforzo.
- Offre una sintassi semplice e chiara per il designer di modelli.
- Offre un modello di programmazione semplice per lo sviluppatore.
- I modelli e il codice sono oggetti separati, che possono quindi essere sviluppati e manutenuti in modo indipendente.
- Il motore si integra facilmente in qualsiasi ambiente applicativo Java, in particolare con l’utilizzo delle servlet.
- Velocity consente ai modelli di accedere a qualsiasi metodo pubblico di oggetti dati inclusi nel contesto.
L’ultimo punto è particolarmente importante perchè significa che è possibile utilizzare nei template le stesse classi già implementate nel codice java.
Dipendenze
Per l’utilizzo del solo engine di Velocity richiede l’inclusione di una sola dipendenza nel progetto che, nel caso di Maven, sarà quindi la seguente:
1 2 3 4 5 |
<dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-engine-core</artifactId> <version>2.0</version> </dependency> |
Esistono poi diverse dipendenze a sotto-progetti, genericamente indicati come velocity Tools, che sono principalmente rivolte all’utilizzo del framework con altre tecnologie come servlet, Struts, Spring, etc.
Utilizzo
Il codice base per l’utilizzo del framework è molto semplice e, indipendentemente dalla tipologia di applicazione che si sta realizzando (un semplice applicativo o una applicazione web), le operazioni da eseguire sono sempre le stesse:
- Inizializzazione di Velocity (eseguita una volta sola).
- Creazione di un oggetto di contesto.
- Inserimento degli oggetti (i dati) al contesto.
- Scelta del template (modello).
- ‘Merge’ del template e dei dati per produrre l’output desiderato.
Relativamente al primo punto Velocity mette a disposizione due opzioni: l’utilizzo di un engine di tipo singleton, ovvero unico per tutta la JVM, o di uno dedicato, ovvero specifico dell’applicazione. In genere a meno di esigenze particolari, come la necessità di condividere la configurazione con altri applicativi nella stessa JVM, è preferibile il secondo modello che consente una maggiore flessibilità.
Il codice che implementa i 5 passi definiti sopra è quindi il seguente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public class Main { public static void main(String[] args) { /* first, get and initialize an engine */ VelocityEngine ve = new VelocityEngine(); ve.addProperty("file.resource.loader.path", "src/main/resources"); ve.init(); /* next, get the Template */ Template t = ve.getTemplate( "helloworld.velocity" ); /* create a context and add data */ VelocityContext context = new VelocityContext(); /* add data to the context */ .... /* now render the template into a StringWriter */ StringWriter writer = new StringWriter(); t.merge( context, writer ); /* show the generated output */ System.out.println( writer.toString() ); } } |
Si noti che è stato necessario valorizzare la variabile file.resource.loader.path
per indicare al framework dove sono posizionati nel progetto i template utilizzati.
Velocity Template Language (VTL)
Attraverso il suo linguaggio di templating Velocity consente di incorporare in modo semplice contenuto dinamico ai modelli di contenuto. Supponiamo ad esempio di voler generare una pagina HTML, la prima cosa che dobbiamo stabilire è il template di pagina e successivamente inserire il contenuto dinamico nei giusti punti del modello. Vediamo brevemente come farlo introducendo le principali direttive del linguaggio VTL.
Variabili
Un primo semplice modo per inserire contenuto nel template è quello di utilizzare variabili. La notazione abbreviata di una variabile consiste in un carattere dollaro $ iniziale seguito da un identificatore VTL. Un identificatore VTL è una sequenza di caratteri che inizia con un carattere alfabetico (a..z o A..Z) seguito da numeri, lettere, trattino (-) o undescore (_).
Supponiamo ad esempio di voler valorizzare dinamicamente il tag <title>
della nostra pagina HTML. Creiamo quindi nella directory resources
un template base del tipo:
1 2 3 4 5 6 7 8 9 |
<!DOCTYPE html> <html> <head> <title>$site</title> </head> <body> ##Body Content </body> </html> |
Il valore assunto dalla variabile $site
è definito attraverso il contesto Velocity. Si tratta di un concetto fondamentale del framework che consente di trasferire i dati dal layer java a template. Sebbene il programmatore sia libero di implementare una propria specifica classe di contesto, è consigliabile utilizzare l’implementazione di default VelocityContext
, il cui utilizzo è semplice come utilizzare una normale classe Java Hashtable. Tornando al nostro esempio, per la valorizzazione la variabile $site
è sufficiente il seguente codice:
1 2 3 |
/* create a context and add data */ VelocityContext context = new VelocityContext(); context.put("site", "Java Boss"); |
1 2 3 4 5 6 7 8 |
<!DOCTYPE html> <html> <head> <title>Java Boss</title> </head> <body> </body> </html> |
Si noti che il commento #Body Content
non compare nell’output generato, in quanto tutti i commenti vengono ignorati.
Oggetti
Altra possibilità è quella di riutilizzare oggetti java già definiti all’interno del programma. Il linguaggio VTL consente di accedere alle proprietà, ma anche ai metodi, dell’oggetto in modo semplice ed intuitivo. Supponiamo ad esempio di disporre di un oggetto Article
caratterizzato dalle proprietà title
e url
:
1 2 3 4 5 6 |
public class Article { private String title; private String url; /* Getter and Setter */ } |
Una volta generato l’oggetto associato è possibile inserirla nel contesto velocity nello stesso modo visto nel caso di una qualsiasi variabile primitiva.
1 2 |
Article article = new Article("Velocity", "http://www.javaboss.it/?p=4274"); context.put( "article", article ); |
L’accesso alle sue proprietà all’interno del template avviene in modo semplice attraverso gli identificativi $article.title
e $article.url
, ma anche mediante $article.getTitle()
e $article.getUrl()
. Il template diviene quindi:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<!DOCTYPE html> <html> <head> <title>$site</title> </head> <body> ##Body Content ## This is the title <h1>Welcome to the fantastic blog $site</h1> <p1> Please reed this article to the following link <a href="$article.url">$article.getTitle()</a> </p1> </body> </html> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<!DOCTYPE html> <html> <head> <title>Java Boss</title> </head> <body> <h1>Welcome to the fantastic blog Java Boss</h1> <p1> Please reed this article to the following link <a href="http://www.javaboss.it/?p=4274">Velocity</a> </p1> </body> </html> |
Collection
Una caratteristica importante del linguaggio VTL è quella di poter gestire le Collection e di poter quindi iterare su di esse attraverso la direttiva #foreach()
. Anche gli Array
sono supportati ma wrappati dal framework in un oggetto che supporta l’interfaccia Iterator
. Questo implica che le Collection
e per gli Array
possono essere utilizzati nei template allo stesso modo: ad esempio è possibile per entrambi accedere ai metodi size()
, isEmpty()
e get()
, oppure utilizzare la notazione [i]
per leggere l’i-esimo elemento.
Torniamo al nostro esempio e supponiamo di voler stampare sulla pagina HTML il nome degli autori con un particolare ringraziamento al primo nella lista. Innanzitutto generiamo ed inseriamo la lista degli autori nel contesto Velocity.
1 2 3 4 5 6 |
List<String> authors = new ArrayList<String>(); authors.add( "Matteo" ); authors.add( "Luca" ); authors.add( "Giovanni" ); context.put( "authors", authors ); |
#foreach #end
:
1 2 3 4 5 6 7 8 9 |
<p1>Blog authors are: <ul> #foreach( $author in $authors ) <li>$author</li> #end </ul> </pi> But a special thanks goes to $authors[0]. |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
<!DOCTYPE html> <html> <head> <title>Java Boss</title> </head> <body> <h1>Welcome to the fantastic blog Java Boss</h1> <p1> Please reed this article to the following link <a href="http://www.javaboss.it/?p=4274">Velocity</a> </p1> <p1>Blog authors are: <ul> <li>Matteo</li> <li>Luca</li> <li>Giovanni</li> </ul> </pi> But a special thanks goes to Matteo. </body> </html> |
Condizioni
Un’altra utile direttiva di Velocity è #if
che consente di includere o meno del testo nell’output prodotto in base al valore assunto dall’espressione di test. In generale tale direttiva assume la forma:
1 2 3 4 5 6 7 |
#if ( ... ) ... #elseif( ... ) ... #else ... #end |
Nelle condizioni di test è possibile specificare direttamente variabili o metodi che ritornino valori boolean, ma anche condizioni di confronto tra variabili oppure congiunzioni (AND), disgiunzioni (OR) e negazioni (NOT) di altre espressioni. Supponiamo ad esempio di voler condizionare la stampa degli autori del blog al fatto che la lista non sia vuota. Il template sopra definito diviene quindi:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#if ( !$names.isEmpty() ) <p1>Blog authors are: <ul> #foreach( $author in $authors ) <li>$author</li> #end </ul> </pi> But a special thanks goes to $authors[0]. #else No author to thanks for the blog #end |
Codice Sorgente
Il codice sorgente con tutti gli esempi mostrati è scaricabile qui velocity