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