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 private final static String TO = "to";
051 private final static String CC = "cc";
052 private final static String SUB = "subject";
053 private final static String BOD = "body";
054 private final static String COMMA = ",";
055
056 public EmailActionExecutor() {
057 super("email");
058 }
059
060 @Override
061 public void initActionType() {
062 super.initActionType();
063 }
064
065 @Override
066 public void start(Context context, WorkflowAction action) throws ActionExecutorException {
067 try {
068 context.setStartData("-", "-", "-");
069 Element actionXml = XmlUtils.parseXml(action.getConf());
070 validateAndMail(context, actionXml);
071 context.setExecutionData("OK", null);
072 }
073 catch (Exception ex) {
074 throw convertException(ex);
075 }
076 }
077
078 @SuppressWarnings("unchecked")
079 protected void validateAndMail(Context context, Element element) throws ActionExecutorException {
080 // The XSD does the min/max occurrence validation for us.
081 Namespace ns = Namespace.getNamespace("uri:oozie:email-action:0.1");
082 String tos[] = new String[0];
083 String ccs[] = new String[0];
084 String subject = "";
085 String body = "";
086 Element child = null;
087
088 // <to> - One ought to exist.
089 String text = element.getChildTextTrim(TO, ns);
090 if (text.isEmpty()) {
091 throw new ActionExecutorException(ErrorType.ERROR, "EM001", "No receipents were specified in the to-address field.");
092 }
093 tos = text.split(COMMA);
094
095 // <cc> - Optional, but only one ought to exist.
096 try {
097 ccs = element.getChildTextTrim(CC, ns).split(COMMA);
098 } catch (Exception e) {
099 // It is alright for cc to be given empty or not be present.
100 ccs = new String[0];
101 }
102
103 // <subject> - One ought to exist.
104 subject = element.getChildTextTrim(SUB, ns);
105
106 // <body> - One ought to exist.
107 body = element.getChildTextTrim(BOD, ns);
108
109 // All good - lets try to mail!
110 email(context, tos, ccs, subject, body);
111 }
112
113 protected void email(Context context, String[] to, String[] cc, String subject, String body) throws ActionExecutorException {
114 // Get mailing server details.
115 String smtpHost = getOozieConf().get("oozie.email.smtp.host",
116 "localhost");
117 String smtpPort = getOozieConf().get("oozie.email.smtp.port", "25");
118 Boolean smtpAuth = getOozieConf().getBoolean("oozie.email.smtp.auth", false);
119 String smtpUser = getOozieConf().get("oozie.email.smtp.username", "");
120 String smtpPassword = getOozieConf().get("oozie.email.smtp.password", "");
121 String fromAddr = getOozieConf().get("oozie.email.from.address",
122 "oozie@localhost");
123
124 Properties properties = new Properties();
125 properties.setProperty("mail.smtp.host", smtpHost);
126 properties.setProperty("mail.smtp.port", smtpPort);
127 properties.setProperty("mail.smtp.auth", smtpAuth.toString());
128
129 Session session;
130 // Do not use default instance (i.e. Session.getDefaultInstance)
131 // (cause it may lead to issues when used second time).
132 if (!smtpAuth) {
133 session = Session.getInstance(properties);
134 } else {
135 session = Session.getInstance(properties, new JavaMailAuthenticator(smtpUser, smtpPassword));
136 }
137
138 Message message = new MimeMessage(session);
139 InternetAddress from;
140 List<InternetAddress> toAddrs = new ArrayList<InternetAddress>(to.length);
141 List<InternetAddress> ccAddrs = new ArrayList<InternetAddress>(cc.length);
142
143 try {
144 from = new InternetAddress(fromAddr);
145 message.setFrom(from);
146 } catch (AddressException e) {
147 throw new ActionExecutorException(ErrorType.ERROR, "EM002", "Bad from address specified in ${oozie.email.from.address}.", e);
148 } catch (MessagingException e) {
149 throw new ActionExecutorException(ErrorType.ERROR, "EM003", "Error setting a from address in the message.", e);
150 }
151
152 try {
153 // Add all <to>
154 for (String toStr : to) {
155 toAddrs.add(new InternetAddress(toStr.trim()));
156 }
157 message.addRecipients(RecipientType.TO, toAddrs.toArray(new InternetAddress[0]));
158
159 // Add all <cc>
160 for (String ccStr : cc) {
161 ccAddrs.add(new InternetAddress(ccStr.trim()));
162 }
163 message.addRecipients(RecipientType.CC, ccAddrs.toArray(new InternetAddress[0]));
164
165 // Set subject, and plain-text body.
166 message.setSubject(subject);
167 message.setContent(body, "text/plain");
168 } catch (AddressException e) {
169 throw new ActionExecutorException(ErrorType.ERROR, "EM004", "Bad address format in <to> or <cc>.", e);
170 } catch (MessagingException e) {
171 throw new ActionExecutorException(ErrorType.ERROR, "EM005", "An error occured while adding recipients.", e);
172 }
173
174 try {
175 // Send over SMTP Transport
176 // (Session+Message has adequate details.)
177 Transport.send(message);
178 } catch (NoSuchProviderException e) {
179 throw new ActionExecutorException(ErrorType.ERROR, "EM006", "Could not find an SMTP transport provider to email.", e);
180 } catch (MessagingException e) {
181 throw new ActionExecutorException(ErrorType.ERROR, "EM007", "Encountered an error while sending the email message over SMTP.", e);
182 }
183 }
184
185 @Override
186 public void end(Context context, WorkflowAction action) throws ActionExecutorException {
187 String externalStatus = action.getExternalStatus();
188 WorkflowAction.Status status = externalStatus.equals("OK") ? WorkflowAction.Status.OK :
189 WorkflowAction.Status.ERROR;
190 context.setEndData(status, getActionSignal(status));
191 }
192
193 @Override
194 public void check(Context context, WorkflowAction action)
195 throws ActionExecutorException {
196
197 }
198
199 @Override
200 public void kill(Context context, WorkflowAction action)
201 throws ActionExecutorException {
202
203 }
204
205 @Override
206 public boolean isCompleted(String externalStatus) {
207 return true;
208 }
209
210 private static class JavaMailAuthenticator extends Authenticator {
211
212 String user;
213 String password;
214
215 public JavaMailAuthenticator(String user, String password) {
216 this.user = user;
217 this.password = password;
218 }
219
220 @Override
221 protected PasswordAuthentication getPasswordAuthentication() {
222 return new PasswordAuthentication(user, password);
223 }
224 }
225 }