Как # включить файл из шаблона Velocity с помощью ClasspathResourceLoader

Я имею дело с некоторым кодом Java, где Velocity 1.7 установлен для извлечения шаблонов через ClasspathResourceLoader. Ниже приведен фрагмент кода. Это из веб-приложения Tapestry, работающего на сервере Jetty.

Класс Java, шаблоны и файл, который должен быть включен, находятся в одной папке "testpackage", поэтому в полученном JAR все они находятся в одном пакете "testpackage".

Проблема заключается в том, что если шаблон содержит

#include("MyInclude.vm")

Velocity не может найти "MyInclude.vm", и он выдает исключение ResourceNotFoundException.

Так как в аргументе getTemplate я должен добавить имя пакета к имени шаблона, я также попытался сделать то же самое в #include внутри шаблона:

#include("testpackage/MyInclude.vm")

но единственное отличие заключается в том, что последнее работает, если я запускаю веб-приложение из Eclipse, а первое не работает даже с Eclipse. Если я создам, развертываю JAR и запускаю веб-приложение из развертывания, оба синтаксиса терпят неудачу с тем же исключением ResourceNotFoundException.

Документ Velocity в http://velocity.apache.org/engine/releases/velocity-1.7/user-guide.html#Include говорит:

"Из соображений безопасности файл, который должен быть включен, может быть только TEMPLATE_ROOT"

Это определенно может быть причиной моей проблемы, но я не смог найти никакой дополнительной информации о том, что на самом деле TEMPLATE_ROOT.

Это звучит очень похоже на переменную среды, но я не знаю, что мне следует установить, поскольку я использую ClasspathResourceLoader, а файл, который будет включен, не является фактическим файлом, находящимся в папке, он внутри JAR, содержащего шаблоны и класс Java (и все в одном пакете).

Я нашел TEMPLATE_ROOT, упомянутый в другом вопросе, Где я должен помещать файлы шаблонов Velocity для утилиты командной строки, созданной с помощью Maven?, но она связана с использованием 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

Повторите пример кода, проблема решена путем добавления еще одного свойства к экземпляру Properties, используемому для инициализации движка:

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

Это позволяет ссылаться на путь файла, который будет включен в качестве пути относительно папки, в которой включен шаблон включения. Поэтому, если оба файла находятся в одной папке, в директиве #include не указывается путь: просто #include("MyInclude.vm").

Я также надеялся узнать что-то о неясном (мне) TEMPLATE_ROOT, как и напр. что это такое, поскольку мне удивительно сложно найти эту информацию в любом месте. Но что бы это ни было, по крайней мере в моем случае это соответствует корневому пакету проекта Java (пакет по умолчанию). Это означает, что, если я не использую дополнительное свойство, упомянутое выше, то размещение файла MyInclude.vm в корневом пакете работает.