View Javadoc
1   package org.apache.maven.plugin.assembly.archive.task;
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.io.File;
23  import java.util.ArrayList;
24  import java.util.Collections;
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.model.Dependency;
31  import org.apache.maven.model.Model;
32  import org.apache.maven.plugin.assembly.AssemblerConfigurationSource;
33  import org.apache.maven.plugin.assembly.InvalidAssemblerConfigurationException;
34  import org.apache.maven.plugin.assembly.archive.ArchiveCreationException;
35  import org.apache.maven.plugin.assembly.format.AssemblyFormattingException;
36  import org.apache.maven.plugin.assembly.model.DependencySet;
37  import org.apache.maven.plugin.assembly.model.FileSet;
38  import org.apache.maven.plugin.assembly.model.UnpackOptions;
39  import org.apache.maven.plugin.assembly.utils.AssemblyFormatUtils;
40  import org.apache.maven.plugin.assembly.utils.FilterUtils;
41  import org.apache.maven.plugin.assembly.utils.TypeConversionUtils;
42  import org.apache.maven.project.MavenProject;
43  import org.apache.maven.project.MavenProjectBuilder;
44  import org.apache.maven.project.ProjectBuildingException;
45  import org.apache.maven.shared.artifact.filter.ScopeArtifactFilter;
46  import org.codehaus.plexus.archiver.Archiver;
47  import org.codehaus.plexus.archiver.ArchiverException;
48  import org.codehaus.plexus.archiver.UnArchiver;
49  import org.codehaus.plexus.archiver.manager.ArchiverManager;
50  import org.codehaus.plexus.archiver.manager.NoSuchArchiverException;
51  import org.codehaus.plexus.logging.Logger;
52  
53  /**
54   * @version $Id: AddDependencySetsTask.java 1633832 2014-10-23 15:03:47Z krosenvold $
55   */
56  public class AddDependencySetsTask
57  {
58  
59      private static final List<String> NON_ARCHIVE_DEPENDENCY_TYPES;
60  
61      static
62      {
63          final List<String> nonArch = new ArrayList<String>();
64  
65          nonArch.add( "pom" );
66  
67          NON_ARCHIVE_DEPENDENCY_TYPES = Collections.unmodifiableList( nonArch );
68      }
69  
70      private final List<DependencySet> dependencySets;
71  
72      private final Logger logger;
73  
74      private final MavenProject project;
75  
76      private MavenProject moduleProject;
77  
78      private final MavenProjectBuilder projectBuilder;
79  
80      private String defaultOutputDirectory;
81  
82      private String defaultOutputFileNameMapping;
83  
84      private Artifact moduleArtifact;
85  
86      private final Set<Artifact> resolvedArtifacts;
87  
88      private final ArchiverManager archiverManager;
89  
90      public AddDependencySetsTask( final List<DependencySet> dependencySets, final Set<Artifact> resolvedArtifacts,
91                                    final MavenProject project, final MavenProjectBuilder projectBuilder,
92                                    final ArchiverManager archiverManager, final Logger logger )
93      {
94          this.dependencySets = dependencySets;
95          this.resolvedArtifacts = resolvedArtifacts;
96          this.project = project;
97          this.projectBuilder = projectBuilder;
98          this.archiverManager = archiverManager;
99          this.logger = logger;
100     }
101 
102     public void execute( final Archiver archiver, final AssemblerConfigurationSource configSource )
103         throws ArchiveCreationException, AssemblyFormattingException, InvalidAssemblerConfigurationException
104     {
105         if ( ( dependencySets == null ) || dependencySets.isEmpty() )
106         {
107             logger.debug( "No dependency sets specified." );
108             return;
109         }
110 
111         @SuppressWarnings( "unchecked" )
112         final List<Dependency> deps = project.getDependencies();
113         if ( ( deps == null ) || deps.isEmpty() )
114         {
115             logger.debug( "Project " + project.getId() + " has no dependencies. Skipping dependency set addition." );
116         }
117 
118         for ( final DependencySet dependencySet : dependencySets )
119         {
120             addDependencySet( dependencySet, archiver, configSource );
121         }
122     }
123 
124     void addDependencySet( final DependencySet dependencySet, final Archiver archiver,
125                            final AssemblerConfigurationSource configSource )
126         throws AssemblyFormattingException, ArchiveCreationException, InvalidAssemblerConfigurationException
127     {
128         logger.debug( "Processing DependencySet (output=" + dependencySet.getOutputDirectory() + ")" );
129 
130         if ( !dependencySet.isUseTransitiveDependencies() && dependencySet.isUseTransitiveFiltering() )
131         {
132             logger.warn( "DependencySet has nonsensical configuration: useTransitiveDependencies == false "
133                 + "AND useTransitiveFiltering == true. Transitive filtering flag will be ignored." );
134         }
135 
136         final Set<Artifact> dependencyArtifacts = resolveDependencyArtifacts( dependencySet );
137 
138         boolean filterContents = false;
139         final UnpackOptions opts = dependencySet.getUnpackOptions();
140         if ( dependencySet.isUnpack() && opts != null && ( opts.isFiltered() || opts.getLineEnding() != null ) )
141         {
142             filterContents = true;
143         }
144         else if ( dependencyArtifacts.size() > 1 )
145         {
146             checkMultiArtifactOutputConfig( dependencySet );
147         }
148 
149         logger.debug( "Adding " + dependencyArtifacts.size() + " dependency artifacts." );
150 
151         for ( final Artifact depArtifact : dependencyArtifacts )
152         {
153             MavenProject depProject;
154             try
155             {
156                 depProject =
157                     projectBuilder.buildFromRepository( depArtifact, configSource.getRemoteRepositories(),
158                                                         configSource.getLocalRepository() );
159             }
160             catch ( final ProjectBuildingException e )
161             {
162                 logger.debug( "Error retrieving POM of module-dependency: " + depArtifact.getId() + "; Reason: "
163                     + e.getMessage() + "\n\nBuilding stub project instance." );
164 
165                 depProject = buildProjectStub( depArtifact );
166             }
167 
168             if ( NON_ARCHIVE_DEPENDENCY_TYPES.contains( depArtifact.getType() ) )
169             {
170                 addNonArchiveDependency( depArtifact, depProject, dependencySet, archiver, configSource );
171             }
172             else
173             {
174                 if ( filterContents )
175                 {
176                     addFilteredUnpackedArtifact( dependencySet, depArtifact, depProject, archiver, configSource );
177                 }
178                 else
179                 {
180                     addNormalArtifact( dependencySet, depArtifact, depProject, archiver, configSource );
181                 }
182             }
183         }
184     }
185 
186     private void checkMultiArtifactOutputConfig( final DependencySet dependencySet )
187     {
188         String dir = dependencySet.getOutputDirectory();
189         if ( dir == null )
190         {
191             dir = defaultOutputDirectory;
192         }
193 
194         String mapping = dependencySet.getOutputFileNameMapping();
195         if ( mapping == null )
196         {
197             mapping = defaultOutputFileNameMapping;
198         }
199 
200         if ( ( dir == null || !dir.contains( "${" ) ) && ( mapping == null || !mapping.contains( "${" ) ) )
201         {
202             logger.warn( "NOTE: Your assembly specifies a dependencySet that matches multiple artifacts, but specifies a concrete output format. "
203                 + "THIS MAY RESULT IN ONE OR MORE ARTIFACTS BEING OBSCURED!\n\nOutput directory: '"
204                 + dir
205                 + "'\nOutput filename mapping: '" + mapping + "'" );
206         }
207     }
208 
209     @SuppressWarnings( "ResultOfMethodCallIgnored" )
210     private void addFilteredUnpackedArtifact( final DependencySet dependencySet, final Artifact depArtifact,
211                                               final MavenProject depProject, final Archiver archiver,
212                                               final AssemblerConfigurationSource configSource )
213         throws ArchiveCreationException, AssemblyFormattingException
214     {
215         logger.debug( "Adding dependency artifact " + depArtifact.getId() + " after filtering the unpacked contents." );
216 
217         final StringBuilder sb =
218             new StringBuilder().append( depArtifact.getGroupId() ).append( "_" ).append( depArtifact.getArtifactId() ).append( "_" ).append( depArtifact.getVersion() );
219 
220         final String classifier = depArtifact.getClassifier();
221         if ( classifier != null )
222         {
223             sb.append( "_" ).append( classifier );
224         }
225 
226         sb.append( "." ).append( depArtifact.getType() );
227 
228         final File dir = new File( configSource.getWorkingDirectory(), sb.toString() );
229         if ( dir.exists() )
230         {
231             logger.debug( "NOT unpacking: " + depArtifact.getId() + ". Directory already exists in workdir:\n\t"
232                 + dir.getAbsolutePath() );
233         }
234         else
235         {
236             dir.mkdirs();
237 
238             UnArchiver unarchiver;
239             try
240             {
241                 unarchiver = archiverManager.getUnArchiver( depArtifact.getFile() );
242             }
243             catch ( final NoSuchArchiverException e )
244             {
245                 throw new ArchiveCreationException( "Failed to retrieve un-archiver for: " + depArtifact.getId()
246                     + ". Dependency filtering cannot proceed.", e );
247             }
248 
249             unarchiver.setDestDirectory( dir );
250             unarchiver.setOverwrite( true );
251             unarchiver.setSourceFile( depArtifact.getFile() );
252             unarchiver.setIgnorePermissions( configSource.isIgnorePermissions() );
253 
254             try
255             {
256                 unarchiver.extract();
257             }
258             catch ( final ArchiverException e )
259             {
260                 throw new ArchiveCreationException( "Failed to unpack dependency archive: " + depArtifact.getId()
261                     + ". Dependency filtering cannot proceed.", e );
262             }
263         }
264 
265         final UnpackOptions opts = dependencySet.getUnpackOptions();
266 
267         final FileSet fs = new FileSet();
268         fs.setDirectory( dir.getAbsolutePath() );
269         fs.setDirectoryMode( dependencySet.getDirectoryMode() );
270         fs.setExcludes( opts.getExcludes() );
271         fs.setFileMode( dependencySet.getFileMode() );
272         fs.setFiltered( opts.isFiltered() );
273         fs.setIncludes( opts.getIncludes() );
274 
275         String outDir = dependencySet.getOutputDirectory();
276         if ( outDir == null )
277         {
278             outDir = defaultOutputDirectory;
279         }
280 
281         String filenameMapping = dependencySet.getOutputFileNameMapping();
282         if ( filenameMapping == null )
283         {
284             filenameMapping = defaultOutputFileNameMapping;
285         }
286 
287         filenameMapping =
288             AssemblyFormatUtils.evaluateFileNameMapping( filenameMapping, depArtifact, configSource.getProject(),
289                                                          moduleProject, moduleArtifact, depProject, configSource );
290 
291         final String outputLocation = new File( outDir, filenameMapping ).getPath();
292 
293         fs.setOutputDirectory( outputLocation );
294 
295         fs.setLineEnding( opts.getLineEnding() );
296         fs.setUseDefaultExcludes( opts.isUseDefaultExcludes() );
297 
298         final AddFileSetsTask task = new AddFileSetsTask( fs );
299         task.setProject( depProject );
300         task.setModuleProject( moduleProject );
301         task.setLogger( logger );
302 
303         task.execute( archiver, configSource );
304     }
305 
306     private void addNormalArtifact( final DependencySet dependencySet, final Artifact depArtifact,
307                                     final MavenProject depProject, final Archiver archiver,
308                                     final AssemblerConfigurationSource configSource )
309         throws AssemblyFormattingException, ArchiveCreationException
310     {
311         logger.debug( "Adding dependency artifact " + depArtifact.getId() + "." );
312 
313         final AddArtifactTask task = new AddArtifactTask( depArtifact, logger );
314 
315         task.setProject( depProject );
316         task.setModuleProject( moduleProject );
317         task.setModuleArtifact( moduleArtifact );
318         task.setOutputDirectory( dependencySet.getOutputDirectory(), defaultOutputDirectory );
319         task.setFileNameMapping( dependencySet.getOutputFileNameMapping(), defaultOutputFileNameMapping );
320 
321         final int dirMode = TypeConversionUtils.modeToInt( dependencySet.getDirectoryMode(), logger );
322         if ( dirMode != -1 )
323         {
324             task.setDirectoryMode( dirMode );
325         }
326 
327         final int fileMode = TypeConversionUtils.modeToInt( dependencySet.getFileMode(), logger );
328         if ( fileMode != -1 )
329         {
330             task.setFileMode( fileMode );
331         }
332 
333         task.setUnpack( dependencySet.isUnpack() );
334 
335         final UnpackOptions opts = dependencySet.getUnpackOptions();
336         if ( dependencySet.isUnpack() && ( opts != null ) )
337         {
338             task.setIncludes( opts.getIncludes() );
339             task.setExcludes( opts.getExcludes() );
340         }
341 
342         task.execute( archiver, configSource );
343     }
344 
345     private MavenProject buildProjectStub( final Artifact depArtifact )
346     {
347         final Model model = new Model();
348         model.setGroupId( depArtifact.getGroupId() );
349         model.setArtifactId( depArtifact.getArtifactId() );
350         model.setVersion( depArtifact.getBaseVersion() );
351         model.setPackaging( depArtifact.getType() );
352 
353         model.setDescription( "Stub for " + depArtifact.getId() );
354 
355         final MavenProject project = new MavenProject( model );
356         project.setArtifact( depArtifact );
357 
358         return project;
359     }
360 
361     Set<Artifact> resolveDependencyArtifacts( final DependencySet dependencySet )
362         throws InvalidAssemblerConfigurationException
363     {
364         final Set<Artifact> dependencyArtifacts = new LinkedHashSet<Artifact>();
365         if ( resolvedArtifacts != null )
366         {
367             dependencyArtifacts.addAll( resolvedArtifacts );
368         }
369 
370         if ( dependencySet.isUseProjectArtifact() )
371         {
372             final Artifact projectArtifact = project.getArtifact();
373             if ( ( projectArtifact != null ) && ( projectArtifact.getFile() != null ) )
374             {
375                 dependencyArtifacts.add( projectArtifact );
376             }
377             else
378             {
379                 logger.warn( "Cannot include project artifact: " + projectArtifact
380                     + "; it doesn't have an associated file or directory." );
381             }
382         }
383 
384         if ( dependencySet.isUseProjectAttachments() )
385         {
386             @SuppressWarnings( "unchecked" )
387             final List<Artifact> attachments = project.getAttachedArtifacts();
388             if ( attachments != null )
389             {
390                 for ( final Artifact attachment : attachments )
391                 {
392                     if ( attachment.getFile() != null )
393                     {
394                         dependencyArtifacts.add( attachment );
395                     }
396                     else
397                     {
398                         logger.warn( "Cannot include attached artifact: " + project.getId() + " for project: "
399                             + project.getId() + "; it doesn't have an associated file or directory." );
400                     }
401                 }
402             }
403         }
404 
405         if ( dependencySet.isUseTransitiveFiltering() )
406         {
407             logger.debug( "Filtering dependency artifacts USING transitive dependency path information." );
408         }
409         else
410         {
411             logger.debug( "Filtering dependency artifacts WITHOUT transitive dependency path information." );
412         }
413 
414         final ScopeArtifactFilter filter = new ScopeArtifactFilter( dependencySet.getScope() );
415 
416         FilterUtils.filterArtifacts( dependencyArtifacts, dependencySet.getIncludes(), dependencySet.getExcludes(),
417                                      dependencySet.isUseStrictFiltering(), dependencySet.isUseTransitiveFiltering(),
418                                      logger, filter );
419 
420         return dependencyArtifacts;
421     }
422 
423     void addNonArchiveDependency( final Artifact depArtifact, final MavenProject depProject,
424                                   final DependencySet dependencySet, final Archiver archiver,
425                                   final AssemblerConfigurationSource configSource )
426         throws AssemblyFormattingException, ArchiveCreationException
427     {
428         final File source = depArtifact.getFile();
429 
430         String outputDirectory = dependencySet.getOutputDirectory();
431 
432         outputDirectory =
433             AssemblyFormatUtils.getOutputDirectory( outputDirectory, configSource.getProject(), moduleProject,
434                                                     depProject, depProject.getBuild().getFinalName(), configSource );
435 
436         final String destName =
437             AssemblyFormatUtils.evaluateFileNameMapping( dependencySet.getOutputFileNameMapping(), depArtifact,
438                                                          configSource.getProject(), moduleProject, moduleArtifact,
439                                                          depProject, configSource );
440 
441         String target;
442 
443         // omit the last char if ends with / or \\
444         if ( outputDirectory.endsWith( "/" ) || outputDirectory.endsWith( "\\" ) )
445         {
446             target = outputDirectory + destName;
447         }
448         else
449         {
450             target = outputDirectory + "/" + destName;
451         }
452 
453         try
454         {
455             final int mode = TypeConversionUtils.modeToInt( dependencySet.getFileMode(), logger );
456             if ( mode > -1 )
457             {
458                 archiver.addFile( source, target, mode );
459             }
460             else
461             {
462                 archiver.addFile( source, target );
463             }
464         }
465         catch ( final ArchiverException e )
466         {
467             throw new ArchiveCreationException( "Error adding file to archive: " + e.getMessage(), e );
468         }
469     }
470 
471     public List<DependencySet> getDependencySets()
472     {
473         return dependencySets;
474     }
475 
476     public Logger getLogger()
477     {
478         return logger;
479     }
480 
481     public void setDefaultOutputDirectory( final String defaultOutputDirectory )
482     {
483         this.defaultOutputDirectory = defaultOutputDirectory;
484     }
485 
486     public void setDefaultOutputFileNameMapping( final String defaultOutputFileNameMapping )
487     {
488         this.defaultOutputFileNameMapping = defaultOutputFileNameMapping;
489     }
490 
491     public void setModuleProject( final MavenProject moduleProject )
492     {
493         this.moduleProject = moduleProject;
494     }
495 
496     public void setModuleArtifact( final Artifact moduleArtifact )
497     {
498         this.moduleArtifact = moduleArtifact;
499     }
500 }