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 }