3

我正在集成 struts2、tiles3 和 Freemarker(来自数据库的模板)。集成已基本完成,但 freemarker 渲染器会立即渲染结果,而不是将其内容插入到模板中预期位置的位置。

关于如何纠正这个问题的任何建议?

预期输出

<!DOCTYPE html>
<html>
    <body>
        <h1>Template</h1>
<strong>A bold statement!</strong><div>a message from the Action...</div><div> I came from: the_location</div><div>a message from the Action...</div>
    </body>
</html>

实际输出(未格式化以提高可读性,因为这是模板问题而不是 html 问题,因此输出计数的准确性):

<strong>A bold statement!</strong><div>a message from the Action...</div><div> I came from: the_location</div>


<!DOCTYPE html>
<html>
    <body>
        <h1>Template</h1>

    </body>
</html>

瓷砖-defs.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE tiles-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN" "http://tiles.apache.org/dtds/tiles-config_3_0.dtd">
<tiles-definitions>
    <definition name="test_def2" template="/WEB-INF/template/template.jsp"> 
        <put-attribute name="body" value="the_location" type="db_freemarker"/>
    </definition>
</tiles-definitions>

/WEB-INF/template/template.jsp

<%@taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %>
<%@taglib prefix="s" uri="/struts-tags"%>
<%@page contentType="text/html" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
    <body>
        <h1>Template</h1>
        <tiles:insertAttribute name="body"/>
    </body>
</html>

使用键“db_freemarker”创建并注册了一个 DbFreemarkerTilesRenderer,这是 DbFreemarkerTilesRenderer 的实现:

package com.quaternion.struts2.result.freemarker;

import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.tiles.request.Request;
import org.apache.tiles.request.freemarker.render.FreemarkerRenderer;
import org.apache.tiles.request.render.CannotRenderException;
import org.apache.tiles.request.servlet.ServletRequest;


public class DbFreemarkerTilesRenderer extends FreemarkerRenderer{
    public DbFreemarkerTilesRenderer(){
        super(null); //Expects a AttributeValueFreemarkerServlet  
    }

        /** {@inheritDoc} */
    @Override
    public void render(String location, Request request) throws IOException {
        if (location == null) {
            throw new CannotRenderException("Cannot dispatch a null path");
        }
        ServletRequest servletRequest = org.apache.tiles.request.servlet.ServletUtil.getServletRequest(request);
        HttpServletRequest httpRequest = servletRequest.getRequest();
        HttpServletResponse httpResponse = servletRequest.getResponse();

        DataBaseTemplateLoader dataBaseTemplateLoader = new DataBaseTemplateLoader();
        try {
            dataBaseTemplateLoader.render(location, httpRequest, httpResponse);
        } catch (Exception ex) {
            Logger.getLogger(DbFreemarkerTilesRenderer.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

“DataBaseTemplateLoader”实际上是从硬编码的字符串中加载 freemarker 模板,但是一旦解决了这个问题,它将从数据库中加载(请参阅渲染 方法,该类的其余部分包含在上下文中,Struts2 开发人员会将其识别为结果类型,Freemarker DB 功能的想法可以作为结果直接提供给 Struts2):

package com.quaternion.struts2.result.freemarker;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.inject.Inject;
import freemarker.cache.StringTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.dispatcher.ServletDispatcherResult;
import org.apache.struts2.views.freemarker.FreemarkerManager;
import org.apache.struts2.views.util.ContextUtil;


public class DataBaseTemplateLoader extends ServletDispatcherResult {

    private static final Logger log = Logger.getLogger(DataBaseTemplateLoader.class.getName());
    private String defaultEncoding = "ISO-8859-1"; //TODO: Hardcoded?
    private FreemarkerManager freemarkerManager;
    private Configuration configuration;

    //Inject works if this is a Struts2 component (Result) however
    //when using this from Tiles, the following is not injected...
    @Inject
    public void setFreemarkerManager(FreemarkerManager mgr) {
        this.freemarkerManager = mgr;
    }

    public DataBaseTemplateLoader() {
        super();
    }

    public DataBaseTemplateLoader(String location) {
        super(location); //TODO: look up template based on "location" string
    }

    protected Configuration getConfiguration() throws TemplateException {
        if (freemarkerManager == null){
            //Force injection
            freemarkerManager  = ActionContext.getContext().getContainer().getInstance(FreemarkerManager.class);
        }
        return freemarkerManager.getConfiguration(ServletActionContext.getServletContext());
    }

    @Override
    public void doExecute(String location, ActionInvocation invocation) throws Exception {
        HttpServletRequest request = ServletActionContext.getRequest();
        HttpServletResponse response = ServletActionContext.getResponse();
        render(location, request, response);
    }

    public void render(String location, HttpServletRequest request, HttpServletResponse response) throws Exception {
        //location is String passed from Struts2 action which will formated 
        //in such a way to apply conventions though to view
        //Format: namespace#action#method 
        //setLocation(location);

        StringTemplateLoader stringLoader = new StringTemplateLoader();

        //BOTH "name" and "source" will be retrieved from the DB
        String name = "firstTemplate";
        String source = "<strong>A bold statement!</strong><div>${action.message}</div><div> I came from: " + location + "</div>";

        stringLoader.putTemplate(name, source);
        // It's possible to add more than one template (they might include each other)
        // String secondTemplate = "<#include \"greetTemplate\"><@greet/> World!";
        // stringLoader.putTemplate("greetTemplate", secondTemplate);
        freemarker.template.Configuration cfg = getConfiguration();
        cfg.setTemplateLoader(stringLoader);
        Template template = cfg.getTemplate(name);
        Map map = ContextUtil.getStandardContext(ActionContext.getContext().getValueStack(), request, response);

        response.setContentType("text/html;charset=" + defaultEncoding);
        template.process(map, response.getWriter());
    }
}

Struts2 动作不需要,只显示“来自动作的消息......”来自哪里:

package com.quaternion.tilesdb.action;

import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.convention.annotation.ParentPackage;
import org.apache.struts2.convention.annotation.Result;

/**
 * Tests Tiles with Freemarker integration, where the freemarker template 
 * is parsed from a String, this is to allow later integration with freemarker
 * database templates
 */
@ParentPackage("quaternion-results")
@Result(type="tiles", location="test_def2")
public class TilesFtlTest2 extends ActionSupport{
    private String message = "a message from the Action...";

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}
4

1 回答 1

2

我只是想结束这个问题,答案是我缺少 web.xml 中的磁贴配置(Freemarker servlet 配置)。

这是允许上述工作的 web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <listener>
        <listener-class>com.quaternion.struts2.result.freemarker.MoreCompleteAutoloadTilesListener</listener-class>
        <!--<listener-class>org.apache.tiles.extras.complete.CompleteAutoloadTilesListener</listener-class>-->
    </listener>    
    <!-- Following is required to enable freemarker(ftl) support-->
    <servlet>
        <servlet-name>freemarker</servlet-name>
        <servlet-class>org.apache.tiles.request.freemarker.servlet.SharedVariableLoaderFreemarkerServlet</servlet-class>

        <!-- FreemarkerServlet settings: -->
        <init-param>
            <param-name>TemplatePath</param-name>
            <param-value>/WEB-INF/template/</param-value>
        </init-param>
        <init-param>
            <param-name>NoCache</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>ContentType</param-name>
            <param-value>text/html</param-value>
        </init-param>

        <!-- FreeMarker settings: -->
        <init-param>
            <param-name>template_update_delay</param-name>
            <param-value>0</param-value> <!-- 0 is for development only! Use higher value otherwise. -->
        </init-param>
        <init-param>
            <param-name>default_encoding</param-name>
            <param-value>ISO-8859-1</param-value>
        </init-param>
        <init-param>
            <param-name>number_format</param-name>
            <param-value>0.##########</param-value>
        </init-param>
        <init-param>
            <param-name>org.apache.tiles.request.freemarker.CUSTOM_SHARED_VARIABLE_FACTORIES</param-name>
            <param-value>tiles,org.apache.tiles.freemarker.TilesSharedVariableFactory</param-value>
        </init-param>
        <load-on-startup>5</load-on-startup>
    </servlet>
    <filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <welcome-file-list>
        <welcome-file>/index.action</welcome-file>
    </welcome-file-list>
</web-app>
于 2013-09-15T16:38:14.013 回答