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.ManagedRepository; 024import org.apache.archiva.admin.model.beans.NetworkProxy; 025import org.apache.archiva.admin.model.beans.ProxyConnector; 026import org.apache.archiva.admin.model.beans.RemoteRepository; 027import org.apache.archiva.admin.model.managed.ManagedRepositoryAdmin; 028import org.apache.archiva.admin.model.networkproxy.NetworkProxyAdmin; 029import org.apache.archiva.admin.model.proxyconnector.ProxyConnectorAdmin; 030import org.apache.archiva.admin.model.remote.RemoteRepositoryAdmin; 031import org.apache.archiva.common.plexusbridge.PlexusSisuBridge; 032import org.apache.archiva.common.plexusbridge.PlexusSisuBridgeException; 033import org.apache.archiva.common.utils.VersionUtil; 034import org.apache.archiva.maven2.metadata.MavenMetadataReader; 035import org.apache.archiva.maven2.model.TreeEntry; 036import org.apache.archiva.metadata.repository.storage.RepositoryPathTranslator; 037import org.apache.archiva.model.ArchivaRepositoryMetadata; 038import org.apache.archiva.proxy.common.WagonFactory; 039import org.apache.archiva.repository.metadata.MetadataTools; 040import org.apache.archiva.xml.XMLException; 041import org.apache.commons.lang.StringUtils; 042import org.apache.maven.artifact.Artifact; 043import org.apache.maven.artifact.factory.ArtifactFactory; 044import org.apache.maven.model.building.DefaultModelBuilderFactory; 045import org.apache.maven.model.building.ModelBuilder; 046import org.apache.maven.repository.internal.DefaultArtifactDescriptorReader; 047import org.apache.maven.repository.internal.DefaultVersionRangeResolver; 048import org.apache.maven.repository.internal.DefaultVersionResolver; 049import org.apache.maven.repository.internal.MavenRepositorySystemSession; 050import org.slf4j.Logger; 051import org.slf4j.LoggerFactory; 052import org.sonatype.aether.RepositorySystem; 053import org.sonatype.aether.RepositorySystemSession; 054import org.sonatype.aether.collection.CollectRequest; 055import org.sonatype.aether.collection.CollectResult; 056import org.sonatype.aether.collection.DependencyCollectionException; 057import org.sonatype.aether.collection.DependencySelector; 058import org.sonatype.aether.graph.Dependency; 059import org.sonatype.aether.graph.DependencyVisitor; 060import org.sonatype.aether.impl.ArtifactDescriptorReader; 061import org.sonatype.aether.impl.VersionRangeResolver; 062import org.sonatype.aether.impl.VersionResolver; 063import org.sonatype.aether.impl.internal.DefaultServiceLocator; 064import org.sonatype.aether.impl.internal.SimpleLocalRepositoryManager; 065import org.sonatype.aether.repository.LocalRepository; 066import org.sonatype.aether.spi.connector.RepositoryConnectorFactory; 067import org.sonatype.aether.util.artifact.DefaultArtifact; 068import org.sonatype.aether.util.graph.selector.AndDependencySelector; 069import org.sonatype.aether.util.graph.selector.ExclusionDependencySelector; 070import org.springframework.stereotype.Service; 071 072import javax.annotation.PostConstruct; 073import javax.inject.Inject; 074import javax.inject.Named; 075import java.io.File; 076import java.util.ArrayList; 077import java.util.HashMap; 078import java.util.List; 079import java.util.Map; 080 081/** 082 * @author Olivier Lamy 083 * @since 1.4-M3 084 */ 085@Service("dependencyTreeBuilder#maven3") 086public class Maven3DependencyTreeBuilder 087 implements DependencyTreeBuilder 088{ 089 private Logger log = LoggerFactory.getLogger( getClass() ); 090 091 @Inject 092 private PlexusSisuBridge plexusSisuBridge; 093 094 @Inject 095 @Named( "repositoryPathTranslator#maven2" ) 096 private RepositoryPathTranslator pathTranslator; 097 098 @Inject 099 private WagonFactory wagonFactory; 100 101 @Inject 102 private ManagedRepositoryAdmin managedRepositoryAdmin; 103 104 @Inject 105 private ProxyConnectorAdmin proxyConnectorAdmin; 106 107 @Inject 108 private NetworkProxyAdmin networkProxyAdmin; 109 110 @Inject 111 private RemoteRepositoryAdmin remoteRepositoryAdmin; 112 113 private ArtifactFactory factory; 114 115 private ModelBuilder builder; 116 117 @PostConstruct 118 public void initialize() 119 throws PlexusSisuBridgeException 120 { 121 factory = plexusSisuBridge.lookup( ArtifactFactory.class, "default" ); 122 123 DefaultModelBuilderFactory defaultModelBuilderFactory = new DefaultModelBuilderFactory(); 124 builder = defaultModelBuilderFactory.newInstance(); 125 } 126 127 @Override 128 public void buildDependencyTree( List<String> repositoryIds, String groupId, String artifactId, String version, 129 DependencyVisitor dependencyVisitor ) 130 throws DependencyTreeBuilderException 131 { 132 Artifact projectArtifact = factory.createProjectArtifact( groupId, artifactId, version ); 133 ManagedRepository repository = null; 134 try 135 { 136 repository = findArtifactInRepositories( repositoryIds, projectArtifact ); 137 } 138 catch ( RepositoryAdminException e ) 139 { 140 // FIXME better exception 141 throw new DependencyTreeBuilderException( "Cannot build project dependency tree " + e.getMessage(), e ); 142 } 143 144 if ( repository == null ) 145 { 146 // metadata could not be resolved 147 return; 148 } 149 150 List<RemoteRepository> remoteRepositories = new ArrayList<>(); 151 Map<String, NetworkProxy> networkProxies = new HashMap<>(); 152 153 try 154 { 155 // MRM-1411 156 // TODO: this is a workaround for a lack of proxy capability in the resolvers - replace when it can all be 157 // handled there. It doesn't cache anything locally! 158 159 Map<String, List<ProxyConnector>> proxyConnectorsMap = proxyConnectorAdmin.getProxyConnectorAsMap(); 160 List<ProxyConnector> proxyConnectors = proxyConnectorsMap.get( repository.getId() ); 161 if ( proxyConnectors != null ) 162 { 163 for ( ProxyConnector proxyConnector : proxyConnectors ) 164 { 165 remoteRepositories.add( 166 remoteRepositoryAdmin.getRemoteRepository( proxyConnector.getTargetRepoId() ) ); 167 168 NetworkProxy networkProxyConfig = networkProxyAdmin.getNetworkProxy( proxyConnector.getProxyId() ); 169 170 if ( networkProxyConfig != null ) 171 { 172 // key/value: remote repo ID/proxy info 173 networkProxies.put( proxyConnector.getTargetRepoId(), networkProxyConfig ); 174 } 175 } 176 } 177 } 178 catch ( RepositoryAdminException e ) 179 { 180 throw new DependencyTreeBuilderException( e.getMessage(), e ); 181 } 182 183 // FIXME take care of relative path 184 ResolveRequest resolveRequest = new ResolveRequest(); 185 resolveRequest.dependencyVisitor = dependencyVisitor; 186 resolveRequest.localRepoDir = repository.getLocation(); 187 resolveRequest.groupId = groupId; 188 resolveRequest.artifactId = artifactId; 189 resolveRequest.version = version; 190 resolveRequest.remoteRepositories = remoteRepositories; 191 resolveRequest.networkProxies = networkProxies; 192 resolve( resolveRequest ); 193 } 194 195 196 @Override 197 public List<TreeEntry> buildDependencyTree( List<String> repositoryIds, String groupId, String artifactId, 198 String version ) 199 throws DependencyTreeBuilderException 200 { 201 202 List<TreeEntry> treeEntries = new ArrayList<>(); 203 TreeDependencyNodeVisitor treeDependencyNodeVisitor = new TreeDependencyNodeVisitor( treeEntries ); 204 205 buildDependencyTree( repositoryIds, groupId, artifactId, version, treeDependencyNodeVisitor ); 206 207 log.debug( "treeEntrie: {}", treeEntries ); 208 return treeEntries; 209 } 210 211 private static class ResolveRequest 212 { 213 String localRepoDir, groupId, artifactId, version; 214 215 DependencyVisitor dependencyVisitor; 216 217 List<RemoteRepository> remoteRepositories; 218 219 Map<String, NetworkProxy> networkProxies; 220 221 } 222 223 224 private void resolve( ResolveRequest resolveRequest ) 225 { 226 227 RepositorySystem system = newRepositorySystem(); 228 229 RepositorySystemSession session = newRepositorySystemSession( system, resolveRequest.localRepoDir ); 230 231 org.sonatype.aether.artifact.Artifact artifact = new DefaultArtifact( 232 resolveRequest.groupId + ":" + resolveRequest.artifactId + ":" + resolveRequest.version ); 233 234 CollectRequest collectRequest = new CollectRequest(); 235 collectRequest.setRoot( new Dependency( artifact, "" ) ); 236 237 // add remote repositories 238 for ( RemoteRepository remoteRepository : resolveRequest.remoteRepositories ) 239 { 240 collectRequest.addRepository( 241 new org.sonatype.aether.repository.RemoteRepository( remoteRepository.getId(), "default", 242 remoteRepository.getUrl() ) ); 243 } 244 collectRequest.setRequestContext( "project" ); 245 246 //collectRequest.addRepository( repo ); 247 248 try 249 { 250 CollectResult collectResult = system.collectDependencies( session, collectRequest ); 251 collectResult.getRoot().accept( resolveRequest.dependencyVisitor ); 252 log.debug( "test" ); 253 } 254 catch ( DependencyCollectionException e ) 255 { 256 log.error( e.getMessage(), e ); 257 } 258 259 260 } 261 262 private RepositorySystem newRepositorySystem() 263 { 264 DefaultServiceLocator locator = new DefaultServiceLocator(); 265 locator.addService( RepositoryConnectorFactory.class, 266 ArchivaRepositoryConnectorFactory.class );// FileRepositoryConnectorFactory.class ); 267 locator.addService( VersionResolver.class, DefaultVersionResolver.class ); 268 locator.addService( VersionRangeResolver.class, DefaultVersionRangeResolver.class ); 269 locator.addService( ArtifactDescriptorReader.class, DefaultArtifactDescriptorReader.class ); 270 //locator.addService( RepositoryConnectorFactory.class, WagonRepositoryConnectorFactory.class ); 271 //locator.setServices( WagonProvider.class, ); 272 273 return locator.getService( RepositorySystem.class ); 274 } 275 276 private RepositorySystemSession newRepositorySystemSession( RepositorySystem system, String localRepoDir ) 277 { 278 MavenRepositorySystemSession session = new MavenRepositorySystemSession(); 279 280 DependencySelector depFilter = new AndDependencySelector( new ExclusionDependencySelector() ); 281 session.setDependencySelector( depFilter ); 282 283 session.setLocalRepositoryManager( 284 new SimpleLocalRepositoryManager( localRepoDir ) ); 285 286 return session; 287 } 288 289 290 private ManagedRepository findArtifactInRepositories( List<String> repositoryIds, Artifact projectArtifact ) 291 throws RepositoryAdminException 292 { 293 for ( String repoId : repositoryIds ) 294 { 295 ManagedRepository managedRepository = managedRepositoryAdmin.getManagedRepository( repoId ); 296 297 File repoDir = new File( managedRepository.getLocation() ); 298 File file = pathTranslator.toFile( repoDir, projectArtifact.getGroupId(), projectArtifact.getArtifactId(), 299 projectArtifact.getBaseVersion(), 300 projectArtifact.getArtifactId() + "-" + projectArtifact.getVersion() 301 + ".pom" ); 302 303 if ( file.exists() ) 304 { 305 return managedRepository; 306 } 307 // try with snapshot version 308 if ( StringUtils.endsWith( projectArtifact.getBaseVersion(), VersionUtil.SNAPSHOT ) ) 309 { 310 File metadataFile = new File( file.getParent(), MetadataTools.MAVEN_METADATA ); 311 if ( metadataFile.exists() ) 312 { 313 try 314 { 315 ArchivaRepositoryMetadata archivaRepositoryMetadata = MavenMetadataReader.read( metadataFile ); 316 int buildNumber = archivaRepositoryMetadata.getSnapshotVersion().getBuildNumber(); 317 String timeStamp = archivaRepositoryMetadata.getSnapshotVersion().getTimestamp(); 318 // rebuild file name with timestamped version and build number 319 String timeStampFileName = 320 new StringBuilder( projectArtifact.getArtifactId() ).append( '-' ).append( 321 StringUtils.remove( projectArtifact.getBaseVersion(), 322 "-" + VersionUtil.SNAPSHOT ) ).append( '-' ).append( 323 timeStamp ).append( '-' ).append( Integer.toString( buildNumber ) ).append( 324 ".pom" ).toString(); 325 File timeStampFile = new File( file.getParent(), timeStampFileName ); 326 log.debug( "try to find timestamped snapshot version file: {}", timeStampFile.getPath() ); 327 if ( timeStampFile.exists() ) 328 { 329 return managedRepository; 330 } 331 } 332 catch ( XMLException e ) 333 { 334 log.warn( "skip fail to find timestamped snapshot pom: {}", e.getMessage() ); 335 } 336 } 337 } 338 } 339 return null; 340 } 341 342}