Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Html head flushing with freemarker

I have a Spring MVC web application that uses freemarker as the template language. I am currently working on the changes to flush the html head section rather than buffering the whole html and flushing at the end. I tried setting the auto_flush freemarker setting to false and used freemarker's builtin <#flush> directive as below, but that doesn't seem to work.

common-header.ftl
<head>
 .......
</head>
<#flush>

page.ftl
<#include "common-header.ftl" />
<body>
 .......
</body>

I would appreciate your help with this. Also, per the API documentation, autoFlush() seems to only work for pages which aren't composed with #include statements and require multiple Template.process() methods. If that's correct, should i write a custom template processor to handle the head and body sections in my page ? Any pointers would be helpful.

Update:

Tried using FreeMarkerView.java as the view class as it uses the default writer (PrinterWriter) of HttpServletResponse to process the writer. This doesn't work either though PrinterWriter does support flush() and the <#flush> freemarker directive in my template doesn't seem to be invoking this.

Tried extending the FreeMarkerView class to wrap the PrinterWriter inside a BufferedWriter, and that doesn't work as well.

<bean id="viewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
    <property name="suffix"><value>.ftl</value></property>
    <property name="contentType"><value>text/html; charset=utf-8</value></property>
    <property name="viewClass" value="org.springframework.web.servlet.view.freemarker.FreeMarkerView"/>
    <property name="exposeSpringMacroHelpers"><value>true</value></property>
</bean>

I would appreciate any help with this.

like image 831
soontobeared Avatar asked Oct 21 '22 08:10

soontobeared


2 Answers

<#flush> simply calls Writer.flush() on the Writer that was given to Template.process. If, for example, that Writer is a StringWriter, the flush() call will not do nothing of course. The thing that passes the Writer to FreeMarker will have to ensure that that Writer does the right thing when its flush() method is called.

auto_flush is unrelated to your problem. (But you misunderstand the API docs. auto_flush is always supported. The docs describe the case when you want to set it to false.)

like image 105
ddekany Avatar answered Oct 23 '22 04:10

ddekany


Not an answer to the original question but to @soontobeared comment. I was never notified about your comment so sorry for the late response.

I'm using a buffered view during development so I can catch various exception and output debug etc.. This will give access to the Writer. I'm using Spring 3.2 and FreeMarker 2.3.

BufferedFreeMarkerView.java

package com.example.web;

import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Set;

import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.view.freemarker.FreeMarkerView;

import freemarker.core.InvalidReferenceException;
import freemarker.template.SimpleHash;
import freemarker.template.Template;
import freemarker.template.TemplateException;

public class BufferedFreeMarkerView extends FreeMarkerView {

    @Override
    protected void processTemplate(Template template, SimpleHash model,
            HttpServletResponse response) throws IOException, TemplateException {
        StringWriter buffer = new StringWriter(50000);
        try {
            template.process(model, buffer);
        } catch (TemplateException e) {
            logger.warn(e.getMessage() + "\n" + e.getFTLInstructionStack(), e);
            throw new RuntimeException(e.getMessage() + "\n"
                    + e.getFTLInstructionStack(), e);
        } // ommited more catches

        try (Writer out = response.getWriter()) {
            out.write(buffer.toString());
        }
    }

}

BufferedFreeMarkerViewResolver.java

package com.example.web;

import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver;

public class BufferedFreeMarkerViewResolver extends FreeMarkerViewResolver {

    /**
     * 
     */
    public BufferedFreeMarkerViewResolver() {
        setViewClass(requiredViewClass());
    }

    @SuppressWarnings("rawtypes")
    @Override
    protected Class requiredViewClass() {
        return BufferedFreeMarkerView.class;
    }

}

From spring dispatcher context

<bean id="viewResolver" class="com.example.web.BufferedFreeMarkerViewResolver">
    <property name="cache" value="true"/>
    <property name="prefix" value=""/>
    <property name="suffix" value=".ftl"/>
    <property name="allowSessionOverride" value="true"/>
    <property name="exposeSpringMacroHelpers" value="true"/>
    <property name="exposeRequestAttributes" value="true"/>
    <property name="exposeSessionAttributes" value="true"/>
    <property name="requestContextAttribute" value="rc"/>
</bean>
like image 37
Goose Avatar answered Oct 23 '22 02:10

Goose