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