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