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