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