This project has retired. For details please refer to its
Attic page.
001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 package org.apache.oozie.action.email;
019
020 import java.util.ArrayList;
021 import java.util.List;
022 import java.util.Properties;
023
024 import javax.mail.Authenticator;
025 import javax.mail.Message;
026 import javax.mail.Message.RecipientType;
027 import javax.mail.MessagingException;
028 import javax.mail.NoSuchProviderException;
029 import javax.mail.PasswordAuthentication;
030 import javax.mail.Session;
031 import javax.mail.Transport;
032 import javax.mail.internet.AddressException;
033 import javax.mail.internet.InternetAddress;
034 import javax.mail.internet.MimeMessage;
035
036 import org.apache.oozie.action.ActionExecutor;
037 import org.apache.oozie.action.ActionExecutorException;
038 import org.apache.oozie.action.ActionExecutorException.ErrorType;
039 import org.apache.oozie.client.WorkflowAction;
040 import org.apache.oozie.util.XmlUtils;
041 import org.jdom.Element;
042 import org.jdom.Namespace;
043
044 /**
045 * Email action executor. It takes to, cc addresses along with a subject and body and sends
046 * out an email.
047 */
048 public class EmailActionExecutor extends ActionExecutor {
049
050 public static final String CONF_PREFIX = "oozie.email.";
051 public static final String EMAIL_SMTP_HOST = CONF_PREFIX + "smtp.host";
052 public static final String EMAIL_SMTP_PORT = CONF_PREFIX + "smtp.port";
053 public static final String EMAIL_SMTP_AUTH = CONF_PREFIX + "smtp.auth";
054 public static final String EMAIL_SMTP_USER = CONF_PREFIX + "smtp.username";
055 public static final String EMAIL_SMTP_PASS = CONF_PREFIX + "smtp.password";
056 public static final String EMAIL_SMTP_FROM = CONF_PREFIX + "from.address";
057
058 private final static String TO = "to";
059 private final static String CC = "cc";
060 private final static String SUB = "subject";
061 private final static String BOD = "body";
062 private final static String COMMA = ",";
063
064 public EmailActionExecutor() {
065 super("email");
066 }
067
068 @Override
069 public void initActionType() {
070 super.initActionType();
071 }
072
073 @Override
074 public void start(Context context, WorkflowAction action) throws ActionExecutorException {
075 try {
076 context.setStartData("-", "-", "-");
077 Element actionXml = XmlUtils.parseXml(action.getConf());
078 validateAndMail(context, actionXml);
079 context.setExecutionData("OK", null);
080 }
081 catch (Exception ex) {
082 throw convertException(ex);
083 }
084 }
085
086 @SuppressWarnings("unchecked")
087 protected void validateAndMail(Context context, Element element) throws ActionExecutorException {
088 // The XSD does the min/max occurrence validation for us.
089 Namespace ns = Namespace.getNamespace("uri:oozie:email-action:0.1");
090 String tos[] = new String[0];
091 String ccs[] = new String[0];
092 String subject = "";
093 String body = "";
094 Element child = null;
095
096 // <to> - One ought to exist.
097 String text = element.getChildTextTrim(TO, ns);
098 if (text.isEmpty()) {
099 throw new ActionExecutorException(ErrorType.ERROR, "EM001", "No receipents were specified in the to-address field.");
100 }
101 tos = text.split(COMMA);
102
103 // <cc> - Optional, but only one ought to exist.
104 try {
105 ccs = element.getChildTextTrim(CC, ns).split(COMMA);
106 } catch (Exception e) {
107 // It is alright for cc to be given empty or not be present.
108 ccs = new String[0];
109 }
110
111 // <subject> - One ought to exist.
112 subject = element.getChildTextTrim(SUB, ns);
113
114 // <body> - One ought to exist.
115 body = element.getChildTextTrim(BOD, ns);
116
117 // All good - lets try to mail!
118 email(context, tos, ccs, subject, body);
119 }
120
121 protected void email(Context context, String[] to, String[] cc, String subject, String body) throws ActionExecutorException {
122 // Get mailing server details.
123 String smtpHost = getOozieConf().get(EMAIL_SMTP_HOST, "localhost");
124 String smtpPort = getOozieConf().get(EMAIL_SMTP_PORT, "25");
125 Boolean smtpAuth = getOozieConf().getBoolean(EMAIL_SMTP_AUTH, false);
126 String smtpUser = getOozieConf().get(EMAIL_SMTP_USER, "");
127 String smtpPassword = getOozieConf().get(EMAIL_SMTP_PASS, "");
128 String fromAddr = getOozieConf().get(EMAIL_SMTP_FROM, "oozie@localhost");
129
130 Properties properties = new Properties();
131 properties.setProperty("mail.smtp.host", smtpHost);
132 properties.setProperty("mail.smtp.port", smtpPort);
133 properties.setProperty("mail.smtp.auth", smtpAuth.toString());
134
135 Session session;
136 // Do not use default instance (i.e. Session.getDefaultInstance)
137 // (cause it may lead to issues when used second time).
138 if (!smtpAuth) {
139 session = Session.getInstance(properties);
140 } else {
141 session = Session.getInstance(properties, new JavaMailAuthenticator(smtpUser, smtpPassword));
142 }
143
144 Message message = new MimeMessage(session);
145 InternetAddress from;
146 List<InternetAddress> toAddrs = new ArrayList<InternetAddress>(to.length);
147 List<InternetAddress> ccAddrs = new ArrayList<InternetAddress>(cc.length);
148
149 try {
150 from = new InternetAddress(fromAddr);
151 message.setFrom(from);
152 } catch (AddressException e) {
153 throw new ActionExecutorException(ErrorType.ERROR, "EM002", "Bad from address specified in ${oozie.email.from.address}.", e);
154 } catch (MessagingException e) {
155 throw new ActionExecutorException(ErrorType.ERROR, "EM003", "Error setting a from address in the message.", e);
156 }
157
158 try {
159 // Add all <to>
160 for (String toStr : to) {
161 toAddrs.add(new InternetAddress(toStr.trim()));
162 }
163 message.addRecipients(RecipientType.TO, toAddrs.toArray(new InternetAddress[0]));
164
165 // Add all <cc>
166 for (String ccStr : cc) {
167 ccAddrs.add(new InternetAddress(ccStr.trim()));
168 }
169 message.addRecipients(RecipientType.CC, ccAddrs.toArray(new InternetAddress[0]));
170
171 // Set subject, and plain-text body.
172 message.setSubject(subject);
173 message.setContent(body, "text/plain");
174 } catch (AddressException e) {
175 throw new ActionExecutorException(ErrorType.ERROR, "EM004", "Bad address format in <to> or <cc>.", e);
176 } catch (MessagingException e) {
177 throw new ActionExecutorException(ErrorType.ERROR, "EM005", "An error occured while adding recipients.", e);
178 }
179
180 try {
181 // Send over SMTP Transport
182 // (Session+Message has adequate details.)
183 Transport.send(message);
184 } catch (NoSuchProviderException e) {
185 throw new ActionExecutorException(ErrorType.ERROR, "EM006", "Could not find an SMTP transport provider to email.", e);
186 } catch (MessagingException e) {
187 throw new ActionExecutorException(ErrorType.ERROR, "EM007", "Encountered an error while sending the email message over SMTP.", e);
188 }
189 }
190
191 @Override
192 public void end(Context context, WorkflowAction action) throws ActionExecutorException {
193 String externalStatus = action.getExternalStatus();
194 WorkflowAction.Status status = externalStatus.equals("OK") ? WorkflowAction.Status.OK :
195 WorkflowAction.Status.ERROR;
196 context.setEndData(status, getActionSignal(status));
197 }
198
199 @Override
200 public void check(Context context, WorkflowAction action)
201 throws ActionExecutorException {
202
203 }
204
205 @Override
206 public void kill(Context context, WorkflowAction action)
207 throws ActionExecutorException {
208
209 }
210
211 @Override
212 public boolean isCompleted(String externalStatus) {
213 return true;
214 }
215
216 public static class JavaMailAuthenticator extends Authenticator {
217
218 String user;
219 String password;
220
221 public JavaMailAuthenticator(String user, String password) {
222 this.user = user;
223 this.password = password;
224 }
225
226 @Override
227 protected PasswordAuthentication getPasswordAuthentication() {
228 return new PasswordAuthentication(user, password);
229 }
230 }
231 }