1 package hudson.tasks; 2 3 import hudson.model.AbstractBuild; 4 import hudson.model.BuildListener; 5 import hudson.model.Result; 6 import hudson.model.User; 7 import hudson.model.AbstractProject; 8 import hudson.Util; 9 import hudson.FilePath; 10 import hudson.scm.ChangeLogSet; 11 import hudson.scm.ChangeLogSet.Entry; 12 13 import javax.mail.internet.MimeMessage ; 14 import javax.mail.internet.InternetAddress ; 15 import javax.mail.Address ; 16 import javax.mail.Transport ; 17 import javax.mail.MessagingException ; 18 import javax.mail.Message ; 19 import java.util.regex.Pattern ; 20 import java.util.regex.Matcher ; 21 import java.util.Date ; 22 import java.util.List ; 23 import java.util.ArrayList ; 24 import java.util.StringTokenizer ; 25 import java.util.Set ; 26 import java.util.HashSet ; 27 import java.util.logging.Logger ; 28 import java.io.File ; 29 import java.io.IOException ; 30 import java.io.StringWriter ; 31 import java.io.PrintWriter ; 32 33 39 public class MailSender<P extends AbstractProject<P,B>,B extends AbstractBuild<P,B>> { 40 43 private String recipients; 44 45 48 private boolean dontNotifyEveryUnstableBuild; 49 50 53 private boolean sendToIndividuals; 54 55 56 public MailSender(String recipients, boolean dontNotifyEveryUnstableBuild, boolean sendToIndividuals) { 57 this.recipients = recipients; 58 this.dontNotifyEveryUnstableBuild = dontNotifyEveryUnstableBuild; 59 this.sendToIndividuals = sendToIndividuals; 60 } 61 62 public boolean execute(B build, BuildListener listener) throws InterruptedException { 63 try { 64 MimeMessage mail = getMail(build, listener); 65 if(mail!=null) { 66 Address [] allRecipients = mail.getAllRecipients(); 67 if(allRecipients!=null) { 68 StringBuffer buf = new StringBuffer ("Sending e-mails to:"); 69 for (Address a : allRecipients) 70 buf.append(' ').append(a); 71 listener.getLogger().println(buf); 72 Transport.send(mail); 73 } else { 74 listener.getLogger().println("An attempt to send an e-mail" 75 + " to empty list of recipients, ignored."); 76 } 77 } 78 } catch (MessagingException e) { 79 e.printStackTrace( listener.error(e.getMessage()) ); 80 } 81 82 return true; 83 } 84 85 private MimeMessage getMail(B build, BuildListener listener) throws MessagingException , InterruptedException { 86 if(build.getResult()== Result.FAILURE) { 87 return createFailureMail(build, listener); 88 } 89 90 if(build.getResult()==Result.UNSTABLE) { 91 B prev = build.getPreviousBuild(); 92 if(!dontNotifyEveryUnstableBuild) 93 return createUnstableMail(build, listener); 94 if(prev!=null) { 95 if(prev.getResult()==Result.SUCCESS) 96 return createUnstableMail(build, listener); 97 } 98 } 99 100 if(build.getResult()==Result.SUCCESS) { 101 B prev = build.getPreviousBuild(); 102 if(prev!=null) { 103 if(prev.getResult()==Result.FAILURE) 104 return createBackToNormalMail(build, "normal", listener); 105 if(prev.getResult()==Result.UNSTABLE) 106 return createBackToNormalMail(build, "stable", listener); 107 } 108 } 109 110 return null; 111 } 112 113 private MimeMessage createBackToNormalMail(B build, String subject, BuildListener listener) throws MessagingException { 114 MimeMessage msg = createEmptyMail(build, listener); 115 116 msg.setSubject(getSubject(build,"Hudson build is back to "+subject +": ")); 117 StringBuffer buf = new StringBuffer (); 118 appendBuildUrl(build,buf); 119 msg.setText(buf.toString()); 120 121 return msg; 122 } 123 124 private MimeMessage createUnstableMail(B build, BuildListener listener) throws MessagingException { 125 MimeMessage msg = createEmptyMail(build, listener); 126 127 msg.setSubject(getSubject(build,"Hudson build became unstable: ")); 128 StringBuffer buf = new StringBuffer (); 129 appendBuildUrl(build,buf); 130 msg.setText(buf.toString()); 131 132 return msg; 133 } 134 135 private void appendBuildUrl(B build, StringBuffer buf) { 136 String baseUrl = Mailer.DESCRIPTOR.getUrl(); 137 if(baseUrl!=null) { 138 buf.append("See ").append(baseUrl).append(Util.encode(build.getUrl())).append("\n\n"); 139 } 140 } 141 142 private MimeMessage createFailureMail(B build, BuildListener listener) throws MessagingException , InterruptedException { 143 MimeMessage msg = createEmptyMail(build, listener); 144 145 msg.setSubject(getSubject(build, "Build failed in Hudson: ")); 146 147 StringBuffer buf = new StringBuffer (); 148 appendBuildUrl(build,buf); 149 150 boolean firstChange = true; 151 for (ChangeLogSet.Entry entry : build.getChangeSet()) { 152 if (firstChange) { 153 firstChange = false; 154 buf.append("Changes:\n\n"); 155 } 156 buf.append('['); 157 buf.append(entry.getAuthor().getFullName()); 158 buf.append("] "); 159 String m = entry.getMsg(); 160 buf.append(m); 161 if (!m.endsWith("\n")) { 162 buf.append('\n'); 163 } 164 buf.append('\n'); 165 } 166 167 buf.append("------------------------------------------\n"); 168 169 try { 170 String log = build.getLog(); 171 String [] lines = log.split("\n"); 172 int start = 0; 173 if (lines.length > MAX_LOG_LINES) { 174 buf.append("[...truncated " + (lines.length - MAX_LOG_LINES) + " lines...]\n"); 177 start = lines.length - MAX_LOG_LINES; 178 } 179 String workspaceUrl = null, artifactUrl = null; 180 Pattern wsPattern = null; 181 String baseUrl = Mailer.DESCRIPTOR.getUrl(); 182 if (baseUrl != null) { 183 workspaceUrl = baseUrl + Util.encode(build.getProject().getUrl()) + "ws/"; 187 artifactUrl = baseUrl + Util.encode(build.getUrl()) + "artifact/"; 188 FilePath ws = build.getProject().getWorkspace(); 189 wsPattern = Pattern.compile("(" + 199 quoteRegexp(ws.getRemote()) + "|" + quoteRegexp(ws.toURI().toString()) + ")[/\\\\]?([^:#\\s]*)"); 200 } 201 for (int i = start; i < lines.length; i++) { 202 String line = lines[i]; 203 if (wsPattern != null) { 204 Matcher m = wsPattern.matcher(line); 206 int pos = 0; 207 while (m.find(pos)) { 208 String path = m.group(2).replace(File.separatorChar, '/'); 209 String linkUrl = artifactMatches(path, build) ? artifactUrl : workspaceUrl; 210 String prefix = line.substring(0, m.start()) + linkUrl + Util.encode(path) + ' '; 212 pos = prefix.length(); 213 line = prefix + line.substring(m.end()); 214 m = wsPattern.matcher(line); 216 } 217 } 218 buf.append(line); 219 buf.append('\n'); 220 } 221 } catch (IOException e) { 222 StringWriter sw = new StringWriter (); 224 e.printStackTrace(new PrintWriter (sw)); 225 buf.append("Failed to access build log\n\n").append(sw); 226 } 227 228 msg.setText(buf.toString()); 229 230 return msg; 231 } 232 233 private MimeMessage createEmptyMail(B build, BuildListener listener) throws MessagingException { 234 MimeMessage msg = new MimeMessage (Mailer.DESCRIPTOR.createSession()); 235 msg.setContent("","text/plain"); 238 msg.setFrom(new InternetAddress (Mailer.DESCRIPTOR.getAdminAddress())); 239 msg.setSentDate(new Date ()); 240 241 List <InternetAddress > rcp = new ArrayList <InternetAddress >(); 242 StringTokenizer tokens = new StringTokenizer (recipients); 243 while(tokens.hasMoreTokens()) 244 rcp.add(new InternetAddress (tokens.nextToken())); 245 if(sendToIndividuals) { 246 Set<User> users = new HashSet <User>(); 247 for (Entry change : build.getChangeSet()) { 248 User a = change.getAuthor(); 249 if(users.add(a)) { 250 String adrs = a.getProperty(Mailer.UserProperty.class).getAddress(); 251 if(adrs!=null) 252 rcp.add(new InternetAddress (adrs)); 253 else { 254 listener.getLogger().println("Failed to send e-mail to "+a.getFullName()+" because no e-mail address is known, and no default e-mail domain is configured"); 255 } 256 } 257 } 258 } 259 msg.setRecipients(Message.RecipientType.TO, rcp.toArray(new InternetAddress [rcp.size()])); 260 return msg; 261 } 262 263 private String getSubject(B build, String caption) { 264 return caption +build.getProject().getName()+" #"+build.getNumber(); 265 } 266 267 270 private static String quoteRegexp(String s) { 271 int slashEIndex = s.indexOf("\\E"); 272 if (slashEIndex == -1) 273 return "\\Q" + s + "\\E"; 274 275 StringBuilder sb = new StringBuilder (s.length() * 2); 276 sb.append("\\Q"); 277 int current = 0; 278 while ((slashEIndex = s.indexOf("\\E", current)) != -1) { 279 sb.append(s.substring(current, slashEIndex)); 280 current = slashEIndex + 2; 281 sb.append("\\E\\\\E\\Q"); 282 } 283 sb.append(s.substring(current, s.length())); 284 sb.append("\\E"); 285 return sb.toString(); 286 } 287 288 289 protected boolean artifactMatches(String path, B build) { 290 return false; 291 } 292 293 294 private static final Logger LOGGER = Logger.getLogger(MailSender.class.getName()); 295 296 private static final int MAX_LOG_LINES = 250; 297 } 298 | Popular Tags |