1 package org.apache.maven.plugin.assembly.archive.task;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
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
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 }