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.io.OutputStreamWriter;
28 import java.io.Writer;
29 import java.util.ArrayList;
30 import java.util.Iterator;
31 import java.util.List;
32 import java.util.Objects;
33
34 import net.sourceforge.pmd.cpd.CPD;
35 import net.sourceforge.pmd.cpd.CPDConfiguration;
36 import net.sourceforge.pmd.cpd.CSVRenderer;
37 import net.sourceforge.pmd.cpd.EcmascriptLanguage;
38 import net.sourceforge.pmd.cpd.JSPLanguage;
39 import net.sourceforge.pmd.cpd.JavaLanguage;
40 import net.sourceforge.pmd.cpd.Language;
41 import net.sourceforge.pmd.cpd.LanguageFactory;
42 import net.sourceforge.pmd.cpd.Match;
43 import net.sourceforge.pmd.cpd.SimpleRenderer;
44 import net.sourceforge.pmd.cpd.XMLRenderer;
45 import net.sourceforge.pmd.cpd.renderer.CPDRenderer;
46 import org.apache.maven.plugin.MojoExecutionException;
47 import org.apache.maven.plugins.pmd.ExcludeDuplicationsFromFile;
48 import org.apache.maven.reporting.MavenReportException;
49 import org.codehaus.plexus.util.FileUtils;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
52
53
54
55
56 public class CpdExecutor extends Executor {
57 private static final Logger LOG = LoggerFactory.getLogger(CpdExecutor.class);
58
59 public static CpdResult execute(CpdRequest request) throws MavenReportException {
60 if (request.getJavaExecutable() != null) {
61 return fork(request);
62 }
63
64 ClassLoader origLoader = Thread.currentThread().getContextClassLoader();
65 try {
66 Thread.currentThread().setContextClassLoader(CpdExecutor.class.getClassLoader());
67 CpdExecutor cpdExecutor = new CpdExecutor(request);
68 return cpdExecutor.run();
69 } finally {
70 Thread.currentThread().setContextClassLoader(origLoader);
71 }
72 }
73
74 private static CpdResult fork(CpdRequest request) throws MavenReportException {
75 File basePmdDir = new File(request.getTargetDirectory(), "pmd");
76 basePmdDir.mkdirs();
77 File cpdRequestFile = new File(basePmdDir, "cpdrequest.bin");
78 try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(cpdRequestFile))) {
79 out.writeObject(request);
80 } catch (IOException e) {
81 throw new MavenReportException(e.getMessage(), e);
82 }
83
84 String classpath = buildClasspath();
85 ProcessBuilder pb = new ProcessBuilder();
86
87 pb.environment().put("CLASSPATH", classpath);
88 pb.command().add(request.getJavaExecutable());
89 pb.command().add(CpdExecutor.class.getName());
90 pb.command().add(cpdRequestFile.getAbsolutePath());
91
92 LOG.debug("Executing: CLASSPATH={}, command={}", classpath, pb.command());
93 try {
94 final Process p = pb.start();
95
96
97 ProcessStreamHandler.start(p.getInputStream(), System.out);
98 ProcessStreamHandler.start(p.getErrorStream(), System.err);
99 int exit = p.waitFor();
100 LOG.debug("CpdExecutor exit code: {}", exit);
101 if (exit != 0) {
102 throw new MavenReportException("CpdExecutor exited with exit code " + exit);
103 }
104 return new CpdResult(new File(request.getTargetDirectory(), "cpd.xml"), request.getOutputEncoding());
105 } catch (IOException e) {
106 throw new MavenReportException(e.getMessage(), e);
107 } catch (InterruptedException e) {
108 Thread.currentThread().interrupt();
109 throw new MavenReportException(e.getMessage(), e);
110 }
111 }
112
113
114
115
116
117
118
119
120
121
122
123
124 public static void main(String[] args) {
125 File requestFile = new File(args[0]);
126 try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(requestFile))) {
127 CpdRequest request = (CpdRequest) in.readObject();
128 CpdExecutor cpdExecutor = new CpdExecutor(request);
129 cpdExecutor.setupLogLevel(request.getLogLevel());
130 cpdExecutor.run();
131 System.exit(0);
132 } catch (IOException | ClassNotFoundException | MavenReportException e) {
133 LOG.error(e.getMessage(), e);
134 }
135 System.exit(1);
136 }
137
138 private final CpdRequest request;
139
140
141 private final ExcludeDuplicationsFromFile excludeDuplicationsFromFile = new ExcludeDuplicationsFromFile();
142
143 public CpdExecutor(CpdRequest request) {
144 this.request = Objects.requireNonNull(request);
145 }
146
147 private CpdResult run() throws MavenReportException {
148 setupPmdLogging(request.isShowPmdLog(), request.getLogLevel());
149
150 try {
151 excludeDuplicationsFromFile.loadExcludeFromFailuresData(request.getExcludeFromFailureFile());
152 } catch (MojoExecutionException e) {
153 throw new MavenReportException("Error loading exclusions", e);
154 }
155
156 CPDConfiguration cpdConfiguration = new CPDConfiguration();
157 cpdConfiguration.setMinimumTileSize(request.getMinimumTokens());
158
159 Language cpdLanguage;
160 if ("java".equals(request.getLanguage()) || null == request.getLanguage()) {
161 cpdLanguage = new JavaLanguage(request.getLanguageProperties());
162 } else if ("javascript".equals(request.getLanguage())) {
163 cpdLanguage = new EcmascriptLanguage();
164 } else if ("jsp".equals(request.getLanguage())) {
165 cpdLanguage = new JSPLanguage();
166 } else {
167 cpdLanguage = LanguageFactory.createLanguage(request.getLanguage(), request.getLanguageProperties());
168 }
169
170 cpdConfiguration.setLanguage(cpdLanguage);
171 cpdConfiguration.setSourceEncoding(request.getSourceEncoding());
172
173 CPD cpd = new CPD(cpdConfiguration);
174 try {
175 cpd.add(request.getFiles());
176 } catch (IOException e) {
177 throw new MavenReportException(e.getMessage(), e);
178 }
179
180 LOG.debug("Executing CPD...");
181 cpd.go();
182 LOG.debug("CPD finished.");
183
184
185
186 writeXmlReport(cpd);
187
188
189 String format = request.getFormat();
190 if (!"html".equals(format) && !"xml".equals(format)) {
191 writeFormattedReport(cpd);
192 }
193
194 return new CpdResult(new File(request.getTargetDirectory(), "cpd.xml"), request.getOutputEncoding());
195 }
196
197 private void writeXmlReport(CPD cpd) throws MavenReportException {
198 File targetFile = writeReport(cpd, new XMLRenderer(request.getOutputEncoding()), "xml");
199 if (request.isIncludeXmlInSite()) {
200 File siteDir = new File(request.getReportOutputDirectory());
201 siteDir.mkdirs();
202 try {
203 FileUtils.copyFile(targetFile, new File(siteDir, "cpd.xml"));
204 } catch (IOException e) {
205 throw new MavenReportException(e.getMessage(), e);
206 }
207 }
208 }
209
210 private File writeReport(CPD cpd, CPDRenderer r, String extension) throws MavenReportException {
211 if (r == null) {
212 return null;
213 }
214
215 File targetDir = new File(request.getTargetDirectory());
216 targetDir.mkdirs();
217 File targetFile = new File(targetDir, "cpd." + extension);
218 try (Writer writer = new OutputStreamWriter(new FileOutputStream(targetFile), request.getOutputEncoding())) {
219 r.render(filterMatches(cpd.getMatches()), writer);
220 writer.flush();
221 } catch (IOException ioe) {
222 throw new MavenReportException(ioe.getMessage(), ioe);
223 }
224 return targetFile;
225 }
226
227 private void writeFormattedReport(CPD cpd) throws MavenReportException {
228 CPDRenderer r = createRenderer(request.getFormat(), request.getOutputEncoding());
229 writeReport(cpd, r, request.getFormat());
230 }
231
232
233
234
235
236
237
238 public static CPDRenderer createRenderer(String format, String outputEncoding) throws MavenReportException {
239 CPDRenderer renderer = null;
240 if ("xml".equals(format)) {
241 renderer = new XMLRenderer(outputEncoding);
242 } else if ("csv".equals(format)) {
243 renderer = new CSVRenderer();
244 } else if ("txt".equals(format)) {
245 renderer = new SimpleRenderer();
246 } else if (!"".equals(format) && !"none".equals(format)) {
247 try {
248 renderer = (CPDRenderer) Class.forName(format).getConstructor().newInstance();
249 } catch (Exception e) {
250 throw new MavenReportException(
251 "Can't find CPD custom format " + format + ": "
252 + e.getClass().getName(),
253 e);
254 }
255 }
256
257 return renderer;
258 }
259
260 private Iterator<Match> filterMatches(Iterator<Match> matches) {
261 LOG.debug("Filtering duplications. Using " + excludeDuplicationsFromFile.countExclusions()
262 + " configured exclusions.");
263
264 List<Match> filteredMatches = new ArrayList<>();
265 int excludedDuplications = 0;
266 while (matches.hasNext()) {
267 Match match = matches.next();
268 if (excludeDuplicationsFromFile.isExcludedFromFailure(match)) {
269 excludedDuplications++;
270 } else {
271 filteredMatches.add(match);
272 }
273 }
274
275 LOG.debug("Excluded " + excludedDuplications + " duplications.");
276 return filteredMatches.iterator();
277 }
278 }