1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugins.pmd.exec;
20
21 import java.io.File;
22 import java.io.FileInputStream;
23 import java.io.FileOutputStream;
24 import java.io.IOException;
25 import java.io.ObjectInputStream;
26 import java.io.ObjectOutputStream;
27 import java.nio.charset.Charset;
28 import java.util.Objects;
29
30 import net.sourceforge.pmd.cpd.CPDConfiguration;
31 import net.sourceforge.pmd.cpd.CPDReportRenderer;
32 import net.sourceforge.pmd.cpd.CSVRenderer;
33 import net.sourceforge.pmd.cpd.CpdAnalysis;
34 import net.sourceforge.pmd.cpd.SimpleRenderer;
35 import net.sourceforge.pmd.cpd.XMLRenderer;
36 import net.sourceforge.pmd.lang.Language;
37 import org.apache.maven.plugin.MojoExecutionException;
38 import org.apache.maven.plugins.pmd.ExcludeDuplicationsFromFile;
39 import org.apache.maven.reporting.MavenReportException;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43
44
45
46 public class CpdExecutor extends Executor {
47 private static final Logger LOG = LoggerFactory.getLogger(CpdExecutor.class);
48
49 public static CpdResult execute(CpdRequest request) throws MavenReportException {
50 if (request.getJavaExecutable() != null) {
51 return fork(request);
52 }
53
54 ClassLoader origLoader = Thread.currentThread().getContextClassLoader();
55 try {
56 Thread.currentThread().setContextClassLoader(CpdExecutor.class.getClassLoader());
57 CpdExecutor cpdExecutor = new CpdExecutor(request);
58 return cpdExecutor.run();
59 } finally {
60 Thread.currentThread().setContextClassLoader(origLoader);
61 }
62 }
63
64 private static CpdResult fork(CpdRequest request) throws MavenReportException {
65 File basePmdDir = new File(request.getTargetDirectory(), "pmd");
66 basePmdDir.mkdirs();
67 File cpdRequestFile = new File(basePmdDir, "cpdrequest.bin");
68 try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(cpdRequestFile))) {
69 out.writeObject(request);
70 } catch (IOException e) {
71 throw new MavenReportException(e.getMessage(), e);
72 }
73
74 String classpath = buildClasspath();
75 ProcessBuilder pb = new ProcessBuilder();
76
77 pb.environment().put("CLASSPATH", classpath);
78 pb.command().add(request.getJavaExecutable());
79 pb.command().add(CpdExecutor.class.getName());
80 pb.command().add(cpdRequestFile.getAbsolutePath());
81
82 LOG.debug("Executing: CLASSPATH={}, command={}", classpath, pb.command());
83 try {
84 final Process p = pb.start();
85
86
87 ProcessStreamHandler.start(p.getInputStream(), System.out);
88 ProcessStreamHandler.start(p.getErrorStream(), System.err);
89 int exit = p.waitFor();
90 LOG.debug("CpdExecutor exit code: {}", exit);
91 if (exit != 0) {
92 throw new MavenReportException("CpdExecutor exited with exit code " + exit);
93 }
94 return new CpdResult(new File(request.getTargetDirectory(), "cpd.xml"), request.getOutputEncoding());
95 } catch (IOException e) {
96 throw new MavenReportException(e.getMessage(), e);
97 } catch (InterruptedException e) {
98 Thread.currentThread().interrupt();
99 throw new MavenReportException(e.getMessage(), e);
100 }
101 }
102
103
104
105
106
107
108
109
110
111
112
113
114 public static void main(String[] args) {
115 File requestFile = new File(args[0]);
116 try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(requestFile))) {
117 CpdRequest request = (CpdRequest) in.readObject();
118 CpdExecutor cpdExecutor = new CpdExecutor(request);
119 cpdExecutor.setupLogLevel(request.getLogLevel());
120 cpdExecutor.run();
121 System.exit(0);
122 } catch (IOException | ClassNotFoundException | MavenReportException e) {
123 LOG.error(e.getMessage(), e);
124 }
125 System.exit(1);
126 }
127
128 private final CpdRequest request;
129
130
131 private final ExcludeDuplicationsFromFile excludeDuplicationsFromFile = new ExcludeDuplicationsFromFile();
132
133 public CpdExecutor(CpdRequest request) {
134 this.request = Objects.requireNonNull(request);
135 }
136
137 private CpdResult run() throws MavenReportException {
138 try {
139 excludeDuplicationsFromFile.loadExcludeFromFailuresData(request.getExcludeFromFailureFile());
140 } catch (MojoExecutionException e) {
141 throw new MavenReportException("Error loading exclusions", e);
142 }
143
144 CPDConfiguration cpdConfiguration = new CPDConfiguration();
145 cpdConfiguration.setMinimumTileSize(request.getMinimumTokens());
146 cpdConfiguration.setIgnoreAnnotations(request.isIgnoreAnnotations());
147 cpdConfiguration.setIgnoreLiterals(request.isIgnoreLiterals());
148 cpdConfiguration.setIgnoreIdentifiers(request.isIgnoreIdentifiers());
149
150 cpdConfiguration.setSkipLexicalErrors(true);
151
152 String languageId = request.getLanguage();
153 if ("javascript".equals(languageId)) {
154 languageId = "ecmascript";
155 } else if (languageId == null) {
156 languageId = "java";
157 }
158 Language cpdLanguage = cpdConfiguration.getLanguageRegistry().getLanguageById(languageId);
159
160 cpdConfiguration.setOnlyRecognizeLanguage(cpdLanguage);
161 cpdConfiguration.setSourceEncoding(Charset.forName(request.getSourceEncoding()));
162
163 request.getFiles().forEach(f -> cpdConfiguration.addInputPath(f.toPath()));
164
165 LOG.debug("Executing CPD...");
166 try (CpdAnalysis cpd = CpdAnalysis.create(cpdConfiguration)) {
167 CpdReportConsumer reportConsumer = new CpdReportConsumer(request, excludeDuplicationsFromFile);
168 cpd.performAnalysis(reportConsumer);
169 } catch (IOException e) {
170 throw new MavenReportException("Error while executing CPD", e);
171 }
172 LOG.debug("CPD finished.");
173
174
175
176 int cpdErrors = cpdConfiguration.getReporter().numErrors();
177 if (cpdErrors == 1) {
178 throw new MavenReportException("There was 1 error while executing CPD");
179 } else if (cpdErrors > 1) {
180 throw new MavenReportException("There were " + cpdErrors + " errors while executing CPD");
181 }
182
183 return new CpdResult(new File(request.getTargetDirectory(), "cpd.xml"), request.getOutputEncoding());
184 }
185
186
187
188
189
190
191
192 public static CPDReportRenderer createRenderer(String format, String outputEncoding) throws MavenReportException {
193 CPDReportRenderer renderer = null;
194 if ("xml".equals(format)) {
195 renderer = new XMLRenderer(outputEncoding);
196 } else if ("csv".equals(format)) {
197 renderer = new CSVRenderer();
198 } else if ("txt".equals(format)) {
199 renderer = new SimpleRenderer();
200 } else if (!"".equals(format) && !"none".equals(format)) {
201 try {
202 renderer = (CPDReportRenderer)
203 Class.forName(format).getConstructor().newInstance();
204 } catch (Exception e) {
205 throw new MavenReportException(
206 "Can't find CPD custom format " + format + ": "
207 + e.getClass().getName(),
208 e);
209 }
210 }
211
212 return renderer;
213 }
214 }