View Javadoc
1   package org.apache.maven.plugins.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.nio.charset.Charset;
24  import java.util.ArrayList;
25  import java.util.Collections;
26  import java.util.LinkedHashSet;
27  import java.util.List;
28  import java.util.Set;
29  
30  import org.apache.maven.artifact.Artifact;
31  import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
32  import org.apache.maven.model.Dependency;
33  import org.apache.maven.model.Model;
34  import org.apache.maven.plugins.assembly.AssemblerConfigurationSource;
35  import org.apache.maven.plugins.assembly.InvalidAssemblerConfigurationException;
36  import org.apache.maven.plugins.assembly.archive.ArchiveCreationException;
37  import org.apache.maven.plugins.assembly.format.AssemblyFormattingException;
38  import org.apache.maven.plugins.assembly.format.ReaderFormatter;
39  import org.apache.maven.plugins.assembly.model.DependencySet;
40  import org.apache.maven.plugins.assembly.model.UnpackOptions;
41  import org.apache.maven.plugins.assembly.utils.AssemblyFormatUtils;
42  import org.apache.maven.plugins.assembly.utils.FilterUtils;
43  import org.apache.maven.plugins.assembly.utils.TypeConversionUtils;
44  import org.apache.maven.project.MavenProject;
45  import org.apache.maven.project.ProjectBuilder;
46  import org.apache.maven.project.ProjectBuildingException;
47  import org.apache.maven.project.ProjectBuildingRequest;
48  import org.apache.maven.project.ProjectBuildingResult;
49  import org.apache.maven.shared.artifact.filter.resolve.ScopeFilter;
50  import org.apache.maven.shared.artifact.filter.resolve.transform.ArtifactIncludeFilterTransformer;
51  import org.codehaus.plexus.archiver.Archiver;
52  import org.codehaus.plexus.archiver.ArchiverException;
53  import org.codehaus.plexus.components.io.functions.InputStreamTransformer;
54  import org.codehaus.plexus.interpolation.fixed.FixedStringSearchInterpolator;
55  import org.codehaus.plexus.logging.Logger;
56  
57  /**
58   * @version $Id: AddDependencySetsTask.html 1016737 2017-08-13 12:01:54Z khmarbaise $
59   */
60  public class AddDependencySetsTask
61  {
62  
63      private static final List<String> NON_ARCHIVE_DEPENDENCY_TYPES;
64  
65      static
66      {
67          final List<String> nonArch = new ArrayList<String>();
68  
69          nonArch.add( "pom" );
70  
71          NON_ARCHIVE_DEPENDENCY_TYPES = Collections.unmodifiableList( nonArch );
72      }
73  
74      private final List<DependencySet> dependencySets;
75  
76      private final Logger logger;
77  
78      private final MavenProject project;
79  
80      private final ProjectBuilder projectBuilder1;
81  
82      private final Set<Artifact> resolvedArtifacts;
83  
84      private MavenProject moduleProject;
85  
86      private String defaultOutputDirectory;
87  
88      private String defaultOutputFileNameMapping;
89  
90      private Artifact moduleArtifact;
91  
92  
93      public AddDependencySetsTask( final List<DependencySet> dependencySets, final Set<Artifact> resolvedArtifacts,
94                                    final MavenProject project, ProjectBuilder projectBuilder, final Logger logger )
95      {
96          this.dependencySets = dependencySets;
97          this.resolvedArtifacts = resolvedArtifacts;
98          this.project = project;
99          this.projectBuilder1 = projectBuilder;
100         this.logger = logger;
101     }
102 
103     public void execute( final Archiver archiver, final AssemblerConfigurationSource configSource )
104         throws ArchiveCreationException, AssemblyFormattingException, InvalidAssemblerConfigurationException
105     {
106         if ( ( dependencySets == null ) || dependencySets.isEmpty() )
107         {
108             logger.debug( "No dependency sets specified." );
109             return;
110         }
111 
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         if ( !unpackTransformsContent( dependencySet ) && dependencyArtifacts.size() > 1 )
139         {
140             checkMultiArtifactOutputConfig( dependencySet );
141         }
142 
143         logger.debug( "Adding " + dependencyArtifacts.size() + " dependency artifacts." );
144 
145         InputStreamTransformer fileSetTransformers =
146             isUnpackWithOptions( dependencySet )
147                 ? ReaderFormatter.getFileSetTransformers( configSource, dependencySet.getUnpackOptions().isFiltered(),
148                                                           dependencySet.getUnpackOptions().getLineEnding() )
149                 : null;
150 
151         for ( final Artifact depArtifact : dependencyArtifacts )
152         {
153             ProjectBuildingRequest pbr = getProjectBuildingRequest( configSource );
154             MavenProject depProject;
155             try
156             {
157                 ProjectBuildingResult build = projectBuilder1.build( depArtifact, pbr );
158                 depProject = build.getProject();
159             }
160             catch ( final ProjectBuildingException e )
161             {
162                 logger.debug(
163                     "Error retrieving POM of module-dependency: " + depArtifact.getId() + "; Reason: " + e.getMessage()
164                         + "\n\nBuilding stub project instance." );
165 
166                 depProject = buildProjectStub( depArtifact );
167             }
168 
169             if ( NON_ARCHIVE_DEPENDENCY_TYPES.contains( depArtifact.getType() ) )
170             {
171                 addNonArchiveDependency( depArtifact, depProject, dependencySet, archiver, configSource );
172             }
173             else
174             {
175                 addNormalArtifact( dependencySet, depArtifact, depProject, archiver, configSource,
176                                    fileSetTransformers );
177             }
178         }
179     }
180 
181     private ProjectBuildingRequest getProjectBuildingRequest( AssemblerConfigurationSource configSource )
182     {
183         return configSource.getMavenSession().getProjectBuildingRequest();
184     }
185 
186     private boolean isUnpackWithOptions( DependencySet dependencySet )
187     {
188         return dependencySet.isUnpack() && dependencySet.getUnpackOptions() != null;
189     }
190 
191     private boolean unpackTransformsContent( DependencySet dependencySet )
192     {
193         return isUnpackWithOptions( dependencySet ) && isConentModifyingOption( dependencySet.getUnpackOptions() );
194     }
195 
196     private boolean isConentModifyingOption( UnpackOptions opts )
197     {
198         return ( opts.isFiltered() || opts.getLineEnding() != null );
199     }
200 
201     private void checkMultiArtifactOutputConfig( final DependencySet dependencySet )
202     {
203         String dir = dependencySet.getOutputDirectory();
204         if ( dir == null )
205         {
206             dir = defaultOutputDirectory;
207         }
208 
209         String mapping = dependencySet.getOutputFileNameMapping();
210         if ( mapping == null )
211         {
212             mapping = defaultOutputFileNameMapping;
213         }
214 
215         if ( ( dir == null || !dir.contains( "${" ) ) && ( mapping == null || !mapping.contains( "${" ) ) )
216         {
217             logger.warn( "NOTE: Your assembly specifies a dependencySet that matches multiple artifacts, but "
218                              + "specifies a concrete output format. THIS MAY RESULT IN ONE OR MORE ARTIFACTS BEING "
219                              + "OBSCURED!\n\n" + "Output directory: '" + dir + "'\nOutput filename mapping: '" + mapping
220                              + "'" );
221         }
222     }
223 
224     private void addNormalArtifact( final DependencySet dependencySet, final Artifact depArtifact,
225                                     final MavenProject depProject, final Archiver archiver,
226                                     final AssemblerConfigurationSource configSource,
227                                     InputStreamTransformer fileSetTransformers )
228         throws AssemblyFormattingException, ArchiveCreationException
229     {
230         logger.debug( "Adding dependency artifact " + depArtifact.getId() + "." );
231 
232         String encoding = isUnpackWithOptions( dependencySet ) ? dependencySet.getUnpackOptions().getEncoding() : null;
233         Charset charset = encoding != null ? Charset.forName( encoding ) : null;
234         final AddArtifactTask task = new AddArtifactTask( depArtifact, logger, fileSetTransformers, charset );
235 
236         task.setProject( depProject );
237         task.setModuleProject( moduleProject );
238         task.setModuleArtifact( moduleArtifact );
239         task.setOutputDirectory( dependencySet.getOutputDirectory(), defaultOutputDirectory );
240         task.setFileNameMapping( dependencySet.getOutputFileNameMapping(), defaultOutputFileNameMapping );
241 
242         final int dirMode = TypeConversionUtils.modeToInt( dependencySet.getDirectoryMode(), logger );
243         if ( dirMode != -1 )
244         {
245             task.setDirectoryMode( dirMode );
246         }
247 
248         final int fileMode = TypeConversionUtils.modeToInt( dependencySet.getFileMode(), logger );
249         if ( fileMode != -1 )
250         {
251             task.setFileMode( fileMode );
252         }
253 
254         task.setUnpack( dependencySet.isUnpack() );
255 
256         final UnpackOptions opts = dependencySet.getUnpackOptions();
257         if ( isUnpackWithOptions( dependencySet ) )
258         {
259             task.setIncludes( opts.getIncludes() );
260             task.setExcludes( opts.getExcludes() );
261         }
262 
263         task.execute( archiver, configSource );
264 
265     }
266 
267     private MavenProject buildProjectStub( final Artifact depArtifact )
268     {
269         final Model model = new Model();
270         model.setGroupId( depArtifact.getGroupId() );
271         model.setArtifactId( depArtifact.getArtifactId() );
272         model.setVersion( depArtifact.getBaseVersion() );
273         model.setPackaging( depArtifact.getType() );
274 
275         model.setDescription( "Stub for " + depArtifact.getId() );
276 
277         final MavenProject project = new MavenProject( model );
278         project.setArtifact( depArtifact );
279 
280         return project;
281     }
282 
283     Set<Artifact> resolveDependencyArtifacts( final DependencySet dependencySet )
284         throws InvalidAssemblerConfigurationException
285     {
286         final Set<Artifact> dependencyArtifacts = new LinkedHashSet<Artifact>();
287         if ( resolvedArtifacts != null )
288         {
289             dependencyArtifacts.addAll( resolvedArtifacts );
290         }
291 
292         if ( dependencySet.isUseProjectArtifact() )
293         {
294             final Artifact projectArtifact = project.getArtifact();
295             if ( ( projectArtifact != null ) && ( projectArtifact.getFile() != null ) )
296             {
297                 dependencyArtifacts.add( projectArtifact );
298             }
299             else
300             {
301                 logger.warn( "Cannot include project artifact: " + projectArtifact
302                                  + "; it doesn't have an associated file or directory." );
303             }
304         }
305 
306         if ( dependencySet.isUseProjectAttachments() )
307         {
308             final List<Artifact> attachments = project.getAttachedArtifacts();
309             if ( attachments != null )
310             {
311                 for ( final Artifact attachment : attachments )
312                 {
313                     if ( attachment.getFile() != null )
314                     {
315                         dependencyArtifacts.add( attachment );
316                     }
317                     else
318                     {
319                         logger.warn(
320                             "Cannot include attached artifact: " + project.getId() + " for project: " + project.getId()
321                                 + "; it doesn't have an associated file or directory." );
322                     }
323                 }
324             }
325         }
326 
327         if ( dependencySet.isUseTransitiveFiltering() )
328         {
329             logger.debug( "Filtering dependency artifacts USING transitive dependency path information." );
330         }
331         else
332         {
333             logger.debug( "Filtering dependency artifacts WITHOUT transitive dependency path information." );
334         }
335 
336         final ScopeFilter scopeFilter = FilterUtils.newScopeFilter( dependencySet.getScope() );
337 
338         final ArtifactFilter filter = new ArtifactIncludeFilterTransformer().transform( scopeFilter );
339         
340         FilterUtils.filterArtifacts( dependencyArtifacts, dependencySet.getIncludes(), dependencySet.getExcludes(),
341                                      dependencySet.isUseStrictFiltering(), dependencySet.isUseTransitiveFiltering(),
342                                      logger, filter );
343 
344         return dependencyArtifacts;
345     }
346 
347     private void addNonArchiveDependency( final Artifact depArtifact, final MavenProject depProject,
348                                           final DependencySet dependencySet, final Archiver archiver,
349                                           final AssemblerConfigurationSource configSource )
350         throws AssemblyFormattingException, ArchiveCreationException
351     {
352         final File source = depArtifact.getFile();
353 
354         String outputDirectory = dependencySet.getOutputDirectory();
355 
356         FixedStringSearchInterpolator moduleProjectInterpolator =
357             AssemblyFormatUtils.moduleProjectInterpolator( moduleProject );
358         FixedStringSearchInterpolator artifactProjectInterpolator =
359             AssemblyFormatUtils.artifactProjectInterpolator( depProject );
360         outputDirectory =
361             AssemblyFormatUtils.getOutputDirectory( outputDirectory, depProject.getBuild().getFinalName(), configSource,
362                                                     moduleProjectInterpolator, artifactProjectInterpolator );
363 
364         final String destName =
365             AssemblyFormatUtils.evaluateFileNameMapping( dependencySet.getOutputFileNameMapping(), depArtifact,
366                                                          configSource.getProject(), moduleArtifact, configSource,
367                                                          moduleProjectInterpolator, artifactProjectInterpolator );
368 
369         String target;
370 
371         // omit the last char if ends with / or \\
372         if ( outputDirectory.endsWith( "/" ) || outputDirectory.endsWith( "\\" ) )
373         {
374             target = outputDirectory + destName;
375         }
376         else
377         {
378             target = outputDirectory + "/" + destName;
379         }
380 
381         try
382         {
383             final int mode = TypeConversionUtils.modeToInt( dependencySet.getFileMode(), logger );
384             if ( mode > -1 )
385             {
386                 archiver.addFile( source, target, mode );
387             }
388             else
389             {
390                 archiver.addFile( source, target );
391             }
392         }
393         catch ( final ArchiverException e )
394         {
395             throw new ArchiveCreationException( "Error adding file to archive: " + e.getMessage(), e );
396         }
397     }
398 
399     public List<DependencySet> getDependencySets()
400     {
401         return dependencySets;
402     }
403 
404     public Logger getLogger()
405     {
406         return logger;
407     }
408 
409     public void setDefaultOutputDirectory( final String defaultOutputDirectory )
410     {
411         this.defaultOutputDirectory = defaultOutputDirectory;
412     }
413 
414     public void setDefaultOutputFileNameMapping( final String defaultOutputFileNameMapping )
415     {
416         this.defaultOutputFileNameMapping = defaultOutputFileNameMapping;
417     }
418 
419     public void setModuleProject( final MavenProject moduleProject )
420     {
421         this.moduleProject = moduleProject;
422     }
423 
424     public void setModuleArtifact( final Artifact moduleArtifact )
425     {
426         this.moduleArtifact = moduleArtifact;
427     }
428 }