Axional Server
provides integrated support for
Apache FreeMarker templates.
A template processor (also known as a template engine or template parser)
is able to combine one or more templates with a data model to produce
a resulting document.
1 Overview
FreeMarker is a template engine: a Java library to generate text output
(HTML web pages, e-mails, configuration files, source code, etc.) based on templates
and changing data. Templates are written in the FreeMarker Template Language (FTL
),
which is a simple, specialized language (not a full-blown programming language like PHP
).
You meant to prepare the data to display in a real programming language,
like issue database queries and do business calculations, and then the template
displays that already prepared data. In the template you are focusing on how to
present the data, and outside the template you are focusing on what data to present.
This approach is often referred to as the MVC
(Model View Controller) pattern,
and is particularly popular for dynamic web pages. It helps in separating web
page designers (HTML authors) from developers (Java programmers usually).
Designers won't face complicated logic in templates, and can change the
appearance of a page without programmers having to change or recompile code.
2 Templates vs JSP
JSPs are a fine concept, but they take the joy out of web development. Having to breakup page templates into separate files: header.jsp and footer.jsp, not being able to call methods in the expression language, and not being able to combine and arrange page parts at runtime makes JSP difficult to use in some scenarios.
Much like JSP, FreeMarker compiles templates to improve runtime performance. Those templates are stored in memory in a format that leaves placeholders for dynamic data. The controller fills those placeholders with dynamic data from the model. For this purpose, the controller must have access to both the model and the view. Once the dynamic data is retrieved from the model, the controller will print the template to a given output stream.
2.1 No Servlet Container need
It does not require servlet container, so you can also render e-mails or anything text based.
2.2 No Classloading PermGen Issues
Since FreeMarker templates aren’t compiled to classes, they don’t take up permgen space and don’t require a new class loader to reload.
2.3 Template Loaders
FreeMarker allows to load your pages and templates from a central place like a database. You can create your own implementation or use one of the built-in classes.
-
ClassTemplateLoader
– loads templates from the classpath. -
FileTemplateLoader
– loads templates from a specific folder in the file system. -
StringTemplateLoader
– loads templates from a Map of strings. -
URLTemplateLoader
– loads templates from a URL. You’ll have to implement the getURL method, but that should be easy to do. -
WebappTemplateLoader
– loads templates from a servlet context.
2.4 Templates Can Be Nested At Runtime
FreeMarker let’s you create real templates, not just fragments — remember the header and footer JSPs? It does this by allowing you to take one template (head.ftl in this case).
<head> <title>${title}</title> </head>
And add it to a placeholder of another template (the body region of site.ftl).
<html> ${body} </html>
You can choose which template goes into the body region programmatically. You can also add multiple templates together into the same region. You can even put a string or calculated value into the body region instead
2.5 No Imports
JSPs requires you to import every class you intend to use, just like a regular Java class. FreeMarker templates on-the-other-hand are just, well, templates. You can include one template in another, but there are no classes that need to be imported.
2.6 Method Calls in the Expression Language
Before Servlet 3.0/ EL 2.2 container, method calls in the expression language are out. Not everyone agrees that method calls in the EL are a good thing, but when you really need them, the JSP workarounds were painful.
FreeMarker, however, treats each of these references the same.
${customer.address.country}
${customer.getAddress().country}
2.7 Built-in Null And Empty String Handling
Both FreeMarker and JSPs can handle null values in their expression languages, but FreeMarker goes a step further in usability.
Invoice Date: ${(customer.invoice.date)!}
The exclamation symbol tells FreeMarker to do automatic null and empty string checking on the attached expression. If any of customer, invoice, or date are either null or empty string, you’ll just get the label:
Another option is to include your default text after the exclamation.
Invoice Date: ${(customer.invoice.date)!'No Invoice Available'}
Again, if any of the values are missing, you get back:
Invoice Date: No Invoice Available
2.8 Shared Variables
FreeMarker’s shared variables feature let’s you set values that are automatically added to all templates.
For example you can set your app’s name as a shared variable.
Configuration configuration = new Configuration(); configuration.setSharedVariable("app", "StackHunter");
Then access it like any other variable.
App: ${app}
2.9 JSON Support
FreeMarker has built-in JSON support.
Let’s say you have the following JSON stored as a String in variable named user.
{ 'firstName': 'John', 'lastName': 'Smith', 'age': 25, 'address': { 'streetAddress': '21 2nd Street', 'city': 'New York', 'state': 'NY', 'postalCode': 10021 }}
Use ?eval to convert it from a string to a JSON object, then use it in expressions as you would any other data.
<#assign user = user?eval> User: ${user.firstName}, ${user.address.city}
2.10 Not just for the Web
Unlike JSPs, FreeMarker templates can be used outside of a servlet container. You can use them to generate emails, config files, XML mapping, etc.
3 Template manager
Axional Server
provides a template configuration manager class that simplfies the
use of FreeMarker.
A FTLConfigurationManager class is a instance of a FreeMarker configuration and templates repository that may be accessed (once instanced) from anyware using a singleton call. This way, the template manager may be instantiated at application startup and may be accessible by web applications using it's identifier.
The following example shows the use of a template manager to render a document.
public class ValueExampleObject { private String name; private String developer; public ValueExampleObject(String name, String developer) { this.name = name; this.developer = developer; } public String getName() { return name; } public String getDeveloper() { return developer; } }
<html> <head> <title>${title} </head> <body> <h1>${title} by ${company}</h1> <p>${exampleObject.name} by ${exampleObject.developer}</p> <ul> <#list systems as system> <li>${system_index + 1}. ${system.name} from ${system.developer}</li> </#list> </ul> </body> </html>
public void test() throws Exception { HashMap<String, Object> input = new HashMap<String, Object>(); input.put("title", "FreeMarker example"); input.put("exampleObject", new ValueExampleObject("Java object", "me")); ArrayList<ValueExampleObject> systems = new ArrayList<ValueExampleObject>(); systems.add(new ValueExampleObject("Android", "Google")); systems.add(new ValueExampleObject("iOS States", "Apple")); systems.add(new ValueExampleObject("Ubuntu", "Canonical")); systems.add(new ValueExampleObject("Windows7", "Microsoft")); input.put("systems", systems); // Register a template engine named test that uses UTF-8 encoding FTLConfigurationManager.registerInstance("test", new File("src/test/java/deister/axional/server/test/lang/ftl"), "UTF-8"); // Later we can access the template engine by it's name FTLConfigurationManager ftl = FTLConfigurationManager.getInstance("test"); // Setup a shared variable ftl.getConfiguration().setSharedVariable("company", "deister"); // get a template (with the default locale) // Will search for // test1_en_US.ftl // test1_en.ftl // test1.ftl Template t1 = ftl.getTemplate("test1.ftl"); t1.process(input, new PrintWriter(System.out)); // get a template (with the locale FRANCE) // Will search for // test1_fr_FR.ftl // test1_fr.ftl // test1.ftl Template t2 = ftl.getTemplate("test1.ftl", Locale.FRANCE); t2.process(input, new PrintWriter(System.out)); }
Running the test will produce the following output
<html> <head> <title>FreeMarker example </head> <body> <h1>FreeMarker example by deister</h1> <p>Java object by me</p> <ul> <li>1. Android from Google</li> <li>2. iOS States from Apple</li> <li>3. Ubuntu from Canonical</li> <li>4. Windows7 from Microsoft</li> </ul> </body>