freemarker.template.TemplateModelException when using Stripes LayoutRenderTag with Freemarker 2.3.18

Description

We managed Stripes LayoutRenderTag to work with the latest version of Freemarker 2.3.x. without the need of a dynamic attribute to be set. Freemarker throws some curious exception when using the LayoutRenderTag without a dynamic attribute.

It's because Stripes overrides the Tagwriter instance of Freemarker in the getContext() method of the LayoutRenderTag class within the doStartTag() method. That's a problem because Freemarks checks if the Writer (Tagwriter) before the Tag is called is the same like after Tag processing.

If you set a dynamic attribute on the layout-render tag the getContext() method in LayoutRenderTag is called within the setDynamicAttribute() method and before the doStartTag() method is invoked. If the LayoutContext is initialized before the doStartTag() method is invoked all goes fine.

So i override the setPageContext() method within the LayoutRenderTag class and call the getContext() method before anything else is invoked.

LayoutRenderTag.java

1 2 3 4 5 @Override public void setPageContext(PageContext pageContext) { super.setPageContext(pageContext); this.getContext(); }

Here is the layout used in my tests:

layout.ftl with the layout-definition

1 2 3 4 5 6 7 <#ftl> <#assign s=JspTaglibs["http://stripes.sourceforge.net/stripes.tld"]/> <@s["layout-definition"]> HEAD: <@s["layout-component"] name="html_head" /><br/> BODY: <@s["layout-component"] name="body" /> </@>

test.ftl with the layout-render

1 2 3 4 5 6 7 8 9 10 11 <#ftl> <#assign s=JspTaglibs["http://stripes.sourceforge.net/stripes.tld"]/> <@s["layout-render"] name="/web/manager/layout/default.ftl"> <@s["layout-component"] name="html_head"> Here is the head </@> <@s["layout-component"] name="body"> Here is the body </@> </@>

There some other really annoying problems when using Stripes and Freemarker we have managed to go away.

1 12:58:42 WARN: freemarker.jsp - Tag.SKIP_PAGE was ignored from a net.sourceforge.stripes.tag.layout.LayoutDefinitionTag tag.

This execption is raised because of the SKIP_PAGE return value in LayoutDefinitionTag class in the doEndTag() method.
I change the return value in EVAL_PAGE to prevent the output of the WARN message.

and

in the LayoutWriter class the flush() method throw an unhandled exception NullPointerException because the expected Writer is null. The root cause is unknown, but i added a null-check to prevent this WARN message too.

After that all pages renders fine, i can use Freemarker templates with layout tags of Stripes.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 12:58:42 WARN: net.sourceforge.stripes.exception.DefaultExceptionHandler - Unhandled exception caught by the Stripes default exception handler. java.lang.NullPointerException at net.sourceforge.stripes.tag.layout.LayoutWriter.flush(LayoutWriter.java:125) at freemarker.ext.jsp.JspWriterAdapter.flush(JspWriterAdapter.java:40) at net.sourceforge.stripes.tag.layout.LayoutWriter.tryFlush(LayoutWriter.java:108) at net.sourceforge.stripes.tag.layout.LayoutWriter.setSilent(LayoutWriter.java:70) at net.sourceforge.stripes.tag.layout.LayoutRenderTag.doEndTag(LayoutRenderTag.java:189) at freemarker.ext.jsp.TagTransformModel$TagWriter.endEvaluation(TagTransformModel.java:430) at freemarker.ext.jsp.TagTransformModel$TagWriter.afterBody(TagTransformModel.java:406) at freemarker.core.Environment.visit(Environment.java:312) at freemarker.core.UnifiedCall.accept(UnifiedCall.java:130) at freemarker.core.Environment.visit(Environment.java:221) at freemarker.core.MixedContent.accept(MixedContent.java:92) at freemarker.core.Environment.visit(Environment.java:221) at freemarker.core.Environment.process(Environment.java:199) at freemarker.template.Template.process(Template.java:237) at freemarker.ext.servlet.FreemarkerServlet.process(FreemarkerServlet.java:452) at freemarker.ext.servlet.FreemarkerServlet.doGet(FreemarkerServlet.java:391) at javax.servlet.http.HttpServlet.service(HttpServlet.java:621) at javax.servlet.http.HttpServlet.service(HttpServlet.java:722) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:304) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:684) at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:471) at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:402) at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:329) at net.sourceforge.stripes.action.ForwardResolution.execute(ForwardResolution.java:110) at net.sourceforge.stripes.controller.DispatcherHelper$7.intercept(DispatcherHelper.java:508) at net.sourceforge.stripes.controller.ExecutionContext.proceed(ExecutionContext.java:158) at org.stripesstuff.plugin.security.SecurityInterceptor.intercept(SecurityInterceptor.java:140) at net.sourceforge.stripes.controller.ExecutionContext.proceed(ExecutionContext.java:155) at net.sourceforge.stripes.controller.HttpCacheInterceptor.intercept(HttpCacheInterceptor.java:99) at net.sourceforge.stripes.controller.ExecutionContext.proceed(ExecutionContext.java:155) at net.sourceforge.stripes.controller.BeforeAfterMethodInterceptor.intercept(BeforeAfterMethodInterceptor.java:113) at net.sourceforge.stripes.controller.ExecutionContext.proceed(ExecutionContext.java:155) at net.sourceforge.stripes.controller.ExecutionContext.wrap(ExecutionContext.java:74) at net.sourceforge.stripes.controller.DispatcherHelper.executeResolution(DispatcherHelper.java:502) at net.sourceforge.stripes.controller.DispatcherServlet.executeResolution(DispatcherServlet.java:286) at net.sourceforge.stripes.controller.DispatcherServlet.service(DispatcherServlet.java:170) at javax.servlet.http.HttpServlet.service(HttpServlet.java:722) at net.sourceforge.stripes.controller.DynamicMappingFilter$2.doFilter(DynamicMappingFilter.java:431) at net.sourceforge.stripes.controller.StripesFilter.doFilter(StripesFilter.java:247) at net.sourceforge.stripes.controller.DynamicMappingFilter.doFilter(DynamicMappingFilter.java:418) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:224) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:169) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:168) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100) at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:929) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:405) at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:964) at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:515) at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:302) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) at java.lang.Thread.run(Thread.java:619)

Environment

Freemarker 2.3.18
Stripes 1.5.6

Status

Assignee

Unassigned

Reporter

xhaggi

Labels

None

Tester

None

Components

Affects versions

Release 1.5.5
Release 1.5.4
Release 1.5.6

Priority

Major
Configure