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