Add the concept of "Project Name" to the RUNS table in the database.
[cfb.git] / prod / net / jaekl / cfb / CFB.java
index d864b6dc8681b9ae3de5d32b6603329385cc83ca..c02aa546aa0f7efe1bd29b36f81703127e3e6497 100644 (file)
 package net.jaekl.cfb;
 
+// Comparative FindBugs
+// 
+// Tool to compare successive runs of FindBugs, 
+// flagging the change from one run to the next.
+// 
+// Copyright (C) 2015 Christian Jaekl
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
+import java.nio.charset.Charset;
 import java.sql.Connection;
 import java.sql.SQLException;
+import java.text.MessageFormat;
+import java.util.Locale;
+import java.util.Locale.Category;
+
+import net.jaekl.cfb.analyze.Analysis;
+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;
+import net.jaekl.cfb.db.driver.PostgresqlDriver;
+import net.jaekl.cfb.store.DbStore;
+import net.jaekl.qd.xml.XmlParseException;
 
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.GnuParser;
 import org.apache.commons.cli.HelpFormatter;
 import org.apache.commons.cli.Options;
 import org.apache.commons.cli.ParseException;
-
-import net.jaekl.cfb.db.CfbSchema;
-import net.jaekl.cfb.db.driver.DbDriver;
-import net.jaekl.cfb.db.driver.PostgresqlDriver;
+import org.xml.sax.SAXException;
 
 public class CFB {
        DbDriver m_driver;
        CfbSchema m_schema;
+       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
        String m_host;  // db host
        int m_port;             // db port
        String m_user;  // db user
        String m_pass;  // db password
+       String m_projName; // project (module) name
+       String m_buildNum; // build number (version)
+       boolean m_removeSchema; // purge DB schema
+       File m_output;  // File to which we should write our output (report)
        
-       CFB() {
+       CFB(Locale locale) {
                m_driver = new PostgresqlDriver();
                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;
                m_host = "localhost";
                m_port = 5432;
                m_pass = "";
                m_user = "user";
+               m_projName = null;
+               m_buildNum = null;
+               m_removeSchema = false;
+               m_output = null;
        }
        
        Options createOptions() {
                Options opt = new Options();
                
-               opt.addOption("d", "dbname", true, "DB name");
-               opt.addOption("h", "host", true, "DB hostname");
-               opt.addOption("p", "pass", true, "DB password");
-               opt.addOption("t", "port", true, "DB port");
-               opt.addOption("u", "user", true, "DB username");
+               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("j",  "project",     true,  "proJect name");
+               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;
        }
@@ -53,15 +104,31 @@ 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");
                        }
+                       if (line.hasOption("f")) {
+                               m_fbp = new File(line.getOptionValue("f"));
+                       }
                        if (line.hasOption("h")) {
                                m_host = line.getOptionValue("h");
                        }
+                       if (line.hasOption("j")) {
+                               m_projName = line.getOptionValue("j");
+                       }
+                       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"));
                        }
@@ -82,27 +149,115 @@ public class CFB {
                help.printHelp(pw, 80, getClass().getName(), "", opt, 0, 0, "", true);
        }
        
-       void doMain(PrintWriter pw, String[] args) throws SQLException {
+       String trans(String key) {
+               return m_bundle.get(key);
+       }
+       
+       String getenv(String varName) {
+               // This is a separate function so that we can override it at unit test time
+               return System.getenv(varName);
+       }
+       
+       String getProperty(String propName) {
+               // This is a separate function so that we can override it at unit test time
+               return System.getProperty(propName);
+       }
+       
+       File getFindBugsDir() {
+               return (null != m_fbDir) ? m_fbDir : new File(".");
+       }
+       
+       void initArgs() {
+               String findBugsDir = getenv("FINDBUGS_HOME");
+               if (null != findBugsDir) {
+                       m_fbDir = new File(findBugsDir);
+               }
+               findBugsDir = getProperty("findbugs.home");
+               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(".");
+               MessageMap messageMap = new MessageMap();
+               messageMap.load(findBugsDir, Locale.getDefault(Category.DISPLAY));
                
                try (Connection con = m_driver.connect(m_host, m_port, m_dbName, m_user, m_pass)) {
-                       if (null == con) {
-                               // TODO:  string table
-                               pw.println("FATAL:  Cannot connect to db.");
+                       m_schema.setMessageMap(messageMap);
+                       
+                       if (m_removeSchema) {
+                               m_schema.purge(con);
                                return;
                        }
-                       m_schema.ensureDbInitialized(con);                      
+                       m_schema.ensureDbInitialized(con);
+                       messageMap.loadIds(con, m_driver);
+               }
+               catch (SQLException exc) {
+                       reportUnableToConnect(pw, exc);
+                       return;
+               }
+               
+               Analyzer analyzer = new Analyzer(messageMap);
+               Analysis analysis = analyzer.analyze(pw, workDir, m_fbp, m_projName, m_buildNum);
+               if (null == analysis) {
+                       pw.println(trans(CfbBundle.ANALYSIS_FAILED));
+                       return;
+               }
+               
+               try (Connection con = m_driver.connect(m_host, m_port, m_dbName, m_user, m_pass)) {
+                       DbStore store = new DbStore(con, m_driver, messageMap.getColl());
+                       
+                       store.put(analysis);
+                       Analysis prior = store.getPrior(analysis);
+                       Delta delta = new Delta(prior, analysis);
+
+                       HtmlReport report = new HtmlReport(m_bundle, messageMap.getColl(), delta);
+                       if (null != m_output) {
+                               report.write(m_output);
+                       }
+                       
+                       Notifier notifier = new Notifier(m_bundle, m_config);
+                       notifier.sendEmailIfNeeded(pw, report);
+               }
+               catch (SQLException exc) {
+                       reportUnableToConnect(pw, exc);
+                       return;
+               }
+       }
+
+       private void reportUnableToConnect(PrintWriter pw, SQLException exc) {
+               String cannotConnectFormat = trans(CfbBundle.CANNOT_CONNECT);
+               String cannotConnect = MessageFormat.format(cannotConnectFormat, m_host, ""+m_port, m_dbName, m_user);
+               exc.printStackTrace(pw);
+               SQLException next = exc.getNextException();
+               while (null != next) {
+                       next.printStackTrace(pw);
+                       next = next.getNextException();
                }
+               pw.println(cannotConnect);
        }
        
        public static void main(String[] args) {
-               CFB cfb = new CFB();
+               CFB cfb = new CFB(Locale.getDefault());
                
-               try (PrintWriter pw = new PrintWriter(System.out)){
+               try (PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out, Charset.defaultCharset()))) {
                        cfb.doMain(pw, args);
-               } catch (SQLException exc) {
+                       pw.flush();
+               } catch (SQLException | IOException | XmlParseException | SAXException | TypeMismatchException exc) {
                        exc.printStackTrace();
                }
        }