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