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