001package org.apache.archiva.dependency.tree.maven2; 002/* 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, 014 * software distributed under the License is distributed on an 015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 016 * KIND, either express or implied. See the License for the 017 * specific language governing permissions and limitations 018 * under the License. 019 */ 020 021 022import org.apache.archiva.admin.model.RepositoryAdminException; 023import org.apache.archiva.admin.model.beans.NetworkProxy; 024import org.apache.archiva.admin.model.beans.ProxyConnector; 025import org.apache.archiva.admin.model.networkproxy.NetworkProxyAdmin; 026import org.apache.archiva.admin.model.proxyconnector.ProxyConnectorAdmin; 027import org.apache.archiva.common.plexusbridge.PlexusSisuBridge; 028import org.apache.archiva.common.plexusbridge.PlexusSisuBridgeException; 029import org.apache.archiva.common.utils.VersionUtil; 030import org.apache.archiva.maven2.metadata.MavenMetadataReader; 031import org.apache.archiva.maven2.model.TreeEntry; 032import org.apache.archiva.metadata.repository.storage.RepositoryPathTranslator; 033import org.apache.archiva.model.ArchivaRepositoryMetadata; 034import org.apache.archiva.repository.ManagedRepository; 035import org.apache.archiva.repository.RemoteRepository; 036import org.apache.archiva.repository.RepositoryRegistry; 037import org.apache.archiva.repository.maven2.MavenSystemManager; 038import org.apache.archiva.repository.metadata.base.MetadataTools; 039import org.apache.archiva.repository.storage.StorageAsset; 040import org.apache.archiva.xml.XMLException; 041import org.apache.commons.lang3.StringUtils; 042import org.apache.maven.artifact.Artifact; 043import org.apache.maven.bridge.MavenRepositorySystem; 044import org.eclipse.aether.RepositorySystem; 045import org.eclipse.aether.RepositorySystemSession; 046import org.eclipse.aether.artifact.DefaultArtifact; 047import org.eclipse.aether.collection.CollectRequest; 048import org.eclipse.aether.collection.CollectResult; 049import org.eclipse.aether.collection.DependencyCollectionException; 050import org.eclipse.aether.graph.Dependency; 051import org.eclipse.aether.graph.DependencyVisitor; 052import org.slf4j.Logger; 053import org.slf4j.LoggerFactory; 054import org.springframework.stereotype.Service; 055 056import javax.annotation.PostConstruct; 057import javax.inject.Inject; 058import javax.inject.Named; 059import java.io.IOException; 060import java.util.ArrayList; 061import java.util.HashMap; 062import java.util.List; 063import java.util.Map; 064 065/** 066 * @author Olivier Lamy 067 * @since 1.4-M3 068 */ 069@Service("dependencyTreeBuilder#maven3") 070public class Maven3DependencyTreeBuilder 071 implements DependencyTreeBuilder 072{ 073 private Logger log = LoggerFactory.getLogger( Maven3DependencyTreeBuilder.class ); 074 075 @Inject 076 private PlexusSisuBridge plexusSisuBridge; 077 078 private MavenRepositorySystem mavenRepositorySystem; 079 080 @Inject 081 @Named( "repositoryPathTranslator#maven2" ) 082 private RepositoryPathTranslator pathTranslator; 083 084 @Inject 085 private ProxyConnectorAdmin proxyConnectorAdmin; 086 087 @Inject 088 private NetworkProxyAdmin networkProxyAdmin; 089 090 @Inject 091 RepositoryRegistry repositoryRegistry; 092 093 @Inject 094 MavenSystemManager mavenSystemManager; 095 096 097 @PostConstruct 098 public void initialize() 099 throws PlexusSisuBridgeException 100 { 101 mavenRepositorySystem = plexusSisuBridge.lookup(MavenRepositorySystem.class); 102 } 103 104 105 106 public void buildDependencyTree( List<String> repositoryIds, String groupId, String artifactId, String version, 107 DependencyVisitor dependencyVisitor ) 108 throws DependencyTreeBuilderException 109 { 110 111 Artifact projectArtifact = mavenRepositorySystem.createProjectArtifact(groupId, artifactId, version); 112 ManagedRepository repository = findArtifactInRepositories( repositoryIds, projectArtifact ); 113 114 if ( repository == null ) 115 { 116 // metadata could not be resolved 117 log.info("Did not find repository with artifact {}/{}/{}", groupId, artifactId, version); 118 return; 119 } 120 121 List<org.apache.archiva.repository.RemoteRepository> remoteRepositories = new ArrayList<>(); 122 Map<String, NetworkProxy> networkProxies = new HashMap<>(); 123 124 try 125 { 126 // MRM-1411 127 // TODO: this is a workaround for a lack of proxy capability in the resolvers - replace when it can all be 128 // handled there. It doesn't cache anything locally! 129 130 Map<String, List<ProxyConnector>> proxyConnectorsMap = proxyConnectorAdmin.getProxyConnectorAsMap(); 131 List<ProxyConnector> proxyConnectors = proxyConnectorsMap.get( repository.getId() ); 132 if ( proxyConnectors != null ) 133 { 134 for ( ProxyConnector proxyConnector : proxyConnectors ) 135 { 136 remoteRepositories.add( 137 repositoryRegistry.getRemoteRepository( proxyConnector.getTargetRepoId() ) ); 138 139 NetworkProxy networkProxyConfig = networkProxyAdmin.getNetworkProxy( proxyConnector.getProxyId() ); 140 141 if ( networkProxyConfig != null ) 142 { 143 // key/value: remote repo ID/proxy info 144 networkProxies.put( proxyConnector.getTargetRepoId(), networkProxyConfig ); 145 } 146 } 147 } 148 } 149 catch ( RepositoryAdminException e ) 150 { 151 throw new DependencyTreeBuilderException( e.getMessage(), e ); 152 } 153 154 // FIXME take care of relative path 155 ResolveRequest resolveRequest = new ResolveRequest(); 156 resolveRequest.dependencyVisitor = dependencyVisitor; 157 resolveRequest.localRepoDir = repository.getContent().getRepoRoot(); 158 resolveRequest.groupId = groupId; 159 resolveRequest.artifactId = artifactId; 160 resolveRequest.version = version; 161 resolveRequest.remoteRepositories = remoteRepositories; 162 resolveRequest.networkProxies = networkProxies; 163 resolve( resolveRequest ); 164 } 165 166 167 @Override 168 public List<TreeEntry> buildDependencyTree( List<String> repositoryIds, String groupId, String artifactId, 169 String version ) 170 throws DependencyTreeBuilderException 171 { 172 173 List<TreeEntry> treeEntries = new ArrayList<>(); 174 TreeDependencyNodeVisitor treeDependencyNodeVisitor = new TreeDependencyNodeVisitor( treeEntries ); 175 176 buildDependencyTree( repositoryIds, groupId, artifactId, version, treeDependencyNodeVisitor ); 177 178 log.debug( "treeEntries: {}", treeEntries ); 179 return treeEntries; 180 } 181 182 private static class ResolveRequest 183 { 184 String localRepoDir, groupId, artifactId, version; 185 186 DependencyVisitor dependencyVisitor; 187 188 List<org.apache.archiva.repository.RemoteRepository> remoteRepositories; 189 190 Map<String, NetworkProxy> networkProxies; 191 192 } 193 194 195 private void resolve( ResolveRequest resolveRequest ) 196 { 197 198 RepositorySystem system = mavenSystemManager.getRepositorySystem(); 199 RepositorySystemSession session = MavenSystemManager.newRepositorySystemSession( resolveRequest.localRepoDir ); 200 201 org.eclipse.aether.artifact.Artifact artifact = new DefaultArtifact( 202 resolveRequest.groupId + ":" + resolveRequest.artifactId + ":" + resolveRequest.version ); 203 204 CollectRequest collectRequest = new CollectRequest(); 205 collectRequest.setRoot( new Dependency( artifact, "" ) ); 206 207 // add remote repositories 208 for ( RemoteRepository remoteRepository : resolveRequest.remoteRepositories ) 209 { 210 org.eclipse.aether.repository.RemoteRepository repo = new org.eclipse.aether.repository.RemoteRepository.Builder( remoteRepository.getId( ), "default", remoteRepository.getLocation( ).toString() ).build( ); 211 collectRequest.addRepository(repo); 212 } 213 collectRequest.setRequestContext( "project" ); 214 215 //collectRequest.addRepository( repo ); 216 217 try 218 { 219 CollectResult collectResult = system.collectDependencies( session, collectRequest ); 220 collectResult.getRoot().accept( resolveRequest.dependencyVisitor ); 221 log.debug("Collected dependency results for resolve"); 222 } 223 catch ( DependencyCollectionException e ) 224 { 225 log.error( "Error while collecting dependencies (resolve): {}", e.getMessage(), e ); 226 } 227 228 229 230 } 231 232 private ManagedRepository findArtifactInRepositories( List<String> repositoryIds, Artifact projectArtifact ) { 233 for ( String repoId : repositoryIds ) 234 { 235 ManagedRepository managedRepo = repositoryRegistry.getManagedRepository(repoId); 236 StorageAsset repoDir = managedRepo.getAsset(""); 237 238 StorageAsset file = pathTranslator.toFile( repoDir, projectArtifact.getGroupId(), projectArtifact.getArtifactId(), 239 projectArtifact.getBaseVersion(), 240 projectArtifact.getArtifactId() + "-" + projectArtifact.getVersion() 241 + ".pom" ); 242 243 if ( file.exists() ) 244 { 245 return managedRepo; 246 } 247 // try with snapshot version 248 if ( StringUtils.endsWith( projectArtifact.getBaseVersion(), VersionUtil.SNAPSHOT ) ) 249 { 250 StorageAsset metadataFile = file.getParent().resolve( MetadataTools.MAVEN_METADATA ); 251 if ( metadataFile.exists() ) 252 { 253 try 254 { 255 ArchivaRepositoryMetadata archivaRepositoryMetadata = MavenMetadataReader.read( metadataFile); 256 int buildNumber = archivaRepositoryMetadata.getSnapshotVersion().getBuildNumber(); 257 String timeStamp = archivaRepositoryMetadata.getSnapshotVersion().getTimestamp(); 258 // rebuild file name with timestamped version and build number 259 String timeStampFileName = 260 new StringBuilder( projectArtifact.getArtifactId() ).append( '-' ).append( 261 StringUtils.remove( projectArtifact.getBaseVersion(), 262 "-" + VersionUtil.SNAPSHOT ) ).append( '-' ).append( 263 timeStamp ).append( '-' ).append( Integer.toString( buildNumber ) ).append( 264 ".pom" ).toString(); 265 StorageAsset timeStampFile = file.getParent().resolve( timeStampFileName ); 266 log.debug( "try to find timestamped snapshot version file: {}", timeStampFile); 267 if ( timeStampFile.exists() ) 268 { 269 return managedRepo; 270 } 271 } 272 catch (XMLException | IOException e ) 273 { 274 log.warn( "skip fail to find timestamped snapshot pom: {}", e.getMessage() ); 275 } 276 } 277 } 278 } 279 return null; 280 } 281 282}