--- /dev/null
+; Path (relative or absolute) to the FINDBUGS_HOME, i.e., where FindBugs is installed
+FindBugsHome=../findbugs-3.0.1/
+
+; List (comma-separated) of email addresses to which notifications should be sent
+notify=chris@localhost
+
+; Mail server setup
+mail.smtp.host=localhost
+mail.from=findbugs@localhost
analyzed.at=Analyzed at {0}
cannot.connect.to.db=Unable to connect to, or to initialize, database {2} on {0}:{1} as user {3}.
cannot.exec=Got result code {1} when attempting to execute command-line: {0}
+cannot.send.mail=Attempt to send email to {0} failed: {1}
cfb=Comparative FindBugs
+cfb.mail.subject=CFB: {0} vs. {1}
cfb.report=Comparative FindBugs Report
fixed.bugs=Fixed Bugs
new.bugs=New Bugs
import net.jaekl.cfb.analyze.Delta;
import net.jaekl.cfb.analyze.HtmlReport;
import net.jaekl.cfb.analyze.MessageMap;
+import net.jaekl.cfb.analyze.Notifier;
import net.jaekl.cfb.db.CfbSchema;
import net.jaekl.cfb.db.TypeMismatchException;
import net.jaekl.cfb.db.driver.DbDriver;
CfbBundle m_bundle;
Locale m_locale;
+ Config m_config;
+
// Command-line parameters
+ File m_configFile;
String m_dbName; // db name
File m_fbp; // FindBugsProject file
File m_fbDir; // Directory where FindBugs is installed
m_schema = new CfbSchema(m_driver);
m_locale = locale;
m_bundle = CfbBundle.getInst(m_locale);
+ m_config = new Config();
+ m_configFile = new File("config.properties");
m_dbName = "CFB";
m_fbp = null;
m_fbDir = null;
Options createOptions() {
Options opt = new Options();
+ opt.addOption("c", "config", true, "Properties configuration file");
opt.addOption("d", "dbname", true, "DB name");
opt.addOption(null, "drop-tables", false, "Remove database schema (drop all data)");
opt.addOption("f", "fbp", true, "FindBugsProject file");
try {
CommandLine line = new GnuParser().parse(opt, args);
+ if (line.hasOption("c")) {
+ m_configFile = new File(line.getOptionValue("c"));
+ }
if (line.hasOption("d")) {
m_dbName = line.getOptionValue("d");
}
if (null != findBugsDir) {
m_fbDir = new File(findBugsDir);
}
- }
+ }
+
+ void readConfig() throws IOException {
+ if (null != m_configFile) {
+ m_config.readFile(m_configFile);
+ }
+ }
void doMain(PrintWriter pw, String[] args) throws SQLException, IOException, XmlParseException, SAXException, TypeMismatchException {
initArgs(); // read environment and system properties
if ( ! parseArgs(pw, args) ) {
return;
}
+ readConfig();
File findBugsDir = getFindBugsDir();
File workDir = new File(".");
Analysis prior = store.getPrior(analysis);
Delta delta = new Delta(prior, analysis);
- HtmlReport report = new HtmlReport(m_bundle, messageMap.getColl());
- report.write(m_output, delta);
+ HtmlReport report = new HtmlReport(m_bundle, messageMap.getColl(), delta);
+ report.write(m_output);
+
+ Notifier notifier = new Notifier(m_bundle, m_config);
+ notifier.sendEmailIfNeeded(pw, report);
}
catch (SQLException exc) {
reportUnableToConnect(pw, exc);
public static final String ANALYZED_AT = "analyzed.at";
public static final String CANNOT_CONNECT = "cannot.connect.to.db";
public static final String CANNOT_EXEC = "cannot.exec";
+ public static final String CANNOT_SEND_MAIL = "cannot.send.mail";
public static final String CFB = "cfb";
+ public static final String CFB_MAIL_SUBJECT = "cfb.mail.subject";
public static final String CFB_REPORT = "cfb.report";
public static final String COMPARING_RUNS = "comparing.runs";
public static final String COMPARING_VERSIONS = "comparing.versions";
--- /dev/null
+package net.jaekl.cfb;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.GnuParser;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+
+public class Config {
+ private static final String FINDBUGS_HOME = "FindBugsHome";
+ private static final String MAIL_FROM = "mail.from";
+ private static final String MAIL_SMTP_HOST = "mail.smtp.host";
+ private static final String NOTIFY = "notify";
+
+ File m_configProperties;
+
+ String m_dbName; // db name
+ File m_fbp; // FindBugsProject file
+ File m_fbDir; // Directory where FindBugs is installed
+ String m_host; // db host
+ int m_port; // db port
+ String m_user; // db user
+ String m_pass; // db password
+ String m_buildNum; // build number (version)
+ boolean m_removeSchema; // purge DB schema
+ File m_output; // File to which we should write our output (report)
+
+ String m_mailFrom;
+ String m_mailSmtpHost;
+ List<String> m_notify;
+
+ Options m_options;
+
+ public Config() {
+ m_dbName = "CFB";
+ m_fbp = null;
+ m_fbDir = null;
+ m_host = "localhost";
+ m_port = 5432;
+ m_pass = "";
+ m_user = "user";
+ m_buildNum = null;
+ m_removeSchema = false;
+ m_output = null;
+
+ m_mailFrom = "findbugs@localhost";
+ m_mailSmtpHost = "localhost";
+ m_notify = new ArrayList<String>();
+
+ m_options = createOptions();
+ }
+
+ public String getMailFrom() { return m_mailFrom; }
+ public String getMailSmtpHost() { return m_mailSmtpHost; }
+ public ArrayList<String> getNotify() { return new ArrayList<String>(m_notify); }
+
+ public void readFile(File configProperties) throws IOException
+ {
+ Properties props = new Properties();
+ FileInputStream fis = null;
+
+ try {
+ fis = new FileInputStream(configProperties);
+ props.load(fis);
+ }
+ finally {
+ if (null != fis) {
+ fis.close();
+ }
+ }
+
+ if (props.containsKey(FINDBUGS_HOME)) {
+ m_fbDir = new File(props.getProperty(FINDBUGS_HOME));
+ }
+ if (props.containsKey(MAIL_FROM)) {
+ m_mailFrom = props.getProperty(MAIL_FROM);
+ }
+ if (props.containsKey(MAIL_SMTP_HOST)) {
+ m_mailSmtpHost = props.getProperty(MAIL_SMTP_HOST);
+ }
+ if (props.containsKey(NOTIFY)) {
+ String[] addresses = props.getProperty(NOTIFY).split(",");
+ m_notify = Arrays.asList(addresses);
+ }
+ }
+
+ public Options createOptions() {
+ Options opt = new Options();
+
+ opt.addOption("c", "config", true, "Properties configuration file");
+ opt.addOption("d", "dbname", true, "DB name");
+ opt.addOption(null, "drop-tables", false, "Remove database schema (drop all data)");
+ opt.addOption("f", "fbp", true, "FindBugsProject file");
+ opt.addOption("h", "host", true, "DB hostname");
+ opt.addOption("n", "number", true, "Build number (version)");
+ opt.addOption("o", "outfile", true, "Output report filename");
+ opt.addOption("p", "pass", true, "DB password");
+ opt.addOption("t", "port", true, "DB port");
+ opt.addOption("u", "user", true, "DB username");
+
+ return opt;
+ }
+
+ public void parseArgs(PrintWriter pw, String[] args) throws ParseException
+ {
+ assert(null != m_options);
+
+ CommandLine line = new GnuParser().parse(m_options, args);
+ if (line.hasOption("c")) {
+ m_configProperties = new File(line.getOptionValue("c"));
+ }
+ if (line.hasOption("d")) {
+ m_dbName = line.getOptionValue("d");
+ }
+ if (line.hasOption("f")) {
+ m_fbp = new File(line.getOptionValue("f"));
+ }
+ if (line.hasOption("h")) {
+ m_host = line.getOptionValue("h");
+ }
+ if (line.hasOption("n")) {
+ m_buildNum = line.getOptionValue("n");
+ }
+ if (line.hasOption("o")) {
+ m_output = new File(line.getOptionValue("o"));
+ }
+ if (line.hasOption("p")) {
+ m_pass = line.getOptionValue("p");
+ }
+ m_removeSchema = line.hasOption("drop-tables");
+ if (line.hasOption("t")) {
+ m_port = Integer.parseInt(line.getOptionValue("t"));
+ }
+ if (line.hasOption("u")) {
+ m_user = line.getOptionValue("u");
+ }
+ }
+}
public class HtmlReport {
CfbBundle m_bundle;
+ Delta m_delta;
MessageCollection m_msgColl;
- public HtmlReport(CfbBundle bundle, MessageCollection msgColl)
+ public HtmlReport(CfbBundle bundle, MessageCollection msgColl, Delta delta)
{
m_bundle = bundle;
+ m_delta = delta;
m_msgColl = msgColl;
}
- public void write(File output, Delta delta) throws IOException
+ public Delta getDelta() { return m_delta; }
+
+ public void write(File output) throws IOException
{
try ( FileOutputStream fos = new FileOutputStream(output);
PrintWriter pw = new PrintWriter(fos); )
{
- write(pw, delta);
+ write(pw);
}
}
- void write(PrintWriter pw, Delta delta)
+ void write(PrintWriter pw)
{
- startPage(pw, delta);
+ startPage(pw);
}
- void startPage(PrintWriter pw, Delta delta)
+ void startPage(PrintWriter pw)
{
- writeHeader(pw, delta);
+ writeHeader(pw);
pw.println(" <BODY>");
- writeBody(pw, delta);
+ writeBody(pw);
pw.println(" </BODY>");
pw.println("</HTML>");
}
return m_bundle.get(key, arguments);
}
- void writeBody(PrintWriter pw, Delta delta)
+ void writeBody(PrintWriter pw)
{
- writeSummary(pw, delta);
- writeBugs(pw, CfbBundle.NEW_BUGS, delta.getNew());
- writeBugs(pw, CfbBundle.FIXED_BUGS, delta.getFixed());
- writeBugs(pw, CfbBundle.OLD_BUGS, delta.getCommon());
+ writeSummary(pw);
+ writeBugs(pw, CfbBundle.NEW_BUGS, m_delta.getNew());
+ writeBugs(pw, CfbBundle.FIXED_BUGS, m_delta.getFixed());
+ writeBugs(pw, CfbBundle.OLD_BUGS, m_delta.getCommon());
}
void writeBugLocations(PrintWriter pw, BugInstance bug)
}
}
- void writeHeader(PrintWriter pw, Delta delta)
+ void writeHeader(PrintWriter pw)
{
String title = trans(CfbBundle.CFB_REPORT);
pw.println(" </P>");
}
- void writeSummary(PrintWriter pw, Delta delta)
+ void writeSummary(PrintWriter pw)
{
final String SEP = ": ";
pw.println(" <TABLE>");
pw.println(" <TR>");
pw.println(" <TD CLASS=\"CategoryName\">" + trans(CfbBundle.NEW_VERSION) + SEP + "</TD>");
- pw.println(" <TD CLASS=\"CategoryValue\">" + delta.getLater().constructVersionText(m_bundle) + "</TD>");
+ pw.println(" <TD CLASS=\"CategoryValue\">" + m_delta.getLater().constructVersionText(m_bundle) + "</TD>");
pw.println(" </TR>");
pw.println(" <TR>");
pw.println(" <TD CLASS=\"CategoryName\">" + trans(CfbBundle.OLD_VERSION) + SEP + "</TD>");
- pw.println(" <TD CLASS=\"CategoryValue\">" + delta.getEarlier().constructVersionText(m_bundle) + "</TD>");
+ pw.println(" <TD CLASS=\"CategoryValue\">" + m_delta.getEarlier().constructVersionText(m_bundle) + "</TD>");
pw.println(" </TR>");
pw.println(" <TR><TD> </TD></TR>");
pw.println(" <TR>");
pw.println(" <TD CLASS=\"CategoryName\">" + trans(CfbBundle.NEW_BUGS) + SEP + "</TD>");
- pw.println(" <TD CLASS=\"CategoryValue\">" + trans(CfbBundle.NUM_BUGS, delta.getNumNew()) + "</TD>");
+ pw.println(" <TD CLASS=\"CategoryValue\">" + trans(CfbBundle.NUM_BUGS, m_delta.getNumNew()) + "</TD>");
pw.println(" </TR>");
pw.println(" <TR>");
pw.println(" <TD CLASS=\"CategoryName\">" + trans(CfbBundle.FIXED_BUGS) + SEP + "</TD>");
- pw.println(" <TD CLASS=\"CategoryValue\">" + trans(CfbBundle.NUM_BUGS, delta.getNumFixed()) + "</TD>");
+ pw.println(" <TD CLASS=\"CategoryValue\">" + trans(CfbBundle.NUM_BUGS, m_delta.getNumFixed()) + "</TD>");
pw.println(" </TR>");
pw.println(" <TR>");
pw.println(" <TD CLASS=\"CategoryName\">" + trans(CfbBundle.OLD_BUGS) + SEP + "</TD>");
- pw.println(" <TD CLASS=\"CategoryValue\">" + trans(CfbBundle.NUM_BUGS_OLD, delta.getNumCommon()) + "</TD>");
+ pw.println(" <TD CLASS=\"CategoryValue\">" + trans(CfbBundle.NUM_BUGS_OLD, m_delta.getNumCommon()) + "</TD>");
pw.println(" </TR>");
pw.println(" </TABLE>");
pw.println(" </P>");
--- /dev/null
+package net.jaekl.cfb.analyze;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Properties;
+
+import javax.mail.Message.RecipientType;
+import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.mail.Transport;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeMessage;
+
+import net.jaekl.cfb.CfbBundle;
+import net.jaekl.cfb.Config;
+
+public class Notifier {
+ private static final String MAIL_SMTP_HOST = "mail.smtp.host";
+ private static final String TEXT_HTML = "text/html";
+
+ CfbBundle m_bundle;
+ Config m_config;
+
+ public Notifier(CfbBundle bundle, Config config) {
+ m_bundle = bundle;
+ m_config = config;
+ }
+
+ public void sendEmailIfNeeded(PrintWriter pw, HtmlReport report) {
+ Delta delta = report.getDelta();
+ if ((delta.getNumNew() > 0) || (delta.getNumFixed() > 0)) {
+ sendEmail(pw, report);
+ }
+ }
+
+ void sendEmail(PrintWriter pw, HtmlReport report) {
+ Properties props = System.getProperties();
+ props.setProperty(MAIL_SMTP_HOST, m_config.getMailSmtpHost());
+ Session sess = Session.getDefaultInstance(props);
+
+ ArrayList<String> recipients = m_config.getNotify();
+ if (recipients.size() < 1) {
+ return;
+ }
+
+ PrintWriter mailWriter = null;
+
+ try {
+ MimeMessage msg = new MimeMessage(sess);
+
+ String earlier = report.getDelta().getEarlier().constructVersionText(m_bundle);
+ String later = report.getDelta().getLater().constructVersionText(m_bundle);
+
+ msg.setFrom(new InternetAddress(m_config.getMailFrom()));
+ msg.setSubject(m_bundle.get(CfbBundle.CFB_MAIL_SUBJECT, earlier, later));
+
+ for (String recipient : recipients) {
+ msg.addRecipient(RecipientType.TO, new InternetAddress(recipient));
+ }
+
+ StringWriter sw = new StringWriter();
+ mailWriter = new PrintWriter(sw);
+ report.write(mailWriter);
+ mailWriter.flush();
+
+ msg.setContent(sw.toString(), TEXT_HTML);
+ Transport.send(msg);
+ }
+ catch (MessagingException exc) {
+ StringBuilder toList = new StringBuilder();
+ for (String recipient : recipients) {
+ if (toList.length() > 0) {
+ toList.append(", ");
+ }
+ toList.append(recipient);
+ }
+ pw.println(m_bundle.get(CfbBundle.CANNOT_SEND_MAIL, toList.toString(), exc.toString()));
+ exc.printStackTrace(pw);
+ }
+ finally {
+ if (null != mailWriter) {
+ mailWriter.close();
+ }
+ }
+ }
+}
export CLASSPATH=${SCRIPT_DIR}/bin
-for x in ${SCRIPT_DIR}/lib/*.jar
-do
- export CLASSPATH=${x}:${CLASSPATH}
-done
+#for x in ${SCRIPT_DIR}/lib/*.jar
+#do
+# export CLASSPATH=${x}:${CLASSPATH}
+#done
export CLASSPATH=/usr/share/java/commons-cli.jar:${CLASSPATH}
export CLASSPATH=/usr/share/java/junit4.jar:${CLASSPATH}
export CLASSPATH=/usr/share/java/postgresql.jar:${CLASSPATH}
+export CLASSPATH=/usr/share/java/gnumail.jar:${CLASSPATH}
+export CLASSPATH=/usr/share/java/activation.jar:${CLASSPATH}
#export CLASSPATH=${SCRIPT_DIR}/jcov/jcov.jar:${CLASSPATH}
--- /dev/null
+package net.jaekl.cfb;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+public class ConfigTest {
+
+ private static final String SAMPLE1 =
+ "; Path (relative or absolute) to the FINDBUGS_HOME, i.e., where FindBugs is installed\n"
+ + "FindBugsHome=../findbugs-3.0.1/\n"
+ + "; List (comma-separated) of email addresses to which notifications should be sent\n"
+ + "notify=chris@localhost\n"
+ + "\n"
+ + "; Mail server setup\n"
+ + "mail.smtp.host=localhost\n"
+ + "mail.from=findbugs@localhost\n";
+
+ @Test
+ public void testReadFile() {
+ // TODO: wrap file access so that we can test this
+ }
+
+}