I haven't been able to access spring.ftl in freemarker includes and Sitemesh decorators. What is the best way to do it?
This is what they had to say about it in the Spring forum:
Examine the source for springframework/web/servlet/view/AbstractTemplateView.java and you can see this code
// Expose RequestContext instance for Spring macros.
model.put(SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE,
new RequestContext(request, getServletContext(), model));
at line 168.. which is where the FreeMarkerView sub-class receives the macro context as a request attribute.
A similar bit of code would have to be added to the Sitemesh-FreemarkerServlet or you can sub-class that
class yourself and add code to insert the macro context on the HttpRequest object before Sitemesh invokes
Freemarker on the decorator template. You probably would have to use Dependancy injection into your
sub-class version of the Sitemesh-FreemarkerServlet to get access to the macro context object to add to the
request object.
Cheers,
Marc
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
If by accessing spring.ftl you mean #import-ing it, then it has nothing to do with the model. The usual way of accessing macro libraries is <#import "/spring.ftl" as spring> or something like that. If it's different in Sitemesh, I don't know....
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
There is a basic conflict between the Spring framework and SiteMesh. SiteMesh-Freemarker is actually implemented
as a Servlet filter (the Sitemesh-Freemarker Servlet just does a pre template process setup). The "Page Filter"
is what really does all the work and it does not do what the FreeMarkerView in spring does, which is how the
spring.ftl file, embedded inside the spring.jar (or is it spring-webmvc.jar?) is made accessable
(by default in spring in spite of what the doc say) to the FM template used as views in a web application.
The problem occurs when a web-app trys to use/include spring.ftl in the sitemesh decorator.ftl file. When
the sitemesh "Page Filter" invokes the Freemarker.template.process() method to convert the decorator.ftl
file into the real HTTP response page, the data model Sitemesh creates and used does NOT contain an entry
for the spring.ftl, that is automatically and silently provided by the Spring Freemarker.view code.
SO IT DON'T WORK, and short of binding the Sitemesh Page Filter directly to Spring (to which i expect the
Sitemesh folks would strongly object) they are not likely to change SiteMesh to fix this. I am currently
building a web-app using Sitemesh and Freemarker with Spring and ran this issue down in the Sitemesh
source.
There is also another NOTEABLE difference between the way Spring invokes FreeMarker in FreeMarkerView and
the way the FreeMarker Servlet does; specifically the Freemarker Servlet builds the 4 xxxxHashModels for
"page", Request, Session, and Application attributes/properties and inserts them all into the data model
using a wrapper named AllHttpScopesHashModel.
What this model adds is automatic NOT FOUND fall-through searching of all 4 scopes for a Freemarker variable reference (usually in an interpolation, e.g. ${message}); I.E is ${message} is not found in page, then
Request is searched, and if not found, then Session is searched, ...
What i discovered is that Spring puts the same xxxHashModels into the data model, but it does NOT go the extra
step of using the AllHttpScopesHashModel to wrap them, and thus the fall-through searching of all 4 scopes
doesn't happen;
THUS if your ${message} was an attribute of the Session scope (from the HttpSession object) a page rendered
by the FreemarkerServlet (or it's sub-class Sitemesh-Freemarker Servlet) will find ${message} but it will
have to be referenced as ${Request.message} in a template rendered by FreeMarkerView in Spring.
I hacked up the FreeMarkerView.java code (cloning AllHttpScopesHashModel as an inner class) and re-coded
FreeMarkerView.java to correct this design difference and reported it to Spring JIRA as
[Spring-JIRA] Updated: (SPR-4962) "FreeMarkerView does NOT setup the model as AllHttpScopesHashModel"
and included the modified FreeMarkerView.java src if anyone wants to tryout the fix this latter issue.
I just got notified today, by Spring that my "Bug" was demoted to an "Improvement" request.
FreeMarker's AllHttpScopesHashModel for some odd reason i don't get, does NOT have a public constructor
(an oversight??) LIKE all the other xxxxHashModels do!, so that is why i had to clone it into spring.
IF the constructor was made public the spring fix could just use the AllHttpScopesHashModel as is, but
there may be something i don't understand about Freemarker that mades package scope preferable;
i haven't got my head around the entire Freemarker source tree, yet.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Just as a side note, since #include/#import itself is not influenced by the contents of the data-model, that you describe is hardly related to the "spring.ftl" not-found issue itself, that the initial question was about.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Actually access to the macros file spring.ftl IS dependent on the data-model, as the following code from
as the following code from spring AbstractTemplateView.java shows
if (this.exposeSpringMacroHelpers) {
if (model.containsKey(SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE)) {
throw new ServletException(
"Cannot expose bind macro helper '" + SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE +
"' because of an existing model object of the same name");
}
// Expose RequestContext instance for Spring macros.
model.put(SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE,
new RequestContext(request, getServletContext(), model));
}
where public static final String SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE = "springMacroRequestContext";
which the spring.ftl bind macros must have to work, as this fragment from spring.ftl shows
<#-- _________________________________________________________________________________
* messageText
*
* Macro to translate a message code into a message,
* using the given default text if no message found.
-->
<#macro messageText code, text>${springMacroRequestContext.getMessage(code, text)}</#macro>
You comment is also technically correct and spring silently adds to the Freemarker template loader path when FreeMarker is configured
and includes an extra path that allows loading spring.ftl from within spring-webmvc.jar file for the package
"org.springframework.web.servlet.view.freemarker" .
In effect spring is providing all the access for both loading spring.ftl and execution of the macros in spring.ftl
As i recall the FreeMarkerConfigurer.java is the source file that adds the loader path for spring.ftl,
so if configuration was done another way, the missing extra path will cause the <#include/import > to fail.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
And even more to the point, is that the template loader path used by the Sitemesh "Page filter" is not configured by the FreeMarkerConfigurer bean
and so the "silent" addition spring makes to the template loader path is NOT available when the PageFilter invokes Freemarker's template.process() method.
But even if you copy spring.ftl out of the jar into your web-app context and setup sitemesh so it can load it, that doesn't create the missing
request context referenced by all the spring.ftl macros.
Essentially, what this means is that you can use the spring macros in a fooform.ftl file that IS renedered by Freemarker view which becomes
the ${body} to be decorated, but NOT use them i the decorator.ftl file itself (or it's include/imports)
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I have been reading the Freemarker, Sitemesh, and Spring sources and i believe there is a fix that will solve this
problem. I think it will require adding a method to the Sitemesh-FreemarkerServlet to override createConfiguration()
so that a new method can be added to allow injection by the spring IOC container of the Freemarker Config used by
a Spring for the FreemarkerView, so that the SiteMesh-FreemarkerServlet can pass that pre-created Configuration
object back to the FreemarkerServlet as the result from createConfiguraiton(). This "should" cause SiteMesh to
end up with the same template loader path as spring and allow access to spring.ftl inside the spring-webmvc.jar.
I think it can even be done by sub-classing the Sitemesh-FreemarkerServlet ( SpringSiteMeshFreeMarkerServlet ? say that 3 time fast!)
But it's 4:35 AM (in CA) and past time to get some sleep, so i will report back later if i can create a fix.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
After extensive analysis of the Sitemesh and Spring sources i conclude that there is no straight forward solution
that will connect the Sitemesh FreemarkerDecoratorServlet (i was using the wrong name above) with the Spring
application context so that the parent FreemarkerServlet (of which SitemeshDecoratorServlet is a sub-class) uses
the Spring Freemarker Configuration object created by Springs FreeMarkerConfigurer which is auto-detected by
springs FreeMarkerView class.
This is due to the fact that the Sitemesh/FreemarkerServlet is not a Spring servlet managed by the spring IOC
container, but a stand alone servlet in the Servlet context, but not in the Spring application context.
This is as it should be, because the SitemeshServlet is a generic decorator tool, that can decorate output from
all types of view implementations, .jsp, .vm, etc, and to decorate the resulting whole html pages without modifying
the original.
I conclude that a better Spring specific "all-Freemarker" solution is to create a FreeMarkerMeshView and performs the
equivalent multi template rendering directly in Spring. Since i have been using Sitemesh, but find the incompatibility
noted in my first comment, about the differing lookup logic, a real problem, i am going in this other direction.
I expect this to be a simple solution, that will allow passing any number of named rendered "html content strings" to a base
layout view template, by using Freemaker directly (not indirectly via a separate servlet rendering in an isolated context).
This will avoid all the complexity of the sitemesh approach (and give up its generality) and be faster, as it will avoid the
need to "extract by parsing" the ${head}, ${body}, ${title} content from the separately rendered "sub panels". A side effect
of this is that the sub-panel templates will be html fragments of just the sub-panel content, not complete html pages,
a trade-off made for performance to eliminate the need to parse the content at all. IE this is a Freemarker composition
solution, not a Site Mesh solution, nor at the level of the Tile2 solution, but since i am creating a NEW web-app, thats OK 4 me.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Hi,
I haven't been able to access spring.ftl in freemarker includes and Sitemesh decorators. What is the best way to do it?
This is what they had to say about it in the Spring forum:
Examine the source for springframework/web/servlet/view/AbstractTemplateView.java and you can see this code
// Expose RequestContext instance for Spring macros.
model.put(SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE,
new RequestContext(request, getServletContext(), model));
at line 168.. which is where the FreeMarkerView sub-class receives the macro context as a request attribute.
A similar bit of code would have to be added to the Sitemesh-FreemarkerServlet or you can sub-class that
class yourself and add code to insert the macro context on the HttpRequest object before Sitemesh invokes
Freemarker on the decorator template. You probably would have to use Dependancy injection into your
sub-class version of the Sitemesh-FreemarkerServlet to get access to the macro context object to add to the
request object.
Cheers,
Marc
If by accessing spring.ftl you mean #import-ing it, then it has nothing to do with the model. The usual way of accessing macro libraries is <#import "/spring.ftl" as spring> or something like that. If it's different in Sitemesh, I don't know....
There is a basic conflict between the Spring framework and SiteMesh. SiteMesh-Freemarker is actually implemented
as a Servlet filter (the Sitemesh-Freemarker Servlet just does a pre template process setup). The "Page Filter"
is what really does all the work and it does not do what the FreeMarkerView in spring does, which is how the
spring.ftl file, embedded inside the spring.jar (or is it spring-webmvc.jar?) is made accessable
(by default in spring in spite of what the doc say) to the FM template used as views in a web application.
The problem occurs when a web-app trys to use/include spring.ftl in the sitemesh decorator.ftl file. When
the sitemesh "Page Filter" invokes the Freemarker.template.process() method to convert the decorator.ftl
file into the real HTTP response page, the data model Sitemesh creates and used does NOT contain an entry
for the spring.ftl, that is automatically and silently provided by the Spring Freemarker.view code.
SO IT DON'T WORK, and short of binding the Sitemesh Page Filter directly to Spring (to which i expect the
Sitemesh folks would strongly object) they are not likely to change SiteMesh to fix this. I am currently
building a web-app using Sitemesh and Freemarker with Spring and ran this issue down in the Sitemesh
source.
There is also another NOTEABLE difference between the way Spring invokes FreeMarker in FreeMarkerView and
the way the FreeMarker Servlet does; specifically the Freemarker Servlet builds the 4 xxxxHashModels for
"page", Request, Session, and Application attributes/properties and inserts them all into the data model
using a wrapper named AllHttpScopesHashModel.
What this model adds is automatic NOT FOUND fall-through searching of all 4 scopes for a Freemarker variable reference (usually in an interpolation, e.g. ${message}); I.E is ${message} is not found in page, then
Request is searched, and if not found, then Session is searched, ...
What i discovered is that Spring puts the same xxxHashModels into the data model, but it does NOT go the extra
step of using the AllHttpScopesHashModel to wrap them, and thus the fall-through searching of all 4 scopes
doesn't happen;
THUS if your ${message} was an attribute of the Session scope (from the HttpSession object) a page rendered
by the FreemarkerServlet (or it's sub-class Sitemesh-Freemarker Servlet) will find ${message} but it will
have to be referenced as ${Request.message} in a template rendered by FreeMarkerView in Spring.
I hacked up the FreeMarkerView.java code (cloning AllHttpScopesHashModel as an inner class) and re-coded
FreeMarkerView.java to correct this design difference and reported it to Spring JIRA as
[Spring-JIRA] Updated: (SPR-4962) "FreeMarkerView does NOT setup the model as AllHttpScopesHashModel"
and included the modified FreeMarkerView.java src if anyone wants to tryout the fix this latter issue.
I just got notified today, by Spring that my "Bug" was demoted to an "Improvement" request.
FreeMarker's AllHttpScopesHashModel for some odd reason i don't get, does NOT have a public constructor
(an oversight??) LIKE all the other xxxxHashModels do!, so that is why i had to clone it into spring.
IF the constructor was made public the spring fix could just use the AllHttpScopesHashModel as is, but
there may be something i don't understand about Freemarker that mades package scope preferable;
i haven't got my head around the entire Freemarker source tree, yet.
Just as a side note, since #include/#import itself is not influenced by the contents of the data-model, that you describe is hardly related to the "spring.ftl" not-found issue itself, that the initial question was about.
I made the AllHttpScopesHashModel constructor public -- there wasn't any good reason it was package protected. This'll become available as of 2.3.13
Sorry, meant to say 2.3.14 - 2.3.13 is already out.
Actually access to the macros file spring.ftl IS dependent on the data-model, as the following code from
as the following code from spring AbstractTemplateView.java shows
if (this.exposeSpringMacroHelpers) {
if (model.containsKey(SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE)) {
throw new ServletException(
"Cannot expose bind macro helper '" + SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE +
"' because of an existing model object of the same name");
}
// Expose RequestContext instance for Spring macros.
model.put(SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE,
new RequestContext(request, getServletContext(), model));
}
where public static final String SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE = "springMacroRequestContext";
which the spring.ftl bind macros must have to work, as this fragment from spring.ftl shows
<#-- _________________________________________________________________________________
* messageText
*
* Macro to translate a message code into a message,
* using the given default text if no message found.
-->
<#macro messageText code, text>${springMacroRequestContext.getMessage(code, text)}</#macro>
You comment is also technically correct and spring silently adds to the Freemarker template loader path when FreeMarker is configured
and includes an extra path that allows loading spring.ftl from within spring-webmvc.jar file for the package
"org.springframework.web.servlet.view.freemarker" .
In effect spring is providing all the access for both loading spring.ftl and execution of the macros in spring.ftl
As i recall the FreeMarkerConfigurer.java is the source file that adds the loader path for spring.ftl,
so if configuration was done another way, the missing extra path will cause the <#include/import > to fail.
And even more to the point, is that the template loader path used by the Sitemesh "Page filter" is not configured by the FreeMarkerConfigurer bean
and so the "silent" addition spring makes to the template loader path is NOT available when the PageFilter invokes Freemarker's template.process() method.
But even if you copy spring.ftl out of the jar into your web-app context and setup sitemesh so it can load it, that doesn't create the missing
request context referenced by all the spring.ftl macros.
Essentially, what this means is that you can use the spring macros in a fooform.ftl file that IS renedered by Freemarker view which becomes
the ${body} to be decorated, but NOT use them i the decorator.ftl file itself (or it's include/imports)
I have been reading the Freemarker, Sitemesh, and Spring sources and i believe there is a fix that will solve this
problem. I think it will require adding a method to the Sitemesh-FreemarkerServlet to override createConfiguration()
so that a new method can be added to allow injection by the spring IOC container of the Freemarker Config used by
a Spring for the FreemarkerView, so that the SiteMesh-FreemarkerServlet can pass that pre-created Configuration
object back to the FreemarkerServlet as the result from createConfiguraiton(). This "should" cause SiteMesh to
end up with the same template loader path as spring and allow access to spring.ftl inside the spring-webmvc.jar.
I think it can even be done by sub-classing the Sitemesh-FreemarkerServlet ( SpringSiteMeshFreeMarkerServlet ? say that 3 time fast!)
But it's 4:35 AM (in CA) and past time to get some sleep, so i will report back later if i can create a fix.
After extensive analysis of the Sitemesh and Spring sources i conclude that there is no straight forward solution
that will connect the Sitemesh FreemarkerDecoratorServlet (i was using the wrong name above) with the Spring
application context so that the parent FreemarkerServlet (of which SitemeshDecoratorServlet is a sub-class) uses
the Spring Freemarker Configuration object created by Springs FreeMarkerConfigurer which is auto-detected by
springs FreeMarkerView class.
This is due to the fact that the Sitemesh/FreemarkerServlet is not a Spring servlet managed by the spring IOC
container, but a stand alone servlet in the Servlet context, but not in the Spring application context.
This is as it should be, because the SitemeshServlet is a generic decorator tool, that can decorate output from
all types of view implementations, .jsp, .vm, etc, and to decorate the resulting whole html pages without modifying
the original.
I conclude that a better Spring specific "all-Freemarker" solution is to create a FreeMarkerMeshView and performs the
equivalent multi template rendering directly in Spring. Since i have been using Sitemesh, but find the incompatibility
noted in my first comment, about the differing lookup logic, a real problem, i am going in this other direction.
I expect this to be a simple solution, that will allow passing any number of named rendered "html content strings" to a base
layout view template, by using Freemaker directly (not indirectly via a separate servlet rendering in an isolated context).
This will avoid all the complexity of the sitemesh approach (and give up its generality) and be faster, as it will avoid the
need to "extract by parsing" the ${head}, ${body}, ${title} content from the separately rendered "sub panels". A side effect
of this is that the sub-panel templates will be html fragments of just the sub-panel content, not complete html pages,
a trade-off made for performance to eliminate the need to parse the content at all. IE this is a Freemarker composition
solution, not a Site Mesh solution, nor at the level of the Tile2 solution, but since i am creating a NEW web-app, thats OK 4 me.