Pages

Sunday, February 23, 2014

Freemarker loading taglibs from classpath

It is pretty common problem to try using various jsp taglibs from freemarker templates[1][2][3][4]. There is some support for this in freemarker, but this support is a bit ugly - it requires dancing with jar files like placing them in WEB-INF/lib folder and there is no easy way to use taglibs just from the classpath. At least - there was no such an easy way. After a day of debugging and investigation I figured the solution that works pretty good for me. The key is to override just the two methods in the ServletContext that is used by freemarker TagLibFactory. To do this I used standard dynamic proxy, but other solutions are possible two. After this taglibs can be referenced by their paths in the classpath - like:

<#assign security=JspTaglibs["/META-INF/security.tld"] />

for spring-security taglib instead of the usual

<#assign security=JspTaglibs["http://www.springframework.org/security/tags"] />

And the InvocationHandler that can be used to create the proxy for the ServletContext is here:

package com.sopovs.moradanen;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

import javax.servlet.ServletContext;


public class ServletContextResourceHandler implements InvocationHandler {
    private final ServletContext target;

    private ServletContextResourceHandler(ServletContext target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if ("getResourceAsStream".equals(method.getName())) {
            Object result = method.invoke(target, args);
            if (result == null) {
                result = ServletContextResourceHandler.class.getResourceAsStream((String) args[0]);
            }
            return result;
        } else if ("getResource".equals(method.getName())) {
            Object result = method.invoke(target, args);
            if (result == null) {
                result = ServletContextResourceHandler.class.getResource((String) args[0]);
            }
            return result;
        }

        return method.invoke(target, args);
    }
}

And the complete solution can be found in my pet-project here.

No comments:

Post a Comment