Emiliano Schiano
Emiliano Schiano

Reputation: 1922

Thymeleaf Spring Mail context error

I´m trying to configure spring mail with thymeleaf template engine but there is an error that I cant figure out why is happening. I´ve seen various topics with this issue, but all suggestions given to solve it seem to be right in my project.

The problem is with the Context trying to be resolved with the ServletContextTemplateResolver.

My configuration is:

@Bean 
public TemplateResolver webTemplateResolver () { 
    final TemplateResolver templateResolver = new ServletContextTemplateResolver(); 
    templateResolver.setPrefix("/WEB-INF/views/"); 
    templateResolver.setSuffix(".html"); 
    templateResolver.setTemplateMode("HTML5"); 
    templateResolver.setOrder(2); 
    return templateResolver; 
}

@Bean 
public TemplateResolver emailTemplateResolver () { 
    TemplateResolver templateResolver = new ClassLoaderTemplateResolver(); 
    templateResolver.setPrefix("mail/"); 
    templateResolver.setSuffix(".html"); 
    templateResolver.setTemplateMode("HTML5"); 
    templateResolver.setOrder(1); 
    return templateResolver; 
} 

@Bean 
public SpringTemplateEngine templateEngine (IMessageResolver messageResolver) { 
    final SpringTemplateEngine templateEngine = new SpringTemplateEngine(); 
    templateEngine.addTemplateResolver(webTemplateResolver()); 
    templateEngine.addTemplateResolver(emailTemplateResolver()); 
    templateEngine.addDialect(new LayoutDialect()); 
    templateEngine.addDialect(new SpringSecurityDialect()); 
    templateEngine.addMessageResolver(messageResolver); 
    return templateEngine; 
} 

The code is:

@Transactional(readOnly = true) 
public void sendContactEmail (final String recipientName, final String recipientEmail, final String imageResourceName, final byte[] imageBytes, final String imageContentType, final Locale locale) throws MessagingException 
{ 
    // Prepare the evaluation context 
    final Context ctx = new Context(locale); 
    ctx.setVariable("name", recipientName); 
    ctx.setVariable("subscriptionDate", new Date()); 
    ctx.setVariable("hobbies", Arrays.asList("Cinema", "Sports", "Music")); 
    ctx.setVariable("imageResourceName", imageResourceName); // so that we can reference it from HTML 

    // Prepare message using a Spring helper 
    final MimeMessage mimeMessage = this.mailSender.createMimeMessage(); 
    final MimeMessageHelper message = new MimeMessageHelper(mimeMessage, true, "UTF-8"); // true = multipart 
    message.setSubject("Example HTML email with inline image"); 
    message.setFrom("[email protected]"); 
    message.setTo(recipientEmail); 

    // Create the HTML body using Thymeleaf 
    final String htmlContent = this.templateEngine.process("contact.html", ctx);
    message.setText(htmlContent, true); // true = isHtml 

    // Add the inline image, referenced from the HTML code as "cid:${imageResourceName}" 
    final InputStreamSource imageSource = new ByteArrayResource(imageBytes); 
    message.addInline(imageResourceName, imageSource, imageContentType); 

    // Send mail 
    this.mailSender.send(mimeMessage); 
} 

And finally the error:

[THYMELEAF][http-bio-8080-exec-8] Exception processing template "contact.html": Resource resolution by ServletContext with org.thymeleaf.resourceresolver.ServletContextResourceResolver can only be performed when context implements org.thymeleaf.context.IWebContext [current context: org.thymeleaf.context.Context] 
Exception handled by GlobalExceptionHandler 
org.thymeleaf.exceptions.TemplateProcessingException: Resource resolution by ServletContext with org.thymeleaf.resourceresolver.ServletContextResourceResolver can only be performed when context implements org.thymeleaf.context.IWebContext [current context: org.thymeleaf.context.Context] 
        at org.thymeleaf.resourceresolver.ServletContextResourceResolver.getResourceAsStream(ServletContextResourceResolver.java:78) 
        at org.thymeleaf.TemplateRepository.getTemplate(TemplateRepository.java:221) 
        at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1104) 
        at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1060) 
        at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1011) 
        at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:924) 
        at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:898) 
        at com.proeza.core.service.MailService.sendContactEmail(MailService.java:47) 
        at com.proeza.core.service.MailService$$FastClassBySpringCGLIB$$f139c878.invoke(<generated>) 
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) 
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:711) 
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) 
        at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:98) 
        at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:262) 
        at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95) 
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) 
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:644) 
        at com.proeza.core.service.MailService$$EnhancerBySpringCGLIB$$42cabd9f.sendContactEmail(<generated>) 
        at com.proeza.sgs.web.controller.RegisterController.register(RegisterController.java:69) 
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) 
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
        at java.lang.reflect.Method.invoke(Method.java:606) 
        at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:215) 
        at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132) 
        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104) 
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:749) 
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:690) 
        at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:83) 
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:945) 
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:876) 
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961) 
        at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:863) 
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:647) 
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837) 
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:728) 
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305) 
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) 
        at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88) 
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108) 
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243) 
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) 
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) 
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243) 
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) 
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) 
        at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:118) 
        at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84) 
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
        at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113) 
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
        at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103) 
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
        at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113) 
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
        at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:154) 
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
        at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45) 
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
        at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:199) 
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
        at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110) 
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
        at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:105) 
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108) 
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
        at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:57) 
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108) 
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
        at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87) 
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
        at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50) 
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108) 
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
        at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192) 
        at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160) 
        at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:344) 
        at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:261) 
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243) 
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) 
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222) 
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123) 
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502) 
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171) 
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100) 
        at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953) 
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118) 
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408) 
        at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1041) 
        at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:603) 
        at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312) 
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) 
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) 
        at java.lang.Thread.run(Thread.java:744) 

Hope someone can help. Thanks in advance.

Regards!

Upvotes: 2

Views: 3219

Answers (1)

Emiliano Schiano
Emiliano Schiano

Reputation: 1922

Ok, debugging thymeleaf I could find the problem. What is wrong is the way telling the engine to resolve the template. This line:

// Create the HTML body using Thymeleaf 
final String htmlContent = this.templateEngine.process("contact.html", ctx);

As the resolver is configured this way

templateResolver.setSuffix(".html");

It is trying to find a template named "contant.html.html" so it´s not finding it cause obvious reasons.

Processing de template this way:

// Create the HTML body using Thymeleaf 
final String htmlContent = this.templateEngine.process("contact", ctx); //Note .html not present

the applicaciont works perfectly.

What is weird, is that the example in thymeleaf docs has the html suffi but I´ve tried it and it works fine.

Can the thymeleaf versions be causing this difference?

2.1.3.RELEASE vs 2.1.4.RELEASE

Reagards.

Upvotes: 3

Related Questions