edu.umd.cs.buildServer.inspection.PMDRunner.java Source code

Java tutorial

Introduction

Here is the source code for edu.umd.cs.buildServer.inspection.PMDRunner.java

Source

/**
 * Marmoset: a student project snapshot, submission, testing and code review
 * system developed by the Univ. of Maryland, College Park
 * 
 * Developed as part of Jaime Spacco's Ph.D. thesis work, continuing effort led
 * by William Pugh. See http://marmoset.cs.umd.edu/
 * 
 * Copyright 2005 - 2011, Univ. of Maryland
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 * 
 */

/*
 * Created on Mar 25, 2005
 */
package edu.umd.cs.buildServer.inspection;

import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.dom4j.Document;
import org.dom4j.Node;

import edu.umd.cs.buildServer.ConfigurationKeys;
import edu.umd.cs.buildServer.ProjectSubmission;
import edu.umd.cs.buildServer.XMLDocumentBuilder;
import edu.umd.cs.buildServer.util.Alarm;
import edu.umd.cs.buildServer.util.DevNullOutputStream;
import edu.umd.cs.buildServer.util.IO;
import edu.umd.cs.marmoset.modelClasses.JUnitTestProperties;
import edu.umd.cs.marmoset.modelClasses.TestOutcome;
import edu.umd.cs.marmoset.modelClasses.TestOutcomeCollection;
import edu.umd.cs.marmoset.utilities.MarmosetUtilities;

/**
 * Run PMD on a compiled submission.
 *
 * @see <a href="http://pmd.sourceforge.net">PMD website</a>
 * @author David Hovemeyer
 * @author Jaime Spacco
 */
public class PMDRunner implements ConfigurationKeys, ISubmissionInspectionStep<JUnitTestProperties> {
    private ProjectSubmission<JUnitTestProperties> projectSubmission;
    private TestOutcomeCollection testOutcomeCollection;

    /**
     * Constructor. setProjectSubmission() must be called before this object can
     * be executed.
     */
    public PMDRunner() {
        this.testOutcomeCollection = new TestOutcomeCollection();
    }

    private static final int PMD_TIMEOUT_IN_SECONDS = 120;

    @Override
    public void setProjectSubmission(ProjectSubmission<JUnitTestProperties> projectSubmission) {
        this.projectSubmission = projectSubmission;
    }

    @Override
    public TestOutcomeCollection getTestOutcomeCollection() {
        return testOutcomeCollection;
    }

    @Override
    public void execute() {
        projectSubmission.getLog().debug("Running PMD on the code");
        // rulesets of pmd detectors

        String RULESETS = "rulesets/basic.xml,rulesets/braces.xml,"
                + "rulesets/codesize.xml,rulesets/controversial.xml," + "rulesets/coupling.xml,rulesets/design.xml,"
                + "rulesets/naming.xml,rulesets/strictexception.xml,"
                + "rulesets/strings.xml,rulesets/unusedcode.xml";

        File buildServerRoot = projectSubmission.getConfig().getBuildServerRoot();

        RULESETS += "," + new File(buildServerRoot, "pmd/langfeatures.xml").getPath();

        // first stry the pmd.sh on the path
        String pmdExe = "pmd.sh";
        // if PMD_HOME is specified, use this instead
        String pmdHome = projectSubmission.getConfig().getConfig().getOptionalProperty(PMD_HOME);
        if (pmdHome != null) {
            pmdExe = new File(pmdHome, "bin/pmd.sh").toString();
        }
        List<String> args = new LinkedList<String>();
        args.add(pmdExe);
        args.add(projectSubmission.getZipFile().getPath());
        args.add("xml");
        args.add(RULESETS);

        projectSubmission.getLog().debug("pmd command: " + MarmosetUtilities.commandToString(args));

        Process process = null;
        boolean exited = false;
        Alarm alarm = new Alarm(PMD_TIMEOUT_IN_SECONDS, Thread.currentThread());
        try {
            process = Runtime.getRuntime().exec(args.toArray(new String[args.size()]));
            alarm.start();

            XMLDocumentBuilder stdoutMonitor = new XMLDocumentBuilder(process.getInputStream(),
                    projectSubmission.getLog());
            Thread stderrMonitor = IO.monitor(process.getErrorStream(), new DevNullOutputStream());
            stdoutMonitor.start();
            stderrMonitor.start();

            // Wait for process to exit
            process.waitFor();
            stdoutMonitor.join();
            stderrMonitor.join();
            exited = true;
            alarm.turnOff();

            readPMDTestOutcomes(stdoutMonitor);

        } catch (IOException e) {
            projectSubmission.getLog().warn("Could not run PMD", e);
        } catch (InterruptedException e) {
            projectSubmission.getLog().info("PMD process timed out", e);
        } finally {
            if (process != null && !exited) {
                process.destroy();
            }
        }
    }

    private void readPMDTestOutcomes(XMLDocumentBuilder stdoutMonitor) {
        Document document = stdoutMonitor.getDocument();
        if (document == null)
            return;

        int count = TestOutcome.FIRST_TEST_NUMBER;
        Iterator<?> fileNodeIter = document.selectNodes("//pmd/file").iterator();
        while (fileNodeIter.hasNext()) {
            Node fileElement = (Node) fileNodeIter.next();
            String fileName = fileElement.valueOf("@name");
            Iterator<?> violationIter = fileElement.selectNodes("./violation").iterator();
            while (violationIter.hasNext()) {
                Node violationElement = (Node) violationIter.next();
                String line = violationElement.valueOf("@line");
                String rule = violationElement.valueOf("@rule");
                String description = violationElement.getText();
                String priority = violationElement.valueOf("@priority");

                // Turn the warning into a TestOutcome
                TestOutcome testOutcome = new TestOutcome();
                testOutcome.setTestType(TestOutcome.TestType.PMD);
                testOutcome.setTestName(rule);
                testOutcome.setOutcome(TestOutcome.STATIC_ANALYSIS);
                testOutcome.setShortTestResult(fileName + ":" + line);
                testOutcome.setLongTestResultCompressIfNeeded(description);
                testOutcome.setTestNumber(Integer.toString(count++));
                testOutcome.setExceptionClassName(priority); // XXX: HACK!

                testOutcomeCollection.add(testOutcome);
            }
        }

        projectSubmission.getLog().info("Recorded " + count + " PMD warnings as test outcomes");
    }

}