matiwinnetou / spring-soy-view

Google Closure Templates integration with Spring MVC
Apache License 2.0
16 stars 9 forks source link

CompositeTemplateFilesResolver? #44

Closed phasebash closed 3 years ago

phasebash commented 10 years ago

I've come about an interesting limitation (feature?) of the PathMatchingResourcePatternResolver used by the ClasspathTemplateFilesResolver. First, some background information...

In our applications, we've started to stray from the War packaging for both services and frontend webapps, instead packaging executable Jars with embedded Jetty servers. When run from Gradle using the application plugin "run" task, the ClasspathTemplateFilesResolver will only find templates on the first classpath root found if a pattern can span multiple classpath roots.

For example: assume two modules

  1. foo-bar-application (like a war, but a jar, has embedded jetty server and app specific templates in /foo-bar-application/soy)
  2. foo-bar-components (a shared jar with soy resources on classpath in /foo-bar-components/soy)

With one ClasspathTemplateFilesResolver we could make a pattern like so:

<bean id="templateFileResolver" class="pl.matisoft.soy.template.ClasspathTemplateFilesResolver">
    <property name="hotReloadMode" value="${hotReloadEnabled}"/>
    <property name="templatesLocation" value="classpath*:foo-bar-*/soy"/>
</bean>

This works, but not all the time.

Note, we cannot use /foo-bar/application and /foo-bar/components due to a limitation in the Java classloader which leaks through the PathMatchingResourcePatternResolver.

Anyway, when running ./gradlew run from foo-bar-application, Soy templates in the application will be of type FileSystemResource, and those from the components Jar should be of type ClassPathResource. The problem is, they will not be found in this particular configuration. Within an IDE, the resources will all be of type FileSystemResource, and templates from both modules are found. If the pattern above is adjusted with less wildcards, it can find templates on either root, just not both at the same time.

Anyway. What do you think about a CompositeTemplateFilesResolver? It would take an Iterable of TemplateFilesResolvers, and allow the client to specify as many simple TemplateFilesResolvers as they want. Or, should ClasspathTemplateFilesResolver take a List of paths (like ApplicationContext does for contextLocations)?

matiwinnetou commented 10 years ago

The idea being to support both developer mode and production mode via this CompositeTemplateFilesResolver?

phasebash commented 10 years ago

The root problem is that PathMatchingResourcePatternResolver is limited heavily by the underlying classloader, so it's very difficult to make patterns which will match across classpath roots. We've got templates in two modules: 1 module that is a published Jar other projects use, and 1 module which is a webapp with it's own app-specific Soys. Using a single ClasspathTemplateFilesResolver, I cannot scan both reliably.

Otherwise, PathMatchingResourcePatternResolver is great, and works very well on one root.

phasebash commented 10 years ago

Here is what we're using now (mind the Groovy):

class CompositeTemplateFilesResolver implements TemplateFilesResolver {

    /**
     * The collection of TemplateFilesResolvers to use.
     */
    private Collection<TemplateFilesResolver> templateFilesResolvers

    @Required
    void setTemplateFilesResolvers(Collection<TemplateFilesResolver> templateFilesResolvers) {
        this.templateFilesResolvers = templateFilesResolvers
    }

    @Override
    Collection<URL> resolve() throws IOException {
        templateFilesResolvers.collectMany {
            it.resolve().findAll { it }
        }
    }

    @Override
    Optional<URL> resolve(String templateName) throws IOException {
        templateFilesResolvers.findResult Optional.absent(), {
            Optional<URL> o = it.resolve(templateName)

            // findResult requires null value to flag 'false'
            o.present ? o : null
        }
    }
}
matiwinnetou commented 10 years ago

I am fine with this contribution if you wish to convert this to Java. I think the real trick will be for people to get their head around in terms how to use all these classes and the right patterns of integrating the library in the project.

phasebash commented 10 years ago

People might hate it like they do java.io and all those decorators. Most Spring classes seem to offer Lists of strings for similar kinds of use cases. I'll look at working that into the ClasspathTFR, if you agree.

matiwinnetou commented 10 years ago

You mean to write a CompositeTemplateFilesResolver or something else? I am lost a bit. Is this referring to this issue or something new?

matiwinnetou commented 10 years ago

Can you contribute some class for this? Actually one could think there are lots of possibilities for this, should this project support this composite?