blob: 3a64ddb4dfee047feb659d1c575955cb31383b71 [file] [log] [blame]
package org.jetbrains.idea.maven.server.embedder;
import org.apache.maven.RepositoryUtils;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.factory.ArtifactFactory;
import org.apache.maven.artifact.metadata.ArtifactMetadataRetrievalException;
import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
import org.apache.maven.artifact.metadata.ResolutionGroup;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.repository.LegacyLocalRepositoryManager;
import org.apache.maven.artifact.repository.RepositoryRequest;
import org.apache.maven.artifact.repository.metadata.Snapshot;
import org.apache.maven.artifact.repository.metadata.SnapshotArtifactRepositoryMetadata;
import org.apache.maven.artifact.resolver.*;
import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.LegacySupport;
import org.apache.maven.repository.legacy.metadata.DefaultMetadataResolutionRequest;
import org.apache.maven.repository.legacy.metadata.MetadataResolutionRequest;
import org.apache.maven.repository.legacy.resolver.conflict.ConflictResolver;
import org.apache.maven.wagon.events.TransferListener;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.component.annotations.Requirement;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.logging.Logger;
import org.jetbrains.idea.maven.model.MavenWorkspaceMap;
import org.jetbrains.idea.maven.server.MavenModelConverter;
import org.jetbrains.idea.maven.server.UnresolvedArtifactsCollector;
import org.sonatype.aether.RepositorySystem;
import org.sonatype.aether.RepositorySystemSession;
import org.sonatype.aether.repository.LocalRepositoryManager;
import org.sonatype.aether.resolution.ArtifactRequest;
import org.sonatype.aether.resolution.ArtifactResult;
import java.io.File;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
/**
* Copy pasted from org.apache.maven.artifact.resolver.DefaultArtifactResolver,
* overriden method resolve( Artifact artifact, List<ArtifactRepository> remoteRepositories, RepositorySystemSession session )
* @author Sergey Evdokimov
*/
@Component(role = ArtifactResolver.class)
public class CustomMaven3ArtifactResolver
implements ArtifactResolver
{
@Requirement
private Logger logger;
@Requirement
protected ArtifactFactory artifactFactory;
@Requirement
private ArtifactCollector artifactCollector;
@Requirement
private ResolutionErrorHandler resolutionErrorHandler;
@Requirement
private ArtifactMetadataSource source;
@Requirement
private PlexusContainer container;
@Requirement
private LegacySupport legacySupport;
@Requirement
private RepositorySystem repoSystem;
private final Executor executor;
private volatile MavenWorkspaceMap myWorkspaceMap;
private volatile UnresolvedArtifactsCollector myUnresolvedCollector;
public CustomMaven3ArtifactResolver()
{
int threads = Integer.getInteger( "maven.artifact.threads", 5 ).intValue();
if ( threads <= 1 )
{
executor = new Executor()
{
public void execute( Runnable command )
{
command.run();
}
};
}
else
{
executor =
new ThreadPoolExecutor( threads, threads, 3, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new DaemonThreadCreator());
}
}
@Override
protected void finalize()
throws Throwable
{
if ( executor instanceof ExecutorService )
{
( (ExecutorService) executor ).shutdown();
}
}
private RepositorySystemSession getSession(ArtifactRepository localRepository) {
return LegacyLocalRepositoryManager.overlay( localRepository, legacySupport.getRepositorySession(), repoSystem );
}
private void injectSession1( RepositoryRequest request, MavenSession session )
{
if ( session != null )
{
request.setOffline( session.isOffline() );
request.setForceUpdate( session.getRequest().isUpdateSnapshots() );
}
}
private void injectSession2( ArtifactResolutionRequest request, MavenSession session )
{
injectSession1( request, session );
if ( session != null )
{
request.setServers( session.getRequest().getServers() );
request.setMirrors( session.getRequest().getMirrors() );
request.setProxies( session.getRequest().getProxies() );
}
}
public void resolve( Artifact artifact, List<ArtifactRepository> remoteRepositories, ArtifactRepository localRepository, TransferListener resolutionListener )
throws ArtifactResolutionException, ArtifactNotFoundException
{
resolve( artifact, remoteRepositories, getSession( localRepository ) );
}
public void resolveAlways( Artifact artifact, List<ArtifactRepository> remoteRepositories, ArtifactRepository localRepository )
throws ArtifactResolutionException, ArtifactNotFoundException
{
resolve( artifact, remoteRepositories, getSession( localRepository ) );
}
private void resolve( Artifact artifact, List<ArtifactRepository> remoteRepositories, RepositorySystemSession session )
throws ArtifactResolutionException, ArtifactNotFoundException {
if (resolveAsModule(artifact)) return;
try {
resolveOld(artifact, remoteRepositories, session);
}
catch (AbstractArtifactResolutionException e) {
myUnresolvedCollector.collectAndSetResolved(artifact);
}
}
private void resolveOld( Artifact artifact, List<ArtifactRepository> remoteRepositories, RepositorySystemSession session )
throws ArtifactResolutionException, ArtifactNotFoundException
{
if ( artifact == null )
{
return;
}
if ( Artifact.SCOPE_SYSTEM.equals( artifact.getScope() ) )
{
File systemFile = artifact.getFile();
if ( systemFile == null )
{
throw new ArtifactNotFoundException( "System artifact: " + artifact + " has no file attached", artifact );
}
if ( !systemFile.exists() )
{
throw new ArtifactNotFoundException( "System artifact: " + artifact + " not found in path: " + systemFile, artifact );
}
if ( !systemFile.isFile() )
{
throw new ArtifactNotFoundException( "System artifact: " + artifact + " is not a file: " + systemFile, artifact );
}
artifact.setResolved( true );
return;
}
if ( !artifact.isResolved() )
{
ArtifactResult result;
try
{
ArtifactRequest artifactRequest = new ArtifactRequest();
artifactRequest.setArtifact( RepositoryUtils.toArtifact( artifact ) );
artifactRequest.setRepositories( RepositoryUtils.toRepos( remoteRepositories ) );
// Maven 2.x quirk: an artifact always points at the local repo, regardless whether resolved or not
LocalRepositoryManager lrm = session.getLocalRepositoryManager();
String path = lrm.getPathForLocalArtifact( artifactRequest.getArtifact() );
artifact.setFile( new File( lrm.getRepository().getBasedir(), path ) );
result = repoSystem.resolveArtifact( session, artifactRequest );
}
catch ( org.sonatype.aether.resolution.ArtifactResolutionException e )
{
if ( e.getCause() instanceof org.sonatype.aether.transfer.ArtifactNotFoundException )
{
throw new ArtifactNotFoundException( e.getMessage(), artifact, remoteRepositories, e ) {};
}
else
{
throw new ArtifactResolutionException( e.getMessage(), artifact, remoteRepositories, e );
}
}
artifact.selectVersion( result.getArtifact().getVersion() );
artifact.setFile( result.getArtifact().getFile() );
artifact.setResolved( true );
if ( artifact.isSnapshot() )
{
Matcher matcher = Artifact.VERSION_FILE_PATTERN.matcher( artifact.getVersion() );
if ( matcher.matches() )
{
Snapshot snapshot = new Snapshot();
snapshot.setTimestamp( matcher.group( 2 ) );
try
{
snapshot.setBuildNumber( Integer.parseInt( matcher.group( 3 ) ) );
artifact.addMetadata( new SnapshotArtifactRepositoryMetadata( artifact, snapshot ) );
}
catch ( NumberFormatException e )
{
logger.warn( "Invalid artifact version " + artifact.getVersion() + ": " + e.getMessage() );
}
}
}
}
}
public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact, ArtifactRepository localRepository, List<ArtifactRepository> remoteRepositories,
ArtifactMetadataSource source, ArtifactFilter filter )
throws ArtifactResolutionException, ArtifactNotFoundException
{
return resolveTransitively( artifacts, originatingArtifact, Collections.EMPTY_MAP, localRepository, remoteRepositories, source, filter );
}
public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact, Map managedVersions, ArtifactRepository localRepository,
List<ArtifactRepository> remoteRepositories, ArtifactMetadataSource source )
throws ArtifactResolutionException, ArtifactNotFoundException
{
return resolveTransitively( artifacts, originatingArtifact, managedVersions, localRepository, remoteRepositories, source, null );
}
public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact, Map managedVersions, ArtifactRepository localRepository,
List<ArtifactRepository> remoteRepositories, ArtifactMetadataSource source, ArtifactFilter filter )
throws ArtifactResolutionException, ArtifactNotFoundException
{
return resolveTransitively( artifacts, originatingArtifact, managedVersions, localRepository, remoteRepositories, source, filter, null );
}
public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact, List<ArtifactRepository> remoteRepositories, ArtifactRepository localRepository,
ArtifactMetadataSource source )
throws ArtifactResolutionException, ArtifactNotFoundException
{
return resolveTransitively( artifacts, originatingArtifact, localRepository, remoteRepositories, source, null );
}
public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact, List<ArtifactRepository> remoteRepositories, ArtifactRepository localRepository,
ArtifactMetadataSource source, List<ResolutionListener> listeners )
throws ArtifactResolutionException, ArtifactNotFoundException
{
return resolveTransitively( artifacts, originatingArtifact, Collections.EMPTY_MAP, localRepository,
remoteRepositories, source, null, listeners );
}
public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact, Map managedVersions, ArtifactRepository localRepository,
List<ArtifactRepository> remoteRepositories, ArtifactMetadataSource source, ArtifactFilter filter, List<ResolutionListener> listeners )
throws ArtifactResolutionException, ArtifactNotFoundException
{
return resolveTransitively( artifacts, originatingArtifact, managedVersions, localRepository, remoteRepositories, source, filter, listeners, null );
}
public ArtifactResolutionResult resolveTransitively( Set<Artifact> artifacts, Artifact originatingArtifact, Map managedVersions, ArtifactRepository localRepository,
List<ArtifactRepository> remoteRepositories, ArtifactMetadataSource source, ArtifactFilter filter, List<ResolutionListener> listeners,
List<ConflictResolver> conflictResolvers )
throws ArtifactResolutionException, ArtifactNotFoundException
{
ArtifactResolutionRequest request = new ArtifactResolutionRequest()
.setArtifact( originatingArtifact )
.setResolveRoot( false )
// This is required by the surefire plugin
.setArtifactDependencies( artifacts )
.setManagedVersionMap( managedVersions )
.setLocalRepository( localRepository )
.setRemoteRepositories( remoteRepositories )
.setCollectionFilter( filter )
.setListeners( listeners );
injectSession2( request, legacySupport.getSession() );
return resolveWithExceptions( request );
}
public ArtifactResolutionResult resolveWithExceptions( ArtifactResolutionRequest request )
throws ArtifactResolutionException, ArtifactNotFoundException
{
ArtifactResolutionResult result = resolve( request );
// We have collected all the problems so let's mimic the way the old code worked and just blow up right here.
// That's right lets just let it rip right here and send a big incomprehensible blob of text at unsuspecting
// users. Bad dog!
resolutionErrorHandler.throwErrors( request, result );
return result;
}
// ------------------------------------------------------------------------
//
// ------------------------------------------------------------------------
public ArtifactResolutionResult resolve( ArtifactResolutionRequest request )
{
Artifact rootArtifact = request.getArtifact();
Set<Artifact> artifacts = request.getArtifactDependencies();
Map managedVersions = request.getManagedVersionMap();
List<ResolutionListener> listeners = request.getListeners();
ArtifactFilter collectionFilter = request.getCollectionFilter();
ArtifactFilter resolutionFilter = request.getResolutionFilter();
RepositorySystemSession session = getSession( request.getLocalRepository() );
//TODO: hack because metadata isn't generated in m2e correctly and i want to run the maven i have in the workspace
if ( source == null )
{
try
{
source = container.lookup( ArtifactMetadataSource.class );
}
catch ( ComponentLookupException e )
{
// won't happen
}
}
if ( listeners == null )
{
listeners = new ArrayList<ResolutionListener>();
if ( logger.isDebugEnabled() )
{
listeners.add( new DebugResolutionListener( logger ) );
}
listeners.add( new WarningResolutionListener( logger ) );
}
ArtifactResolutionResult result = new ArtifactResolutionResult();
// The root artifact may, or may not be resolved so we need to check before we attempt to resolve.
// This is often an artifact like a POM that is taken from disk and we already have hold of the
// file reference. But this may be a Maven Plugin that we need to resolve from a remote repository
// as well as its dependencies.
if ( request.isResolveRoot() /* && rootArtifact.getFile() == null */ )
{
try
{
resolve( rootArtifact, request.getRemoteRepositories(), session );
}
catch ( ArtifactResolutionException e )
{
result.addErrorArtifactException( e );
return result;
}
catch ( ArtifactNotFoundException e )
{
result.addMissingArtifact( request.getArtifact() );
return result;
}
}
ArtifactResolutionRequest collectionRequest = request;
if ( request.isResolveTransitively() )
{
MetadataResolutionRequest metadataRequest = new DefaultMetadataResolutionRequest( request );
metadataRequest.setArtifact( rootArtifact );
metadataRequest.setResolveManagedVersions( managedVersions == null );
try
{
ResolutionGroup resolutionGroup = source.retrieve( metadataRequest );
if ( managedVersions == null )
{
managedVersions = resolutionGroup.getManagedVersions();
}
Set<Artifact> directArtifacts = resolutionGroup.getArtifacts();
if ( artifacts == null || artifacts.isEmpty() )
{
artifacts = directArtifacts;
}
else
{
List<Artifact> allArtifacts = new ArrayList<Artifact>();
allArtifacts.addAll( artifacts );
allArtifacts.addAll( directArtifacts );
Map<String, Artifact> mergedArtifacts = new LinkedHashMap<String, Artifact>();
for ( Artifact artifact : allArtifacts )
{
String conflictId = artifact.getDependencyConflictId();
if ( !mergedArtifacts.containsKey( conflictId ) )
{
mergedArtifacts.put( conflictId, artifact );
}
}
artifacts = new LinkedHashSet<Artifact>( mergedArtifacts.values() );
}
collectionRequest = new ArtifactResolutionRequest( request );
collectionRequest.setServers( request.getServers() );
collectionRequest.setMirrors( request.getMirrors() );
collectionRequest.setProxies( request.getProxies() );
collectionRequest.setRemoteRepositories( resolutionGroup.getResolutionRepositories() );
}
catch ( ArtifactMetadataRetrievalException e )
{
ArtifactResolutionException are =
new ArtifactResolutionException( "Unable to get dependency information for " + rootArtifact.getId()
+ ": " + e.getMessage(), rootArtifact, metadataRequest.getRemoteRepositories(), e );
result.addMetadataResolutionException( are );
return result;
}
}
if ( artifacts == null || artifacts.isEmpty() )
{
if ( request.isResolveRoot() )
{
result.addArtifact( rootArtifact );
}
return result;
}
// After the collection we will have the artifact object in the result but they will not be resolved yet.
result =
artifactCollector.collect( artifacts, rootArtifact, managedVersions, collectionRequest, source,
collectionFilter, listeners, null );
// We have metadata retrieval problems, or there are cycles that have been detected
// so we give this back to the calling code and let them deal with this information
// appropriately.
if ( result.hasMetadataResolutionExceptions() || result.hasVersionRangeViolations() || result.hasCircularDependencyExceptions() )
{
return result;
}
if ( result.getArtifactResolutionNodes() != null )
{
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
CountDownLatch latch = new CountDownLatch( result.getArtifactResolutionNodes().size() );
for ( ResolutionNode node : result.getArtifactResolutionNodes() )
{
Artifact artifact = node.getArtifact();
if ( resolutionFilter == null || resolutionFilter.include( artifact ) )
{
executor.execute( new ResolveTask( classLoader, latch, artifact, session,
node.getRemoteRepositories(), result ) );
}
else
{
latch.countDown();
}
}
try
{
latch.await();
}
catch ( InterruptedException e )
{
result.addErrorArtifactException( new ArtifactResolutionException( "Resolution interrupted",
rootArtifact, e ) );
}
}
// We want to send the root artifact back in the result but we need to do this after the other dependencies
// have been resolved.
if ( request.isResolveRoot() )
{
// Add the root artifact (as the first artifact to retain logical order of class path!)
Set<Artifact> allArtifacts = new LinkedHashSet<Artifact>();
allArtifacts.add( rootArtifact );
allArtifacts.addAll( result.getArtifacts() );
result.setArtifacts( allArtifacts );
}
return result;
}
public void resolve( Artifact artifact, List<ArtifactRepository> remoteRepositories, ArtifactRepository localRepository )
throws ArtifactResolutionException, ArtifactNotFoundException
{
resolve( artifact, remoteRepositories, localRepository, null );
}
public void customize(MavenWorkspaceMap workspaceMap, boolean failOnUnresolved) {
myWorkspaceMap = workspaceMap;
myUnresolvedCollector = new UnresolvedArtifactsCollector(failOnUnresolved);
}
public void reset() {
myWorkspaceMap = null;
myUnresolvedCollector = null;
}
public UnresolvedArtifactsCollector getUnresolvedCollector() {
return myUnresolvedCollector;
}
private boolean resolveAsModule(Artifact a) {
// method is called from different threads, so we have to copy the reference so ensure there is no race conditions.
MavenWorkspaceMap map = myWorkspaceMap;
if (map == null) return false;
MavenWorkspaceMap.Data resolved = map.findFileAndOriginalId(MavenModelConverter.createMavenId(a));
if (resolved == null) return false;
a.setResolved(true);
a.setFile(resolved.getFile(a.getType()));
a.selectVersion(resolved.originalId.getVersion());
return true;
}
/**
* ThreadCreator for creating daemon threads with fixed ThreadGroup-name.
*/
final static class DaemonThreadCreator
implements ThreadFactory
{
static final String THREADGROUP_NAME = "org.apache.maven.artifact.resolver.DefaultArtifactResolver";
final static ThreadGroup group = new ThreadGroup( THREADGROUP_NAME );
final static AtomicInteger threadNumber = new AtomicInteger( 1 );
public Thread newThread( Runnable r )
{
Thread newThread = new Thread( group, r, "resolver-" + threadNumber.getAndIncrement() );
newThread.setDaemon( true );
return newThread;
}
}
private class ResolveTask
implements Runnable
{
private final ClassLoader classLoader;
private final CountDownLatch latch;
private final Artifact artifact;
private final RepositorySystemSession session;
private final List<ArtifactRepository> remoteRepositories;
private final ArtifactResolutionResult result;
public ResolveTask( ClassLoader classLoader, CountDownLatch latch, Artifact artifact, RepositorySystemSession session,
List<ArtifactRepository> remoteRepositories, ArtifactResolutionResult result )
{
this.classLoader = classLoader;
this.latch = latch;
this.artifact = artifact;
this.session = session;
this.remoteRepositories = remoteRepositories;
this.result = result;
}
public void run()
{
try
{
Thread.currentThread().setContextClassLoader( classLoader );
resolve( artifact, remoteRepositories, session );
}
catch ( ArtifactNotFoundException anfe )
{
// These are cases where the artifact just isn't present in any of the remote repositories
// because it wasn't deployed, or it was deployed in the wrong place.
synchronized ( result )
{
result.addMissingArtifact( artifact );
}
}
catch ( ArtifactResolutionException e )
{
// This is really a wagon TransferFailedException so something went wrong after we successfully
// retrieved the metadata.
synchronized ( result )
{
result.addErrorArtifactException( e );
}
}
finally
{
latch.countDown();
}
}
}
}