]> jaekl.net Git - cfb.git/commitdiff
Add email notifications.
authorChris Jaekl <cejaekl@yahoo.com>
Mon, 26 Oct 2015 13:37:13 +0000 (22:37 +0900)
committerChris Jaekl <cejaekl@yahoo.com>
Mon, 26 Oct 2015 13:37:13 +0000 (22:37 +0900)
config.properties [new file with mode: 0644]
prod/cfb.properties
prod/net/jaekl/cfb/CFB.java
prod/net/jaekl/cfb/CfbBundle.java
prod/net/jaekl/cfb/Config.java [new file with mode: 0644]
prod/net/jaekl/cfb/analyze/HtmlReport.java
prod/net/jaekl/cfb/analyze/Notifier.java [new file with mode: 0644]
setcp.sh
test/net/jaekl/cfb/ConfigTest.java [new file with mode: 0644]

diff --git a/config.properties b/config.properties
new file mode 100644 (file)
index 0000000..caf5e75
--- /dev/null
@@ -0,0 +1,9 @@
+; 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
index e56f819a60ed295b9af6ed37fae1cd5834f94c81..7ddb59db1fa2c403b81f7ef3159191644b074083 100644 (file)
@@ -2,7 +2,9 @@ analysis.failed=Attempt to analyze source code failed.  Will now stop.
 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
index 94ef08f8a996cecf7ce681574b95bc3fd1ad62f9..33a1ca04b23312d3e19b05efbc8e8b5cfc2f1546 100644 (file)
@@ -21,6 +21,7 @@ import net.jaekl.cfb.analyze.Analyzer;
 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;
@@ -41,7 +42,10 @@ public class CFB {
        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
@@ -58,7 +62,9 @@ public class CFB {
                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;
@@ -74,6 +80,7 @@ public class CFB {
        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");
@@ -92,6 +99,9 @@ public class CFB {
                
                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");
                        }
@@ -158,13 +168,20 @@ public class CFB {
                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(".");
@@ -200,8 +217,11 @@ public class CFB {
                        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);
index 8ea3c68f669d8306f6e43027251f47aa22e46ed8..0cb7a4082cd574a27b04882d0314c0c92606e783 100644 (file)
@@ -15,7 +15,9 @@ public class CfbBundle {
        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";
diff --git a/prod/net/jaekl/cfb/Config.java b/prod/net/jaekl/cfb/Config.java
new file mode 100644 (file)
index 0000000..8bb401d
--- /dev/null
@@ -0,0 +1,146 @@
+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");
+               }
+       }
+}
index 4482f0a5611b4c194e3c58834930809ce34f4e0f..e2e76cb4bae3b1e2f678fd93046931f81c3000ab 100644 (file)
@@ -13,33 +13,37 @@ import net.jaekl.cfb.xml.messages.MessageCollection;
 
 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>");
        }
@@ -48,12 +52,12 @@ public class HtmlReport {
                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)
@@ -117,7 +121,7 @@ public class HtmlReport {
                }
        }
        
-       void writeHeader(PrintWriter pw, Delta delta)
+       void writeHeader(PrintWriter pw)
        {
                String title = trans(CfbBundle.CFB_REPORT);
                
@@ -136,7 +140,7 @@ public class HtmlReport {
                pw.println("    </P>");
        }
        
-       void writeSummary(PrintWriter pw, Delta delta)
+       void writeSummary(PrintWriter pw)
        {
                final String SEP = ":&nbsp;&nbsp;";
                
@@ -145,24 +149,24 @@ public class HtmlReport {
                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>&nbsp;</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>");
diff --git a/prod/net/jaekl/cfb/analyze/Notifier.java b/prod/net/jaekl/cfb/analyze/Notifier.java
new file mode 100644 (file)
index 0000000..6203727
--- /dev/null
@@ -0,0 +1,87 @@
+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();
+                       }
+               }
+       }
+}
index a27737a094e53092bdadd7a35392f51fe06f6f95..3346766b85db931effa3021f06e1d365c72fe77d 100644 (file)
--- a/setcp.sh
+++ b/setcp.sh
@@ -4,12 +4,14 @@ SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
 
 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}
diff --git a/test/net/jaekl/cfb/ConfigTest.java b/test/net/jaekl/cfb/ConfigTest.java
new file mode 100644 (file)
index 0000000..3fafe87
--- /dev/null
@@ -0,0 +1,24 @@
+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
+       }
+
+}