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