1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.repository.internal.relocation;
20
21 import javax.inject.Named;
22 import javax.inject.Singleton;
23
24 import java.util.Arrays;
25 import java.util.List;
26 import java.util.Objects;
27 import java.util.function.Predicate;
28 import java.util.stream.Collectors;
29 import java.util.stream.Stream;
30
31 import org.apache.maven.model.Model;
32 import org.apache.maven.repository.internal.MavenArtifactRelocationSource;
33 import org.apache.maven.repository.internal.RelocatedArtifact;
34 import org.eclipse.aether.RepositorySystemSession;
35 import org.eclipse.aether.artifact.Artifact;
36 import org.eclipse.aether.artifact.DefaultArtifact;
37 import org.eclipse.aether.resolution.ArtifactDescriptorException;
38 import org.eclipse.aether.resolution.ArtifactDescriptorResult;
39 import org.eclipse.sisu.Priority;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43
44
45
46
47
48
49 @Singleton
50 @Named(UserPropertiesArtifactRelocationSource.NAME)
51 @Priority(50)
52 @Deprecated(since = "4.0.0")
53 public final class UserPropertiesArtifactRelocationSource implements MavenArtifactRelocationSource {
54 public static final String NAME = "userProperties";
55 private static final Logger LOGGER = LoggerFactory.getLogger(UserPropertiesArtifactRelocationSource.class);
56
57 private static final String CONFIG_PROP_RELOCATIONS_ENTRIES = "maven.relocations.entries";
58
59 private static final Artifact SENTINEL = new DefaultArtifact("org.apache.maven.banned:user-relocation:1.0");
60
61 @Override
62 public Artifact relocatedTarget(
63 RepositorySystemSession session, ArtifactDescriptorResult artifactDescriptorResult, Model model)
64 throws ArtifactDescriptorException {
65 Relocations relocations = (Relocations) session.getData()
66 .computeIfAbsent(getClass().getName() + ".relocations", () -> parseRelocations(session));
67 if (relocations != null) {
68 Artifact original = artifactDescriptorResult.getRequest().getArtifact();
69 Relocation relocation = relocations.getRelocation(original);
70 if (relocation != null
71 && (isProjectContext(artifactDescriptorResult.getRequest().getRequestContext())
72 || relocation.global)) {
73 if (relocation.target == SENTINEL) {
74 String message = "The artifact " + original + " has been banned from resolution: "
75 + (relocation.global ? "User global ban" : "User project ban");
76 LOGGER.debug(message);
77 throw new ArtifactDescriptorException(artifactDescriptorResult, message);
78 }
79 Artifact result = new RelocatedArtifact(
80 original,
81 isAny(relocation.target.getGroupId()) ? null : relocation.target.getGroupId(),
82 isAny(relocation.target.getArtifactId()) ? null : relocation.target.getArtifactId(),
83 isAny(relocation.target.getClassifier()) ? null : relocation.target.getClassifier(),
84 isAny(relocation.target.getExtension()) ? null : relocation.target.getExtension(),
85 isAny(relocation.target.getVersion()) ? null : relocation.target.getVersion(),
86 relocation.global ? "User global relocation" : "User project relocation");
87 LOGGER.debug(
88 "The artifact {} has been relocated to {}: {}",
89 original,
90 result,
91 relocation.global ? "User global relocation" : "User project relocation");
92 return result;
93 }
94 }
95 return null;
96 }
97
98 private boolean isProjectContext(String context) {
99 return context != null && context.startsWith("project");
100 }
101
102 private static boolean isAny(String str) {
103 return "*".equals(str);
104 }
105
106 private static boolean matches(String pattern, String str) {
107 if (isAny(pattern)) {
108 return true;
109 } else if (pattern.endsWith("*")) {
110 return str.startsWith(pattern.substring(0, pattern.length() - 1));
111 } else {
112 return Objects.equals(pattern, str);
113 }
114 }
115
116 private static Predicate<Artifact> artifactPredicate(Artifact artifact) {
117 return a -> matches(artifact.getGroupId(), a.getGroupId())
118 && matches(artifact.getArtifactId(), a.getArtifactId())
119 && matches(artifact.getBaseVersion(), a.getBaseVersion())
120 && matches(artifact.getExtension(), a.getExtension())
121 && matches(artifact.getClassifier(), a.getClassifier());
122 }
123
124 private static class Relocation {
125 private final Predicate<Artifact> predicate;
126 private final boolean global;
127 private final Artifact source;
128 private final Artifact target;
129
130 private Relocation(boolean global, Artifact source, Artifact target) {
131 this.predicate = artifactPredicate(source);
132 this.global = global;
133 this.source = source;
134 this.target = target;
135 }
136
137 @Override
138 public String toString() {
139 return source + (global ? " >> " : " > ") + target;
140 }
141 }
142
143 private static class Relocations {
144 private final List<Relocation> relocations;
145
146 private Relocations(List<Relocation> relocations) {
147 this.relocations = relocations;
148 }
149
150 private Relocation getRelocation(Artifact artifact) {
151 return relocations.stream()
152 .filter(r -> r.predicate.test(artifact))
153 .findFirst()
154 .orElse(null);
155 }
156 }
157
158 private Relocations parseRelocations(RepositorySystemSession session) {
159 String relocationsEntries = (String) session.getConfigProperties().get(CONFIG_PROP_RELOCATIONS_ENTRIES);
160 if (relocationsEntries == null) {
161 return null;
162 }
163 String[] entries = relocationsEntries.split(",");
164 try (Stream<String> lines = Arrays.stream(entries)) {
165 List<Relocation> relocationList = lines.filter(
166 l -> l != null && !l.trim().isEmpty())
167 .map(l -> {
168 boolean global;
169 String splitExpr;
170 if (l.contains(">>")) {
171 global = true;
172 splitExpr = ">>";
173 } else if (l.contains(">")) {
174 global = false;
175 splitExpr = ">";
176 } else {
177 throw new IllegalArgumentException("Unrecognized entry: " + l);
178 }
179 String[] parts = l.split(splitExpr);
180 if (parts.length < 1) {
181 throw new IllegalArgumentException("Unrecognized entry: " + l);
182 }
183 Artifact s = parseArtifact(parts[0]);
184 Artifact t;
185 if (parts.length > 1) {
186 t = parseArtifact(parts[1]);
187 } else {
188 t = SENTINEL;
189 }
190 return new Relocation(global, s, t);
191 })
192 .collect(Collectors.toList());
193 LOGGER.info("Parsed {} user relocations", relocationList.size());
194 return new Relocations(relocationList);
195 }
196 }
197
198 private static Artifact parseArtifact(String coords) {
199 Artifact s;
200 String[] parts = coords.split(":");
201 switch (parts.length) {
202 case 3:
203 s = new DefaultArtifact(parts[0], parts[1], "*", "*", parts[2]);
204 break;
205 case 4:
206 s = new DefaultArtifact(parts[0], parts[1], "*", parts[2], parts[3]);
207 break;
208 case 5:
209 s = new DefaultArtifact(parts[0], parts[1], parts[2], parts[3], parts[4]);
210 break;
211 default:
212 throw new IllegalArgumentException("Bad artifact coordinates " + coords
213 + ", expected format is <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>");
214 }
215 return s;
216 }
217 }