1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 package org.codehaus.mojo.license;
26
27 import java.io.File;
28 import java.io.IOException;
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.Collection;
32 import java.util.Comparator;
33 import java.util.HashMap;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.Set;
37 import java.util.SortedMap;
38 import java.util.SortedSet;
39 import java.util.TreeSet;
40 import java.util.regex.Matcher;
41 import java.util.regex.Pattern;
42
43 import org.apache.commons.collections.CollectionUtils;
44 import org.apache.commons.lang.StringUtils;
45 import org.apache.maven.artifact.Artifact;
46 import org.apache.maven.artifact.factory.ArtifactFactory;
47 import org.apache.maven.artifact.repository.ArtifactRepository;
48 import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
49 import org.apache.maven.artifact.resolver.ArtifactResolutionException;
50 import org.apache.maven.artifact.resolver.ArtifactResolver;
51 import org.apache.maven.model.License;
52 import org.apache.maven.project.MavenProject;
53 import org.apache.maven.project.MavenProjectBuilder;
54 import org.apache.maven.project.MavenProjectHelper;
55 import org.codehaus.mojo.license.model.LicenseMap;
56 import org.codehaus.plexus.logging.AbstractLogEnabled;
57 import org.codehaus.plexus.logging.Logger;
58
59
60
61
62
63
64
65
66 public class DefaultThirdPartyTool extends AbstractLogEnabled implements ThirdPartyTool {
67 public static final String DESCRIPTOR_CLASSIFIER = "third-party";
68
69 public static final String DESCRIPTOR_TYPE = "properties";
70
71
72
73
74
75
76
77
78
79
80 private ArtifactResolver artifactResolver;
81
82
83
84
85
86
87 private ArtifactFactory artifactFactory;
88
89
90
91
92
93
94 private MavenProjectBuilder mavenProjectBuilder;
95
96
97
98
99
100
101 private MavenProjectHelper projectHelper;
102
103
104
105
106 private final Comparator<MavenProject> projectComparator = MojoHelper.newMavenProjectComparator();
107
108
109
110
111 @Override
112 public void attachThirdPartyDescriptor(MavenProject project, File file) {
113
114 projectHelper.attachArtifact(project, DESCRIPTOR_TYPE, DESCRIPTOR_CLASSIFIER, file);
115 }
116
117
118
119
120 @Override
121 public SortedSet<MavenProject> getProjectsWithNoLicense(LicenseMap licenseMap, boolean doLog) {
122
123 Logger log = getLogger();
124
125
126 SortedSet<MavenProject> unsafeDependencies = licenseMap.get(LicenseMap.getUnknownLicenseMessage());
127
128 if (doLog) {
129 if (CollectionUtils.isEmpty(unsafeDependencies)) {
130 log.debug("There is no dependency with no license from poms.");
131 } else {
132 log.debug("There is " + unsafeDependencies.size() + " dependencies with no license from poms : ");
133 for (MavenProject dep : unsafeDependencies) {
134
135
136 log.debug(" - " + MojoHelper.getArtifactId(dep.getArtifact()));
137 }
138 }
139 }
140
141 return unsafeDependencies;
142 }
143
144
145
146
147 @Override
148 public SortedProperties loadThirdPartyDescriptorsForUnsafeMapping(String encoding,
149 Collection<MavenProject> projects, SortedSet<MavenProject> unsafeDependencies, LicenseMap licenseMap,
150 ArtifactRepository localRepository, List<ArtifactRepository> remoteRepositories)
151 throws ThirdPartyToolException, IOException {
152
153 SortedProperties result = new SortedProperties(encoding);
154 Map<String, MavenProject> unsafeProjects = new HashMap<String, MavenProject>();
155 for (MavenProject unsafeDependency : unsafeDependencies) {
156 String id = MojoHelper.getArtifactId(unsafeDependency.getArtifact());
157 unsafeProjects.put(id, unsafeDependency);
158 }
159
160 for (MavenProject mavenProject : projects) {
161
162 if (CollectionUtils.isEmpty(unsafeDependencies)) {
163
164
165 break;
166 }
167
168 File thirdPartyDescriptor = resolvThirdPartyDescriptor(mavenProject, localRepository, remoteRepositories);
169
170 if (thirdPartyDescriptor != null && thirdPartyDescriptor.exists() && thirdPartyDescriptor.length() > 0) {
171
172 if (getLogger().isInfoEnabled()) {
173 getLogger().info("Detects third party descriptor " + thirdPartyDescriptor);
174 }
175
176
177 SortedProperties unsafeMappings = new SortedProperties(encoding);
178
179 if (thirdPartyDescriptor.exists()) {
180
181 getLogger().debug("Load missing file " + thirdPartyDescriptor);
182
183
184 unsafeMappings.load(thirdPartyDescriptor);
185 }
186
187 for (String id : unsafeProjects.keySet()) {
188
189 if (unsafeMappings.containsKey(id)) {
190
191 String license = (String) unsafeMappings.get(id);
192 if (StringUtils.isEmpty(license)) {
193
194
195 continue;
196 }
197
198
199 MavenProject resolvedProject = unsafeProjects.get(id);
200 unsafeDependencies.remove(resolvedProject);
201
202
203 result.put(id, license.trim());
204
205 addLicense(licenseMap, resolvedProject, license);
206 }
207 }
208 }
209 }
210 return result;
211 }
212
213
214
215
216 @Override
217 public File resolvThirdPartyDescriptor(MavenProject project, ArtifactRepository localRepository,
218 List<ArtifactRepository> repositories) throws ThirdPartyToolException {
219 if (project == null) {
220 throw new IllegalArgumentException("The parameter 'project' can not be null");
221 }
222 if (localRepository == null) {
223 throw new IllegalArgumentException("The parameter 'localRepository' can not be null");
224 }
225 if (repositories == null) {
226 throw new IllegalArgumentException("The parameter 'remoteArtifactRepositories' can not be null");
227 }
228
229 try {
230 return resolveThirdPartyDescriptor(project, localRepository, repositories);
231 } catch (ArtifactNotFoundException e) {
232 getLogger().debug("ArtifactNotFoundException: Unable to locate third party descriptor: " + e);
233 return null;
234 } catch (ArtifactResolutionException e) {
235 throw new ThirdPartyToolException("ArtifactResolutionException: Unable to locate third party descriptor: "
236 + e.getMessage(), e);
237 } catch (IOException e) {
238 throw new ThirdPartyToolException(
239 "IOException: Unable to locate third party descriptor: " + e.getMessage(), e);
240 }
241 }
242
243
244
245
246 @Override
247 public void addLicense(LicenseMap licenseMap, MavenProject project, String licenseName) {
248 License license = new License();
249 license.setName(licenseName.trim());
250 license.setUrl(licenseName.trim());
251 addLicense(licenseMap, project, license);
252 }
253
254
255
256
257 @Override
258 public void addLicense(LicenseMap licenseMap, MavenProject project, License license) {
259 addLicense(licenseMap, project, Arrays.asList(license));
260 }
261
262
263
264
265 @Override
266 public void addLicense(LicenseMap licenseMap, MavenProject project, List<?> licenses) {
267
268 if (Artifact.SCOPE_SYSTEM.equals(project.getArtifact().getScope())) {
269
270
271 return;
272 }
273
274 if (CollectionUtils.isEmpty(licenses)) {
275
276
277 licenseMap.put(LicenseMap.getUnknownLicenseMessage(), project);
278 return;
279 }
280
281 for (Object o : licenses) {
282 String id = MojoHelper.getArtifactId(project.getArtifact());
283 if (o == null) {
284 getLogger().warn("could not acquire the license for " + id);
285 continue;
286 }
287 License license = (License) o;
288 String licenseKey = license.getName();
289
290
291
292 if (StringUtils.isEmpty(license.getName())) {
293 getLogger().debug("No license name defined for " + id);
294 licenseKey = license.getUrl();
295 }
296
297 if (StringUtils.isEmpty(licenseKey)) {
298 getLogger().debug("No license url defined for " + id);
299 licenseKey = LicenseMap.getUnknownLicenseMessage();
300 }
301 licenseMap.put(licenseKey, project);
302 }
303 }
304
305
306
307
308 @Override
309 public void mergeLicenses(LicenseMap licenseMap, String... licenses) {
310 if (licenses.length == 0) {
311 return;
312 }
313
314 String mainLicense = licenses[0].trim();
315 SortedSet<MavenProject> mainSet = licenseMap.get(mainLicense);
316 if (mainSet == null) {
317 getLogger().debug("No license [" + mainLicense + "] found, will create it.");
318 mainSet = new TreeSet<MavenProject>(projectComparator);
319 licenseMap.put(mainLicense, mainSet);
320 }
321 int size = licenses.length;
322 for (int i = 1; i < size; i++) {
323 String license = licenses[i].trim();
324 SortedSet<MavenProject> set = licenseMap.get(license);
325 if (set == null) {
326 getLogger().debug("No license [" + license + "] found, skip this merge.");
327 continue;
328 }
329 getLogger().debug("Merge license [" + license + "] (" + set.size() + " depedencies).");
330 mainSet.addAll(set);
331 set.clear();
332 licenseMap.remove(license);
333 }
334 }
335
336
337
338
339 @Override
340 public SortedProperties loadUnsafeMapping(LicenseMap licenseMap, SortedMap<String, MavenProject> artifactCache,
341 String encoding, File missingFile) throws IOException {
342 SortedSet<MavenProject> unsafeDependencies = getProjectsWithNoLicense(licenseMap, false);
343
344 SortedProperties unsafeMappings = new SortedProperties(encoding);
345
346 if (missingFile.exists()) {
347
348
349 getLogger().debug("Load missing file " + missingFile);
350
351
352 unsafeMappings.load(missingFile);
353 }
354
355
356 List<String> unknownDependenciesId = new ArrayList<String>();
357
358
359
360 Map<String, String> migrateKeys = migrateMissingFileKeys(unsafeMappings.keySet());
361
362 for (Object o : migrateKeys.keySet()) {
363 String id = (String) o;
364 String migratedId = migrateKeys.get(id);
365
366 MavenProject project = artifactCache.get(migratedId);
367 if (project == null) {
368
369 unknownDependenciesId.add(id);
370 } else {
371 if (!id.equals(migratedId)) {
372
373
374 getLogger().info("Migrates [" + id + "] to [" + migratedId + "] in the missing file.");
375 Object value = unsafeMappings.get(id);
376 unsafeMappings.remove(id);
377 unsafeMappings.put(migratedId, value);
378 }
379 }
380 }
381
382 if (!unknownDependenciesId.isEmpty()) {
383
384
385 for (String id : unknownDependenciesId) {
386 getLogger()
387 .debug("dependency [" + id + "] does not exist in project, remove it from the missing file.");
388 unsafeMappings.remove(id);
389 }
390
391 unknownDependenciesId.clear();
392 }
393
394
395 for (Object o : unsafeMappings.keySet()) {
396 String id = (String) o;
397
398 MavenProject project = artifactCache.get(id);
399 if (project == null) {
400 getLogger().warn("dependency [" + id + "] does not exist in project.");
401 continue;
402 }
403
404 String license = (String) unsafeMappings.get(id);
405 if (StringUtils.isEmpty(license)) {
406
407
408 continue;
409 }
410
411
412 addLicense(licenseMap, project, license);
413
414
415 unsafeDependencies.remove(project);
416 }
417
418 if (unsafeDependencies.isEmpty()) {
419
420
421 licenseMap.remove(LicenseMap.getUnknownLicenseMessage());
422 } else {
423
424
425 for (MavenProject project : unsafeDependencies) {
426 String id = MojoHelper.getArtifactId(project.getArtifact());
427 if (getLogger().isDebugEnabled()) {
428 getLogger().debug("dependency [" + id + "] has no license, add it in the missing file.");
429 }
430 unsafeMappings.setProperty(id, "");
431 }
432 }
433 return unsafeMappings;
434 }
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455 private File resolveThirdPartyDescriptor(MavenProject project, ArtifactRepository localRepository,
456 List<ArtifactRepository> repositories) throws IOException, ArtifactResolutionException,
457 ArtifactNotFoundException {
458 File result;
459
460
461 Artifact artifact = artifactFactory.createArtifactWithClassifier(project.getGroupId(), project.getArtifactId(),
462 project.getVersion(), DESCRIPTOR_TYPE, DESCRIPTOR_CLASSIFIER);
463 try {
464 artifactResolver.resolve(artifact, repositories, localRepository);
465
466 result = artifact.getFile();
467
468
469 if (result.length() == 0) {
470 getLogger().debug("Skipped third party descriptor");
471 }
472 } catch (ArtifactNotFoundException e) {
473 getLogger().debug("Unable to locate third party files descriptor : " + e);
474
475
476
477 result = new File(localRepository.getBasedir(), localRepository.pathOf(artifact));
478
479 FileUtil.createNewFile(result);
480 }
481
482 return result;
483 }
484
485 private final Pattern GAV_PLUS_TYPE_PATTERN = Pattern.compile("(.+)--(.+)--(.+)--(.+)");
486
487 private final Pattern GAV_PLUS_TYPE_AND_CLASSIFIER_PATTERN = Pattern.compile("(.+)--(.+)--(.+)--(.+)--(.+)");
488
489 private Map<String, String> migrateMissingFileKeys(Set<Object> missingFileKeys) {
490 Map<String, String> migrateKeys = new HashMap<String, String>();
491 for (Object object : missingFileKeys) {
492 String id = (String) object;
493 Matcher matcher;
494
495 String newId = id;
496 matcher = GAV_PLUS_TYPE_AND_CLASSIFIER_PATTERN.matcher(id);
497 if (matcher.matches()) {
498 newId = matcher.group(1) + "--" + matcher.group(2) + "--" + matcher.group(3);
499
500 } else {
501 matcher = GAV_PLUS_TYPE_PATTERN.matcher(id);
502 if (matcher.matches()) {
503 newId = matcher.group(1) + "--" + matcher.group(2) + "--" + matcher.group(3);
504
505 }
506 }
507 migrateKeys.put(id, newId);
508 }
509 return migrateKeys;
510 }
511 }