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

时间:2014-02-12 14:12:38

标签: java templates velocity

我正在处理一些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 doc说:

  

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

这肯定是我的问题的原因,但我无法找到有关TEMPLATE_ROOT实际上是什么的更多信息。

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

我在另一个问题Where should I put Velocity template files for a command line utility built with Maven?中发现了TEMPLATE_ROOT,但它与使用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;
  }
}

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>

1 个答案:

答案 0 :(得分:18)

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

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

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

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