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