【问题标题】:Velocity in OSGi: how to load templates from classpathOSGi 中的 Velocity:如何从类路径加载模板
【发布时间】:2012-01-26 01:48:12
【问题描述】:

我正在使用速度模板引擎为 OSGi 开发应用程序。 它非常适合通过文件加载器加载我的模板,但现在我必须在我的 jar 中实现这个模板并将其作为资源加载。

我怎样才能让它工作?

我的代码:

ve = new VelocityEngine();
ve.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
ve.setProperty("classpath.resource.loader.class", 
    ClasspathResourceLoader.class.getName());
ve.setProperty("classpath.resource.loader.path", "/velocitytemplates");
ve.init();

ve.getTemplate("foo.vm");

这会抛出一个异常

找不到资源“index.vm”

原因:

org.apache.velocity.exception.ResourceNotFoundException:找不到资源“index.vm”

【问题讨论】:

  • 如果资源在你的包中,那么 Velocity 需要使用你的包的类加载器来加载它们。 Velocity 很可能正在使用其 自己的 类加载器,因此无法看到您的包中的资源。你需要找到一种方法来配置ClasspathResourceLoader 来告诉它使用哪个类加载器,否则你需要使用其他类型的资源加载策略。不幸的是,我对 Velocity 的了解不够,无法提供更具体的建议。

标签: java osgi velocity


【解决方案1】:

遗憾的是,Velocity 对 OSGi 并不友好。因此,您不能使用内置的 ClasspathResourceLoader,也很难添加自定义开发的 ResourceLoader。

我建议您应该以任何普通方式将您的模板作为阅读器并选择以下方法之一:

  • 如果您需要合并 很少使用模板(性能无关紧要)
  • 使用 Velocity 的内部类手动创建模板

如果您不必经常合并模板,则可以使用第一个选项,因此性能不是关键要求。

这是第二个选项的示例,其中创建的模板对象可以通过调用其上的合并函数来重用(期望您已经获得了 vm 文件或资源的 Reader):

RuntimeInstance runtimeInstance = new RuntimeInstance();
runtimeInstance.init();
SimpleNode simpleNode = runtimeInstance.parse(reader, "nameOfYourTemplateResource");

Template template = new Template();
simpleNode.init(new InternalContextAdapterImpl(new VelocityContext()), runtimeInstance);
template.setData(simpleNode);

template.merge(...);
...

要在 OSGi 中获取 vm 文件的读取器,您应该选择一个肯定与您的 vm 资源在同一个包中的类并调用 SameBundleClass.class.getResourceAsStream... 您可以使用 InputStreamReader 将您的流转换为写入器。

请注意,该示例遗漏了一些 try-catch-finally 块。

【讨论】:

  • 我没有使用 OSGi,但我们确实有一种本地开发的东西,让人想起它与多个类加载器 - 我们遇到了同样的问题。您的第二种方法,即手动创建模板,成功了!
【解决方案2】:

需要验证的两件事

1。类路径问题

确保通过 MANIFEST.MF 设置 OSGi 包的类路径以包含一个点:

Bundle-ClassPath: .

点表示将包的根包含在类加载层次结构中,您的文件夹“velocitytemplates”可能所在的位置。

并且您需要将 Velocity jar 文件放在模板文件所在的同一包中,否则您将遇到类加载问题,因为 Velocity 将驻留在不同的包中,因此在都在它的类路径中。

2。 ClasspathResourceLoader 没有“路径”

ClasspathResourceLoader 不支持设置“路径”,因为它根据定义使用 Classpath,因此要么将“velocitytemplates”添加到 OSGi 包 (MANIFESt.MF) 中的 Classpath 中,要么引用具有完整路径的速度模板,即“速度模板/index.vm"

【讨论】:

    【解决方案3】:

    我在基于类加载器的模板中遇到了类似的问题,我想指定不同的根目录。我通过子类化 ClasspathResourceLoader 来解决它。

    package my.package;
    
    import java.io.InputStream;
    import org.apache.commons.collections.ExtendedProperties;
    import org.apache.velocity.exception.ResourceNotFoundException;
    import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
    
    public class PrefixedClasspathResourceLoader 
        extends ClasspathResourceLoader
    {
        /** Prefix to be added to any names */
        private String prefix = "";
    
        @Override
        public void init(ExtendedProperties configuration) {
            prefix = configuration.getString("prefix","");
        }
    
        @Override
        public InputStream getResourceStream(String name)
                throws ResourceNotFoundException 
        {
            return super.getResourceStream(prefix+name);
        }
    }
    

    设置以下属性

    resource.loader=myloader
    myloader.resource.loader.class=my.package.PrefixedClasspathResourceLoader
    myloader.resource.loader.prefix=/velocitytemplates/
    

    这样,如果你有一个名为“index.vm”的模板,velocity 将使用类加载器找到一个名为“/velocitytemplates/index.vm”的资源

    【讨论】:

    • 但是,如果它在外部包中,velocity 如何找到您的下载器?如果我这样做,速度就找不到类PrefixedClasspathResourceLoader
    • @iberbeu 只要编译后的文件包含在类路径中,Velocity 就应该能够通过名称找到它。就我而言,我只是在调用 Velocity 的应用程序中包含了类加载器
    • 但在 OSGI 中,它必须包含在速度包的类路径中。那么如何告诉你将这个包导入到你的类加载器所在的位置呢?
    • 我不是 OSGI 专家,所以帮不上什么忙。除非您可以将 Velocity 包替换为您自己构建的包含 Velocity 和自定义加载器的包。快速谷歌搜索给出了spring.io/blog/2009/01/19/exposing-the-boot-classpath-in-osgi,如果您准备好让所有捆绑包都可以使用该类,这可能会很有用。
    【解决方案4】:

    两天后,我和一位同事找到了 Velocity Engine 默认的解决方案:file.resource.loader.class=org.apache.velocity.runtime.resource.loader.FileResourceLoader

    像这样创建自己的类资源加载器

    public static final class PdfResourceLoader extends ResourceLoader
    
    @Override
    public void init(ExtendedProperties configuration)
    {
    }
    
    @Override
    public InputStream getResourceStream(String source) throws ResourceNotFoundException
    {
      return getClass().getResourceAsStream(source);
    }
    
    @Override
    public boolean isSourceModified(Resource resource)
    {
      return false;
    }
    
    @Override
    public long getLastModified(Resource resource)
    {
      return 0;
    }
    }
    

    设置新的上下文类加载器

    Thread.currentThread().setContextClassLoader(PdfResourceLoader.class.getClassLoader());  
        VelocityEngine ve = new VelocityEngine();
    

    更改了速度引擎中的默认属性

    ve.setProperty("resource.loader", "pdf"); 
    ve.setProperty("pdf.resource.loader.class",
    PdfResourceLoader.class.getName());
    ve.init(); 
    

    示例名称路径模板

    String pathTemplate = "/templates/yourTemplateName.html";      
    Template t = ve.getTemplate(pathTemplate, "UTF-8"); 
    

    原来如此

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-10-27
      • 2010-11-19
      • 2016-08-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-15
      • 2018-04-16
      相关资源
      最近更新 更多