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