1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.aether.internal.impl;
20
21 import java.nio.file.Files;
22 import java.nio.file.Path;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.Map;
28 import java.util.Objects;
29 import java.util.Properties;
30 import java.util.concurrent.ConcurrentHashMap;
31
32 import org.eclipse.aether.RepositorySystemSession;
33 import org.eclipse.aether.artifact.Artifact;
34 import org.eclipse.aether.metadata.Metadata;
35 import org.eclipse.aether.repository.LocalArtifactRegistration;
36 import org.eclipse.aether.repository.LocalArtifactRequest;
37 import org.eclipse.aether.repository.LocalArtifactResult;
38 import org.eclipse.aether.repository.RemoteRepository;
39 import org.eclipse.aether.repository.RepositoryKeyFunction;
40
41 import static java.util.Objects.requireNonNull;
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62 class EnhancedLocalRepositoryManager extends SimpleLocalRepositoryManager {
63
64 private static final String LOCAL_REPO_ID = "";
65
66
67
68
69
70 private static final Properties EMPTY_PROPERTIES = new Properties() {
71 @Override
72 public synchronized Object put(Object key, Object value) {
73 throw new UnsupportedOperationException("EMPTY_PROPERTIES is read-only");
74 }
75
76 @Override
77 public synchronized Object remove(Object key) {
78 throw new UnsupportedOperationException("EMPTY_PROPERTIES is read-only");
79 }
80
81 @Override
82 public synchronized void clear() {
83 throw new UnsupportedOperationException("EMPTY_PROPERTIES is read-only");
84 }
85 };
86
87 private final String trackingFilename;
88
89 private final TrackingFileManager trackingFileManager;
90
91 private final LocalPathPrefixComposer localPathPrefixComposer;
92
93
94
95
96
97
98
99
100
101
102
103
104 private final ConcurrentHashMap<Path, Properties> trackingFileCache = new ConcurrentHashMap<>();
105
106 EnhancedLocalRepositoryManager(
107 Path basedir,
108 LocalPathComposer localPathComposer,
109 RepositoryKeyFunction repositoryKeyFunction,
110 String trackingFilename,
111 TrackingFileManager trackingFileManager,
112 LocalPathPrefixComposer localPathPrefixComposer) {
113 super(basedir, "enhanced", localPathComposer, repositoryKeyFunction);
114 this.trackingFilename = requireNonNull(trackingFilename);
115 this.trackingFileManager = requireNonNull(trackingFileManager);
116 this.localPathPrefixComposer = requireNonNull(localPathPrefixComposer);
117 }
118
119 private String concatPaths(String prefix, String artifactPath) {
120 if (prefix == null || prefix.isEmpty()) {
121 return artifactPath;
122 }
123 return prefix + '/' + artifactPath;
124 }
125
126 @Override
127 public String getPathForLocalArtifact(Artifact artifact) {
128 return concatPaths(
129 localPathPrefixComposer.getPathPrefixForLocalArtifact(artifact),
130 super.getPathForLocalArtifact(artifact));
131 }
132
133 @Override
134 public String getPathForRemoteArtifact(Artifact artifact, RemoteRepository repository, String context) {
135 return concatPaths(
136 localPathPrefixComposer.getPathPrefixForRemoteArtifact(artifact, repository),
137 super.getPathForRemoteArtifact(artifact, repository, context));
138 }
139
140 @Override
141 public String getPathForLocalMetadata(Metadata metadata) {
142 return concatPaths(
143 localPathPrefixComposer.getPathPrefixForLocalMetadata(metadata),
144 super.getPathForLocalMetadata(metadata));
145 }
146
147 @Override
148 public String getPathForRemoteMetadata(Metadata metadata, RemoteRepository repository, String context) {
149 return concatPaths(
150 localPathPrefixComposer.getPathPrefixForRemoteMetadata(metadata, repository),
151 super.getPathForRemoteMetadata(metadata, repository, context));
152 }
153
154 @Override
155 public LocalArtifactResult find(RepositorySystemSession session, LocalArtifactRequest request) {
156 Artifact artifact = request.getArtifact();
157 LocalArtifactResult result = new LocalArtifactResult(request);
158
159 Path filePath;
160
161
162 if (Objects.equals(artifact.getVersion(), artifact.getBaseVersion())) {
163 filePath = getAbsolutePathForLocalArtifact(artifact);
164 checkFind(filePath, result);
165 }
166
167 if (!result.isAvailable()) {
168 for (RemoteRepository repository : request.getRepositories()) {
169 filePath = getAbsolutePathForRemoteArtifact(artifact, repository, request.getContext());
170
171 checkFind(filePath, result);
172
173 if (result.isAvailable()) {
174 break;
175 }
176 }
177 }
178
179 return result;
180 }
181
182 private void checkFind(Path path, LocalArtifactResult result) {
183 if (Files.isRegularFile(path)) {
184 result.setPath(path);
185
186 Properties props = readRepos(path);
187
188 if (props.get(getKey(path, LOCAL_REPO_ID)) != null) {
189
190 result.setAvailable(true);
191 } else {
192 String context = result.getRequest().getContext();
193 for (RemoteRepository repository : result.getRequest().getRepositories()) {
194 if (props.get(getKey(path, getRepositoryKey(repository, context))) != null) {
195
196
197 result.setAvailable(true);
198 result.setRepository(repository);
199 break;
200 }
201 }
202 if (!result.isAvailable() && !isTracked(props, path)) {
203
204
205
206
207 result.setAvailable(true);
208 }
209 }
210 }
211 }
212
213 @Override
214 public void add(RepositorySystemSession session, LocalArtifactRegistration request) {
215 Collection<String> repositories;
216 if (request.getRepository() == null) {
217 repositories = Collections.singleton(LOCAL_REPO_ID);
218 } else {
219 repositories = getRepositoryKeys(request.getRepository(), request.getContexts());
220 }
221 if (request.getRepository() == null) {
222 addArtifact(request.getArtifact(), repositories, null, null);
223 } else {
224 for (String context : request.getContexts()) {
225 addArtifact(request.getArtifact(), repositories, request.getRepository(), context);
226 }
227 }
228 }
229
230 private Collection<String> getRepositoryKeys(RemoteRepository repository, Collection<String> contexts) {
231 Collection<String> keys = new HashSet<>();
232
233 if (contexts != null) {
234 for (String context : contexts) {
235 keys.add(getRepositoryKey(repository, context));
236 }
237 }
238
239 return keys;
240 }
241
242 private void addArtifact(
243 Artifact artifact, Collection<String> repositories, RemoteRepository repository, String context) {
244 requireNonNull(artifact, "artifact cannot be null");
245 Path file = repository == null
246 ? getAbsolutePathForLocalArtifact(artifact)
247 : getAbsolutePathForRemoteArtifact(artifact, repository, context);
248 addRepo(file, repositories);
249 }
250
251 private Properties readRepos(Path artifactPath) {
252 Path trackingFile = getTrackingFile(artifactPath);
253 return trackingFileCache.computeIfAbsent(trackingFile, tf -> {
254 Properties props = trackingFileManager.read(tf);
255 return (props != null) ? props : EMPTY_PROPERTIES;
256 });
257 }
258
259 private void addRepo(Path artifactPath, Collection<String> repositories) {
260 Map<String, String> updates = new HashMap<>();
261 for (String repository : repositories) {
262 updates.put(getKey(artifactPath, repository), "");
263 }
264
265 Path trackingPath = getTrackingFile(artifactPath);
266
267
268
269
270 trackingFileCache.remove(trackingPath);
271 trackingFileManager.update(trackingPath, updates);
272 }
273
274 private Path getTrackingFile(Path artifactPath) {
275 return artifactPath.getParent().resolve(trackingFilename);
276 }
277
278 private String getKey(Path path, String repository) {
279 return path.getFileName() + ">" + repository;
280 }
281
282 private boolean isTracked(Properties props, Path path) {
283 if (props != null) {
284 String keyPrefix = path.getFileName() + ">";
285 for (Object key : props.keySet()) {
286 if (key.toString().startsWith(keyPrefix)) {
287 return true;
288 }
289 }
290 }
291 return false;
292 }
293 }