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