1 package com.atlassian.maven.plugins.licenses;
2
3 import java.io.File;
4 import java.net.MalformedURLException;
5 import java.util.ArrayList;
6 import java.util.Arrays;
7 import java.util.Iterator;
8 import java.util.List;
9 import java.util.Set;
10 import java.util.StringTokenizer;
11 import java.util.TreeSet;
12 import java.util.jar.JarFile;
13
14 import org.apache.maven.artifact.Artifact;
15 import org.apache.maven.artifact.factory.ArtifactFactory;
16 import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
17 import org.apache.maven.artifact.repository.ArtifactRepository;
18 import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
19 import org.apache.maven.artifact.resolver.ArtifactResolutionException;
20 import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
21 import org.apache.maven.artifact.resolver.ArtifactResolver;
22 import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
23 import org.apache.maven.model.License;
24 import org.apache.maven.plugin.AbstractMojo;
25 import org.apache.maven.plugin.MojoExecutionException;
26 import org.apache.maven.project.MavenProject;
27 import org.apache.maven.project.MavenProjectBuilder;
28 import org.apache.maven.project.ProjectBuildingException;
29 import org.apache.oro.text.regex.MalformedPatternException;
30
31
32
33
34
35
36
37
38 public abstract class AbstractLicensesMojo extends AbstractMojo
39 {
40
41
42
43
44 protected static final List LICENSE_PATH_OPTIONS = Arrays.asList(new String[]
45 {
46 "LICENSE",
47 "LICENSE.txt",
48 "META-INF/LICENSE",
49 "META-INF/LICENSE.txt",
50 "license",
51 "license.txt",
52 "META-INF/license",
53 "META-INF/license.txt",
54 });
55
56 public static final String LICENSE_TYPE = "license";
57
58
59
60
61 protected ArtifactResolver resolver;
62
63
64
65
66 protected ArtifactRepository localRepository;
67
68
69
70
71 protected List remoteRepositories;
72
73
74
75
76 protected ArtifactFactory artifactFactory;
77
78
79
80
81
82 protected ArtifactMetadataSource artifactMetadataSource;
83
84
85
86
87 protected MavenProjectBuilder mavenProjectBuilder;
88
89
90
91
92
93
94 private MavenProject project;
95
96
97
98
99
100
101 protected List excludedScopeList;
102
103
104
105
106
107
108
109
110 protected String excludedScopes;
111
112
113
114
115
116
117
118
119 protected List excludedLibraryList;
120
121
122
123
124
125
126
127
128
129
130 protected String excludedLibraries;
131
132
133
134
135
136
137
138 private List reactorProjects;
139
140
141
142
143
144
145 protected boolean aggregate;
146
147 protected void visitDependencyProjects(ProjectVisitor visitor) throws MojoExecutionException
148 {
149 if (aggregate && !project.isExecutionRoot())
150 {
151 return;
152 }
153
154 final Set dependencies = resolveDependencies();
155
156 visitor.onInit(dependencies.size());
157
158 for (Iterator iterator = dependencies.iterator(); iterator.hasNext();)
159 {
160 final Artifact depArtifact = (Artifact) iterator.next();
161 try
162 {
163
164
165 MavenProject depProject = mavenProjectBuilder.buildFromRepository(depArtifact, remoteRepositories,
166 localRepository);
167 visitor.visit(depProject);
168 }
169 catch (ProjectBuildingException e)
170 {
171 throw new MojoExecutionException("Failed to build a project for artifact: " + depArtifact, e);
172 }
173 }
174 visitor.onComplete();
175 }
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190 protected String createValidLicenseUrl(License license, Artifact artifact)
191 {
192 String urlString = findLicenseInRepository(artifact);
193 if (urlString != null)
194 return urlString;
195
196 if (license != null)
197 {
198 urlString = license.getUrl();
199 }
200
201 if (urlString == null || urlString.startsWith("/") || urlString.startsWith("\\"))
202 {
203
204 return findLicenseInJar(artifact, urlString);
205 }
206
207
208 return urlString;
209 }
210
211
212
213
214
215
216
217
218
219
220 protected String findLicenseInRepository(Artifact artifact)
221 {
222 Artifact licenseArtifact = artifactFactory.createArtifact(artifact.getGroupId(), artifact.getArtifactId(),
223 artifact.getVersion(), null, LICENSE_TYPE);
224 try
225 {
226 resolver.resolve(licenseArtifact, remoteRepositories, localRepository);
227 if (licenseArtifact.getFile() != null)
228 {
229 try
230 {
231 return licenseArtifact.getFile().toURL().toString();
232 }
233 catch (MalformedURLException e)
234 {
235 getLog().warn("Failed to convert " + licenseArtifact.getFile() + " to a URL", e);
236 return "file:" + licenseArtifact.getFile().getAbsolutePath();
237 }
238 }
239 else
240 return licenseArtifact.getDownloadUrl();
241 }
242 catch (ArtifactResolutionException e)
243 {
244 getLog().info("Failed to resolve license artifact " + licenseArtifact, e);
245 return null;
246 }
247 catch (ArtifactNotFoundException e)
248 {
249 if (getLog().isDebugEnabled())
250 getLog().debug("License artifact " + licenseArtifact + " is not found", e);
251 return null;
252 }
253 }
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268 private String findLicenseInJar(Artifact artifact, String filePath)
269 {
270 final String jarPath = "jar:" + localRepository.getUrl() + File.separator + localRepository.pathOf(artifact);
271
272 if (filePath != null && filePath.length() != 0)
273 {
274
275
276
277 if (filePath.startsWith("/") || filePath.startsWith("\\"))
278 {
279 filePath = "META-INF" + filePath;
280 }
281 else
282 {
283 filePath = "META-INF/" + filePath;
284 }
285
286
287 String url = toUrl(jarPath, filePath);
288 if (url != null)
289 return url;
290 }
291
292 for (Iterator iter = LICENSE_PATH_OPTIONS.iterator(); iter.hasNext();)
293 {
294 String url = toUrl(jarPath, (String) iter.next());
295 if (url != null)
296 return url;
297 }
298
299 return null;
300 }
301
302
303
304
305
306
307
308
309
310
311
312
313 private String toUrl(String jarPath, String entry)
314 {
315 try
316 {
317 if (new JarFile(jarPath).getEntry(entry) != null)
318 {
319 return jarPath + "!/" + entry;
320 }
321 else
322 {
323 if (getLog().isDebugEnabled())
324 getLog().debug("Entry " + entry + " is not found in " + jarPath);
325 return null;
326 }
327 }
328 catch (Exception e)
329 {
330 if (getLog().isDebugEnabled())
331 getLog().debug("Path is invalid: " + jarPath, e);
332 return null;
333 }
334 }
335
336 private Set resolveDependencies() throws MojoExecutionException
337 {
338
339
340 Set dependencies = new TreeSet(project.getDependencyArtifacts());
341
342
343 for (Iterator i = reactorProjects.iterator(); i.hasNext();)
344 {
345 MavenProject subProject = (MavenProject) i.next();
346 if (subProject != project)
347 {
348 getLog().info("Aggregating dependency for " + subProject.getName());
349 dependencies.add(subProject.getArtifact());
350 }
351 }
352
353 try
354 {
355
356 ArtifactFilter filter = createExclusionFilter();
357 ArtifactResolutionResult result = resolver.resolveTransitively(dependencies, project.getArtifact(),
358 localRepository, remoteRepositories, artifactMetadataSource, filter);
359 return result.getArtifacts();
360 }
361 catch (MalformedPatternException e)
362 {
363 throw new MojoExecutionException("Failed to parse an exclusion filter", e);
364 }
365 catch (ArtifactResolutionException e)
366 {
367 throw new MojoExecutionException("Failed to resolve transitively.", e);
368 }
369 catch (ArtifactNotFoundException e)
370 {
371 throw new MojoExecutionException("Failed to resolve transitively.", e);
372 }
373 }
374
375 protected ArtifactExclusionFilter createExclusionFilter() throws MalformedPatternException
376 {
377
378 validateExclusionList();
379 return new ArtifactExclusionFilter(excludedLibraryList, excludedScopeList);
380 }
381
382
383
384
385
386 private void validateExclusionList()
387 {
388 this.excludedScopeList = fillListIfNull(excludedScopes, excludedScopeList);
389 this.excludedLibraryList = fillListIfNull(excludedLibraries, excludedLibraryList);
390 }
391
392 private static List fillListIfNull(String csvString, List list)
393 {
394 if (list == null)
395 {
396 list = new ArrayList();
397 if (csvString != null && csvString.length() > 0)
398 {
399 StringTokenizer tokenizer = new StringTokenizer(csvString, ",");
400 while (tokenizer.hasMoreTokens())
401 list.add(tokenizer.nextToken());
402 }
403 }
404 return list;
405 }
406 }