In this chapter we'll write a small Java application that will process a template and send the results to the console. There are several goals to this exercise. The first is to get you up and running with FreeMarker quickly. The second is to provide a clear demonstration about what FreeMarker does by showing it in operation outside of any other application framework. Finally, you'll see how little effort is actually required to generate a working FreeMarker application.
If you haven't done so, create a directory to work in. I'm going to keep this as simple as possible, so we won't need a complicated directory structure. Everything can be done in one directory. Put the freemarker.jar
in the directory. All future talk about files and running from the command-line will refer to your working directory. If you want to, you can set up a more advanced project-like set of directories.
This is a quick start, so let's just dive in and write the template. Open a file for editing called hello.ftl
. The ftl
extension is customary for FreeMarker Template Language files, but you are free to name your template files anything you want. Put this line in your file:
Hello, ${name}!
FreeMarker will replace the ${name}
expression with the value of an element called name
in the model. FreeMarker calls this an interpolation. I prefer to refer to this as "evaluating an expression", but you will encounter the term interpolation in the documentation.
Everything else you have put in this initial template is static text. If name
contained the value World
, then this template would evaluate to:
Hello, World!
Templates are not scripts that can be run, so we need to write some Java code to invoke the FreeMarker engine and combine the template with a populated model. Here is that code:
import java.io.*;
import java.util.*;
import freemarker.template.*;
public class HelloFreemarker {
public static void main(String[] args)
throws IOException, TemplateException {
Configuration cfg = new Configuration();
cfg.setObjectWrapper(new DefaultObjectWrapper());
cfg.setDirectoryForTemplateLoading(new File("."));
Map<String, Object> model = new HashMap<String, Object>();
model.put("name", "World");
Template template = cfg.getTemplate("hello.ftl");
template.process(model,
new OutputStreamWriter(System.out));
}
}
The first thing we do is create a FreeMarker freemarker.template.Configuration
object. This acts as a factory for freemarker.template.Template
objects.
FreeMarker has its own internal object types that it uses to extract values from the model. In order to use the objects that you supply, it must wrap these in its own native types. The job of doing this is done by an object wrapper. You must provide an object wrapper. It will always be FreeMarker's own freemarker.template.DefaultObjectWrapper
unless you have special object wrapping requirements.
Finally, we set the root directory for loading templates. For the purposes of our sample code, everything is in the same directory so we just set it to ".
". Setting the template directory can throw an java.lang.IOException
exception in this code. We simply allow that to be thrown out of the method.
Next, we create our model, which is a simple map of java.lang.String
keys to java.lang.Object
values. The values can be simple object types such as String
or java.lang.Number
, or they can be complex object types, including arrays and collections. Our needs are simple here, so we're going to map "name" to the string "World".
The next step is to get a Template
object. We ask the Configuration
instance to load the template into a Template
object. This can also throw an IOException
.
The magic finally happens when we ask the Template
instance to process the model and create an output. We already have the model, but where does the output go? For this, we need an implementation of java.io.Writer
. For convenience, we are going to wrap the java.io.PrintWriter
in java.lang.System.out
with a java.io.OutputStreamWriter
and give that to the template.
After compiling this program, we can run it from the command line:
java -cp .;freemarker.jar HelloFreemarker
For Linux or OSX, you would use a ":
" instead of a ";
" in the command:
java -cp .:freemarker.jar HelloFreemarker
The result should be that the program prints out:
Hello, World!
Downloading the example code
You can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.
If you plan to create simple templates populated with preformatted text, then you now know all you need to know about FreeMarker. Chances are that you will, so let's take a look at how FreeMarker handles formatting other types and complex objects.
Let's try binding the "name"
object in our model to some other types of objects. We can replace:
model.put("name", "World");
with:
model.put("name", 123456789);
The output format of the program will depend on the default locale, so if you are in the United States, you will see this:
Hello, 123,456,789!
If your default locale was set to Germany, you would see this:
Hello, 123.456.789!
FreeMarker does not call toString()
method on instances of Number types it employs java.text.DecimalFormat
. Unless you want to pass all of your values to FreeMarker as preformatted strings, you are going to need to understand how to control the way FreeMarker converts values to text.
In the previous section, we saw how FreeMarker will choose a default method of formatting numbers. One of the features of this method is that it employs grouping separators: a comma or a period every three digits. It may also use a comma rather than a period to denote the decimal portion of the number. This is great for humans who may expect these formatting details, but if your number is destined to be parsed by a computer, it needs to be free of grouping separators and it must use a period as a decimal point. In this case, you need a way to control how FreeMarker decides to format a number.
In order to control exactly how model objects are converted to text, FreeMarker provides operators called built-ins. Let's create a new template called types.ftl
and put in some expressions that use built-ins to control formatting:
String: ${string?html} Number: ${number?c} Boolean: ${boolean?string("+++++", "-----")} Date: ${.now?time} Complex: ${object}
This template is a little more complicated than the last template. The "?
" at the end of a variable name denotes the use of a built-in. Before we explore these particular built-ins, let's see them in action. Create a java program, FreemarkerTypes
, which populates a model with values for our new template:
import java.io.*; import java.math.BigDecimal; import java.util.*; import freemarker.template.*; public class FreemarkerTypes { public static void main(String[] args) throws IOException, TemplateException { Configuration cfg = new Configuration(); cfg.setObjectWrapper(new DefaultObjectWrapper()); cfg.setDirectoryForTemplateLoading(new File(".")); Map<String, Object> model = new HashMap<String, Object>(); model.put("string", "easy & fast "); model.put("number", new BigDecimal("1234.5678")); model.put("boolean", true); model.put("object", Locale.US); Template template = cfg.getTemplate("types.ftl"); template.process(model, new OutputStreamWriter(System.out)); } }
Run the FreemarkerType
program the same way you ran HelloFreemarker
. You will see this output:
String: easy & fast
Number: 1234.5678
Boolean: +++++
Date: 9:12:33 AM
Complex: en_US
Let's walk through the template and see how the built-ins affected the output. Our purpose is to get a solid foundation in the basics. We'll look at more details about how to use FreeMarker features in later chapters.
String
modified with the html
built-in. This encoded the string for HTML, turning the &
into the &
HTML entity. You will want this applied to a lot of your expressions on HTML pages in order to ensure proper display of your text and to prevent cross-site scripting (XSS) attacks.c
built-in. This tells FreeMarker that the number should be written for parsing by computers. As we saw in the previous section, FreeMarker will by default format numbers with grouping separators. It will also localize the decimal point, using a comma instead of a period. This is great when you are displaying numbers to humans, but not computers. If you want to put an ID number in a URL or a price in an XML document, you will want to use this built-in to format it.string
built-in, FreeMarker will not format a Boolean value at all. In fact, it throws an exception. Conceptually, "true" and "false" have no universal text representation. If you use string
with no arguments, the interpolation will evaluate to either "true" or "false", but this is a default you can change. Here, we have told the built-in to use a series of + characters for "true" and a series of – characters for "false".java.util.Date
. The main issue here is that FreeMarker doesn't know whether you want to display a date, a time, or both. By specifying the time
built-in we are letting FreeMarker know that we want to display a time. The output shown previously was generated shortly past nine o'clock in the morning.toString()
method, so you can use string built-ins on them.We've reached the end of the Quick start section. You've created two simple templates and worked with some of the basic features of FreeMarker. You might be wondering what are the other built-ins, or what options they offer. In the upcoming sections we'll look at these options and also ways to change the default behavior.
Another issue we've glossed over is errors. Once you have applied some of these built-ins, you must make sure that you supply the correct types for the named model elements. We also haven't looked at what happens when a referenced model element is missing. The FreeMarker manual provides excellent reference for all of this. Rather than trying to find your way around on your own, we'll take a guided tour through the important features in the Top Features section of the book.
A key difference between the Quick start and Top Features sections is that we'll be starting with the sample output. In this chapter, we created templates and evaluated them to see what we would get. In a real-world project, you will get better results if you worked backwards from the desired result.
In many cases, you won't have a choice. The sample output will be generated by web designers and you will be expected to produce the same HTML with dynamic content. In other cases, you will need to work from mock-ups and decide the HTML for yourself. In these cases, it is still worth creating a static sample document. These static samples will show you where you need to apply some of the techniques which you'll learn in the Top Features section.
18.225.235.144