Unify SMTP mail sending (#4954)

* Add mail dependencies

* Create util to send SMTP mail

* Add unit tests to SMTP mail sender

* Use SMTP mail util on quota alert

* Use SMTP mail util on alert

* Use SMTP mail util on project

* Use SMTP mail util on usage alert

* Remove copyright line in license header

Co-authored-by: Gabriel Beims Bräscher <gabrascher@gmail.com>

* Remove copyright line in license header

Co-authored-by: Gabriel Beims Bräscher <gabrascher@gmail.com>

* Remove copyright line in license header

Co-authored-by: Gabriel Beims Bräscher <gabrascher@gmail.com>

* Remove copyright line in license header

Co-authored-by: Gabriel Beims Bräscher <gabrascher@gmail.com>

* Remove copyright line in license header

Co-authored-by: Gabriel Beims Bräscher <gabrascher@gmail.com>

Co-authored-by: Daniel Augusto Veronezi Salvador <daniel@scclouds.com.br>
Co-authored-by: Gabriel Beims Bräscher <gabrascher@gmail.com>
This commit is contained in:
Daniel Augusto Veronezi Salvador 2021-06-15 18:18:26 -03:00 committed by GitHub
parent bf6266188c
commit e962f0f271
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 1338 additions and 606 deletions

View File

@ -16,24 +16,15 @@
//under the License. //under the License.
package org.apache.cloudstack.quota; package org.apache.cloudstack.quota;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Properties;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.inject.Inject; import javax.inject.Inject;
import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.URLName;
import javax.mail.internet.InternetAddress;
import javax.naming.ConfigurationException; import javax.naming.ConfigurationException;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
@ -55,15 +46,14 @@ import com.cloud.user.AccountVO;
import com.cloud.user.UserVO; import com.cloud.user.UserVO;
import com.cloud.user.dao.AccountDao; import com.cloud.user.dao.AccountDao;
import com.cloud.user.dao.UserDao; import com.cloud.user.dao.UserDao;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.component.ManagerBase; import com.cloud.utils.component.ManagerBase;
import com.cloud.utils.db.TransactionLegacy; import com.cloud.utils.db.TransactionLegacy;
import com.google.common.base.Strings; import java.util.HashSet;
import com.sun.mail.smtp.SMTPMessage; import java.util.Set;
import com.sun.mail.smtp.SMTPSSLTransport; import org.apache.cloudstack.utils.mailing.MailAddress;
import com.sun.mail.smtp.SMTPTransport; import org.apache.cloudstack.utils.mailing.SMTPMailProperties;
import org.apache.commons.lang3.StringUtils; import org.apache.cloudstack.utils.mailing.SMTPMailSender;
import org.apache.commons.lang.BooleanUtils; import org.apache.commons.lang3.BooleanUtils;
@Component @Component
public class QuotaAlertManagerImpl extends ManagerBase implements QuotaAlertManager { public class QuotaAlertManagerImpl extends ManagerBase implements QuotaAlertManager {
@ -84,8 +74,9 @@ public class QuotaAlertManagerImpl extends ManagerBase implements QuotaAlertMana
@Inject @Inject
private QuotaManager _quotaManager; private QuotaManager _quotaManager;
private EmailQuotaAlert _emailQuotaAlert;
private boolean _lockAccountEnforcement = false; private boolean _lockAccountEnforcement = false;
private String senderAddress;
protected SMTPMailSender mailSender;
boolean _smtpDebug = false; boolean _smtpDebug = false;
@ -109,19 +100,16 @@ public class QuotaAlertManagerImpl extends ManagerBase implements QuotaAlertMana
mergeConfigs(configs, params); mergeConfigs(configs, params);
} }
final String smtpHost = configs.get(QuotaConfig.QuotaSmtpHost.key()); senderAddress = configs.get(QuotaConfig.QuotaSmtpSender.key());
int smtpPort = NumbersUtil.parseInt(configs.get(QuotaConfig.QuotaSmtpPort.key()), 25); _lockAccountEnforcement = BooleanUtils.toBoolean(configs.get(QuotaConfig.QuotaEnableEnforcement.key()));
String useAuthStr = configs.get(QuotaConfig.QuotaSmtpAuthType.key());
boolean useAuth = ((useAuthStr != null) && Boolean.parseBoolean(useAuthStr));
String smtpUsername = configs.get(QuotaConfig.QuotaSmtpUser.key()); String smtpUsername = configs.get(QuotaConfig.QuotaSmtpUser.key());
String smtpPassword = configs.get(QuotaConfig.QuotaSmtpPassword.key());
String emailSender = configs.get(QuotaConfig.QuotaSmtpSender.key());
String smtpEnabledSecurityProtocols = configs.get(QuotaConfig.QuotaSmtpEnabledSecurityProtocols.key());
String useStartTLSStr = configs.get(QuotaConfig.QuotaSmtpUseStartTLS.key());
boolean useStartTLS = BooleanUtils.toBoolean(useStartTLSStr);
_lockAccountEnforcement = "true".equalsIgnoreCase(configs.get(QuotaConfig.QuotaEnableEnforcement.key()));
_emailQuotaAlert = new EmailQuotaAlert(smtpHost, smtpPort, useAuth, smtpUsername, smtpPassword, emailSender, smtpEnabledSecurityProtocols, useStartTLS, _smtpDebug); String namespace = "quota.usage.smtp";
configs.put(String.format("%s.debug", namespace), String.valueOf(_smtpDebug));
configs.put(String.format("%s.username", namespace), smtpUsername);
mailSender = new SMTPMailSender(configs, namespace);
return true; return true;
} }
@ -234,7 +222,7 @@ public class QuotaAlertManagerImpl extends ManagerBase implements QuotaAlertMana
final String subject = templateEngine.replace(emailTemplate.getTemplateSubject()); final String subject = templateEngine.replace(emailTemplate.getTemplateSubject());
final String body = templateEngine.replace(emailTemplate.getTemplateBody()); final String body = templateEngine.replace(emailTemplate.getTemplateBody());
try { try {
_emailQuotaAlert.sendQuotaAlert(emailRecipients, subject, body); sendQuotaAlert(emailRecipients, subject, body);
emailToBeSent.sentSuccessfully(_quotaAcc); emailToBeSent.sentSuccessfully(_quotaAcc);
} catch (Exception e) { } catch (Exception e) {
s_logger.error(String.format("Unable to send quota alert email (subject=%s; body=%s) to account %s (%s) recipients (%s) due to error (%s)", subject, body, account.getAccountName(), s_logger.error(String.format("Unable to send quota alert email (subject=%s; body=%s) to account %s (%s) recipients (%s) due to error (%s)", subject, body, account.getAccountName(),
@ -337,99 +325,22 @@ public class QuotaAlertManagerImpl extends ManagerBase implements QuotaAlertMana
} }
}; };
static class EmailQuotaAlert { protected void sendQuotaAlert(List<String> emails, String subject, String body) {
private final Session _smtpSession; SMTPMailProperties mailProperties = new SMTPMailProperties();
private final String _smtpHost;
private final int _smtpPort;
private final boolean _smtpUseAuth;
private final String _smtpUsername;
private final String _smtpPassword;
private final String _emailSender;
private final boolean smtpUseStartTLS;
public EmailQuotaAlert(String smtpHost, int smtpPort, boolean smtpUseAuth, final String smtpUsername, final String smtpPassword, String emailSender, String smtpEnabledSecurityProtocols, boolean smtpUseStartTLS, boolean smtpDebug) { mailProperties.setSender(new MailAddress(senderAddress));
_smtpHost = smtpHost; mailProperties.setSubject(subject);
_smtpPort = smtpPort; mailProperties.setContent(body);
_smtpUseAuth = smtpUseAuth; mailProperties.setContentType("text/html; charset=utf-8");
_smtpUsername = smtpUsername;
_smtpPassword = smtpPassword;
_emailSender = emailSender;
this.smtpUseStartTLS = smtpUseStartTLS;
if (!Strings.isNullOrEmpty(_smtpHost)) { Set<MailAddress> addresses = new HashSet<>();
Properties smtpProps = new Properties(); for (String email : emails) {
smtpProps.put("mail.smtp.host", smtpHost); addresses.add(new MailAddress(email));
smtpProps.put("mail.smtp.port", smtpPort);
smtpProps.put("mail.smtp.auth", "" + smtpUseAuth);
if (smtpUsername != null) {
smtpProps.put("mail.smtp.user", smtpUsername);
}
smtpProps.put("mail.smtps.host", smtpHost);
smtpProps.put("mail.smtps.port", smtpPort);
smtpProps.put("mail.smtps.auth", "" + smtpUseAuth);
if (!Strings.isNullOrEmpty(smtpUsername)) {
smtpProps.put("mail.smtps.user", smtpUsername);
}
if (StringUtils.isNotBlank(smtpEnabledSecurityProtocols)) {
smtpProps.put("mail.smtp.ssl.protocols", smtpEnabledSecurityProtocols);
}
if (smtpUseAuth) {
smtpProps.put("mail.smtp.starttls.enable", smtpUseStartTLS);
}
if (!Strings.isNullOrEmpty(smtpUsername) && !Strings.isNullOrEmpty(smtpPassword)) {
_smtpSession = Session.getInstance(smtpProps, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(smtpUsername, smtpPassword);
}
});
} else {
_smtpSession = Session.getInstance(smtpProps);
}
_smtpSession.setDebug(smtpDebug);
} else {
_smtpSession = null;
}
} }
public void sendQuotaAlert(List<String> emails, String subject, String body) throws MessagingException, UnsupportedEncodingException { mailProperties.setRecipients(addresses);
if (_smtpSession == null) {
s_logger.error("Unable to create smtp session.");
return;
}
SMTPMessage msg = new SMTPMessage(_smtpSession);
msg.setSender(new InternetAddress(_emailSender, _emailSender));
msg.setFrom(new InternetAddress(_emailSender, _emailSender));
for (String email : emails) { mailSender.sendMail(mailProperties);
if (email != null && !email.isEmpty()) {
try {
InternetAddress address = new InternetAddress(email, email);
msg.addRecipient(Message.RecipientType.TO, address);
} catch (Exception pokemon) {
s_logger.error("Exception in creating address for:" + email, pokemon);
}
}
}
msg.setSubject(subject);
msg.setSentDate(new Date());
msg.setContent(body, "text/html; charset=utf-8");
msg.saveChanges();
SMTPTransport smtpTrans = null;
if (_smtpUseAuth && !this.smtpUseStartTLS) {
smtpTrans = new SMTPSSLTransport(_smtpSession, new URLName("smtp", _smtpHost, _smtpPort, null, _smtpUsername, _smtpPassword));
} else {
smtpTrans = new SMTPTransport(_smtpSession, new URLName("smtp", _smtpHost, _smtpPort, null, _smtpUsername, _smtpPassword));
}
smtpTrans.connect();
smtpTrans.sendMessage(msg, msg.getAllRecipients());
smtpTrans.close();
}
} }
} }

View File

@ -52,6 +52,8 @@ import com.cloud.user.dao.UserDao;
import com.cloud.utils.db.TransactionLegacy; import com.cloud.utils.db.TransactionLegacy;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.cloudstack.utils.mailing.SMTPMailProperties;
import org.apache.cloudstack.utils.mailing.SMTPMailSender;
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class QuotaAlertManagerImplTest extends TestCase { public class QuotaAlertManagerImplTest extends TestCase {
@ -68,8 +70,6 @@ public class QuotaAlertManagerImplTest extends TestCase {
private QuotaEmailTemplatesDao quotaEmailTemplateDao; private QuotaEmailTemplatesDao quotaEmailTemplateDao;
@Mock @Mock
private ConfigurationDao configDao; private ConfigurationDao configDao;
@Mock
private QuotaAlertManagerImpl.EmailQuotaAlert emailQuotaAlert;
@Spy @Spy
@InjectMocks @InjectMocks
@ -77,7 +77,6 @@ public class QuotaAlertManagerImplTest extends TestCase {
@Before @Before
public void setup() throws IllegalAccessException, NoSuchFieldException, ConfigurationException { public void setup() throws IllegalAccessException, NoSuchFieldException, ConfigurationException {
// Dummy transaction stack setup
TransactionLegacy.open("QuotaAlertManagerImplTest"); TransactionLegacy.open("QuotaAlertManagerImplTest");
} }
@ -135,7 +134,8 @@ public class QuotaAlertManagerImplTest extends TestCase {
quotaAccount.setQuotaAlertDate(null); quotaAccount.setQuotaAlertDate(null);
quotaAccount.setQuotaEnforce(0); quotaAccount.setQuotaEnforce(0);
QuotaAlertManagerImpl.DeferredQuotaEmail email = new QuotaAlertManagerImpl.DeferredQuotaEmail(account, quotaAccount, new BigDecimal(100), QuotaConfig.QuotaEmailTemplateTypes.QUOTA_LOW); QuotaAlertManagerImpl.DeferredQuotaEmail email = new QuotaAlertManagerImpl.DeferredQuotaEmail(account, quotaAccount, new BigDecimal(100),
QuotaConfig.QuotaEmailTemplateTypes.QUOTA_LOW);
QuotaEmailTemplatesVO quotaEmailTemplatesVO = new QuotaEmailTemplatesVO(); QuotaEmailTemplatesVO quotaEmailTemplatesVO = new QuotaEmailTemplatesVO();
quotaEmailTemplatesVO.setTemplateSubject("Low quota"); quotaEmailTemplatesVO.setTemplateSubject("Low quota");
@ -156,9 +156,13 @@ public class QuotaAlertManagerImplTest extends TestCase {
users.add(user); users.add(user);
Mockito.when(userDao.listByAccount(Mockito.anyLong())).thenReturn(users); Mockito.when(userDao.listByAccount(Mockito.anyLong())).thenReturn(users);
quotaAlertManager.mailSender = Mockito.mock(SMTPMailSender.class);
Mockito.when(quotaAlertManager.mailSender.sendMail(Mockito.anyObject())).thenReturn(Boolean.TRUE);
quotaAlertManager.sendQuotaAlert(email); quotaAlertManager.sendQuotaAlert(email);
assertTrue(email.getSendDate() != null); assertTrue(email.getSendDate() != null);
Mockito.verify(emailQuotaAlert, Mockito.times(1)).sendQuotaAlert(Mockito.anyListOf(String.class), Mockito.anyString(), Mockito.anyString()); Mockito.verify(quotaAlertManager, Mockito.times(1)).sendQuotaAlert(Mockito.anyListOf(String.class), Mockito.anyString(), Mockito.anyString());
Mockito.verify(quotaAlertManager.mailSender, Mockito.times(1)).sendMail(Mockito.any(SMTPMailProperties.class));
} }
@Test @Test

View File

@ -23,20 +23,12 @@ import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Properties;
import java.util.Timer; import java.util.Timer;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import javax.inject.Inject; import javax.inject.Inject;
import javax.mail.Authenticator;
import javax.mail.Message.RecipientType;
import javax.mail.MessagingException; import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.SendFailedException;
import javax.mail.Session;
import javax.mail.URLName;
import javax.mail.internet.InternetAddress;
import javax.naming.ConfigurationException; import javax.naming.ConfigurationException;
import org.apache.cloudstack.framework.config.ConfigDepot; import org.apache.cloudstack.framework.config.ConfigDepot;
@ -78,13 +70,15 @@ import com.cloud.resource.ResourceManager;
import com.cloud.service.ServiceOfferingVO; import com.cloud.service.ServiceOfferingVO;
import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.service.dao.ServiceOfferingDao;
import com.cloud.storage.StorageManager; import com.cloud.storage.StorageManager;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.component.ManagerBase; import com.cloud.utils.component.ManagerBase;
import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.concurrency.NamedThreadFactory;
import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria;
import com.sun.mail.smtp.SMTPMessage; import java.util.HashSet;
import com.sun.mail.smtp.SMTPSSLTransport; import java.util.Set;
import com.sun.mail.smtp.SMTPTransport; import org.apache.cloudstack.utils.mailing.MailAddress;
import org.apache.cloudstack.utils.mailing.SMTPMailProperties;
import org.apache.cloudstack.utils.mailing.SMTPMailSender;
import org.apache.commons.lang3.math.NumberUtils;
public class AlertManagerImpl extends ManagerBase implements AlertManager, Configurable { public class AlertManagerImpl extends ManagerBase implements AlertManager, Configurable {
private static final Logger s_logger = Logger.getLogger(AlertManagerImpl.class.getName()); private static final Logger s_logger = Logger.getLogger(AlertManagerImpl.class.getName());
@ -94,7 +88,6 @@ public class AlertManagerImpl extends ManagerBase implements AlertManager, Confi
private static final DecimalFormat DfPct = new DecimalFormat("###.##"); private static final DecimalFormat DfPct = new DecimalFormat("###.##");
private static final DecimalFormat DfWhole = new DecimalFormat("########"); private static final DecimalFormat DfWhole = new DecimalFormat("########");
private EmailAlert _emailAlert;
@Inject @Inject
private AlertDao _alertDao; private AlertDao _alertDao;
@Inject @Inject
@ -138,6 +131,10 @@ public class AlertManagerImpl extends ManagerBase implements AlertManager, Confi
private final ExecutorService _executor; private final ExecutorService _executor;
protected SMTPMailSender mailSender;
protected String[] recipients = null;
protected String senderAddress = null;
public AlertManagerImpl() { public AlertManagerImpl() {
_executor = Executors.newCachedThreadPool(new NamedThreadFactory("Email-Alerts-Sender")); _executor = Executors.newCachedThreadPool(new NamedThreadFactory("Email-Alerts-Sender"));
} }
@ -148,27 +145,23 @@ public class AlertManagerImpl extends ManagerBase implements AlertManager, Confi
// set up the email system for alerts // set up the email system for alerts
String emailAddressList = configs.get("alert.email.addresses"); String emailAddressList = configs.get("alert.email.addresses");
String[] emailAddresses = null;
if (emailAddressList != null) { if (emailAddressList != null) {
emailAddresses = emailAddressList.split(","); recipients = emailAddressList.split(",");
} }
String smtpHost = configs.get("alert.smtp.host"); senderAddress = configs.get("alert.email.sender");
int smtpPort = NumbersUtil.parseInt(configs.get("alert.smtp.port"), 25);
String useAuthStr = configs.get("alert.smtp.useAuth");
boolean useAuth = ((useAuthStr == null) ? false : Boolean.parseBoolean(useAuthStr));
String smtpUsername = configs.get("alert.smtp.username");
String smtpPassword = configs.get("alert.smtp.password");
String emailSender = configs.get("alert.email.sender");
String smtpDebugStr = configs.get("alert.smtp.debug");
int smtpTimeout = NumbersUtil.parseInt(configs.get("alert.smtp.timeout"), 30000);
int smtpConnectionTimeout = NumbersUtil.parseInt(configs.get("alert.smtp.connectiontimeout"), 30000);
boolean smtpDebug = false;
if (smtpDebugStr != null) {
smtpDebug = Boolean.parseBoolean(smtpDebugStr);
}
_emailAlert = new EmailAlert(emailAddresses, smtpHost, smtpPort, smtpConnectionTimeout, smtpTimeout, useAuth, smtpUsername, smtpPassword, emailSender, smtpDebug); String namespace = "alert.smtp";
String timeoutConfig = String.format("%s.timeout", namespace);
String connectionTimeoutConfig = String.format("%s.connectiontimeout", namespace);
int smtpTimeout = NumberUtils.toInt(configs.get(timeoutConfig), 30000);
int smtpConnectionTimeout = NumberUtils.toInt(configs.get(connectionTimeoutConfig), 30000);
configs.put(timeoutConfig, String.valueOf(smtpTimeout));
configs.put(connectionTimeoutConfig, String.valueOf(smtpConnectionTimeout));
mailSender = new SMTPMailSender(configs, namespace);
String publicIPCapacityThreshold = _configDao.getValue(Config.PublicIpCapacityThreshold.key()); String publicIPCapacityThreshold = _configDao.getValue(Config.PublicIpCapacityThreshold.key());
String privateIPCapacityThreshold = _configDao.getValue(Config.PrivateIpCapacityThreshold.key()); String privateIPCapacityThreshold = _configDao.getValue(Config.PrivateIpCapacityThreshold.key());
@ -231,9 +224,7 @@ public class AlertManagerImpl extends ManagerBase implements AlertManager, Confi
@Override @Override
public void clearAlert(AlertType alertType, long dataCenterId, long podId) { public void clearAlert(AlertType alertType, long dataCenterId, long podId) {
try { try {
if (_emailAlert != null) { clearAlert(alertType.getType(), dataCenterId, podId);
_emailAlert.clearAlert(alertType.getType(), dataCenterId, podId);
}
} catch (Exception ex) { } catch (Exception ex) {
s_logger.error("Problem clearing email alert", ex); s_logger.error("Problem clearing email alert", ex);
} }
@ -248,8 +239,8 @@ public class AlertManagerImpl extends ManagerBase implements AlertManager, Confi
// TODO: queue up these messages and send them as one set of issues once a certain number of issues is reached? If that's the case, // TODO: queue up these messages and send them as one set of issues once a certain number of issues is reached? If that's the case,
// shouldn't we have a type/severity as part of the API so that severe errors get sent right away? // shouldn't we have a type/severity as part of the API so that severe errors get sent right away?
try { try {
if (_emailAlert != null) { if (mailSender != null) {
_emailAlert.sendAlert(alertType, dataCenterId, podId, null, subject, body); sendAlert(alertType, dataCenterId, podId, null, subject, body);
} else { } else {
s_logger.warn("AlertType:: " + alertType + " | dataCenterId:: " + dataCenterId + " | podId:: " + podId + s_logger.warn("AlertType:: " + alertType + " | dataCenterId:: " + dataCenterId + " | podId:: " + podId +
" | message:: " + subject + " | body:: " + body); " | message:: " + subject + " | body:: " + body);
@ -441,8 +432,7 @@ public class AlertManagerImpl extends ManagerBase implements AlertManager, Confi
recalculateCapacity(); recalculateCapacity();
// abort if we can't possibly send an alert... if (mailSender == null) {
if (_emailAlert == null) {
return; return;
} }
@ -642,7 +632,7 @@ public class AlertManagerImpl extends ManagerBase implements AlertManager, Confi
s_logger.debug(msgSubject); s_logger.debug(msgSubject);
s_logger.debug(msgContent); s_logger.debug(msgContent);
} }
_emailAlert.sendAlert(alertType, dc.getId(), podId, clusterId, msgSubject, msgContent); sendAlert(alertType, dc.getId(), podId, clusterId, msgSubject, msgContent);
} catch (Exception ex) { } catch (Exception ex) {
s_logger.error("Exception in CapacityChecker", ex); s_logger.error("Exception in CapacityChecker", ex);
} }
@ -679,170 +669,73 @@ public class AlertManagerImpl extends ManagerBase implements AlertManager, Confi
} }
class EmailAlert { public void clearAlert(short alertType, long dataCenterId, Long podId) {
private Session _smtpSession; if (alertType != -1) {
private InternetAddress[] _recipientList; AlertVO alert = _alertDao.getLastAlert(alertType, dataCenterId, podId, null);
private final String _smtpHost; if (alert != null) {
private int _smtpPort = -1; AlertVO updatedAlert = _alertDao.createForUpdate();
private boolean _smtpUseAuth = false; updatedAlert.setResolved(new Date());
private final String _smtpUsername; _alertDao.update(alert.getId(), updatedAlert);
private final String _smtpPassword;
private final String _emailSender;
private int _smtpTimeout;
private int _smtpConnectionTimeout;
public EmailAlert(String[] recipientList, String smtpHost, int smtpPort, int smtpConnectionTimeout, int smtpTimeout, boolean smtpUseAuth,
final String smtpUsername,
final String smtpPassword, String emailSender, boolean smtpDebug) {
if (recipientList != null) {
_recipientList = new InternetAddress[recipientList.length];
for (int i = 0; i < recipientList.length; i++) {
try {
_recipientList[i] = new InternetAddress(recipientList[i], recipientList[i]);
} catch (Exception ex) {
s_logger.error("Exception creating address for: " + recipientList[i], ex);
}
}
}
_smtpHost = smtpHost;
_smtpPort = smtpPort;
_smtpUseAuth = smtpUseAuth;
_smtpUsername = smtpUsername;
_smtpPassword = smtpPassword;
_emailSender = emailSender;
_smtpTimeout = smtpTimeout;
_smtpConnectionTimeout = smtpConnectionTimeout;
if (_smtpHost != null) {
Properties smtpProps = new Properties();
smtpProps.put("mail.smtp.host", smtpHost);
smtpProps.put("mail.smtp.port", smtpPort);
smtpProps.put("mail.smtp.auth", "" + smtpUseAuth);
smtpProps.put("mail.smtp.timeout", _smtpTimeout);
smtpProps.put("mail.smtp.connectiontimeout", _smtpConnectionTimeout);
if (smtpUsername != null) {
smtpProps.put("mail.smtp.user", smtpUsername);
}
smtpProps.put("mail.smtps.host", smtpHost);
smtpProps.put("mail.smtps.port", smtpPort);
smtpProps.put("mail.smtps.auth", "" + smtpUseAuth);
smtpProps.put("mail.smtps.timeout", _smtpTimeout);
smtpProps.put("mail.smtps.connectiontimeout", _smtpConnectionTimeout);
if (smtpUsername != null) {
smtpProps.put("mail.smtps.user", smtpUsername);
}
if ((smtpUsername != null) && (smtpPassword != null)) {
_smtpSession = Session.getInstance(smtpProps, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(smtpUsername, smtpPassword);
}
});
} else {
_smtpSession = Session.getInstance(smtpProps);
}
_smtpSession.setDebug(smtpDebug);
} else {
_smtpSession = null;
} }
} }
}
// TODO: make sure this handles SSL transport (useAuth is true) and regular public void sendAlert(AlertType alertType, long dataCenterId, Long podId, Long clusterId, String subject, String content)
public void sendAlert(AlertType alertType, long dataCenterId, Long podId, Long clusterId, String subject, String content) throws MessagingException, throws MessagingException, UnsupportedEncodingException {
UnsupportedEncodingException { s_logger.warn(String.format("alertType=[%s] dataCenterId=[%s] podId=[%s] clusterId=[%s] message=[%s].", alertType, dataCenterId, podId, clusterId, subject));
s_logger.warn("AlertType:: " + alertType + " | dataCenterId:: " + dataCenterId + " | podId:: " + AlertVO alert = null;
podId + " | clusterId:: " + clusterId + " | message:: " + subject); if ((alertType != AlertManager.AlertType.ALERT_TYPE_HOST) && (alertType != AlertManager.AlertType.ALERT_TYPE_USERVM)
AlertVO alert = null; && (alertType != AlertManager.AlertType.ALERT_TYPE_DOMAIN_ROUTER) && (alertType != AlertManager.AlertType.ALERT_TYPE_CONSOLE_PROXY)
if ((alertType != AlertManager.AlertType.ALERT_TYPE_HOST) && && (alertType != AlertManager.AlertType.ALERT_TYPE_SSVM) && (alertType != AlertManager.AlertType.ALERT_TYPE_STORAGE_MISC)
(alertType != AlertManager.AlertType.ALERT_TYPE_USERVM) && && (alertType != AlertManager.AlertType.ALERT_TYPE_MANAGMENT_NODE) && (alertType != AlertManager.AlertType.ALERT_TYPE_RESOURCE_LIMIT_EXCEEDED)
(alertType != AlertManager.AlertType.ALERT_TYPE_DOMAIN_ROUTER) && && (alertType != AlertManager.AlertType.ALERT_TYPE_UPLOAD_FAILED) && (alertType != AlertManager.AlertType.ALERT_TYPE_OOBM_AUTH_ERROR)
(alertType != AlertManager.AlertType.ALERT_TYPE_CONSOLE_PROXY) && && (alertType != AlertManager.AlertType.ALERT_TYPE_HA_ACTION) && (alertType != AlertManager.AlertType.ALERT_TYPE_CA_CERT)) {
(alertType != AlertManager.AlertType.ALERT_TYPE_SSVM) && alert = _alertDao.getLastAlert(alertType.getType(), dataCenterId, podId, clusterId);
(alertType != AlertManager.AlertType.ALERT_TYPE_STORAGE_MISC) && }
(alertType != AlertManager.AlertType.ALERT_TYPE_MANAGMENT_NODE) &&
(alertType != AlertManager.AlertType.ALERT_TYPE_RESOURCE_LIMIT_EXCEEDED) &&
(alertType != AlertManager.AlertType.ALERT_TYPE_UPLOAD_FAILED) &&
(alertType != AlertManager.AlertType.ALERT_TYPE_OOBM_AUTH_ERROR) &&
(alertType != AlertManager.AlertType.ALERT_TYPE_HA_ACTION) &&
(alertType != AlertManager.AlertType.ALERT_TYPE_CA_CERT) &&
(alertType != AlertManager.AlertType.ALERT_TYPE_VM_SNAPSHOT)) {
alert = _alertDao.getLastAlert(alertType.getType(), dataCenterId, podId, clusterId);
}
if (alert == null) { if (alert == null) {
// set up a new alert AlertVO newAlert = new AlertVO();
AlertVO newAlert = new AlertVO(); newAlert.setType(alertType.getType());
newAlert.setType(alertType.getType()); newAlert.setSubject(subject);
newAlert.setSubject(subject); newAlert.setContent(content);
newAlert.setContent(content); newAlert.setClusterId(clusterId);
newAlert.setClusterId(clusterId); newAlert.setPodId(podId);
newAlert.setPodId(podId); newAlert.setDataCenterId(dataCenterId);
newAlert.setDataCenterId(dataCenterId); newAlert.setSentCount(1);
newAlert.setSentCount(1); // Initialize sent count to 1 since we are now sending an alert. newAlert.setLastSent(new Date());
newAlert.setLastSent(new Date()); newAlert.setName(alertType.getName());
newAlert.setName(alertType.getName()); _alertDao.persist(newAlert);
_alertDao.persist(newAlert); } else {
} else { if (s_logger.isDebugEnabled()) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Have already sent: " + alert.getSentCount() + " emails for alert type '" + alertType + "' -- skipping send email"); s_logger.debug("Have already sent: " + alert.getSentCount() + " emails for alert type '" + alertType + "' -- skipping send email");
} }
return; return;
}
if (_smtpSession != null) {
SMTPMessage msg = new SMTPMessage(_smtpSession);
msg.setSender(new InternetAddress(_emailSender, _emailSender));
msg.setFrom(new InternetAddress(_emailSender, _emailSender));
for (InternetAddress address : _recipientList) {
msg.addRecipient(RecipientType.TO, address);
}
msg.setSubject(subject);
msg.setSentDate(new Date());
msg.setContent(content, "text/plain");
msg.saveChanges();
SMTPTransport smtpTrans = null;
if (_smtpUseAuth) {
smtpTrans = new SMTPSSLTransport(_smtpSession, new URLName("smtp", _smtpHost, _smtpPort, null, _smtpUsername, _smtpPassword));
} else {
smtpTrans = new SMTPTransport(_smtpSession, new URLName("smtp", _smtpHost, _smtpPort, null, _smtpUsername, _smtpPassword));
}
sendMessage(smtpTrans, msg);
}
} }
private void sendMessage(final SMTPTransport smtpTrans, final SMTPMessage msg) { SMTPMailProperties mailProps = new SMTPMailProperties();
_executor.execute(new Runnable() { mailProps.setSender(new MailAddress(senderAddress));
@Override mailProps.setSubject(subject);
public void run() { mailProps.setContent(content);
try { mailProps.setContentType("text/plain");
smtpTrans.connect();
smtpTrans.sendMessage(msg, msg.getAllRecipients()); Set<MailAddress> addresses = new HashSet<>();
smtpTrans.close(); for (String recipient : recipients) {
} catch (SendFailedException e) { addresses.add(new MailAddress(recipient));
s_logger.error(" Failed to send email alert " + e);
} catch (MessagingException e) {
s_logger.error(" Failed to send email alert " + e);
}
}
});
} }
public void clearAlert(short alertType, long dataCenterId, Long podId) { mailProps.setRecipients(addresses);
if (alertType != -1) {
AlertVO alert = _alertDao.getLastAlert(alertType, dataCenterId, podId, null); sendMessage(mailProps);
if (alert != null) {
AlertVO updatedAlert = _alertDao.createForUpdate(); }
updatedAlert.setResolved(new Date());
_alertDao.update(alert.getId(), updatedAlert); private void sendMessage(SMTPMailProperties mailProps) {
} _executor.execute(new Runnable() {
@Override
public void run() {
mailSender.sendMail(mailProps);
} }
} });
} }
private static String formatPercent(double percentage) { private static String formatPercent(double percentage) {

View File

@ -17,11 +17,9 @@
package com.cloud.projects; package com.cloud.projects;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Properties;
import java.util.Random; import java.util.Random;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.UUID; import java.util.UUID;
@ -31,13 +29,7 @@ import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.inject.Inject; import javax.inject.Inject;
import javax.mail.Authenticator;
import javax.mail.Message.RecipientType;
import javax.mail.MessagingException; import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.URLName;
import javax.mail.internet.InternetAddress;
import javax.naming.ConfigurationException; import javax.naming.ConfigurationException;
import org.apache.cloudstack.acl.ProjectRole; import org.apache.cloudstack.acl.ProjectRole;
@ -79,8 +71,6 @@ import com.cloud.user.ResourceLimitService;
import com.cloud.user.User; import com.cloud.user.User;
import com.cloud.user.dao.AccountDao; import com.cloud.user.dao.AccountDao;
import com.cloud.user.dao.UserDao; import com.cloud.user.dao.UserDao;
import com.cloud.utils.DateUtil;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.component.ManagerBase; import com.cloud.utils.component.ManagerBase;
import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.concurrency.NamedThreadFactory;
import com.cloud.utils.db.DB; import com.cloud.utils.db.DB;
@ -90,14 +80,16 @@ import com.cloud.utils.db.TransactionCallbackNoReturn;
import com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn; import com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn;
import com.cloud.utils.db.TransactionStatus; import com.cloud.utils.db.TransactionStatus;
import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.exception.CloudRuntimeException;
import com.sun.mail.smtp.SMTPMessage; import java.util.HashSet;
import com.sun.mail.smtp.SMTPSSLTransport; import java.util.Set;
import com.sun.mail.smtp.SMTPTransport; import org.apache.cloudstack.utils.mailing.MailAddress;
import org.apache.cloudstack.utils.mailing.SMTPMailProperties;
import org.apache.cloudstack.utils.mailing.SMTPMailSender;
import org.apache.commons.lang3.BooleanUtils;
@Component @Component
public class ProjectManagerImpl extends ManagerBase implements ProjectManager { public class ProjectManagerImpl extends ManagerBase implements ProjectManager {
public static final Logger s_logger = Logger.getLogger(ProjectManagerImpl.class); public static final Logger s_logger = Logger.getLogger(ProjectManagerImpl.class);
private EmailInvite _emailInvite;
@Inject @Inject
private DomainDao _domainDao; private DomainDao _domainDao;
@ -137,33 +129,23 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager {
protected boolean _allowUserToCreateProject = true; protected boolean _allowUserToCreateProject = true;
protected ScheduledExecutorService _executor; protected ScheduledExecutorService _executor;
protected int _projectCleanupExpInvInterval = 60; //Interval defining how often project invitation cleanup thread is running protected int _projectCleanupExpInvInterval = 60; //Interval defining how often project invitation cleanup thread is running
private String senderAddress;
protected SMTPMailSender mailSender;
@Override @Override
public boolean configure(final String name, final Map<String, Object> params) throws ConfigurationException { public boolean configure(final String name, final Map<String, Object> params) throws ConfigurationException {
Map<String, String> configs = _configDao.getConfiguration(params); Map<String, String> configs = _configDao.getConfiguration(params);
_invitationRequired = Boolean.valueOf(configs.get(Config.ProjectInviteRequired.key())); _invitationRequired = BooleanUtils.toBoolean(configs.get(Config.ProjectInviteRequired.key()));
String value = configs.get(Config.ProjectInvitationExpirationTime.key()); String value = configs.get(Config.ProjectInvitationExpirationTime.key());
_invitationTimeOut = Long.parseLong(value != null ? value : "86400") * 1000; _invitationTimeOut = Long.parseLong(value != null ? value : "86400") * 1000;
_allowUserToCreateProject = Boolean.valueOf(configs.get(Config.AllowUserToCreateProject.key())); _allowUserToCreateProject = BooleanUtils.toBoolean(configs.get(Config.AllowUserToCreateProject.key()));
senderAddress = configs.get("project.email.sender");
// set up the email system for project invitations String namespace = "project.smtp";
String smtpHost = configs.get("project.smtp.host"); mailSender = new SMTPMailSender(configs, namespace);
int smtpPort = NumbersUtil.parseInt(configs.get("project.smtp.port"), 25);
String useAuthStr = configs.get("project.smtp.useAuth");
boolean useAuth = ((useAuthStr == null) ? false : Boolean.parseBoolean(useAuthStr));
String smtpUsername = configs.get("project.smtp.username");
String smtpPassword = configs.get("project.smtp.password");
String emailSender = configs.get("project.email.sender");
String smtpDebugStr = configs.get("project.smtp.debug");
boolean smtpDebug = false;
if (smtpDebugStr != null) {
smtpDebug = Boolean.parseBoolean(smtpDebugStr);
}
_emailInvite = new EmailInvite(smtpHost, smtpPort, useAuth, smtpUsername, smtpPassword, emailSender, smtpDebug);
_executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("Project-ExpireInvitations")); _executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("Project-ExpireInvitations"));
return true; return true;
@ -1081,7 +1063,7 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager {
ProjectInvitation projectInvitation = _projectInvitationDao.persist(projectInvitationVO); ProjectInvitation projectInvitation = _projectInvitationDao.persist(projectInvitationVO);
try { try {
_emailInvite.sendInvite(token, email, project.getId()); sendInvite(token, email, project.getId());
} catch (Exception ex) { } catch (Exception ex) {
s_logger.warn("Failed to send project id=" + project + " invitation to the email " + email + "; removing the invitation record from the db", ex); s_logger.warn("Failed to send project id=" + project + " invitation to the email " + email + "; removing the invitation record from the db", ex);
_projectInvitationDao.remove(projectInvitation.getId()); _projectInvitationDao.remove(projectInvitation.getId());
@ -1091,6 +1073,27 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager {
return projectInvitation; return projectInvitation;
} }
protected void sendInvite(String token, String email, long projectId) throws MessagingException, UnsupportedEncodingException {
String subject = String.format("You are invited to join the cloud stack project id=[%s].", projectId);
String content = String.format("You've been invited to join the CloudStack project id=[%s]. Please use token [%s] to complete registration", projectId, token);
SMTPMailProperties mailProperties = new SMTPMailProperties();
mailProperties.setSender(new MailAddress(senderAddress));
mailProperties.setSubject(subject);
mailProperties.setContent(content);
mailProperties.setContentType("text/plain");
Set<MailAddress> addresses = new HashSet<>();
addresses.add(new MailAddress(email));
mailProperties.setRecipients(addresses);
mailSender.sendMail(mailProperties);
}
private boolean expireInvitation(ProjectInvitationVO invite) { private boolean expireInvitation(ProjectInvitationVO invite) {
s_logger.debug("Expiring invitation id=" + invite.getId()); s_logger.debug("Expiring invitation id=" + invite.getId());
invite.setState(ProjectInvitation.State.Expired); invite.setState(ProjectInvitation.State.Expired);
@ -1305,91 +1308,6 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager {
} }
return sb.toString(); return sb.toString();
} }
class EmailInvite {
private Session _smtpSession;
private final String _smtpHost;
private int _smtpPort = -1;
private boolean _smtpUseAuth = false;
private final String _smtpUsername;
private final String _smtpPassword;
private final String _emailSender;
public EmailInvite(String smtpHost, int smtpPort, boolean smtpUseAuth, final String smtpUsername, final String smtpPassword, String emailSender, boolean smtpDebug) {
_smtpHost = smtpHost;
_smtpPort = smtpPort;
_smtpUseAuth = smtpUseAuth;
_smtpUsername = smtpUsername;
_smtpPassword = smtpPassword;
_emailSender = emailSender;
if (_smtpHost != null) {
Properties smtpProps = new Properties();
smtpProps.put("mail.smtp.host", smtpHost);
smtpProps.put("mail.smtp.port", smtpPort);
smtpProps.put("mail.smtp.auth", "" + smtpUseAuth);
if (smtpUsername != null) {
smtpProps.put("mail.smtp.user", smtpUsername);
}
smtpProps.put("mail.smtps.host", smtpHost);
smtpProps.put("mail.smtps.port", smtpPort);
smtpProps.put("mail.smtps.auth", "" + smtpUseAuth);
if (smtpUsername != null) {
smtpProps.put("mail.smtps.user", smtpUsername);
}
if ((smtpUsername != null) && (smtpPassword != null)) {
_smtpSession = Session.getInstance(smtpProps, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(smtpUsername, smtpPassword);
}
});
} else {
_smtpSession = Session.getInstance(smtpProps);
}
_smtpSession.setDebug(smtpDebug);
} else {
_smtpSession = null;
}
}
public void sendInvite(String token, String email, long projectId) throws MessagingException, UnsupportedEncodingException {
if (_smtpSession != null) {
InternetAddress address = null;
if (email != null) {
try {
address = new InternetAddress(email, email);
} catch (Exception ex) {
s_logger.error("Exception creating address for: " + email, ex);
}
}
String content = "You've been invited to join the CloudStack project id=" + projectId + ". Please use token " + token + " to complete registration";
SMTPMessage msg = new SMTPMessage(_smtpSession);
msg.setSender(new InternetAddress(_emailSender, _emailSender));
msg.setFrom(new InternetAddress(_emailSender, _emailSender));
msg.addRecipient(RecipientType.TO, address);
msg.setSubject("You are invited to join the cloud stack project id=" + projectId);
msg.setSentDate(new Date(DateUtil.currentGMTTime().getTime() >> 10));
msg.setContent(content, "text/plain");
msg.saveChanges();
SMTPTransport smtpTrans = null;
if (_smtpUseAuth) {
smtpTrans = new SMTPSSLTransport(_smtpSession, new URLName("smtp", _smtpHost, _smtpPort, null, _smtpUsername, _smtpPassword));
} else {
smtpTrans = new SMTPTransport(_smtpSession, new URLName("smtp", _smtpHost, _smtpPort, null, _smtpUsername, _smtpPassword));
}
smtpTrans.connect();
smtpTrans.sendMessage(msg, msg.getAllRecipients());
smtpTrans.close();
} else {
throw new CloudRuntimeException("Unable to send email invitation; smtp ses");
}
}
}
@Override @Override
@DB @DB

View File

@ -16,19 +16,10 @@
// under the License. // under the License.
package com.cloud.usage; package com.cloud.usage;
import java.io.UnsupportedEncodingException;
import java.util.Date; import java.util.Date;
import java.util.Map; import java.util.Map;
import java.util.Properties;
import javax.inject.Inject; import javax.inject.Inject;
import javax.mail.Authenticator;
import javax.mail.Message.RecipientType;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.URLName;
import javax.mail.internet.InternetAddress;
import javax.naming.ConfigurationException; import javax.naming.ConfigurationException;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
@ -38,18 +29,21 @@ import org.springframework.stereotype.Component;
import com.cloud.alert.AlertManager; import com.cloud.alert.AlertManager;
import com.cloud.alert.AlertVO; import com.cloud.alert.AlertVO;
import com.cloud.alert.dao.AlertDao; import com.cloud.alert.dao.AlertDao;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.component.ManagerBase; import com.cloud.utils.component.ManagerBase;
import com.sun.mail.smtp.SMTPMessage; import java.util.HashSet;
import com.sun.mail.smtp.SMTPSSLTransport; import java.util.Set;
import com.sun.mail.smtp.SMTPTransport; import org.apache.cloudstack.utils.mailing.MailAddress;
import org.apache.cloudstack.utils.mailing.SMTPMailProperties;
import org.apache.cloudstack.utils.mailing.SMTPMailSender;
@Component @Component
public class UsageAlertManagerImpl extends ManagerBase implements AlertManager { public class UsageAlertManagerImpl extends ManagerBase implements AlertManager {
private static final Logger s_logger = Logger.getLogger(UsageAlertManagerImpl.class.getName()); private static final Logger s_logger = Logger.getLogger(UsageAlertManagerImpl.class.getName());
private static final Logger s_alertsLogger = Logger.getLogger("org.apache.cloudstack.alerts");
private EmailAlert _emailAlert; private String senderAddress;
protected SMTPMailSender mailSender;
protected String[] recipients;
@Inject @Inject
private AlertDao _alertDao; private AlertDao _alertDao;
@Inject @Inject
@ -59,185 +53,75 @@ public class UsageAlertManagerImpl extends ManagerBase implements AlertManager {
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException { public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
Map<String, String> configs = _configDao.getConfiguration("management-server", params); Map<String, String> configs = _configDao.getConfiguration("management-server", params);
// set up the email system for alerts senderAddress = configs.get("alert.email.sender");
String emailAddressList = configs.get("alert.email.addresses"); String emailAddressList = configs.get("alert.email.addresses");
String[] emailAddresses = null; recipients = null;
if (emailAddressList != null) { if (emailAddressList != null) {
emailAddresses = emailAddressList.split(","); recipients = emailAddressList.split(",");
} }
String smtpHost = configs.get("alert.smtp.host"); String namespace = "alert.smtp";
int smtpPort = NumbersUtil.parseInt(configs.get("alert.smtp.port"), 25);
String useAuthStr = configs.get("alert.smtp.useAuth"); mailSender = new SMTPMailSender(configs, namespace);
boolean useAuth = ((useAuthStr == null) ? false : Boolean.parseBoolean(useAuthStr));
String smtpUsername = configs.get("alert.smtp.username");
String smtpPassword = configs.get("alert.smtp.password");
String emailSender = configs.get("alert.email.sender");
String smtpDebugStr = configs.get("alert.smtp.debug");
boolean smtpDebug = false;
if (smtpDebugStr != null) {
smtpDebug = Boolean.parseBoolean(smtpDebugStr);
}
_emailAlert = new EmailAlert(emailAddresses, smtpHost, smtpPort, useAuth, smtpUsername, smtpPassword, emailSender, smtpDebug);
return true; return true;
} }
@Override @Override
public void clearAlert(AlertType alertType, long dataCenterId, long podId) { public void clearAlert(AlertType alertType, long dataCenterId, long podId) {
try { try {
if (_emailAlert != null) { clearAlert(alertType.getType(), dataCenterId, podId);
_emailAlert.clearAlert(alertType.getType(), dataCenterId, podId);
}
} catch (Exception ex) { } catch (Exception ex) {
s_logger.error("Problem clearing email alert", ex); s_logger.error("Problem clearing email alert", ex);
} }
} }
@Override @Override
public void sendAlert(AlertType alertType, long dataCenterId, Long podId, String subject, String body) { public void sendAlert(AlertType alertType, long dataCenterId, Long podId, String subject, String content) {
// TODO: queue up these messages and send them as one set of issues once a certain number of issues is reached? If that's the case, AlertVO alert = null;
// shouldn't we have a type/severity as part of the API so that severe errors get sent right away? if ((alertType != AlertManager.AlertType.ALERT_TYPE_HOST) && (alertType != AlertManager.AlertType.ALERT_TYPE_USERVM)
try { && (alertType != AlertManager.AlertType.ALERT_TYPE_DOMAIN_ROUTER) && (alertType != AlertManager.AlertType.ALERT_TYPE_CONSOLE_PROXY)
if (_emailAlert != null) { && (alertType != AlertManager.AlertType.ALERT_TYPE_SSVM) && (alertType != AlertManager.AlertType.ALERT_TYPE_STORAGE_MISC)
_emailAlert.sendAlert(alertType, dataCenterId, podId, subject, body); && (alertType != AlertManager.AlertType.ALERT_TYPE_MANAGMENT_NODE)) {
} else { alert = _alertDao.getLastAlert(alertType.getType(), dataCenterId, podId);
s_alertsLogger.warn(" alertType:: " + alertType + " // dataCenterId:: " + dataCenterId + " // podId:: " + podId + " // clusterId:: " + null +
" // message:: " + subject + " // body:: " + body);
}
} catch (Exception ex) {
s_logger.error("Problem sending email alert", ex);
} }
if (alert == null) {
AlertVO newAlert = new AlertVO();
newAlert.setType(alertType.getType());
newAlert.setSubject(subject);
newAlert.setPodId(podId);
newAlert.setDataCenterId(dataCenterId);
newAlert.setSentCount(1);
newAlert.setLastSent(new Date());
newAlert.setName(alertType.getName());
_alertDao.persist(newAlert);
} else {
s_logger.debug(String.format("Have already sent: [%s] emails for alert type [%s] -- skipping send email.", alert.getSentCount(), alertType));
return;
}
SMTPMailProperties mailProps = new SMTPMailProperties();
mailProps.setSender(new MailAddress(senderAddress));
mailProps.setSubject(subject);
mailProps.setContent(content);
mailProps.setContentType("text/plain");
Set<MailAddress> addresses = new HashSet<>();
for (String recipient : recipients) {
addresses.add(new MailAddress(recipient));
}
mailProps.setRecipients(addresses);
mailSender.sendMail(mailProps);
} }
class EmailAlert { public void clearAlert(short alertType, long dataCenterId, Long podId) {
private Session _smtpSession; if (alertType != -1) {
private InternetAddress[] _recipientList; AlertVO alert = _alertDao.getLastAlert(alertType, dataCenterId, podId);
private final String _smtpHost; if (alert != null) {
private int _smtpPort = -1; AlertVO updatedAlert = _alertDao.createForUpdate();
private boolean _smtpUseAuth = false; updatedAlert.setResolved(new Date());
private final String _smtpUsername; _alertDao.update(alert.getId(), updatedAlert);
private final String _smtpPassword;
private final String _emailSender;
public EmailAlert(String[] recipientList, String smtpHost, int smtpPort, boolean smtpUseAuth, final String smtpUsername, final String smtpPassword,
String emailSender, boolean smtpDebug) {
if (recipientList != null) {
_recipientList = new InternetAddress[recipientList.length];
for (int i = 0; i < recipientList.length; i++) {
try {
_recipientList[i] = new InternetAddress(recipientList[i], recipientList[i]);
} catch (Exception ex) {
s_logger.error("Exception creating address for: " + recipientList[i], ex);
}
}
}
_smtpHost = smtpHost;
_smtpPort = smtpPort;
_smtpUseAuth = smtpUseAuth;
_smtpUsername = smtpUsername;
_smtpPassword = smtpPassword;
_emailSender = emailSender;
if (_smtpHost != null) {
Properties smtpProps = new Properties();
smtpProps.put("mail.smtp.host", smtpHost);
smtpProps.put("mail.smtp.port", smtpPort);
smtpProps.put("mail.smtp.auth", "" + smtpUseAuth);
if (smtpUsername != null) {
smtpProps.put("mail.smtp.user", smtpUsername);
}
smtpProps.put("mail.smtps.host", smtpHost);
smtpProps.put("mail.smtps.port", smtpPort);
smtpProps.put("mail.smtps.auth", "" + smtpUseAuth);
if (smtpUsername != null) {
smtpProps.put("mail.smtps.user", smtpUsername);
}
if ((smtpUsername != null) && (smtpPassword != null)) {
_smtpSession = Session.getInstance(smtpProps, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(smtpUsername, smtpPassword);
}
});
} else {
_smtpSession = Session.getInstance(smtpProps);
}
_smtpSession.setDebug(smtpDebug);
} else {
_smtpSession = null;
}
}
// TODO: make sure this handles SSL transport (useAuth is true) and regular
protected void sendAlert(AlertType alertType, long dataCenterId, Long podId, String subject, String content) throws MessagingException,
UnsupportedEncodingException {
s_alertsLogger.warn(" alertType:: " + alertType + " // dataCenterId:: " + dataCenterId + " // podId:: " +
podId + " // clusterId:: " + null + " // message:: " + subject);
AlertVO alert = null;
if ((alertType != AlertManager.AlertType.ALERT_TYPE_HOST) &&
(alertType != AlertManager.AlertType.ALERT_TYPE_USERVM) &&
(alertType != AlertManager.AlertType.ALERT_TYPE_DOMAIN_ROUTER) &&
(alertType != AlertManager.AlertType.ALERT_TYPE_CONSOLE_PROXY) &&
(alertType != AlertManager.AlertType.ALERT_TYPE_SSVM) &&
(alertType != AlertManager.AlertType.ALERT_TYPE_STORAGE_MISC) &&
(alertType != AlertManager.AlertType.ALERT_TYPE_MANAGMENT_NODE)) {
alert = _alertDao.getLastAlert(alertType.getType(), dataCenterId, podId);
}
if (alert == null) {
// set up a new alert
AlertVO newAlert = new AlertVO();
newAlert.setType(alertType.getType());
newAlert.setSubject(subject);
newAlert.setPodId(podId);
newAlert.setDataCenterId(dataCenterId);
newAlert.setSentCount(1); // initialize sent count to 1 since we are now sending an alert
newAlert.setLastSent(new Date());
newAlert.setName(alertType.getName());
_alertDao.persist(newAlert);
} else {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Have already sent: " + alert.getSentCount() + " emails for alert type '" + alertType + "' -- skipping send email");
}
return;
}
if (_smtpSession != null) {
SMTPMessage msg = new SMTPMessage(_smtpSession);
msg.setSender(new InternetAddress(_emailSender, _emailSender));
msg.setFrom(new InternetAddress(_emailSender, _emailSender));
for (InternetAddress address : _recipientList) {
msg.addRecipient(RecipientType.TO, address);
}
msg.setSubject(subject);
msg.setSentDate(new Date());
msg.setContent(content, "text/plain");
msg.saveChanges();
SMTPTransport smtpTrans = null;
if (_smtpUseAuth) {
smtpTrans = new SMTPSSLTransport(_smtpSession, new URLName("smtp", _smtpHost, _smtpPort, null, _smtpUsername, _smtpPassword));
} else {
smtpTrans = new SMTPTransport(_smtpSession, new URLName("smtp", _smtpHost, _smtpPort, null, _smtpUsername, _smtpPassword));
}
smtpTrans.connect();
smtpTrans.sendMessage(msg, msg.getAllRecipients());
smtpTrans.close();
}
}
public void clearAlert(short alertType, long dataCenterId, Long podId) {
if (alertType != -1) {
AlertVO alert = _alertDao.getLastAlert(alertType, dataCenterId, podId);
if (alert != null) {
AlertVO updatedAlert = _alertDao.createForUpdate();
updatedAlert.setResolved(new Date());
_alertDao.update(alert.getId(), updatedAlert);
}
} }
} }
} }
@ -254,7 +138,7 @@ public class UsageAlertManagerImpl extends ManagerBase implements AlertManager {
sendAlert(alertType, dataCenterId, podId, msg, msg); sendAlert(alertType, dataCenterId, podId, msg, msg);
return true; return true;
} catch (Exception ex) { } catch (Exception ex) {
s_logger.warn("Failed to generate an alert of type=" + alertType + "; msg=" + msg); s_logger.warn("Failed to generate an alert of type=" + alertType + "; msg=" + msg, ex);
return false; return false;
} }
} }

View File

@ -184,6 +184,15 @@
<artifactId>commons-compress</artifactId> <artifactId>commons-compress</artifactId>
<version>${cs.commons-compress.version}</version> <version>${cs.commons-compress.version}</version>
</dependency> </dependency>
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-email</artifactId>
<version>1.5</version>
</dependency>
<dependency> <dependency>
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId> <artifactId>commons-lang3</artifactId>

View File

@ -0,0 +1,63 @@
/*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cloudstack.utils.mailing;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
public class MailAddress {
private final String address;
private final String personal;
public MailAddress(String address) {
this.address = address;
this.personal = address;
}
public MailAddress(String address, String personal) {
this.address = address;
this.personal = personal;
}
public String getAddress() {
return address;
}
public String getPersonal() {
return personal;
}
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
@Override
public boolean equals(Object obj) {
String[] excludedFields = {"personal"};
return EqualsBuilder.reflectionEquals(this, obj, excludedFields);
}
@Override
public String toString() {
ToStringBuilder tsb = new ToStringBuilder(this, ToStringStyle.JSON_STYLE);
tsb.append("mailAddress", address);
return tsb.build();
}
}

View File

@ -0,0 +1,89 @@
/*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cloudstack.utils.mailing;
import java.util.Date;
import java.util.Set;
public class SMTPMailProperties {
private MailAddress sender;
private MailAddress from;
private Set<MailAddress> recipients;
private String subject;
private Date sentDate;
private Object content;
private String contentType;
public SMTPMailProperties() {
}
public MailAddress getSender() {
return sender;
}
public void setSender(MailAddress sender) {
this.sender = sender;
}
public MailAddress getFrom() {
return from;
}
public void setFrom(MailAddress from) {
this.from = from;
}
public Set<MailAddress> getRecipients() {
return recipients;
}
public void setRecipients(Set<MailAddress> recipients) {
this.recipients = recipients;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
public Date getSentDate() {
return sentDate;
}
public void setSentDate(Date sentDate) {
this.sentDate = sentDate;
}
public Object getContent() {
return content;
}
public void setContent(Object content) {
this.content = content;
}
public String getContentType() {
return contentType;
}
public void setContentType(String contentType) {
this.contentType = contentType;
}
}

View File

@ -0,0 +1,231 @@
/*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cloudstack.utils.mailing;
import com.sun.mail.smtp.SMTPMessage;
import com.sun.mail.smtp.SMTPSSLTransport;
import com.sun.mail.smtp.SMTPTransport;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.URLName;
import javax.mail.internet.InternetAddress;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.commons.mail.EmailConstants;
import org.apache.log4j.Logger;
public class SMTPMailSender {
private Logger logger = Logger.getLogger(SMTPMailSender.class);
protected Session session = null;
protected SMTPSessionProperties sessionProps;
protected static final String CONFIG_HOST = "host";
protected static final String CONFIG_PORT = "port";
protected static final String CONFIG_USE_AUTH = "useAuth";
protected static final String CONFIG_USERNAME = "username";
protected static final String CONFIG_PASSWORD = "password";
protected static final String CONFIG_DEBUG_MODE = "debug";
protected static final String CONFIG_USE_STARTTLS = "useStartTLS";
protected static final String CONFIG_ENABLED_SECURITY_PROTOCOLS = "enabledSecurityProtocols";
protected static final String CONFIG_TIMEOUT = "timeout";
protected static final String CONFIG_CONNECTION_TIMEOUT = "connectiontimeout";
protected Map<String, String> configs;
protected String namespace;
public SMTPMailSender(Map<String, String> configs, String namespace) {
if (namespace == null) {
logger.error("Unable to configure SMTP session due to null namespace.");
return;
}
this.configs = configs;
this.namespace = namespace;
this.sessionProps = configureSessionProperties();
if (StringUtils.isNotBlank(sessionProps.getHost())) {
Properties props = new Properties();
props.put(EmailConstants.MAIL_HOST, sessionProps.getHost());
props.put(EmailConstants.MAIL_PORT, sessionProps.getPort());
props.put(EmailConstants.MAIL_SMTP_AUTH, sessionProps.getUseAuth());
String username = sessionProps.getUsername();
if (username != null) {
props.put(EmailConstants.MAIL_SMTP_USER, username);
}
String protocols = sessionProps.getEnabledSecurityProtocols();
if (StringUtils.isNotBlank(protocols)) {
props.put("mail.smtp.ssl.protocols", protocols);
}
if (sessionProps.getUseAuth()) {
props.put(EmailConstants.MAIL_TRANSPORT_STARTTLS_ENABLE, sessionProps.getUseStartTLS());
}
if (sessionProps.getTimeout() != null) {
props.put(EmailConstants.MAIL_SMTP_TIMEOUT, sessionProps.getTimeout());
}
if (sessionProps.getConnectionTimeout() != null) {
props.put(EmailConstants.MAIL_SMTP_CONNECTIONTIMEOUT, sessionProps.getConnectionTimeout());
}
String password = sessionProps.getPassword();
if (StringUtils.isNotBlank(username) && StringUtils.isNotBlank(password)) {
session = Session.getInstance(props, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
} else {
session = Session.getInstance(props);
}
session.setDebug(sessionProps.getDebugMode());
} else {
logger.debug("Unable to instantiate SMTP mail session due to empty or null host.");
}
}
protected String getConfig(String config) {
return this.configs.get(String.format("%s.%s", namespace, config));
}
protected SMTPSessionProperties configureSessionProperties() {
String host = getConfig(CONFIG_HOST);
String port = getConfig(CONFIG_PORT);
String useAuth = getConfig(CONFIG_USE_AUTH);
String username = getConfig(CONFIG_USERNAME);
String password = getConfig(CONFIG_PASSWORD);
String debugMode = getConfig(CONFIG_DEBUG_MODE);
String useStartTLS = getConfig(CONFIG_USE_STARTTLS);
String enabledSecurityProtocols = getConfig(CONFIG_ENABLED_SECURITY_PROTOCOLS);
String timeout = getConfig(CONFIG_TIMEOUT);
String connectionTimeout = getConfig(CONFIG_CONNECTION_TIMEOUT);
SMTPSessionProperties sessionProps = new SMTPSessionProperties();
sessionProps.setHost(host);
sessionProps.setPort(NumberUtils.toInt(port, 25));
sessionProps.setUseAuth(BooleanUtils.toBoolean(useAuth));
sessionProps.setUsername(username);
sessionProps.setPassword(password);
sessionProps.setUseStartTLS(BooleanUtils.toBoolean(useStartTLS));
sessionProps.setEnabledSecurityProtocols(enabledSecurityProtocols);
sessionProps.setDebugMode(BooleanUtils.toBoolean(debugMode));
sessionProps.setTimeout(timeout == null ? null : NumberUtils.toInt(timeout));
sessionProps.setConnectionTimeout(connectionTimeout == null ? null : NumberUtils.toInt(connectionTimeout));
return sessionProps;
}
public boolean sendMail(SMTPMailProperties mailProps) {
if (session == null) {
logger.error("Unable to send mail due to null session.");
return false;
}
try {
SMTPMessage message = createMessage(mailProps);
SMTPTransport smtpTrans = createSmtpTransport();
smtpTrans.connect();
smtpTrans.sendMessage(message, message.getAllRecipients());
smtpTrans.close();
return true;
} catch (MessagingException | UnsupportedEncodingException ex) {
logger.error(String.format("Unable to send mail [%s] to the recipcients [%s].", mailProps.getSubject(), mailProps.getRecipients().toString()), ex);
}
return false;
}
protected SMTPMessage createMessage(SMTPMailProperties mailProps) throws MessagingException, UnsupportedEncodingException {
SMTPMessage message = new SMTPMessage(session);
MailAddress sender = mailProps.getSender();
MailAddress from = mailProps.getFrom();
if (from == null) {
from = sender;
}
message.setSender(new InternetAddress(sender.getAddress(), sender.getPersonal()));
message.setFrom(new InternetAddress(from.getAddress(), from.getPersonal()));
setMailRecipients(message, mailProps.getRecipients(), mailProps.getSubject());
message.setSubject(mailProps.getSubject());
message.setSentDate(mailProps.getSentDate() != null ? mailProps.getSentDate() : new Date());
message.setContent(mailProps.getContent(), mailProps.getContentType());
message.saveChanges();
return message;
}
protected SMTPTransport createSmtpTransport() {
URLName urlName = new URLName("smtp", sessionProps.getHost(), sessionProps.getPort(), null, sessionProps.getUsername(), sessionProps.getPassword());
if (sessionProps.getUseAuth() && !sessionProps.getUseStartTLS()) {
return new SMTPSSLTransport(session, urlName);
}
return new SMTPTransport(session, urlName);
}
protected boolean setMailRecipients(SMTPMessage message, Set<MailAddress> recipients, String subject) throws UnsupportedEncodingException, MessagingException {
for (MailAddress recipient : recipients) {
if (StringUtils.isNotBlank(recipient.getAddress())) {
try {
InternetAddress address = new InternetAddress(recipient.getAddress(), recipient.getPersonal());
message.addRecipient(Message.RecipientType.TO, address);
} catch (MessagingException ex) {
logger.error(String.format("Unable to create InternetAddres for address [%s].", recipient), ex);
}
}
}
if (recipients.isEmpty() || message.getAllRecipients().length == 0) {
logger.error("Unable to send mail due to empty list of recipients.");
logger.debug(String.format("Unable to send message [%s].", subject));
return false;
}
return true;
}
}

View File

@ -0,0 +1,113 @@
/*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cloudstack.utils.mailing;
public class SMTPSessionProperties {
private String host;
private int port;
private Boolean useAuth;
private String username;
private String password;
private Boolean useStartTLS;
private String enabledSecurityProtocols;
private Boolean debugMode;
private Integer timeout;
private Integer connectionTimeout;
public SMTPSessionProperties() {
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public Boolean getUseAuth() {
return useAuth;
}
public void setUseAuth(Boolean useAuth) {
this.useAuth = useAuth;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Boolean getUseStartTLS() {
return useStartTLS;
}
public void setUseStartTLS(Boolean useStartTLS) {
this.useStartTLS = useStartTLS;
}
public String getEnabledSecurityProtocols() {
return enabledSecurityProtocols;
}
public void setEnabledSecurityProtocols(String enabledSecurityProtocols) {
this.enabledSecurityProtocols = enabledSecurityProtocols;
}
public Boolean getDebugMode() {
return debugMode;
}
public void setDebugMode(Boolean debugMode) {
this.debugMode = debugMode;
}
public Integer getTimeout() {
return timeout;
}
public void setTimeout(Integer timeout) {
this.timeout = timeout;
}
public Integer getConnectionTimeout() {
return connectionTimeout;
}
public void setConnectionTimeout(Integer connectionTimeout) {
this.connectionTimeout = connectionTimeout;
}
}

View File

@ -0,0 +1,617 @@
/*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cloudstack.utils.mailing;
import com.sun.mail.smtp.SMTPMessage;
import com.sun.mail.smtp.SMTPTransport;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javax.mail.Address;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.internet.MimeMessage;
import junit.framework.TestCase;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.commons.mail.EmailConstants;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class SMTPMailSenderTest extends TestCase {
private SMTPMailSender smtpMailSender;
private Map<String, String> configsMock = Mockito.mock(Map.class);
private String namespace = "test";
private String enabledProtocols = "mail.smtp.ssl.protocols";
@Before
public void before() {
smtpMailSender = new SMTPMailSender(configsMock, namespace);
}
private String getConfigName(String config) {
return String.format("%s.%s", namespace, config);
}
@Test
public void validateSetSessionPropertiesUseStartTLSTrue() {
Map<String, String> configs = new HashMap<>();
configs.put(getConfigName(SMTPMailSender.CONFIG_USE_STARTTLS), "true");
smtpMailSender.configs = configs;
SMTPSessionProperties props = smtpMailSender.configureSessionProperties();
assertTrue(props.getUseStartTLS());
}
@Test
public void validateSetSessionPropertiesUseStartTLSFalse() {
Map<String, String> configs = new HashMap<>();
configs.put(getConfigName(SMTPMailSender.CONFIG_USE_STARTTLS), "false");
smtpMailSender.configs = configs;
SMTPSessionProperties props = smtpMailSender.configureSessionProperties();
assertFalse(props.getUseStartTLS());
}
@Test
public void validateSetSessionPropertiesUseStartTLSUndefinedUseDefaultFalse() {
SMTPMailSender smtpMailSender = new SMTPMailSender(configsMock, namespace);
SMTPSessionProperties props = smtpMailSender.configureSessionProperties();
assertFalse(props.getUseStartTLS());
}
@Test
public void validateSetSessionPropertiesUseAuthTrue() {
Map<String, String> configs = new HashMap<>();
configs.put(getConfigName(SMTPMailSender.CONFIG_USE_AUTH), "true");
smtpMailSender.configs = configs;
SMTPSessionProperties props = smtpMailSender.configureSessionProperties();
assertTrue(props.getUseAuth());
}
@Test
public void validateSetSessionPropertiesUseAuthFalse() {
Map<String, String> configs = new HashMap<>();
configs.put(getConfigName(SMTPMailSender.CONFIG_USE_AUTH), "false");
smtpMailSender.configs = configs;
SMTPSessionProperties props = smtpMailSender.configureSessionProperties();
assertFalse(props.getUseAuth());
}
@Test
public void validateSetSessionPropertiesUseAuthUndefinedUseDefaultFalse() {
SMTPMailSender smtpMailSender = new SMTPMailSender(configsMock, namespace);
SMTPSessionProperties props = smtpMailSender.configureSessionProperties();
assertFalse(props.getUseAuth());
}
@Test
public void validateSetSessionPropertiesDebugModeTrue() {
Map<String, String> configs = new HashMap<>();
configs.put(getConfigName(SMTPMailSender.CONFIG_DEBUG_MODE), "true");
smtpMailSender.configs = configs;
SMTPSessionProperties props = smtpMailSender.configureSessionProperties();
assertTrue(props.getDebugMode());
}
@Test
public void validateSetSessionPropertiesDebugModeFalse() {
Map<String, String> configs = new HashMap<>();
configs.put(getConfigName(SMTPMailSender.CONFIG_DEBUG_MODE), "false");
smtpMailSender.configs = configs;
SMTPSessionProperties props = smtpMailSender.configureSessionProperties();
assertFalse(props.getDebugMode());
}
@Test
public void validateSetSessionPropertiesDebugModeUndefinedUseDefaultFalse() {
SMTPMailSender smtpMailSender = new SMTPMailSender(configsMock, namespace);
SMTPSessionProperties props = smtpMailSender.configureSessionProperties();
assertFalse(props.getDebugMode());
}
@Test
public void validateSMTPMailSenderConstructorHostDefinedAsNullNoSessionCreated() {
SMTPMailSender smtpMailSender = new SMTPMailSender(new HashMap<>(), namespace);
assertNull(smtpMailSender.sessionProps.getHost());
assertNull(smtpMailSender.session);
}
@Test
public void validateSMTPMailSenderConstructorHostDefinedAsEmptyNoSessionCreated() {
Map<String, String> configs = new HashMap<>();
String host = "";
configs.put(getConfigName(SMTPMailSender.CONFIG_HOST), host);
SMTPMailSender smtpMailSender = new SMTPMailSender(configs, namespace);
assertEquals(host, smtpMailSender.sessionProps.getHost());
assertNull(smtpMailSender.session);
}
@Test
public void validateSMTPMailSenderConstructorHostDefinedAsBlankNoSessionCreated() {
Map<String, String> configs = new HashMap<>();
String host = " ";
configs.put(getConfigName(SMTPMailSender.CONFIG_HOST), host);
SMTPMailSender smtpMailSender = new SMTPMailSender(configs, namespace);
assertEquals(host, smtpMailSender.sessionProps.getHost());
assertNull(smtpMailSender.session);
}
@Test
public void validateSMTPMailSenderConstructorHostDefinedSessionCreated() {
Map<String, String> configs = new HashMap<>();
String host = "smtp.acme.org";
configs.put(getConfigName(SMTPMailSender.CONFIG_HOST), host);
SMTPMailSender smtpMailSender = new SMTPMailSender(configs, namespace);
assertEquals(host, smtpMailSender.sessionProps.getHost());
assertNotNull(smtpMailSender.session);
}
private Map<String, String> getConfigsWithHost() {
Map<String, String> configs = new HashMap<>();
String host = "smtp.acme.org";
configs.put(getConfigName(SMTPMailSender.CONFIG_HOST), host);
return configs;
}
@Test
public void validateSMTPMailSenderConstructorPortUndefinedUseDefault25() {
Map<String, String> configs = getConfigsWithHost();
SMTPMailSender smtpMailSender = new SMTPMailSender(configs, namespace);
assertEquals(25, smtpMailSender.sessionProps.getPort());
}
@Test
public void validateSMTPMailSenderConstructorPortDefined() {
Map<String, String> configs = getConfigsWithHost();
int port = 465;
configs.put(getConfigName(SMTPMailSender.CONFIG_PORT), String.valueOf(port));
SMTPMailSender smtpMailSender = new SMTPMailSender(configs, namespace);
assertEquals(port, smtpMailSender.sessionProps.getPort());
}
@Test
public void validateSMTPMailSenderConstructorWithTimeoutUndefined() {
Map<String, String> configs = getConfigsWithHost();
SMTPMailSender smtpMailSender = new SMTPMailSender(configs, namespace);
assertNull(smtpMailSender.sessionProps.getTimeout());
}
@Test
public void validateSMTPMailSenderConstructorWithTimeoutDefined() {
Map<String, String> configs = getConfigsWithHost();
Integer timeout = 12345;
configs.put(getConfigName(SMTPMailSender.CONFIG_TIMEOUT), String.valueOf(timeout));
SMTPMailSender smtpMailSender = new SMTPMailSender(configs, namespace);
assertEquals(timeout, smtpMailSender.sessionProps.getTimeout());
}
@Test
public void validateSMTPMailSenderConstructorWithConnectionTimeoutUndefined() {
Map<String, String> configs = getConfigsWithHost();
SMTPMailSender smtpMailSender = new SMTPMailSender(configs, namespace);
assertNull(smtpMailSender.sessionProps.getConnectionTimeout());
}
@Test
public void validateSMTPMailSenderConstructorWithConnectionTimeoutDefined() {
Map<String, String> configs = getConfigsWithHost();
Integer connectionTimeout = 12345;
configs.put(getConfigName(SMTPMailSender.CONFIG_CONNECTION_TIMEOUT), String.valueOf(connectionTimeout));
SMTPMailSender smtpMailSender = new SMTPMailSender(configs, namespace);
assertEquals(connectionTimeout, smtpMailSender.sessionProps.getConnectionTimeout());
}
@Test
public void validateSMTPMailSenderConstructorWithUsernameUndefined() {
Map<String, String> configs = getConfigsWithHost();
SMTPMailSender smtpMailSender = new SMTPMailSender(configs, namespace);
assertNull(smtpMailSender.sessionProps.getUsername());
}
@Test
public void validateSMTPMailSenderConstructorWithUsernameDefinedAsEmpty() {
Map<String, String> configs = getConfigsWithHost();
String username = "";
configs.put(getConfigName(SMTPMailSender.CONFIG_USERNAME), username);
SMTPMailSender smtpMailSender = new SMTPMailSender(configs, namespace);
assertNotNull(smtpMailSender.sessionProps.getUsername());
assertEquals(username, smtpMailSender.session.getProperties().get(EmailConstants.MAIL_SMTP_USER));
}
@Test
public void validateSMTPMailSenderConstructorWithUsernameDefinedAsBlank() {
Map<String, String> configs = getConfigsWithHost();
String username = " ";
configs.put(getConfigName(SMTPMailSender.CONFIG_USERNAME), username);
SMTPMailSender smtpMailSender = new SMTPMailSender(configs, namespace);
assertNotNull(smtpMailSender.sessionProps.getUsername());
assertEquals(username, smtpMailSender.session.getProperties().get(EmailConstants.MAIL_SMTP_USER));
}
@Test
public void validateSMTPMailSenderConstructorWithValidUsername() {
Map<String, String> configs = getConfigsWithHost();
String username = "test@test.com";
configs.put(getConfigName(SMTPMailSender.CONFIG_USERNAME), username);
SMTPMailSender smtpMailSender = new SMTPMailSender(configs, namespace);
assertNotNull(smtpMailSender.sessionProps.getUsername());
assertEquals(username, smtpMailSender.session.getProperties().get(EmailConstants.MAIL_SMTP_USER));
}
@Test
public void validateSMTPMailSenderConstructorWithProtocolsUndefined() {
Map<String, String> configs = getConfigsWithHost();
SMTPMailSender smtpMailSender = new SMTPMailSender(configs, namespace);
assertNull(smtpMailSender.sessionProps.getEnabledSecurityProtocols());
assertNull(smtpMailSender.session.getProperties().get(enabledProtocols));
}
@Test
public void validateSMTPMailSenderConstructorWithProtocolsDefinedAsEmpty() {
Map<String, String> configs = getConfigsWithHost();
String protocols = "";
configs.put(getConfigName(SMTPMailSender.CONFIG_ENABLED_SECURITY_PROTOCOLS), protocols);
SMTPMailSender smtpMailSender = new SMTPMailSender(configs, namespace);
assertEquals(protocols, smtpMailSender.sessionProps.getEnabledSecurityProtocols());
assertNull(smtpMailSender.session.getProperties().get(enabledProtocols));
}
@Test
public void validateSMTPMailSenderConstructorWithProtocolsDefinedAsBlank() {
Map<String, String> configs = getConfigsWithHost();
String protocols = " ";
configs.put(getConfigName(SMTPMailSender.CONFIG_ENABLED_SECURITY_PROTOCOLS), protocols);
SMTPMailSender smtpMailSender = new SMTPMailSender(configs, namespace);
assertEquals(protocols, smtpMailSender.sessionProps.getEnabledSecurityProtocols());
assertNull(smtpMailSender.session.getProperties().get(enabledProtocols));
}
@Test
public void validateSMTPMailSenderConstructorWithValidProtocol() {
Map<String, String> configs = getConfigsWithHost();
String protocols = "TLSv1";
configs.put(getConfigName(SMTPMailSender.CONFIG_ENABLED_SECURITY_PROTOCOLS), protocols);
SMTPMailSender smtpMailSender = new SMTPMailSender(configs, namespace);
assertEquals(protocols, smtpMailSender.sessionProps.getEnabledSecurityProtocols());
assertEquals(protocols, smtpMailSender.session.getProperties().get(enabledProtocols));
}
@Test
public void validateSMTPMailSenderConstructorWithMultipleValidsProtocols() {
Map<String, String> configs = getConfigsWithHost();
String protocols = "TLSv1 TLSv1.2";
configs.put(getConfigName(SMTPMailSender.CONFIG_ENABLED_SECURITY_PROTOCOLS), protocols);
SMTPMailSender smtpMailSender = new SMTPMailSender(configs, namespace);
assertEquals(protocols, smtpMailSender.sessionProps.getEnabledSecurityProtocols());
assertEquals(protocols, smtpMailSender.session.getProperties().get(enabledProtocols));
}
@Test
public void validateSMTPMailSenderConstructorUseAuthFalseUseStartTLSFalseStartTLSEnabledMustBeNull() {
Map<String, String> configs = getConfigsWithHost();
Boolean useAuth = false;
Boolean useStartTLS = false;
configs.put(getConfigName(SMTPMailSender.CONFIG_USE_AUTH), String.valueOf(useAuth));
configs.put(getConfigName(SMTPMailSender.CONFIG_USE_STARTTLS), String.valueOf(useStartTLS));
SMTPMailSender smtpMailSender = new SMTPMailSender(configs, namespace);
assertNull(smtpMailSender.session.getProperties().get(EmailConstants.MAIL_TRANSPORT_STARTTLS_ENABLE));
}
@Test
public void validateSMTPMailSenderConstructorUseAuthFalseUseStartTLSTrueStartTLSEnabledMustBeNull() {
Map<String, String> configs = getConfigsWithHost();
Boolean useAuth = false;
Boolean useStartTLS = true;
configs.put(getConfigName(SMTPMailSender.CONFIG_USE_AUTH), String.valueOf(useAuth));
configs.put(getConfigName(SMTPMailSender.CONFIG_USE_STARTTLS), String.valueOf(useStartTLS));
SMTPMailSender smtpMailSender = new SMTPMailSender(configs, namespace);
assertNull(smtpMailSender.session.getProperties().get(EmailConstants.MAIL_TRANSPORT_STARTTLS_ENABLE));
}
@Test
public void validateSMTPMailSenderConstructorUseAuthTrueUseStartTLSFalseStartTLSEnabledMustBeFalse() {
Map<String, String> configs = getConfigsWithHost();
Boolean useAuth = true;
Boolean useStartTLS = false;
configs.put(getConfigName(SMTPMailSender.CONFIG_USE_AUTH), String.valueOf(useAuth));
configs.put(getConfigName(SMTPMailSender.CONFIG_USE_STARTTLS), String.valueOf(useStartTLS));
SMTPMailSender smtpMailSender = new SMTPMailSender(configs, namespace);
assertFalse((boolean)smtpMailSender.session.getProperties().get(EmailConstants.MAIL_TRANSPORT_STARTTLS_ENABLE));
}
@Test
public void validateSMTPMailSenderConstructorUseAuthTrueUseStartTLSTrueStartTLSEnabledMustBeFalse() {
Map<String, String> configs = getConfigsWithHost();
Boolean useAuth = true;
Boolean useStartTLS = true;
configs.put(getConfigName(SMTPMailSender.CONFIG_USE_AUTH), String.valueOf(useAuth));
configs.put(getConfigName(SMTPMailSender.CONFIG_USE_STARTTLS), String.valueOf(useStartTLS));
SMTPMailSender smtpMailSender = new SMTPMailSender(configs, namespace);
assertTrue((boolean)smtpMailSender.session.getProperties().get(EmailConstants.MAIL_TRANSPORT_STARTTLS_ENABLE));
}
@Test
public void validateSMTPMailSenderCreateMessageFromDefinedAsNull() throws MessagingException, UnsupportedEncodingException {
smtpMailSender = smtpMailSender = Mockito.spy(smtpMailSender);
SMTPMailProperties mailProps = new SMTPMailProperties();
mailProps.setSender(new MailAddress("test@test.com"));
mailProps.setContent("A simple test");
mailProps.setContentType("text/plain");
Mockito.doReturn(true).when(smtpMailSender).setMailRecipients(Mockito.any(SMTPMessage.class), Mockito.any(), Mockito.any());
SMTPMessage message = smtpMailSender.createMessage(mailProps);
assertEquals("\"test@test.com\" <test@test.com>", message.getFrom()[0].toString());
}
@Test
public void validateSMTPMailSenderCreateMessageFromDefined() throws MessagingException, UnsupportedEncodingException {
smtpMailSender = smtpMailSender = Mockito.spy(smtpMailSender);
SMTPMailProperties mailProps = new SMTPMailProperties();
mailProps.setSender(new MailAddress("test@test.com"));
mailProps.setFrom(new MailAddress("test2@test2.com", "TEST2"));
mailProps.setContent("A simple test");
mailProps.setContentType("text/plain");
Mockito.doReturn(true).when(smtpMailSender).setMailRecipients(Mockito.any(SMTPMessage.class), Mockito.any(), Mockito.any());
SMTPMessage message = smtpMailSender.createMessage(mailProps);
assertEquals("TEST2 <test2@test2.com>", message.getFrom()[0].toString());
}
@Test
public void validateSMTPMailSenderCreateMessageSentDateDefined() throws MessagingException, UnsupportedEncodingException {
smtpMailSender = smtpMailSender = Mockito.spy(smtpMailSender);
SMTPMailProperties mailProps = new SMTPMailProperties();
mailProps.setSender(new MailAddress("test@test.com"));
mailProps.setContent("A simple test");
mailProps.setContentType("text/plain");
Calendar cal = Calendar.getInstance();
cal.setTime(new Date());
cal.set(Calendar.YEAR, 2015);
cal.set(Calendar.MONTH, 1);
cal.set(Calendar.DAY_OF_MONTH, 1);
mailProps.setSentDate(cal.getTime());
Mockito.doReturn(true).when(smtpMailSender).setMailRecipients(Mockito.any(SMTPMessage.class), Mockito.any(), Mockito.any());
SMTPMessage message = smtpMailSender.createMessage(mailProps);
assertTrue(DateUtils.truncatedEquals(cal.getTime(), message.getSentDate(), Calendar.SECOND));
}
@Test
public void validateSMTPMailSenderCreateMessageSubjectContentAndContentTypeDefined() throws MessagingException, UnsupportedEncodingException, IOException {
smtpMailSender = smtpMailSender = Mockito.spy(smtpMailSender);
SMTPMailProperties mailProps = new SMTPMailProperties();
String subject = "A TEST";
String content = "A simple test";
String contentType = "text/plain;charset=utf8";
mailProps.setSender(new MailAddress("test@test.com"));
mailProps.setSubject(subject);
mailProps.setContent(content);
mailProps.setContentType(contentType);
Mockito.doReturn(true).when(smtpMailSender).setMailRecipients(Mockito.any(SMTPMessage.class), Mockito.any(), Mockito.any());
SMTPMessage message = smtpMailSender.createMessage(mailProps);
assertEquals(subject, message.getSubject());
assertEquals(content, message.getContent());
assertEquals(contentType, message.getContentType());
}
@Test
public void setMailRecipientsTest() throws UnsupportedEncodingException, MessagingException {
SMTPMessage messageMock = new SMTPMessage(Mockito.mock(MimeMessage.class));
Set<MailAddress> recipients = new HashSet<>();
recipients.add(new MailAddress(null));
recipients.add(new MailAddress(""));
recipients.add(new MailAddress(" "));
recipients.add(new MailAddress("smtp.acme.org"));
recipients.add(new MailAddress("smtp.acme2.org", "Coyote"));
boolean returnOfSetEmail = smtpMailSender.setMailRecipients(messageMock, recipients, "A simple test");
Address[] allRecipients = messageMock.getAllRecipients();
int expectedNumberOfValidRecipientsConfigured = 2;
assertEquals(expectedNumberOfValidRecipientsConfigured, allRecipients.length);
assertEquals("\"smtp.acme.org\" <smtp.acme.org>", allRecipients[0].toString());
assertEquals("Coyote <smtp.acme2.org>", allRecipients[1].toString());
assertTrue(returnOfSetEmail);
}
@Test
public void setMailRecipientsTestOnlyInvalidEmailSettings() throws UnsupportedEncodingException, MessagingException {
SMTPMessage messageMock = new SMTPMessage(Mockito.mock(MimeMessage.class));
messageMock = messageMock = Mockito.spy(messageMock);
Mockito.doReturn(new Address[0]).when(messageMock).getAllRecipients();
Set<MailAddress> recipients = new HashSet<>();
recipients.add(new MailAddress(null));
recipients.add(new MailAddress(""));
recipients.add(new MailAddress(" "));
boolean returnOfSetEmail = smtpMailSender.setMailRecipients(messageMock, recipients, "A simple test");
Address[] allRecipients = messageMock.getAllRecipients();
int expectedNumberOfValidRecipientsConfigured = 0;
assertEquals(expectedNumberOfValidRecipientsConfigured, allRecipients.length);
assertFalse(returnOfSetEmail);
}
@Test
public void validateSMTPMailSenderSendMailWithNullSession() {
SMTPMailProperties mailProps = new SMTPMailProperties();
boolean returnOfSendMail = smtpMailSender.sendMail(mailProps);
assertFalse(returnOfSendMail);
}
@Test
public void validateSMTPMailSenderSendMailWithValidSession() throws MessagingException, UnsupportedEncodingException {
smtpMailSender = smtpMailSender = Mockito.spy(smtpMailSender);
SMTPMailProperties mailProps = new SMTPMailProperties();
smtpMailSender.session = Session.getDefaultInstance(Mockito.mock(Properties.class));
Mockito.doReturn(Mockito.mock(SMTPMessage.class)).when(smtpMailSender).createMessage(Mockito.any(SMTPMailProperties.class));
Mockito.doReturn(Mockito.mock(SMTPTransport.class)).when(smtpMailSender).createSmtpTransport();
boolean returnOfSendMail = smtpMailSender.sendMail(mailProps);
assertTrue(returnOfSendMail);
}
@Test
public void validateSMTPMailSenderGetConfigPropertyUndefinedMustReturnNull() {
smtpMailSender = smtpMailSender = Mockito.spy(smtpMailSender);
String returnOfPropertyThatDoesNotExist = smtpMailSender.getConfig("test");
assertNull(returnOfPropertyThatDoesNotExist);
}
public void validateSMTPMailSenderGetConfigPropertyDefinedMustReturnIt() {
smtpMailSender = smtpMailSender = Mockito.spy(smtpMailSender);
Map<String, String> configs = new HashMap<>();
String host = "smtp.acme.org";
configs.put(getConfigName(SMTPMailSender.CONFIG_HOST), host);
smtpMailSender.configs = configs;
String returnOfPropertyThatExist = smtpMailSender.getConfig(getConfigName(SMTPMailSender.CONFIG_HOST));
assertNotNull(returnOfPropertyThatExist);
assertNotNull(host, returnOfPropertyThatExist);
}
}