package org.apache.archiva.dependency.tree.maven2; /* * 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.archiva.admin.model.RepositoryAdminException; import org.apache.archiva.admin.model.beans.ManagedRepository; import org.apache.archiva.admin.model.beans.NetworkProxy; import org.apache.archiva.admin.model.beans.ProxyConnector; import org.apache.archiva.admin.model.beans.RemoteRepository; import org.apache.archiva.admin.model.managed.ManagedRepositoryAdmin; import org.apache.archiva.admin.model.networkproxy.NetworkProxyAdmin; import org.apache.archiva.admin.model.proxyconnector.ProxyConnectorAdmin; import org.apache.archiva.admin.model.remote.RemoteRepositoryAdmin; import org.apache.archiva.common.plexusbridge.PlexusSisuBridge; import org.apache.archiva.common.plexusbridge.PlexusSisuBridgeException; import org.apache.archiva.common.utils.VersionUtil; import org.apache.archiva.maven2.metadata.MavenMetadataReader; import org.apache.archiva.maven2.model.TreeEntry; import org.apache.archiva.metadata.repository.storage.RepositoryPathTranslator; import org.apache.archiva.model.ArchivaRepositoryMetadata; import org.apache.archiva.proxy.common.WagonFactory; import org.apache.archiva.repository.metadata.MetadataTools; import org.apache.archiva.xml.XMLException; import org.apache.commons.lang.StringUtils; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.factory.ArtifactFactory; import org.apache.maven.model.building.DefaultModelBuilderFactory; import org.apache.maven.model.building.ModelBuilder; import org.apache.maven.repository.internal.DefaultArtifactDescriptorReader; import org.apache.maven.repository.internal.DefaultVersionRangeResolver; import org.apache.maven.repository.internal.DefaultVersionResolver; import org.apache.maven.repository.internal.MavenRepositorySystemSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonatype.aether.RepositorySystem; import org.sonatype.aether.RepositorySystemSession; import org.sonatype.aether.collection.CollectRequest; import org.sonatype.aether.collection.CollectResult; import org.sonatype.aether.collection.DependencyCollectionException; import org.sonatype.aether.collection.DependencySelector; import org.sonatype.aether.graph.Dependency; import org.sonatype.aether.graph.DependencyVisitor; import org.sonatype.aether.impl.ArtifactDescriptorReader; import org.sonatype.aether.impl.VersionRangeResolver; import org.sonatype.aether.impl.VersionResolver; import org.sonatype.aether.impl.internal.DefaultServiceLocator; import org.sonatype.aether.impl.internal.SimpleLocalRepositoryManager; import org.sonatype.aether.repository.LocalRepository; import org.sonatype.aether.spi.connector.RepositoryConnectorFactory; import org.sonatype.aether.util.artifact.DefaultArtifact; import org.sonatype.aether.util.graph.selector.AndDependencySelector; import org.sonatype.aether.util.graph.selector.ExclusionDependencySelector; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import javax.inject.Inject; import javax.inject.Named; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @author Olivier Lamy * @since 1.4-M3 */ @Service("dependencyTreeBuilder#maven3") public class Maven3DependencyTreeBuilder implements DependencyTreeBuilder { private Logger log = LoggerFactory.getLogger( getClass() ); @Inject private PlexusSisuBridge plexusSisuBridge; @Inject @Named( "repositoryPathTranslator#maven2" ) private RepositoryPathTranslator pathTranslator; @Inject private WagonFactory wagonFactory; @Inject private ManagedRepositoryAdmin managedRepositoryAdmin; @Inject private ProxyConnectorAdmin proxyConnectorAdmin; @Inject private NetworkProxyAdmin networkProxyAdmin; @Inject private RemoteRepositoryAdmin remoteRepositoryAdmin; private ArtifactFactory factory; private ModelBuilder builder; @PostConstruct public void initialize() throws PlexusSisuBridgeException { factory = plexusSisuBridge.lookup( ArtifactFactory.class, "default" ); DefaultModelBuilderFactory defaultModelBuilderFactory = new DefaultModelBuilderFactory(); builder = defaultModelBuilderFactory.newInstance(); } @Override public void buildDependencyTree( List<String> repositoryIds, String groupId, String artifactId, String version, DependencyVisitor dependencyVisitor ) throws DependencyTreeBuilderException { Artifact projectArtifact = factory.createProjectArtifact( groupId, artifactId, version ); ManagedRepository repository = null; try { repository = findArtifactInRepositories( repositoryIds, projectArtifact ); } catch ( RepositoryAdminException e ) { // FIXME better exception throw new DependencyTreeBuilderException( "Cannot build project dependency tree " + e.getMessage(), e ); } if ( repository == null ) { // metadata could not be resolved return; } List<RemoteRepository> remoteRepositories = new ArrayList<>(); Map<String, NetworkProxy> networkProxies = new HashMap<>(); try { // MRM-1411 // TODO: this is a workaround for a lack of proxy capability in the resolvers - replace when it can all be // handled there. It doesn't cache anything locally! Map<String, List<ProxyConnector>> proxyConnectorsMap = proxyConnectorAdmin.getProxyConnectorAsMap(); List<ProxyConnector> proxyConnectors = proxyConnectorsMap.get( repository.getId() ); if ( proxyConnectors != null ) { for ( ProxyConnector proxyConnector : proxyConnectors ) { remoteRepositories.add( remoteRepositoryAdmin.getRemoteRepository( proxyConnector.getTargetRepoId() ) ); NetworkProxy networkProxyConfig = networkProxyAdmin.getNetworkProxy( proxyConnector.getProxyId() ); if ( networkProxyConfig != null ) { // key/value: remote repo ID/proxy info networkProxies.put( proxyConnector.getTargetRepoId(), networkProxyConfig ); } } } } catch ( RepositoryAdminException e ) { throw new DependencyTreeBuilderException( e.getMessage(), e ); } // FIXME take care of relative path ResolveRequest resolveRequest = new ResolveRequest(); resolveRequest.dependencyVisitor = dependencyVisitor; resolveRequest.localRepoDir = repository.getLocation(); resolveRequest.groupId = groupId; resolveRequest.artifactId = artifactId; resolveRequest.version = version; resolveRequest.remoteRepositories = remoteRepositories; resolveRequest.networkProxies = networkProxies; resolve( resolveRequest ); } @Override public List<TreeEntry> buildDependencyTree( List<String> repositoryIds, String groupId, String artifactId, String version ) throws DependencyTreeBuilderException { List<TreeEntry> treeEntries = new ArrayList<>(); TreeDependencyNodeVisitor treeDependencyNodeVisitor = new TreeDependencyNodeVisitor( treeEntries ); buildDependencyTree( repositoryIds, groupId, artifactId, version, treeDependencyNodeVisitor ); log.debug( "treeEntrie: {}", treeEntries ); return treeEntries; } private static class ResolveRequest { String localRepoDir, groupId, artifactId, version; DependencyVisitor dependencyVisitor; List<RemoteRepository> remoteRepositories; Map<String, NetworkProxy> networkProxies; } private void resolve( ResolveRequest resolveRequest ) { RepositorySystem system = newRepositorySystem(); RepositorySystemSession session = newRepositorySystemSession( system, resolveRequest.localRepoDir ); org.sonatype.aether.artifact.Artifact artifact = new DefaultArtifact( resolveRequest.groupId + ":" + resolveRequest.artifactId + ":" + resolveRequest.version ); CollectRequest collectRequest = new CollectRequest(); collectRequest.setRoot( new Dependency( artifact, "" ) ); // add remote repositories for ( RemoteRepository remoteRepository : resolveRequest.remoteRepositories ) { collectRequest.addRepository( new org.sonatype.aether.repository.RemoteRepository( remoteRepository.getId(), "default", remoteRepository.getUrl() ) ); } collectRequest.setRequestContext( "project" ); //collectRequest.addRepository( repo ); try { CollectResult collectResult = system.collectDependencies( session, collectRequest ); collectResult.getRoot().accept( resolveRequest.dependencyVisitor ); log.debug( "test" ); } catch ( DependencyCollectionException e ) { log.error( e.getMessage(), e ); } } private RepositorySystem newRepositorySystem() { DefaultServiceLocator locator = new DefaultServiceLocator(); locator.addService( RepositoryConnectorFactory.class, ArchivaRepositoryConnectorFactory.class );// FileRepositoryConnectorFactory.class ); locator.addService( VersionResolver.class, DefaultVersionResolver.class ); locator.addService( VersionRangeResolver.class, DefaultVersionRangeResolver.class ); locator.addService( ArtifactDescriptorReader.class, DefaultArtifactDescriptorReader.class ); //locator.addService( RepositoryConnectorFactory.class, WagonRepositoryConnectorFactory.class ); //locator.setServices( WagonProvider.class, ); return locator.getService( RepositorySystem.class ); } private RepositorySystemSession newRepositorySystemSession( RepositorySystem system, String localRepoDir ) { MavenRepositorySystemSession session = new MavenRepositorySystemSession(); DependencySelector depFilter = new AndDependencySelector( new ExclusionDependencySelector() ); session.setDependencySelector( depFilter ); session.setLocalRepositoryManager( new SimpleLocalRepositoryManager( localRepoDir ) ); return session; } private ManagedRepository findArtifactInRepositories( List<String> repositoryIds, Artifact projectArtifact ) throws RepositoryAdminException { for ( String repoId : repositoryIds ) { ManagedRepository managedRepository = managedRepositoryAdmin.getManagedRepository( repoId ); File repoDir = new File( managedRepository.getLocation() ); File file = pathTranslator.toFile( repoDir, projectArtifact.getGroupId(), projectArtifact.getArtifactId(), projectArtifact.getBaseVersion(), projectArtifact.getArtifactId() + "-" + projectArtifact.getVersion() + ".pom" ); if ( file.exists() ) { return managedRepository; } // try with snapshot version if ( StringUtils.endsWith( projectArtifact.getBaseVersion(), VersionUtil.SNAPSHOT ) ) { File metadataFile = new File( file.getParent(), MetadataTools.MAVEN_METADATA ); if ( metadataFile.exists() ) { try { ArchivaRepositoryMetadata archivaRepositoryMetadata = MavenMetadataReader.read( metadataFile ); int buildNumber = archivaRepositoryMetadata.getSnapshotVersion().getBuildNumber(); String timeStamp = archivaRepositoryMetadata.getSnapshotVersion().getTimestamp(); // rebuild file name with timestamped version and build number String timeStampFileName = new StringBuilder( projectArtifact.getArtifactId() ).append( '-' ).append( StringUtils.remove( projectArtifact.getBaseVersion(), "-" + VersionUtil.SNAPSHOT ) ).append( '-' ).append( timeStamp ).append( '-' ).append( Integer.toString( buildNumber ) ).append( ".pom" ).toString(); File timeStampFile = new File( file.getParent(), timeStampFileName ); log.debug( "try to find timestamped snapshot version file: {}", timeStampFile.getPath() ); if ( timeStampFile.exists() ) { return managedRepository; } } catch ( XMLException e ) { log.warn( "skip fail to find timestamped snapshot pom: {}", e.getMessage() ); } } } } return null; } }