0

I have an emailService implementation in my Java Spring Boot project. To send email, I use JavaMailSender. In my service I need an access to HttpServletRequest and HttpServletResponse in order to instantiate WebContext. Now I am passing these 2 - request and response through my mailsender methods what sounds like a bad idea. I would like to have them in my service so I can call methods in my mailsender with only 2 variables: what email template to use and a map of variables that will be printed on that email. Is there a way to instantiate request and response in my service? Can I somehow autowire them in the service? Thats how my service looks like:

@Service
@Qualifier("MailSender")
public class MailSenderService {

@Autowired 
private JavaMailSender mailSender;

@Autowired 
private ServletContextTemplateResolver emailTemplateResolver;


public boolean sendMail(HttpServletRequest request, HttpServletResponse     
response, HashMap<String, String> info, String template) throws 
MessagingException, IOException{

    final MimeMessage mimeMessage = this.mailSender.createMimeMessage();
    final MimeMessageHelper message = new 
    MimeMessageHelper(mimeMessage,true, "UTF-8"); // true = multipart

        message.setFrom("[email protected]");
        message.setTo("[email protected]");
        message.setSubject("This is the message subject");
        TemplateEngine engine = new TemplateEngine();
        engine.setTemplateResolver(emailTemplateResolver);
        WebContext ctx =  new WebContext(request, response, 
        request.getServletContext(), request.getLocale());

        ctx.setVariable("info", info);


    try{        
        String messageContent=  engine.process(template, ctx);
        mimeMessage.setContent(tt, "text/html; charset=utf-8");

    }catch(Exception e){
        e.printStackTrace();
    }

    this.mailSender.send(mimeMessage);
    return true;

}
}
3
  • 1
    IMHO you shouldn't be using the request and response and should strive for a solution in which you don't need those. You should be using a default context. Also you shouldn't be recreating the TemplateEngine but simply configure that in your context (with the required TemplateResolvers) and inject it. Commented Mar 24, 2015 at 14:51
  • M.Deinum I really appreciate this hint. Yet I'm not sure how I can do that. Are you suggesting that I should have a separate class that instantiates TemplateResolver once and has request and responce there? I was not able to find any Java email package that can send email template without using request and repsonse btw Commented Mar 24, 2015 at 15:05
  • No I'm saying that you should let Spring configure your TemplateEngine and just inject that instead of ServletContextTemplateResolver. YOu shouldn't bother with the configuration/setup in your mail sending class (it isn't its responsibility). Also you should be using a plain Context and not a WebContext. Commented Mar 25, 2015 at 6:52

4 Answers 4

1

For starters your MailSenderService shouldn't be bothered with creating the TemplateEngine nor know about how to resolve templates, that should be transparant. See this turorial on the Thymeleaf website.

Assuming that you are using XML for bean configuration and Spring 4 add the following. (you probably already have an instance of the ServletContextTemplateResolver configured).

<bean id="webTemplateResolver" class="org.thymeleaf.templateresolver.ServletContextTemplateResolver">
  <property name="prefix" value="/WEB-INF/templates/" />
  <property name="templateMode" value="HTML5" />
  <property name="characterEncoding" value="UTF-8" />
  <property name="order" value="2" />
</bean>

<!-- THYMELEAF: Template Engine (Spring3-specific version) -->
<bean id="templateEngine" class="org.thymeleaf.spring4.SpringTemplateEngine">
  <property name="templateResolvers">
    <set>
      <ref bean="emailTemplateResolver" />
      <ref bean="webTemplateResolver" />
    </set>
  </property>
</bean>

Now in your MailSenderService you can simply inject the TemplateEngine.

@Service
@Qualifier("MailSender")
public class MailSenderService {

@Autowired 
private JavaMailSender mailSender;

@Autowired 
private TemplateEngine engine;

    public boolean sendMail(HttpServletRequest request, HttpServletResponse response, HashMap<String, String> info, String template) throws MessagingException, IOException{

        final MimeMessage mimeMessage = this.mailSender.createMimeMessage();
        final MimeMessageHelper message = new 
        MimeMessageHelper(mimeMessage,true, "UTF-8"); // true = multipart

        message.setFrom("[email protected]");
        message.setTo("[email protected]");
        message.setSubject("This is the message subject");

        WebContext ctx =  new WebContext(request, response, 
        request.getServletContext(), request.getLocale());

        ctx.setVariable("info", info);

        try{        
            String messageContent=  engine.process(template, ctx);
            mimeMessage.setContent(tt, "text/html; charset=utf-8");

        }catch(Exception e){
            e.printStackTrace();
        }

        this.mailSender.send(mimeMessage);
        return true;

    }
}

Instead of request.getLocale() simply use LocaleContextHolder.getLocale() and put all the properties you need in your template in your info variable. With this you could rewrite your method to the following.

public boolean sendMail(Map<String, String> info, String template) throws MessagingException, IOException{

        final MimeMessage mimeMessage = this.mailSender.createMimeMessage();
        final MimeMessageHelper message = new 
        MimeMessageHelper(mimeMessage,true, "UTF-8"); // true = multipart

        message.setFrom("[email protected]");
        message.setTo("[email protected]");
        message.setSubject("This is the message subject");

        Context ctx =  new Context(LocaleContextHolder.getLocale());
        ctx.setVariable("info", info);

        try{        
            String messageContent=  engine.process(template, ctx);
            mimeMessage.setContent(tt, "text/html; charset=utf-8");

        }catch(Exception e){
            e.printStackTrace();
        }

        this.mailSender.send(mimeMessage);
        return true;

    }

NO need anymore for a HttpServletRequest and HttpServletResponse. Just a plain service. Generally you don't want that your service layer depends on the web as that is what you effectivly did in your solution.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you M.Deinum! Great instructions
1

If the class is request scoped you can autowire it as follows :

@Autowired private HttpServletRequest request;

Cheers..

Comments

1

You should be able to autowire HttpServletRequest, though the same doesn't work for HttpServletResponse.

eg.

@Autowired HttpServletRequest request; // works
@Autowired HttpServletResponse response; // doesn't work

There is some discussion about that here -- though in the end it looks like you may have to pass the response manually.

Comments

0

So this is how i solved it with annotations. Big thanks to M. Deinum !

I finally got it all working. I am not using XML. I work with Spring Boot but if I want to configure something, its all in my configuration methods. For everyone lost like I was, here is my configuration of templateEngine in my @Configuration class:

 @Bean
public ClassLoaderTemplateResolver emailTemplateResolver(){
    ClassLoaderTemplateResolver emailTemplateResolver = new      
    ClassLoaderTemplateResolver();
        emailTemplateResolver.setPrefix("/WEB-INF/emails/");
        emailTemplateResolver.setSuffix(".html");
        emailTemplateResolver.setTemplateMode("HTML5");
        emailTemplateResolver.setCharacterEncoding("UTF-8");
        emailTemplateResolver.setOrder(1);
        return emailTemplateResolver;
    }

    @Bean
    public TemplateEngine templateEngine(){
        TemplateEngine templateEngine = new TemplateEngine();
        templateEngine.setTemplateResolver(emailTemplateResolver());
        return templateEngine;
    }

With this configuration I can just @Autowire

   private TemplateEngine templateEngine; 

In my service and now I do not need request, response. Everything works with Context

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.