View Javadoc

1   package org.apache.maven.plugin.assembly.io;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.io.FileInputStream;
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.io.InputStreamReader;
27  import java.io.Reader;
28  import java.io.StringWriter;
29  import java.io.UnsupportedEncodingException;
30  import java.util.ArrayList;
31  import java.util.Collections;
32  import java.util.Enumeration;
33  import java.util.HashMap;
34  import java.util.HashSet;
35  import java.util.Iterator;
36  import java.util.List;
37  import java.util.Map;
38  import java.util.Properties;
39  import java.util.Set;
40  
41  import org.apache.maven.execution.MavenSession;
42  import org.apache.maven.plugin.MojoExecutionException;
43  import org.apache.maven.plugin.MojoFailureException;
44  import org.apache.maven.plugin.assembly.AssemblerConfigurationSource;
45  import org.apache.maven.plugin.assembly.InvalidAssemblerConfigurationException;
46  import org.apache.maven.plugin.assembly.interpolation.AssemblyExpressionEvaluator;
47  import org.apache.maven.plugin.assembly.interpolation.AssemblyInterpolationException;
48  import org.apache.maven.plugin.assembly.interpolation.AssemblyInterpolator;
49  import org.apache.maven.plugin.assembly.model.Assembly;
50  import org.apache.maven.plugin.assembly.model.Component;
51  import org.apache.maven.plugin.assembly.model.ContainerDescriptorHandlerConfig;
52  import org.apache.maven.plugin.assembly.model.DependencySet;
53  import org.apache.maven.plugin.assembly.model.FileItem;
54  import org.apache.maven.plugin.assembly.model.FileSet;
55  import org.apache.maven.plugin.assembly.model.ModuleSet;
56  import org.apache.maven.plugin.assembly.model.Repository;
57  import org.apache.maven.plugin.assembly.model.io.xpp3.AssemblyXpp3Reader;
58  import org.apache.maven.plugin.assembly.model.io.xpp3.AssemblyXpp3Writer;
59  import org.apache.maven.plugin.assembly.model.io.xpp3.ComponentXpp3Reader;
60  import org.apache.maven.project.MavenProject;
61  import org.apache.maven.shared.io.location.ClasspathResourceLocatorStrategy;
62  import org.apache.maven.shared.io.location.FileLocatorStrategy;
63  import org.apache.maven.shared.io.location.Location;
64  import org.apache.maven.shared.io.location.Locator;
65  import org.apache.maven.shared.io.location.LocatorStrategy;
66  import org.codehaus.plexus.logging.AbstractLogEnabled;
67  import org.codehaus.plexus.logging.Logger;
68  import org.codehaus.plexus.logging.console.ConsoleLogger;
69  import org.codehaus.plexus.util.DirectoryScanner;
70  import org.codehaus.plexus.util.IOUtil;
71  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
72  
73  /**
74   * @version $Id: DefaultAssemblyReader.java 1163853 2011-08-31 22:42:32Z jdcasey $
75   */
76  @org.codehaus.plexus.component.annotations.Component( role = AssemblyReader.class )
77  public class DefaultAssemblyReader
78      extends AbstractLogEnabled
79      implements AssemblyReader
80  {
81  
82      public List<Assembly> readAssemblies( final AssemblerConfigurationSource configSource )
83          throws AssemblyReadException, InvalidAssemblerConfigurationException
84      {
85          final Locator locator = new Locator();
86  
87          final List<LocatorStrategy> strategies = new ArrayList<LocatorStrategy>();
88          strategies.add( new RelativeFileLocatorStrategy( configSource.getBasedir() ) );
89          strategies.add( new FileLocatorStrategy() );
90  
91          final List<LocatorStrategy> refStrategies = new ArrayList<LocatorStrategy>();
92          refStrategies.add( new PrefixedClasspathLocatorStrategy( "/assemblies/" ) );
93  
94          final List<Assembly> assemblies = new ArrayList<Assembly>();
95  
96          final String descriptor = configSource.getDescriptor();
97          final String descriptorId = configSource.getDescriptorId();
98          final String[] descriptors = configSource.getDescriptors();
99          final String[] descriptorRefs = configSource.getDescriptorReferences();
100         final File descriptorSourceDirectory = configSource.getDescriptorSourceDirectory();
101 
102         if ( descriptor != null )
103         {
104             locator.setStrategies( strategies );
105             addAssemblyFromDescriptor( descriptor, locator, configSource, assemblies );
106         }
107 
108         if ( descriptorId != null )
109         {
110             locator.setStrategies( refStrategies );
111             addAssemblyForDescriptorReference( descriptorId, configSource, assemblies );
112         }
113 
114         if ( ( descriptors != null ) && ( descriptors.length > 0 ) )
115         {
116             locator.setStrategies( strategies );
117             for ( int i = 0; i < descriptors.length; i++ )
118             {
119                 getLogger().info( "Reading assembly descriptor: " + descriptors[i] );
120                 addAssemblyFromDescriptor( descriptors[i], locator, configSource, assemblies );
121             }
122         }
123 
124         if ( ( descriptorRefs != null ) && ( descriptorRefs.length > 0 ) )
125         {
126             locator.setStrategies( refStrategies );
127             for ( int i = 0; i < descriptorRefs.length; i++ )
128             {
129                 addAssemblyForDescriptorReference( descriptorRefs[i], configSource, assemblies );
130             }
131         }
132 
133         if ( ( descriptorSourceDirectory != null ) && descriptorSourceDirectory.isDirectory() )
134         {
135             locator.setStrategies( Collections.singletonList( new RelativeFileLocatorStrategy(
136                                                                                                descriptorSourceDirectory ) ) );
137 
138             final DirectoryScanner scanner = new DirectoryScanner();
139             scanner.setBasedir( descriptorSourceDirectory );
140             scanner.setIncludes( new String[] { "**/*.xml" } );
141             scanner.addDefaultExcludes();
142 
143             try
144             {
145                 scanner.scan();
146             }
147             // FIXME: plexus-utils >= 1.3-SNAPSHOT should fix this.
148             catch ( final NullPointerException e )
149             {
150                 final StackTraceElement frameZero = e.getStackTrace()[0];
151 
152                 if ( "org.codehaus.plexus.util.DirectoryScanner".equals( frameZero.getClassName() )
153                     && "scandir".equals( frameZero.getMethodName() ) )
154                 {
155                     if ( getLogger().isDebugEnabled() )
156                     {
157                         getLogger().debug( "Caught filesystem error while scanning directories..."
158                                                + "using zero-length list as the result.", e );
159                     }
160                 }
161                 else
162                 {
163                     throw e;
164                 }
165             }
166 
167             final String[] paths = scanner.getIncludedFiles();
168 
169             if ( paths != null )
170             {
171                 for ( int i = 0; i < paths.length; i++ )
172                 {
173                     addAssemblyFromDescriptor( paths[i], locator, configSource, assemblies );
174                 }
175             }
176         }
177 
178         if ( assemblies.isEmpty() )
179         {
180             if ( configSource.isIgnoreMissingDescriptor() )
181             {
182                 getLogger().debug( "Ignoring missing assembly descriptors per configuration. See messages above for specifics." );
183             }
184             else
185             {
186                 throw new AssemblyReadException( "No assembly descriptors found." );
187             }
188         }
189 
190         // check unique IDs
191         final Set<String> ids = new HashSet<String>();
192         for ( final Iterator<Assembly> i = assemblies.iterator(); i.hasNext(); )
193         {
194             final Assembly assembly = i.next();
195             if ( !ids.add( assembly.getId() ) )
196             {
197                 getLogger().warn( "The assembly id " + assembly.getId() + " is used more than once." );
198             }
199 
200         }
201         return assemblies;
202     }
203 
204     public Assembly getAssemblyForDescriptorReference( final String ref, final AssemblerConfigurationSource configSource )
205         throws AssemblyReadException, InvalidAssemblerConfigurationException
206     {
207         return addAssemblyForDescriptorReference( ref, configSource, new ArrayList<Assembly>( 1 ) );
208     }
209 
210     public Assembly getAssemblyFromDescriptorFile( final File file, final AssemblerConfigurationSource configSource )
211         throws AssemblyReadException, InvalidAssemblerConfigurationException
212     {
213         return addAssemblyFromDescriptorFile( file, configSource, new ArrayList<Assembly>( 1 ) );
214     }
215 
216     private Assembly addAssemblyForDescriptorReference( final String ref,
217                                                         final AssemblerConfigurationSource configSource,
218                                                         final List<Assembly> assemblies )
219         throws AssemblyReadException, InvalidAssemblerConfigurationException
220     {
221         final InputStream resourceAsStream =
222             Thread.currentThread().getContextClassLoader().getResourceAsStream( "assemblies/" + ref + ".xml" );
223 
224         if ( resourceAsStream == null )
225         {
226             if ( configSource.isIgnoreMissingDescriptor() )
227             {
228                 getLogger().debug( "Ignoring missing assembly descriptor with ID '" + ref + "' per configuration." );
229                 return null;
230             }
231             else
232             {
233                 throw new AssemblyReadException( "Descriptor with ID '" + ref + "' not found" );
234             }
235         }
236 
237         try
238         {
239             // TODO use ReaderFactory.newXmlReader() when plexus-utils is upgraded to 1.4.5+
240             final Assembly assembly =
241                 readAssembly( new InputStreamReader( resourceAsStream, "UTF-8" ), ref, null, configSource );
242 
243             assemblies.add( assembly );
244             return assembly;
245         }
246         catch ( final UnsupportedEncodingException e )
247         {
248             // should not occur since UTF-8 support is mandatory
249             throw new AssemblyReadException( "Encoding not supported for descriptor with ID '" + ref + "'" );
250         }
251     }
252 
253     private Assembly addAssemblyFromDescriptorFile( final File descriptor,
254                                                     final AssemblerConfigurationSource configSource,
255                                                     final List<Assembly> assemblies )
256         throws AssemblyReadException, InvalidAssemblerConfigurationException
257     {
258         if ( !descriptor.exists() )
259         {
260             if ( configSource.isIgnoreMissingDescriptor() )
261             {
262                 getLogger().debug( "Ignoring missing assembly descriptor: '" + descriptor + "' per configuration." );
263                 return null;
264             }
265             else
266             {
267                 throw new AssemblyReadException( "Descriptor: '" + descriptor + "' not found" );
268             }
269         }
270 
271         Reader r = null;
272         try
273         {
274             // TODO use ReaderFactory.newXmlReader() when plexus-utils is upgraded to 1.4.5+
275             r = new InputStreamReader( new FileInputStream( descriptor ), "UTF-8" );
276             final Assembly assembly =
277                 readAssembly( r, descriptor.getAbsolutePath(), descriptor.getParentFile(), configSource );
278 
279             assemblies.add( assembly );
280 
281             return assembly;
282         }
283         catch ( final IOException e )
284         {
285             throw new AssemblyReadException( "Error reading assembly descriptor: " + descriptor, e );
286         }
287         finally
288         {
289             IOUtil.close( r );
290         }
291     }
292 
293     private Assembly addAssemblyFromDescriptor( final String spec, final Locator locator,
294                                                 final AssemblerConfigurationSource configSource,
295                                                 final List<Assembly> assemblies )
296         throws AssemblyReadException, InvalidAssemblerConfigurationException
297     {
298         final Location location = locator.resolve( spec );
299 
300         if ( location == null )
301         {
302             if ( configSource.isIgnoreMissingDescriptor() )
303             {
304                 getLogger().debug( "Ignoring missing assembly descriptor with ID '" + spec
305                                        + "' per configuration.\nLocator output was:\n\n"
306                                        + locator.getMessageHolder().render() );
307                 return null;
308             }
309             else
310             {
311                 throw new AssemblyReadException( "Error locating assembly descriptor: " + spec + "\n\n"
312                     + locator.getMessageHolder().render() );
313             }
314         }
315 
316         Reader r = null;
317         try
318         {
319             // TODO use ReaderFactory.newXmlReader() when plexus-utils is upgraded to 1.4.5+
320             r = new InputStreamReader( location.getInputStream(), "UTF-8" );
321 
322             File dir = null;
323             if ( location.getFile() != null )
324             {
325                 dir = location.getFile().getParentFile();
326             }
327 
328             final Assembly assembly = readAssembly( r, spec, dir, configSource );
329 
330             assemblies.add( assembly );
331 
332             return assembly;
333         }
334         catch ( final IOException e )
335         {
336             throw new AssemblyReadException( "Error reading assembly descriptor: " + spec, e );
337         }
338         finally
339         {
340             IOUtil.close( r );
341         }
342 
343     }
344 
345     protected Assembly readAssembly( final Reader reader, final String locationDescription, final File assemblyDir,
346                                      final AssemblerConfigurationSource configSource )
347         throws AssemblyReadException, InvalidAssemblerConfigurationException
348     {
349         Assembly assembly;
350 
351         final File basedir = configSource.getBasedir();
352         final MavenProject project = configSource.getProject();
353 
354         try
355         {
356             final Map<String, String> context = new HashMap<String, String>();
357             final MavenSession session = configSource.getMavenSession();
358 
359             Properties commandLineProperties = System.getProperties();
360             if ( session != null )
361             {
362                 commandLineProperties = new Properties();
363                 if ( session.getExecutionProperties() != null )
364                 {
365                     commandLineProperties.putAll( session.getExecutionProperties() );
366                 }
367 
368                 if ( session.getUserProperties() != null )
369                 {
370                     commandLineProperties.putAll( session.getUserProperties() );
371                 }
372             }
373 
374             for ( final Enumeration<Object> e = commandLineProperties.keys(); e.hasMoreElements(); )
375             {
376                 final String key = (String) e.nextElement();
377                 if ( key == null || key.trim().length() < 1 )
378                 {
379                     continue;
380                 }
381 
382                 context.put( key, commandLineProperties.getProperty( key ) );
383             }
384 
385             context.put( "basedir", basedir.getAbsolutePath() );
386 
387             final AssemblyXpp3Reader r = new AssemblyXpp3Reader();
388             assembly = r.read( reader );
389 
390             mergeComponentsWithMainAssembly( assembly, assemblyDir, configSource );
391 
392             debugPrintAssembly( "Before assembly is interpolated:", assembly );
393 
394             assembly = new AssemblyInterpolator().interpolate( assembly, project, configSource );
395 
396             debugPrintAssembly( "After assembly is interpolated:", assembly );
397         }
398         catch ( final IOException e )
399         {
400             throw new AssemblyReadException(
401                                              "Error reading descriptor: " + locationDescription + ": " + e.getMessage(),
402                                              e );
403         }
404         catch ( final XmlPullParserException e )
405         {
406             throw new AssemblyReadException(
407                                              "Error reading descriptor: " + locationDescription + ": " + e.getMessage(),
408                                              e );
409         }
410         catch ( final AssemblyInterpolationException e )
411         {
412             throw new AssemblyReadException(
413                                              "Error reading descriptor: " + locationDescription + ": " + e.getMessage(),
414                                              e );
415         }
416         finally
417         {
418             IOUtil.close( reader );
419         }
420 
421         if ( configSource.isSiteIncluded() || assembly.isIncludeSiteDirectory() )
422         {
423             includeSiteInAssembly( assembly, configSource );
424         }
425 
426         return assembly;
427     }
428 
429     private void debugPrintAssembly( final String message, final Assembly assembly )
430     {
431         final StringWriter sWriter = new StringWriter();
432         try
433         {
434             new AssemblyXpp3Writer().write( sWriter, assembly );
435         }
436         catch ( final IOException e )
437         {
438             getLogger().debug( "Failed to print debug message with assembly descriptor listing, and message: "
439                                    + message, e );
440         }
441 
442         getLogger().debug( message + "\n\n" + sWriter.toString() + "\n\n" );
443     }
444 
445     /**
446      * Add the contents of all included components to main assembly
447      * 
448      * @param assembly
449      * @param assemblyDir
450      * @throws AssemblyReadException
451      * @throws MojoFailureException
452      * @throws MojoExecutionException
453      */
454     protected void mergeComponentsWithMainAssembly( final Assembly assembly, final File assemblyDir,
455                                                     final AssemblerConfigurationSource configSource )
456         throws AssemblyReadException
457     {
458         final Locator locator = new Locator();
459 
460         if ( assemblyDir != null && assemblyDir.exists() && assemblyDir.isDirectory() )
461         {
462             locator.addStrategy( new RelativeFileLocatorStrategy( assemblyDir ) );
463         }
464 
465         // allow absolute paths in componentDescriptor... MASSEMBLY-486
466         locator.addStrategy( new RelativeFileLocatorStrategy( configSource.getBasedir() ) );
467         locator.addStrategy( new FileLocatorStrategy() );
468         locator.addStrategy( new ClasspathResourceLocatorStrategy() );
469 
470         final AssemblyExpressionEvaluator aee = new AssemblyExpressionEvaluator( configSource );
471 
472         final List<String> componentLocations = assembly.getComponentDescriptors();
473 
474         for ( final Iterator<String> it = componentLocations.iterator(); it.hasNext(); )
475         {
476             String location = it.next();
477 
478             // allow expressions in path to component descriptor... MASSEMBLY-486
479             try
480             {
481                 location = aee.evaluate( location ).toString();
482             }
483             catch ( final Exception eee )
484             {
485                 getLogger().error( "Error interpolating componentDescriptor: " + location, eee );
486             }
487 
488             final Location resolvedLocation = locator.resolve( location );
489 
490             if ( resolvedLocation == null )
491             {
492                 throw new AssemblyReadException( "Failed to locate component descriptor: " + location );
493             }
494 
495             Component component = null;
496             Reader reader = null;
497             try
498             {
499                 reader = new InputStreamReader( resolvedLocation.getInputStream() );
500                 component = new ComponentXpp3Reader().read( reader );
501             }
502             catch ( final IOException e )
503             {
504                 throw new AssemblyReadException( "Error reading component descriptor: " + location + " (resolved to: "
505                     + resolvedLocation.getSpecification() + ")", e );
506             }
507             catch ( final XmlPullParserException e )
508             {
509                 throw new AssemblyReadException( "Error reading component descriptor: " + location + " (resolved to: "
510                     + resolvedLocation.getSpecification() + ")", e );
511             }
512             finally
513             {
514                 IOUtil.close( reader );
515             }
516 
517             mergeComponentWithAssembly( component, assembly );
518         }
519     }
520 
521     /**
522      * Add the content of a single Component to main assembly
523      * 
524      * @param component
525      * @param assembly
526      */
527     protected void mergeComponentWithAssembly( final Component component, final Assembly assembly )
528     {
529         final List<ContainerDescriptorHandlerConfig> containerHandlerDescriptors =
530             component.getContainerDescriptorHandlers();
531 
532         for ( final Iterator<ContainerDescriptorHandlerConfig> it = containerHandlerDescriptors.iterator(); it.hasNext(); )
533         {
534             final ContainerDescriptorHandlerConfig cfg = it.next();
535             assembly.addContainerDescriptorHandler( cfg );
536         }
537 
538         final List<DependencySet> dependencySetList = component.getDependencySets();
539 
540         for ( final Iterator<DependencySet> it = dependencySetList.iterator(); it.hasNext(); )
541         {
542             final DependencySet dependencySet = it.next();
543             assembly.addDependencySet( dependencySet );
544         }
545 
546         final List<FileSet> fileSetList = component.getFileSets();
547 
548         for ( final Iterator<FileSet> it = fileSetList.iterator(); it.hasNext(); )
549         {
550             final FileSet fileSet = it.next();
551 
552             assembly.addFileSet( fileSet );
553         }
554 
555         final List<FileItem> fileList = component.getFiles();
556 
557         for ( final Iterator<FileItem> it = fileList.iterator(); it.hasNext(); )
558         {
559             final FileItem fileItem = it.next();
560 
561             assembly.addFile( fileItem );
562         }
563 
564         final List<Repository> repositoriesList = component.getRepositories();
565 
566         for ( final Iterator<Repository> it = repositoriesList.iterator(); it.hasNext(); )
567         {
568             final Repository repository = it.next();
569 
570             assembly.addRepository( repository );
571         }
572 
573         final List<ModuleSet> moduleSets = component.getModuleSets();
574         for ( final ModuleSet moduleSet : moduleSets )
575         {
576             assembly.addModuleSet( moduleSet );
577         }
578     }
579 
580     public void includeSiteInAssembly( final Assembly assembly, final AssemblerConfigurationSource configSource )
581         throws InvalidAssemblerConfigurationException
582     {
583         final File siteDirectory = configSource.getSiteDirectory();
584 
585         if ( !siteDirectory.exists() )
586         {
587             throw new InvalidAssemblerConfigurationException(
588                                                               "site did not exist in the target directory - please run site:site before creating the assembly" );
589         }
590 
591         getLogger().info( "Adding site directory to assembly : " + siteDirectory );
592 
593         final FileSet siteFileSet = new FileSet();
594 
595         siteFileSet.setDirectory( siteDirectory.getPath() );
596 
597         siteFileSet.setOutputDirectory( "/site" );
598 
599         assembly.addFileSet( siteFileSet );
600     }
601 
602     @Override
603     protected Logger getLogger()
604     {
605         Logger logger = super.getLogger();
606 
607         if ( logger == null )
608         {
609             logger = new ConsoleLogger( Logger.LEVEL_INFO, "assemblyReader-internal" );
610             enableLogging( logger );
611         }
612 
613         return logger;
614     }
615 
616 }