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