001: /*
002: * Hammurapi
003: * Automated Java code review system.
004: * Copyright (C) 2004 Hammurapi Group
005: *
006: * This program is free software; you can redistribute it and/or modify
007: * it under the terms of the GNU General Public License as published by
008: * the Free Software Foundation; either version 2 of the License, or
009: * (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: *
020: * URL: http://www.hammurapi.org
021: * e-Mail: support@hammurapi.biz
022: */
023: package org.hammurapi;
024:
025: import java.io.File;
026: import java.io.FileInputStream;
027: import java.io.FileNotFoundException;
028: import java.io.FileReader;
029: import java.io.IOException;
030: import java.io.InputStream;
031: import java.io.InputStreamReader;
032: import java.io.Reader;
033: import java.sql.SQLException;
034: import java.text.MessageFormat;
035: import java.util.ArrayList;
036: import java.util.Collection;
037: import java.util.Date;
038: import java.util.Iterator;
039: import java.util.LinkedList;
040: import java.util.List;
041:
042: import org.apache.commons.cli.CommandLine;
043: import org.apache.commons.cli.CommandLineParser;
044: import org.apache.commons.cli.Options;
045: import org.apache.commons.cli.PosixParser;
046: import org.apache.tools.ant.AntClassLoader;
047: import org.apache.tools.ant.BuildException;
048: import org.apache.tools.ant.DirectoryScanner;
049: import org.apache.tools.ant.Project;
050: import org.hammurapi.results.CompositeResults;
051: import org.hammurapi.results.ResultsFactory;
052: import org.hammurapi.results.quick.PackageTotal;
053: import org.hammurapi.results.simple.QuickResultsFactory;
054:
055: import com.pavelvlasov.config.Component;
056: import com.pavelvlasov.config.ConfigurationException;
057: import com.pavelvlasov.jsel.JselException;
058: import com.pavelvlasov.jsel.impl.CompilationUnitImpl;
059: import com.pavelvlasov.logging.AntLogger;
060: import com.pavelvlasov.logging.Logger;
061: import com.pavelvlasov.sql.DataAccessObject;
062: import com.pavelvlasov.util.ClassResourceLoader;
063: import com.pavelvlasov.util.VisitorStack;
064: import com.pavelvlasov.util.VisitorStackSource;
065:
066: /**
067: * Performs automatic code reviews. Quick mode - doesn't store anything to the database,
068: * package and repository level inspectors are not invoked.
069: * <section name="Example" suppress-description="yes">
070: If you copy content of Hammurapi lib directory to ant lib directory then you can
071: invoke Hammurapi in the following way:
072: <pre>
073: <taskdef name="quickurappi" classname="org.hammurapi.QuickHammurapiTask" /><br/>
074: <br/>
075: <quickurappi><br/>
076: <tab/><src dir="src"/><br/>
077: <tab/><output dir="review"/><br/>
078: </quickurappi></pre>
079: or, if you didn't copy jar files to Ant lib directory, use this syntax:
080: <pre>
081: <taskdef name="quickurappi" classname="org.hammurapi.QuickHammurapiTask"><br/>
082: <tab/><classpath><br/>
083: <tab/><tab/><fileset dir="${hammurapi.home}/lib" includes="*.jar"/><br/>
084: <tab/></classpath><br/>
085: </taskdef><br/>
086: <br/>
087: <quickurappi><br/>
088: <tab/><src dir="src"/><br/>
089: <tab/><output dir="review"/><br/>
090: </quickurappi>
091: </pre>
092:
093: </section>
094: * @ant.element name="hammurapi" display-name="Automatic code review task"
095: * @author Pavel Vlasov
096: * @version $Revision: 1.10 $
097: */
098: public class QuickHammurapiTask extends TaskBase {
099: public void execute() throws BuildException {
100: if (!suppressLogo) {
101: log("Quick Hammurapi 3 Copyright (C) 2004 Hammurapi Group");
102: }
103:
104: File archiveTmpDir = processArchive();
105:
106: try {
107: long start = System.currentTimeMillis();
108:
109: Logger logger = new AntLogger(this );
110:
111: final VisitorStack[] visitorStack = { null };
112: final VisitorStackSource visitorStackSource = new VisitorStackSource() {
113: public VisitorStack getVisitorStack() {
114: return visitorStack[0];
115: }
116: };
117:
118: final SessionImpl session = new SessionImpl();
119:
120: InspectorSet inspectorSet = new InspectorSet(
121: new InspectorContextFactory() {
122: public InspectorContext newContext(
123: InspectorDescriptor descriptor,
124: Logger logger) {
125: return new InspectorContextImpl(descriptor,
126: logger, visitorStackSource,
127: session, violationFilters);
128: }
129: }, logger);
130:
131: if (embeddedInspectors) {
132: log("Loading embedded inspectors", Project.MSG_VERBOSE);
133: loadEmbeddedInspectors(inspectorSet);
134: }
135:
136: log("Loading inspectors", Project.MSG_VERBOSE);
137: Iterator it = inspectors.iterator();
138: while (it.hasNext()) {
139: Object o = it.next();
140: if (o instanceof InspectorSource) {
141: ((InspectorSource) o).loadInspectors(inspectorSet);
142: } else {
143: InspectorEntry inspectorEntry = (InspectorEntry) o;
144: inspectorSet.addDescriptor(inspectorEntry);
145: inspectorSet
146: .addInspectorSourceInfo(new InspectorSourceInfo(
147: "Inline inspector "
148: + inspectorEntry.getName(),
149: "Build file: "
150: + inspectorEntry
151: .getLocation()
152: .toString(), ""));
153: }
154: }
155:
156: log("Inspectors loaded: " + inspectorSet.size(),
157: Project.MSG_VERBOSE);
158:
159: log("Loading waivers", Project.MSG_VERBOSE);
160: Date now = new Date();
161: WaiverSet waiverSet = new WaiverSet();
162: it = waivers.iterator();
163: while (it.hasNext()) {
164: ((WaiverSource) it.next()).loadWaivers(waiverSet, now);
165: }
166:
167: log("Waivers loaded: " + waiverSet.size(),
168: Project.MSG_VERBOSE);
169:
170: log("Loading listeners", Project.MSG_VERBOSE);
171: List listeners = new LinkedList();
172: it = listenerEntries.iterator();
173: while (it.hasNext()) {
174: listeners.add(((ListenerEntry) it.next())
175: .getObject(null));
176: }
177:
178: //Outputs
179: listeners.addAll(outputs);
180: listeners.add(new ReviewToLogListener(project));
181:
182: Collection inspectors = new LinkedList(inspectorSet
183: .getInspectors());
184: session.setInspectors(inspectorSet);
185: Iterator inspectorsIt = inspectors.iterator();
186: log("Inspectors mapping", Project.MSG_VERBOSE);
187: while (inspectorsIt.hasNext()) {
188: Inspector inspector = (Inspector) inspectorsIt.next();
189: log("\t"
190: + inspector.getContext().getDescriptor()
191: .getName() + " -> "
192: + inspector.getClass().getName(),
193: Project.MSG_VERBOSE);
194: }
195:
196: ClassLoader classLoader;
197: if (classPath == null) {
198: classLoader = this .getClass().getClassLoader();
199: } else {
200: classLoader = new AntClassLoader(project, classPath,
201: false);
202: session.setClassPath(classPath.list());
203: }
204:
205: new QuickResultsFactory(waiverSet, classLoader, tabSize,
206: logger).install();
207:
208: Iterator lit = listeners.iterator();
209: while (lit.hasNext()) {
210: ((Listener) lit.next()).onBegin(inspectorSet);
211: }
212:
213: try {
214: QuickResultsCollector collector = new QuickResultsCollector(
215: this , title, listeners);
216:
217: // Initializing violation filters
218: Iterator vfit = violationFilters.iterator();
219: while (vfit.hasNext()) {
220: Object vf = vfit.next();
221: if (vf instanceof DataAccessObject) {
222: ((DataAccessObject) vf)
223: .setSQLProcessor(collector
224: .getProcessor());
225: }
226:
227: if (vf instanceof Component) {
228: ((Component) vf).start();
229: }
230: }
231:
232: try {
233: inspectors.add(collector);
234:
235: QuickReviewEngine engine = new QuickReviewEngine(
236: inspectors, this , collector);
237: visitorStack[0] = engine.getVisitorStack();
238: session.setVisitor(engine.getVisitor());
239:
240: it = srcFileSets.iterator();
241: while (it.hasNext()) {
242: HammurapiFileSet fs = (HammurapiFileSet) it
243: .next();
244: fs.setDefaultIncludes();
245: DirectoryScanner scanner = fs
246: .getDirectoryScanner(project);
247: String[] includedFiles = scanner
248: .getIncludedFiles();
249: for (int i = 0; i < includedFiles.length; i++) {
250: review(new File(scanner.getBasedir(),
251: includedFiles[i]), engine,
252: classLoader, logger);
253: }
254: }
255:
256: it = srcFiles.iterator();
257: while (it.hasNext()) {
258: review((File) it.next(), engine, classLoader,
259: logger);
260: }
261:
262: collector.getEngine().deleteOld();
263:
264: Collection packageResults = new ArrayList();
265: Iterator pit = collector.getEngine()
266: .getPackageTotal().iterator();
267: while (pit.hasNext()) {
268: CompositeResults packageResult = new QuickPackageResults(
269: (PackageTotal) pit.next(), collector,
270: inspectorSet);
271: packageResult.commit();
272: Iterator llit = listeners.iterator();
273: while (llit.hasNext()) {
274: ((Listener) llit.next())
275: .onPackage(packageResult);
276: }
277:
278: packageResults.add(packageResult);
279: }
280:
281: log("Building summary");
282:
283: CompositeResults summary = new QuickSummary(title,
284: collector, inspectorSet, packageResults);
285: ResultsFactory.pushThreadResults(summary);
286:
287: // Stopping violation filters
288: vfit = violationFilters.iterator();
289: while (vfit.hasNext()) {
290: Object vf = vfit.next();
291: if (vf instanceof Component) {
292: ((Component) vf).stop();
293: }
294: }
295:
296: Iterator slit = listeners.iterator();
297: while (slit.hasNext()) {
298: ((Listener) slit.next()).onSummary(summary,
299: inspectorSet);
300: }
301:
302: Iterator rit = getReviewAcceptorEntries()
303: .iterator();
304: while (rit.hasNext()) {
305: ((ReviewAcceptor) ((ReviewAcceptorEntry) rit
306: .next()).getObject(null))
307: .accept(summary);
308: }
309:
310: long finish = System.currentTimeMillis();
311:
312: long elapsedSec = (finish - start) / 1000;
313: long min = elapsedSec / 60;
314: long sec = elapsedSec % 60;
315:
316: log("Time: " + min + " min. " + sec + " sec.");
317: log(MessageFormat.format(
318: "Performance {0, number,###.000000}",
319: new Object[] { new Double((double) summary
320: .getCodeBase()
321: * 1000 / (finish - start)) }));
322:
323: Integer severityThreshold = getSeverityThreshold();
324: if (severityThreshold != null) {
325: final int sth = getSeverityThreshold()
326: .intValue();
327: new ReviewAcceptor() {
328: public void accept(CompositeResults summary)
329: throws HammurapiException {
330: Number severity = summary
331: .getMaxSeverity();
332: if (severity != null
333: && severity.intValue() <= sth) {
334: throw new HammurapiException(
335: "Severity threshold ("
336: + sth
337: + ") infringed");
338: }
339: }
340: }.accept(summary);
341: }
342:
343: Double sigmaThreshold = getSigmaThreshold();
344: if (sigmaThreshold != null) {
345: final double cth = sigmaThreshold.doubleValue();
346: new ReviewAcceptor() {
347: public void accept(CompositeResults summary)
348: throws HammurapiException {
349: try {
350: if (Double.parseDouble(summary
351: .getSigma()) < cth) {
352: throw new HammurapiException(
353: "Sigma is below threshold ("
354: + cth + ")");
355: }
356: } catch (NumberFormatException e) {
357: throw new HammurapiException(
358: "Sigma is not valid");
359: }
360: }
361: }.accept(summary);
362: }
363:
364: Integer dpmoThreshold = getDpmoThreshold();
365: if (dpmoThreshold != null) {
366: final int cth = dpmoThreshold.intValue();
367: new ReviewAcceptor() {
368: public void accept(CompositeResults summary)
369: throws HammurapiException {
370: try {
371: if (Integer.parseInt(summary
372: .getDPMO()) > cth) {
373: throw new HammurapiException(
374: "DPMO is above threshold ("
375: + cth + ")");
376: }
377: } catch (NumberFormatException e) {
378: throw new HammurapiException(
379: "DPMO is not valid");
380: }
381: }
382: }.accept(summary);
383: }
384:
385: if (isFailOnWarnings()
386: && !summary.getWarnings().isEmpty()) {
387: throw new HammurapiNonConsumableException(
388: "There have been warnings during execution.");
389: }
390:
391: summary.commit();
392: } finally {
393: session.shutdown();
394: collector.shutdown();
395: }
396: } finally {
397: if (archiveTmpDir != null) {
398: deleteFile(archiveTmpDir);
399: }
400: }
401:
402: if (hadExceptions) {
403: throw new BuildException(
404: "There have been exceptions during execution. Check log output.");
405: }
406: } catch (ClassNotFoundException e) {
407: throw new BuildException(e);
408: } catch (SQLException e) {
409: throw new BuildException(e);
410: } catch (JselException e) {
411: throw new BuildException(e);
412: } catch (HammurapiException e) {
413: throw new BuildException(e);
414: } catch (ConfigurationException e) {
415: throw new BuildException(e);
416: } catch (FileNotFoundException e) {
417: throw new BuildException(e);
418: } catch (IOException e) {
419: throw new BuildException(e);
420: }
421: }
422:
423: /**
424: * @param file
425: * @param engine
426: * @param classLoader
427: * @param logger
428: * @throws JselException
429: * @throws FileNotFoundException
430: * @throws SQLException
431: */
432: private void review(File file, QuickReviewEngine engine,
433: ClassLoader classLoader, Logger logger)
434: throws JselException, FileNotFoundException, SQLException,
435: IOException {
436: if (file.isFile()) {
437: _review(file, engine, classLoader, logger);
438: } else if (file.isDirectory()) {
439: File[] files = file.listFiles();
440: for (int i = 0; i < files.length; i++) {
441: review(files[i], engine, classLoader, logger);
442: }
443: }
444: }
445:
446: /**
447: * @param file
448: * @param engine
449: * @param classLoader
450: * @param logger
451: * @throws JselException
452: * @throws FileNotFoundException
453: * @throws SQLException
454: */
455: private void _review(File file, QuickReviewEngine engine,
456: ClassLoader classLoader, Logger logger)
457: throws JselException, FileNotFoundException, SQLException,
458: IOException {
459: InputStream is = new FileInputStream(file);
460: Reader reader = getEncoding() == null ? new InputStreamReader(
461: is) : new InputStreamReader(is, getEncoding());
462: try {
463: engine.review(new CompilationUnitImpl(reader,
464: getEncoding(), null, file.getAbsolutePath(),
465: tabSize, classLoader, logger));
466: } finally {
467: reader.close();
468: is.close();
469: }
470: }
471:
472: /**
473: * Use it for inspector debugging
474: * @param args
475: */
476: public static void main(String[] args) {
477: System.out
478: .println("Hammurapi 3 Copyright (C) 2004 Hammurapi Group");
479:
480: Options options = new Options();
481:
482: populateOptions(options);
483:
484: CommandLineParser parser = new PosixParser();
485: CommandLine line = null;
486: try {
487: line = parser.parse(options, args);
488: } catch (org.apache.commons.cli.ParseException e) {
489: System.err.println(e.getMessage());
490: System.err.flush();
491: printHelpAndExit(options);
492: }
493:
494: if (line.hasOption("h")) {
495: printHelpAndExit(options);
496: }
497:
498: QuickHammurapiTask task = new QuickHammurapiTask();
499: Project project = new Project();
500: task.setProject(project);
501: project.setCoreLoader(task.getClass().getClassLoader());
502:
503: task.configure(options, line);
504:
505: task.suppressLogo = true;
506:
507: task.setTaskName("quickurapi");
508:
509: try {
510: task.execute();
511: System.exit(0);
512: } catch (Exception e) {
513: e.printStackTrace();
514: System.exit(2);
515: }
516: }
517:
518: protected static void loadEmbeddedInspectors(
519: InspectorSet inspectorSet) throws BuildException,
520: HammurapiException {
521: TaskBase.loadEmbeddedInspectors(inspectorSet);
522:
523: ClassResourceLoader crl = new ClassResourceLoader(
524: QuickHammurapiTask.class);
525: InputStream inspectorStream = crl.getResourceAsStream(null,
526: null, "xml");
527: if (inspectorStream == null) {
528: throw new BuildException(
529: "Cannot load quick embedded inspectors");
530: }
531:
532: DomInspectorSource source = new DomInspectorSource(
533: inspectorStream, "Hammurapi.jar");
534: source.loadInspectors(inspectorSet);
535: }
536: }
|