/*
* Computoser is a music-composition algorithm and a website to present the results
* Copyright (C) 2012-2014 Bozhidar Bozhanov
*
* Computoser is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* Computoser is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Computoser. If not, see <http://www.gnu.org/licenses/>.
*/
package com.music.service;
import java.io.Serializable;
import java.io.StringWriter;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.inject.Inject;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.mail.Email;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.HtmlEmail;
import org.apache.commons.mail.SimpleEmail;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.context.Context;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.music.model.persistent.User;
@Service
public class EmailService {
/**
* Sends an email. No exception is thrown even if the message sending fails.
* (This service may decide to reattempt sending after a while)
* @param details
*/
private static final Logger logger = LoggerFactory.getLogger(EmailService.class);
@Value("${smtp.host}")
private String smtpHost;
@Value("${smtp.user}")
private String smtpUser;
@Value("${smtp.password}")
private String smtpPassword;
@Value("${smtp.bounce.email}")
private String smtpBounceEmail;
@Value("${base.url}")
private String baseUrl;
@Inject
private VelocityEngine engine;
private Email createEmail(boolean html) {
Email e = null;
if (html) {
e = new HtmlEmail();
} else {
e = new SimpleEmail();
}
e.setHostName(smtpHost);
if (!StringUtils.isEmpty(smtpUser)) {
e.setAuthentication(smtpUser, smtpPassword);
}
if (!StringUtils.isEmpty(smtpBounceEmail)) {
e.setBounceAddress(smtpBounceEmail);
}
e.setTLS(true);
e.setSmtpPort(587); //tls port
e.setCharset("UTF8");
//e.setDebug(true);
return e;
}
@Async
public void send(EmailDetails details) {
if (details.getSubject() == null || !BooleanUtils.xor(ArrayUtils.toArray(details.getMessage() != null, details.getMessageTemplate() != null))) {
throw new IllegalStateException("Either subject or subjectKey / either template/message/messageKey should be specified");
}
Validate.notBlank(details.getFrom());
Email email = createEmail(details.isHtml());
String subject = constructSubject(details);
email.setSubject(subject);
String emailMessage = constructEmailMessages(details);
try {
if (details.isHtml()) {
((HtmlEmail) email).setHtmlMsg(emailMessage);
} else {
email.setMsg(emailMessage);
}
for (String to : details.getTo()) {
email.addTo(to);
}
email.setFrom(details.getFrom());
email.send();
} catch (EmailException ex) {
logger.error("Exception occurred when sending email to " + details.getTo(), ex);
}
}
private String constructSubject(EmailDetails details) {
String subject = "";
if (details.getSubject() != null) {
subject = details.getSubject();
}
return subject;
}
private String constructEmailMessages(EmailDetails details) {
String emailMessage = null;
if (details.getMessageTemplate() != null) {
Template template = engine.getTemplate(details.getMessageTemplate(), "UTF-8");
StringWriter writer = new StringWriter();
Context ctx = new VelocityContext();
for (Map.Entry<String, Object> entry : details.getMessageTemplateModel().entrySet()) {
ctx.put(entry.getKey(), entry.getValue());
}
ctx.put("baseUrl", baseUrl);
ctx.put("currentUser", details.getCurrentUser());
template.merge(ctx, writer);
return writer.toString();
}
if (details.getMessage() != null) {
emailMessage = details.getMessage();
}
return emailMessage;
}
public static class EmailDetails implements Serializable {
private static final long serialVersionUID = 4456840962920327178L;
private String from;
private List<String> to = Lists.newArrayList();
private String messageTemplate;
private Map<String, Object> messageTemplateModel = Maps.newHashMap();
private String subject;
private String message;
private String[] subjectParams = new String[0];
private String[] messageParams = new String[0];
private String[] extraMessageParams = new String[0];
private boolean html;
private Locale locale = Locale.ENGLISH;
private User currentUser;
public String[] getSubjectParams() {
return subjectParams;
}
public EmailDetails setSubjectParams(String[] sibjectParams) {
this.subjectParams = sibjectParams;
return this;
}
public String[] getMessageParams() {
return messageParams;
}
public EmailDetails setMessageParams(String[] messageParams) {
this.messageParams = messageParams;
return this;
}
public boolean isHtml() {
return html;
}
public EmailDetails setHtml(boolean html) {
this.html = html;
return this;
}
public Locale getLocale() {
return locale;
}
public EmailDetails setLocale(Locale locale) {
this.locale = locale;
return this;
}
public String getFrom() {
return from;
}
public EmailDetails setFrom(String from) {
this.from = from;
return this;
}
public List<String> getTo() {
return to;
}
public EmailDetails setTo(String to) {
this.to.clear();
return addTo(to);
}
public EmailDetails addTo(String to) {
this.to.add(to);
return this;
}
public String[] getExtraMessageParams() {
return extraMessageParams;
}
public EmailDetails setExtraMessageParams(String[] extraMessageParams) {
this.extraMessageParams = extraMessageParams;
return this;
}
public String getSubject() {
return subject;
}
public EmailDetails setSubject(String subject) {
this.subject = subject;
return this;
}
public String getMessage() {
return message;
}
public EmailDetails setMessage(String message) {
this.message = message;
return this;
}
public String getMessageTemplate() {
return messageTemplate;
}
public EmailDetails setMessageTemplate(String messageTemplate) {
this.messageTemplate = messageTemplate;
return this;
}
public Map<String, Object> getMessageTemplateModel() {
return messageTemplateModel;
}
public EmailDetails setMessageTemplateModel(Map<String, Object> messageTemplateModel) {
this.messageTemplateModel = messageTemplateModel;
return this;
}
public EmailDetails addToMessageTemplateModel(String key, Object value) {
messageTemplateModel.put(key, value);
return this;
}
public User getCurrentUser() {
return currentUser;
}
public EmailDetails setCurrentUser(User currentUser) {
this.currentUser = currentUser;
return this;
}
}
}