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