001 /* 002 * Sonar, open source software quality management tool. 003 * Copyright (C) 2008-2011 SonarSource 004 * mailto:contact AT sonarsource DOT com 005 * 006 * Sonar is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 3 of the License, or (at your option) any later version. 010 * 011 * Sonar 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 GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with Sonar; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 019 */ 020 package org.sonar.plugins.pmd; 021 022 import net.sourceforge.pmd.*; 023 import net.sourceforge.pmd.renderers.Renderer; 024 import net.sourceforge.pmd.renderers.XMLRenderer; 025 import org.apache.commons.io.FileUtils; 026 import org.apache.commons.io.IOUtils; 027 import org.apache.commons.lang.StringUtils; 028 import org.slf4j.Logger; 029 import org.slf4j.LoggerFactory; 030 import org.sonar.api.BatchExtension; 031 import org.sonar.api.resources.Java; 032 import org.sonar.api.resources.Project; 033 import org.sonar.api.utils.SonarException; 034 import org.sonar.api.utils.TimeProfiler; 035 import org.sonar.java.api.JavaUtils; 036 037 import java.io.*; 038 import java.util.List; 039 040 public class PmdExecutor implements BatchExtension { 041 042 private static final Logger LOG = LoggerFactory.getLogger(PmdExecutor.class); 043 044 private PmdConfiguration configuration; 045 private Project project; 046 047 public PmdExecutor(Project project, PmdConfiguration configuration) { 048 this.project = project; 049 this.configuration = configuration; 050 } 051 052 public File execute() throws IOException, PMDException { 053 TimeProfiler profiler = new TimeProfiler().start("Execute PMD " + PmdVersion.getVersion()); 054 055 ClassLoader initialClassLoader = Thread.currentThread().getContextClassLoader(); 056 Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); 057 try { 058 PMD pmd = new PMD(); 059 setJavaVersion(pmd, project); 060 RuleContext ruleContext = new RuleContext(); 061 Report report = new Report(); 062 ruleContext.setReport(report); 063 064 RuleSets rulesets = createRulesets(); 065 066 for (File file : project.getFileSystem().getSourceFiles(Java.INSTANCE)) { 067 ruleContext.setSourceCodeFilename(file.getAbsolutePath()); 068 Reader fileReader = new InputStreamReader(new FileInputStream(file), project.getFileSystem().getSourceCharset()); 069 try { 070 pmd.processFile(fileReader, rulesets, ruleContext); 071 072 } catch (PMDException e) { 073 LOG.error("Fail to execute PMD. Following file is ignored: " + file, e.getCause()); 074 075 } catch (Exception e) { 076 LOG.error("Fail to execute PMD. Following file is ignored: " + file, e); 077 078 } finally { 079 IOUtils.closeQuietly(fileReader); 080 } 081 } 082 083 return writeXmlReport(project, report); 084 085 } finally { 086 profiler.stop(); 087 Thread.currentThread().setContextClassLoader(initialClassLoader); 088 } 089 } 090 091 private RuleSets createRulesets() { 092 RuleSets rulesets = new RuleSets(); 093 RuleSetFactory ruleSetFactory = new RuleSetFactory(); 094 095 List<String> rulesetPaths = configuration.getRulesets(); 096 LOG.info("PMD configuration: " + StringUtils.join(rulesetPaths, ", ")); 097 098 for (String rulesetPath : rulesetPaths) { 099 InputStream rulesInput = openRuleset(rulesetPath); 100 rulesets.addRuleSet(ruleSetFactory.createRuleSet(rulesInput)); 101 IOUtils.closeQuietly(rulesInput); 102 } 103 return rulesets; 104 } 105 106 private InputStream openRuleset(String rulesetPath) { 107 try { 108 File file = new File(rulesetPath); 109 boolean found; 110 if (file.exists()) { 111 found = true; 112 } else { 113 file = new File(project.getFileSystem().getBasedir(), rulesetPath); 114 found = file.exists(); 115 } 116 if (found) { 117 return new FileInputStream(file); 118 } 119 InputStream stream = getClass().getResourceAsStream(rulesetPath); 120 if (stream == null) { 121 throw new RuntimeException("The PMD ruleset can not be found: " + rulesetPath); 122 } 123 return stream; 124 125 } catch (FileNotFoundException e) { 126 throw new SonarException("The PMD ruleset can not be found: " + rulesetPath, e); 127 } 128 } 129 130 private File writeXmlReport(Project project, Report report) throws IOException { 131 Renderer xmlRenderer = new XMLRenderer(); 132 Writer stringwriter = new StringWriter(); 133 xmlRenderer.setWriter(stringwriter); 134 xmlRenderer.start(); 135 xmlRenderer.renderFileReport(report); 136 xmlRenderer.end(); 137 138 File xmlReport = new File(project.getFileSystem().getSonarWorkingDirectory(), "pmd-result.xml"); 139 LOG.info("PMD output report: " + xmlReport.getAbsolutePath()); 140 FileUtils.write(xmlReport, stringwriter.toString()); 141 return xmlReport; 142 } 143 144 static String getNormalizedJavaVersion(String javaVersion) { 145 if (StringUtils.equals("1.1", javaVersion) || StringUtils.equals("1.2", javaVersion)) { 146 javaVersion = "1.3"; 147 } else if (StringUtils.equals("5", javaVersion)) { 148 javaVersion = "1.5"; 149 } else if (StringUtils.equals("6", javaVersion)) { 150 javaVersion = "1.6"; 151 } 152 return javaVersion; 153 } 154 155 private void setJavaVersion(PMD pmd, Project project) { 156 String javaVersion = getNormalizedJavaVersion(JavaUtils.getSourceVersion(project)); 157 if (javaVersion != null) { 158 SourceType sourceType = SourceType.getSourceTypeForId("java " + javaVersion); 159 if (sourceType != null) { 160 LOG.info("Java version: " + javaVersion); 161 pmd.setJavaVersion(sourceType); 162 } else { 163 throw new SonarException("Unsupported Java version for PMD: " + javaVersion); 164 } 165 } 166 } 167 }