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 CpdResult fork(String javaExecutable) throws MavenReportException {
50 File basePmdDir = new File(request.getTargetDirectory(), "pmd");
51 basePmdDir.mkdirs();
52 File cpdRequestFile = new File(basePmdDir, "cpdrequest.bin");
53 try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(cpdRequestFile))) {
54 out.writeObject(request);
55 } catch (IOException e) {
56 throw new MavenReportException(e.getMessage(), e);
57 }
58
59 String classpath = buildClasspath();
60 ProcessBuilder pb = new ProcessBuilder();
61
62 pb.environment().put("CLASSPATH", classpath);
63 pb.command().add(javaExecutable);
64 pb.command().add(CpdExecutor.class.getName());
65 pb.command().add(cpdRequestFile.getAbsolutePath());
66
67 LOG.debug("Executing: CLASSPATH={}, command={}", classpath, pb.command());
68 try {
69 final Process p = pb.start();
70
71
72 ProcessStreamHandler.start(p.getInputStream(), System.out);
73 ProcessStreamHandler.start(p.getErrorStream(), System.err);
74 int exit = p.waitFor();
75 LOG.debug("CpdExecutor exit code: {}", exit);
76 if (exit != 0) {
77 throw new MavenReportException("CpdExecutor exited with exit code " + exit);
78 }
79 return new CpdResult(new File(request.getTargetDirectory(), "cpd.xml"), request.getOutputEncoding());
80 } catch (IOException e) {
81 throw new MavenReportException(e.getMessage(), e);
82 } catch (InterruptedException e) {
83 Thread.currentThread().interrupt();
84 throw new MavenReportException(e.getMessage(), e);
85 }
86 }
87
88
89
90
91
92
93
94
95
96
97
98
99 public static void main(String[] args) {
100 File requestFile = new File(args[0]);
101 try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(requestFile))) {
102 CpdRequest request = (CpdRequest) in.readObject();
103 CpdExecutor cpdExecutor = new CpdExecutor(request);
104 cpdExecutor.run();
105 System.exit(0);
106 } catch (IOException | ClassNotFoundException | MavenReportException e) {
107 LOG.error(e.getMessage(), e);
108 }
109 System.exit(1);
110 }
111
112 private final CpdRequest request;
113
114
115 private final ExcludeDuplicationsFromFile excludeDuplicationsFromFile = new ExcludeDuplicationsFromFile();
116
117 public CpdExecutor(CpdRequest request) {
118 this.request = Objects.requireNonNull(request);
119 }
120
121 public CpdResult run() throws MavenReportException {
122 try {
123 excludeDuplicationsFromFile.loadExcludeFromFailuresData(request.getExcludeFromFailureFile());
124 } catch (MojoExecutionException e) {
125 throw new MavenReportException("Error loading exclusions", e);
126 }
127
128 CPDConfiguration cpdConfiguration = new CPDConfiguration();
129 cpdConfiguration.setMinimumTileSize(request.getMinimumTokens());
130 cpdConfiguration.setIgnoreAnnotations(request.isIgnoreAnnotations());
131 cpdConfiguration.setIgnoreLiterals(request.isIgnoreLiterals());
132 cpdConfiguration.setIgnoreIdentifiers(request.isIgnoreIdentifiers());
133
134 cpdConfiguration.setSkipLexicalErrors(true);
135
136 String languageId = request.getLanguage();
137 if ("javascript".equals(languageId)) {
138 languageId = "ecmascript";
139 } else if (languageId == null) {
140 languageId = "java";
141 }
142 Language cpdLanguage = cpdConfiguration.getLanguageRegistry().getLanguageById(languageId);
143
144 cpdConfiguration.setOnlyRecognizeLanguage(cpdLanguage);
145 cpdConfiguration.setSourceEncoding(Charset.forName(request.getSourceEncoding()));
146
147 request.getFiles().forEach(f -> cpdConfiguration.addInputPath(f.toPath()));
148
149 LOG.debug("Executing CPD...");
150 try (CpdAnalysis cpd = CpdAnalysis.create(cpdConfiguration)) {
151 CpdReportConsumer reportConsumer = new CpdReportConsumer(request, excludeDuplicationsFromFile);
152 cpd.performAnalysis(reportConsumer);
153 } catch (IOException e) {
154 throw new MavenReportException("Error while executing CPD", e);
155 }
156 LOG.debug("CPD finished.");
157
158
159
160 int cpdErrors = cpdConfiguration.getReporter().numErrors();
161 if (cpdErrors == 1) {
162 throw new MavenReportException("There was 1 error while executing CPD");
163 } else if (cpdErrors > 1) {
164 throw new MavenReportException("There were " + cpdErrors + " errors while executing CPD");
165 }
166
167 return new CpdResult(new File(request.getTargetDirectory(), "cpd.xml"), request.getOutputEncoding());
168 }
169
170
171
172
173
174
175
176 public static CPDReportRenderer createRenderer(String format, String outputEncoding) throws MavenReportException {
177 CPDReportRenderer renderer = null;
178 if ("xml".equals(format)) {
179 renderer = new XMLRenderer(outputEncoding);
180 } else if ("csv".equals(format)) {
181 renderer = new CSVRenderer();
182 } else if ("txt".equals(format)) {
183 renderer = new SimpleRenderer();
184 } else if (!"".equals(format) && !"none".equals(format)) {
185 try {
186 renderer = (CPDReportRenderer)
187 Class.forName(format).getConstructor().newInstance();
188 } catch (Exception e) {
189
190 throw new MavenReportException(
191 "Can't find CPD custom format " + format + ": "
192 + e.getClass().getName(),
193 e);
194 }
195 }
196
197 return renderer;
198 }
199 }