View Javadoc

1   /*
2    StatCvs - CVS statistics generation
3    Copyright (C) 2002  Lukasz Pekacki <lukasz@pekacki.de>
4    http://statcvs.sf.net/
5   
6    This library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10  
11   This library is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   Lesser General Public License for more details.
15  
16   You should have received a copy of the GNU Lesser General Public
17   License along with this library; if not, write to the Free Software
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  
20   $RCSfile: Main.java,v $
21   Created on $Date: 2005/03/20 19:12:25 $
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 			// fall-back to older option.
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"); // primary is
132 		// statcvs.properties in net.sf.statsvn
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 		// max. 80 chars
159 		        // 12345678901234567890123456789012345678901234567890123456789012345678901234567890
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 		// make JFreeChart work on systems without GUI
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 			// add new reports
309 			final List extraReports = new ArrayList();
310 
311 			if ("xml".equalsIgnoreCase(ConfigurationOptions.getOutputFormat())) {
312 				new ReportSuiteMaker(config, ConfigurationOptions.getNotes(), extraReports).toXml();
313 			} else {
314 				//				extraReports.add(new RepoMapPageMaker(config).toFile());
315 				//				extraReports.add(new ChurnPageMaker(config).toFile());
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 }