3 // Comparative FindBugs
5 // Tool to compare successive runs of FindBugs,
6 // flagging the change from one run to the next.
8 // Copyright (C) 2015 Christian Jaekl
11 import java.io.IOException;
12 import java.io.PrintWriter;
13 import java.sql.Connection;
14 import java.sql.SQLException;
15 import java.text.MessageFormat;
16 import java.util.Locale;
17 import java.util.Locale.Category;
19 import net.jaekl.cfb.analyze.Analysis;
20 import net.jaekl.cfb.analyze.Analyzer;
21 import net.jaekl.cfb.analyze.Delta;
22 import net.jaekl.cfb.analyze.HtmlReport;
23 import net.jaekl.cfb.analyze.MessageMap;
24 import net.jaekl.cfb.analyze.Notifier;
25 import net.jaekl.cfb.db.CfbSchema;
26 import net.jaekl.cfb.db.TypeMismatchException;
27 import net.jaekl.cfb.db.driver.DbDriver;
28 import net.jaekl.cfb.db.driver.PostgresqlDriver;
29 import net.jaekl.cfb.store.DbStore;
30 import net.jaekl.qd.xml.XmlParseException;
32 import org.apache.commons.cli.CommandLine;
33 import org.apache.commons.cli.GnuParser;
34 import org.apache.commons.cli.HelpFormatter;
35 import org.apache.commons.cli.Options;
36 import org.apache.commons.cli.ParseException;
37 import org.xml.sax.SAXException;
47 // Command-line parameters
49 String m_dbName; // db name
50 File m_fbp; // FindBugsProject file
51 File m_fbDir; // Directory where FindBugs is installed
52 String m_host; // db host
53 int m_port; // db port
54 String m_user; // db user
55 String m_pass; // db password
56 String m_buildNum; // build number (version)
57 boolean m_removeSchema; // purge DB schema
58 File m_output; // File to which we should write our output (report)
61 m_driver = new PostgresqlDriver();
62 m_schema = new CfbSchema(m_driver);
64 m_bundle = CfbBundle.getInst(m_locale);
65 m_config = new Config();
67 m_configFile = new File("config.properties");
76 m_removeSchema = false;
80 Options createOptions() {
81 Options opt = new Options();
83 opt.addOption("c", "config", true, "Properties configuration file");
84 opt.addOption("d", "dbname", true, "DB name");
85 opt.addOption(null, "drop-tables", false, "Remove database schema (drop all data)");
86 opt.addOption("f", "fbp", true, "FindBugsProject file");
87 opt.addOption("h", "host", true, "DB hostname");
88 opt.addOption("n", "number", true, "Build number (version)");
89 opt.addOption("o", "outfile", true, "Output report filename");
90 opt.addOption("p", "pass", true, "DB password");
91 opt.addOption("t", "port", true, "DB port");
92 opt.addOption("u", "user", true, "DB username");
97 boolean parseArgs(PrintWriter pw, String[] args) {
98 Options opt = createOptions();
101 CommandLine line = new GnuParser().parse(opt, args);
102 if (line.hasOption("c")) {
103 m_configFile = new File(line.getOptionValue("c"));
105 if (line.hasOption("d")) {
106 m_dbName = line.getOptionValue("d");
108 if (line.hasOption("f")) {
109 m_fbp = new File(line.getOptionValue("f"));
111 if (line.hasOption("h")) {
112 m_host = line.getOptionValue("h");
114 if (line.hasOption("n")) {
115 m_buildNum = line.getOptionValue("n");
117 if (line.hasOption("o")) {
118 m_output = new File(line.getOptionValue("o"));
120 if (line.hasOption("p")) {
121 m_pass = line.getOptionValue("p");
123 m_removeSchema = line.hasOption("drop-tables");
124 if (line.hasOption("t")) {
125 m_port = Integer.parseInt(line.getOptionValue("t"));
127 if (line.hasOption("u")) {
128 m_user = line.getOptionValue("u");
131 catch (ParseException exc) {
139 void usage(PrintWriter pw, Options opt) {
140 HelpFormatter help = new HelpFormatter();
141 help.printHelp(pw, 80, getClass().getName(), "", opt, 0, 0, "", true);
144 String trans(String key) {
145 return m_bundle.get(key);
148 String getenv(String varName) {
149 // This is a separate function so that we can override it at unit test time
150 return System.getenv(varName);
153 String getProperty(String propName) {
154 // This is a separate function so that we can override it at unit test time
155 return System.getProperty(propName);
158 File getFindBugsDir() {
159 return (null != m_fbDir) ? m_fbDir : new File(".");
163 String findBugsDir = getenv("FINDBUGS_HOME");
164 if (null != findBugsDir) {
165 m_fbDir = new File(findBugsDir);
167 findBugsDir = getProperty("findbugs.home");
168 if (null != findBugsDir) {
169 m_fbDir = new File(findBugsDir);
173 void readConfig() throws IOException {
174 if (null != m_configFile) {
175 m_config.readFile(m_configFile);
179 void doMain(PrintWriter pw, String[] args) throws SQLException, IOException, XmlParseException, SAXException, TypeMismatchException {
180 initArgs(); // read environment and system properties
181 if ( ! parseArgs(pw, args) ) {
186 File findBugsDir = getFindBugsDir();
187 File workDir = new File(".");
188 MessageMap messageMap = new MessageMap();
189 messageMap.load(findBugsDir, Locale.getDefault(Category.DISPLAY));
191 try (Connection con = m_driver.connect(m_host, m_port, m_dbName, m_user, m_pass)) {
192 m_schema.setMessageMap(messageMap);
194 if (m_removeSchema) {
198 m_schema.ensureDbInitialized(con);
199 messageMap.loadIds(con, m_driver);
201 catch (SQLException exc) {
202 reportUnableToConnect(pw, exc);
206 Analyzer analyzer = new Analyzer(messageMap);
207 Analysis analysis = analyzer.analyze(pw, workDir, m_fbp, m_buildNum);
208 if (null == analysis) {
209 pw.println(trans(CfbBundle.ANALYSIS_FAILED));
213 try (Connection con = m_driver.connect(m_host, m_port, m_dbName, m_user, m_pass)) {
214 DbStore store = new DbStore(con, m_driver, messageMap.getColl());
217 Analysis prior = store.getPrior(analysis);
218 Delta delta = new Delta(prior, analysis);
220 HtmlReport report = new HtmlReport(m_bundle, messageMap.getColl(), delta);
221 if (null != m_output) {
222 report.write(m_output);
225 Notifier notifier = new Notifier(m_bundle, m_config);
226 notifier.sendEmailIfNeeded(pw, report);
228 catch (SQLException exc) {
229 reportUnableToConnect(pw, exc);
234 private void reportUnableToConnect(PrintWriter pw, SQLException exc) {
235 String cannotConnectFormat = trans(CfbBundle.CANNOT_CONNECT);
236 String cannotConnect = MessageFormat.format(cannotConnectFormat, m_host, ""+m_port, m_dbName, m_user);
237 exc.printStackTrace(pw);
238 SQLException next = exc.getNextException();
239 while (null != next) {
240 next.printStackTrace(pw);
241 next = next.getNextException();
243 pw.println(cannotConnect);
246 public static void main(String[] args) {
247 CFB cfb = new CFB(Locale.getDefault());
249 try (PrintWriter pw = new PrintWriter(System.out)){
250 cfb.doMain(pw, args);
252 } catch (SQLException | IOException | XmlParseException | SAXException | TypeMismatchException exc) {
253 exc.printStackTrace();