KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > hudson > tasks > MailSender


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 JavaDoc;
14 import javax.mail.internet.InternetAddress JavaDoc;
15 import javax.mail.Address JavaDoc;
16 import javax.mail.Transport JavaDoc;
17 import javax.mail.MessagingException JavaDoc;
18 import javax.mail.Message JavaDoc;
19 import java.util.regex.Pattern JavaDoc;
20 import java.util.regex.Matcher JavaDoc;
21 import java.util.Date JavaDoc;
22 import java.util.List JavaDoc;
23 import java.util.ArrayList JavaDoc;
24 import java.util.StringTokenizer JavaDoc;
25 import java.util.Set JavaDoc;
26 import java.util.HashSet JavaDoc;
27 import java.util.logging.Logger JavaDoc;
28 import java.io.File JavaDoc;
29 import java.io.IOException JavaDoc;
30 import java.io.StringWriter JavaDoc;
31 import java.io.PrintWriter JavaDoc;
32
33 /**
34  * Core logic of sending out notification e-mail.
35  *
36  * @author Jesse Glick
37  * @author Kohsuke Kawaguchi
38  */

39 public class MailSender<P extends AbstractProject<P,B>,B extends AbstractBuild<P,B>> {
40     /**
41      * Whitespace-separated list of e-mail addresses that represent recipients.
42      */

43     private String JavaDoc recipients;
44
45     /**
46      * If true, only the first unstable build will be reported.
47      */

48     private boolean dontNotifyEveryUnstableBuild;
49
50     /**
51      * If true, individuals will receive e-mails regarding who broke the build.
52      */

53     private boolean sendToIndividuals;
54
55
56     public MailSender(String JavaDoc 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 JavaDoc {
63         try {
64             MimeMessage JavaDoc mail = getMail(build, listener);
65             if(mail!=null) {
66                 Address JavaDoc[] allRecipients = mail.getAllRecipients();
67                 if(allRecipients!=null) {
68                 StringBuffer JavaDoc buf = new StringBuffer JavaDoc("Sending e-mails to:");
69                     for (Address JavaDoc 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 JavaDoc e) {
79             e.printStackTrace( listener.error(e.getMessage()) );
80         }
81
82         return true;
83     }
84
85     private MimeMessage JavaDoc getMail(B build, BuildListener listener) throws MessagingException JavaDoc, InterruptedException JavaDoc {
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 JavaDoc createBackToNormalMail(B build, String JavaDoc subject, BuildListener listener) throws MessagingException JavaDoc {
114         MimeMessage JavaDoc msg = createEmptyMail(build, listener);
115
116         msg.setSubject(getSubject(build,"Hudson build is back to "+subject +": "));
117         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
118         appendBuildUrl(build,buf);
119         msg.setText(buf.toString());
120
121         return msg;
122     }
123
124     private MimeMessage JavaDoc createUnstableMail(B build, BuildListener listener) throws MessagingException JavaDoc {
125         MimeMessage JavaDoc msg = createEmptyMail(build, listener);
126
127         msg.setSubject(getSubject(build,"Hudson build became unstable: "));
128         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
129         appendBuildUrl(build,buf);
130         msg.setText(buf.toString());
131
132         return msg;
133     }
134
135     private void appendBuildUrl(B build, StringBuffer JavaDoc buf) {
136         String JavaDoc 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 JavaDoc createFailureMail(B build, BuildListener listener) throws MessagingException JavaDoc, InterruptedException JavaDoc {
143         MimeMessage JavaDoc msg = createEmptyMail(build, listener);
144
145         msg.setSubject(getSubject(build, "Build failed in Hudson: "));
146
147         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
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 JavaDoc 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 JavaDoc log = build.getLog();
171             String JavaDoc[] lines = log.split("\n");
172             int start = 0;
173             if (lines.length > MAX_LOG_LINES) {
174                 // Avoid sending enormous logs over email.
175
// Interested users can always look at the log on the web server.
176
buf.append("[...truncated " + (lines.length - MAX_LOG_LINES) + " lines...]\n");
177                 start = lines.length - MAX_LOG_LINES;
178             }
179             String JavaDoc workspaceUrl = null, artifactUrl = null;
180             Pattern JavaDoc wsPattern = null;
181             String JavaDoc baseUrl = Mailer.DESCRIPTOR.getUrl();
182             if (baseUrl != null) {
183                 // Hyperlink local file paths to the repository workspace or build artifacts.
184
// Note that it is possible for a failure mail to refer to a file using a workspace
185
// URL which has already been corrected in a subsequent build. To fix, archive.
186
workspaceUrl = baseUrl + Util.encode(build.getProject().getUrl()) + "ws/";
187                 artifactUrl = baseUrl + Util.encode(build.getUrl()) + "artifact/";
188                 FilePath ws = build.getProject().getWorkspace();
189                 // Match either file or URL patterns, i.e. either
190
// c:\hudson\workdir\jobs\foo\workspace\src\Foo.java
191
// file:/c:/hudson/workdir/jobs/foo/workspace/src/Foo.java
192
// will be mapped to one of:
193
// http://host/hudson/job/foo/ws/src/Foo.java
194
// http://host/hudson/job/foo/123/artifact/src/Foo.java
195
// Careful with path separator between $1 and $2:
196
// workspaceDir will not normally end with one;
197
// workspaceDir.toURI() will end with '/' if and only if workspaceDir.exists() at time of call
198
wsPattern = Pattern.compile("(" +
199                     quoteRegexp(ws.getRemote()) + "|" + quoteRegexp(ws.toURI().toString()) + ")[/\\\\]?([^:#\\s]*)");
200             }
201             for (int i = start; i < lines.length; i++) {
202                 String JavaDoc line = lines[i];
203                 if (wsPattern != null) {
204                     // Perl: $line =~ s{$rx}{$path = $2; $path =~ s!\\\\!/!g; $workspaceUrl . $path}eg;
205
Matcher JavaDoc m = wsPattern.matcher(line);
206                     int pos = 0;
207                     while (m.find(pos)) {
208                         String JavaDoc path = m.group(2).replace(File.separatorChar, '/');
209                         String JavaDoc linkUrl = artifactMatches(path, build) ? artifactUrl : workspaceUrl;
210                         // Append ' ' to make sure mail readers do not interpret following ':' as part of URL:
211
String JavaDoc prefix = line.substring(0, m.start()) + linkUrl + Util.encode(path) + ' ';
212                         pos = prefix.length();
213                         line = prefix + line.substring(m.end());
214                         // XXX better style to reuse Matcher and fix offsets, but more work
215
m = wsPattern.matcher(line);
216                     }
217                 }
218                 buf.append(line);
219                 buf.append('\n');
220             }
221         } catch (IOException JavaDoc e) {
222             // somehow failed to read the contents of the log
223
StringWriter JavaDoc sw = new StringWriter JavaDoc();
224             e.printStackTrace(new PrintWriter JavaDoc(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 JavaDoc createEmptyMail(B build, BuildListener listener) throws MessagingException JavaDoc {
234         MimeMessage JavaDoc msg = new MimeMessage JavaDoc(Mailer.DESCRIPTOR.createSession());
235         // TODO: I'd like to put the URL to the page in here,
236
// but how do I obtain that?
237
msg.setContent("","text/plain");
238         msg.setFrom(new InternetAddress JavaDoc(Mailer.DESCRIPTOR.getAdminAddress()));
239         msg.setSentDate(new Date JavaDoc());
240
241         List JavaDoc<InternetAddress JavaDoc> rcp = new ArrayList JavaDoc<InternetAddress JavaDoc>();
242         StringTokenizer JavaDoc tokens = new StringTokenizer JavaDoc(recipients);
243         while(tokens.hasMoreTokens())
244             rcp.add(new InternetAddress JavaDoc(tokens.nextToken()));
245         if(sendToIndividuals) {
246             Set<User> users = new HashSet JavaDoc<User>();
247             for (Entry change : build.getChangeSet()) {
248                 User a = change.getAuthor();
249                 if(users.add(a)) {
250                     String JavaDoc adrs = a.getProperty(Mailer.UserProperty.class).getAddress();
251                     if(adrs!=null)
252                         rcp.add(new InternetAddress JavaDoc(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 JavaDoc[rcp.size()]));
260         return msg;
261     }
262
263     private String JavaDoc getSubject(B build, String JavaDoc caption) {
264         return caption +build.getProject().getName()+" #"+build.getNumber();
265     }
266
267     /**
268      * Copied from JDK5, to avoid 5.0 dependency.
269      */

270     private static String JavaDoc quoteRegexp(String JavaDoc s) {
271         int slashEIndex = s.indexOf("\\E");
272         if (slashEIndex == -1)
273             return "\\Q" + s + "\\E";
274
275         StringBuilder JavaDoc sb = new StringBuilder JavaDoc(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     /** Check whether a path (/-separated) will be archived. */
289     protected boolean artifactMatches(String JavaDoc path, B build) {
290         return false;
291     }
292
293
294     private static final Logger JavaDoc LOGGER = Logger.getLogger(MailSender.class.getName());
295
296     private static final int MAX_LOG_LINES = 250;
297 }
298
Popular Tags