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