Error handling - FreeMarker Manual

- Home
- Manual
- Java API

- Contribute
- Report a Bug
- Download

Manual
Previous Next

# Error handling

## The possible exceptions

The exceptions that can occur regarding FreeMarker could be classified like this:

- Exceptions occurring when you configure FreeMarker: Typically you configure FreeMarker only once in your application, when your application initializes itself. Of course, during this, exceptions can occur, as it is obvious from the FreeMarker API...

- Exceptions occurring when loading and parsing templates: When you call `Configuration.getTemplate(...)`, FreeMarker has to load the template file into the memory and parse it (unless the template is already cached in that `Configuration` object). During this, two kind of exceptions can occur:

  - `IOException` because the template file was not found, or other I/O problem occurred while trying to read it, for example you have no right to read the file, or there are disk errors. The emitter of these errors is the `TemplateLoader` object, which is plugged into the `Configuration` object. (For the sake of correctness: When I say ``file'' here, that's a simplification. For example, templates can be stored in a table of a relational database as well. This is the business of the `TemplateLoader`.)

  - `freemarker.core.ParseException` because the template file is syntactically incorrect according the rules of the FTL language. The point is that this error occurs when you obtain the `Template` object (`Configuration.getTemplate(...)`), and not when you execute (`Template.process(...)`) the template. This exception is an `IOException` subclass.

- Exceptions occurring when executing (processing) templates, that is, when you call `Template.process(...)`. Two kind of exceptions can occur:

  - `IOException` because there was an error when trying to write into the output writer.

  - `freemarker.template.TemplatException` because other problem occurred while executing the template. For example, a frequent error is when a template refers to a variable which is not existing. Be default, when a `TemplatException` occurs, FreeMarker prints the FTL error message and the stack trace to the output writer with plain text format, and then aborts the template execution by re-throwing the `TemplatException`, which then you can catch as `Template.process(...)` throws it. But this behavior can be customized. FreeMarker always logs `TemplatException`-s.

## Customizing the behavior regarding TemplatException-s

`TemplateException`-s thrown during the template processing are handled by the `freemarker.template.TemplateExceptionHandler` object, which is plugged into the `Configuration` object with its `setTemplateExceptionHandler(...)` mehod. The `TemplateExceptionHandler` contains 1 method:

    void handleTemplateException(TemplateException te, Environment env, Writer out)
            throws TemplateException;

Whenever a `TemplateException` occurs, this method will be called. The exception to handle is passed with the `te` argument, the runtime environment of the template processing is accessible with the `env` argument, and the handler can print to the output using the `out` argument. If the method throws exception (usually it re-throws `te`), then the template processing will be aborted, and `Template.process(...)` will throw the same exception. If `handleTemplateException` doesn't throw exception, then template processing continues as if nothing had happen, but the statement that caused the exception will be skipped (see more later). Of course, the handler can still print an error indicator to the output.

In any case, before the `TemplateExceptionHandler` is invoked, FreeMarker will log the exception.

Let's see how FreeMarker skips ``statements'' when the error handler doesn't throw exception, through examples. Assume we are using this template exception handler:

    class MyTemplateExceptionHandler implements TemplateExceptionHandler {
        public void handleTemplateException(TemplateException te, Environment env, java.io.Writer out)
                throws TemplateException {
            try {
                out.write("[ERROR: " + te.getMessage() + "]");
            } catch (IOException e) {
                throw new TemplateException("Failed to print error message. Cause: " + e, env);
            }
        }
    }

    ...

    cfg.setTemplateExceptionHandler(new MyTemplateExceptionHandler());

If an error occurs in an interpolation which is not inside an FTL tag (that is, not enclosed into `<#...>` or `<@...>`), then the whole interpolation will be skipped. So this template (assuming that `badVar` is missing from the data-model):

    a${badVar}b

will print this if we use the `MyTemplateExceptionHandler`:

    a[ERROR: Expression badVar is undefined on line 1, column 4 in test.ftl.]b

This template will print the same (except that the column number will differ...):

    a${"moo" + badVar}b

since, as it was written, the whole interpolation is skipped if any error occurs inside it.

If an error occurs when evaluating the value of a parameter for a directive call, or if there are other problems with the parameter list, or if an error occurs when evaluating `exp` in `<@exp ...>`, or if the value of `exp` is not an user-defined directive, then the whole directive call is skipped. For example this:

    a<#if badVar>Foo</#if>b

will print this:

    a[ERROR: Expression badVar is undefined on line 1, column 7 in test.ftl.]b

Note that the error occurred in the `if` start-tag (`<#if badVar>`), but the whole directive call was skipped. Logically, the nested content (`Foo`) was skipped with this, since the nested content is handled (printed) by the enclosing directive (`if`).

The output will be the same with this (except that the column number will differ...):

    a<#if "foo${badVar}" == "foobar">Foo</#if>b

since, as it was written, the whole directive calling will be skipped if any error occurs during the parameter evaluation.

The directive call will not be skipped if the error occurs after the execution of the directive was already started. That is, if an error occurs in the nested content:

    a
    <#if true>
      Foo
      ${badVar}
      Bar
    </#if>
    c

or in the macro definition body:

    a
    <@test />
    b
    <#macro test>
      Foo
      ${badVar}
      Bar
    </#macro>

the output will be something like:

    a
      Foo
      [ERROR: Expression badVar is undefined on line 4, column 5 in test.ftl.]
      Bar
    c

FreeMarker comes with these prewritten error handlers:

- `TemplateExceptionHandler.DEBUG_HANDLER`: Prints stack trace (includes FTL error message and FTL stack trace) and re-throws the exception. This is the default handler (that is, it is initially prugged into all new `Configuration` objects).

- `TemplateExceptionHandler.HTML_DEBUG_HANDLER`: Same as `DEBUG_HANDLER`, but it formats the stack trace so that it will be readable with Web browsers. Recommended over `DEBUG_HANDLER` when you generate HTML pages.

- `TemplateExceptionHandler.IGNORE_HANDLER`: Simply suppresses all exceptions (but remember, FreeMarker will still log them). It does nothing to handle the event. It does not re-throw the exception.

- `TemplateExceptionHandler.RETHROW_HANDLER`: Simply re-throws all exceptions, it doesn't do anything else. This handler can be good for Web applications (assuming you don't want to continue template processing after exception), because it gives the most control to the Web application over page generation on error conditions (since FreeMarker doesn't print anything to the output about the error). For more information about handling errors in Web applications see the FAQ.

## Explicit error handling in templates

Although it has nothing to do with the FreeMarker configuration (the topic of this chapter), for the sake of completeness it is mentioned here that you can handle errors directly in templates as well. This is usually a bad practice (try keep templates simple and non-technical), but nonetheless necessary sometimes:

- Handling missing/null variables: Template Author's Guide/The Template/Expressions/Handling missing values

- Surviving malfunctioning ``portlets'' and such expendable page sections: Template Language Reference/Directive Reference/attempt, recover

Previous Next