如何使用ClasspathResourceLoader从Velocity模板中#include一个文件

San*_*ors 17 java templates velocity

我正在处理一些Java代码,其中Velocity 1.7设置为通过ClasspathResourceLoader检索模板.下面是代码的精简样本.它来自在Jetty服务器上运行的Tapestry Web应用程序.

Java类,模板和要包含的文件都在同一个文件夹"testpackage"中,因此在生成的JAR中它们都在同一个包"testpackage"中.

问题是,如果模板包含

#include("MyInclude.vm")
Run Code Online (Sandbox Code Playgroud)

指令,Velocity找不到"MyInclude.vm",它抛出一个ResourceNotFoundException.

因为在getTemplate的参数中我必须将包名称添加到模板名称,我还尝试在模板内的#include中执行相同的操作:

#include("testpackage/MyInclude.vm")
Run Code Online (Sandbox Code Playgroud)

但唯一的区别是后者可以运行,如果我从Eclipse运行Web应用程序,而前者甚至不能从Eclipse工作.如果我构建,部署JAR并从部署中运行Web应用程序,则两种语法都会失败并出现相同的ResourceNotFoundException.

http://velocity.apache.org/engine/releases/velocity-1.7/user-guide.html#Include上的Velocity文档说:

"出于安全原因,要包含的文件可能只在TEMPLATE_ROOT下"

这肯定可能是我的问题的原因,但我还没有找到任何关于TEMPLATE_ROOT实际上是什么的进一步信息.

这听起来很像环境变量,但我不知道应该将它设置为什么,因为我使用的是ClasspathResourceLoader,并且要包含的文件不是位于文件夹中的实际文件,它位于JAR内部包含模板和Java类(以及所有在同一个包中).

我在另一个问题中找到了TEMPLATE_ROOT,我应该在哪里为使用Maven构建的命令行实用程序放置Velocity模板文件?,但它与使用FileResourceLoader有关.我需要继续使用ClasspathResourceLoader,我需要所有文件都在JAR中,而不是像某些文件夹中的普通文件一样.

TestVelocity.java

package testpackage;

import java.io.StringWriter;
import java.util.Properties;

import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;

public class TestVelocity
{
  public static String getText()
  {
    String text = "";

    Properties properties = new Properties();

    properties.setProperty("resource.loader", "class");

    properties.setProperty("class.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");

    VelocityEngine engine = new VelocityEngine();

    VelocityContext context = new VelocityContext();

    StringWriter writer = new StringWriter();

    try
    {
      engine.init(properties);

      // This works because the template doesn't contain any #include
      Template template = engine.getTemplate("testpackage/TemplateWithoutInclude.vm");

      // This causes ResourceNotFoundException: Unable to find resource 'TemplateWithInclude.vm'
      // Template template = engine.getTemplate("testpackage/TemplateWithInclude.vm");

      template.merge(context, writer);

      text = writer.toString();
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
    return text;
  }
}
Run Code Online (Sandbox Code Playgroud)

TemplateWithoutInclude.vm

<!DOCTYPE html>
<html>
    <head></head>
    <body>
        <p>Hello</p>
    </body>
</html>
Run Code Online (Sandbox Code Playgroud)

TemplateWithInclude.vm

<!DOCTYPE html>
<html>
    <head></head>
    <body>
        #include("MyInclude.vm")
    </body>
</html>
Run Code Online (Sandbox Code Playgroud)

MyInclude.vm

<p>
    Hello
</p>
Run Code Online (Sandbox Code Playgroud)

San*_*ors 19

重新获取示例代码,通过向Properties用于初始化引擎的实例添加一个属性来解决问题:

properties.setProperty(RuntimeConstants.EVENTHANDLER_INCLUDE, IncludeRelativePath.class.getName());
Run Code Online (Sandbox Code Playgroud)

这允许引用要包括的文件的路径作为相对于包含模板所在的文件夹的路径.因此,如果两个文件都在同一个文件夹中,则不需要在#include指令中指定路径:只是#include("MyInclude.vm").

我也希望学习一些关于模糊的东西(对我来说)TEMPLATE_ROOT,就像前者一样.它是什么,因为我很难在任何地方找到这些信息.但不管它是什么,至少在我的情况下它对应于Java项目的根包("默认"包).这意味着如果我不使用上面提到的附加属性,那么将文件MyInclude.vm放在根包中就可以了.