Store db parameters in Config object, not in CFB object.
[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.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;
20
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;
33
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;
40
41 public class CFB {
42         DbDriver m_driver;
43         CfbSchema m_schema;
44         CfbBundle m_bundle;     
45         Locale m_locale;
46         
47         Config m_config;
48         
49         // Command-line parameters
50         File m_configFile;
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)
57         
58         CFB(Locale locale) {
59                 m_driver = new PostgresqlDriver();
60                 m_schema = new CfbSchema(m_driver);
61                 m_locale = locale;
62                 m_bundle = CfbBundle.getInst(m_locale);
63                 m_config = new Config();
64                 
65                 m_configFile = new File("config.properties");
66                 m_fbp    = null;
67                 m_fbDir  = null;
68                 m_projName = null;
69                 m_buildNum = null;
70                 m_removeSchema = false;
71                 m_output = null;
72         }
73         
74         Options createOptions() {
75                 Options opt = new Options();
76                 
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");
88                 
89                 return opt;
90         }
91         
92         boolean parseArgs(PrintWriter pw, String[] args) {
93                 Options opt = createOptions();
94                 
95                 try {
96                         CommandLine line = new GnuParser().parse(opt, args);
97                         if (line.hasOption("c")) {
98                                 m_configFile = new File(line.getOptionValue("c"));
99                         }
100                         if (line.hasOption("d")) {
101                                 m_config.setDbName(line.getOptionValue("d"));
102                         }
103                         if (line.hasOption("f")) {
104                                 m_fbp = new File(line.getOptionValue("f"));
105                         }
106                         if (line.hasOption("h")) {
107                                 m_config.setDbHost(line.getOptionValue("h"));
108                         }
109                         if (line.hasOption("j")) {
110                                 m_projName = line.getOptionValue("j");
111                         }
112                         if (line.hasOption("n")) {
113                                 m_buildNum = line.getOptionValue("n");
114                         }
115                         if (line.hasOption("o")) {
116                                 m_output = new File(line.getOptionValue("o"));
117                         }
118                         if (line.hasOption("p")) {
119                                 m_config.setDbPass(line.getOptionValue("p"));
120                         }
121                         m_removeSchema = line.hasOption("drop-tables");
122                         if (line.hasOption("t")) {
123                                 m_config.setDbPort(Integer.parseInt(line.getOptionValue("t")));
124                         }
125                         if (line.hasOption("u")) {
126                                 m_config.setDbUser(line.getOptionValue("u"));
127                         }
128                 } 
129                 catch (ParseException exc) {
130                         usage(pw, opt);
131                         return false;
132                 }
133                 
134                 return true;
135         }
136         
137         void usage(PrintWriter pw, Options opt) {
138                 HelpFormatter help = new HelpFormatter();
139                 help.printHelp(pw, 80, getClass().getName(), "", opt, 0, 0, "", true);
140         }
141         
142         String trans(String key) {
143                 return m_bundle.get(key);
144         }
145         
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);
149         }
150         
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);
154         }
155         
156         File getFindBugsDir() {
157                 return (null != m_fbDir) ? m_fbDir : new File(".");
158         }
159         
160         void initArgs() {
161                 String findBugsDir = getenv("FINDBUGS_HOME");
162                 if (null != findBugsDir) {
163                         m_fbDir = new File(findBugsDir);
164                 }
165                 findBugsDir = getProperty("findbugs.home");
166                 if (null != findBugsDir) {
167                         m_fbDir = new File(findBugsDir);
168                 }
169         }
170         
171         void readConfig() throws IOException {
172                 if (null != m_configFile) {
173                         m_config.readFile(m_configFile);
174                 }
175         }
176         
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) ) {
180                         return;
181                 }
182                 readConfig();
183
184                 File findBugsDir = getFindBugsDir();
185                 File workDir = new File(".");
186                 MessageMap messageMap = new MessageMap();
187                 messageMap.load(findBugsDir, Locale.getDefault(Category.DISPLAY));
188                 
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())
193                         ) 
194                 {
195                         m_schema.setMessageMap(messageMap);
196                         
197                         if (m_removeSchema) {
198                                 m_schema.purge(con);
199                                 return;
200                         }
201                         m_schema.ensureDbInitialized(con);
202                         messageMap.loadIds(con, m_driver);
203                 }
204                 catch (SQLException exc) {
205                         reportUnableToConnect(pw, exc);
206                         return;
207                 }
208                 
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));
213                         return;
214                 }
215                 
216                 try (
217                                 Connection con = m_driver.connect(
218                                                 m_config.getDbHost(), m_config.getDbPort(), 
219                                                 m_config.getDbName(),
220                                                 m_config.getDbUser(), m_config.getDbPass()
221                                         )
222                         )
223                 {
224                         DbStore store = new DbStore(con, m_driver, messageMap.getColl());
225                         
226                         store.put(analysis);
227                         Analysis prior = store.getPrior(analysis);
228                         Delta delta = new Delta(prior, analysis);
229
230                         HtmlReport report = new HtmlReport(m_bundle, messageMap.getColl(), delta);
231                         if (null != m_output) {
232                                 report.write(m_output);
233                         }
234                         
235                         Notifier notifier = new Notifier(m_bundle, m_config);
236                         notifier.sendEmailIfNeeded(pw, report);
237                 }
238                 catch (SQLException exc) {
239                         reportUnableToConnect(pw, exc);
240                         return;
241                 }
242         }
243
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(), 
250                                                                                         m_config.getDbUser()
251                                                                                 );
252                 exc.printStackTrace(pw);
253                 SQLException next = exc.getNextException();
254                 while (null != next) {
255                         next.printStackTrace(pw);
256                         next = next.getNextException();
257                 }
258                 pw.println(cannotConnect);
259         }
260         
261         public static void main(String[] args) {
262                 CFB cfb = new CFB(Locale.getDefault());
263                 
264                 try (PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out, Charset.defaultCharset()))) {
265                         cfb.doMain(pw, args);
266                         pw.flush();
267                 } catch (SQLException | IOException | XmlParseException | SAXException | TypeMismatchException exc) {
268                         exc.printStackTrace();
269                 }
270         }
271
272 }