martes, 11 de febrero de 2014

Thymeleaf: Resource resolution by ServletContext with ServletContextResourceResolver

Al momento de configurar Thymeleaf, trate de forzar con LinkedHashSet que el set siempre estuviera ordenado. Aunque Thymeleaf lo hace internamente me imagino, para no perder el orden de los template resolvers, solo queria asegurarme que desde un principio estuvieran ordenados y mantuvieran el orden.

<bean id="templateEngine" class="org.thymeleaf.spring3.SpringTemplateEngine">
<property name="templateResolvers">
<set value-type="java.util.LinkedHashSet">
                                <ref bean="emailTemplateResolver"/>
<ref bean="webTemplateResolver"/>
</set>
</property>
<property name="additionalDialects">
<set>
<bean class="org.thymeleaf.extras.springsecurity3.dialect.SpringSecurityDialect" />
<bean class="org.thymeleaf.extras.conditionalcomments.dialect.ConditionalCommentsDialect" />
<bean class="nz.net.ultraq.thymeleaf.LayoutDialect" />
</set>
</property>
</bean>

Al final siempre obtenia la misma excepcion. Leyendo el codigo de Thymeleaf en la clase ServletContextResourceResolver se dispara la excepcion cuando el contexto que se recibe como parametro no es una instancia de IWebContext.

Pero en TemplateRepository existe un ciclo que itera los template resolvers definidos en el xml de configuracion e inyectados por spring, los cuales vienen en un Set, en un LinkedHashSet, pero en mis pruebas siempre viene ServletContextResourceResolver (webTemplateResolver) primero y después ClassLoaderTemplateResolver (emailTemplateResolver) por alguna razon no se iteran en orden.

La solucion que encontre mas sencilla es: en lugar de lanzar la expecion, regresar null, de esta manera el ciclo en TemplateRepository seguira buscando algun template resolver que pueda manejar la plantilla que se le envia, y por lo tanto le da oportunidad de que ClassLoaderTemplateResolver pueda encontrar la plantilla, y la encuentra.

public InputStream getResourceAsStream(final TemplateProcessingParameters templateProcessingParameters, final String resourceName) {
       
        Validate.notNull(templateProcessingParameters, "Template Processing Parameters cannot be null");
        Validate.notNull(resourceName, "Resource name cannot be null");
       
        final IContext context = templateProcessingParameters.getContext();
        if (!(context instanceof IWebContext)) {
        if (logger.isWarnEnabled()) {
        logger.warn(
                    "Resource resolution by ServletContext with {} " +
                    "can only be performed " +
                    "when context implements {} " +
                    " [current context: {} ]", new Object[] { this.getClass().getName(),
                    IWebContext.class.getName(), context.getClass().getName() });
        }
            return null;
        }
       
        final ServletContext servletContext =
            ((IWebContext)context).getServletContext();
        if (servletContext == null) {
            throw new TemplateProcessingException("Thymeleaf context returned a null ServletContext");
        }
       
        return servletContext.getResourceAsStream(resourceName);
       
    }

El jar de la version 2.0.15 que es la version que estoy usando.