Author: virag
Date: Fri May 31 00:35:39 2013
New Revision: 1488068

URL: http://svn.apache.org/r1488068
Log:
OOZIE-1294 SLA Email Notification (ryota via virag)

Added:
    
oozie/trunk/core/src/main/java/org/apache/oozie/sla/listener/SLAEmailEventListener.java
    
oozie/trunk/core/src/test/java/org/apache/oozie/sla/TestSLAEmailEventListener.java
Modified:
    
oozie/trunk/core/src/main/java/org/apache/oozie/action/email/EmailActionExecutor.java
    oozie/trunk/core/src/main/java/org/apache/oozie/sla/service/SLAService.java
    oozie/trunk/core/src/main/resources/oozie-default.xml
    oozie/trunk/release-log.txt

Modified: 
oozie/trunk/core/src/main/java/org/apache/oozie/action/email/EmailActionExecutor.java
URL: 
http://svn.apache.org/viewvc/oozie/trunk/core/src/main/java/org/apache/oozie/action/email/EmailActionExecutor.java?rev=1488068&r1=1488067&r2=1488068&view=diff
==============================================================================
--- 
oozie/trunk/core/src/main/java/org/apache/oozie/action/email/EmailActionExecutor.java
 (original)
+++ 
oozie/trunk/core/src/main/java/org/apache/oozie/action/email/EmailActionExecutor.java
 Fri May 31 00:35:39 2013
@@ -6,9 +6,9 @@
  * 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.
@@ -47,6 +47,14 @@ import org.jdom.Namespace;
  */
 public class EmailActionExecutor extends ActionExecutor {
 
+    public static final String CONF_PREFIX = "oozie.email.";
+    public static final String EMAIL_SMTP_HOST = CONF_PREFIX + "smtp.host";
+    public static final String EMAIL_SMTP_PORT = CONF_PREFIX + "smtp.port";
+    public static final String EMAIL_SMTP_AUTH = CONF_PREFIX + "smtp.auth";
+    public static final String EMAIL_SMTP_USER = CONF_PREFIX + "smtp.username";
+    public static final String EMAIL_SMTP_PASS = CONF_PREFIX + "smtp.password";
+    public static final String EMAIL_SMTP_FROM = CONF_PREFIX + "from.address";
+
     private final static String TO = "to";
     private final static String CC = "cc";
     private final static String SUB = "subject";
@@ -112,14 +120,12 @@ public class EmailActionExecutor extends
 
     protected void email(Context context, String[] to, String[] cc, String 
subject, String body) throws ActionExecutorException {
         // Get mailing server details.
-        String smtpHost = getOozieConf().get("oozie.email.smtp.host",
-            "localhost");
-        String smtpPort = getOozieConf().get("oozie.email.smtp.port", "25");
-        Boolean smtpAuth = getOozieConf().getBoolean("oozie.email.smtp.auth", 
false);
-        String smtpUser = getOozieConf().get("oozie.email.smtp.username", "");
-        String smtpPassword = getOozieConf().get("oozie.email.smtp.password", 
"");
-        String fromAddr = getOozieConf().get("oozie.email.from.address",
-            "oozie@localhost");
+        String smtpHost = getOozieConf().get(EMAIL_SMTP_HOST, "localhost");
+        String smtpPort = getOozieConf().get(EMAIL_SMTP_PORT, "25");
+        Boolean smtpAuth = getOozieConf().getBoolean(EMAIL_SMTP_AUTH, false);
+        String smtpUser = getOozieConf().get(EMAIL_SMTP_USER, "");
+        String smtpPassword = getOozieConf().get(EMAIL_SMTP_PASS, "");
+        String fromAddr = getOozieConf().get(EMAIL_SMTP_FROM, 
"oozie@localhost");
 
         Properties properties = new Properties();
         properties.setProperty("mail.smtp.host", smtpHost);
@@ -207,7 +213,7 @@ public class EmailActionExecutor extends
         return true;
     }
 
-    private static class JavaMailAuthenticator extends Authenticator {
+    public static class JavaMailAuthenticator extends Authenticator {
 
         String user;
         String password;

Added: 
oozie/trunk/core/src/main/java/org/apache/oozie/sla/listener/SLAEmailEventListener.java
URL: 
http://svn.apache.org/viewvc/oozie/trunk/core/src/main/java/org/apache/oozie/sla/listener/SLAEmailEventListener.java?rev=1488068&view=auto
==============================================================================
--- 
oozie/trunk/core/src/main/java/org/apache/oozie/sla/listener/SLAEmailEventListener.java
 (added)
+++ 
oozie/trunk/core/src/main/java/org/apache/oozie/sla/listener/SLAEmailEventListener.java
 Fri May 31 00:35:39 2013
@@ -0,0 +1,403 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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.apache.oozie.sla.listener;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.mail.Address;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.NoSuchProviderException;
+import javax.mail.SendFailedException;
+import javax.mail.Session;
+import javax.mail.Transport;
+import javax.mail.internet.AddressException;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimeMessage.RecipientType;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.oozie.action.email.EmailActionExecutor;
+import org.apache.oozie.action.email.EmailActionExecutor.JavaMailAuthenticator;
+import org.apache.oozie.client.event.SLAEvent;
+import org.apache.oozie.sla.listener.SLAEventListener;
+import org.apache.oozie.sla.service.SLAService;
+import org.apache.oozie.util.XLog;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+
+public class SLAEmailEventListener extends SLAEventListener {
+
+    public static final String SMTP_CONNECTION_TIMEOUT = 
EmailActionExecutor.CONF_PREFIX + "smtp.connectiontimeout";
+    public static final String SMTP_TIMEOUT = EmailActionExecutor.CONF_PREFIX 
+ "smtp.timeout";
+    public static final String BLACKLIST_CACHE_TIMEOUT = 
EmailActionExecutor.CONF_PREFIX + "blacklist.cachetimeout";
+    public static final String BLACKLIST_FAIL_COUNT = 
EmailActionExecutor.CONF_PREFIX + "blacklist.failcount";
+    public static final String OOZIE_BASE_URL = "oozie.base.url";
+    private Session session;
+    private String oozieBaseUrl;
+    private InternetAddress fromAddr;
+    private String ADDRESS_SEPARATOR = ",";
+    private LoadingCache<String, AtomicInteger> blackList;
+    private int blacklistFailCount;
+    private final String BLACKLIST_CACHE_TIMEOUT_DEFAULT = "1800"; // in sec. 
default to 30 min
+    private final String BLACKLIST_FAIL_COUNT_DEFAULT = "2"; // stop sending 
when fail count equals or exceeds
+    private final String SMTP_HOST_DEFAULT = "localhost";
+    private final String SMTP_PORT_DEFAULT = "25";
+    private final boolean SMTP_AUTH_DEFAULT = false;
+    private final String SMTP_SOURCE_DEFAULT = "oozie@localhost";
+    private final String SMTP_CONNECTION_TIMEOUT_DEFAULT = "5000";
+    private final String SMTP_TIMEOUT_DEFAULT = "5000";
+    private static XLog LOG = XLog.getLog(SLAEmailEventListener.class);
+    private Set<SLAEvent.EventStatus> alertEvents;
+    public static String EMAIL_BODY_FIELD_SEPARATER = " - ";
+    public static String EMAIL_BODY_FIELD_INDENT = "  ";
+    public static String EMAIL_BODY_HEADER_SEPARATER = ":";
+
+    public enum EmailField {
+        EVENT_STATUS("SLA Status"), APP_TYPE("App Type"), APP_NAME("App 
Name"), USER("User"), JOBID("Job ID"), PARENT_JOBID(
+                "Parent Job ID"), JOB_URL("Job URL"), PARENT_JOB_URL("Parent 
Job URL"), NOMINAL_TIME("Nominal Time"),
+                EXPECTED_START_TIME("Expected Start Time"), 
ACTUAL_START_TIME("Actual Start Time"),
+                EXPECTED_END_TIME("Expected End Time"), 
ACTUAL_END_TIME("Actual End Time"), EXPECTED_DURATION("Expected Duration"),
+                ACTUAL_DURATION("Actual Duration"), 
NOTIFICATION_MESSAGE("Notification Message"), UPSTREAM_APPS("Upstream Apps"),
+                JOB_STATUS("Job Status");
+        private String name;
+
+        private EmailField(String name) {
+            this.name = name;
+        }
+
+        public String toString() {
+            return name;
+        }
+    };
+
+    @Override
+    public void init(Configuration conf) throws Exception {
+
+        oozieBaseUrl = conf.get(OOZIE_BASE_URL);
+        // Get SMTP properties from the configuration used in Email Action
+        String smtpHost = conf.get(EmailActionExecutor.EMAIL_SMTP_HOST, 
SMTP_HOST_DEFAULT);
+        String smtpPort = conf.get(EmailActionExecutor.EMAIL_SMTP_PORT, 
SMTP_PORT_DEFAULT);
+        Boolean smtpAuth = 
conf.getBoolean(EmailActionExecutor.EMAIL_SMTP_AUTH, SMTP_AUTH_DEFAULT);
+        String smtpUser = conf.get(EmailActionExecutor.EMAIL_SMTP_USER, "");
+        String smtpPassword = conf.get(EmailActionExecutor.EMAIL_SMTP_PASS, 
"");
+        String smtpConnectTimeout = conf.get(SMTP_CONNECTION_TIMEOUT, 
SMTP_CONNECTION_TIMEOUT_DEFAULT);
+        String smtpTimeout = conf.get(SMTP_TIMEOUT, SMTP_TIMEOUT_DEFAULT);
+
+        int blacklistTimeOut = 
Integer.valueOf(conf.get(BLACKLIST_CACHE_TIMEOUT, 
BLACKLIST_CACHE_TIMEOUT_DEFAULT));
+        blacklistFailCount = Integer.valueOf(conf.get(BLACKLIST_FAIL_COUNT, 
BLACKLIST_FAIL_COUNT_DEFAULT));
+
+        // blacklist email addresses causing SendFailedException with cache 
timeout
+        blackList = CacheBuilder.newBuilder()
+                .expireAfterWrite(blacklistTimeOut, TimeUnit.SECONDS)
+                .build(new CacheLoader<String, AtomicInteger>() {
+                    public AtomicInteger load(String key) throws Exception {
+                        return new AtomicInteger();
+                    }
+                });
+
+        // Set SMTP properties
+        Properties properties = new Properties();
+        properties.setProperty("mail.smtp.host", smtpHost);
+        properties.setProperty("mail.smtp.port", smtpPort);
+        properties.setProperty("mail.smtp.auth", smtpAuth.toString());
+        properties.setProperty("mail.smtp.connectiontimeout", 
smtpConnectTimeout);
+        properties.setProperty("mail.smtp.timeout", smtpTimeout);
+
+        try {
+            fromAddr = new 
InternetAddress(conf.get("oozie.email.from.address", SMTP_SOURCE_DEFAULT));
+        }
+        catch (AddressException ae) {
+            LOG.error("Bad Source Address specified in 
oozie.email.from.address", ae);
+            throw ae;
+        }
+
+        if (!smtpAuth) {
+            session = Session.getInstance(properties);
+        }
+        else {
+            session = Session.getInstance(properties, new 
JavaMailAuthenticator(smtpUser, smtpPassword));
+        }
+
+        alertEvents = new HashSet<SLAEvent.EventStatus>();
+        String alertEventsStr = conf.get(SLAService.CONF_ALERT_EVENTS);
+        if (alertEventsStr != null) {
+            String[] alertEvt = alertEventsStr.split(",", -1);
+            for (String evt : alertEvt) {
+                alertEvents.add(SLAEvent.EventStatus.valueOf(evt));
+            }
+        }
+    }
+
+    @Override
+    public void destroy() {
+    }
+
+    private void sendSLAEmail(SLAEvent event) throws Exception {
+        Message message = new MimeMessage(session);
+        setMessageHeader(message, event);
+        setMessageBody(message, event);
+        sendEmail(message);
+    }
+
+    @Override
+    public void onStartMiss(SLAEvent event) {
+        boolean flag = false;
+        if (event.getAlertEvents() == null) {
+            flag = alertEvents.contains(SLAEvent.EventStatus.START_MISS);
+        }
+        else if 
(event.getAlertEvents().contains(SLAEvent.EventStatus.START_MISS.name())) {
+            flag = true;
+        }
+
+        if (flag) {
+            try {
+                sendSLAEmail(event);
+            }
+            catch (Exception e) {
+                LOG.error("Failed to send StartMiss alert email", e);
+            }
+        }
+    }
+
+    @Override
+    public void onEndMiss(SLAEvent event) {
+        boolean flag = false;
+        if (event.getAlertEvents() == null) {
+            flag = alertEvents.contains(SLAEvent.EventStatus.END_MISS);
+        }
+        else if 
(event.getAlertEvents().contains(SLAEvent.EventStatus.END_MISS.name())) {
+            flag = true;
+        }
+
+        if (flag) {
+            try {
+                sendSLAEmail(event);
+            }
+            catch (Exception e) {
+                LOG.error("Failed to send EndMiss alert email", e);
+            }
+        }
+    }
+
+    @Override
+    public void onDurationMiss(SLAEvent event) {
+        boolean flag = false;
+        if (event.getAlertEvents() == null) {
+            flag = alertEvents.contains(SLAEvent.EventStatus.DURATION_MISS);
+        }
+        else if 
(event.getAlertEvents().contains(SLAEvent.EventStatus.DURATION_MISS.name())) {
+            flag = true;
+        }
+
+        if (flag) {
+            try {
+                sendSLAEmail(event);
+            }
+            catch (Exception e) {
+                LOG.error("Failed to send DurationMiss alert email", e);
+            }
+        }
+    }
+
+    private Address[] parseAddress(String str) {
+        Address[] addrs = null;
+        List<InternetAddress> addrList = new ArrayList<InternetAddress>();
+        String[] emails = str.split(ADDRESS_SEPARATOR, -1);
+
+        for (String email : emails) {
+            boolean isBlackListed = false;
+            AtomicInteger val = blackList.getIfPresent(email);
+            if(val != null){
+                isBlackListed = ( val.get() >= blacklistFailCount );
+            }
+            if (!isBlackListed) {
+                try {
+                    // turn on strict syntax check by setting 2nd argument true
+                    addrList.add(new InternetAddress(email, true));
+                }
+                catch (AddressException ae) {
+                    // simply skip bad address but do not throw exception
+                    LOG.error("Skipping bad destination address: " + email, 
ae);
+                }
+            }
+        }
+
+        if (addrList.size() > 0)
+            addrs = (Address[]) addrList.toArray(new 
InternetAddress[addrList.size()]);
+
+        return addrs;
+    }
+
+    private void setMessageHeader(Message msg, SLAEvent event) throws 
MessagingException {
+        Address[] from = new InternetAddress[] { fromAddr };
+        Address[] to;
+        StringBuilder subject = new StringBuilder();
+
+        to = parseAddress(event.getAlertContact());
+        if (to == null) {
+            LOG.error("Destination address is null or invalid, stop sending 
SLA alert email");
+            throw new IllegalArgumentException("Destination address is not 
specified properly");
+        }
+        subject.append("OOZIE - SLA ");
+        subject.append(event.getEventStatus().name());
+        subject.append(" (AppName=");
+        subject.append(event.getAppName());
+        subject.append(", JobID=");
+        subject.append(event.getId());
+        subject.append(")");
+
+        try {
+            msg.addFrom(from);
+            msg.addRecipients(RecipientType.TO, to);
+            msg.setSubject(subject.toString());
+        }
+        catch (MessagingException me) {
+            LOG.error("Message Exception in setting message header of SLA 
alert email", me);
+            throw me;
+        }
+    }
+
+    private void setMessageBody(Message msg, SLAEvent event) throws 
MessagingException {
+        StringBuilder body = new StringBuilder();
+        printHeading(body, "Status");
+        printField(body, EmailField.EVENT_STATUS.toString(), 
event.getEventStatus(), false);
+        printField(body, EmailField.JOB_STATUS.toString(), 
event.getJobStatus(), false);
+        printField(body, EmailField.NOTIFICATION_MESSAGE.toString(), 
event.getNotificationMsg(), false);
+
+        printHeading(body, "Job Details");
+        printField(body, EmailField.APP_NAME.toString(), event.getAppName(), 
false);
+        printField(body, EmailField.APP_TYPE.toString(), event.getAppType(), 
false);
+        printField(body, EmailField.USER.toString(), event.getUser(), false);
+        printField(body, EmailField.JOBID.toString(), event.getId(), false);
+        printField(body, EmailField.JOB_URL.toString(), 
getJobLink(event.getId()), false);
+        printField(body, EmailField.PARENT_JOBID.toString(), 
event.getParentId() != null ? event.getParentId() : "N/A",
+                false);
+        printField(body, EmailField.PARENT_JOB_URL.toString(),
+                event.getParentId() != null ? getJobLink(event.getParentId()) 
: "N/A", false);
+        printField(body, EmailField.UPSTREAM_APPS.toString(), 
event.getUpstreamApps(), false);
+
+        printHeading(body, "SLA Details");
+        printField(body, EmailField.NOMINAL_TIME.toString(), 
event.getNominalTime(), false);
+        printField(body, EmailField.EXPECTED_START_TIME.toString(), 
event.getExpectedStart(), false);
+        printField(body, EmailField.ACTUAL_START_TIME.toString(), 
event.getActualStart(), false);
+        printField(body, EmailField.EXPECTED_END_TIME.toString(), 
event.getExpectedEnd(), false);
+        printField(body, EmailField.ACTUAL_END_TIME.toString(), 
event.getActualEnd(), false);
+        printField(body, EmailField.EXPECTED_DURATION.toString(), 
event.getExpectedDuration(), false);
+        long actualDuration = (event.getActualEnd() != null && 
event.getActualStart() != null) ? (event.getActualEnd()
+                .getTime() - event.getActualStart().getTime()) / (1000 * 60) : 
-1;
+        printField(body, EmailField.ACTUAL_DURATION.toString(), 
actualDuration, false);
+
+        try {
+            msg.setText(body.toString());
+        }
+        catch (MessagingException me) {
+            LOG.error("Message Exception in setting message body of SLA alert 
email", me);
+            throw me;
+        }
+    }
+
+    private String getJobLink(String jobId) {
+        StringBuffer url = new StringBuffer();
+        String param = "/?job=";
+        url.append(oozieBaseUrl);
+        url.append(param);
+        url.append(jobId);
+        return url.toString();
+    }
+
+    private void printField(StringBuilder st, String name, Object value, 
boolean last) {
+        String lineFeed = "\n";
+        if (value != null) {
+            st.append(EMAIL_BODY_FIELD_INDENT);
+            st.append(name);
+            st.append(EMAIL_BODY_FIELD_SEPARATER);
+            st.append(value);
+            if (!last)
+                st.append(lineFeed);
+        }
+    }
+
+    private void printHeading(StringBuilder st, String header) {
+        st.append(header);
+        st.append(EMAIL_BODY_HEADER_SEPARATER);
+        st.append("\n");
+    }
+
+    private void sendEmail(Message message) throws MessagingException {
+        try {
+            Transport.send(message);
+        }
+        catch (NoSuchProviderException se) {
+            LOG.error("Could not find an SMTP transport provider to email", 
se);
+            throw se;
+        }
+        catch (MessagingException me) {
+            LOG.error("Message Exception in transporting SLA alert email", me);
+            if (me instanceof SendFailedException) {
+                Address[] invalidAddrs = ((SendFailedException) 
me).getInvalidAddresses();
+                if (invalidAddrs != null && invalidAddrs.length > 0) {
+                    for (Address addr : invalidAddrs) {
+                        try {
+                            // 'get' method loads key into cache when it 
doesn't exist
+                            AtomicInteger val = blackList.get(addr.toString());
+                            val.incrementAndGet();
+                        }
+                        catch (Exception e) {
+                            LOG.debug("blacklist loading throwed exception");
+                        }
+                    }
+                }
+            }
+            throw me;
+        }
+    }
+
+    @VisibleForTesting
+    public void addBlackList(String email) throws Exception {
+        // this is for testing
+        if(email == null || email.equals("")){
+            return;
+        }
+        AtomicInteger val = blackList.get(email);
+        val.set(blacklistFailCount);
+    }
+
+    @Override
+    public void onStartMet(SLAEvent work) {
+    }
+
+    @Override
+    public void onEndMet(SLAEvent work) {
+    }
+
+    @Override
+    public void onDurationMet(SLAEvent work) {
+    }
+
+}

Modified: 
oozie/trunk/core/src/main/java/org/apache/oozie/sla/service/SLAService.java
URL: 
http://svn.apache.org/viewvc/oozie/trunk/core/src/main/java/org/apache/oozie/sla/service/SLAService.java?rev=1488068&r1=1488067&r2=1488068&view=diff
==============================================================================
--- oozie/trunk/core/src/main/java/org/apache/oozie/sla/service/SLAService.java 
(original)
+++ oozie/trunk/core/src/main/java/org/apache/oozie/sla/service/SLAService.java 
Fri May 31 00:35:39 2013
@@ -39,7 +39,7 @@ public class SLAService implements Servi
     public static final String CONF_PREFIX = "oozie.sla.service.SLAService.";
     public static final String CONF_CALCULATOR_IMPL = CONF_PREFIX + 
"calculator.impl";
     public static final String CONF_CAPACITY = CONF_PREFIX + "capacity";
-    public static final String CONF_ALERT_TYPES = CONF_PREFIX + "alert.types";
+    public static final String CONF_ALERT_EVENTS = CONF_PREFIX + 
"alert.events";
 
     private static SLACalculator calcImpl;
     private static boolean slaEnabled = false;

Modified: oozie/trunk/core/src/main/resources/oozie-default.xml
URL: 
http://svn.apache.org/viewvc/oozie/trunk/core/src/main/resources/oozie-default.xml?rev=1488068&r1=1488067&r2=1488068&view=diff
==============================================================================
--- oozie/trunk/core/src/main/resources/oozie-default.xml (original)
+++ oozie/trunk/core/src/main/resources/oozie-default.xml Fri May 31 00:35:39 
2013
@@ -1892,7 +1892,7 @@
     </property>
 
     <property>
-        <name>oozie.sla.service.SLAService.alert.types</name>
+        <name>oozie.sla.service.SLAService.alert.events</name>
         <value>END_MISS</value>
         <description>
              Default types of SLA events for being alerted of.

Added: 
oozie/trunk/core/src/test/java/org/apache/oozie/sla/TestSLAEmailEventListener.java
URL: 
http://svn.apache.org/viewvc/oozie/trunk/core/src/test/java/org/apache/oozie/sla/TestSLAEmailEventListener.java?rev=1488068&view=auto
==============================================================================
--- 
oozie/trunk/core/src/test/java/org/apache/oozie/sla/TestSLAEmailEventListener.java
 (added)
+++ 
oozie/trunk/core/src/test/java/org/apache/oozie/sla/TestSLAEmailEventListener.java
 Fri May 31 00:35:39 2013
@@ -0,0 +1,393 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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.apache.oozie.sla;
+
+import java.util.Date;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.oozie.AppType;
+import org.apache.oozie.action.email.EmailActionExecutor;
+import org.apache.oozie.client.event.JobEvent;
+import org.apache.oozie.client.event.SLAEvent;
+import org.apache.oozie.client.event.SLAEvent.EventStatus;
+import org.apache.oozie.service.Services;
+import org.apache.oozie.sla.SLACalcStatus;
+import org.apache.oozie.sla.SLARegistrationBean;
+import org.apache.oozie.test.XTestCase;
+import org.apache.oozie.util.DateUtils;
+import org.junit.After;
+import org.junit.Before;
+import com.icegreen.greenmail.util.GreenMail;
+import com.icegreen.greenmail.util.ServerSetup;
+import javax.mail.Message.RecipientType;
+import javax.mail.internet.MimeMessage;
+import org.apache.oozie.sla.listener.SLAEmailEventListener;
+import org.apache.oozie.sla.listener.SLAEmailEventListener.EmailField;
+import org.apache.oozie.sla.service.SLAService;
+
+public class TestSLAEmailEventListener extends XTestCase {
+
+    private Services services;
+    private static final int SMTP_TEST_PORT = 3025;
+    private GreenMail greenMail;
+    private SLAEmailEventListener slaEmailListener;
+    private Configuration conf = null;
+
+    @Before
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        services = new Services();
+        conf = services.getConf();
+        conf.set(EmailActionExecutor.EMAIL_SMTP_HOST, "localhost");
+        conf.set(EmailActionExecutor.EMAIL_SMTP_PORT, 
String.valueOf(SMTP_TEST_PORT));
+        conf.set(EmailActionExecutor.EMAIL_SMTP_AUTH, "false");
+        conf.set(EmailActionExecutor.EMAIL_SMTP_USER, "");
+        conf.set(EmailActionExecutor.EMAIL_SMTP_PASS, "");
+        conf.set(EmailActionExecutor.EMAIL_SMTP_FROM, "oozie@localhost");
+        conf.set(SLAEmailEventListener.BLACKLIST_CACHE_TIMEOUT, "1");
+        conf.set(SLAEmailEventListener.BLACKLIST_FAIL_COUNT, "2");
+        conf.set(SLAService.CONF_ALERT_EVENTS, 
SLAEvent.EventStatus.START_MISS.name() + ","
+                + SLAEvent.EventStatus.END_MISS + "," + 
SLAEvent.EventStatus.DURATION_MISS);
+
+        greenMail = new GreenMail(new ServerSetup(SMTP_TEST_PORT, null, 
"smtp"));
+        greenMail.start();
+        services.init();
+        slaEmailListener = new SLAEmailEventListener();
+        slaEmailListener.init(conf);
+    }
+
+    @After
+    @Override
+    protected void tearDown() throws Exception {
+        if (null != greenMail) {
+            greenMail.stop();
+        }
+        services.destroy();
+        super.tearDown();
+    }
+
+    public void testOnStartMiss() throws Exception {
+        SLACalcStatus event = new SLACalcStatus(new SLARegistrationBean());
+        SLARegistrationBean eventBean = event.getSLARegistrationBean();
+        Date startDate = DateUtils.parseDateUTC("2013-01-01T00:00Z");
+        Date actualstartDate = DateUtils.parseDateUTC("2013-01-01T01:00Z");
+        event.setEventStatus(EventStatus.START_MISS);
+        event.setJobStatus(JobEvent.EventStatus.STARTED.toString());
+        event.setJobId("0000000-000000000000001-oozie-wrkf-C@1");
+        eventBean.setParentId("0000000-000000000000001-oozie-wrkf-C");
+        eventBean.setAppName("Test-SLA-Start-Miss");
+        eventBean.setUser("dummyuser");
+        eventBean.setNominalTime(startDate);
+        eventBean.setExpectedStart(startDate);
+        eventBean.setNotificationMsg("Notification of Missing Expected Start 
Time");
+        eventBean.setAlertContact("[email protected]");
+        event.setActualStart(actualstartDate);
+        eventBean.setAppType(AppType.COORDINATOR_ACTION);
+
+        slaEmailListener.onStartMiss(event);
+
+        MimeMessage[] msgs = greenMail.getReceivedMessages();
+        MimeMessage msg = msgs[0];
+        // check message header
+        assertEquals(msg.getFrom()[0].toString(), "oozie@localhost");
+        assertEquals(msg.getRecipients(RecipientType.TO)[0].toString(), 
"[email protected]");
+        assertEquals(msg.getSubject(), "OOZIE - SLA " + EventStatus.START_MISS
+                + " (AppName=Test-SLA-Start-Miss, 
JobID=0000000-000000000000001-oozie-wrkf-C@1)");
+        // check message body
+        String msgBody = msg.getContent().toString();
+        String headerSep = SLAEmailEventListener.EMAIL_BODY_HEADER_SEPARATER;
+        String sep = SLAEmailEventListener.EMAIL_BODY_FIELD_SEPARATER;
+        String indent = SLAEmailEventListener.EMAIL_BODY_FIELD_INDENT;
+        assertTrue(msgBody.indexOf("Status" + headerSep) > -1);
+        assertTrue(msgBody.indexOf(indent + EmailField.EVENT_STATUS.toString() 
+ sep
+                + EventStatus.START_MISS.toString()) > -1);
+        assertTrue(msgBody.indexOf(indent + EmailField.JOB_STATUS.toString() + 
sep
+                + JobEvent.EventStatus.STARTED.toString()) > -1);
+        assertTrue(msgBody.indexOf(indent + 
EmailField.NOTIFICATION_MESSAGE.toString() + sep
+                + "Notification of Missing Expected Start Time") > -1);
+        assertTrue(msgBody.indexOf("Job Details" + headerSep) > -1);
+        assertTrue(msgBody.indexOf(indent + EmailField.APP_TYPE.toString() + 
sep + AppType.COORDINATOR_ACTION) > -1);
+        assertTrue(msgBody.indexOf(indent + EmailField.APP_NAME.toString() + 
sep + "Test-SLA-Start-Miss") > -1);
+        assertTrue(msgBody.indexOf(indent + EmailField.USER.toString() + sep + 
"dummyuser") > -1);
+        assertTrue(msgBody.indexOf(indent + EmailField.JOBID.toString() + sep
+                + "0000000-000000000000001-oozie-wrkf-C@1") > -1);
+        assertTrue(msgBody.indexOf(indent + EmailField.PARENT_JOBID.toString() 
+ sep
+                + "0000000-000000000000001-oozie-wrkf-C") > -1);
+        assertTrue(msgBody.indexOf(indent + EmailField.JOB_URL.toString() + sep
+                + conf.get(SLAEmailEventListener.OOZIE_BASE_URL) + "/?job=" + 
"0000000-000000000000001-oozie-wrkf-C@1") > -1);
+        assertTrue(msgBody.indexOf(indent + 
EmailField.PARENT_JOB_URL.toString() + sep
+                + conf.get(SLAEmailEventListener.OOZIE_BASE_URL) + "/?job=" + 
"0000000-000000000000001-oozie-wrkf-C") > -1);
+        assertTrue(msgBody.indexOf("SLA Details" + headerSep) > -1);
+        assertTrue(msgBody.indexOf(indent + EmailField.NOMINAL_TIME.toString() 
+ sep + startDate) > -1);
+        assertTrue(msgBody.indexOf(indent + 
EmailField.EXPECTED_START_TIME.toString() + sep + startDate) > -1);
+        assertTrue(msgBody.indexOf(indent + 
EmailField.ACTUAL_START_TIME.toString() + sep + actualstartDate) > -1);
+    }
+
+    public void testOnEndMiss() throws Exception {
+        SLACalcStatus event = new SLACalcStatus(new SLARegistrationBean());
+        SLARegistrationBean eventBean = event.getSLARegistrationBean();
+        Date expectedStartDate = DateUtils.parseDateUTC("2013-01-01T00:00Z");
+        Date actualStartDate = DateUtils.parseDateUTC("2013-01-01T01:00Z");
+        Date expectedEndDate = DateUtils.parseDateUTC("2013-01-01T12:00Z");
+        Date actualEndDate = DateUtils.parseDateUTC("2013-01-01T13:00Z");
+        event.setJobId("0000000-000000000000001-oozie-wrkf-C@1");
+        eventBean.setParentId("0000000-000000000000001-oozie-wrkf-C");
+        event.setEventStatus(EventStatus.END_MISS);
+        event.setJobStatus(JobEvent.EventStatus.SUCCESS.toString());
+        eventBean.setAppName("Test-SLA-End-Miss");
+        eventBean.setUser("dummyuser");
+        eventBean.setNominalTime(expectedStartDate);
+        eventBean.setExpectedStart(expectedStartDate);
+        eventBean.setExpectedEnd(expectedEndDate);
+        eventBean.setNotificationMsg("notification of end miss");
+        eventBean.setAlertContact("[email protected]");
+        eventBean.setAppType(AppType.COORDINATOR_ACTION);
+        eventBean.setExpectedStart(expectedStartDate);
+        eventBean.setExpectedEnd(expectedEndDate);
+        event.setActualStart(actualStartDate);
+        event.setActualEnd(actualEndDate);
+
+        slaEmailListener.onEndMiss(event);
+
+        MimeMessage[] msgs = greenMail.getReceivedMessages();
+        MimeMessage msg = msgs[0];
+        // check message header
+        assertEquals(msg.getFrom()[0].toString(), "oozie@localhost");
+        assertEquals(msg.getRecipients(RecipientType.TO)[0].toString(), 
"[email protected]");
+        assertEquals(msg.getSubject(), "OOZIE - SLA " + EventStatus.END_MISS
+                + " (AppName=Test-SLA-End-Miss, 
JobID=0000000-000000000000001-oozie-wrkf-C@1)");
+        // check message body
+        String msgBody = msg.getContent().toString();
+        String headerSep = SLAEmailEventListener.EMAIL_BODY_HEADER_SEPARATER;
+        String sep = SLAEmailEventListener.EMAIL_BODY_FIELD_SEPARATER;
+        String indent = SLAEmailEventListener.EMAIL_BODY_FIELD_INDENT;
+        assertTrue(msgBody.indexOf("Status" + headerSep) > -1);
+        assertTrue(msgBody.indexOf(indent + EmailField.EVENT_STATUS.toString() 
+ sep + EventStatus.END_MISS.toString()) > -1);
+        assertTrue(msgBody.indexOf(indent + EmailField.JOB_STATUS.toString() + 
sep
+                + JobEvent.EventStatus.SUCCESS.toString()) > -1);
+        assertTrue(msgBody.indexOf(indent + 
EmailField.NOTIFICATION_MESSAGE.toString() + sep
+                + "notification of end miss") > -1);
+
+        assertTrue(msgBody.indexOf("Job Details" + headerSep) > -1);
+        assertTrue(msgBody.indexOf(indent + EmailField.APP_TYPE.toString() + 
sep + AppType.COORDINATOR_ACTION) > -1);
+        assertTrue(msgBody.indexOf(indent + EmailField.APP_NAME.toString() + 
sep + "Test-SLA-End-Miss") > -1);
+        assertTrue(msgBody.indexOf(indent + EmailField.USER.toString() + sep + 
"dummyuser") > -1);
+        assertTrue(msgBody.indexOf(indent + EmailField.JOBID.toString() + sep
+                + "0000000-000000000000001-oozie-wrkf-C@1") > -1);
+        assertTrue(msgBody.indexOf(indent + EmailField.PARENT_JOBID.toString() 
+ sep
+                + "0000000-000000000000001-oozie-wrkf-C") > -1);
+        assertTrue(msgBody.indexOf(indent + EmailField.JOB_URL.toString() + sep
+                + conf.get(SLAEmailEventListener.OOZIE_BASE_URL) + "/?job=" + 
"0000000-000000000000001-oozie-wrkf-C@1") > -1);
+        assertTrue(msgBody.indexOf(indent + 
EmailField.PARENT_JOB_URL.toString() + sep
+                + conf.get(SLAEmailEventListener.OOZIE_BASE_URL) + "/?job=" + 
"0000000-000000000000001-oozie-wrkf-C") > -1);
+
+        assertTrue(msgBody.indexOf("SLA Details" + headerSep) > -1);
+        assertTrue(msgBody.indexOf(indent + EmailField.NOMINAL_TIME.toString() 
+ sep + expectedStartDate) > -1);
+        assertTrue(msgBody.indexOf(indent + 
EmailField.EXPECTED_START_TIME.toString() + sep + expectedStartDate) > -1);
+        assertTrue(msgBody.indexOf(indent + 
EmailField.ACTUAL_START_TIME.toString() + sep + actualStartDate) > -1);
+        assertTrue(msgBody.indexOf(indent + 
EmailField.EXPECTED_END_TIME.toString() + sep + expectedEndDate) > -1);
+        assertTrue(msgBody.indexOf(indent + 
EmailField.ACTUAL_END_TIME.toString() + sep + actualEndDate) > -1);
+
+    }
+
+    public void testOnDurationMiss() throws Exception {
+        SLACalcStatus event = new SLACalcStatus(new SLARegistrationBean());
+        SLARegistrationBean eventBean = event.getSLARegistrationBean();
+        Date expectedStartDate = DateUtils.parseDateUTC("2013-01-01T00:00Z");
+        Date actualStartDate = DateUtils.parseDateUTC("2013-01-01T00:10Z");
+        Date expectedEndDate = DateUtils.parseDateUTC("2013-01-01T00:20Z");
+        Date actualEndDate = DateUtils.parseDateUTC("2013-01-01T00:40Z");
+        long expectedDuration = (expectedEndDate.getTime() - 
expectedStartDate.getTime()) / (1000 * 60);
+        long actualDuration = (actualEndDate.getTime() - 
actualStartDate.getTime()) / (1000 * 60);
+        event.setJobId("0000000-000000000000001-oozie-wrkf-C@1");
+        eventBean.setParentId("0000000-000000000000001-oozie-wrkf-C");
+        event.setEventStatus(EventStatus.DURATION_MISS);
+        event.setJobStatus(JobEvent.EventStatus.SUCCESS.toString());
+        eventBean.setAppName("Test-SLA-Duration-Miss");
+        eventBean.setUser("dummyuser");
+        eventBean.setNominalTime(expectedStartDate);
+        eventBean.setExpectedStart(expectedStartDate);
+        eventBean.setExpectedEnd(expectedEndDate);
+        eventBean.setNotificationMsg("notification of duration miss");
+        eventBean.setAlertContact("[email protected]");
+        eventBean.setAppType(AppType.COORDINATOR_ACTION);
+        eventBean.setExpectedStart(expectedStartDate);
+        eventBean.setExpectedEnd(expectedEndDate);
+        event.setActualStart(actualStartDate);
+        event.setActualEnd(actualEndDate);
+        eventBean.setExpectedDuration(expectedDuration);
+        event.setActualDuration(actualDuration);
+
+        slaEmailListener.onEndMiss(event);
+
+        MimeMessage[] msgs = greenMail.getReceivedMessages();
+        MimeMessage msg = msgs[0];
+        // check message header
+        assertEquals(msg.getFrom()[0].toString(), "oozie@localhost");
+        assertEquals(msg.getRecipients(RecipientType.TO)[0].toString(), 
"[email protected]");
+        assertEquals(msg.getSubject(), "OOZIE - SLA " + 
EventStatus.DURATION_MISS
+                + " (AppName=Test-SLA-Duration-Miss, 
JobID=0000000-000000000000001-oozie-wrkf-C@1)");
+        // check message body
+        String msgBody = msg.getContent().toString();
+        String headerSep = SLAEmailEventListener.EMAIL_BODY_HEADER_SEPARATER;
+        String sep = SLAEmailEventListener.EMAIL_BODY_FIELD_SEPARATER;
+        String indent = SLAEmailEventListener.EMAIL_BODY_FIELD_INDENT;
+        assertTrue(msgBody.indexOf("Status" + headerSep) > -1);
+        assertTrue(msgBody.indexOf(indent + EmailField.EVENT_STATUS.toString() 
+ sep
+                + EventStatus.DURATION_MISS.toString()) > -1);
+        assertTrue(msgBody.indexOf(indent + EmailField.JOB_STATUS.toString() + 
sep
+                + JobEvent.EventStatus.SUCCESS.toString()) > -1);
+        assertTrue(msgBody.indexOf(indent + 
EmailField.NOTIFICATION_MESSAGE.toString() + sep
+                + "notification of duration miss") > -1);
+
+        assertTrue(msgBody.indexOf("Job Details" + headerSep) > -1);
+        assertTrue(msgBody.indexOf(indent + EmailField.APP_TYPE.toString() + 
sep + AppType.COORDINATOR_ACTION) > -1);
+        assertTrue(msgBody.indexOf(indent + EmailField.APP_NAME.toString() + 
sep + "Test-SLA-Duration-Miss") > -1);
+        assertTrue(msgBody.indexOf(indent + EmailField.USER.toString() + sep + 
"dummyuser") > -1);
+        assertTrue(msgBody.indexOf(indent + EmailField.JOBID.toString() + sep
+                + "0000000-000000000000001-oozie-wrkf-C@1") > -1);
+        assertTrue(msgBody.indexOf(indent + EmailField.PARENT_JOBID.toString() 
+ sep
+                + "0000000-000000000000001-oozie-wrkf-C") > -1);
+        assertTrue(msgBody.indexOf(indent + EmailField.JOB_URL.toString() + sep
+                + conf.get(SLAEmailEventListener.OOZIE_BASE_URL) + "/?job=" + 
"0000000-000000000000001-oozie-wrkf-C@1") > -1);
+        assertTrue(msgBody.indexOf(indent + 
EmailField.PARENT_JOB_URL.toString() + sep
+                + conf.get(SLAEmailEventListener.OOZIE_BASE_URL) + "/?job=" + 
"0000000-000000000000001-oozie-wrkf-C") > -1);
+
+        assertTrue(msgBody.indexOf("SLA Details" + headerSep) > -1);
+        assertTrue(msgBody.indexOf(indent + EmailField.NOMINAL_TIME.toString() 
+ sep + expectedStartDate) > -1);
+        assertTrue(msgBody.indexOf(indent + 
EmailField.EXPECTED_START_TIME.toString() + sep + expectedStartDate) > -1);
+        assertTrue(msgBody.indexOf(indent + 
EmailField.ACTUAL_START_TIME.toString() + sep + actualStartDate) > -1);
+        assertTrue(msgBody.indexOf(indent + 
EmailField.EXPECTED_END_TIME.toString() + sep + expectedEndDate) > -1);
+        assertTrue(msgBody.indexOf(indent + 
EmailField.ACTUAL_END_TIME.toString() + sep + actualEndDate) > -1);
+        assertTrue(msgBody.indexOf(indent + 
EmailField.EXPECTED_DURATION.toString() + sep + expectedDuration) > -1);
+        assertTrue(msgBody.indexOf(indent + 
EmailField.ACTUAL_DURATION.toString() + sep + actualDuration) > -1);
+    }
+
+    public void testUserAlertEventSetting() throws Exception {
+        SLACalcStatus event = new SLACalcStatus(new SLARegistrationBean());
+        SLARegistrationBean eventBean = event.getSLARegistrationBean();
+        // user choose only END MISS, thus, START_MISS email should not be sent
+        eventBean.setAlertEvents(EventStatus.END_MISS.name());
+
+        Date startDate = DateUtils.parseDateUTC("2013-01-01T00:00Z");
+        Date actualstartDate = DateUtils.parseDateUTC("2013-01-01T01:00Z");
+        event.setEventStatus(EventStatus.START_MISS);
+        event.setJobId("0000000-000000000000001-oozie-wrkf-C@1");
+        eventBean.setAppName("Test-SLA-Start-Miss");
+        eventBean.setUser("dummyuser");
+        eventBean.setNominalTime(startDate);
+        eventBean.setExpectedStart(startDate);
+        eventBean.setAlertContact("[email protected]");
+        event.setActualStart(actualstartDate);
+        eventBean.setAppType(AppType.COORDINATOR_ACTION);
+
+        slaEmailListener.onStartMiss(event);
+
+        // START_MISS should not be sent
+        MimeMessage[] msgs = greenMail.getReceivedMessages();
+        assertEquals(msgs.length, 0);
+
+        // DURATION_MISS should not be sent
+        event.setEventStatus(EventStatus.DURATION_MISS);
+        slaEmailListener.onDurationMiss(event);
+        msgs = greenMail.getReceivedMessages();
+        assertEquals(msgs.length, 0);
+
+        // END_MISS should be sent
+        event.setEventStatus(EventStatus.END_MISS);
+        slaEmailListener.onEndMiss(event);
+        msgs = greenMail.getReceivedMessages();
+        assertNotNull(msgs[0]);
+    }
+
+    public void testInvalidDestAddress() throws Exception {
+        SLACalcStatus event = new SLACalcStatus(new SLARegistrationBean());
+        SLARegistrationBean eventBean = event.getSLARegistrationBean();
+        Date startDate = DateUtils.parseDateUTC("2013-01-01T00:00Z");
+        // set invalid address as alert contact
+        eventBean.setAlertContact("invalidAddress");
+        event.setEventStatus(EventStatus.START_MISS);
+        event.setJobId("0000000-000000000000001-oozie-wrkf-C@1");
+        eventBean.setAppType(AppType.COORDINATOR_ACTION);
+        eventBean.setAppName("Test-SLA-Start-Miss");
+        eventBean.setUser("dummyuser");
+        eventBean.setExpectedStart(startDate);
+        eventBean.setNotificationMsg("notification of start miss");
+        eventBean.setAppType(AppType.COORDINATOR_ACTION);
+        event.setActualStart(DateUtils.parseDateUTC("2013-01-01T01:00Z"));
+
+        slaEmailListener.onStartMiss(event);
+
+        MimeMessage[] msgs = greenMail.getReceivedMessages();
+        assertEquals(msgs.length, 0);
+    }
+
+    public void testMultipleDestAddress() throws Exception {
+        SLACalcStatus event = new SLACalcStatus(new SLARegistrationBean());
+        SLARegistrationBean eventBean = event.getSLARegistrationBean();
+        Date startDate = DateUtils.parseDateUTC("2013-01-01T00:00Z");
+        // set multiple addresses as alert contact
+        eventBean.setAlertContact("[email protected], 
[email protected]");
+        event.setEventStatus(EventStatus.START_MISS);
+        event.setJobId("0000000-000000000000001-oozie-wrkf-C@1");
+        eventBean.setAppType(AppType.COORDINATOR_ACTION);
+        eventBean.setAppName("Test-SLA-Start-Miss");
+        eventBean.setUser("dummyuser");
+        eventBean.setExpectedStart(startDate);
+        eventBean.setNotificationMsg("notification of start miss");
+        eventBean.setAppType(AppType.COORDINATOR_ACTION);
+        event.setActualStart(DateUtils.parseDateUTC("2013-01-01T01:00Z"));
+
+        slaEmailListener.onStartMiss(event);
+
+        MimeMessage[] msgs = greenMail.getReceivedMessages();
+        MimeMessage msg = msgs[0];
+        assertEquals(msg.getFrom()[0].toString(), "oozie@localhost");
+        assertEquals(msg.getRecipients(RecipientType.TO)[0].toString(), 
"[email protected]");
+        assertEquals(msg.getRecipients(RecipientType.TO)[1].toString(), 
"[email protected]");
+    }
+
+    public void testBlackList() throws Exception {
+        String blackListedEmail = "[email protected]";
+        // add email to blacklist
+        slaEmailListener.addBlackList(blackListedEmail);
+        SLACalcStatus event = new SLACalcStatus(new SLARegistrationBean());
+        SLARegistrationBean eventBean = event.getSLARegistrationBean();
+        event.setEventStatus(EventStatus.START_MISS);
+        eventBean.setAlertContact(blackListedEmail);
+        eventBean.setAppType(AppType.COORDINATOR_ACTION);
+        eventBean.setAppName("Test-SLA-Start-Miss");
+        eventBean.setUser("dummyuser");
+        eventBean.setAlertContact("[email protected]");
+        event.setActualStart(DateUtils.parseDateUTC("2013-01-01T01:00Z"));
+
+        // blacklist blocks email from being sent out
+        slaEmailListener.onStartMiss(event);
+        MimeMessage[] msgs = greenMail.getReceivedMessages();
+        assertEquals(msgs.length, 0);
+
+        // wait 1.5 sec (cache timeout set to 1sec in test's setup)
+        Thread.sleep(1500);
+
+        // cache is evicted
+        slaEmailListener.onStartMiss(event);
+        msgs = greenMail.getReceivedMessages();
+        assertEquals(msgs.length, 1);
+    }
+}

Modified: oozie/trunk/release-log.txt
URL: 
http://svn.apache.org/viewvc/oozie/trunk/release-log.txt?rev=1488068&r1=1488067&r2=1488068&view=diff
==============================================================================
--- oozie/trunk/release-log.txt (original)
+++ oozie/trunk/release-log.txt Fri May 31 00:35:39 2013
@@ -1,5 +1,6 @@
 -- Oozie 4.1.0 release (trunk - unreleased)
 
+OOZIE-1294 SLA Email Notification (ryota via virag)
 OOZIE-1395 Using Yarn's CapacityScheduler causes some tests to time out 
(rkanter)
 OOZIE-1373 Oozie compilation fails with jdk7 (tucu, rohini via rohini)
 OOZIE-1296 SLA JMS Event Listener for publishing notifications related to SLA 
information (ryota via mona)


Reply via email to