4

I have 2 projects. One project (A) contains all my core functionality such as my entities/services/daos etc. It also contains many .ftl templates I wish to take advantage of and reuse in my other project (B). I have successfully gotten (B) to utilise the classes from (A) but I'm having no luck in reusing the freemarker templates.

Project (B) is a Spring Boot application (v2.1.3) and so I think I'm right in using the application.property: spring.freemarker.template-loader-path as opposed to defining a new @Bean.

Due to being a spring-boot application, by default and without this property the project will look in the projects own src/main/resources/templates location but my aim is for Project (A) to have none of its own templates and for my controller to return the templates found within Project (B).

Within my Maven Dependencies the hierachy is as thus:

projectA-0.0.1.jar
    templates
        folder1
            exampleA.ftl
        folder2
            exampleB.ftl

Currently my controllers are set to return things like return new ModelAndView("folder1/exampleA") where the context prefix is src/main/resources/templates/

Does anyone know the correct format of the value I need to give to the spring.freemarker.template-loader-path property in order to point to the templates in my dependency jar instead of the local src/main/resources/templates?

CtrlAltMe
  • 145
  • 2
  • 4
  • 14
  • 1
    So you are using `spring.freemarker.template-loader-path=classpath:/templates/`, and in the dependency jar the templates are also under `templates/`, and yet it doesn't find them? Not sure what technical issue gets into the way then... usually, that should just work. – ddekany Mar 31 '19 at 14:02
  • I don't know if it makes a difference or not, but the `templates/` in my dependency jar appears in my IDE as a package rather than a typical folder. But looking at the qualified name the value/location is the same as what I'd expect so I suspect not? – CtrlAltMe Apr 01 '19 at 08:07
  • 1
    It's fine if it shows it as a package, since there's only a single tree inside a jar, unlike in the source code that may follows Maven conventions to separate things to `java` and `resources`. – ddekany Apr 01 '19 at 08:22
  • 1
    I'm not familiar with the FreeMarker integration of Spring, but one thing that can be a problem in general when loading resources with `ClassLoader`-s is the choice of the class whose `ClassLoader` will be used for that. If Spring MVC uses its own class as the basic for that, and the related Spring jar is loaded from some location that's potentially shared by multiple applications (not impossible if you deploy to some fancy "enterprise" application server), then its class loader won't see application classes and resources. – ddekany Apr 01 '19 at 08:32
  • Thanks @ddekany, it was your last comment which got me to my conclusion. I thought I could take the shortcut and use the `spring.freemarker.template-loader-path` property due to using a version of Spring Boot that supported it. But your point about the `ClassLoader` (which I would never have thought about) led me to add the `@Bean` I added to the question (for visibility and formatting) to my config class and get it working perfectly! – CtrlAltMe Apr 01 '19 at 09:12
  • Why does this work though? I don't see anything there that could fix a `ClassLoader` related issue. Perhaps something was overriding the `spring.freemarker.template-loader-path` coming from `application.properties`, but now you force to the desired value? By the way, FreeMarker logs what the `TemplateLoader` was when it doesn't find a template; perhaps that contains a clue. – ddekany Apr 01 '19 at 09:18
  • I assumed a `@Bean` would have more say so in regards to configuration than that of the `spring.freemarker.template-loader-path` property so I figured it was worth a shot. Perhaps there was something overridding it as you suspect. I have seen nothing in any of my logs relating to `TemplateLoader` so unfortunately I have no clues to share at this time. – CtrlAltMe Apr 01 '19 at 09:35
  • You certainly have to set the `freemarker` log category to `DEBUG` in your logging configuration for that. – ddekany Apr 01 '19 at 16:16

4 Answers4

7

So spring.freemarker.template-loader-path=classpath:/templates/ was the answer to my original question, however it didn't solve my problem.

Adding the following @Bean to my config class did, credit to @ddekany

@Bean public FreeMarkerConfigurationFactoryBean getFreeMarkerConfiguration() { FreeMarkerConfigurationFactoryBean bean = new FreeMarkerConfigurationFactoryBean(); bean.setTemplateLoaderPath("classpath:/templates/"); return bean; }

Would appear that although I could use a property, due to other factors a @Bean was required in my scenario.

CtrlAltMe
  • 145
  • 2
  • 4
  • 14
2

Setting into property:

spring.freemarker.cache=false
spring.freemarker.template-loader-path=file:src/main/resources/templates/

worked for me.

Andrey Kostin
  • 21
  • 1
  • 1
0

It didn't work for me. But this approach does work:

    @Bean
    @Primary
    public FreeMarkerConfigurationFactoryBean getFreeMarkerConfiguration() throws IOException {
        FreeMarkerConfigurationFactoryBean bean = new FreeMarkerConfigurationFactoryBean();
        ClassTemplateLoader ctl = new ClassTemplateLoader(getClass(), "/templates");
        bean.setPostTemplateLoaders(ctl);
        return bean;
    }
Nesd
  • 101
  • 1
  • 8
0

I ended up with the following bean. If you are not using the spring-freemarker-starter maven dependency. I was using simpleJavamail so didn't need all the dependencies.

@Bean("freeMarkerConfiguration")
public freemarker.template.Configuration freeMarkerConfigurationFactoryBean() throws IOException {
    freemarker.template.Configuration freeMarkerConfiguration =
            new freemarker.template.Configuration(freemarker.template.Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
    freeMarkerConfiguration.setTemplateLoader(new ClassTemplateLoader(getClass(), "/templates"));
    return freeMarkerConfiguration;
}
Dhana Krishnasamy
  • 2,126
  • 17
  • 36
Sherin Syriac
  • 447
  • 6
  • 13