-
Notifications
You must be signed in to change notification settings - Fork 1
/
ProjectDependencyReader.java
157 lines (142 loc) · 6.54 KB
/
ProjectDependencyReader.java
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package com.exasol.projectkeeper.validators.dependencies;
import static com.exasol.projectkeeper.shared.dependencies.BaseDependency.Type.*;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.model.*;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.ProjectBuildingException;
import com.exasol.errorreporting.ExaError;
import com.exasol.projectkeeper.pom.MavenModelFromRepositoryReader;
import com.exasol.projectkeeper.shared.dependencies.*;
import com.exasol.projectkeeper.shared.dependencies.License;
/**
* This class reads all dependencies of a pom file (including the plugins) together with their license.
*/
public class ProjectDependencyReader {
private final MavenModelFromRepositoryReader artifactModelReader;
private final MavenProject project;
/**
* Create a new instance of {@link ProjectDependencyReader}.
*
* @param artifactModelReader maven dependency reader
* @param project maven project
*/
public ProjectDependencyReader(final MavenModelFromRepositoryReader artifactModelReader,
final MavenProject project) {
this.artifactModelReader = artifactModelReader;
this.project = project;
}
/**
* Read the dependencies of the pom file (including plugins).
*
* @return list of dependencies
*/
public ProjectDependencies readDependencies() {
final List<ProjectDependency> dependencies = getDependenciesIncludingPlugins()
.map(dependency -> getLicense(dependency, project)) //
.collect(Collectors.toList());
return new ProjectDependencies(dependencies);
}
private Stream<Dependency> getDependenciesIncludingPlugins() {
return Stream.concat(getDependencies(), getPluginDependencies());
}
private Stream<Dependency> getDependencies() {
return project.getModel().getDependencies().stream();
}
private Stream<Dependency> getPluginDependencies() {
return project.getModel().getBuild().getPlugins().stream() //
.filter(this::isExplicitPlugin) //
.map(this::convertPluginToDependency);
}
/**
* Check if the given plugin is an explicit or implicit plugin.
*
* <ul>
* <li>Direct plugins (e.g. {@code org.apache.maven.plugins:maven-failsafe-plugin}) are explicitly added to the
* build in a POM or parent POM.
* <ul>
* <li>Source model ID is {@code com.exasol:project-keeper-shared-test-setup-generated-parent:${revision}}
* or {@code com.exasol:project-keeper-cli:${revision}}</li></li>
* <li>Source location is {@code /path/to/project-keeper/project-keeper-cli/pom.xml} or
* {@code /path/to/project-keeper/project-keeper-maven-plugin/pk_generated_parent.pom}</li>
* </ul>
* <li>Indirect plugins (e.g. {@code org.apache.maven.plugins:maven-clean-plugin}) are implicitly added to the build
* a Maven lifecycle.
* <ul>
* <li>Source model ID is {@code org.apache.maven:maven-core:3.8.7:default-lifecycle-bindings}</li>
* <li>Source location is {@code null}</li>
* </ul>
* </li>
* </ul>
* The Maven API allows distinguishing both types via Source model ID and Source location. We decided to only use
* the source location as this requires only a simple not-null check.
*
* @param plugin plugin to check
* @return {@code true} if the plugin is explicitly added to the build
*/
// [impl -> dsn~dependency.md-file-validator-excludes-implicit-plugins~1]
private boolean isExplicitPlugin(final Plugin plugin) {
final String location = plugin.getLocation("").getSource().getLocation();
return location != null;
}
private Dependency convertPluginToDependency(final Plugin plugin) {
final var dependency = new Dependency();
dependency.setGroupId(plugin.getGroupId());
dependency.setArtifactId(plugin.getArtifactId());
dependency.setVersion(plugin.getVersion());
dependency.setScope("plugin");
return dependency;
}
private ProjectDependency getLicense(final Dependency dependency, final MavenProject project) {
try {
@SuppressWarnings("deprecation") // Maven class ArtifactRepository is deprecated
final List<ArtifactRepository> repos = project.getRemoteArtifactRepositories();
final var dependenciesPom = this.artifactModelReader.readModel(dependency.getArtifactId(),
dependency.getGroupId(), dependency.getVersion(), repos);
final List<License> licenses = dependenciesPom.getLicenses().stream()
.map(license -> new License(license.getName(), license.getUrl())).collect(Collectors.toList());
return ProjectDependency.builder() //
.type(mapScopeToDependencyType(dependency.getScope())) //
.name(getDependencyName(dependenciesPom)) //
.websiteUrl(dependenciesPom.getUrl()) //
.licenses(licenses) //
.build();
} catch (final ProjectBuildingException exception) {
throw new IllegalStateException(ExaError.messageBuilder("E-PK-MPC-49")
.message("Failed to get license information for dependency {{groupId}}:{{artifactId}}.",
dependency.getGroupId(), dependency.getArtifactId())
.toString(), exception);
}
}
private String getDependencyName(final Model dependenciesPom) {
if ((dependenciesPom.getName() == null) || dependenciesPom.getName().isBlank()) {
return dependenciesPom.getArtifactId();
} else {
return dependenciesPom.getName();
}
}
private BaseDependency.Type mapScopeToDependencyType(final String scope) {
if (scope == null) {
return COMPILE;
} else {
switch (scope) {
case "compile":
case "provided":
case "system":
case "import":
return COMPILE;
case "test":
return TEST;
case "runtime":
return RUNTIME;
case "plugin":
return PLUGIN;
default:
throw new IllegalStateException(ExaError.messageBuilder("F-PK-MPC-54")
.message("Unimplemented dependency scope {{scope}}.", scope).ticketMitigation().toString());
}
}
}
}