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