/*
* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you 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.wso2.carbon.event.output.adapter.email;
import org.apache.axis2.transport.mail.MailConstants;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.event.output.adapter.core.EventAdapterUtil;
import org.wso2.carbon.event.output.adapter.core.OutputEventAdapter;
import org.wso2.carbon.event.output.adapter.core.OutputEventAdapterConfiguration;
import org.wso2.carbon.event.output.adapter.core.exception.ConnectionUnavailableException;
import org.wso2.carbon.event.output.adapter.core.exception.OutputEventAdapterException;
import org.wso2.carbon.event.output.adapter.core.exception.TestConnectionNotSupportedException;
import org.wso2.carbon.event.output.adapter.email.internal.util.EmailEventAdapterConstants;
import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.util.Date;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* The Email event adapter sends mail using an SMTP server configuration defined
* in output-event-adapters.xml email adapter sender definition.
*/
public class EmailEventAdapter implements OutputEventAdapter {
private static final Log log = LogFactory.getLog(EmailEventAdapter.class);
private static ThreadPoolExecutor threadPoolExecutor;
private static Session session;
private OutputEventAdapterConfiguration eventAdapterConfiguration;
private Map<String, String> globalProperties;
private int tenantId;
/**
* Default from address for outgoing messages.
*/
private InternetAddress smtpFromAddress = null;
public EmailEventAdapter(OutputEventAdapterConfiguration eventAdapterConfiguration,
Map<String, String> globalProperties) {
this.eventAdapterConfiguration = eventAdapterConfiguration;
this.globalProperties = globalProperties;
}
/**
* Initialize the thread pool to send emails.
*
* @throws OutputEventAdapterException on error.
*/
@Override
public void init() throws OutputEventAdapterException {
tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId();
//ThreadPoolExecutor will be assigned if it is null.
if (threadPoolExecutor == null) {
int minThread;
int maxThread;
long defaultKeepAliveTime;
int jobQueSize;
//If global properties are available those will be assigned else constant values will be assigned
if (globalProperties.get(EmailEventAdapterConstants.MIN_THREAD_NAME) != null) {
minThread = Integer.parseInt(globalProperties.get(EmailEventAdapterConstants.MIN_THREAD_NAME));
} else {
minThread = EmailEventAdapterConstants.MIN_THREAD;
}
if (globalProperties.get(EmailEventAdapterConstants.MAX_THREAD_NAME) != null) {
maxThread = Integer.parseInt(globalProperties.get(EmailEventAdapterConstants.MAX_THREAD_NAME));
} else {
maxThread = EmailEventAdapterConstants.MAX_THREAD;
}
if (globalProperties.get(EmailEventAdapterConstants.ADAPTER_KEEP_ALIVE_TIME_NAME) != null) {
defaultKeepAliveTime = Integer.parseInt(globalProperties.get(
EmailEventAdapterConstants.ADAPTER_KEEP_ALIVE_TIME_NAME));
} else {
defaultKeepAliveTime = EmailEventAdapterConstants.DEFAULT_KEEP_ALIVE_TIME_IN_MILLS;
}
if (globalProperties.get(EmailEventAdapterConstants.ADAPTER_EXECUTOR_JOB_QUEUE_SIZE_NAME) != null) {
jobQueSize = Integer.parseInt(globalProperties.get(
EmailEventAdapterConstants.ADAPTER_EXECUTOR_JOB_QUEUE_SIZE_NAME));
} else {
jobQueSize = EmailEventAdapterConstants.ADAPTER_EXECUTOR_JOB_QUEUE_SIZE;
}
threadPoolExecutor = new ThreadPoolExecutor(minThread, maxThread, defaultKeepAliveTime,
TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(jobQueSize));
}
}
@Override
public void testConnect() throws TestConnectionNotSupportedException {
throw new TestConnectionNotSupportedException("Test connection is not available");
}
/**
* Initialize the Email SMTP session and be ready to send emails.
*
* @throws ConnectionUnavailableException on error.
*/
@Override
public void connect() throws ConnectionUnavailableException {
if (session == null) {
/**
* Default SMTP properties for outgoing messages.
*/
String smtpFrom;
String smtpHost;
String smtpPort;
/**
* Default from username and password for outgoing messages.
*/
final String smtpUsername;
final String smtpPassword;
// initialize SMTP session.
Properties props = new Properties();
props.putAll(globalProperties);
//Verifying default SMTP properties of the SMTP server.
smtpFrom = props.getProperty(MailConstants.MAIL_SMTP_FROM);
smtpHost = props.getProperty(EmailEventAdapterConstants.MAIL_SMTP_HOST);
smtpPort = props.getProperty(EmailEventAdapterConstants.MAIL_SMTP_PORT);
if (smtpFrom == null) {
String msg = "failed to connect to the mail server due to null smtpFrom value";
throw new ConnectionUnavailableException("The adapter " +
eventAdapterConfiguration.getName() + " " + msg);
}
if (smtpHost == null) {
String msg = "failed to connect to the mail server due to null smtpHost value";
throw new ConnectionUnavailableException
("The adapter " + eventAdapterConfiguration.getName() + " " + msg);
}
if (smtpPort == null) {
String msg = "failed to connect to the mail server due to null smtpPort value";
throw new ConnectionUnavailableException
("The adapter " + eventAdapterConfiguration.getName() + " " + msg);
}
try {
smtpFromAddress = new InternetAddress(smtpFrom);
} catch (AddressException e) {
log.error("Error in retrieving smtp address : " +
smtpFrom, e);
String msg = "failed to connect to the mail server due to error in retrieving " +
"smtp from address";
throw new ConnectionUnavailableException
("The adapter " + eventAdapterConfiguration.getName() + " " + msg, e);
}
//Retrieving username and password of SMTP server.
smtpUsername = props.getProperty(MailConstants.MAIL_SMTP_USERNAME);
smtpPassword = props.getProperty(MailConstants.MAIL_SMTP_PASSWORD);
//initializing SMTP server to create session object.
if (smtpUsername != null && smtpPassword != null && !smtpUsername.isEmpty() && !smtpPassword.isEmpty()) {
session = Session.getInstance(props, new Authenticator() {
public PasswordAuthentication
getPasswordAuthentication() {
return new PasswordAuthentication(smtpUsername, smtpPassword);
}
});
} else {
session = Session.getInstance(props);
log.info("Connecting adapter " + eventAdapterConfiguration.getName() + "without user authentication for tenant " + tenantId);
}
}
}
/**
* This will be invoked upon a successful trigger of
* a data stream.
*
* @param message the event stream data.
* @param dynamicProperties the dynamic attributes of the email.
*/
@Override
public void publish(Object message, Map<String, String> dynamicProperties) {
//Get subject and emailIds from dynamic properties
String subject = dynamicProperties.get(EmailEventAdapterConstants.ADAPTER_MESSAGE_EMAIL_SUBJECT);
String[] emailIds = dynamicProperties.get(EmailEventAdapterConstants.ADAPTER_MESSAGE_EMAIL_ADDRESS)
.replaceAll(" ", "").split(EmailEventAdapterConstants.EMAIL_SEPARATOR);
String emailType = dynamicProperties.get(EmailEventAdapterConstants.APAPTER_MESSAGE_EMAIL_TYPE);
//Send email for each emailId
for (String email : emailIds) {
try {
threadPoolExecutor.submit(new EmailSender(email, subject, message.toString(), emailType));
} catch (RejectedExecutionException e) {
EventAdapterUtil.logAndDrop(eventAdapterConfiguration.getName(), message, "Job queue is full", e, log, tenantId);
}
}
}
@Override
public void disconnect() {
//not required
}
@Override
public void destroy() {
//not required
}
@Override
public boolean isPolled() {
return false;
}
class EmailSender implements Runnable {
String to;
String subject;
String body;
String type;
EmailSender(String to, String subject, String body, String type) {
this.to = to;
this.subject = subject;
this.body = body;
this.type = type;
}
/**
* Sending emails to the corresponding Email IDs'.
*/
@Override
public void run() {
if (log.isDebugEnabled()) {
log.debug("Format of the email:" + " " + to + "->" + type);
}
//Creating MIME object using initiated session.
MimeMessage message = new MimeMessage(session);
//Setting up the Email attributes and Email payload.
try {
message.setFrom(smtpFromAddress);
message.addRecipient(Message.RecipientType.TO,
new InternetAddress(to));
message.setSubject(subject);
message.setSentDate(new Date());
message.setContent(body, type);
if (log.isDebugEnabled()) {
log.debug("Meta data of the email configured successfully");
}
Transport.send(message);
if (log.isDebugEnabled()) {
log.debug("Mail sent to the EmailID" + " " + to + " " + "Successfully");
}
} catch (MessagingException e) {
EventAdapterUtil.logAndDrop(eventAdapterConfiguration.getName(), message, "Error in message format", e, log, tenantId);
} catch (Exception e) {
EventAdapterUtil.logAndDrop(eventAdapterConfiguration.getName(), message, "Error sending email to '" + to + "'", e, log, tenantId);
}
}
}
}