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 String smtpPort = getOozieConf().get("oozie.email.smtp.port", "25");
117 Boolean smtpAuth = getOozieConf().getBoolean("oozie.email.smtp.auth", false);
118 String smtpUser = getOozieConf().get("oozie.email.smtp.username", "");
119 String smtpPassword = getOozieConf().get("oozie.email.smtp.password", "");
120 String fromAddr = getOozieConf().get("oozie.email.from.address");
121
122 Properties properties = new Properties();
123 properties.setProperty("mail.smtp.host", smtpHost);
124 properties.setProperty("mail.smtp.port", smtpPort);
125 properties.setProperty("mail.smtp.auth", smtpAuth.toString());
126
127 Session session;
128 // Do not use default instance (i.e. Session.getDefaultInstance)
129 // (cause it may lead to issues when used second time).
130 if (!smtpAuth) {
131 session = Session.getInstance(properties);
132 } else {
133 session = Session.getInstance(properties, new JavaMailAuthenticator(smtpUser, smtpPassword));
134 }
135
136 Message message = new MimeMessage(session);
137 InternetAddress from;
138 List<InternetAddress> toAddrs = new ArrayList<InternetAddress>(to.length);
139 List<InternetAddress> ccAddrs = new ArrayList<InternetAddress>(cc.length);
140
141 try {
142 from = new InternetAddress(fromAddr);
143 message.setFrom(from);
144 } catch (AddressException e) {
145 throw new ActionExecutorException(ErrorType.ERROR, "EM002", "Bad from address specified in ${oozie.email.from.address}.", e);
146 } catch (MessagingException e) {
147 throw new ActionExecutorException(ErrorType.ERROR, "EM003", "Error setting a from address in the message.", e);
148 }
149
150 try {
151 // Add all <to>
152 for (String toStr : to) {
153 toAddrs.add(new InternetAddress(toStr.trim()));
154 }
155 message.addRecipients(RecipientType.TO, toAddrs.toArray(new InternetAddress[0]));
156
157 // Add all <cc>
158 for (String ccStr : cc) {
159 ccAddrs.add(new InternetAddress(ccStr.trim()));
160 }
161 message.addRecipients(RecipientType.CC, ccAddrs.toArray(new InternetAddress[0]));
162
163 // Set subject, and plain-text body.
164 message.setSubject(subject);
165 message.setContent(body, "text/plain");
166 } catch (AddressException e) {
167 throw new ActionExecutorException(ErrorType.ERROR, "EM004", "Bad address format in <to> or <cc>.", e);
168 } catch (MessagingException e) {
169 throw new ActionExecutorException(ErrorType.ERROR, "EM005", "An error occured while adding recipients.", e);
170 }
171
172 try {
173 // Send over SMTP Transport
174 // (Session+Message has adequate details.)
175 Transport.send(message);
176 } catch (NoSuchProviderException e) {
177 throw new ActionExecutorException(ErrorType.ERROR, "EM006", "Could not find an SMTP transport provider to email.", e);
178 } catch (MessagingException e) {
179 throw new ActionExecutorException(ErrorType.ERROR, "EM007", "Encountered an error while sending the email message over SMTP.", e);
180 }
181 }
182
183 @Override
184 public void end(Context context, WorkflowAction action) throws ActionExecutorException {
185 String externalStatus = action.getExternalStatus();
186 WorkflowAction.Status status = externalStatus.equals("OK") ? WorkflowAction.Status.OK :
187 WorkflowAction.Status.ERROR;
188 context.setEndData(status, getActionSignal(status));
189 }
190
191 @Override
192 public void check(Context context, WorkflowAction action)
193 throws ActionExecutorException {
194
195 }
196
197 @Override
198 public void kill(Context context, WorkflowAction action)
199 throws ActionExecutorException {
200
201 }
202
203 @Override
204 public boolean isCompleted(String externalStatus) {
205 return true;
206 }
207
208 private static class JavaMailAuthenticator extends Authenticator {
209
210 String user;
211 String password;
212
213 public JavaMailAuthenticator(String user, String password) {
214 this.user = user;
215 this.password = password;
216 }
217
218 @Override
219 protected PasswordAuthentication getPasswordAuthentication() {
220 return new PasswordAuthentication(user, password);
221 }
222 }
223 }