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