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