Template con Apache Velocity
Apache Velocity è un sistema di template semplice e veloce. In questo articolo vediamo le caratteristiche principali e come utilizzarlo con i principali framework MVC.
In molte occasioni, invece di reinventare la ruota, Velocity può essere utilizzato all’interno dei nostri programmi con estrema semplicità. Il principio di funzionamento di questa libreria è abbastanza simile a prodotti simili che permettono di creare template come JasperReport, FreeMarker etc. etc. Abbiamo un template, dove è presente un modello da restituire mischiato a delle variabili. A runtime passiamo questo template a Velocity, fornendo il valore di queste variabili e quindi avremo il risultato finale.
Primi template
Come sempre vediamo la stampa del classico Hello World utilizzando Velocity. Nel nostro progetto andremo a definire la seguente dipendenza Maven
<dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity</artifactId> <version>1.7</version> </dependency>
Ora dobbiamo definire è un template che verrà utilizzato nel esempio
Hello $stringa
Il testo contiene una variabile, identificata dal prefisso $. Ora vediamo come utilizzare Velocity per richiamare il template helloword.vm e passargli la variabile $stringa
package com.javastaff.velocitytest; import java.io.StringWriter; import org.apache.velocity.Template; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.VelocityEngine; import org.apache.velocity.runtime.RuntimeConstants; import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader; public class HelloWorldVelocity { public static void main(String[] args) { VelocityEngine velocityEngine = new VelocityEngine(); velocityEngine.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath"); velocityEngine.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName()); velocityEngine.init(); Template template = velocityEngine.getTemplate("helloworld.vm"); VelocityContext context = new VelocityContext(); context.put("stringa", "world"); StringWriter writer = new StringWriter(); template.merge(context, writer); System.out.println(writer); } }
Il caricamento del file template avviene utilizzando il classpath, dove andremo a posizionare helloworld.vm. VelocityContext rappresenta invece la parte di dati che si possono associare al template e in questo caso andiamo ad aggiungere una semplice variabile “stringa”. Infine effettuiamo il merge tra context e template, riversando il risultato in uno StringWriter che poi potremo utilizzare per visualizzare il risultato.
E’ possibile istanziare l’engine di Velocity in due modi differenti, singleton o separate instance. Nel primo caso avremo una sola istanza nella nostra JVM, mentre nel secondo caso (come nell’esempio che abbiamo appena riportato) viene creata un’istanza specifica dell’engine. Quest’ultima modalità può essere utile quando abbiamo differenti modi d’utilizzo di Velocity all’interno del nostro programma, che vengono specificate attraverso il settaggio di alcune proprietà.
Altro caso comune è il modo differente in cui vengono reperiti i template. Nel caso primo esempio questi sono stati recuperati dal classpath ma ad esempio potremmo avere l’esigenza di caricarli dinamicamente perchè memorizzati su database. Nel prossimo esempio potete vedere utilizzare Velocity con un template recuperato da un InputStream
package com.javastaff.velocitytest; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.StringWriter; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.Velocity; public class LoadInputStream { public static void main(String a[]) { VelocityContext context = new VelocityContext(); context.put("input", "una stringa"); StringWriter swOut = new StringWriter(); Reader templateReader = new BufferedReader( new InputStreamReader(load())); Velocity.evaluate(context, swOut, "logTag", templateReader); System.out.println(swOut); } public static InputStream load() { String template=new String("Caricato da $input"); ByteArrayInputStream bais= new ByteArrayInputStream(template.getBytes()); return bais; } }
Direttive
All’interno dei template è possibile utilizzare una serie di direttive che ci permettono di manipolare i dati che dobbiamo visualizzare. Di seguito è riportata la lista delle direttive presenti in Velocity
- #set() : definizione di variabili
- #if() : espressione condizionale
- #else : espressione condizionale
- #elseif() : espressione condizionale
- #end : espressione che permette di chiuderne un’altra aperta precedentemente (foreach,macro,if)
- #foreach() : istruzione che permette di scorrere una lista di oggetti
- #include() : include un’altra risorsa, in maniera statica
- #parse() : include un’altra risorsa che può essere un template
- #macro() : permette la definizione di macro che posso essere richiamate nel template
Per capire meglio alcune direttive vediamo subito un esempio di template dove ce ne sono diverse
#parse( $header ) #if( $livello >= 7 ) Caro dirigente #elseif( $livello <= 5 ) Caro sottoposto #else Caro impiegato #end Ecco una lista di oggetti #foreach( $item in $itemList ) $item #end #include ($footer)
All’inizio e alla fine del template sono presenti due direttive, parse e include, che aggiungono altre risorse. Nel caso di parse, la risorsa inclusa è a sua volta un template che verrà valutato. Successivamente c’è l’impiego dei costrutti condizionali if/else/elseif, che in questo caso vengono utilizzati per stampare una diversa frase in base al valore di una variabile. Infine c’è il foreach che come in Java ed altri linguaggi scorre una lista di oggetti e li stampa semplicemente. Di seguito il codice che possiamo utilizzare per passare i valori a questo template
import java.io.StringWriter; import java.util.ArrayList; import java.util.List; import org.apache.velocity.Template; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.VelocityEngine; import org.apache.velocity.runtime.RuntimeConstants; import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader; public class Direttive { public static void main(String a[]) { VelocityEngine velocityEngine = new VelocityEngine(); velocityEngine.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath"); velocityEngine.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName()); velocityEngine.init(); Template template = velocityEngine.getTemplate("direttive.vm"); VelocityContext context = new VelocityContext(); context.put("header", "header.vm"); context.put("livello", 6); context.put("luogo", "Roma"); context.put("data", "08/06/2015"); List<String> itemList=new ArrayList<String>(); itemList.add("Item1"); itemList.add("Item2"); itemList.add("Item3"); context.put("itemList", itemList); context.put("footer", "footer.vm"); StringWriter writer = new StringWriter(); template.merge(context, writer); System.out.println(writer); } }
Macro
Definire le macro all’interno dei template ci permette di avere raggruppate delle funzioni che è possibile richiamare. Questo può tornare utile quando appunto bisogna riutilizzare porzioni simili di codice per eseguire una certa operazione. Partiamo da un esempio in cui abbiamo una classe Utente con una serie di informazioni e una lista di questi utenti che dobbiamo visualizzare. Il codice Java potrebbe essere il seguente
package com.javastaff.velocitytest; import java.io.StringWriter; import java.util.ArrayList; import java.util.List; import org.apache.velocity.Template; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.VelocityEngine; import org.apache.velocity.runtime.RuntimeConstants; import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader; public class Macro { public static void main(String a[]) { VelocityEngine velocityEngine = new VelocityEngine(); velocityEngine.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath"); velocityEngine.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName()); velocityEngine.init(); Template template = velocityEngine.getTemplate("macro.vm"); List<Utente> utenteList=listUtenti(); VelocityContext context = new VelocityContext(); context.put("utenteList", utenteList); StringWriter writer = new StringWriter(); template.merge(context, writer); System.out.println(writer); } public static List<Utente> listUtenti() { List<Utente> utenteList=new ArrayList<Utente>(); Utente utente=new Utente(); utente.setCognome("Rossi"); utente.setNome("Mario"); utente.setCodiceFiscale("1234567890"); utenteList.add(utente); Utente utente2=new Utente(); utente2.setCognome("Bianchi"); utente2.setNome("Andrea"); utente2.setCodiceFiscale("0987654321"); utenteList.add(utente2); return utenteList; } }
All’interno del template dovremo scorrere la lista di utenti e visualizzare le informazioni. Per effettuare questa stampa di informazioni è possibile appunto utilizzare una macro, definita come nel seguente template
#macro (visualizza $utente) Nome: $utente.getNome() Cognome: $utente.getCognome() Codice Fiscale: $utente.getCodiceFiscale() #end Dettagli utenti ------------ #foreach ($utente in $utenteList) #visualizza($utente) #end ------------
Spring MVC e Velocity
Velocity, come altri sistemi di template, può essere utilizzato nella generazione delle pagine all’interno di progetti web. All’interno della configurazione di Spring MVC dobbiamo andare ad aggiungere un nuovo resolver come riportato di seguito
<bean id="velocityConfig" class="org.springframework.web.servlet.view.velocity.VelocityConfigurer"> <property name="resourceLoaderPath" value="/WEB-INF/views/" /> </bean> <bean id="viewResolver" class= "org.springframework.web.servlet.view.velocity.VelocityLayoutViewResolver"> <property name="cache" value="true" /> <property name="prefix" value="" /> <property name="layoutUrl" value="layout.vm"/> <property name="suffix" value=".vm" /> </bean>
Le viste che utilizzeranno il resolver di Velocity avranno un layout comune, layout.vm, che potrebbe essere quello riportato
<html> <head> <title>Template Velocity</title> </head> <body> $screen_content </body> </html>
$screen_content sarà popolato con il valore del template che utilizzeremo.
Struts e Velocity
Struts2 per quanto riguarda la parte di View, gestisce già per default Velocity. Infatti all’interno del file struts-default.xml è già configurato il return type relativo a Velocity. Quindi per gestire una vista con un template Velocity possiamo utilizzare semplicemente questa definizione
<result name="success" type="velocity"> <param name="location">prova.vm</param> </result>

Looking for a right “about me”…
Commenti recenti