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 javax.inject.Inject;
22 import javax.inject.Named;
23 import javax.inject.Singleton;
24
25 import java.io.File;
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.Collections;
29 import java.util.HashMap;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.concurrent.Executor;
33
34 import org.eclipse.aether.ConfigurationProperties;
35 import org.eclipse.aether.RepositoryEvent;
36 import org.eclipse.aether.RepositoryEvent.EventType;
37 import org.eclipse.aether.RepositorySystemSession;
38 import org.eclipse.aether.RequestTrace;
39 import org.eclipse.aether.SyncContext;
40 import org.eclipse.aether.impl.MetadataResolver;
41 import org.eclipse.aether.impl.OfflineController;
42 import org.eclipse.aether.impl.RemoteRepositoryFilterManager;
43 import org.eclipse.aether.impl.RemoteRepositoryManager;
44 import org.eclipse.aether.impl.RepositoryConnectorProvider;
45 import org.eclipse.aether.impl.RepositoryEventDispatcher;
46 import org.eclipse.aether.impl.UpdateCheck;
47 import org.eclipse.aether.impl.UpdateCheckManager;
48 import org.eclipse.aether.metadata.Metadata;
49 import org.eclipse.aether.repository.ArtifactRepository;
50 import org.eclipse.aether.repository.LocalMetadataRegistration;
51 import org.eclipse.aether.repository.LocalMetadataRequest;
52 import org.eclipse.aether.repository.LocalMetadataResult;
53 import org.eclipse.aether.repository.LocalRepository;
54 import org.eclipse.aether.repository.LocalRepositoryManager;
55 import org.eclipse.aether.repository.RemoteRepository;
56 import org.eclipse.aether.repository.RepositoryPolicy;
57 import org.eclipse.aether.resolution.MetadataRequest;
58 import org.eclipse.aether.resolution.MetadataResult;
59 import org.eclipse.aether.spi.connector.MetadataDownload;
60 import org.eclipse.aether.spi.connector.RepositoryConnector;
61 import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilter;
62 import org.eclipse.aether.spi.synccontext.SyncContextFactory;
63 import org.eclipse.aether.transfer.MetadataNotFoundException;
64 import org.eclipse.aether.transfer.MetadataTransferException;
65 import org.eclipse.aether.transfer.NoRepositoryConnectorException;
66 import org.eclipse.aether.transfer.RepositoryOfflineException;
67 import org.eclipse.aether.util.concurrency.ExecutorUtils;
68 import org.eclipse.aether.util.concurrency.RunnableErrorForwarder;
69
70 import static java.util.Objects.requireNonNull;
71
72
73
74 @Singleton
75 @Named
76 public class DefaultMetadataResolver implements MetadataResolver {
77 private static final String CONFIG_PROPS_PREFIX = ConfigurationProperties.PREFIX_AETHER + "metadataResolver.";
78
79
80
81
82
83
84
85
86
87 public static final String CONFIG_PROP_THREADS = CONFIG_PROPS_PREFIX + "threads";
88
89 public static final int DEFAULT_THREADS = 4;
90
91 private final RepositoryEventDispatcher repositoryEventDispatcher;
92
93 private final UpdateCheckManager updateCheckManager;
94
95 private final RepositoryConnectorProvider repositoryConnectorProvider;
96
97 private final RemoteRepositoryManager remoteRepositoryManager;
98
99 private final SyncContextFactory syncContextFactory;
100
101 private final OfflineController offlineController;
102
103 private final RemoteRepositoryFilterManager remoteRepositoryFilterManager;
104
105 @Inject
106 public DefaultMetadataResolver(
107 RepositoryEventDispatcher repositoryEventDispatcher,
108 UpdateCheckManager updateCheckManager,
109 RepositoryConnectorProvider repositoryConnectorProvider,
110 RemoteRepositoryManager remoteRepositoryManager,
111 SyncContextFactory syncContextFactory,
112 OfflineController offlineController,
113 RemoteRepositoryFilterManager remoteRepositoryFilterManager) {
114 this.repositoryEventDispatcher =
115 requireNonNull(repositoryEventDispatcher, "repository event dispatcher cannot be null");
116 this.updateCheckManager = requireNonNull(updateCheckManager, "update check manager cannot be null");
117 this.repositoryConnectorProvider =
118 requireNonNull(repositoryConnectorProvider, "repository connector provider cannot be null");
119 this.remoteRepositoryManager =
120 requireNonNull(remoteRepositoryManager, "remote repository provider cannot be null");
121 this.syncContextFactory = requireNonNull(syncContextFactory, "sync context factory cannot be null");
122 this.offlineController = requireNonNull(offlineController, "offline controller cannot be null");
123 this.remoteRepositoryFilterManager =
124 requireNonNull(remoteRepositoryFilterManager, "remote repository filter manager cannot be null");
125 }
126
127 @Override
128 public List<MetadataResult> resolveMetadata(
129 RepositorySystemSession session, Collection<? extends MetadataRequest> requests) {
130 requireNonNull(session, "session cannot be null");
131 requireNonNull(requests, "requests cannot be null");
132 try (SyncContext shared = syncContextFactory.newInstance(session, true);
133 SyncContext exclusive = syncContextFactory.newInstance(session, false)) {
134 Collection<Metadata> metadata = new ArrayList<>(requests.size());
135 for (MetadataRequest request : requests) {
136 metadata.add(request.getMetadata());
137 }
138
139 return resolve(shared, exclusive, metadata, session, requests);
140 }
141 }
142
143 @SuppressWarnings("checkstyle:methodlength")
144 private List<MetadataResult> resolve(
145 SyncContext shared,
146 SyncContext exclusive,
147 Collection<Metadata> subjects,
148 RepositorySystemSession session,
149 Collection<? extends MetadataRequest> requests) {
150 SyncContext current = shared;
151 try {
152 while (true) {
153 current.acquire(null, subjects);
154
155 final List<MetadataResult> results = new ArrayList<>(requests.size());
156 final List<ResolveTask> tasks = new ArrayList<>(requests.size());
157 final Map<File, Long> localLastUpdates = new HashMap<>();
158 final RemoteRepositoryFilter remoteRepositoryFilter =
159 remoteRepositoryFilterManager.getRemoteRepositoryFilter(session);
160
161 for (MetadataRequest request : requests) {
162 RequestTrace trace = RequestTrace.newChild(request.getTrace(), request);
163
164 MetadataResult result = new MetadataResult(request);
165 results.add(result);
166
167 Metadata metadata = request.getMetadata();
168 RemoteRepository repository = request.getRepository();
169
170 if (repository == null) {
171 LocalRepository localRepo =
172 session.getLocalRepositoryManager().getRepository();
173
174 metadataResolving(session, trace, metadata, localRepo);
175
176 File localFile = getLocalFile(session, metadata);
177
178 if (localFile != null) {
179 metadata = metadata.setFile(localFile);
180 result.setMetadata(metadata);
181 } else {
182 result.setException(new MetadataNotFoundException(metadata, localRepo));
183 }
184
185 metadataResolved(session, trace, metadata, localRepo, result.getException());
186 continue;
187 }
188
189 if (remoteRepositoryFilter != null) {
190 RemoteRepositoryFilter.Result filterResult =
191 remoteRepositoryFilter.acceptMetadata(repository, metadata);
192 if (!filterResult.isAccepted()) {
193 result.setException(
194 new MetadataNotFoundException(metadata, repository, filterResult.reasoning()));
195 continue;
196 }
197 }
198
199 List<RemoteRepository> repositories =
200 getEnabledSourceRepositories(repository, metadata.getNature());
201
202 if (repositories.isEmpty()) {
203 continue;
204 }
205
206 metadataResolving(session, trace, metadata, repository);
207 LocalRepositoryManager lrm = session.getLocalRepositoryManager();
208 LocalMetadataRequest localRequest =
209 new LocalMetadataRequest(metadata, repository, request.getRequestContext());
210 LocalMetadataResult lrmResult = lrm.find(session, localRequest);
211
212 File metadataFile = lrmResult.getFile();
213
214 try {
215 Utils.checkOffline(session, offlineController, repository);
216 } catch (RepositoryOfflineException e) {
217 if (metadataFile != null) {
218 metadata = metadata.setFile(metadataFile);
219 result.setMetadata(metadata);
220 } else {
221 String msg = "Cannot access " + repository.getId() + " (" + repository.getUrl()
222 + ") in offline mode and the metadata " + metadata
223 + " has not been downloaded from it before";
224 result.setException(new MetadataNotFoundException(metadata, repository, msg, e));
225 }
226
227 metadataResolved(session, trace, metadata, repository, result.getException());
228 continue;
229 }
230
231 Long localLastUpdate = null;
232 if (request.isFavorLocalRepository()) {
233 File localFile = getLocalFile(session, metadata);
234 localLastUpdate = localLastUpdates.get(localFile);
235 if (localLastUpdate == null) {
236 localLastUpdate = localFile != null ? localFile.lastModified() : 0;
237 localLastUpdates.put(localFile, localLastUpdate);
238 }
239 }
240
241 List<UpdateCheck<Metadata, MetadataTransferException>> checks = new ArrayList<>();
242 Exception exception = null;
243 for (RemoteRepository repo : repositories) {
244 RepositoryPolicy policy = getPolicy(session, repo, metadata.getNature());
245
246 UpdateCheck<Metadata, MetadataTransferException> check = new UpdateCheck<>();
247 check.setLocalLastUpdated((localLastUpdate != null) ? localLastUpdate : 0);
248 check.setItem(metadata);
249
250
251 File checkFile = new File(
252 session.getLocalRepository().getBasedir(),
253 session.getLocalRepositoryManager()
254 .getPathForRemoteMetadata(metadata, repository, request.getRequestContext()));
255 check.setFile(checkFile);
256 check.setRepository(repository);
257 check.setAuthoritativeRepository(repo);
258 check.setArtifactPolicy(policy.getArtifactUpdatePolicy());
259 check.setMetadataPolicy(policy.getMetadataUpdatePolicy());
260
261 if (lrmResult.isStale()) {
262 checks.add(check);
263 } else {
264 updateCheckManager.checkMetadata(session, check);
265 if (check.isRequired()) {
266 checks.add(check);
267 } else if (exception == null) {
268 exception = check.getException();
269 }
270 }
271 }
272
273 if (!checks.isEmpty()) {
274 RepositoryPolicy policy = getPolicy(session, repository, metadata.getNature());
275
276
277 File installFile = new File(
278 session.getLocalRepository().getBasedir(),
279 session.getLocalRepositoryManager()
280 .getPathForRemoteMetadata(
281 metadata, request.getRepository(), request.getRequestContext()));
282
283 ResolveTask task = new ResolveTask(
284 session, trace, result, installFile, checks, policy.getChecksumPolicy());
285 tasks.add(task);
286 } else {
287 result.setException(exception);
288 if (metadataFile != null) {
289 metadata = metadata.setFile(metadataFile);
290 result.setMetadata(metadata);
291 }
292 metadataResolved(session, trace, metadata, repository, result.getException());
293 }
294 }
295
296 if (!tasks.isEmpty() && current == shared) {
297 current.close();
298 current = exclusive;
299 continue;
300 }
301
302 if (!tasks.isEmpty()) {
303 int threads = ExecutorUtils.threadCount(session, DEFAULT_THREADS, CONFIG_PROP_THREADS);
304 Executor executor = ExecutorUtils.executor(
305 Math.min(tasks.size(), threads), getClass().getSimpleName() + '-');
306 try {
307 RunnableErrorForwarder errorForwarder = new RunnableErrorForwarder();
308
309 for (ResolveTask task : tasks) {
310 metadataDownloading(
311 task.session, task.trace, task.request.getMetadata(), task.request.getRepository());
312
313 executor.execute(errorForwarder.wrap(task));
314 }
315
316 errorForwarder.await();
317
318 for (ResolveTask task : tasks) {
319
320
321
322
323
324 for (UpdateCheck<Metadata, MetadataTransferException> check : task.checks) {
325 updateCheckManager.touchMetadata(task.session, check.setException(task.exception));
326 }
327
328 metadataDownloaded(
329 session,
330 task.trace,
331 task.request.getMetadata(),
332 task.request.getRepository(),
333 task.metadataFile,
334 task.exception);
335
336 task.result.setException(task.exception);
337 }
338 } finally {
339 ExecutorUtils.shutdown(executor);
340 }
341 for (ResolveTask task : tasks) {
342 Metadata metadata = task.request.getMetadata();
343
344 LocalMetadataRequest localRequest = new LocalMetadataRequest(
345 metadata, task.request.getRepository(), task.request.getRequestContext());
346 File metadataFile = session.getLocalRepositoryManager()
347 .find(session, localRequest)
348 .getFile();
349 if (metadataFile != null) {
350 metadata = metadata.setFile(metadataFile);
351 task.result.setMetadata(metadata);
352 }
353 if (task.result.getException() == null) {
354 task.result.setUpdated(true);
355 }
356 metadataResolved(
357 session,
358 task.trace,
359 metadata,
360 task.request.getRepository(),
361 task.result.getException());
362 }
363 }
364
365 return results;
366 }
367 } finally {
368 current.close();
369 }
370 }
371
372 private File getLocalFile(RepositorySystemSession session, Metadata metadata) {
373 LocalRepositoryManager lrm = session.getLocalRepositoryManager();
374 LocalMetadataResult localResult = lrm.find(session, new LocalMetadataRequest(metadata, null, null));
375 return localResult.getFile();
376 }
377
378 private List<RemoteRepository> getEnabledSourceRepositories(RemoteRepository repository, Metadata.Nature nature) {
379 List<RemoteRepository> repositories = new ArrayList<>();
380
381 if (repository.isRepositoryManager()) {
382 for (RemoteRepository repo : repository.getMirroredRepositories()) {
383 if (isEnabled(repo, nature)) {
384 repositories.add(repo);
385 }
386 }
387 } else if (isEnabled(repository, nature)) {
388 repositories.add(repository);
389 }
390
391 return repositories;
392 }
393
394 private boolean isEnabled(RemoteRepository repository, Metadata.Nature nature) {
395 if (!Metadata.Nature.SNAPSHOT.equals(nature)
396 && repository.getPolicy(false).isEnabled()) {
397 return true;
398 }
399 return !Metadata.Nature.RELEASE.equals(nature)
400 && repository.getPolicy(true).isEnabled();
401 }
402
403 private RepositoryPolicy getPolicy(
404 RepositorySystemSession session, RemoteRepository repository, Metadata.Nature nature) {
405 boolean releases = !Metadata.Nature.SNAPSHOT.equals(nature);
406 boolean snapshots = !Metadata.Nature.RELEASE.equals(nature);
407 return remoteRepositoryManager.getPolicy(session, repository, releases, snapshots);
408 }
409
410 private void metadataResolving(
411 RepositorySystemSession session, RequestTrace trace, Metadata metadata, ArtifactRepository repository) {
412 RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_RESOLVING);
413 event.setTrace(trace);
414 event.setMetadata(metadata);
415 event.setRepository(repository);
416
417 repositoryEventDispatcher.dispatch(event.build());
418 }
419
420 private void metadataResolved(
421 RepositorySystemSession session,
422 RequestTrace trace,
423 Metadata metadata,
424 ArtifactRepository repository,
425 Exception exception) {
426 RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_RESOLVED);
427 event.setTrace(trace);
428 event.setMetadata(metadata);
429 event.setRepository(repository);
430 event.setException(exception);
431 event.setFile(metadata.getFile());
432
433 repositoryEventDispatcher.dispatch(event.build());
434 }
435
436 private void metadataDownloading(
437 RepositorySystemSession session, RequestTrace trace, Metadata metadata, ArtifactRepository repository) {
438 RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_DOWNLOADING);
439 event.setTrace(trace);
440 event.setMetadata(metadata);
441 event.setRepository(repository);
442
443 repositoryEventDispatcher.dispatch(event.build());
444 }
445
446 private void metadataDownloaded(
447 RepositorySystemSession session,
448 RequestTrace trace,
449 Metadata metadata,
450 ArtifactRepository repository,
451 File file,
452 Exception exception) {
453 RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_DOWNLOADED);
454 event.setTrace(trace);
455 event.setMetadata(metadata);
456 event.setRepository(repository);
457 event.setException(exception);
458 event.setFile(file);
459
460 repositoryEventDispatcher.dispatch(event.build());
461 }
462
463 class ResolveTask implements Runnable {
464 final RepositorySystemSession session;
465
466 final RequestTrace trace;
467
468 final MetadataResult result;
469
470 final MetadataRequest request;
471
472 final File metadataFile;
473
474 final String policy;
475
476 final List<UpdateCheck<Metadata, MetadataTransferException>> checks;
477
478 volatile MetadataTransferException exception;
479
480 ResolveTask(
481 RepositorySystemSession session,
482 RequestTrace trace,
483 MetadataResult result,
484 File metadataFile,
485 List<UpdateCheck<Metadata, MetadataTransferException>> checks,
486 String policy) {
487 this.session = session;
488 this.trace = trace;
489 this.result = result;
490 this.request = result.getRequest();
491 this.metadataFile = metadataFile;
492 this.policy = policy;
493 this.checks = checks;
494 }
495
496 public void run() {
497 Metadata metadata = request.getMetadata();
498 RemoteRepository requestRepository = request.getRepository();
499
500 try {
501 List<RemoteRepository> repositories = new ArrayList<>();
502 for (UpdateCheck<Metadata, MetadataTransferException> check : checks) {
503 repositories.add(check.getAuthoritativeRepository());
504 }
505
506 MetadataDownload download = new MetadataDownload();
507 download.setMetadata(metadata);
508 download.setRequestContext(request.getRequestContext());
509 download.setFile(metadataFile);
510 download.setChecksumPolicy(policy);
511 download.setRepositories(repositories);
512 download.setListener(SafeTransferListener.wrap(session));
513 download.setTrace(trace);
514
515 try (RepositoryConnector connector =
516 repositoryConnectorProvider.newRepositoryConnector(session, requestRepository)) {
517 connector.get(null, Collections.singletonList(download));
518 }
519
520 exception = download.getException();
521
522 if (exception == null) {
523
524 List<String> contexts = Collections.singletonList(request.getRequestContext());
525 LocalMetadataRegistration registration =
526 new LocalMetadataRegistration(metadata, requestRepository, contexts);
527
528 session.getLocalRepositoryManager().add(session, registration);
529 } else if (request.isDeleteLocalCopyIfMissing() && exception instanceof MetadataNotFoundException) {
530 download.getFile().delete();
531 }
532 } catch (NoRepositoryConnectorException e) {
533 exception = new MetadataTransferException(metadata, requestRepository, e);
534 }
535 }
536 }
537 }