Search code examples
freemarkerrestlet

Freemarker URL Template Loader


I would like to load Freemarker templates from one or more URLs so I subclassed the URLTemplate loader class and overrode the getURL(..) method to return the target URL (see below). I then added a couple of instances of this class to a multi template loader and added that to the Freemarker config. This works fine when the first URL returns a template but when it doesn't none of the other template loaders are called. What have I done wrong? I'm using v2.3 of Freemarker via the Restlet framework.

:    :     :    :     :    :     :    :     :    :     
TemplateLoader[] loaders = new TemplateLoader[] {
    new MyTemplateLoader(new URL(request.getRootRef() + app.getRoot())),
    new MyTemplateLoader(new URL(request.getRootRef() + "/"))
};

freemarkerConfig.setTemplateLoader(new MultiTemplateLoader(loaders));  

:    :     :    :     :    :     :    :     :    :     

public class MyTemplateLoader extends URLTemplateLoader {
    private URL root;

    public MyTemplateLoader(URL root) {
        super();
        this.root = root;
    }

    @Override
    protected URL getURL(String template) {
        try {
            URL tu = new URL(root,  "./" + template);
            return tu;
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
        return null;
    }
}

Solution

  • A template is considered to be missing if TemplateLoader.findTemplateSource returns null for it. If it returns a non-null object, then MultiTemplateLoader assumes that it has found the template. In the case of URLTemplateLoader, findTemplateSource just returns what getURL does. So you have to check if the target exists, and then return null as URL if it doesn't. This works well for ClassTemplateLoader because getResource returns null URL for missing resources. But in general (if you don't know what kind of URL do you have) you will have to open an URLConnection and then connect() to see if the target exists. Or at least I guess that most URLSrteamHandler-s will check if the target exists at that point.