001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.nbbuild;
043:
044: import java.io.*;
045: import java.util.*;
046: import java.util.regex.*;
047:
048: import org.apache.tools.ant.*;
049: import org.apache.tools.mail.MailMessage;
050:
051: /** Task to complain (via email) to people when things fail in a build.
052: * XXX In Ant 1.4 this could be better written using TaskContainer, probably.
053: * XXX support fallback address for errors not otherwise handled
054: * XXX do not send the same error to more than one culprit
055: * XXX support ignore patterns for errors, to be useful with e.g. compile deprecations
056: * @author Jesse Glick
057: */
058: public class Kvetcher extends Task implements BuildListener {
059:
060: private Explanation explanation = null;
061:
062: public final class Explanation {
063: StringBuffer text = new StringBuffer();
064:
065: public void addText(String s) {
066: text.append(getProject().replaceProperties(s));
067: }
068: }
069:
070: /** Provide some explanatory text for the message. */
071: public Explanation createExplanation() {
072: return explanation = new Explanation();
073: }
074:
075: private String target;
076:
077: /** Set the target to run while listening. */
078: public void setTarget(String t) {
079: target = t;
080: }
081:
082: private String from = null;
083:
084: /** Set the mail address to send from. */
085: public void setFrom(String f) {
086: from = f;
087: }
088:
089: private String subject = "Errors in sources";
090:
091: /** Set the mail subject. */
092: public void setSubject(String s) {
093: subject = s;
094: }
095:
096: private String mailhost = "localhost";
097:
098: /** Set the SMTP mailhost to send from. */
099: public void setMailhost(String mh) {
100: mailhost = mh;
101: }
102:
103: private List<Culprit> culprits = new ArrayList<Culprit>(20);
104:
105: public Culprit createCulprit() {
106: Culprit c = new Culprit();
107: culprits.add(c);
108: return c;
109: }
110:
111: public final class Culprit {
112: List<Address> to = new ArrayList<Address>(1);
113: List<Address> cc = new ArrayList<Address>(1);
114: List<Regexp> regexp = new ArrayList<Regexp>(5);
115:
116: public Address createTo() {
117: Address a = new Address();
118: to.add(a);
119: return a;
120: }
121:
122: public Address createCC() {
123: Address a = new Address();
124: cc.add(a);
125: return a;
126: }
127:
128: public Regexp createRegexp() {
129: Regexp r = new Regexp();
130: regexp.add(r);
131: return r;
132: }
133:
134: boolean isValid() {
135: if ((to.isEmpty() && cc.isEmpty()) || regexp.isEmpty())
136: return false;
137: Iterator it = to.iterator();
138: while (it.hasNext()) {
139: if (!((Address) it.next()).isValid())
140: return false;
141: }
142: it = cc.iterator();
143: while (it.hasNext()) {
144: if (!((Address) it.next()).isValid())
145: return false;
146: }
147: it = regexp.iterator();
148: while (it.hasNext()) {
149: if (!((Regexp) it.next()).isValid())
150: return false;
151: }
152: return true;
153: }
154: }
155:
156: public final class Address {
157: String name;
158:
159: public void setName(String n) {
160: name = n;
161: }
162:
163: boolean isValid() {
164: return name != null;
165: }
166: }
167:
168: public final class Regexp {
169: Pattern pattern;
170: int group = -1;
171:
172: public void setPattern(String p) throws BuildException {
173: try {
174: pattern = Pattern.compile(p);
175: } catch (PatternSyntaxException rese) {
176: throw new BuildException(rese, getLocation());
177: }
178: }
179:
180: /** Set which part of the message to actually send.
181: * By default, the entire message.
182: * But you may set this to 0 to mean the matched portion,
183: * or some number >0 to mean that parenthesized group.
184: */
185: public void setGroup(int g) {
186: group = g;
187: }
188:
189: boolean isValid() {
190: return pattern != null;
191: }
192: }
193:
194: private List<String> messages = new ArrayList<String>(1000);
195:
196: public void execute() throws BuildException {
197: if (target == null)
198: throw new BuildException("set the target");
199: if (culprits.isEmpty())
200: throw new BuildException("add some culprits");
201: Iterator it = culprits.iterator();
202: while (it.hasNext()) {
203: if (!((Culprit) it.next()).isValid())
204: throw new BuildException("invalid <culprit>");
205: }
206: getProject().addBuildListener(this );
207: boolean success = false;
208: try {
209: getProject().executeTarget(target);
210: success = true;
211: } finally {
212: getProject().removeBuildListener(this );
213: }
214: if (success)
215: sendMail();
216: }
217:
218: public void messageLogged(BuildEvent buildEvent) {
219: int pri = buildEvent.getPriority();
220: if (pri == Project.MSG_WARN || pri == Project.MSG_ERR) {
221: messages.add(buildEvent.getMessage());
222: }
223: }
224:
225: private void sendMail() throws BuildException {
226: for (Culprit c : culprits) {
227: try {
228: MailMessage mail = null;
229: //boolean send = false;
230: PrintStream ps = null;
231: for (String msg : messages) {
232: for (Regexp r : c.regexp) {
233: Matcher m = r.pattern.matcher(msg);
234: if (m.find()) {
235: if (mail == null) {
236: // OK, time to start sending.
237: log("Sending mail to "
238: + c.to.get(0).name);
239: mail = new MailMessage(mailhost);
240: if (from == null)
241: from = "kvetcher@" + mailhost;
242: mail.from(from);
243: for (Address a : c.to) {
244: mail.to(a.name);
245: }
246: for (Address a : c.cc) {
247: mail.cc(a.name);
248: }
249: mail.setSubject(subject);
250: ps = mail.getPrintStream();
251: if (explanation != null) {
252: ps.println(explanation.text
253: .toString());
254: }
255: ps.println();
256: }
257: ps.println(r.group == -1 ? msg : m
258: .group(r.group));
259: break; // this regexp matches, look for other messages
260: }
261: }
262: }
263: if (mail != null) {
264: mail.sendAndClose();
265: }
266: } catch (IOException ioe) {
267: throw new BuildException("While sending mail", ioe,
268: getLocation());
269: }
270: }
271: }
272:
273: // Ignore these:
274: public void taskStarted(BuildEvent buildEvent) {
275: }
276:
277: public void buildStarted(BuildEvent buildEvent) {
278: }
279:
280: public void buildFinished(BuildEvent buildEvent) {
281: }
282:
283: public void taskFinished(BuildEvent buildEvent) {
284: }
285:
286: public void targetFinished(BuildEvent buildEvent) {
287: }
288:
289: public void targetStarted(BuildEvent buildEvent) {
290: }
291:
292: }
|