1 package org.apache.maven.plugin.dependency.analyze;
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.StringWriter;
24 import java.util.Set;
25
26 import org.apache.commons.lang.StringUtils;
27 import org.apache.maven.artifact.Artifact;
28 import org.apache.maven.plugin.AbstractMojo;
29 import org.apache.maven.plugin.MojoExecutionException;
30 import org.apache.maven.plugin.MojoFailureException;
31 import org.apache.maven.plugins.annotations.Parameter;
32 import org.apache.maven.project.MavenProject;
33 import org.apache.maven.shared.dependency.analyzer.ProjectDependencyAnalysis;
34 import org.apache.maven.shared.dependency.analyzer.ProjectDependencyAnalyzer;
35 import org.apache.maven.shared.dependency.analyzer.ProjectDependencyAnalyzerException;
36 import org.codehaus.plexus.PlexusConstants;
37 import org.codehaus.plexus.PlexusContainer;
38 import org.codehaus.plexus.context.Context;
39 import org.codehaus.plexus.context.ContextException;
40 import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
41 import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
42
43
44
45
46
47
48
49
50
51 public abstract class AbstractAnalyzeMojo
52 extends AbstractMojo
53 implements Contextualizable
54 {
55
56
57
58
59
60
61 private Context context;
62
63
64
65
66 @Parameter( defaultValue = "${project}", readonly = true, required = true )
67 private MavenProject project;
68
69
70
71
72
73
74
75
76
77
78
79 @Parameter( property = "analyzer", defaultValue = "default" )
80 private String analyzer;
81
82
83
84
85 @Parameter( property = "failOnWarning", defaultValue = "false" )
86 private boolean failOnWarning;
87
88
89
90
91 @Parameter( property = "verbose", defaultValue = "false" )
92 private boolean verbose;
93
94
95
96
97 @Parameter( property = "ignoreNonCompile", defaultValue = "false" )
98 private boolean ignoreNonCompile;
99
100
101
102
103
104
105 @Parameter( property = "outputXML", defaultValue = "false" )
106 private boolean outputXML;
107
108
109
110
111
112
113 @Parameter( property = "scriptableOutput", defaultValue = "false" )
114 private boolean scriptableOutput;
115
116
117
118
119
120
121 @Parameter( property = "scriptableFlag", defaultValue = "$$$%%%" )
122 private String scriptableFlag;
123
124
125
126
127
128
129 @Parameter( defaultValue = "${basedir}", readonly = true )
130 private File baseDir;
131
132
133
134
135
136
137 @Parameter( defaultValue = "${project.build.directory}", readonly = true )
138 private File outputDirectory;
139
140
141
142
143
144
145
146 @Parameter
147 private String[] usedDependencies;
148
149
150
151
152
153
154 @Parameter( property = "mdep.analyze.skip", defaultValue = "false" )
155 private boolean skip;
156
157
158
159
160
161
162 public void execute()
163 throws MojoExecutionException, MojoFailureException
164 {
165 if ( isSkip() )
166 {
167 getLog().info( "Skipping plugin execution" );
168 return;
169 }
170
171 if ( "pom".equals( project.getPackaging() ) )
172 {
173 getLog().info( "Skipping pom project" );
174 return;
175 }
176
177 if ( outputDirectory == null || !outputDirectory.exists() )
178 {
179 getLog().info( "Skipping project with no build directory" );
180 return;
181 }
182
183 boolean warning = checkDependencies();
184
185 if ( warning && failOnWarning )
186 {
187 throw new MojoExecutionException( "Dependency problems found" );
188 }
189 }
190
191 protected ProjectDependencyAnalyzer createProjectDependencyAnalyzer()
192 throws MojoExecutionException
193 {
194
195 final String role = ProjectDependencyAnalyzer.ROLE;
196 final String roleHint = analyzer;
197
198 try
199 {
200 final PlexusContainer container = (PlexusContainer) context.get( PlexusConstants.PLEXUS_KEY );
201
202 return (ProjectDependencyAnalyzer) container.lookup( role, roleHint );
203 }
204 catch ( Exception exception )
205 {
206 throw new MojoExecutionException(
207 "Failed to instantiate ProjectDependencyAnalyser with role " + role + " / role-hint " + roleHint,
208 exception );
209 }
210 }
211
212 public void contextualize( Context context )
213 throws ContextException
214 {
215 this.context = context;
216 }
217
218 public boolean isSkip()
219 {
220 return skip;
221 }
222
223 public void setSkip( boolean skip )
224 {
225 this.skip = skip;
226 }
227
228
229
230 private boolean checkDependencies()
231 throws MojoExecutionException
232 {
233 ProjectDependencyAnalysis analysis;
234 try
235 {
236 analysis = createProjectDependencyAnalyzer().analyze( project );
237
238 if ( usedDependencies != null )
239 {
240 analysis = analysis.forceDeclaredDependenciesUsage( usedDependencies );
241 }
242 }
243 catch ( ProjectDependencyAnalyzerException exception )
244 {
245 throw new MojoExecutionException( "Cannot analyze dependencies", exception );
246 }
247
248 if ( ignoreNonCompile )
249 {
250 analysis = analysis.ignoreNonCompile();
251 }
252
253 Set<Artifact> usedDeclared = analysis.getUsedDeclaredArtifacts();
254 Set<Artifact> usedUndeclared = analysis.getUsedUndeclaredArtifacts();
255 Set<Artifact> unusedDeclared = analysis.getUnusedDeclaredArtifacts();
256
257 if ( ( !verbose || usedDeclared.isEmpty() ) && usedUndeclared.isEmpty() && unusedDeclared.isEmpty() )
258 {
259 getLog().info( "No dependency problems found" );
260 return false;
261 }
262
263 if ( verbose && !usedDeclared.isEmpty() )
264 {
265 getLog().info( "Used declared dependencies found:" );
266
267 logArtifacts( analysis.getUsedDeclaredArtifacts(), false );
268 }
269
270 if ( !usedUndeclared.isEmpty() )
271 {
272 getLog().warn( "Used undeclared dependencies found:" );
273
274 logArtifacts( usedUndeclared, true );
275 }
276
277 if ( !unusedDeclared.isEmpty() )
278 {
279 getLog().warn( "Unused declared dependencies found:" );
280
281 logArtifacts( unusedDeclared, true );
282 }
283
284 if ( outputXML )
285 {
286 writeDependencyXML( usedUndeclared );
287 }
288
289 if ( scriptableOutput )
290 {
291 writeScriptableOutput( usedUndeclared );
292 }
293
294 return !usedUndeclared.isEmpty() || !unusedDeclared.isEmpty();
295 }
296
297 private void logArtifacts( Set<Artifact> artifacts, boolean warn )
298 {
299 if ( artifacts.isEmpty() )
300 {
301 getLog().info( " None" );
302 }
303 else
304 {
305 for ( Artifact artifact : artifacts )
306 {
307
308 artifact.isSnapshot();
309
310 if ( warn )
311 {
312 getLog().warn( " " + artifact );
313 }
314 else
315 {
316 getLog().info( " " + artifact );
317 }
318
319 }
320 }
321 }
322
323 private void writeDependencyXML( Set<Artifact> artifacts )
324 {
325 if ( !artifacts.isEmpty() )
326 {
327 getLog().info( "Add the following to your pom to correct the missing dependencies: " );
328
329 StringWriter out = new StringWriter();
330 PrettyPrintXMLWriter writer = new PrettyPrintXMLWriter( out );
331
332 for ( Artifact artifact : artifacts )
333 {
334
335 artifact.isSnapshot();
336
337 writer.startElement( "dependency" );
338 writer.startElement( "groupId" );
339 writer.writeText( artifact.getGroupId() );
340 writer.endElement();
341 writer.startElement( "artifactId" );
342 writer.writeText( artifact.getArtifactId() );
343 writer.endElement();
344 writer.startElement( "version" );
345 writer.writeText( artifact.getBaseVersion() );
346 if ( !StringUtils.isBlank( artifact.getClassifier() ) )
347 {
348 writer.startElement( "classifier" );
349 writer.writeText( artifact.getClassifier() );
350 writer.endElement();
351 }
352 writer.endElement();
353
354 if ( !Artifact.SCOPE_COMPILE.equals( artifact.getScope() ) )
355 {
356 writer.startElement( "scope" );
357 writer.writeText( artifact.getScope() );
358 writer.endElement();
359 }
360 writer.endElement();
361 }
362
363 getLog().info( "\n" + out.getBuffer() );
364 }
365 }
366
367 private void writeScriptableOutput( Set<Artifact> artifacts )
368 {
369 if ( !artifacts.isEmpty() )
370 {
371 getLog().info( "Missing dependencies: " );
372 String pomFile = baseDir.getAbsolutePath() + File.separatorChar + "pom.xml";
373 StringBuilder buf = new StringBuilder();
374
375 for ( Artifact artifact : artifacts )
376 {
377
378 artifact.isSnapshot();
379
380 buf.append(scriptableFlag).append(":").append(pomFile).append(":").append(artifact.getDependencyConflictId()).append(":").append(artifact.getClassifier()).append(":").append(artifact.getBaseVersion()).append(":").append(artifact.getScope()).append("\n");
381 }
382 getLog().info( "\n" + buf );
383 }
384 }
385 }