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_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_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("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;
}
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("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"));
}
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_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();
}
}