1   package org.apache.maven.plugin.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.plugin.assembly.AssemblerConfigurationSource;
24  import org.apache.maven.plugin.assembly.InvalidAssemblerConfigurationException;
25  import org.apache.maven.plugin.assembly.archive.archiver.AssemblyProxyArchiver;
26  import org.apache.maven.plugin.assembly.archive.phase.AssemblyArchiverPhase;
27  import org.apache.maven.plugin.assembly.archive.phase.AssemblyArchiverPhaseComparator;
28  import org.apache.maven.plugin.assembly.artifact.DependencyResolutionException;
29  import org.apache.maven.plugin.assembly.filter.ComponentsXmlArchiverFileFilter;
30  import org.apache.maven.plugin.assembly.filter.ContainerDescriptorHandler;
31  import org.apache.maven.plugin.assembly.format.AssemblyFormattingException;
32  import org.apache.maven.plugin.assembly.interpolation.AssemblyExpressionEvaluator;
33  import org.apache.maven.plugin.assembly.model.Assembly;
34  import org.apache.maven.plugin.assembly.model.ContainerDescriptorHandlerConfig;
35  import org.apache.maven.plugin.assembly.utils.AssemblyFileUtils;
36  import org.apache.maven.plugin.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.List;
77  import java.util.Map;
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  @Component( role = AssemblyArchiver.class )
88  public class DefaultAssemblyArchiver
89      extends AbstractLogEnabled
90      implements AssemblyArchiver, Contextualizable
91  {
92  
93      @Requirement
94      private ArchiverManager archiverManager;
95  
96      @Requirement( role = AssemblyArchiverPhase.class )
97      private List<AssemblyArchiverPhase> assemblyPhases;
98  
99      @SuppressWarnings( "MismatchedQueryAndUpdateOfCollection" )
100     @Requirement( role = ContainerDescriptorHandler.class )
101     private Map<String, ContainerDescriptorHandler> containerDescriptorHandlers;
102 
103     private PlexusContainer container;
104 
105     @SuppressWarnings( "UnusedDeclaration" )
106     public DefaultAssemblyArchiver()
107     {
108     }
109 
110     
111 
112     
113 
114 
115 
116     protected DefaultAssemblyArchiver( final ArchiverManager archiverManager,
117                                        final List<AssemblyArchiverPhase> assemblyPhases )
118     {
119         this.archiverManager = archiverManager;
120         this.assemblyPhases = assemblyPhases;
121     }
122 
123     private List<AssemblyArchiverPhase> sortedPhases()
124     {
125         List<AssemblyArchiverPhase> sorted = new ArrayList<AssemblyArchiverPhase>( assemblyPhases );
126         Collections.sort( sorted, new AssemblyArchiverPhaseComparator() );
127         return sorted;
128     }
129 
130     
131 
132 
133     public File createArchive( final Assembly assembly, final String fullName, final String format,
134                                final AssemblerConfigurationSource configSource, boolean recompressZippedFiles )
135         throws ArchiveCreationException, AssemblyFormattingException, InvalidAssemblerConfigurationException
136     {
137         validate( assembly );
138 
139         String filename = fullName;
140         if ( !configSource.isIgnoreDirFormatExtensions() || !format.startsWith( "dir" ) )
141         {
142             filename += "." + format;
143         }
144 
145         AssemblyFileUtils.verifyTempDirectoryAvailability( configSource.getTemporaryRootDirectory() );
146 
147         final File outputDirectory = configSource.getOutputDirectory();
148 
149         final File destFile = new File( outputDirectory, filename );
150 
151         try
152         {
153             final String finalName = configSource.getFinalName();
154             final String specifiedBasedir = assembly.getBaseDirectory();
155 
156             String basedir = finalName;
157 
158             if ( specifiedBasedir != null )
159             {
160                 basedir = AssemblyFormatUtils.getOutputDirectory( specifiedBasedir, finalName, configSource,
161                                                                   AssemblyFormatUtils.moduleProjectInterpolator(
162                                                                       configSource.getProject() ),
163                                                                   AssemblyFormatUtils.artifactProjectInterpolator(
164                                                                       null ) );
165             }
166 
167             final List<ContainerDescriptorHandler> containerHandlers =
168                 selectContainerDescriptorHandlers( assembly.getContainerDescriptorHandlers(), configSource );
169 
170             final Archiver archiver =
171                 createArchiver( format, assembly.isIncludeBaseDirectory(), basedir, configSource, containerHandlers,
172                                 recompressZippedFiles );
173 
174             archiver.setDestFile( destFile );
175 
176             for ( AssemblyArchiverPhase phase : sortedPhases() )
177             {
178                 phase.execute( assembly, archiver, configSource );
179             }
180 
181             archiver.createArchive();
182         }
183         catch ( final ArchiverException e )
184         {
185             throw new ArchiveCreationException(
186                 "Error creating assembly archive " + assembly.getId() + ": " + e.getMessage(), e );
187         }
188         catch ( final 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<ContainerDescriptorHandlerConfig>();
231         }
232 
233         final List<ContainerDescriptorHandler> handlers = new ArrayList<ContainerDescriptorHandler>();
234         final List<String> hints = new ArrayList<String>();
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     protected Archiver createArchiver( final String format, final boolean includeBaseDir, final String finalName,
286                                        final AssemblerConfigurationSource configSource,
287                                        final List<ContainerDescriptorHandler> containerHandlers,
288                                        boolean recompressZippedFiles )
289         throws NoSuchArchiverException
290     {
291         Archiver archiver;
292         if ( "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<FileSelector>();
311         final List<ArchiveFinalizer> extraFinalizers = new ArrayList<ArchiveFinalizer>();
312         if ( archiver instanceof JarArchiver )
313         {
314             extraSelectors.add( new JarSecurityFileSelector() );
315 
316             extraFinalizers.add(
317                 new ManifestCreationFinalizer( configSource.getMavenSession(), configSource.getProject(),
318                                                configSource.getJarArchiveConfiguration() ) );
319 
320         }
321 
322         if ( configSource.getArchiverConfig() != null )
323         {
324             configureArchiver( archiver, configSource );
325         }
326 
327         String prefix = "";
328         if ( includeBaseDir )
329         {
330             prefix = finalName;
331         }
332 
333         archiver = new AssemblyProxyArchiver( prefix, archiver, containerHandlers, extraSelectors, extraFinalizers,
334                                               configSource.getWorkingDirectory(), getLogger() );
335         if ( configSource.isDryRun() )
336         {
337             archiver = new DryRunArchiver( archiver, getLogger() );
338         }
339 
340         archiver.setUseJvmChmod( configSource.isUpdateOnly() );
341         archiver.setIgnorePermissions( configSource.isIgnorePermissions() );
342         archiver.setForced( !configSource.isUpdateOnly() );
343 
344         return archiver;
345     }
346 
347     private void configureContainerDescriptorHandler( final ContainerDescriptorHandler handler, final Xpp3Dom config,
348                                                       final AssemblerConfigurationSource configSource )
349         throws InvalidAssemblerConfigurationException
350     {
351         getLogger().debug( "Configuring handler: '" + handler.getClass().getName() + "' -->" );
352 
353         try
354         {
355             configureComponent( handler, config, configSource );
356         }
357         catch ( final ComponentConfigurationException e )
358         {
359             throw new InvalidAssemblerConfigurationException(
360                 "Failed to configure handler: " + handler.getClass().getName(), e );
361         }
362         catch ( final ComponentLookupException e )
363         {
364             throw new InvalidAssemblerConfigurationException(
365                 "Failed to lookup configurator for setup of handler: " + handler.getClass().getName(), e );
366         }
367 
368         getLogger().debug( "-- end configuration --" );
369     }
370 
371     private void configureArchiver( final Archiver archiver, final AssemblerConfigurationSource configSource )
372     {
373         Xpp3Dom config;
374         try
375         {
376             config = Xpp3DomBuilder.build( new StringReader( configSource.getArchiverConfig() ) );
377         }
378         catch ( final XmlPullParserException e )
379         {
380             throw new ArchiverException( "Failed to parse archiver configuration for: " + archiver.getClass().getName(),
381                                          e );
382         }
383         catch ( final IOException e )
384         {
385             throw new ArchiverException( "Failed to parse archiver configuration for: " + archiver.getClass().getName(),
386                                          e );
387         }
388 
389         getLogger().debug( "Configuring archiver: '" + archiver.getClass().getName() + "' -->" );
390 
391         try
392         {
393             configureComponent( archiver, config, configSource );
394         }
395         catch ( final ComponentConfigurationException e )
396         {
397             throw new ArchiverException( "Failed to configure archiver: " + archiver.getClass().getName(), e );
398         }
399         catch ( final ComponentLookupException e )
400         {
401             throw new ArchiverException(
402                 "Failed to lookup configurator for setup of archiver: " + archiver.getClass().getName(), e );
403         }
404 
405         getLogger().debug( "-- end configuration --" );
406     }
407 
408     private void configureComponent( final Object component, final Xpp3Dom config,
409                                      final AssemblerConfigurationSource configSource )
410         throws ComponentLookupException, ComponentConfigurationException
411     {
412         final ComponentConfigurator configurator =
413             (ComponentConfigurator) container.lookup( ComponentConfigurator.ROLE, "basic" );
414 
415         final ConfigurationListener listener = new DebugConfigurationListener( getLogger() );
416 
417         final ExpressionEvaluator expressionEvaluator = new AssemblyExpressionEvaluator( configSource );
418 
419         final XmlPlexusConfiguration configuration = new XmlPlexusConfiguration( config );
420 
421         final Object[] containerRealm = getContainerRealm();
422 
423         
424 
425 
426 
427         try
428         {
429             final Method configureComponent =
430                 ComponentConfigurator.class.getMethod( "configureComponent", Object.class, PlexusConfiguration.class,
431                                                        ExpressionEvaluator.class, (Class<?>) containerRealm[1],
432                                                        ConfigurationListener.class );
433 
434             configureComponent.invoke( configurator, component, configuration, expressionEvaluator, containerRealm[0],
435                                        listener );
436         }
437         catch ( final NoSuchMethodException e )
438         {
439             throw new RuntimeException( e );
440         }
441         catch ( final IllegalAccessException e )
442         {
443             throw new RuntimeException( e );
444         }
445         catch ( final InvocationTargetException e )
446         {
447             if ( e.getCause() instanceof ComponentConfigurationException )
448             {
449                 throw (ComponentConfigurationException) e.getCause();
450             }
451             throw new RuntimeException( e.getCause() );
452         }
453     }
454 
455     private Object[] getContainerRealm()
456     {
457         
458 
459 
460 
461         try
462         {
463             final Method getContainerRealm = container.getClass().getMethod( "getContainerRealm" );
464             return new Object[]{ getContainerRealm.invoke( container ), getContainerRealm.getReturnType() };
465         }
466         catch ( final NoSuchMethodException e )
467         {
468             throw new RuntimeException( e );
469         }
470         catch ( final IllegalAccessException e )
471         {
472             throw new RuntimeException( e );
473         }
474         catch ( final InvocationTargetException e )
475         {
476             throw new RuntimeException( e.getCause() );
477         }
478     }
479 
480     protected Archiver createWarArchiver()
481         throws NoSuchArchiverException
482     {
483         final WarArchiver warArchiver = (WarArchiver) archiverManager.getArchiver( "war" );
484         warArchiver.setIgnoreWebxml( false ); 
485 
486         return warArchiver;
487     }
488 
489     protected Archiver createTarArchiver( final String format, final TarLongFileMode tarLongFileMode )
490         throws NoSuchArchiverException
491     {
492         final TarArchiver tarArchiver = (TarArchiver) archiverManager.getArchiver( "tar" );
493         final int index = format.indexOf( '.' );
494         if ( index >= 0 )
495         {
496             TarArchiver.TarCompressionMethod tarCompressionMethod;
497             
498             
499             final String compression = format.substring( index + 1 );
500             if ( "gz".equals( compression ) )
501             {
502                 tarCompressionMethod = TarArchiver.TarCompressionMethod.gzip;
503             }
504             else if ( "bz2".equals( compression ) )
505             {
506                 tarCompressionMethod = TarArchiver.TarCompressionMethod.bzip2;
507             }
508             else
509             {
510                 
511                 throw new IllegalArgumentException( "Unknown compression format: " + compression );
512             }
513             tarArchiver.setCompression( tarCompressionMethod );
514         }
515         else if ( "tgz".equals( format ) )
516         {
517             tarArchiver.setCompression( TarArchiver.TarCompressionMethod.gzip );
518         }
519         else if ( "tbz2".equals( format ) )
520         {
521             tarArchiver.setCompression( TarArchiver.TarCompressionMethod.bzip2 );
522         }
523 
524         tarArchiver.setLongfile( tarLongFileMode );
525 
526         return tarArchiver;
527     }
528 
529     public void contextualize( final Context context )
530         throws ContextException
531     {
532         container = (PlexusContainer) context.get( PlexusConstants.PLEXUS_KEY );
533     }
534 
535     protected void setContainer( final PlexusContainer container )
536     {
537         this.container = container;
538     }
539 
540 }