I am currently migrating a Spring MVC Webapp (xml-config to java-config, tomcat to embedded tomcat via spring-boot).
The webapp uses freemarker as templating engine and JSP Taglibs. Now when I call a freemarker page I get the following error:
freemarker.ext.jsp.TaglibFactory$TaglibGettingException:
No TLD was found for the "http://www.springframework.org/tags/form" JSP taglib URI. (TLD-s are searched according the JSP 2.2 specification. In development- and embedded-servlet-container setups you may also need the "MetaInfTldSources" and "ClasspathTlds" freemarker.ext.servlet.FreemarkerServlet init-params or the similar system properites.)
The freemarker-header.ftl begins with following snippet:
<#assign form=JspTaglibs["http://www.springframework.org/tags/form"]>
<#assign core=JspTaglibs["http://java.sun.com/jstl/core"]>
<#assign spring=JspTaglibs["http://www.springframework.org/tags"]>
<#assign osc=JspTaglibs["/WEB-INF/osc.tld"]>
I did not find any usable search results for MetaInfTldSources and ClasspathTlds. Any one solved this problem before?
KR Habib
While rendering template, freemarker call TaglibFactory, which search for TLD in fours ways:
All of these methods are in TablibFactory class at freemarker jar. That last one, scan every jar in WEB-INF/lib searching for /META-INF/**/*.tld. You can see this logging if debug mode for freemarker is enabled.
Take a look how your project is deployed. In my case, using eclipse, wtp, tomcat and maven, the maven dependencies was configured in Eclipse/Deployment assembly as maven dependencies, of course :), hence these libs are not in WEB-INF/lib and so, was not found by
addTldLocationsFromMetaInfTlds
.A way to solve is forcing deployment to copy all maven dependencies to WEB-INF/lib. I did it opening server configuration, at eclipse view 'servers', under server options uncheck all checkboxes but 'Module auto reload by default'.
Spring Boot doesn't support the use of JSP taglibs with Freemarker out of the box. There's an open enhancement request that you might be interested in. It contains a link to a possible workaround where you configure
FreemarkerConfigurer
's tag lib factory with some additional TLDs to be loaded from the classpath:This really should be built-in.
First, disable the built in
FreeMarkerAutoConfiguration
on yourApplication
:Then add this custom configuration:
(adapted from https://github.com/isopov/fan/blob/master/fan-web/src/main/java/com/sopovs/moradanen/fan/WebApplicationConfiguration.java; Added an
ObjectWrapper
to theTaglibFactory
and removed theaddResourceHandlers()
override)Add the following to your
pom.xml
:Then you can load in your template:
It is actually an easy task if you know how to do it. All you need is already embedded into FreeMarker, for instance it is
TaglibFactory.ClasspathMetaInfTldSource
class. I spend several hours to investigate that problem, so I want to share a solution.I implemented it as
BeanPostProcessor
because now there is no way to setTaglibFactory
beforeFreeMarkerConfigurer
bean is initialized.The only restriction is that
*.tld
files must have<uri>
xml tag inside. All standard spring/spring-security TLDs have it. And also these files must be insideMETA-INF
folder of classpath, likeMETA-INF/mytaglib.tld
. All standard spring/spring-security TLDs are also follow this convention.Commented line is just for example of how you can add "custom" paths of
*.tld
files if for some reason you can't place them into standard location (maybe some external jar, which doesn't follow the convention). It can be extended to some sort of classpath scanning, searching for all*.tld
files and adding them intoclasspathTlds
. But usually it is just doesn't required if your TLDs follow JSP conventions to be placed insideMETA-INF
directory.I have tested this in my FreeMarker template and it works:
For custom tag ("http://my-custom-tag-library/tags") to work, it must be
*.tld
file insrc/main/resources/META-INF/some.tld
and it must contain the<uri>
xml tag, like<uri>http://my-custom-tag-library/tags</uri>
. It will be found by FreeMarker then.I hope it helps someone to save several hours to find "right" solution for this problem.
Tested with spring-boot v2.0.5.RELEASE