View Javadoc
1   package org.apache.maven.plugin.assembly.artifact;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.maven.artifact.Artifact;
23  import org.apache.maven.artifact.factory.ArtifactFactory;
24  import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
25  import org.apache.maven.artifact.repository.ArtifactRepository;
26  import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
27  import org.apache.maven.artifact.resolver.ArtifactResolutionException;
28  import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
29  import org.apache.maven.artifact.resolver.ArtifactResolver;
30  import org.apache.maven.artifact.resolver.MultipleArtifactsNotFoundException;
31  import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
32  import org.apache.maven.plugin.assembly.AssemblerConfigurationSource;
33  import org.apache.maven.plugin.assembly.archive.ArchiveCreationException;
34  import org.apache.maven.plugin.assembly.archive.phase.ModuleSetAssemblyPhase;
35  import org.apache.maven.plugin.assembly.model.Assembly;
36  import org.apache.maven.plugin.assembly.model.DependencySet;
37  import org.apache.maven.plugin.assembly.model.ModuleBinaries;
38  import org.apache.maven.plugin.assembly.model.ModuleSet;
39  import org.apache.maven.plugin.assembly.model.Repository;
40  import org.apache.maven.plugin.assembly.resolved.AssemblyId;
41  import org.apache.maven.plugin.assembly.utils.FilterUtils;
42  import org.apache.maven.project.MavenProject;
43  import org.apache.maven.project.artifact.InvalidDependencyVersionException;
44  import org.codehaus.plexus.component.annotations.Component;
45  import org.codehaus.plexus.component.annotations.Requirement;
46  import org.codehaus.plexus.logging.AbstractLogEnabled;
47  import org.codehaus.plexus.logging.Logger;
48  import org.codehaus.plexus.util.StringUtils;
49  
50  import java.util.ArrayList;
51  import java.util.Collections;
52  import java.util.HashSet;
53  import java.util.LinkedHashMap;
54  import java.util.LinkedHashSet;
55  import java.util.List;
56  import java.util.Map;
57  import java.util.Set;
58  
59  /**
60   * @author jdcasey
61   * @version $Id: DefaultDependencyResolver.java 1639422 2014-11-13 18:08:07Z krosenvold $
62   */
63  @Component( role = DependencyResolver.class )
64  public class DefaultDependencyResolver
65      extends AbstractLogEnabled
66      implements DependencyResolver
67  {
68  
69      @Requirement
70      private ArtifactResolver resolver;
71  
72      @Requirement
73      private ArtifactMetadataSource metadataSource;
74  
75      @Requirement
76      private ArtifactFactory factory;
77  
78      @SuppressWarnings( "UnusedDeclaration" )
79      public DefaultDependencyResolver()
80      {
81          // for plexus init
82      }
83  
84      protected DefaultDependencyResolver( final ArtifactResolver resolver, final ArtifactMetadataSource metadataSource,
85                                           final ArtifactFactory factory, final Logger logger )
86      {
87          this.resolver = resolver;
88          this.metadataSource = metadataSource;
89          this.factory = factory;
90          enableLogging( logger );
91      }
92  
93      public Map<DependencySet, Set<Artifact>> resolveDependencySets( final Assembly assembly, ModuleSet moduleSet,
94                                                                      final AssemblerConfigurationSource configSource,
95                                                                      List<DependencySet> dependencySets )
96          throws DependencyResolutionException
97      {
98          Map<DependencySet, Set<Artifact>> result = new LinkedHashMap<DependencySet, Set<Artifact>>();
99  
100         for ( DependencySet dependencySet : dependencySets )
101         {
102 
103             final MavenProject currentProject = configSource.getProject();
104 
105             final ResolutionManagementInfo info = new ResolutionManagementInfo( currentProject );
106             updateRepositoryResolutionRequirements( assembly, info );
107             final AssemblyId assemblyId = AssemblyId.createAssemblyId( assembly );
108             updateDependencySetResolutionRequirements( dependencySet, info, assemblyId, currentProject );
109             updateModuleSetResolutionRequirements( assemblyId, moduleSet, dependencySet, info, configSource );
110 
111             Set<Artifact> artifacts;
112             if ( info.isResolutionRequired() )
113             {
114                 final List<ArtifactRepository> repos =
115                     aggregateRemoteArtifactRepositories( configSource.getRemoteRepositories(),
116                                                          info.getEnabledProjects() );
117 
118                 artifacts = info.getArtifacts();
119                 if ( info.isResolvedTransitively() )
120                 {
121                     getLogger().debug( "Resolving project dependencies transitively." );
122                     artifacts = resolveTransitively( artifacts, repos, info, configSource );
123                 }
124                 else
125                 {
126                     getLogger().debug(
127                         "Resolving project dependencies ONLY. Transitive dependencies WILL NOT be included in the results." );
128                     artifacts = resolveNonTransitively( assembly, artifacts, configSource, repos );
129                 }
130             }
131             else
132             {
133                 artifacts = new HashSet<Artifact>();
134             }
135             result.put( dependencySet, artifacts );
136 
137         }
138         return result;
139     }
140 
141     public Map<DependencySet, Set<Artifact>> resolveDependencySets( final Assembly assembly,
142                                                                     final AssemblerConfigurationSource configSource,
143                                                                     List<DependencySet> dependencySets )
144         throws DependencyResolutionException
145     {
146         Map<DependencySet, Set<Artifact>> result = new LinkedHashMap<DependencySet, Set<Artifact>>();
147 
148         for ( DependencySet dependencySet : dependencySets )
149         {
150 
151             final MavenProject currentProject = configSource.getProject();
152 
153             final ResolutionManagementInfo info = new ResolutionManagementInfo( currentProject );
154             updateRepositoryResolutionRequirements( assembly, info );
155             final AssemblyId assemblyId = AssemblyId.createAssemblyId( assembly );
156             updateDependencySetResolutionRequirements( dependencySet, info, assemblyId, currentProject );
157 
158             Set<Artifact> artifacts;
159             if ( info.isResolutionRequired() )
160             {
161                 final List<ArtifactRepository> repos =
162                     aggregateRemoteArtifactRepositories( configSource.getRemoteRepositories(),
163                                                          info.getEnabledProjects() );
164 
165                 artifacts = info.getArtifacts();
166                 if ( info.isResolvedTransitively() )
167                 {
168                     getLogger().debug( "Resolving project dependencies transitively." );
169                     artifacts = resolveTransitively( artifacts, repos, info, configSource );
170                 }
171                 else
172                 {
173                     getLogger().debug(
174                         "Resolving project dependencies ONLY. Transitive dependencies WILL NOT be included in the results." );
175                     artifacts = resolveNonTransitively( assembly, artifacts, configSource, repos );
176                 }
177             }
178             else
179             {
180                 artifacts = new HashSet<Artifact>();
181             }
182             result.put( dependencySet, artifacts );
183 
184         }
185         return result;
186     }
187 
188     Set<Artifact> resolveNonTransitively( final Assembly assembly, final Set<Artifact> dependencyArtifacts,
189                                           final AssemblerConfigurationSource configSource,
190                                           final List<ArtifactRepository> repos )
191         throws DependencyResolutionException
192     {
193 
194         final List<Artifact> missing = new ArrayList<Artifact>();
195         final Set<Artifact> resolved = new LinkedHashSet<Artifact>();
196         for ( final Artifact depArtifact : dependencyArtifacts )
197         {
198             try
199             {
200                 resolver.resolve( depArtifact, repos, configSource.getLocalRepository() );
201                 resolved.add( depArtifact );
202             }
203             catch ( final ArtifactResolutionException e )
204             {
205                 if ( getLogger().isDebugEnabled() )
206                 {
207                     getLogger().debug(
208                         "Failed to resolve: " + depArtifact.getId() + " for assembly: " + assembly.getId() );
209                 }
210                 missing.add( depArtifact );
211             }
212             catch ( final ArtifactNotFoundException e )
213             {
214                 if ( getLogger().isDebugEnabled() )
215                 {
216                     getLogger().debug(
217                         "Failed to resolve: " + depArtifact.getId() + " for assembly: " + assembly.getId() );
218                 }
219                 missing.add( depArtifact );
220             }
221         }
222 
223         if ( !missing.isEmpty() )
224         {
225             final MavenProject project = configSource.getProject();
226             final Artifact rootArtifact = project.getArtifact();
227 
228             final Throwable error =
229                 new MultipleArtifactsNotFoundException( rootArtifact, new ArrayList<Artifact>( resolved ), missing,
230                                                         repos );
231 
232             throw new DependencyResolutionException( "Failed to resolve dependencies for: " + assembly.getId(), error );
233         }
234 
235         return resolved;
236     }
237 
238     @SuppressWarnings( "unchecked" )
239     private Set<Artifact> resolveTransitively( final Set<Artifact> dependencyArtifacts,
240                                                final List<ArtifactRepository> repos,
241                                                final ResolutionManagementInfo info,
242                                                final AssemblerConfigurationSource configSource )
243         throws DependencyResolutionException
244     {
245         final MavenProject project = configSource.getProject();
246 
247         final ArtifactFilter filter = info.getScopeFilter();
248         final ArtifactRepository localRepository = configSource.getLocalRepository();
249 
250         ArtifactResolutionResult result;
251         try
252         {
253             result = resolver.resolveTransitively( dependencyArtifacts, project.getArtifact(),
254                                                    project.getManagedVersionMap(), localRepository, repos,
255                                                    metadataSource, filter );
256         }
257         catch ( final ArtifactResolutionException e )
258         {
259             throw new DependencyResolutionException( "Failed to resolve dependencies for assembly: ", e );
260         }
261         catch ( final ArtifactNotFoundException e )
262         {
263             throw new DependencyResolutionException( "Failed to resolve dependencies for assembly: ", e );
264         }
265 
266         getLogger().debug( "While resolving dependencies of " + project.getId() + ":" );
267 
268         FilterUtils.reportFilteringStatistics( Collections.singleton( filter ), getLogger() );
269 
270         return result.getArtifacts();
271     }
272 
273     void updateRepositoryResolutionRequirements( final Assembly assembly, final ResolutionManagementInfo requirements )
274     {
275         final List<Repository> repositories = assembly.getRepositories();
276 
277         if ( repositories != null && !repositories.isEmpty() )
278         {
279             requirements.setResolutionRequired( true );
280             for ( final Repository repo : repositories )
281             {
282                 enableScope( repo.getScope(), requirements );
283             }
284         }
285     }
286 
287 
288     void updateModuleSetResolutionRequirements( AssemblyId assemblyId, ModuleSet set, DependencySet dependencySet,
289                                                 final ResolutionManagementInfo requirements,
290                                                 final AssemblerConfigurationSource configSource )
291         throws DependencyResolutionException
292     {
293         final ModuleBinaries binaries = set.getBinaries();
294         if ( binaries != null )
295         {
296             Set<MavenProject> projects;
297             try
298             {
299                 projects = ModuleSetAssemblyPhase.getModuleProjects( set, configSource, getLogger() );
300             }
301             catch ( final ArchiveCreationException e )
302             {
303                 throw new DependencyResolutionException( "Error determining project-set for moduleSet with binaries.",
304                                                          e );
305             }
306 
307             if ( !projects.isEmpty() )
308             {
309                 for ( final MavenProject p : projects )
310                 {
311                     requirements.enableProjectResolution( p );
312 
313                     if ( p.getArtifact() == null )
314                     {
315                         // TODO: such a call in MavenMetadataSource too - packaging not really the intention of
316                         // type
317                         final Artifact artifact =
318                             factory.createBuildArtifact( p.getGroupId(), p.getArtifactId(), p.getVersion(),
319                                                          p.getPackaging() );
320                         p.setArtifact( artifact );
321                     }
322                 }
323             }
324 
325             if ( binaries.isIncludeDependencies() )
326             {
327                 updateDependencySetResolutionRequirements( dependencySet, requirements, assemblyId,
328                                                            projects.toArray( new MavenProject[projects.size()] ) );
329             }
330         }
331     }
332 
333 
334     @SuppressWarnings( "unchecked" )
335     void updateDependencySetResolutionRequirements( final DependencySet set,
336                                                     final ResolutionManagementInfo requirements, AssemblyId assemblyId,
337                                                     final MavenProject... projects )
338         throws DependencyResolutionException
339     {
340         requirements.setResolutionRequired( true );
341 
342         requirements.setResolvedTransitively( set.isUseTransitiveDependencies() );
343 
344         enableScope( set.getScope(), requirements );
345 
346         for ( final MavenProject project : projects )
347         {
348             if ( project == null )
349             {
350                 continue;
351             }
352 
353             Set<Artifact> dependencyArtifacts = project.getDependencyArtifacts();
354             if ( dependencyArtifacts == null )
355             {
356                 try
357                 {
358                     dependencyArtifacts = project.createArtifacts( factory, null, requirements.getScopeFilter() );
359                     project.setDependencyArtifacts( dependencyArtifacts );
360                 }
361                 catch ( final InvalidDependencyVersionException e )
362                 {
363                     throw new DependencyResolutionException(
364                         "Failed to create dependency artifacts for resolution. Assembly: " + assemblyId, e );
365                 }
366             }
367 
368             requirements.addArtifacts( dependencyArtifacts );
369             getLogger().debug( "Dependencies for project: " + project.getId() + " are:\n" + StringUtils.join(
370                 dependencyArtifacts.iterator(), "\n" ) );
371         }
372     }
373 
374     private void enableScope( final String scope, final ResolutionManagementInfo requirements )
375     {
376         if ( Artifact.SCOPE_COMPILE.equals( scope ) )
377         {
378             requirements.enableCompileScope();
379         }
380         else if ( Artifact.SCOPE_PROVIDED.equals( scope ) )
381         {
382             requirements.enableProvidedScope();
383         }
384         else if ( Artifact.SCOPE_RUNTIME.equals( scope ) )
385         {
386             requirements.enableRuntimeScope();
387         }
388         else if ( Artifact.SCOPE_SYSTEM.equals( scope ) )
389         {
390             requirements.enableSystemScope();
391         }
392         else if ( Artifact.SCOPE_TEST.equals( scope ) )
393         {
394             requirements.enableTestScope();
395         }
396     }
397 
398     @SuppressWarnings( "unchecked" )
399     List<ArtifactRepository> aggregateRemoteArtifactRepositories( final List<ArtifactRepository> remoteRepositories,
400                                                                   final Set<MavenProject> projects )
401     {
402         final List<List<ArtifactRepository>> repoLists = new ArrayList<List<ArtifactRepository>>();
403 
404         repoLists.add( remoteRepositories );
405         for ( final MavenProject project : projects )
406         {
407             repoLists.add( project.getRemoteArtifactRepositories() );
408         }
409 
410         final List<ArtifactRepository> remoteRepos = new ArrayList<ArtifactRepository>();
411         final Set<String> encounteredUrls = new HashSet<String>();
412 
413         for ( final List<ArtifactRepository> repositoryList : repoLists )
414         {
415             if ( ( repositoryList != null ) && !repositoryList.isEmpty() )
416             {
417                 for ( final ArtifactRepository repo : repositoryList )
418                 {
419                     if ( !encounteredUrls.contains( repo.getUrl() ) )
420                     {
421                         remoteRepos.add( repo );
422                         encounteredUrls.add( repo.getUrl() );
423                     }
424                 }
425             }
426         }
427 
428         return remoteRepos;
429     }
430 
431 }