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.OutputStreamWriter;
13 import java.io.PrintWriter;
14 import java.nio.charset.Charset;
15 import java.sql.Connection;
16 import java.sql.SQLException;
17 import java.text.MessageFormat;
18 import java.util.Locale;
19 import java.util.Locale.Category;
21 import net.jaekl.cfb.analyze.Analysis;
22 import net.jaekl.cfb.analyze.Analyzer;
23 import net.jaekl.cfb.analyze.Delta;
24 import net.jaekl.cfb.analyze.HtmlReport;
25 import net.jaekl.cfb.analyze.MessageMap;
26 import net.jaekl.cfb.analyze.Notifier;
27 import net.jaekl.cfb.db.CfbSchema;
28 import net.jaekl.cfb.db.TypeMismatchException;
29 import net.jaekl.cfb.db.driver.DbDriver;
30 import net.jaekl.cfb.db.driver.PostgresqlDriver;
31 import net.jaekl.cfb.store.DbStore;
32 import net.jaekl.qd.xml.XmlParseException;
34 import org.apache.commons.cli.CommandLine;
35 import org.apache.commons.cli.GnuParser;
36 import org.apache.commons.cli.HelpFormatter;
37 import org.apache.commons.cli.Options;
38 import org.apache.commons.cli.ParseException;
39 import org.xml.sax.SAXException;
49 // Command-line parameters
51 File m_fbp; // FindBugsProject file
52 File m_fbDir; // Directory where FindBugs is installed
53 String m_projName; // project (module) name
54 String m_buildNum; // build number (version)
55 boolean m_removeSchema; // purge DB schema
56 File m_output; // File to which we should write our output (report)
59 m_driver = new PostgresqlDriver();
60 m_schema = new CfbSchema(m_driver);
62 m_bundle = CfbBundle.getInst(m_locale);
63 m_config = new Config();
65 m_configFile = new File("config.properties");
70 m_removeSchema = false;
74 Options createOptions() {
75 Options opt = new Options();
77 opt.addOption("c", "config", true, "Properties configuration file");
78 opt.addOption("d", "dbname", true, "DB name");
79 opt.addOption(null, "drop-tables", false, "Remove database schema (drop all data)");
80 opt.addOption("f", "fbp", true, "FindBugsProject file");
81 opt.addOption("h", "host", true, "DB hostname");
82 opt.addOption("j", "project", true, "proJect name");
83 opt.addOption("n", "number", true, "Build number (version)");
84 opt.addOption("o", "outfile", true, "Output report filename");
85 opt.addOption("p", "pass", true, "DB password");
86 opt.addOption("t", "port", true, "DB port");
87 opt.addOption("u", "user", true, "DB username");
92 boolean parseArgs(PrintWriter pw, String[] args) {
93 Options opt = createOptions();
96 CommandLine line = new GnuParser().parse(opt, args);
97 if (line.hasOption("c")) {
98 m_configFile = new File(line.getOptionValue("c"));
100 if (line.hasOption("d")) {
101 m_config.setDbName(line.getOptionValue("d"));
103 if (line.hasOption("f")) {
104 m_fbp = new File(line.getOptionValue("f"));
106 if (line.hasOption("h")) {
107 m_config.setDbHost(line.getOptionValue("h"));
109 if (line.hasOption("j")) {
110 m_projName = line.getOptionValue("j");
112 if (line.hasOption("n")) {
113 m_buildNum = line.getOptionValue("n");
115 if (line.hasOption("o")) {
116 m_output = new File(line.getOptionValue("o"));
118 if (line.hasOption("p")) {
119 m_config.setDbPass(line.getOptionValue("p"));
121 m_removeSchema = line.hasOption("drop-tables");
122 if (line.hasOption("t")) {
123 m_config.setDbPort(Integer.parseInt(line.getOptionValue("t")));
125 if (line.hasOption("u")) {
126 m_config.setDbUser(line.getOptionValue("u"));
129 catch (ParseException exc) {
137 void usage(PrintWriter pw, Options opt) {
138 HelpFormatter help = new HelpFormatter();
139 help.printHelp(pw, 80, getClass().getName(), "", opt, 0, 0, "", true);
142 String trans(String key) {
143 return m_bundle.get(key);
146 String getenv(String varName) {
147 // This is a separate function so that we can override it at unit test time
148 return System.getenv(varName);
151 String getProperty(String propName) {
152 // This is a separate function so that we can override it at unit test time
153 return System.getProperty(propName);
156 File getFindBugsDir() {
157 return (null != m_fbDir) ? m_fbDir : new File(".");
161 String findBugsDir = getenv("FINDBUGS_HOME");
162 if (null != findBugsDir) {
163 m_fbDir = new File(findBugsDir);
165 findBugsDir = getProperty("findbugs.home");
166 if (null != findBugsDir) {
167 m_fbDir = new File(findBugsDir);
171 void readConfig() throws IOException {
172 if (null != m_configFile) {
173 m_config.readFile(m_configFile);
177 void doMain(PrintWriter pw, String[] args) throws SQLException, IOException, XmlParseException, SAXException, TypeMismatchException {
178 initArgs(); // read environment and system properties
179 if ( ! parseArgs(pw, args) ) {
184 File findBugsDir = getFindBugsDir();
185 File workDir = new File(".");
186 MessageMap messageMap = new MessageMap();
187 messageMap.load(findBugsDir, Locale.getDefault(Category.DISPLAY));
189 try (Connection con = m_driver.connect(
190 m_config.getDbHost(), m_config.getDbPort(),
191 m_config.getDbName(),
192 m_config.getDbUser(), m_config.getDbPass())
195 m_schema.setMessageMap(messageMap);
197 if (m_removeSchema) {
201 m_schema.ensureDbInitialized(con);
202 messageMap.loadIds(con, m_driver);
204 catch (SQLException exc) {
205 reportUnableToConnect(pw, exc);
209 Analyzer analyzer = new Analyzer(messageMap);
210 Analysis analysis = analyzer.analyze(pw, workDir, m_fbp, m_projName, m_buildNum);
211 if (null == analysis) {
212 pw.println(trans(CfbBundle.ANALYSIS_FAILED));
217 Connection con = m_driver.connect(
218 m_config.getDbHost(), m_config.getDbPort(),
219 m_config.getDbName(),
220 m_config.getDbUser(), m_config.getDbPass()
224 DbStore store = new DbStore(con, m_driver, messageMap.getColl());
227 Analysis prior = store.getPrior(analysis);
228 Delta delta = new Delta(prior, analysis);
230 HtmlReport report = new HtmlReport(m_bundle, messageMap.getColl(), delta);
231 if (null != m_output) {
232 report.write(m_output);
235 Notifier notifier = new Notifier(m_bundle, m_config);
236 notifier.sendEmailIfNeeded(pw, report);
238 catch (SQLException exc) {
239 reportUnableToConnect(pw, exc);
244 private void reportUnableToConnect(PrintWriter pw, SQLException exc) {
245 String cannotConnectFormat = trans(CfbBundle.CANNOT_CONNECT);
246 String cannotConnect = MessageFormat.format(cannotConnectFormat,
247 m_config.getDbHost(),
248 ""+m_config.getDbPort(),
249 m_config.getDbName(),
252 exc.printStackTrace(pw);
253 SQLException next = exc.getNextException();
254 while (null != next) {
255 next.printStackTrace(pw);
256 next = next.getNextException();
258 pw.println(cannotConnect);
261 public static void main(String[] args) {
262 CFB cfb = new CFB(Locale.getDefault());
264 try (PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out, Charset.defaultCharset()))) {
265 cfb.doMain(pw, args);
267 } catch (SQLException | IOException | XmlParseException | SAXException | TypeMismatchException exc) {
268 exc.printStackTrace();