1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package io.wcm.devops.conga.tooling.maven.plugin.util;
21
22 import static org.apache.maven.artifact.Artifact.SCOPE_COMPILE;
23
24 import java.io.IOException;
25 import java.util.ArrayList;
26 import java.util.Collection;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Objects;
30 import java.util.Set;
31
32 import org.apache.commons.lang3.StringUtils;
33 import org.apache.maven.project.MavenProject;
34 import org.eclipse.aether.RepositorySystem;
35 import org.eclipse.aether.RepositorySystemSession;
36 import org.eclipse.aether.artifact.Artifact;
37 import org.eclipse.aether.artifact.ArtifactType;
38 import org.eclipse.aether.artifact.DefaultArtifact;
39 import org.eclipse.aether.graph.Dependency;
40 import org.eclipse.aether.repository.RemoteRepository;
41 import org.eclipse.aether.resolution.ArtifactDescriptorException;
42 import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
43 import org.eclipse.aether.resolution.ArtifactDescriptorResult;
44 import org.eclipse.aether.resolution.ArtifactRequest;
45 import org.eclipse.aether.resolution.ArtifactResolutionException;
46 import org.eclipse.aether.resolution.ArtifactResult;
47
48 import io.wcm.devops.conga.generator.spi.context.PluginContextOptions;
49 import io.wcm.devops.conga.model.environment.Environment;
50 import io.wcm.devops.conga.tooling.maven.plugin.urlfile.MavenUrlFilePlugin;
51
52
53
54
55 public class MavenArtifactHelper {
56
57 private final MavenProject project;
58 private final RepositorySystem repoSystem;
59 private final RepositorySystemSession repoSession;
60 private final List<RemoteRepository> remoteRepos;
61 private final Map<String, String> artifactTypeMappings;
62 private final List<String> environmentDependencyUrls;
63 private final PluginContextOptions pluginContextOptions;
64
65
66
67
68
69 public MavenArtifactHelper(Environment environment, PluginContextOptions pluginContextOptions) {
70 MavenContext mavenContext = (MavenContext)pluginContextOptions.getContainerContext();
71 this.project = mavenContext.getProject();
72 this.repoSystem = mavenContext.getRepoSystem();
73 this.repoSession = mavenContext.getRepoSession();
74 this.remoteRepos = mavenContext.getRemoteRepos();
75 this.artifactTypeMappings = mavenContext.getArtifactTypeMappings();
76 this.environmentDependencyUrls = environment != null ? environment.getDependencies() : List.of();
77 this.pluginContextOptions = pluginContextOptions;
78 }
79
80
81
82
83
84
85
86 public Artifact resolveArtifact(String artifactCoords) throws IOException {
87 Artifact artifact;
88 if (StringUtils.contains(artifactCoords, "/")) {
89 artifact = getArtifactFromMavenCoordinatesPaxUrlStyle(artifactCoords);
90 }
91 else {
92 artifact = getArtifactFromMavenCoordinates(artifactCoords);
93 }
94 return resolveArtifact(artifact);
95 }
96
97
98
99
100
101
102
103
104
105
106
107 @SuppressWarnings("PMD.UseObjectForClearerAPI")
108 public Artifact resolveArtifact(String groupId, String artifactId, String type, String classifier, String version) throws IOException {
109 Artifact artifact = createArtifact(groupId, artifactId, type, classifier, version);
110 return resolveArtifact(artifact);
111 }
112
113
114
115
116
117
118
119 public List<Artifact> getTransitiveDependencies(Artifact artifact) throws IOException {
120 List<Artifact> dependencies = new ArrayList<>();
121 ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest();
122 descriptorRequest.setArtifact(artifact);
123 descriptorRequest.setRepositories(remoteRepos);
124 try {
125 ArtifactDescriptorResult result = repoSystem.readArtifactDescriptor(repoSession, descriptorRequest);
126 for (Dependency dependency : result.getDependencies()) {
127 if (StringUtils.equals(dependency.getScope(), SCOPE_COMPILE)) {
128 Artifact resolvedArtifact = resolveArtifact(dependency.getArtifact());
129 dependencies.add(resolvedArtifact);
130 }
131 }
132 }
133 catch (ArtifactDescriptorException ex) {
134 throw new IOException("Unable to get artifact descriptor for: '" + artifact + "': " + ex.getMessage(), ex);
135 }
136 return dependencies;
137 }
138
139
140
141
142
143
144
145
146 public List<Artifact> dependencyUrlsToArtifactsWithTransitiveDependencies(Collection<String> dependencyUrls) throws IOException {
147 List<Artifact> artifacts = new ArrayList<>();
148 for (String dependencyUrl : environmentDependencyUrls) {
149 String resolvedDependencyUrl = ClassLoaderUtil.resolveDependencyUrl(dependencyUrl, pluginContextOptions);
150 if (!StringUtils.startsWith(resolvedDependencyUrl, MavenUrlFilePlugin.PREFIX)) {
151 continue;
152 }
153
154 String mavenCoords = MavenUrlFilePlugin.getMavenCoords(resolvedDependencyUrl);
155 Artifact artifact = resolveArtifact(mavenCoords);
156 artifacts.add(artifact);
157 artifacts.addAll(getTransitiveDependencies(artifact));
158 }
159 return artifacts;
160 }
161
162
163
164
165
166
167 @SuppressWarnings("PMD.PreserveStackTrace")
168 private Artifact getArtifactFromMavenCoordinates(String artifactCoords) throws IOException {
169 try {
170 Artifact artifact = new DefaultArtifact(artifactCoords);
171 return createArtifact(artifact.getGroupId(), artifact.getArtifactId(), artifact.getExtension(), artifact.getClassifier(), artifact.getVersion());
172 }
173 catch (IllegalArgumentException ex) {
174 throw new IOException(ex.getMessage());
175 }
176 }
177
178
179
180
181
182
183 private Artifact getArtifactFromMavenCoordinatesPaxUrlStyle(String artifactCoords) throws IOException {
184 String[] parts = StringUtils.splitPreserveAllTokens(artifactCoords, "/");
185
186 String version = null;
187 String packaging = null;
188 String classifier = null;
189
190 switch (parts.length) {
191 case 2:
192
193 break;
194
195 case 3:
196
197 version = StringUtils.defaultIfBlank(parts[2], null);
198 break;
199
200 case 4:
201
202 packaging = StringUtils.defaultIfBlank(parts[3], null);
203 version = StringUtils.defaultIfBlank(parts[2], null);
204 break;
205
206 case 5:
207
208 packaging = StringUtils.defaultIfBlank(parts[3], null);
209 classifier = StringUtils.defaultIfBlank(parts[4], null);
210 version = StringUtils.defaultIfBlank(parts[2], null);
211 break;
212
213 default:
214 throw new IOException("Invalid artifact: " + artifactCoords);
215 }
216
217 String groupId = StringUtils.defaultIfBlank(parts[0], null);
218 String artifactId = StringUtils.defaultIfBlank(parts[1], null);
219
220 return createArtifact(groupId, artifactId, packaging, classifier, version);
221 }
222
223 private Artifact createArtifact(String groupId, String artifactId, String type, String classifier, String version) throws IOException {
224
225 String artifactTypeString = Objects.toString(type, "jar");
226 String artifactExtension = artifactTypeString;
227
228 ArtifactType artifactType = repoSession.getArtifactTypeRegistry().get(artifactExtension);
229 if (artifactType != null) {
230 artifactExtension = artifactType.getExtension();
231 }
232
233
234 if (artifactTypeMappings != null && artifactTypeMappings.containsKey(artifactExtension)) {
235 artifactExtension = artifactTypeMappings.get(artifactExtension);
236 }
237
238 String artifactVersion = version;
239 if (artifactVersion == null) {
240 artifactVersion = resolveArtifactVersion(groupId, artifactId, artifactTypeString, classifier);
241 }
242
243 if (StringUtils.isBlank(groupId) || StringUtils.isBlank(artifactId) || StringUtils.isBlank(artifactVersion)) {
244 throw new IOException("Invalid Maven artifact reference: "
245 + "artifactId=" + artifactId + ", "
246 + "groupId=" + groupId + ", "
247 + "version=" + artifactVersion + ", "
248 + "extension=" + artifactExtension + ", "
249 + "classifier=" + classifier + ","
250 + "type=" + artifactType);
251 }
252
253 return new DefaultArtifact(groupId, artifactId, classifier, artifactExtension, artifactVersion, artifactType);
254 }
255
256 private Artifact resolveArtifact(Artifact artifact) throws IOException {
257 ArtifactRequest artifactRequest = new ArtifactRequest();
258 artifactRequest.setArtifact(artifact);
259 artifactRequest.setRepositories(remoteRepos);
260 try {
261 ArtifactResult result = repoSystem.resolveArtifact(repoSession, artifactRequest);
262 return result.getArtifact();
263 }
264 catch (final ArtifactResolutionException ex) {
265 throw new IOException("Unable to get artifact for '" + artifact + "': " + ex.getMessage(), ex);
266 }
267 }
268
269 private String resolveArtifactVersion(String groupId, String artifactId, String type, String classifier) throws IOException {
270 String version = findVersionInMavenProject(groupId, artifactId, type, classifier);
271 if (version == null) {
272 version = findVersionInEnvironmentDependencies(groupId, artifactId, type, classifier);
273 }
274 return version;
275 }
276
277 private String findVersionInMavenProject(String groupId, String artifactId, String type, String classifier) {
278 Set<org.apache.maven.artifact.Artifact> dependencies = project.getArtifacts();
279 if (dependencies != null) {
280 for (org.apache.maven.artifact.Artifact dependency : dependencies) {
281 if (artifactEquals(dependency, artifactId, groupId, type, classifier)) {
282 return dependency.getVersion();
283 }
284 }
285 }
286 return null;
287 }
288
289 private boolean artifactEquals(org.apache.maven.artifact.Artifact dependency, String artifactId, String groupId, String type, String classifier) {
290 return StringUtils.equals(dependency.getGroupId(), groupId)
291 && StringUtils.equals(dependency.getArtifactId(), artifactId)
292 && StringUtils.equals(StringUtils.defaultString(dependency.getClassifier()), StringUtils.defaultString(classifier))
293 && StringUtils.equals(dependency.getType(), type);
294 }
295
296 private String findVersionInEnvironmentDependencies(String groupId, String artifactId, String packaging, String classifier) throws IOException {
297 List<Artifact> dependencies = dependencyUrlsToArtifactsWithTransitiveDependencies(environmentDependencyUrls);
298
299 for (Artifact dependency : dependencies) {
300 if (artifactEquals(dependency, groupId, artifactId, packaging, classifier)) {
301 return dependency.getVersion();
302 }
303 }
304
305 return null;
306 }
307
308 private boolean artifactEquals(Artifact dependency, String groupId, String artifactId, String type, String classifier) {
309 return StringUtils.equals(dependency.getGroupId(), groupId)
310 && StringUtils.equals(dependency.getArtifactId(), artifactId)
311 && StringUtils.equals(StringUtils.defaultString(dependency.getClassifier()), StringUtils.defaultString(classifier))
312 && StringUtils.equals(dependency.getExtension(), type);
313 }
314
315 }