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