Reputation: 33605
I have a correspondence application where each user creates a correspondence and send it to multiple users (average send us between 2-30 user), with each send I open a new thread and send email to group of users in the following flow (connect to mail server > send > close connection) as follows:
public class EmailService {
private String emailProtocol = null;
private String emailHostSMTP = null;
private String senderEmail = null;
private String senderUser = null;
private String senderPassword = null;
private String senderDisplayName = null;
private String emailPort = null;
public void initConfig() {
emailProtocol = GeneralServices.getConfig("emailProtocol");
emailHostSMTP = GeneralServices.getConfig("emailHostSMTP");
senderEmail = GeneralServices.getConfig("senderEmail");
senderUser = GeneralServices.getConfig("senderUser");
senderPassword = GeneralServices.getConfig("senderPassword");
senderDisplayName = GeneralServices.getConfig("senderDisplayName");
emailPort = GeneralServices.getConfig("emailPort");
if (StringUtils.isBlank(emailPort))
emailPort = "587";
}
public void setProps(Properties props) {
props.put("mail.transport.protocol", emailProtocol);
props.put("mail.smtp.host", emailHostSMTP);
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.port", emailPort);
if (ConfigurationUtils.isEnableStartTlsInEmail())
props.put("mail.smtp.starttls.enable", "true");
if (ConfigurationUtils.isEnableDebugInEmail())
props.put("mail.debug", "true");
}
public void sendEmail(String toUser, String subject, String emailHtmlBody, String bannerPath) throws Exception {
try {
if (StringUtils.isBlank(toUser)) {
return;
}
List<String> toUsers = new ArrayList<String>(1);
toUsers.add(toUser);
sendEmail(toUsers, null, null, subject, emailHtmlBody, bannerPath);
} catch (Exception e) {
throw e;
}
}
public void sendEmail(String fromEmail, String fromDisplayName, List<String> toList, List<String> ccList,
String subject, String emailBody, String filePhysicalPath, String fileName, String fileContentType)
throws Exception {
Transport transport = null;
try {
initConfig();
MimeMultipart multipart = new MimeMultipart();
Authenticator authenticator = new SMTPAuthenticator();
MailSSLSocketFactory sslSocketFactory = new MailSSLSocketFactory();
MimeBodyPart bodyPart = new MimeBodyPart();
String html = "";
Properties props = System.getProperties();
setProps(props);
sslSocketFactory.setTrustAllHosts(true);
props.put("mail.smtp.ssl.socketFactory", sslSocketFactory);
Session session = Session.getInstance(props, authenticator);
// session.setDebug(true);
emailBody = emailBody + "<br/><br/>مرسل بواسطة : " + fromDisplayName;
html = "<html><body style='text-align:right'> " + emailBody + " </body></html>";
bodyPart.setContent(html, "text/html; charset=UTF-8");
multipart.addBodyPart(bodyPart);
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress(senderEmail, fromDisplayName));
message.setReplyTo(new Address[] { new InternetAddress(fromEmail) });
if (toList != null && toList.size() > 0) {
for (String to : toList) {
message.addRecipient(Message.RecipientType.TO, new InternetAddress(to));
}
} else {
throw new Exception("List of users to send email to is empty");
}
if (ccList != null && ccList.size() > 0) {
for (String cc : ccList) {
message.addRecipient(Message.RecipientType.CC, new InternetAddress(cc));
}
}
// attach file
BodyPart mimeBodyPart = new MimeBodyPart();
DataSource source = new FileDataSource(filePhysicalPath);
mimeBodyPart.setDataHandler(new DataHandler(source));
mimeBodyPart.setFileName(MimeUtility.encodeText(fileName, "utf-8", "B"));
multipart.addBodyPart(mimeBodyPart);
// end of file attach
message.setSubject(subject, "UTF-8");
message.setContent(multipart);
message.setSentDate(new Date());
transport = session.getTransport(emailProtocol);
transport.connect(senderEmail, senderPassword);
transport.sendMessage(message, message.getAllRecipients());
} catch (Exception ex) {
throw ex;
} finally {
if (transport != null)
transport.close();
}
}
public void sendEmail(List<String> toList, List<String> ccList, List<String> bccList, String subject,
String emailHtmlBody, String bannerPath) throws Exception {
if ((toList == null || toList.size() == 0) && (ccList == null || ccList.size() == 0)
&& (bccList == null || bccList.size() == 0)) {
return;
}
Transport transport = null;
try {
initConfig();
MimeMultipart multipart = new MimeMultipart();
Authenticator authenticator = new SMTPAuthenticator();
MailSSLSocketFactory sslSocketFactory = new MailSSLSocketFactory();
MimeBodyPart bodyPart = new MimeBodyPart();
String html = "";
Properties props = System.getProperties();
setProps(props);
sslSocketFactory.setTrustAllHosts(true);
props.put("mail.smtp.ssl.socketFactory", sslSocketFactory);
Session session = Session.getInstance(props, authenticator);
html = "<html><body> " + emailHtmlBody + " </body></html>";
bodyPart.setContent(html, "text/html; charset=UTF-8");
multipart.addBodyPart(bodyPart);
// add banner path
bodyPart = new MimeBodyPart();
DataSource ds = new FileDataSource(bannerPath);
bodyPart.setDataHandler(new DataHandler(ds));
bodyPart.setHeader("Content-ID", "<MOAMALAT_LOGO>");
multipart.addBodyPart(bodyPart);
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress(senderEmail, senderDisplayName));
message.setReplyTo(new Address[] { new InternetAddress(senderEmail) });
if (toList != null && toList.size() > 0) {
for (String email : toList)
message.addRecipient(Message.RecipientType.TO, new InternetAddress(email));
}
if (ccList != null && ccList.size() > 0) {
for (String email : ccList)
message.addRecipient(Message.RecipientType.CC, new InternetAddress(email));
}
if (bccList != null && bccList.size() > 0) {
for (String email : bccList)
message.addRecipient(Message.RecipientType.BCC, new InternetAddress(email));
}
message.setSubject(subject, "UTF-8");
message.setContent(multipart);
message.setSentDate(new Date());
transport = session.getTransport(emailProtocol);
transport.connect(senderEmail, senderPassword);
transport.sendMessage(message, message.getAllRecipients());
} catch (Exception ex) {
throw ex;
} finally {
if (transport != null)
transport.close();
}
}
private class SMTPAuthenticator extends javax.mail.Authenticator {
@Override
public PasswordAuthentication getPasswordAuthentication() {
String username = senderUser;
String password = senderPassword;
return new PasswordAuthentication(username, password);
}
}
}
Sometimes I get the error:
com.sun.mail.smtp.SMTPSendFailedException: 421 4.4.2 Message submission rate for this client has exceeded the configured limit
but after reviewing with the Exchange Server Admin, he said the I have not send emails above the limit.
Sometimes also I get the error:
java.net.SocketException: Connection reset
Also sometimes I get:
javax.mail.MessagingException: Can't send command to SMTP host,Caused by: java.net.SocketException: Connection closed by remote host
I read that some people make the transport object static and make connection to exchange server only once and then reuse it, will that help to solve the issue, and for how long will the connection be open?
Also I have thought of a solution to save the emails details in a database table and make a job class to send the emails in a batch periodically.
Upvotes: 9
Views: 2715
Reputation: 360
below code must be run once only, not within every sendEmail calls:
initConfig()
and
transport = session.getTransport(emailProtocol);
transport.connect(senderEmail, senderPassword);
I would create a factory class which will initialize and return a transport instance, so i can use and reuse to maintain multiple connection to MX,
setting up session and transport must be done before any sendEmail call is made, so constructor is the right place for you.
you can make use of small object pool of session and transport object and every call to sendEmail method must obtain a transport instance from pool and after completion return it back to pool, this will ensure easy loading in concurrent request environment, Further to deduce bulk push limits, start with adding time gap between pushes and reduce the time gap to know close to accurate limit your MX allows between pushes and correlate it with push size to define final limits.
Upvotes: 2
Reputation: 1826
I edited your secondly declared sendEmail method (as follows). If you liked it, apply same technic to other sendEmail method.
As a solution, i grabbed sendMessage part into for loop of cc list. So, for each "to" email and for each corresponding "cc" email, email will be sent. Absence of cc list must also be evaluated by you. Code might not be compiling, but you should see the point.
public void sendEmail(String fromEmail, String fromDisplayName, List<String> toList, List<String> ccList,
String subject, String emailBody, String filePhysicalPath, String fileName, String fileContentType)
throws Exception {
Transport transport = null;
try {
initConfig();
MimeMultipart multipart = new MimeMultipart();
Authenticator authenticator = new SMTPAuthenticator();
MailSSLSocketFactory sslSocketFactory = new MailSSLSocketFactory();
MimeBodyPart bodyPart = new MimeBodyPart();
String html = "";
Properties props = System.getProperties();
setProps(props);
sslSocketFactory.setTrustAllHosts(true);
props.put("mail.smtp.ssl.socketFactory", sslSocketFactory);
Session session = Session.getInstance(props, authenticator);
// session.setDebug(true);
emailBody = emailBody + "<br/><br/>مرسل بواسطة : " + fromDisplayName;
html = "<html><body style='text-align:right'> " + emailBody + " </body></html>";
bodyPart.setContent(html, "text/html; charset=UTF-8");
multipart.addBodyPart(bodyPart);
MimeMessage message = new MimeMessage(session);
// attach file
BodyPart mimeBodyPart = new MimeBodyPart();
DataSource source = new FileDataSource(filePhysicalPath);
mimeBodyPart.setDataHandler(new DataHandler(source));
mimeBodyPart.setFileName(MimeUtility.encodeText(fileName, "utf-8", "B"));
multipart.addBodyPart(mimeBodyPart);
// end of file attach
message.setSubject(subject, "UTF-8");
message.setContent(multipart);
message.setFrom(new InternetAddress(senderEmail, fromDisplayName));
message.setReplyTo(new Address[] { new InternetAddress(fromEmail) });
transport = session.getTransport(emailProtocol);
transport.connect(senderEmail, senderPassword);
if (toList != null && toList.size() > 0) {
for (String to : toList) {
message.addRecipient(Message.RecipientType.TO, new InternetAddress(to));
if (ccList != null && ccList.size() > 0) {
for (String cc : ccList) {
message.addRecipient(Message.RecipientType.CC, new InternetAddress(cc));
message.setSentDate(new Date());
transport.sendMessage(message, message.getAllRecipients());
}
}
}
} else {
throw new Exception("List of users to send email to is empty");
}
} catch (Exception ex) {
throw ex;
} finally {
if (transport != null)
transport.close();
}
}
Upvotes: 2
Reputation: 2658
According the error message:
Message submission rate for this client has exceeded the configured limit
1.) This might be caused by the Exchange trotting policy which is often not known by Exchange Administrators (as mentioned here).
So let your Exchange Administrator control the MessageRateLimit inside the trotting policy via:
Get-ThrottlingPolicy | select Name,MessageRateLimit
Because Microsoft document this parameter as:
The MessageRateLimit parameter specifies the number of messages per minute that can be submitted to transport by POP3 or IMAP4 clients that use SMTP. Clients receive a transient error if they submit messages at a rate that exceeds the value of this parameter. Exchange attempts to connect and send the messages at a later time.
And a value which is to low might cause this issues. The Exchange Administrator can also create a new trotting policy with higher values for only this taskuser you are using and which isn´t then causing any issues with existing users (see here for an example). So there is no need to change the default trotting policy.
Update: 2.) As you now checked the trotting policy, another solution might be an SMTP solution which controls / monitors the SMTP traffic. This could be client side related or server side related (depending on your environment). Possible options here are: Firewall & AntivirusClients
Upvotes: 1