package org.eclipse.che.maven; /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ 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.AbstractArtifactResolutionException; import org.apache.maven.artifact.resolver.ArtifactCollector; import org.apache.maven.artifact.resolver.ArtifactNotFoundException; import org.apache.maven.artifact.resolver.ArtifactResolutionException; import org.apache.maven.artifact.resolver.ArtifactResolutionRequest; import org.apache.maven.artifact.resolver.ArtifactResolutionResult; import org.apache.maven.artifact.resolver.ArtifactResolver; import org.apache.maven.artifact.resolver.DebugResolutionListener; import org.apache.maven.artifact.resolver.ResolutionErrorHandler; import org.apache.maven.artifact.resolver.ResolutionListener; import org.apache.maven.artifact.resolver.ResolutionNode; import org.apache.maven.artifact.resolver.WarningResolutionListener; 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.codehaus.plexus.personality.plexus.lifecycle.phase.Disposable; import org.eclipse.aether.RepositorySystem; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.repository.LocalRepositoryManager; import org.eclipse.aether.resolution.ArtifactRequest; import org.eclipse.aether.resolution.ArtifactResult; import org.eclipse.che.maven.data.MavenKey; import org.eclipse.che.maven.data.MavenWorkspaceCache; import org.eclipse.che.maven.data.MavenWorkspaceCache.Entry; import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Matcher; /** * Copied from org.apache.maven.artifact.resolver.DefaultArtifactResolver * changed method {@link CheArtifactResolver#resolve(Artifact, List, RepositorySystemSession)}, it's avoid resolving artifacts provided by * maven workspace. * * @author Evgen Vidolob */ @Component(role = ArtifactResolver.class, hint = "che") public class CheArtifactResolver implements ArtifactResolver, Disposable { @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 MavenWorkspaceCache workspaceCache; private boolean failOnUnresolvedDependency; public CheArtifactResolver() { int threads = Integer.getInteger("maven.artifact.threads", 5); 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()); } } 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(isModule(artifact)){ return; } try { resolveOrig(artifact, remoteRepositories, session); } catch (AbstractArtifactResolutionException e){ if(!failOnUnresolvedDependency){ artifact.setResolved(true); } } } private boolean isModule(Artifact artifact){ MavenWorkspaceCache cache = workspaceCache; if(cache == null){ return false; } Entry entry = cache.findEntry(new MavenKey(artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion())); if(entry == null){ return false; } artifact.setResolved(true); artifact.setFile(entry.getFile(artifact.getType())); artifact.selectVersion(entry.getKey().getVersion()); return true; } private void resolveOrig(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.eclipse.aether.resolution.ArtifactResolutionException e) { if (e.getCause() instanceof org.eclipse.aether.transfer.ArtifactNotFoundException) { throw new ArtifactNotFoundException(e.getMessage(), artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion(), artifact.getType(), artifact.getClassifier(), remoteRepositories, artifact.getDownloadUrl(), artifact.getDependencyTrail(), 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<String, Artifact> 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); } /** * ThreadCreator for creating daemon threads with fixed ThreadGroup-name. */ static final class DaemonThreadCreator implements ThreadFactory { static final String THREADGROUP_NAME = "org.apache.maven.artifact.resolver.DefaultArtifactResolver"; static final ThreadGroup GROUP = new ThreadGroup(THREADGROUP_NAME); static final AtomicInteger THREAD_NUMBER = new AtomicInteger(1); public Thread newThread(Runnable r) { Thread newThread = new Thread(GROUP, r, "resolver-" + THREAD_NUMBER.getAndIncrement()); newThread.setDaemon(true); newThread.setContextClassLoader(null); 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() { ClassLoader old = Thread.currentThread().getContextClassLoader(); 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(); Thread.currentThread().setContextClassLoader(old); } } } @Override public void dispose() { if (executor instanceof ExecutorService) { ((ExecutorService)executor).shutdownNow(); } } public void setWorkspaceCache(MavenWorkspaceCache cache, boolean failOnUnresolvedDependency) { workspaceCache = cache; this.failOnUnresolvedDependency = failOnUnresolvedDependency; } public void reset() { workspaceCache = null; } }