d202d6fcb518b1ee54697588fc6c0204fd92e4b7
[cfb.git] / prod / net / jaekl / cfb / CFB.java
1 package net.jaekl.cfb;
2
3 // Comparative FindBugs
4 // 
5 // Tool to compare successive runs of FindBugs, 
6 // flagging the change from one run to the next.
7 // 
8 // Copyright (C) 2015 Christian Jaekl
9
10 import java.io.File;
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;
18
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.MessageMap;
23 import net.jaekl.cfb.db.CfbSchema;
24 import net.jaekl.cfb.db.TypeMismatchException;
25 import net.jaekl.cfb.db.driver.DbDriver;
26 import net.jaekl.cfb.db.driver.PostgresqlDriver;
27 import net.jaekl.cfb.store.DbStore;
28 import net.jaekl.qd.xml.XmlParseException;
29
30 import org.apache.commons.cli.CommandLine;
31 import org.apache.commons.cli.GnuParser;
32 import org.apache.commons.cli.HelpFormatter;
33 import org.apache.commons.cli.Options;
34 import org.apache.commons.cli.ParseException;
35 import org.xml.sax.SAXException;
36
37 public class CFB {
38         DbDriver m_driver;
39         CfbSchema m_schema;
40         CfbBundle m_bundle;     
41         Locale m_locale;
42         
43         // Command-line parameters
44         String m_dbName; // db name
45         File m_fbp;             // FindBugsProject file
46         File m_fbDir;   // Directory where FindBugs is installed
47         String m_host;  // db host
48         int m_port;             // db port
49         String m_user;  // db user
50         String m_pass;  // db password
51         String m_buildNum; // build number (version)
52         boolean m_removeSchema; // purge DB schema
53         
54         CFB(Locale locale) {
55                 m_driver = new PostgresqlDriver();
56                 m_schema = new CfbSchema(m_driver);
57                 m_locale = locale;
58                 m_bundle = CfbBundle.getInst(m_locale);
59                 
60                 m_dbName = "CFB";
61                 m_fbp    = null;
62                 m_fbDir  = null;
63                 m_host = "localhost";
64                 m_port = 5432;
65                 m_pass = "";
66                 m_user = "user";
67                 m_buildNum = null;
68                 m_removeSchema = false;
69         }
70         
71         Options createOptions() {
72                 Options opt = new Options();
73                 
74                 opt.addOption("d", "dbname", true, "DB name");
75                 opt.addOption("f", "fbp",    true, "FindBugsProject file");
76                 opt.addOption("h", "host",   true, "DB hostname");
77                 opt.addOption("n", "number", true, "Build number (version)");
78                 opt.addOption("p", "pass",   true, "DB password");
79                 opt.addOption("r", "remove", false, "Remove database schema (drop all data)");
80                 opt.addOption("t", "port",   true, "DB port");
81                 opt.addOption("u", "user",   true, "DB username");
82                 
83                 return opt;
84         }
85         
86         boolean parseArgs(PrintWriter pw, String[] args) {
87                 Options opt = createOptions();
88                 
89                 try {
90                         CommandLine line = new GnuParser().parse(opt, args);
91                         if (line.hasOption("d")) {
92                                 m_dbName = line.getOptionValue("d");
93                         }
94                         if (line.hasOption("f")) {
95                                 m_fbp = new File(line.getOptionValue("f"));
96                         }
97                         if (line.hasOption("h")) {
98                                 m_host = line.getOptionValue("h");
99                         }
100                         if (line.hasOption("n")) {
101                                 m_buildNum = line.getOptionValue("n");
102                         }
103                         if (line.hasOption("p")) {
104                                 m_pass = line.getOptionValue("p");
105                         }
106                         m_removeSchema = line.hasOption("r");
107                         if (line.hasOption("t")) {
108                                 m_port = Integer.parseInt(line.getOptionValue("t"));
109                         }
110                         if (line.hasOption("u")) {
111                                 m_user = line.getOptionValue("u");
112                         }
113                 } 
114                 catch (ParseException exc) {
115                         usage(pw, opt);
116                         return false;
117                 }
118                 
119                 return true;
120         }
121         
122         void usage(PrintWriter pw, Options opt) {
123                 HelpFormatter help = new HelpFormatter();
124                 help.printHelp(pw, 80, getClass().getName(), "", opt, 0, 0, "", true);
125         }
126         
127         String trans(String key) {
128                 return m_bundle.get(key);
129         }
130         
131         String getenv(String varName) {
132                 // This is a separate function so that we can override it at unit test time
133                 return System.getenv(varName);
134         }
135         
136         String getProperty(String propName) {
137                 // This is a separate function so that we can override it at unit test time
138                 return System.getProperty(propName);
139         }
140         
141         File getFindBugsDir() {
142                 return (null != m_fbDir) ? m_fbDir : new File(".");
143         }
144         
145         void initArgs() {
146                 String findBugsDir = getenv("FINDBUGS_HOME");
147                 if (null != findBugsDir) {
148                         m_fbDir = new File(findBugsDir);
149                 }
150                 findBugsDir = getProperty("findbugs.home");
151                 if (null != findBugsDir) {
152                         m_fbDir = new File(findBugsDir);
153                 }
154         } 
155         
156         void doMain(PrintWriter pw, String[] args) throws SQLException, IOException, XmlParseException, SAXException, TypeMismatchException {
157                 initArgs();     // read environment and system properties
158                 if ( ! parseArgs(pw, args) ) {
159                         return;
160                 }
161
162                 File findBugsDir = getFindBugsDir();
163                 File workDir = new File(".");
164                 MessageMap messageMap = new MessageMap();
165                 messageMap.load(findBugsDir, Locale.getDefault(Category.DISPLAY));
166                 
167                 try (Connection con = m_driver.connect(m_host, m_port, m_dbName, m_user, m_pass)) {
168                         m_schema.setMessageMap(messageMap);
169                         
170                         if (m_removeSchema) {
171                                 m_schema.purge(con);
172                                 return;
173                         }
174                         m_schema.ensureDbInitialized(con);
175                         messageMap.loadIds(con, m_driver);
176                 }
177                 catch (SQLException exc) {
178                         reportUnableToConnect(pw, exc);
179                         return;
180                 }
181                 
182                 Analyzer analyzer = new Analyzer(messageMap);
183                 Analysis analysis = analyzer.analyze(pw, workDir, m_fbp, m_buildNum);
184                 if (null == analysis) {
185                         pw.println(trans(CfbBundle.ANALYSIS_FAILED));
186                         return;
187                 }
188                 
189                 try (Connection con = m_driver.connect(m_host, m_port, m_dbName, m_user, m_pass)) {
190                         DbStore store = new DbStore(con, m_driver, messageMap.getColl());
191                         
192                         store.put(analysis);
193                         Analysis prior = store.getPrior(analysis);
194                         Delta delta = new Delta(prior, analysis);
195                         delta.dump(pw);
196                 }
197                 catch (SQLException exc) {
198                         reportUnableToConnect(pw, exc);
199                         return;
200                 }
201         }
202
203         private void reportUnableToConnect(PrintWriter pw, SQLException exc) {
204                 String cannotConnectFormat = trans(CfbBundle.CANNOT_CONNECT);
205                 String cannotConnect = MessageFormat.format(cannotConnectFormat, m_host, ""+m_port, m_dbName, m_user);
206                 exc.printStackTrace(pw);
207                 SQLException next = exc.getNextException();
208                 while (null != next) {
209                         next.printStackTrace(pw);
210                         next = next.getNextException();
211                 }
212                 pw.println(cannotConnect);
213         }
214         
215         public static void main(String[] args) {
216                 CFB cfb = new CFB(Locale.getDefault());
217                 
218                 try (PrintWriter pw = new PrintWriter(System.out)){
219                         cfb.doMain(pw, args);
220                         pw.flush();
221                 } catch (SQLException | IOException | XmlParseException | SAXException | TypeMismatchException exc) {
222                         exc.printStackTrace();
223                 }
224         }
225
226 }