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
|
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.container.plugin.util;
import org.apache.maven.artifact.Artifact;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import static java.util.stream.Collectors.toMap;
/**
* Translates the scope of dependencies when constructing a test bundle.
* Used by {@link Artifacts} to determine which artifacts that are provided by the runtime or must be included in the bundle.
*
* Dependencies of scope 'test' are by default translated to 'compile'. Dependencies of other scopes are kept as is.
*
* Default scope translation for 'test' scope dependencies can be overridden through a comma-separated configuration string.
* Each substring is a triplet on the form [groupId]:[artifactId]:[scope].
* Scope translation overrides affects all transitive dependencies.
* The ordering of the triplets determines the priority - only the first matching override will affect a given dependency.
*
* @author bjorncs
*/
public class TestBundleDependencyScopeTranslator implements Artifacts.ScopeTranslator {
private static final Logger log = Logger.getLogger(TestBundleDependencyScopeTranslator.class.getName());
private final Map<Artifact, String> dependencyScopes;
private TestBundleDependencyScopeTranslator(Map<Artifact, String> dependencyScopes) {
this.dependencyScopes = dependencyScopes;
}
@Override
public String scopeOf(Artifact artifact) {
return Objects.requireNonNull(dependencyScopes.get(artifact), () -> "Could not lookup scope for " + artifact);
}
public static TestBundleDependencyScopeTranslator from(Collection<Artifact> dependencies, String rawConfig) {
List<DependencyOverride> dependencyOverrides = toDependencyOverrides(rawConfig);
Map<Artifact, String> dependencyScopes = new HashMap<>();
Map<String, Artifact> dependenciesById = dependencies.stream().collect(toMap(Artifact::getId, Function.identity()));
for (Artifact dependency : dependencies) {
dependencyScopes.put(dependency, getScopeForDependency(dependency, dependencyOverrides, dependenciesById));
}
return new TestBundleDependencyScopeTranslator(dependencyScopes);
}
private static List<DependencyOverride> toDependencyOverrides(String rawConfig) {
if (rawConfig == null || rawConfig.isBlank()) return List.of();
return Arrays.stream(rawConfig.split(","))
.map(String::strip)
.filter(s -> !s.isBlank())
.map(TestBundleDependencyScopeTranslator::toDependencyOverride)
.toList();
}
private static DependencyOverride toDependencyOverride(String overrideString) {
String[] elements = overrideString.split(":");
if (elements.length != 3) {
throw new IllegalArgumentException("Invalid dependency override: " + overrideString);
}
return new DependencyOverride(elements[0], elements[1], elements[2]);
}
private static String getScopeForDependency(
Artifact dependency, List<DependencyOverride> overrides, Map<String, Artifact> otherArtifacts) {
String oldScope = dependency.getScope();
if (!oldScope.equals(Artifact.SCOPE_TEST)) return oldScope;
for (DependencyOverride override : overrides) {
for (Artifact dependent : dependencyTrailOf(dependency, otherArtifacts)) {
if (override.isForArtifact(dependent)) {
log.fine(() -> String.format(
"Overriding scope of '%s'; scope '%s' overridden to '%s'",
dependency.getId(), oldScope, override.scope));
return override.scope;
}
}
}
log.fine(() -> String.format(
"Using default scope translation for '%s'; scope 'test' translated to 'compile'",
dependency.getId()));
return Artifact.SCOPE_COMPILE;
}
private static List<Artifact> dependencyTrailOf(Artifact artifact, Map<String, Artifact> otherArtifacts) {
return artifact.getDependencyTrail().stream()
.skip(1) // Maven project itself is the first entry
.map(otherArtifacts::get)
.filter(Objects::nonNull)
.toList();
}
private static class DependencyOverride {
final String groupId;
final String artifactId;
final String scope;
DependencyOverride(String groupId, String artifactId, String scope) {
this.groupId = groupId;
this.artifactId = artifactId;
this.scope = scope;
}
boolean isForArtifact(Artifact artifact) {
return artifact.getGroupId().equals(groupId) && artifact.getArtifactId().equals(artifactId);
}
}
}
|