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