1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package net.sf.statsvn;
24
25 import java.io.FileInputStream;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.PrintWriter;
29 import java.io.StringWriter;
30 import java.util.ArrayList;
31 import java.util.List;
32 import java.util.Locale;
33 import java.util.logging.LogManager;
34
35 import net.sf.statcvs.Messages;
36 import net.sf.statcvs.input.LogSyntaxException;
37 import net.sf.statcvs.model.Repository;
38 import net.sf.statcvs.output.ConfigurationException;
39 import net.sf.statcvs.output.ConfigurationOptions;
40 import net.sf.statcvs.output.ReportConfig;
41 import net.sf.statcvs.pages.ReportSuiteMaker;
42 import net.sf.statsvn.input.Builder;
43 import net.sf.statsvn.input.RepositoryFileManager;
44 import net.sf.statsvn.input.SvnLogfileParser;
45 import net.sf.statsvn.output.SvnCommandLineParser;
46 import net.sf.statsvn.output.SvnConfigurationOptions;
47 import net.sf.statsvn.util.SvnStartupUtils;
48 import net.sf.statsvn.util.SvnVersionMismatchException;
49
50 /**
51 * StatSvn Main Class; it starts the application and controls command-line
52 * related stuff
53 *
54 * @author Lukasz Pekacki
55 * @author Richard Cyganiak
56 * @version $Id: Main.java,v 1.47 2005/03/20 19:12:25 squig Exp $
57 */
58 public final class Main {
59 private static final int KB_IN_ONE_MB = 1024;
60
61 private static final int NUMBER_OF_MS_IN_ONE_SEC = 1000;
62
63 private static final LogManager LM = LogManager.getLogManager();
64
65 /**
66 * A utility class (only static methods) should be final and have a private
67 * constructor.
68 */
69 private Main() {
70 }
71
72 /**
73 * Main method of StatSVN
74 *
75 * @param args
76 * command line options
77 */
78 public static void main(final String[] args) {
79 init();
80 verifyArguments(args);
81 generate();
82 System.exit(0);
83 }
84
85 private static void verifyArguments(final String[] args) {
86 if (args.length == 0) {
87 printProperUsageAndExit();
88 }
89 if (args.length == 1) {
90 final String arg = args[0].toLowerCase(Locale.getDefault());
91 if (arg.equals("-h") || arg.equals("-help")) {
92 printProperUsageAndExit();
93 } else if (arg.equals("-version")) {
94 printVersionAndExit();
95 }
96 }
97
98 try {
99 new SvnCommandLineParser(args).parse();
100 } catch (final ConfigurationException cex) {
101 SvnConfigurationOptions.getTaskLogger().error(cex.getMessage());
102 System.exit(1);
103 }
104 }
105
106 public static void generate() {
107 try {
108 final boolean isNewerDiffPossible = SvnStartupUtils.checkDiffPerRevPossible(SvnStartupUtils.checkSvnVersionSufficient());
109
110 if (!isNewerDiffPossible) {
111 SvnConfigurationOptions.setLegacyDiff(true);
112 }
113
114 SvnStartupUtils.checkRepoRootAvailable();
115 generateDefaultHTMLSuite();
116 } catch (final ConfigurationException cex) {
117 SvnConfigurationOptions.getTaskLogger().error(cex.getMessage());
118 System.exit(1);
119 } catch (final LogSyntaxException lex) {
120 printLogErrorMessageAndExit(lex.getMessage());
121 } catch (final IOException ioex) {
122 printIoErrorMessageAndExit(ioex.getMessage());
123 } catch (final OutOfMemoryError oome) {
124 printOutOfMemMessageAndExit();
125 } catch (final SvnVersionMismatchException ever) {
126 printErrorMessageAndExit(ever.getMessage());
127 }
128 }
129
130 public static void init() {
131 Messages.setPrimaryResource("net.sf.statsvn.statcvs");
132
133
134 SvnConfigurationOptions.getTaskLogger().info(Messages.getString("PROJECT_NAME") + Messages.NL);
135 }
136
137 private static void initLogManager(final String loggingProperties) {
138 InputStream stream = null;
139 try {
140 stream = Main.class.getResourceAsStream(loggingProperties);
141 LM.readConfiguration(stream);
142 } catch (final IOException e) {
143 SvnConfigurationOptions.getTaskLogger().error("ERROR: Logging could not be initialized!");
144 } finally {
145 if (stream != null) {
146 try {
147 stream.close();
148 } catch (final IOException e) {
149 SvnConfigurationOptions.getTaskLogger().error("ERROR: could not close stream!");
150 }
151 }
152 }
153 }
154
155 private static void printProperUsageAndExit() {
156 final String cr = System.getProperty("line.separator");
157 SvnConfigurationOptions.getTaskLogger().error(
158
159
160 "Usage: java -jar statsvn.jar [options] <logfile> <directory>" + cr + cr + "Required parameters:" + cr
161 + " <logfile> path to the svn logfile of the module" + cr
162 + " <directory> path to the directory of the checked out module" + cr + cr
163 + "Some options:" + cr
164 + " -version print the version information and exit" + cr
165 + " -output-dir <dir> directory where HTML suite will be saved" + cr
166 + " -include <pattern> include only files matching pattern, e.g. **/*.c;**/*.h" + cr
167 + " -exclude <pattern> exclude matching files, e.g. tests/**;docs/**" + cr
168 + " -tags <regexp> show matching tags in lines of code chart, e.g. version-.*" + cr
169 + " -title <title> Project title to be used in reports" + cr
170 + " -viewvc <url> integrate with ViewVC installation at <url>" +cr
171 + " -trac <url> integrate with Trac at <url>" + cr
172 + " -bugzilla <url> integrate with Bugzilla installation at <url>" + cr
173 + " -username <svnusername> username to pass to svn" + cr
174 + " -password <svnpassword> password to pass to svn" + cr
175 + " -verbose print extra progress information" + cr
176 + " -xdoc optional switch output to xdoc" + cr
177 + " -xml optional switch output to xml" + cr
178 + " -threads <int> how many threads for svn diff (default: 25)" + cr
179 + " -concurrency-threshold <millisec> switch to concurrent svn diff if 1st call>threshold (default: 4000)" + cr
180 + " -dump dump the Repository content on console" + cr
181 + " -charset <charset> specify the charset to use for html/xdoc\n"
182 + " -tags-dir <directory> optional, specifies the director for tags (default '/tags/')" + cr + cr
183 + "Full options list: http://www.statsvn.org");
184 System.exit(1);
185 }
186
187 private static void printVersionAndExit() {
188 SvnConfigurationOptions.getTaskLogger().error("Version " + Messages.getString("PROJECT_VERSION"));
189 System.exit(1);
190 }
191
192 private static void printOutOfMemMessageAndExit() {
193 SvnConfigurationOptions.getTaskLogger().error("OutOfMemoryError.");
194 SvnConfigurationOptions.getTaskLogger().error("Try running java with the -mx option (e.g. -mx128m for 128Mb).");
195 System.exit(1);
196 }
197
198 private static void printLogErrorMessageAndExit(final String message) {
199 SvnConfigurationOptions.getTaskLogger().error("Logfile parsing failed.");
200 SvnConfigurationOptions.getTaskLogger().error(message);
201 System.exit(1);
202 }
203
204 private static void printIoErrorMessageAndExit(final String message) {
205 SvnConfigurationOptions.getTaskLogger().error(message);
206 System.exit(1);
207 }
208
209 public static String printStackTrace(final Exception e) {
210 try {
211 final StringWriter sw = new StringWriter();
212 final PrintWriter pw = new PrintWriter(sw);
213 e.printStackTrace(pw);
214 return sw.toString();
215 } catch (final Exception e2) {
216 if (e != null) {
217 return e.getMessage();
218 } else {
219 return "";
220 }
221 }
222 }
223
224 private static void printErrorMessageAndExit(final String message) {
225 SvnConfigurationOptions.getTaskLogger().error(message);
226 System.exit(1);
227 }
228
229 /**
230 * Generates HTML report. {@link net.sf.statsvn.output.ConfigurationOptions}
231 * must be initialized before calling this method.
232 *
233 * @throws LogSyntaxException
234 * if the logfile contains unexpected syntax
235 * @throws IOException
236 * if some file can't be read or written
237 * @throws ConfigurationException
238 * if a required ConfigurationOption was not set
239 */
240 public static void generateDefaultHTMLSuite() throws LogSyntaxException, IOException, ConfigurationException {
241 generateDefaultHTMLSuite(new RepositoryFileManager(ConfigurationOptions.getCheckedOutDirectory()));
242 }
243
244 /**
245 * Generates HTML report. {@link net.sf.statsvn.output.ConfigurationOptions}
246 * must be initialized before calling this method.
247 *
248 * @param externalRepositoryFileManager
249 * RepositoryFileManager which is used to access the files in the
250 * repository.
251 *
252 * @throws LogSyntaxException
253 * if the logfile contains unexpected syntax
254 * @throws IOException
255 * if some file can't be read or written
256 * @throws ConfigurationException
257 * if a required ConfigurationOption was not set
258 */
259 public static void generateDefaultHTMLSuite(final RepositoryFileManager repFileMan) throws LogSyntaxException, IOException, ConfigurationException {
260
261 if (ConfigurationOptions.getLogFileName() == null) {
262 throw new ConfigurationException("Missing logfile name");
263 }
264 if (ConfigurationOptions.getCheckedOutDirectory() == null) {
265 throw new ConfigurationException("Missing checked out directory");
266 }
267
268 final long memoryUsedOnStart = Runtime.getRuntime().totalMemory();
269 final long startTime = System.currentTimeMillis();
270
271 initLogManager(ConfigurationOptions.getLoggingProperties());
272
273 SvnConfigurationOptions.getTaskLogger().info(
274 "Parsing SVN log '" + ConfigurationOptions.getLogFileName() + "' exclude pattern '" + ConfigurationOptions.getExcludePattern() + "'");
275
276 final FileInputStream logFile = new FileInputStream(ConfigurationOptions.getLogFileName());
277 final Builder builder = new Builder(repFileMan, ConfigurationOptions.getIncludePattern(), ConfigurationOptions.getExcludePattern(),
278 ConfigurationOptions.getSymbolicNamesPattern());
279 new SvnLogfileParser(repFileMan, logFile, builder).parse();
280 logFile.close();
281
282 if (ConfigurationOptions.getProjectName() == null) {
283 ConfigurationOptions.setProjectName(builder.getProjectName());
284 }
285 if (ConfigurationOptions.getWebRepository() != null) {
286 ConfigurationOptions.getWebRepository().setAtticFileNames(builder.getAtticFileNames());
287 }
288
289 SvnConfigurationOptions.getTaskLogger().info(
290 "Generating report for " + ConfigurationOptions.getProjectName() + " into " + ConfigurationOptions.getOutputDir());
291 SvnConfigurationOptions.getTaskLogger().info("Using " + ConfigurationOptions.getCssHandler());
292 final Repository content = builder.createRepository();
293
294
295 System.setProperty("java.awt.headless", "true");
296
297 final ReportConfig config = new ReportConfig(content, ConfigurationOptions.getProjectName(), ConfigurationOptions.getOutputDir(), ConfigurationOptions
298 .getMarkupSyntax(), ConfigurationOptions.getCssHandler(), ConfigurationOptions.getCharSet());
299 config.setWebRepository(ConfigurationOptions.getWebRepository());
300 config.setWebBugtracker(ConfigurationOptions.getWebBugtracker());
301 config.setNonDeveloperLogins(ConfigurationOptions.getNonDeveloperLogins());
302
303 validate(config);
304
305 if (SvnConfigurationOptions.isDumpContent()) {
306 new RepoDump(content).dump();
307 } else {
308
309 final List extraReports = new ArrayList();
310
311 if ("xml".equalsIgnoreCase(ConfigurationOptions.getOutputFormat())) {
312 new ReportSuiteMaker(config, ConfigurationOptions.getNotes(), extraReports).toXml();
313 } else {
314
315
316 new ReportSuiteMaker(config, ConfigurationOptions.getNotes(), extraReports).toFile().write();
317 }
318 }
319 final long endTime = System.currentTimeMillis();
320 final long memoryUsedOnEnd = Runtime.getRuntime().totalMemory();
321
322 SvnConfigurationOptions.getTaskLogger().info("runtime: " + (((double) endTime - startTime) / NUMBER_OF_MS_IN_ONE_SEC) + " seconds");
323 SvnConfigurationOptions.getTaskLogger().info("memory usage: " + (((double) memoryUsedOnEnd - memoryUsedOnStart) / KB_IN_ONE_MB) + " kb");
324 }
325
326 private static void validate(final ReportConfig config) {
327 if (config.getRepository() == null || config.getRepository().getRoot() == null || config.getRepository().getDirectories() == null) {
328 printErrorMessageAndExit("The repository object is not valid. Please check your settings." + System.getProperty("line.separator")
329 + "Is the log file empty? Do you run from a checked out directory? Do you have non-committed items?");
330 }
331 }
332 }