Exkurz do templatovacích enginů v Javě

Templatovací jazyky v Javě mají poměrně dlouhou minulost. První a zřejmě nejznámnější jsou JSP, které jsou součástí javy. Jsou nejstarší z rodiny templatovacích jazyků a přestože jsou masivně používány dodnes, mnoho lidí k nim má své výhrady:

  • psaní JSP je obtížné pro ne-java programátory - přestože původní myšlenkou bylo, aby JSP psali odborníci na web (tedy "webdevelopeři") tato myšlenka zcela jistě minula realitu; praxe je taková, že JSP píší z různých důvodu opět Java developři, jejichž je jednak nedostatek a jednak jejich zaměření je spíš na aplikační kód než na validitu a použitelnost HTML výstupu
  • JSP stránky nejsou použitelné, díky životnímu cyklu JSP (JSP -> .java -> .class), mimo servletový kontejner - to znamená, že teprve servletový kontejner po nadeployování web aplikce JSP převede na implementace Servletů a přeloží je do binární podoby. Dopady tohoto mechanismu jsou poměrně jasné
    • JSP stránky mají své pevné umístění - hledají se vždy na filesystemu v adresáři web aplikace; nelze je umístit na classpath (a učinit je tak součástí přenositelných knihoven), nelze je načítat z jiného zdroje - např. databáze (a umožnit tak vznik nových stránek za běhu na systémech, kde nemáme přístup na filesystém), nelze definovat jakoukoliv složitější logiku načítání stránek krom dodání Erorr 404 stránky (např. custom logika ve smyslu neexistuje-li primární šablona, použij záložní, neexistuje-li ani ta, zobraz chybu)
    • JSP stránky není možné jednoduše testovat - jejich výstup získáme teprve až dotazem na servletový kontejner
    • JSP nelze použít pro skládání jiného výstupu než do web browseru - např. pro skládání těl emailů musíme volit jiný templatovací engine
    • debugování JSP nebylo poměrně dlouho možné a ani dnes to není zcela samozřejmá a jednoduchá věc (co se setupu týče)
    • chybové hlášky JSP stránek jsou při určitém (často standardním) nastavení kontejnerů nečitelné (vztahují se k vygenerovaným servletům a nikoliv k původní template) - změna tohoto nastavení typicky vyžaduje stop/start kontejneru
  • JSP stránky díky možnosti psaní scriptletů vybízejí k míchání aplikační logiky do prezentační vrstvy - s tím se myslím setkal každý z nás a leckdo se k tomu někdy i uchýlil
  • v případech některých kontejnerů je při změně JSP a požadavku na její opětovné vyrenderování znatelná časová prodleva - kompilace JSP vyžaduje nějaký čas (možná se jedná jen o zlomky vteřiny, maximálně vteřiny, ale při ladění nějakých drobnostní na stránce se jakákoliv prodleva počítá)

Reakcí na zmíněné nevýhody byl vznik interpretovaných templatovacích jazyků. V tomto příspěvku bych se chtěl podívat na zoubek dvěma nejznámnějším - Apache Velocity a FreeMarkeru.

Apache Velocity

Technorati Profile

Jako první si vezmu na paškál Velocity. Nevím, zda je to pouze moje zkušenost, ale zdá se mi, že Apache Velocity je hned po JSP nejpoužívanějším templatovacím jazykem v Javě. Možná je to dáno tím, že je vývojově starší a také proto, že je vždy propagováno jako velmi jednoduché k osvojení.

Skutečně Velocity je velmi jednoduché a dokážete jej nasadit a používat týž den, kdy s tímto jazykem začnete. Velocity interpretuje rovnou originální šablonu (respektive si z šablony vytváří vlastní AST) - bez mezikroku kompilace do Java kódu, což urychluje první zobrazení po změně.

Pro vývojáře je podstatné, že umožňuje dodat vlastní implementaci ResourceLoaderu - respektive vybrat si nejvíce vyhovující z již existujících implementací (ClasspathResourceLoader, DataSourceResourceLoader, FileResourceLoader, JarResourceLoader, StringResourceLoader, URLResourceLoader).

Pokud se zabýváte modulárními web systémy, brzy narazíte na problém, jak spolu s modulem dodávat jeho prezenační vrstvu. Jedním z možných rešení (avšak velmi nepěkným) je mergování WAR archívů popsané v postu na SoftEU blogu. Druhým je použití templatovacího nástroje, který vám umožní dodávat šablony z classpath modulu, který pak může zůstat obyčejným JARem. Rozhraní ResourceLoaderu vám však umožňuje daleko víc - načítání šablon může být klidně i podmínečné, takže jednoduše dokážete implementovat logiku načítání šablon podle priority (např. nejdříve se hledá šablona na disku a teprve, když není nalezena bere se výchozí šablona z classpath). To všechno jsou věci, které s JSP nedokážete.

Templatovací jazyk je velmi jednoduchý a asi není jej třeba nějak do detailu rozebírat. Jednoduchá šablona ve Velocity může vypadat třeba takto:


<HTML>
    <HEAD>
      <TITLE>Hello World!</TITLE>
    </HEAD>
    <BODY>
      <CENTER>
      <B>We've reached $downloadCount of Firefox 3!</B>
      <BR/>
      We are proud to announce that we've achieved new Guiness record.
      <BR/>
      Top countries in download were:
      <TABLE>
        #foreach($country in $countryList)
          <TR>
            <TD>$velocityCount.</TD>
            <TD>$country</TD>
          </TR>
        #end
      </TABLE>
      <BR/>
      <I>Stay loyal!</I>
      </CENTER>
    </BODY>
</HTML>

Vygenerování výsledného výstupu z Javy může vypadat například takhle:


/*  first, get and initialize an engine  */
VelocityEngine ve = new VelocityEngine();
ve.init();
/*  next, get the Template  */
Template t = ve.getTemplate( "helloworld.vm" );
/*  create a context and add data */
VelocityContext context = new VelocityContext();
context.put("downloadCount", new Integer(10000000));
List topCountries = new ArrayList();
topCountries.add("United States of America");
topCountries.add("Germany");
topCountries.add("Spain");
context.put("countryList", topCountries);
/* now render the template into a StringWriter */
StringWriter writer = new StringWriter();
t.merge( context, writer );
/* show the World */
System.out.println( writer.toString() );

Velocity vám poskytne základní věci, které byste od šablonovacího enginu čekali: include (parse) direktivy pro skládání šablon, makra, jednoduchou práci s objekty, se kterými pracujete v Java - tzn. JavaBeanami apod. Pro přenášení dat do a z šablon slouží kontext, což je takový kbelík na data, se kterými se v šabloně pracuje - příjemné je, že i z šablony mohou vzejít nějaká data, o která byste mohli mít zájem v javovském kódu (a ty právě najdete ve zmíněném kontextu).

Knihovna má také již připravený vlastní servlet, který vyrenderuje výstup šablony přímo do HttpResponse.

Podívejme se však o vývojový stupeň dál.

Freemarker

Už předchozí věta je trochu hozením rukavice. Po zkušenosti s oběma knihovnami však musím konstatovat, že FreeMarker je skutečně o vývojovou etapu dál. Můžete od něj očekávat to samé co od velocity, ale řeší také plno nedostatků výše zmíněné knihovny. Na tyto nedostatky bych se chtěl podívat na následujících řádcích:

Detalní chybové hlášení

Jedním z palčivých problémů Velocity jsou chybová hlášení. Ještě nedávno byly ve Velocity problémem chybová hlášení - ani u základních chyb nebylo lehce rozeznatelné v jaké šabloně se jaká chyba vyskytuje. V tomto ohledu již Velocity udělalo pokrok, nicméně výstup FreeMarkerových chyb se mi zdá pro webdevelopry daleko srozumitelnější. Výhoda také je, že FreeMarker vypíše i stack volání jednotlivých template, takže se dá velmi jednoduše orientovat i v chybách uvnitř maker, nebo nestovaných šablon.

Užívání namespace pro linkované knihovny maker nebo JSP tagů

Ve velocity jsou všechna makra házena do na jednu kupu. Pokud byste se tedy vášnivě pustili do výraznějšího vývoje ve Velocity, za chvíli byste začali narážet na konflikty jmen. FreeMarker zavádí pro makra a proměnné namespace, přes které se na ně dá odkázat. To výrazně zpřehledňuje strukturu šablon.

Podpora JSP tagů

Jedna ze zásadních věcí ve FreeMarkeru je možnost používat JSP tagy. Pokud tedy přecházíte z JSP (jako jsme přešli my), jistě uvítáte možnost zachovat beze změny všechen kód v připravených JSP tazích. FreeMarker zvládne najít všechny tagy v odkazovaných TLD z web.xml a také dokonce všechny tagy v TLD souborech v META-INF na classpath. Jediné co FreeMarker neinterpretuje jsou JSP EL funkce definované v TLD. Tato funkcionalita nám ušetřila mraky času.

Direktivy pro práci s whitespace

Ve Velocity je velkým problémem dodržet vzhed výsledného textu ve smyslu odřádkování a odsazení. Velocity neumožňuje nijak odstraňovat nepotřebný whitespace, který ve výsledném textu nepůsobí dobře, ale ve v šabloně je kvůli přehlednosti naopak potřeba (v souvislosti se strukturou řídících direktiv apod.). FreeMarker tyto direktivy nabízí.

Metaprogramování

Ve FreeMarkeru můžete přiřadit kód šablony do proměnné, kterou pak dále používat. Interpretace obsahu proběhne pouze jednou a v dalších "použitích" se jen vypíše již interpretovaný obsah proměnné.

Jednoduše lze také interpretovat kód v libovolné proměnné - např. mějme třídu:


public class Test {
   public String getFreemarkerSnippet() {
      return "${x}";
   }
}

A potom freemarker šablonu (viz. dokumentace):


<#assign x = "something">
Test: ${test.freemarkerSnippet?interpret}

Výsledkem bude potom tento výstup:


Test: something

Výkon

FreeMarker je podle některých statistik rychlejší jak Velocity. Každopádně jak píší v dokumentaci k FreeMarkeru - FreeMarker pravděpodobně nikdy nebude úzkým hrdlem vaší aplikace. Nejnáročnější operací je parsování do AST, které je pak už ale cachované, takže se provádí už jen processing šablony a ten by se v rychlosti dal jistě s JSP srovnat. Pokud tedy narazíte na výkonnostní problémy - buď máte vypnutou cache, nebo bude váš problém ležet ve vrstvách níže.

Závěrem

Pokud jste nikdy nevyzkoušeli žádný z templatovacích enginů, doporučuji některý z nich určitě vyzkoušet. Pokud používáte Apache Velocity - což je pravděpodobnější případ - doporučuji rozhodně kouknout na FreeMarker, protože vás bude čekat příjemné překvapení. FreeMarker zvládnete nasadit za den, stejně jako Velocity, ale odmění se vám daleko širšími možnostmi použití a hlavně se vám s ním bude lépe pracovat.

Zajímavé jsou zkušenosti ostatních vývojářů, které lze na webu najít (pár jich uvádím v užitečných odkazech na konci článku). Poměrně zajímavé je konstatování, že valná většina lidí, která přešla od Velocity k FreeMarkeru už u FreeMarkeru zůstala. Ti kteří šli opačnou cestou od FreeMarkeru k Velocity se zase hodně rychle vrátili. Už jen to o něčem svědčí.

Zajímavé závěry lze také udělat z roadmapy k Velocity v. 2 - jistě si všimnete, že v plánovaných vylepšeních jsou věci, které ve FreeMarkeru už nějaký ten pátek máme.

Pro FreeMarker dostante podporu ve většině IDE. Do nejlepšího z nich ;-) se podpora teprve chystá (měla by být do konce roku 2008). Velocity nemá takové zastoupení v Java editorech, nicméně je daleko větší podpora v jednodušších editorech (které jsou naopak více používány mezi webaři). V tomto ohledu má tedy FreeMarker ještě co dohánět.

Užitečné odkazy