Unable to Send Rich Text Email - Thymeleaf + Spring 4 - java

I'm trying to send a rich text email using Thymeleaf + Spring 4. I've followed the example on Thymeleaf's website but for some reason it's throwing an exception.
Here is my config:
#Bean
public ServletContextTemplateResolver templateResolver() {
ServletContextTemplateResolver resolver = new ServletContextTemplateResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".html");
resolver.setCharacterEncoding("UTF-8");
resolver.setTemplateMode("HTML5");
resolver.setOrder(2);
return resolver;
}
#Bean
public ClassLoaderTemplateResolver emailTemplateResolver() {
ClassLoaderTemplateResolver resolver = new ClassLoaderTemplateResolver();
resolver.setPrefix("/WEB-INF/views/mail/");
resolver.setSuffix(".html");
resolver.setTemplateMode("HTML5");
resolver.setCharacterEncoding("UTF8");
resolver.setOrder(1);
return resolver;
}
#Bean
public SpringTemplateEngine templateEngine() {
final SpringTemplateEngine engine = new SpringTemplateEngine();
final Set<TemplateResolver> templateResolvers = new HashSet<TemplateResolver>();
templateResolvers.add(templateResolver());
templateResolvers.add(emailTemplateResolver());
engine.setTemplateResolvers(templateResolvers);
return engine;
}
As you can see from the above config, I've two resolvers; one for the pages and one for the email template. Based on the research I've done online when I try to fire up an email using the template that I have, it is trying to use the templateResolver() instead of the emailTemplateResolver() although orders are set correctly.
Here is my email service:
Autowired
private JavaMailSender mailSender;
#Autowired
private TemplateEngine templateEngine;
private static Logger logger = LoggerFactory.getLogger(EmailService.class);
/*
* Send HTML mail (simple)
*/
public void sendSimpleMail(final Locale locale)
throws MessagingException {
final String recipientName = "Name";
final String recipientEmail = "Email";
// Prepare the evaluation context
final Context ctx = new Context(locale);
ctx.setVariable("name", recipientName);
// Prepare message using a Spring helper
final MimeMessage mimeMessage = this.mailSender.createMimeMessage();
final MimeMessageHelper message = new MimeMessageHelper(mimeMessage, "UTF-8");
message.setSubject("Example HTML email (simple)");
message.setFrom("thymeleaf#example.com");
message.setTo(recipientEmail);
// Create the HTML body using Thymeleaf
final String htmlContent = this.templateEngine.process("email-simple", ctx);
message.setText(htmlContent, true /* isHtml */);
// Send email
this.mailSender.send(mimeMessage);
}
Here is the exception that is being thrown:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is 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]
Has anyone had any ideas how I can resolve this issue?
Thanks in advance

Hey I ran into this issue before and after some research I found a way around it. Change your Java config to something like this, you will also have to move your html content to /WEB-INF/classes/mail/ because ClassLoaderTemplateResolver looks in the classes directory for your files.
#Bean
public TemplateResolver templateResolver() {
TemplateResolver resolver = new ServletContextTemplateResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".html");
resolver.setCharacterEncoding("UTF-8");
resolver.setTemplateMode("HTML5");
resolver.setOrder(2);
return resolver;
}
#Bean
public TemplateResolver emailTemplateResolver() {
TemplateResolver resolver = new ClassLoaderTemplateResolver();
resolver.setPrefix("mail/");
resolver.setSuffix(".html");
resolver.setTemplateMode("HTML5");
resolver.setCharacterEncoding("UTF-8");
resolver.setOrder(1);
return resolver;
}
#Bean
public SpringTemplateEngine templateEngine() {
final SpringTemplateEngine engine = new SpringTemplateEngine();
final Set<TemplateResolver> templateResolvers = new HashSet<TemplateResolver>();
templateResolvers.add(emailTemplateResolver());
templateResolvers.add(templateResolver());
engine.setTemplateResolvers(templateResolvers);
return engine;
}
Hopefully this helps!

Below Code will help will work
WebContext wc=new WebContext(request,response,servletContext,request.getLocale());//servletContext Spring Context
MimeMessage message = this.mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true);
wc.setVariable("name", "abc");// If you want to pass params to Template
final String htmlContent = this.templateEngine.process("TemplateName", wc);

Related

Thymeleaf with mule not resolving the HTML path under the resource

I have used references:
What is TemplateResolver.setOrder user for in thymleaf?
https://memorynotfound.com/spring-mail-sending-email-thymeleaf-html-template-example/
I have project structure as :
prj/
module/
src/
main/
java/
services/
servicePgm1.java
resource/
servicePgm1.html
In servicePgm1.java I have coded as:
private SpringResourceTemplateResolver templateResolver() {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setPrefix("src/main/resources/");
resolver.setSuffix(".html");
resolver.setTemplateMode(TemplateMode.HTML);
resolver.setOrder(1);
resolver.setCacheable(false);
return resolver;
}
String getEndpoint() throws MuleException {
final TemplateEngine tplEngine = new TemplateEngine();
engine.setTemplateResolver(templateResolver());
Context context = new Context();
context.setVariable(SUB_TITLE_PLACEHOLDER, "title 1");
engine.process("servicePgm1", context);
}
The engine.setTemplateResolver(templateResolver()) is returning InvocationTargetException from abstract class AbstractJavaResourceMethodDispatcher implements ResourceMethodDispatcher

Could not open ServletContext resource [/templates/show.xsl]

I have a spring-boot + thymeleaf application - so im basicly using no xml files for configuration. Classic html templates work fine for me, but i am having troubles with xsl. I followed this tutorial Spring MVC XstlView and XsltViewResolver Example but ended up with java.io.FileNotFoundException: Could not open ServletContext resource [/templates/show.xsl]
This is how my configuration class looks like
#Configuration
#EnableWebMvc
public class MvcConfiguration extends WebMvcConfigurerAdapter {
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
"classpath:/META-INF/resources/", "classpath:/resources/",
"classpath:/static/", "classpath:/public/" };
#Bean
#Description("Thymeleaf template resolver serving HTML 5")
public ClassLoaderTemplateResolver htmlTemplateResolver() {
ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setPrefix("templates/");
templateResolver.setCacheable(false);
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode("HTML5");
templateResolver.setCharacterEncoding("UTF-8");
return templateResolver;
}
#Bean
#Description("Thymeleaf template resolver serving XML")
public ClassLoaderTemplateResolver xmlTemplateResolver() {
ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setPrefix("templates/");
templateResolver.setCacheable(false);
templateResolver.setSuffix(".xml");
templateResolver.setTemplateMode("XML");
templateResolver.setCharacterEncoding("UTF-8");
return templateResolver;
}
#Bean
#Description("Thymeleaf template engine with Spring integration")
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.addTemplateResolver(htmlTemplateResolver());
templateEngine.addTemplateResolver(xmlTemplateResolver());
return templateEngine;
}
#Bean
#Description("Thymeleaf view resolver")
public ViewResolver viewResolver() {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine());
viewResolver.setCharacterEncoding("UTF-8");
return viewResolver;
}
#Bean
public ViewResolver getXSLTViewResolver(){
XsltViewResolver xsltViewResolver = new XsltViewResolver();
xsltViewResolver.setOrder(1);
xsltViewResolver.setSourceKey("xmlSource");
xsltViewResolver.setViewClass(XsltView.class);
xsltViewResolver.setViewNames(new String[] {"show"});
xsltViewResolver.setPrefix("templates/");
xsltViewResolver.setSuffix(".xsl");
return xsltViewResolver;
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!registry.hasMappingForPattern("/webjars/**")) {
registry.addResourceHandler("/webjars/**").addResourceLocations(
"classpath:/META-INF/resources/webjars/");
}
if (!registry.hasMappingForPattern("/**")) {
registry.addResourceHandler("/**").addResourceLocations(
CLASSPATH_RESOURCE_LOCATIONS);
}
}
}
From controller i am trying to redirect to my xsl template like this:
#RequestMapping(value="/form", method=RequestMethod.POST, params="action=show")
public ModelAndView show(#ModelAttribute Team team) throws MarshalException {
Source source = new StreamSource(new ByteArrayInputStream(xmlService.getXmlStream(team).toByteArray()));
// adds the XML source file to the model so the XsltView can detect
ModelAndView model = new ModelAndView("show");
model.addObject("xmlSource", source);
return model;
}
This is how my project files look like.
I would appreciate any suggestions how to make it work. Thank you
If you are using spring boot, then, no need to mention any configuration setting like, TemplateMode, Prefix, Suffix, etc. Spring boot automatically configures the template as per default setting.
Do no mention any configuration in #Configuration class or application.properties file.
Try this, it worked for me.

Spring Boot Thymeleaf 3 external javascript template configuration

I have a spring boot application which is working as an OAuth2 client.
I'm using Thymeleaf 3 as template engine. These are the Thymeleaf 3 related dependencies in my build.gradle.
compile('org.thymeleaf:thymeleaf:3.0.1.RELEASE')
compile('org.thymeleaf:thymeleaf-spring4:3.0.1.RELEASE')
compile('nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:2.0.4')
compile('org.thymeleaf.extras:thymeleaf-extras-springsecurity4:3.0.1.RELEASE')
It makes no difference if I reference the dependencies like this:
ext["thymeleaf.version"] = "3.0.2.RELEASE"
ext["thymeleaf-layout-dialect.version"] = "2.1.1"
dependencies {
compile('org.springframework.boot:spring-boot-starter-thymeleaf')
}
I want to be able to use Thymeleaf 3's HTML and Javascript template modes.
HTML mode works, but in javascript mode the messageSource is not working properly.
Here is my WebMvcConfiguration class:
#Configuration
public class WebMVCConfig extends WebMvcConfigurerAdapter implements ApplicationContextAware {
private static final String CHARACTER_ENCODING = "UTF-8";
private ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
#Bean
public ViewResolver htmlViewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine(htmlTemplateResolver()));
resolver.setContentType("text/html");
resolver.setCharacterEncoding(CHARACTER_ENCODING);
resolver.setViewNames(new String[] {"*.html"});
return resolver;
}
#Bean
public ViewResolver javascriptViewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine(javascriptTemplateResolver()));
resolver.setContentType("application/javascript");
resolver.setCharacterEncoding(CHARACTER_ENCODING);
resolver.setViewNames(new String[] {"*.js"});
return resolver;
}
private TemplateEngine templateEngine(ITemplateResolver templateResolver) {
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.setTemplateResolver(templateResolver);
return engine;
}
private ITemplateResolver htmlTemplateResolver() {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setApplicationContext(applicationContext);
resolver.setPrefix("templates/");
resolver.setCacheable(false);
resolver.setTemplateMode(TemplateMode.HTML);
resolver.setSuffix(".html");
return resolver;
}
public ITemplateResolver javascriptTemplateResolver() {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setApplicationContext(applicationContext);
resolver.setPrefix("classpath:/static/js/");
resolver.setCacheable(false);
resolver.setTemplateMode(TemplateMode.JAVASCRIPT);
// resolver.setSuffix(".js");
return resolver;
}
}
Please note that I had to use "classpath:/static/js/" in javascriptTemplateResolver, because when I used only "static/js/", I got the following exception:
java.io.FileNotFoundException: Could not open ServletContext resource [/static/js/headerconfig.js]
Also I had to comment out setSuffix, because with it I get the following exception:
java.io.FileNotFoundException: class path resource [static/js/typeutils.js.js] cannot be opened because it does not exist
I think this already indicates the main problem but I can't figure out what may cause it.
I have a controller for handling javascript templates:
#Controller
public class JavascriptController {
#RequestMapping(method = RequestMethod.GET, value = "/js/{template}.js")
public String jsMapping(#PathVariable("template") String template, Model model) {
model.addAttribute("myAttribute", "Attribute works!");
return template;
}
}
My messages.properties files are located in
src/main/resources/messages.properties
src/main/resources/messages_hu.properties
I have an HTML file in src/main/resources/templates/ folder, which references a javascript file like so:
<script th:src="#{js/typeutils.js}"></script>
The referenced javascript file (js/typeutils.js):
var a = [[${myAttribute}]];
var b = [[#{test}]];
console.log(a);
console.log(b);
When I run the application and check the javascript console, this is what's being printed out:
Attribute works!
??test_hu_HU??
So the model attribute was successfully passed to the javascript file, and the localization has been detected, but the message wasn't found for 'test'.
It seems as if the javascript template mode behaves completely different from HTML template mode.
How should if fix the javascript template mode config in order for it to process the messages.properties files, too?
Thanks!
I figured out the solution (by looking through thymeleaf sourcecode), which might not be perfect, but it works!
Unlike the example given by Baeldung, the following solved the problem for me:
#Configuration
public class WebMVCConfig implements WebMvcConfigurer, ApplicationContextAware {
private static final String CHARACTER_ENCODING = "UTF-8";
private ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
#Bean
public ViewResolver htmlViewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine());
resolver.setContentType("text/html");
resolver.setCharacterEncoding(CHARACTER_ENCODING);
resolver.setViewNames(new String[] { "*.html" });
return resolver;
}
#Bean
public ViewResolver javascriptViewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine());
resolver.setContentType("application/javascript");
resolver.setCharacterEncoding(CHARACTER_ENCODING);
resolver.setViewNames(new String[] { "*.js" });
return resolver;
}
#Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.setMessageSource(messageSource());
engine.addTemplateResolver(htmlTemplateResolver());
engine.addTemplateResolver(javascriptTemplateResolver());
return engine;
}
private ITemplateResolver htmlTemplateResolver() {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setOrder(0);
resolver.setCheckExistence(true);
resolver.setApplicationContext(applicationContext);
resolver.setPrefix("classpath:templates/");
resolver.setCacheable(false);
resolver.setTemplateMode(TemplateMode.HTML);
resolver.setSuffix(".html");
return resolver;
}
public ITemplateResolver javascriptTemplateResolver() {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setApplicationContext(applicationContext);
resolver.setOrder(1);
resolver.setCheckExistence(true);
resolver.setPrefix("classpath:/static/js/");
resolver.setCacheable(false);
resolver.setTemplateMode(TemplateMode.JAVASCRIPT);
return resolver;
}
#Bean
public MessageSource messageSource() {
ResourceBundleMessageSource msgSource = new ResourceBundleMessageSource();
msgSource.setAlwaysUseMessageFormat(false);
msgSource.setBasename("messages");
msgSource.setDefaultEncoding(CHARACTER_ENCODING);
msgSource.setFallbackToSystemLocale(true);
msgSource.setUseCodeAsDefaultMessage(false);
return msgSource;
}
}
Basically what happens is we set templateEngine to be a bean, so that we override ThymeleafDefaultConfiguration's default templateEngine implementation bean. This means that each time a template needs to be resolved, the same templateEngine will be used for sure.
We set the order of the htmlTemplateResolver to be 0, and the order of the javascriptTemplateResolver to be 1, so that we will try to resolve each template first in HTML template mode, then in Javascript mode.
It's important, too, that we set the checkExistence flag of the SpringResourceTemplateResolvers to true, because this way if a template can't be found, we will try with the next TemplateResolver.
There is one drawback of this solution, which is we will try to resolve javascript templates unnecessary in HTML mode first, then in JAVASCRIPT mode, so there is an extra step when resolving javascript resources.
I will try to solve the problem better but for now, this works for me.

How to force my template resolver to use ClassLoaderTemplateResolver for /mail/* templates

I am using two template resolvers for my Spring Boot Application ServletContextTemplateResolver and ClassLoaderTemplateResolver.
I am using ServletContextTemplateResolver for /WEB-INF/* html templates for my web pages. I will on the other hand, use ClassLoaderTemplateResolver for my /mail/ html templates. Here's the code:
#Bean(name ="templateResolver")
public ServletContextTemplateResolver getTemplateResolver() {
ServletContextTemplateResolver templateResolver = new
ServletContextTemplateResolver();
templateResolver.setPrefix("/WEB-INF/templates/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode("XHTML");
templateResolver.setOrder(Integer.valueOf(1));
return templateResolver;
}
#Bean(name ="emailTemplateResolver")
public ClassLoaderTemplateResolver getEmailTemplateResolver() {
ClassLoaderTemplateResolver templateResolver = new
ClassLoaderTemplateResolver();
templateResolver.setOrder(Integer.valueOf(2));
templateResolver.setPrefix("/mail/");
templateResolver.setSuffix(".html");
return templateResolver;
}
It shows the following error when calling the mail template:
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]
How can I force it to use ClassLoaderTemplateResolver for /mail/* templates?
You are probably rendering an html file and want to keep the resultant html file in a string variable which later you will be using it to email by using html to pdf conversion. So the following code will help you in Spring boot:
#Controller
public class jataController {
#Autowired
private TemplateEngine templateEngine;
#GetMapping(value = "/manual-thym")
#ResponseBody
public void justSample() {
Context context = new Context();
String filename = "templates/view/generated-ticket.html";
String html = renderHtml(filename, context);
System.out.println("template\n" + html);
}
private String renderHtml(String filename, Context context) {
ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode(TemplateMode.HTML);
templateResolver.setCacheable(false);
templateResolver.setOrder(1);
templateResolver.setCharacterEncoding("UTF-8");
templateEngine.setTemplateResolver(templateResolver);
String html = templateEngine.process(filename, context);
return html;
}
}

Spring Boot + Thymeleaf + Apache tiles - cannot find a definition file

I have a weird problem with my configuration. I'm trying to configure apache tiles with thymeleaf. I'm trying to do the same things they are on theirs documentation page.
I have following configuration file:
#Configuration
public class ViewConfig {
#Bean
public ViewResolver tilesViewResolver() {
ThymeleafViewResolver vr = new ThymeleafViewResolver();
vr.setTemplateEngine(templateEngine());
vr.setViewClass(ThymeleafTilesView.class);
vr.setCharacterEncoding("UTF-8");
vr.setOrder(Ordered.LOWEST_PRECEDENCE);
return vr;
}
#Bean
public ViewResolver thymeleafViewResolver() {
ThymeleafViewResolver vr = new ThymeleafViewResolver();
vr.setTemplateEngine(templateEngine());
vr.setCharacterEncoding("UTF-8");
vr.setOrder(Ordered.HIGHEST_PRECEDENCE);
// all message/* views will not be handled by this resolver;
vr.setExcludedViewNames(new String[]{"message/*"});
return vr;
}
#Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.addDialect(new TilesDialect());
templateEngine.setTemplateResolver(templateResolver());
return templateEngine;
}
#Bean
public ThymeleafTilesConfigurer tilesConfigurer() {
ThymeleafTilesConfigurer ttc = new ThymeleafTilesConfigurer();
ttc.setValidateDefinitions(false);
ttc.setDefinitions(new String[]{"/templates/tiles-defs.xml"});
return ttc;
}
#Bean
public TemplateResolver templateResolver() {
ServletContextTemplateResolver resolver = new ServletContextTemplateResolver();
resolver.setSuffix(".html");
resolver.setPrefix("templates/");
resolver.setTemplateMode("HTML5");
resolver.setCharacterEncoding("UTF-8");
return resolver;
}
}
I've added TemplateResolver for my purposes, but removing it doesn't help (for having the same configuration as documentation).
This is my resource folder structure:
And the problem is that I've got following exception:
Caused by: java.io.FileNotFoundException: ServletContext resource [/templates/tiles-defs.xml] cannot be resolved to URL because it does not exist
The most interesting is that I've checked target/classes (which is in classpath) and there is the file templates/tiles-defs.xml.
I've tried a couple of times to change the definition path (also I've tried to remove definition and get it from default WEB-INF/tiles.xml path) but no outcome - same exception.
What I'm doing wrong?
Here is how I solved it:
#Bean
public ThymeleafTilesConfigurer tilesConfigurer() {
ThymeleafTilesConfigurer ttc = new ThymeleafTilesConfigurer();
//ttc.setValidateDefinitions(false);
ttc.setDefinitions(ThymeleafProperties.DEFAULT_PREFIX + "tiles-defs.xml");
return ttc;
}

Categories