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