1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18 package org.apache.tools.ant.listener;
19
20 import java.io.FileInputStream;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.PrintStream;
24 import java.util.Hashtable;
25 import java.util.Vector;
26 import java.util.Properties;
27 import java.util.Enumeration;
28 import java.util.StringTokenizer;
29
30 import org.apache.tools.ant.BuildEvent;
31 import org.apache.tools.ant.BuildException;
32 import org.apache.tools.ant.DefaultLogger;
33 import org.apache.tools.ant.Project;
34 import org.apache.tools.ant.taskdefs.email.EmailAddress;
35 import org.apache.tools.ant.taskdefs.email.Message;
36 import org.apache.tools.ant.taskdefs.email.Mailer;
37 import org.apache.tools.ant.util.ClasspathUtils;
38 import org.apache.tools.ant.util.DateUtils;
39 import org.apache.tools.ant.util.StringUtils;
40 import org.apache.tools.mail.MailMessage;
41
42 /**
43 * Buffers log messages from DefaultLogger, and sends an e-mail with the
44 * results. The following Project properties are used to send the mail.
45 * <ul>
46 * <li> MailLogger.mailhost [default: localhost] - Mail server to use</li>
47 * <li> MailLogger.port [default: 25] - Default port for SMTP </li>
48 * <li> MailLogger.from [required] - Mail "from" address</li>
49 * <li> MailLogger.failure.notify [default: true] - Send build failure
50 * e-mails?</li>
51 * <li> MailLogger.success.notify [default: true] - Send build success
52 * e-mails?</li>
53 * <li> MailLogger.failure.to [required if failure mail to be sent] - Address
54 * to send failure messages to</li>
55 * <li> MailLogger.success.to [required if success mail to be sent] - Address
56 * to send success messages to</li>
57 * <li> MailLogger.failure.subject [default: "Build Failure"] - Subject of
58 * failed build</li>
59 * <li> MailLogger.success.subject [default: "Build Success"] - Subject of
60 * successful build</li>
61 * </ul>
62 * These properties are set using standard Ant property setting mechanisms
63 * (<property>, command-line -D, etc). Ant properties can be overridden
64 * by specifying the filename of a properties file in the <i>
65 * MailLogger.properties.file property</i> . Any properties defined in that
66 * file will override Ant properties.
67 *
68 */
69 public class MailLogger extends DefaultLogger {
70 /** Buffer in which the message is constructed prior to sending */
71 private StringBuffer buffer = new StringBuffer();
72
73 /**
74 * Sends an e-mail with the log results.
75 *
76 * @param event the build finished event
77 */
78 public void buildFinished(BuildEvent event) {
79 super.buildFinished(event);
80
81 Project project = event.getProject();
82 Hashtable properties = project.getProperties();
83
84 // overlay specified properties file (if any), which overrides project
85 // settings
86 Properties fileProperties = new Properties();
87 String filename = (String) properties.get("MailLogger.properties.file");
88 if (filename != null) {
89 InputStream is = null;
90 try {
91 is = new FileInputStream(filename);
92 fileProperties.load(is);
93 } catch (IOException ioe) {
94 // ignore because properties file is not required
95 } finally {
96 if (is != null) {
97 try {
98 is.close();
99 } catch (IOException e) {
100 // ignore
101 }
102 }
103 }
104 }
105
106 for (Enumeration e = fileProperties.keys(); e.hasMoreElements();) {
107 String key = (String) e.nextElement();
108 String value = fileProperties.getProperty(key);
109 properties.put(key, project.replaceProperties(value));
110 }
111
112 boolean success = (event.getException() == null);
113 String prefix = success ? "success" : "failure";
114
115 try {
116 boolean notify = Project.toBoolean(getValue(properties,
117 prefix + ".notify", "on"));
118
119 if (!notify) {
120 return;
121 }
122 Values values = new Values()
123 .mailhost(getValue(properties, "mailhost", "localhost"))
124 .port(Integer.parseInt(
125 getValue(
126 properties, "port",
127 String.valueOf(MailMessage.DEFAULT_PORT))))
128 .user(getValue(properties, "user", ""))
129 .password(getValue(properties, "password", ""))
130 .ssl(Project.toBoolean(getValue(properties,
131 "ssl", "off")))
132 .from(getValue(properties, "from", null))
133 .replytoList(getValue(properties, "replyto", ""))
134 .toList(getValue(properties, prefix + ".to", null))
135 .subject(getValue(
136 properties, prefix + ".subject",
137 (success) ? "Build Success" : "Build Failure"));
138 if (values.user().equals("")
139 && values.password().equals("")
140 && !values.ssl()) {
141 sendMail(values, buffer.substring(0));
142 } else {
143 sendMimeMail(
144 event.getProject(), values, buffer.substring(0));
145 }
146 } catch (Exception e) {
147 System.out.println("MailLogger failed to send e-mail!");
148 e.printStackTrace(System.err);
149 }
150 }
151
152 private static class Values {
153 private String mailhost;
154 public String mailhost() {
155 return mailhost;
156 }
157 public Values mailhost(String mailhost) {
158 this.mailhost = mailhost;
159 return this;
160 }
161 private int port;
162 public int port() {
163 return port;
164 }
165 public Values port(int port) {
166 this.port = port;
167 return this;
168 }
169 private String user;
170 public String user() {
171 return user;
172 }
173 public Values user(String user) {
174 this.user = user;
175 return this;
176 }
177 private String password;
178 public String password() {
179 return password;
180 }
181 public Values password(String password) {
182 this.password = password;
183 return this;
184 }
185 private boolean ssl;
186 public boolean ssl() {
187 return ssl;
188 }
189 public Values ssl(boolean ssl) {
190 this.ssl = ssl;
191 return this;
192 }
193 private String from;
194 public String from() {
195 return from;
196 }
197 public Values from(String from) {
198 this.from = from;
199 return this;
200 }
201 private String replytoList;
202 public String replytoList() {
203 return replytoList;
204 }
205 public Values replytoList(String replytoList) {
206 this.replytoList = replytoList;
207 return this;
208 }
209 private String toList;
210 public String toList() {
211 return toList;
212 }
213 public Values toList(String toList) {
214 this.toList = toList;
215 return this;
216 }
217 private String subject;
218 public String subject() {
219 return subject;
220 }
221 public Values subject(String subject) {
222 this.subject = subject;
223 return this;
224 }
225 }
226
227 /**
228 * Receives and buffers log messages.
229 *
230 * @param message the message being logger
231 */
232 protected void log(String message) {
233 buffer.append(message).append(StringUtils.LINE_SEP);
234 }
235
236
237 /**
238 * Gets the value of a property.
239 *
240 * @param properties Properties to obtain value from
241 * @param name suffix of property name. "MailLogger." will be
242 * prepended internally.
243 * @param defaultValue value returned if not present in the properties.
244 * Set to null to make required.
245 * @return The value of the property, or default value.
246 * @exception Exception thrown if no default value is specified and the
247 * property is not present in properties.
248 */
249 private String getValue(Hashtable properties, String name,
250 String defaultValue) throws Exception {
251 String propertyName = "MailLogger." + name;
252 String value = (String) properties.get(propertyName);
253
254 if (value == null) {
255 value = defaultValue;
256 }
257
258 if (value == null) {
259 throw new Exception("Missing required parameter: " + propertyName);
260 }
261
262 return value;
263 }
264
265
266 /**
267 * Send the mail
268 * @param values the various values.
269 * @param message mail body
270 * @exception IOException thrown if sending message fails
271 */
272 private void sendMail(Values values, String message) throws IOException {
273 MailMessage mailMessage = new MailMessage(
274 values.mailhost(), values.port());
275 mailMessage.setHeader("Date", DateUtils.getDateForHeader());
276
277 mailMessage.from(values.from());
278 if (!values.replytoList().equals("")) {
279 StringTokenizer t = new StringTokenizer(
280 values.replytoList(), ", ", false);
281 while (t.hasMoreTokens()) {
282 mailMessage.replyto(t.nextToken());
283 }
284 }
285 StringTokenizer t = new StringTokenizer(values.toList(), ", ", false);
286 while (t.hasMoreTokens()) {
287 mailMessage.to(t.nextToken());
288 }
289
290 mailMessage.setSubject(values.subject());
291
292 PrintStream ps = mailMessage.getPrintStream();
293 ps.println(message);
294
295 mailMessage.sendAndClose();
296 }
297 /**
298 * Send the mail (MimeMail)
299 * @param project current ant project
300 * @param values various values
301 * @param message mail body
302 */
303 private void sendMimeMail(Project project, Values values, String message) {
304 // convert the replyTo string into a vector of emailaddresses
305 Mailer mailer = null;
306 try {
307 mailer = (Mailer) ClasspathUtils.newInstance(
308 "org.apache.tools.ant.taskdefs.email.MimeMailer",
309 MailLogger.class.getClassLoader(), Mailer.class);
310 } catch (BuildException e) {
311 Throwable t = e.getCause() == null ? e : e.getCause();
312 log("Failed to initialise MIME mail: " + t.getMessage());
313 return;
314 }
315 Vector replyToList = vectorizeEmailAddresses(values.replytoList());
316 mailer.setHost(values.mailhost());
317 mailer.setPort(values.port());
318 mailer.setUser(values.user());
319 mailer.setPassword(values.password());
320 mailer.setSSL(values.ssl());
321 Message mymessage = new Message(message);
322 mymessage.setProject(project);
323 mailer.setMessage(mymessage);
324 mailer.setFrom(new EmailAddress(values.from()));
325 mailer.setReplyToList(replyToList);
326 Vector toList = vectorizeEmailAddresses(values.toList());
327 mailer.setToList(toList);
328 mailer.setCcList(new Vector());
329 mailer.setBccList(new Vector());
330 mailer.setFiles(new Vector());
331 mailer.setSubject(values.subject());
332 mailer.send();
333 }
334 private Vector vectorizeEmailAddresses(String listString) {
335 Vector emailList = new Vector();
336 StringTokenizer tokens = new StringTokenizer(listString, ",");
337 while (tokens.hasMoreTokens()) {
338 emailList.addElement(new EmailAddress(tokens.nextToken()));
339 }
340 return emailList;
341 }
342 }
343
344