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