How to send an asynchrone email with spring boot - java

I'm using spring boot 1.5. I'm trying to send async mail when user create an alert but it doesnt work and it doesn't display any error even when I'm debugging but in vain, so here's what I obtain. My question is how to test if my implementation of mail is correct or not, because I don't have any controller of it. Please help. Thank you for any suggestions

You can send asynchronous emails easily by using #Async annotation on your mail sending method. To enable Async support in your spring boot application use #EnableAsync like this:
#SpringBootApplication
#EnableAsync
public class SpringTestAppApplication {
public static void main(String[] args) {
SpringApplication.run(SpringTestAppApplication.class, args);
}
}
Create a MailSender service and annotate mail sender method with #Async like this:
#Service
public class MailSenderService {
#Async
public void sendMail(....) {
// your code
}
}
Autowire the above service in your service and then call sendMessage method, example:
#Service
public class UserService {
#Autowired
private MailSenderService mailSenderService;
User exampleMethod() {
..
..
mailSenderService.sendMail(...);
}
}
You can check this link for more information: https://www.baeldung.com/spring-async
UPDATE:
If you want to use java mail api . Then you can take reference of below code:
import java.util.Properties;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
#Service
public class MailService {
#Autowired
EnvConfiguration envConfiguration;
private static final Logger LOGGER = LoggerFactory.getLogger(MailService.class);
#Async
public void sendMail(String to, String subject, String htmlText, String fileName) {
Properties props = System.getProperties();
props.put("mail.transport.protocol", "smtps");
props.put("mail.smtp.port", envConfiguration.getSMTPPort());
props.put("mail.smtp.auth", envConfiguration.smtpAuthorized());
props.put("mail.smtp.starttls.enable", envConfiguration.isStarTlsEnabled());
props.put("mail.smtp.starttls.required", envConfiguration.isStarTlsRequired());
Session session = Session.getDefaultInstance(props);
Transport transport = null;
try {
MimeMessage msg = new MimeMessage(session);
msg.setFrom(new InternetAddress(envConfiguration.getEmailFrom().replaceAll("\"", "")));
msg.setRecipient(Message.RecipientType.TO, new InternetAddress(to));
msg.setSubject(subject);
MimeBodyPart messageBodyPart = new MimeBodyPart();
messageBodyPart.setContent(htmlText, "text/html");
transport = session.getTransport();
transport.connect(envConfiguration.getEmailHost(), envConfiguration.getEmailUserName(),
envConfiguration.getEmailPassword());
transport.sendMessage(msg, msg.getAllRecipients());
LOGGER.info("Mail send successfully");
} catch (MessagingException e) {
LOGGER.error("Unable to send email trace" + e);
} finally {
try {
transport.close();
} catch (MessagingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}

Related

Javamail:Required type: javax.mail.internet.MimeMessage

I am working on a project about"SpringBoot", and when I want to send a mail by "Javamail",I met some problems:
1.JavaMailSender:Library source does not match the bytecode for JavaMailSender,
2.it has problems about the code of
package com.nowcoder.community.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
#Component
public class MailClient {
private static final Logger logger = LoggerFactory.getLogger(MailClient.class);
#Autowired
private JavaMailSender mailSender;
#Value("${spring.mail.username}")
private String from;
public void sendMail(String to, String subject, String content) {
try {
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message);
helper.setFrom(from);
helper.setTo(to);
helper.setSubject(subject);
helper.setText(content, true);
mailSender.send(helper.getMimeMessage());
} catch (MessagingException e) {
logger.error("发送邮件失败:" + e.getMessage());
}
}
}
it has errors about
MimeMessage message = mailSender.createMimeMessage();
and the error:
Required type:javax.mail.internet.MimeMessage
Provided: jakarta.mail.internet.MimeMessage
I try to clean the dependency and download again and replace the version of "spring-boot-starter-mail" to 2.1.5 RELEASE,2.1.4 RELEASE,2.7.2, but the above doesn't work。

Cannot read email body with Spring: javax.mail.FolderClosedException

I'm trying to listen my Gmail inbox for incoming mails. Every time new mail arrives, I want to see it's subject and content.
So far, I have this:
import java.io.IOException;
import javax.mail.BodyPart;
import javax.mail.Folder;
import javax.mail.internet.ContentType;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.mail.util.MimeMessageParser;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.mail.transformer.MailToStringTransformer;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessagingException;
public class GmailInboundImapIdleAdapterTestApp {
private static Log logger = LogFactory.getLog(GmailInboundImapIdleAdapterTestApp.class);
public static void main (String[] args) throws Exception {
#SuppressWarnings("resource")
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("/META-INF/spring/integration/gmail-imap-idle-config.xml");
DirectChannel inputChannel = ac.getBean("receiveChannel", DirectChannel.class);
inputChannel.subscribe(new MessageHandler() {
public void handleMessage(Message<?> message){
MimeMessage mm = (MimeMessage) message.getPayload();
try {
System.out.println("Subject: "+mm.getSubject());
System.out.println("Body: "+readPlainContent(mm));
}
catch (javax.mail.MessagingException e) {
System.out.println("MessagingException: "+e.getMessage());
e.printStackTrace();
}
catch (Exception e) {
System.out.println("Exception: "+e.getMessage());
e.printStackTrace();
}
}
});
}
private static String readHtmlContent(MimeMessage message) throws Exception {
return new MimeMessageParser(message).parse().getHtmlContent();
}
private static String readPlainContent(MimeMessage message) throws Exception {
return new MimeMessageParser(message).parse().getPlainContent();
}
}
It can read the mail subject correctly. But no luck with mail body.javax.mail.FolderClosedException hit me. How to fix this?
As Gary said: simple-content="true" or since recently autoCloseFolder = false: https://docs.spring.io/spring-integration/docs/5.2.0.RELEASE/reference/html/mail.html#mail-inbound
Starting with version 5.2, the autoCloseFolder option is provided on the mail receiver. Setting it to false doesn’t close the folder automatically after a fetch, but instead an IntegrationMessageHeaderAccessor.CLOSEABLE_RESOURCE header (see MessageHeaderAccessor API for more information) is populated into every message to producer from the channel adapter. It is the target application’s responsibility to call the close() on this header whenever it is necessary in the downstream flow:

I want to turn a main method into a new non-main method class

I have a program that I can send emails through. However, this needs to be a part of a much bigger program. The Email class is under a different package whereas my other 2 classes (The driver class/main program, as well as another object class) are both in the default package. Can I access the email class despite it being in a different package or do I need them all to be in one package? And how do I go about doing either of these? Currently, I tried removing the main method part of the email class and putting it in the default package with my driver class, this resulted in many syntax errors. Below are some photos showing my classes and some code. The SendMail class is the same as SendMailTLS just with the main method being removed and put into the default package. The SendMailTLS class works perfectly, I just need to be able to access it from the IA class.
SendMail Class:
import java.util.Properties;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
public class SendMail {
final String username = "treybyroncollier#gmail.com";
final String password = "13october";
Properties props = new Properties();
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.host", "smtp.gmail.com");
props.put("mail.smtp.port", "587");
Session session = Session.getInstance(props,
new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
try {
Message message = new MimeMessage(session);
message.setFrom(new InternetAddress("treybyroncollier#gmail.com"));
message.setRecipients(Message.RecipientType.TO,
InternetAddress.parse("treycollier#live.co.uk"));
message.setSubject("THIS EMAIL IS A TEST");
message.setText("Hello Trey, just to let you know that this email is a test and everything is working with Java.");
Transport.send(message);
System.out.println("Done");
} catch (MessagingException e) {
throw new RuntimeException(e);
}
}
SendMailTLS Class:
package com.mkyong.common;
import java.util.Properties;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
public class SendMailTLS {
public static void main(String[] args) {
final String username = "treybyroncollier#gmail.com";
final String password = "13october";
Properties props = new Properties();
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.host", "smtp.gmail.com");
props.put("mail.smtp.port", "587");
Session session = Session.getInstance(props,
new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
try {
Message message = new MimeMessage(session);
message.setFrom(new InternetAddress("treybyroncollier#gmail.com"));
message.setRecipients(Message.RecipientType.TO,
InternetAddress.parse("treycollier#live.co.uk"));
message.setSubject("THIS EMAIL IS A TEST");
message.setText("Hello Trey, just to let you know that this email is a test and everything is working with Java.");
Transport.send(message);
System.out.println("Done");
} catch (MessagingException e) {
throw new RuntimeException(e);
}
}
}
You should learn the basics first and then start with such a comlicated program.
In your sendMail class, you added all of your code directly into the class body, that's not going to work. Instead, create a method in that class and paste your code in there.
Then you can call that method from your other class after you imported the package.
import java.util.Properties;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
public class SendMail {
public static void start() {
final String username = "treybyroncollier#gmail.com";
final String password = "13october";
Properties props = new Properties();
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.host", "smtp.gmail.com");
props.put("mail.smtp.port", "587");
Session session = Session.getInstance(props,
new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
try {
Message message = new MimeMessage(session);
message.setFrom(new InternetAddress("treybyroncollier#gmail.com"));
message.setRecipients(Message.RecipientType.TO,
InternetAddress.parse("treycollier#live.co.uk"));
message.setSubject("THIS EMAIL IS A TEST");
message.setText("Hello Trey, just to let you know that this email is a test and everything is working with Java.");
Transport.send(message);
System.out.println("Done");
} catch (MessagingException e) {
throw new RuntimeException(e);
}
}
}
You can then run that code from your new main method or any other method you like.
public class YourMainClass {
public static void main(String[] args) {
SendMail.start();
}
}
In short, if you don't want your main method to execute when you start the program, just change its name from main to something of your choice and remove the parameter String[] args.

How do you send 2 emails to two different email addresses in Java?

I have created a module for contact us. Here one user can send a mail about his concerns to admin and admin can send a reply mail to that particular user. So I have created 2 different mail templates, But I don't know how to send both at the same time.
Now I have completed sending reply email to user, but I have not done sending mail to admin user.
My code for sending reply mail to user:
package com.spring.test.service;
import java.io.IOException;
import java.util.Properties;
import javax.mail.internet.InternetAddress;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.mail.MailSender;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.web.client.RestTemplate;
public class MailMail {
private MailSender mailSender;
#Autowired
#Qualifier(value = "mailSender")
public void setMailSender(MailSender mailSender) {
this.mailSender = mailSender;
}
public void sendMail(String to, String subject, String msg) {
//creating message
System.out.println("mailsender == " + this.mailSender);
try {
String from = "emailFromAddress";
JavaMailSenderImpl sender = new JavaMailSenderImpl();
javax.mail.internet.MimeMessage mimeMessage = sender.createMimeMessage();
org.springframework.mail.javamail.MimeMessageHelper helper = new org.springframework.mail.javamail.MimeMessageHelper(mimeMessage, false, "utf-8");
mimeMessage.setContent(msg, "text/html");
helper.setSubject(subject);
helper.setFrom(from);
helper.setTo(to);
sender.setHost("smtp.gmail.com");
sender.setUsername("emailServerUserName");
sender.setPassword("emailServerPassword");
sender.setPort(587);
Properties props = new Properties();
props.put("mail.smtp.auth",true);
props.put("mail.smtp.starttls.enable","true");
sender.setJavaMailProperties(props);
sender.send(mimeMessage);
} catch(Exception e){
e.printStackTrace();
}
}
}
Can anyone tell me how to send mail to admin users also at a time?
Your implementation of sendMail should be completely driven from externalised parameters like email address to, email address from, subject , email template path and data with placeholder properties. Means none of the mentioned parameters be intialized, declared or manipulate in this method.
Probably with this your sendMail becomes a complete stateless service which you can call for sending various emails with different parameters

Gmail API configuration issue (in Java)

Here is my Gmail service configuration/factory class:
import java.io.File;
import java.io.IOException;
import java.security.GeneralSecurityException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.services.gmail.Gmail;
import com.google.api.services.gmail.GmailScopes;
public class GmailServiceFactoryBean {
private #Autowired Environment env;
private final NetHttpTransport transport;
private final JacksonFactory jacksonFactory;
public GmailServiceFactoryBean() throws GeneralSecurityException, IOException {
this.transport = GoogleNetHttpTransport.newTrustedTransport();
this.jacksonFactory = JacksonFactory.getDefaultInstance();
}
public Gmail getGmailService() throws IOException, GeneralSecurityException {
return new Gmail.Builder(transport, jacksonFactory, getCredential())
.setApplicationName(env.getProperty("gmail.api.application.name")).build();
}
private HttpRequestInitializer getCredential() throws IOException, GeneralSecurityException {
File p12File = new File(this.getClass().getClassLoader().getResource("google-key.p12").getFile());
Credential credential = new GoogleCredential.Builder()
.setServiceAccountId(env.getProperty("gmail.api.service.account.email"))
.setServiceAccountPrivateKeyId(env.getProperty("gmail.api.private.key.id"))
.setServiceAccountPrivateKeyFromP12File(p12File)
.setTransport(transport)
.setJsonFactory(jacksonFactory)
.setServiceAccountScopes(GmailScopes.all())
//.setServiceAccountUser(env.getProperty("gmail.api.user.email"))
.build();
credential.refreshToken();
return credential;
}
}
Here is my inner mailing service that uses previous bean under the hood:
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.List;
import java.util.Properties;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMessage.RecipientType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import com.google.api.client.repackaged.org.apache.commons.codec.binary.Base64;
import com.google.api.services.gmail.Gmail;
import com.google.api.services.gmail.model.Message;
import com.example.factory.GmailServiceFactoryBean;
import com.example.service.MailService;
import com.example.service.exception.MailServiceException;
#Service
public class MailServiceImpl implements MailService {
private #Autowired GmailServiceFactoryBean gmailServiceFactoryBean;
private #Autowired Environment env;
#Override
public void send(com.example.model.Message message, String recipient) throws MailServiceException {
try {
Gmail gmailService = gmailServiceFactoryBean.getGmailService();
MimeMessage mimeMessage = createMimeMessage(message, recipient);
Message gMessage = createMessageWithEmail(mimeMessage);
gmailService.users().messages().send("me", gMessage).execute();
} catch(MessagingException | IOException | GeneralSecurityException e) {
throw new MailServiceException(e.getMessage(), e.getCause());
}
}
#Override
public void send(com.example.model.Message message, List<String> recipients) throws MailServiceException {
for (String recipient : recipients) {
send(message, recipient);
}
}
private MimeMessage createMimeMessage(com.example.model.Message message, String recipient) throws MessagingException {
Session session = Session.getDefaultInstance(new Properties());
MimeMessage email = new MimeMessage(session);
InternetAddress toAddress = new InternetAddress(recipient);
InternetAddress fromAddress = new InternetAddress(env.getProperty("gmail.api.service.account.email"));
email.setFrom(fromAddress);
email.addRecipient(RecipientType.TO, toAddress);
email.setSubject(message.getTitle());
email.setText(message.getContent(), env.getProperty("application.encoding"));
return email;
}
private Message createMessageWithEmail(MimeMessage email) throws MessagingException, IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
email.writeTo(baos);
return new Message().setRaw(Base64.encodeBase64URLSafeString(baos.toByteArray()));
}
}
When I execute method send(Message message, String recipient) of class MailServiceImpl I get following response:
400 Bad Request
{
"code" : 400,
"errors" : [ {
"domain" : "global",
"message" : "Bad Request",
"reason" : "failedPrecondition"
} ],
"message" : "Bad Request"
}
Does anyone know what's wrong?
For GMail API to work, you have to "Delegate domain-wide authority to the service account" within your Google Apps account.
Service account doesn't represent a human Google account. You also can't delegate authority to whole Google domain(***#gmail.com).
The other way out could be with OAuth 2.0 for Web Server Applications or Java Mail api
For more do check: GMail REST API: Using Google Credentials Without Impersonate
Check if you have enabled gmail to send mails using 3rd party applications.
Go to my account ->Sign in and Security -> Connected Apps
now scroll to the bottom of the page you will get Less secure apps ->change it to on !!
Hope this will work

Categories