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;
20
21 import javax.inject.Inject;
22 import javax.inject.Named;
23
24 import java.io.File;
25 import java.io.IOException;
26 import java.io.StringReader;
27 import java.lang.reflect.InvocationTargetException;
28 import java.lang.reflect.Method;
29 import java.nio.file.attribute.FileTime;
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.List;
33 import java.util.Map;
34
35 import org.apache.maven.plugins.assembly.AssemblerConfigurationSource;
36 import org.apache.maven.plugins.assembly.InvalidAssemblerConfigurationException;
37 import org.apache.maven.plugins.assembly.archive.archiver.AssemblyProxyArchiver;
38 import org.apache.maven.plugins.assembly.archive.phase.AssemblyArchiverPhase;
39 import org.apache.maven.plugins.assembly.archive.phase.AssemblyArchiverPhaseComparator;
40 import org.apache.maven.plugins.assembly.artifact.DependencyResolutionException;
41 import org.apache.maven.plugins.assembly.filter.ComponentsXmlArchiverFileFilter;
42 import org.apache.maven.plugins.assembly.filter.ContainerDescriptorHandler;
43 import org.apache.maven.plugins.assembly.format.AssemblyFormattingException;
44 import org.apache.maven.plugins.assembly.internal.DebugConfigurationListener;
45 import org.apache.maven.plugins.assembly.interpolation.AssemblyExpressionEvaluator;
46 import org.apache.maven.plugins.assembly.model.Assembly;
47 import org.apache.maven.plugins.assembly.model.ContainerDescriptorHandlerConfig;
48 import org.apache.maven.plugins.assembly.utils.AssemblyFileUtils;
49 import org.apache.maven.plugins.assembly.utils.AssemblyFormatUtils;
50 import org.codehaus.plexus.PlexusContainer;
51 import org.codehaus.plexus.archiver.ArchiveFinalizer;
52 import org.codehaus.plexus.archiver.Archiver;
53 import org.codehaus.plexus.archiver.ArchiverException;
54 import org.codehaus.plexus.archiver.diags.DryRunArchiver;
55 import org.codehaus.plexus.archiver.filters.JarSecurityFileSelector;
56 import org.codehaus.plexus.archiver.jar.JarArchiver;
57 import org.codehaus.plexus.archiver.manager.ArchiverManager;
58 import org.codehaus.plexus.archiver.manager.NoSuchArchiverException;
59 import org.codehaus.plexus.archiver.tar.TarArchiver;
60 import org.codehaus.plexus.archiver.tar.TarLongFileMode;
61 import org.codehaus.plexus.archiver.war.WarArchiver;
62 import org.codehaus.plexus.archiver.zip.AbstractZipArchiver;
63 import org.codehaus.plexus.component.configurator.ComponentConfigurationException;
64 import org.codehaus.plexus.component.configurator.ComponentConfigurator;
65 import org.codehaus.plexus.component.configurator.ConfigurationListener;
66 import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
67 import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
68 import org.codehaus.plexus.components.io.fileselectors.FileSelector;
69 import org.codehaus.plexus.configuration.PlexusConfiguration;
70 import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration;
71 import org.codehaus.plexus.util.StringUtils;
72 import org.codehaus.plexus.util.xml.Xpp3Dom;
73 import org.codehaus.plexus.util.xml.Xpp3DomBuilder;
74 import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
75 import org.slf4j.Logger;
76 import org.slf4j.LoggerFactory;
77
78 import static java.util.Objects.requireNonNull;
79
80
81
82
83
84
85
86
87
88
89 @Named
90 public class DefaultAssemblyArchiver implements AssemblyArchiver {
91 private static final Logger LOGGER = LoggerFactory.getLogger(DefaultAssemblyArchiver.class);
92
93 private final ArchiverManager archiverManager;
94
95 private final List<AssemblyArchiverPhase> assemblyPhases;
96
97 @SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
98 private final Map<String, ContainerDescriptorHandler> containerDescriptorHandlers;
99
100 private final PlexusContainer container;
101
102 @Inject
103 public DefaultAssemblyArchiver(
104 ArchiverManager archiverManager,
105 List<AssemblyArchiverPhase> assemblyPhases,
106 Map<String, ContainerDescriptorHandler> containerDescriptorHandlers,
107 PlexusContainer container) {
108 this.archiverManager = requireNonNull(archiverManager);
109 this.assemblyPhases = requireNonNull(assemblyPhases);
110 this.containerDescriptorHandlers = requireNonNull(containerDescriptorHandlers);
111 this.container = requireNonNull(container);
112 }
113
114 private List<AssemblyArchiverPhase> sortedPhases() {
115 List<AssemblyArchiverPhase> sorted = new ArrayList<>(assemblyPhases);
116 Collections.sort(sorted, new AssemblyArchiverPhaseComparator());
117 return sorted;
118 }
119
120
121
122
123 @Override
124 public File createArchive(
125 final Assembly assembly,
126 final String fullName,
127 final String format,
128 final AssemblerConfigurationSource configSource,
129 boolean recompressZippedFiles,
130 String mergeManifestMode,
131 FileTime outputTimestamp)
132 throws ArchiveCreationException, AssemblyFormattingException, InvalidAssemblerConfigurationException {
133 validate(assembly);
134
135 String filename = fullName;
136 if (!configSource.isIgnoreDirFormatExtensions() || !format.startsWith("dir")) {
137 filename += "." + format;
138 }
139
140 AssemblyFileUtils.verifyTempDirectoryAvailability(configSource.getTemporaryRootDirectory());
141
142 final File outputDirectory = configSource.getOutputDirectory();
143
144 final File destFile = new File(outputDirectory, filename);
145
146 try {
147 final String finalName = configSource.getFinalName();
148 final String specifiedBasedir = assembly.getBaseDirectory();
149
150 String basedir = finalName;
151
152 if (specifiedBasedir != null) {
153 basedir = AssemblyFormatUtils.getOutputDirectory(
154 specifiedBasedir,
155 finalName,
156 configSource,
157 AssemblyFormatUtils.moduleProjectInterpolator(configSource.getProject()),
158 AssemblyFormatUtils.artifactProjectInterpolator(null));
159 }
160
161 final List<ContainerDescriptorHandler> containerHandlers =
162 selectContainerDescriptorHandlers(assembly.getContainerDescriptorHandlers(), configSource);
163
164 final Archiver archiver = createArchiver(
165 format,
166 assembly.isIncludeBaseDirectory(),
167 basedir,
168 configSource,
169 containerHandlers,
170 recompressZippedFiles,
171 mergeManifestMode,
172 outputTimestamp);
173
174 archiver.setDestFile(destFile);
175
176 for (AssemblyArchiverPhase phase : sortedPhases()) {
177 phase.execute(assembly, archiver, configSource);
178 }
179
180 archiver.createArchive();
181 } catch (final ArchiverException | IOException e) {
182 throw new ArchiveCreationException(
183 "Error creating assembly archive " + assembly.getId() + ": " + e.getMessage(), e);
184 } catch (final NoSuchArchiverException e) {
185 throw new ArchiveCreationException(
186 "Unable to obtain archiver for extension '" + format + "', for assembly: '" + assembly.getId()
187 + "'",
188 e);
189 } catch (final DependencyResolutionException e) {
190 throw new ArchiveCreationException(
191 "Unable to resolve dependencies for assembly '" + assembly.getId() + "'", e);
192 }
193
194 return destFile;
195 }
196
197 private void validate(final Assembly assembly) throws InvalidAssemblerConfigurationException {
198 if (assembly.getId() == null || assembly.getId().trim().length() < 1) {
199 throw new InvalidAssemblerConfigurationException("Assembly ID must be present and non-empty.");
200 }
201 }
202
203
204 private List<ContainerDescriptorHandler> selectContainerDescriptorHandlers(
205 List<ContainerDescriptorHandlerConfig> requestedContainerDescriptorHandlers,
206 final AssemblerConfigurationSource configSource)
207 throws InvalidAssemblerConfigurationException
208
209 {
210 LOGGER.debug("All known ContainerDescriptorHandler components: "
211 + (containerDescriptorHandlers == null
212 ? "none; map is null."
213 : "" + containerDescriptorHandlers.keySet()));
214
215 if (requestedContainerDescriptorHandlers == null) {
216 requestedContainerDescriptorHandlers = new ArrayList<>();
217 }
218
219 final List<ContainerDescriptorHandler> handlers = new ArrayList<>();
220 final List<String> hints = new ArrayList<>();
221
222 if (!requestedContainerDescriptorHandlers.isEmpty()) {
223 for (final ContainerDescriptorHandlerConfig config : requestedContainerDescriptorHandlers) {
224 final String hint = config.getHandlerName();
225 final ContainerDescriptorHandler handler = containerDescriptorHandlers.get(hint);
226
227 if (handler == null) {
228 throw new InvalidAssemblerConfigurationException(
229 "Cannot find ContainerDescriptorHandler with hint: " + hint);
230 }
231
232 LOGGER.debug("Found container descriptor handler with hint: " + hint + " (component: " + handler + ")");
233
234 if (config.getConfiguration() != null) {
235 LOGGER.debug("Configuring handler with:\n\n" + config.getConfiguration() + "\n\n");
236
237 configureContainerDescriptorHandler(handler, (Xpp3Dom) config.getConfiguration(), configSource);
238 }
239
240 handlers.add(handler);
241 hints.add(hint);
242 }
243 }
244
245 if (!hints.contains("plexus")) {
246 handlers.add(new ComponentsXmlArchiverFileFilter());
247 }
248
249 return handlers;
250 }
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266 protected Archiver createArchiver(
267 final String format,
268 final boolean includeBaseDir,
269 final String finalName,
270 final AssemblerConfigurationSource configSource,
271 final List<ContainerDescriptorHandler> containerHandlers,
272 boolean recompressZippedFiles,
273 String mergeManifestMode,
274 FileTime outputTimestamp)
275 throws NoSuchArchiverException {
276 Archiver archiver;
277
278
279 if ("tzst".equals(format)) {
280 archiver = createTarZstArchiver();
281 } else {
282 archiver = archiverManager.getArchiver(format);
283 }
284
285 if (archiver instanceof TarArchiver) {
286 ((TarArchiver) archiver).setLongfile(TarLongFileMode.valueOf(configSource.getTarLongFileMode()));
287 }
288
289 if (archiver instanceof WarArchiver) {
290 ((WarArchiver) archiver).setExpectWebXml(false);
291 }
292
293 if (archiver instanceof AbstractZipArchiver) {
294 ((AbstractZipArchiver) archiver).setRecompressAddedZips(recompressZippedFiles);
295 }
296
297 final List<FileSelector> extraSelectors = new ArrayList<>();
298 final List<ArchiveFinalizer> extraFinalizers = new ArrayList<>();
299 if (archiver instanceof JarArchiver) {
300 configureJarArchiver((JarArchiver) archiver, mergeManifestMode);
301
302 extraSelectors.add(new JarSecurityFileSelector());
303
304 extraFinalizers.add(new ManifestCreationFinalizer(
305 configSource.getMavenSession(),
306 configSource.getProject(),
307 configSource.getJarArchiveConfiguration()));
308 }
309
310 if (configSource.getArchiverConfig() != null) {
311 configureArchiver(archiver, configSource);
312 }
313
314 String prefix = "";
315 if (includeBaseDir) {
316 prefix = finalName;
317 }
318
319 archiver = new AssemblyProxyArchiver(
320 prefix,
321 archiver,
322 containerHandlers,
323 extraSelectors,
324 extraFinalizers,
325 configSource.getWorkingDirectory());
326 if (configSource.isDryRun()) {
327 archiver = new DryRunArchiver(archiver, LOGGER);
328 }
329
330 archiver.setIgnorePermissions(configSource.isIgnorePermissions());
331 archiver.setForced(!configSource.isUpdateOnly());
332
333
334 if (outputTimestamp != null) {
335 archiver.configureReproducibleBuild(outputTimestamp);
336 }
337
338 if (configSource.getOverrideUid() != null) {
339 archiver.setOverrideUid(configSource.getOverrideUid());
340 }
341 if (StringUtils.isNotBlank(configSource.getOverrideUserName())) {
342 archiver.setOverrideUserName(StringUtils.trim(configSource.getOverrideUserName()));
343 }
344 if (configSource.getOverrideGid() != null) {
345 archiver.setOverrideGid(configSource.getOverrideGid());
346 }
347 if (StringUtils.isNotBlank(configSource.getOverrideGroupName())) {
348 archiver.setOverrideGroupName(StringUtils.trim(configSource.getOverrideGroupName()));
349 }
350
351 return archiver;
352 }
353
354 private void configureJarArchiver(JarArchiver archiver, String mergeManifestMode) {
355
356 if (mergeManifestMode != null) {
357 archiver.setFilesetmanifest(JarArchiver.FilesetManifestConfig.valueOf(mergeManifestMode));
358 }
359
360 archiver.setMinimalDefaultManifest(true);
361 }
362
363 private void configureContainerDescriptorHandler(
364 final ContainerDescriptorHandler handler,
365 final Xpp3Dom config,
366 final AssemblerConfigurationSource configSource)
367 throws InvalidAssemblerConfigurationException {
368 LOGGER.debug("Configuring handler: '" + handler.getClass().getName() + "' -->");
369
370 try {
371 configureComponent(handler, config, configSource);
372 } catch (final ComponentConfigurationException e) {
373 throw new InvalidAssemblerConfigurationException(
374 "Failed to configure handler: " + handler.getClass().getName(), e);
375 } catch (final ComponentLookupException e) {
376 throw new InvalidAssemblerConfigurationException(
377 "Failed to lookup configurator for setup of handler: "
378 + handler.getClass().getName(),
379 e);
380 }
381
382 LOGGER.debug("-- end configuration --");
383 }
384
385 private void configureArchiver(final Archiver archiver, final AssemblerConfigurationSource configSource) {
386 Xpp3Dom config;
387 try {
388 config = Xpp3DomBuilder.build(new StringReader(configSource.getArchiverConfig()));
389 } catch (final XmlPullParserException | IOException e) {
390 throw new ArchiverException(
391 "Failed to parse archiver configuration for: "
392 + archiver.getClass().getName(),
393 e);
394 }
395
396 LOGGER.debug("Configuring archiver: '" + archiver.getClass().getName() + "' -->");
397
398 try {
399 configureComponent(archiver, config, configSource);
400 } catch (final ComponentConfigurationException e) {
401 throw new ArchiverException(
402 "Failed to configure archiver: " + archiver.getClass().getName(), e);
403 } catch (final ComponentLookupException e) {
404 throw new ArchiverException(
405 "Failed to lookup configurator for setup of archiver: "
406 + archiver.getClass().getName(),
407 e);
408 }
409
410 LOGGER.debug("-- end configuration --");
411 }
412
413 private void configureComponent(
414 final Object component, final Xpp3Dom config, final AssemblerConfigurationSource configSource)
415 throws ComponentLookupException, ComponentConfigurationException {
416 final ComponentConfigurator configurator = container.lookup(ComponentConfigurator.class, "basic");
417
418 final ConfigurationListener listener = new DebugConfigurationListener(LOGGER);
419
420 final ExpressionEvaluator expressionEvaluator = new AssemblyExpressionEvaluator(configSource);
421
422 final XmlPlexusConfiguration configuration = new XmlPlexusConfiguration(config);
423
424 final Object[] containerRealm = getContainerRealm();
425
426
427
428
429
430 try {
431 final Method configureComponent = ComponentConfigurator.class.getMethod(
432 "configureComponent",
433 Object.class,
434 PlexusConfiguration.class,
435 ExpressionEvaluator.class,
436 (Class<?>) containerRealm[1],
437 ConfigurationListener.class);
438
439 configureComponent.invoke(
440 configurator, component, configuration, expressionEvaluator, containerRealm[0], listener);
441 } catch (final NoSuchMethodException | IllegalAccessException e) {
442 throw new RuntimeException(e);
443 } catch (final InvocationTargetException e) {
444 if (e.getCause() instanceof ComponentConfigurationException) {
445 throw (ComponentConfigurationException) e.getCause();
446 }
447 throw new RuntimeException(e.getCause());
448 }
449 }
450
451 private Object[] getContainerRealm() {
452
453
454
455
456 try {
457 final Method getContainerRealm = container.getClass().getMethod("getContainerRealm");
458 return new Object[] {getContainerRealm.invoke(container), getContainerRealm.getReturnType()};
459 } catch (final NoSuchMethodException | IllegalAccessException e) {
460 throw new RuntimeException(e);
461 } catch (final InvocationTargetException e) {
462 throw new RuntimeException(e.getCause());
463 }
464 }
465
466 protected Archiver createTarZstArchiver() throws NoSuchArchiverException {
467 final TarArchiver tarArchiver = (TarArchiver) archiverManager.getArchiver("tar");
468 tarArchiver.setCompression(TarArchiver.TarCompressionMethod.zstd);
469 return tarArchiver;
470 }
471 }