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