package org.jfrog.build.extractor.maven.resolver; import org.apache.maven.artifact.DefaultArtifact; import org.apache.maven.plugin.internal.DefaultPluginDependenciesResolver; import org.apache.maven.project.DefaultProjectDependenciesResolver; import org.apache.maven.repository.internal.DefaultArtifactDescriptorReader; import org.codehaus.plexus.PlexusConstants; 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.context.Context; import org.codehaus.plexus.context.ContextException; import org.codehaus.plexus.logging.Logger; import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable; import org.eclipse.aether.AbstractRepositoryListener; import org.eclipse.aether.RepositoryEvent; import org.eclipse.aether.RepositoryListener; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.internal.impl.DefaultRepositorySystem; import org.eclipse.aether.metadata.Metadata; import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.repository.RepositoryPolicy; import org.eclipse.aether.resolution.ArtifactRequest; import org.jfrog.build.extractor.maven.BuildInfoRecorder; import java.lang.reflect.Field; import java.util.Properties; /** * Repository listener when running in Maven 3.1.x * The listener is used for updating the BuildInfoRecorder with each resolved artifact. * * @author Shay Yaakov */ @Component(role = RepositoryListener.class) public class ArtifactoryEclipseRepositoryListener extends AbstractRepositoryListener implements Contextualizable { @Requirement private Logger logger; @Requirement private DefaultProjectDependenciesResolver pojectDependenciesResolver; @Requirement private DefaultPluginDependenciesResolver pluginDependenciesResolver; @Requirement private DefaultArtifactDescriptorReader descriptorReader; @Requirement private DefaultRepositorySystem repositorySystem; private BuildInfoRecorder buildInfoRecorder = null; private PlexusContainer plexusContainer; Boolean artifactoryRepositoriesEnforced = false; private ArtifactoryEclipseArtifactResolver artifactResolver = null; private ArtifactoryEclipseMetadataResolver metadataResolver = null; /** * The method replaces the DefaultArtifactResolver instance with an instance of ArtifactoryEclipseArtifactResolver. * The new class sets the configured Artifactory resolution repositories for each resolved artifact. * * @throws ComponentLookupException */ private void enforceArtifactoryResolver() throws ComponentLookupException, NoSuchFieldException, IllegalAccessException { logger.debug("Enforcing Artifactory artifact resolver"); artifactResolver = (ArtifactoryEclipseArtifactResolver)plexusContainer.lookup(ArtifactoryEclipseArtifactResolver.class.getName()); metadataResolver = (ArtifactoryEclipseMetadataResolver)plexusContainer.lookup(ArtifactoryEclipseMetadataResolver.class.getName()); buildInfoRecorder = (BuildInfoRecorder)plexusContainer.lookup(BuildInfoRecorder.class.getName()); descriptorReader.setArtifactResolver(artifactResolver); repositorySystem.setArtifactResolver(artifactResolver); repositorySystem.setMetadataResolver(metadataResolver); Field repoSystemProjectField = pojectDependenciesResolver.getClass().getDeclaredField("repoSystem"); repoSystemProjectField.setAccessible(true); repoSystemProjectField.set(pojectDependenciesResolver, repositorySystem); Field repoSystemPluginField = pluginDependenciesResolver.getClass().getDeclaredField("repoSystem"); repoSystemPluginField.setAccessible(true); repoSystemPluginField.set(pluginDependenciesResolver, repositorySystem); artifactoryRepositoriesEnforced = true; synchronized (artifactoryRepositoriesEnforced) { artifactoryRepositoriesEnforced.notifyAll(); } } private BuildInfoRecorder getBuildInfoRecorder() { return buildInfoRecorder; } @Override public void metadataDownloading(RepositoryEvent event) { verifyArtifactoryResolutionEnforced(event); } @Override public void artifactDownloading(RepositoryEvent event) { verifyArtifactoryResolutionEnforced(event); } private void waitForResolutionToBeSet() { // In case the Artifactory resolver is not yet set, we wait for it first: if (!artifactoryRepositoriesEnforced) { synchronized (artifactoryRepositoriesEnforced) { if (!artifactoryRepositoriesEnforced) { try { artifactoryRepositoriesEnforced.wait(); } catch (InterruptedException e) { logger.error("Failed while waiting for Artifactory repositories enforcement", e); } } } } } /** * The enforceArtifactoryResolver() method replaces the default artifact resolver instance with a resolver that enforces Artifactory * resolution repositories. However, since there's a chance that Maven started resolving a few artifacts before the instance replacement, * this method makes sure those artifacts will be resolved from Artifactory as well. * @param event */ private void verifyArtifactoryResolutionEnforced(RepositoryEvent event) { initResolutionHelper(event.getSession()); if (!getBuildInfoRecorder().getResolutionHelper().resolutionRepositoriesConfigured()) { return; } if (event.getArtifact() == null && event.getMetadata() == null) { return; } if (!(event.getRepository() instanceof RemoteRepository)) { return; } RemoteRepository repo = (RemoteRepository)event.getRepository(); waitForResolutionToBeSet(); // Now that the resolver enforcement is done, we make sure that the Artifactory resolution repositories in the resolver are initialized: artifactResolver.initResolutionRepositories(event.getSession()); // Take the Artifactory resolution repositories from the Artifactory resolver: RemoteRepository artifactorySnapshotRepo; RemoteRepository artifactoryReleaseRepo; boolean snapshot; if (event.getArtifact() != null) { artifactorySnapshotRepo = artifactResolver.getSnapshotRepository(event.getSession()); artifactoryReleaseRepo = artifactResolver.getReleaseRepository(event.getSession()); snapshot = event.getArtifact().isSnapshot(); } else { artifactorySnapshotRepo = metadataResolver.getSnapshotRepository(event.getSession()); artifactoryReleaseRepo = metadataResolver.getReleaseRepository(event.getSession()); snapshot = event.getMetadata().getNature() == Metadata.Nature.SNAPSHOT; } // If the artifact about to be downloaded was not handled by the Artifactory resolution resolver, but by the default resolver (before // it had been replaced), modify the repository URL: try { if (snapshot && !repo.getUrl().equals(artifactorySnapshotRepo.getUrl())) { logger.debug("Replacing resolution repository URL: " + repo + " with: " + artifactorySnapshotRepo.getUrl()); copyRepositoryFields(artifactorySnapshotRepo, repo); setRepositoryPolicy(repo); } else if (!snapshot && !repo.getUrl().equals(artifactoryReleaseRepo.getUrl())) { logger.debug("Replacing resolution repository URL: " + repo + " with: " + artifactoryReleaseRepo.getUrl()); copyRepositoryFields(artifactoryReleaseRepo, repo); setRepositoryPolicy(repo); } } catch (Exception e) { logger.error("Failed while replacing resolution repository URL", e); } } private void initResolutionHelper(RepositorySystemSession session) { ResolutionHelper helper = getBuildInfoRecorder().getResolutionHelper(); if (helper.isInitialized()) { return; } Properties allMavenProps = new Properties(); allMavenProps.putAll(session.getSystemProperties()); allMavenProps.putAll(session.getUserProperties()); helper.init(allMavenProps); } private void copyRepositoryFields(RemoteRepository fromRepo, RemoteRepository toRepo) throws IllegalAccessException, NoSuchFieldException { Field url = RemoteRepository.class.getDeclaredField("url"); url.setAccessible(true); url.set(toRepo, fromRepo.getUrl()); if (fromRepo.getAuthentication() != null) { Field authentication = RemoteRepository.class.getDeclaredField("authentication"); authentication.setAccessible(true); authentication.set(toRepo, fromRepo.getAuthentication()); } if (fromRepo.getProxy() != null) { Field proxy = RemoteRepository.class.getDeclaredField("proxy"); proxy.setAccessible(true); proxy.set(toRepo, fromRepo.getProxy()); } } /** * Enables both snapshot and release polocies for a repository */ private void setRepositoryPolicy(RemoteRepository repo) throws NoSuchFieldException, IllegalAccessException { RepositoryPolicy policy = new RepositoryPolicy(true, RepositoryPolicy.UPDATE_POLICY_DAILY, RepositoryPolicy.CHECKSUM_POLICY_WARN); Field releasePolicyField = RemoteRepository.class.getDeclaredField("releasePolicy"); Field snapshotPolicyField = RemoteRepository.class.getDeclaredField("snapshotPolicy"); releasePolicyField.setAccessible(true); snapshotPolicyField.setAccessible(true); releasePolicyField.set(repo, policy); snapshotPolicyField.set(repo, policy); } /** * Intercepts resolved artifacts and updates the BuildInfoRecorder, so that build-info includes all resolved artifacts. */ @Override public void artifactResolved(RepositoryEvent event) { waitForResolutionToBeSet(); String requestContext = ((ArtifactRequest)event.getTrace().getData()).getRequestContext(); String scope = getBuildInfoRecorder().getResolutionHelper().getScopeByRequestContext(requestContext); org.apache.maven.artifact.Artifact artifact = toMavenArtifact(event.getArtifact(), scope); if (event.getRepository() != null) { logger.debug("[buildinfo] Resolved artifact: " + artifact + " from: " + event.getRepository() + " Context is: " + requestContext); getBuildInfoRecorder().artifactResolved(artifact); } else { logger.debug("[buildinfo] Could not resolve artifact: " + artifact); } super.artifactResolved(event); } /** * Converts org.eclipse.aether.artifact.Artifact objects into org.apache.maven.artifact.Artifact objects. */ private org.apache.maven.artifact.Artifact toMavenArtifact(final org.eclipse.aether.artifact.Artifact art, String scope) { if (art == null) { return null; } String classifier = art.getClassifier(); classifier = classifier == null ? "" : classifier; DefaultArtifact artifact = new DefaultArtifact(art.getGroupId(), art.getArtifactId(), art.getVersion(), scope, art.getExtension(), classifier, null); artifact.setFile(art.getFile()); return artifact; } @Override public void contextualize(Context context) throws ContextException { plexusContainer = (PlexusContainer)context.get(PlexusConstants.PLEXUS_KEY); try { enforceArtifactoryResolver(); } catch (Exception e) { logger.error("Failed while enforcing Artifactory artifact resolver", e); } } }