一尘不染

如何使用ClasspathResourceLoader从Velocity模板中包含文件

java

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

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

问题是,如果模板包含

#include("MyInclude.vm")

指令,Velocity无法找到“ MyInclude.vm”,并引发ResourceNotFoundException。

由于在getTemplate的参数中,我必须在程序包名称之前加上模板名称,因此我也尝试在模板中的#include中执行相同的操作:

#include("testpackage/MyInclude.vm")

但是唯一的区别是,如果我从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中,而不是像某些文件夹中的普通文件一样位于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;
  }
}

TemplateWithoutInclude.vm

<!DOCTYPE html>
<html>
    <head></head>
    <body>
        <p>Hello</p>
    </body>
</html>

TemplateWithInclude.vm

<!DOCTYPE html>
<html>
    <head></head>
    <body>
        #include("MyInclude.vm")
    </body>
</html>

MyInclude.vm

<p>
    Hello
</p>


阅读 702

收藏
2020-12-03

共1个答案

一尘不染

返回示例代码,可通过在Properties用于初始化引擎的实例中添加更多属性来解决该问题:

properties.setProperty(RuntimeConstants.EVENTHANDLER_INCLUDE, IncludeRelativePath.class.getName());

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

我也希望(对我)了解一些晦涩难懂的东西(例如对我来说)TEMPLATE_ROOT。是什么,因为我很难在任何地方找到记录的信息。但是无论如何,至少在我看来,它对应于Java项目的根软件包(“默认”软件包)。这意味着如果我不使用上面提到的其他属性,则将文件MyInclude.vm放在根包中即可。

2020-12-03