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