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 org.apache.commons.io.IOUtils;
23  import org.apache.maven.plugin.assembly.AssemblerConfigurationSource;
24  import org.apache.maven.plugin.assembly.InvalidAssemblerConfigurationException;
25  import org.apache.maven.plugin.assembly.interpolation.AssemblyExpressionEvaluator;
26  import org.apache.maven.plugin.assembly.interpolation.AssemblyInterpolationException;
27  import org.apache.maven.plugin.assembly.interpolation.AssemblyInterpolator;
28  import org.apache.maven.plugin.assembly.model.Assembly;
29  import org.apache.maven.plugin.assembly.model.Component;
30  import org.apache.maven.plugin.assembly.model.ContainerDescriptorHandlerConfig;
31  import org.apache.maven.plugin.assembly.model.DependencySet;
32  import org.apache.maven.plugin.assembly.model.FileItem;
33  import org.apache.maven.plugin.assembly.model.FileSet;
34  import org.apache.maven.plugin.assembly.model.ModuleSet;
35  import org.apache.maven.plugin.assembly.model.Repository;
36  import org.apache.maven.plugin.assembly.model.io.xpp3.AssemblyXpp3Reader;
37  import org.apache.maven.plugin.assembly.model.io.xpp3.AssemblyXpp3Writer;
38  import org.apache.maven.plugin.assembly.model.io.xpp3.ComponentXpp3Reader;
39  import org.apache.maven.plugin.assembly.utils.InterpolationConstants;
40  import org.apache.maven.project.MavenProject;
41  import org.apache.maven.shared.io.location.ClasspathResourceLocatorStrategy;
42  import org.apache.maven.shared.io.location.FileLocatorStrategy;
43  import org.apache.maven.shared.io.location.Location;
44  import org.apache.maven.shared.io.location.Locator;
45  import org.apache.maven.shared.io.location.LocatorStrategy;
46  import org.apache.maven.shared.utils.ReaderFactory;
47  import org.codehaus.plexus.interpolation.fixed.FixedStringSearchInterpolator;
48  import org.codehaus.plexus.interpolation.fixed.PrefixedObjectValueSource;
49  import org.codehaus.plexus.interpolation.fixed.PrefixedPropertiesValueSource;
50  import org.codehaus.plexus.logging.AbstractLogEnabled;
51  import org.codehaus.plexus.logging.Logger;
52  import org.codehaus.plexus.logging.console.ConsoleLogger;
53  import org.codehaus.plexus.util.DirectoryScanner;
54  import org.codehaus.plexus.util.IOUtil;
55  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
56  
57  import java.io.File;
58  import java.io.IOException;
59  import java.io.InputStream;
60  import java.io.InputStreamReader;
61  import java.io.Reader;
62  import java.io.StringWriter;
63  import java.util.ArrayList;
64  import java.util.Collections;
65  import java.util.HashSet;
66  import java.util.List;
67  import java.util.Set;
68  
69  /**
70   * @version $Id: DefaultAssemblyReader.java 1639422 2014-11-13 18:08:07Z krosenvold $
71   */
72  @org.codehaus.plexus.component.annotations.Component( role = AssemblyReader.class )
73  public class DefaultAssemblyReader
74      extends AbstractLogEnabled
75      implements AssemblyReader
76  {
77  
78      public List<Assembly> readAssemblies( final AssemblerConfigurationSource configSource )
79          throws AssemblyReadException, InvalidAssemblerConfigurationException
80      {
81          final Locator locator = new Locator();
82  
83          final List<LocatorStrategy> strategies = new ArrayList<LocatorStrategy>();
84          strategies.add( new RelativeFileLocatorStrategy( configSource.getBasedir() ) );
85          strategies.add( new FileLocatorStrategy() );
86  
87          final List<LocatorStrategy> refStrategies = new ArrayList<LocatorStrategy>();
88          refStrategies.add( new PrefixedClasspathLocatorStrategy( "/assemblies/" ) );
89  
90          final List<Assembly> assemblies = new ArrayList<Assembly>();
91  
92          final String descriptor = configSource.getDescriptor();
93          final String descriptorId = configSource.getDescriptorId();
94          final String[] descriptors = configSource.getDescriptors();
95          final String[] descriptorRefs = configSource.getDescriptorReferences();
96          final File descriptorSourceDirectory = configSource.getDescriptorSourceDirectory();
97  
98          if ( descriptor != null )
99          {
100             locator.setStrategies( strategies );
101             addAssemblyFromDescriptor( descriptor, locator, configSource, assemblies );
102         }
103 
104         if ( descriptorId != null )
105         {
106             locator.setStrategies( refStrategies );
107             addAssemblyForDescriptorReference( descriptorId, configSource, assemblies );
108         }
109 
110         if ( ( descriptors != null ) && ( descriptors.length > 0 ) )
111         {
112             locator.setStrategies( strategies );
113             for ( String descriptor1 : descriptors )
114             {
115                 getLogger().info( "Reading assembly descriptor: " + descriptor1 );
116                 addAssemblyFromDescriptor( descriptor1, locator, configSource, assemblies );
117             }
118         }
119 
120         if ( ( descriptorRefs != null ) && ( descriptorRefs.length > 0 ) )
121         {
122             locator.setStrategies( refStrategies );
123             for ( String descriptorRef : descriptorRefs )
124             {
125                 addAssemblyForDescriptorReference( descriptorRef, configSource, assemblies );
126             }
127         }
128 
129         if ( ( descriptorSourceDirectory != null ) && descriptorSourceDirectory.isDirectory() )
130         {
131             locator.setStrategies(
132                 Collections.singletonList( new RelativeFileLocatorStrategy( descriptorSourceDirectory ) ) );
133 
134             final DirectoryScanner scanner = new DirectoryScanner();
135             scanner.setBasedir( descriptorSourceDirectory );
136             scanner.setIncludes( new String[]{ "**/*.xml" } );
137             scanner.addDefaultExcludes();
138 
139             scanner.scan();
140 
141             final String[] paths = scanner.getIncludedFiles();
142 
143             for ( String path : paths )
144             {
145                 addAssemblyFromDescriptor( path, locator, configSource, assemblies );
146             }
147         }
148 
149         if ( assemblies.isEmpty() )
150         {
151             if ( configSource.isIgnoreMissingDescriptor() )
152             {
153                 getLogger().debug(
154                     "Ignoring missing assembly descriptors per configuration. See messages above for specifics." );
155             }
156             else
157             {
158                 throw new AssemblyReadException( "No assembly descriptors found." );
159             }
160         }
161 
162         // check unique IDs
163         final Set<String> ids = new HashSet<String>();
164         for ( final Assembly assembly : assemblies )
165         {
166             if ( !ids.add( assembly.getId() ) )
167             {
168                 getLogger().warn( "The assembly id " + assembly.getId() + " is used more than once." );
169             }
170 
171         }
172         return assemblies;
173     }
174 
175     public Assembly getAssemblyForDescriptorReference( final String ref,
176                                                        final AssemblerConfigurationSource configSource )
177         throws AssemblyReadException, InvalidAssemblerConfigurationException
178     {
179         return addAssemblyForDescriptorReference( ref, configSource, new ArrayList<Assembly>( 1 ) );
180     }
181 
182     public Assembly getAssemblyFromDescriptorFile( final File file, final AssemblerConfigurationSource configSource )
183         throws AssemblyReadException, InvalidAssemblerConfigurationException
184     {
185         return addAssemblyFromDescriptorFile( file, configSource, new ArrayList<Assembly>( 1 ) );
186     }
187 
188     private Assembly addAssemblyForDescriptorReference( final String ref,
189                                                         final AssemblerConfigurationSource configSource,
190                                                         final List<Assembly> assemblies )
191         throws AssemblyReadException, InvalidAssemblerConfigurationException
192     {
193         final InputStream resourceAsStream =
194             Thread.currentThread().getContextClassLoader().getResourceAsStream( "assemblies/" + ref + ".xml" );
195 
196         if ( resourceAsStream == null )
197         {
198             if ( configSource.isIgnoreMissingDescriptor() )
199             {
200                 getLogger().debug( "Ignoring missing assembly descriptor with ID '" + ref + "' per configuration." );
201                 return null;
202             }
203             else
204             {
205                 throw new AssemblyReadException( "Descriptor with ID '" + ref + "' not found" );
206             }
207         }
208 
209         Reader reader = null;
210         try
211         {
212             reader = ReaderFactory.newXmlReader( resourceAsStream );
213             final Assembly assembly = readAssembly( reader, ref, null, configSource );
214 
215             assemblies.add( assembly );
216             return assembly;
217         }
218         catch ( final IOException e )
219         {
220             throw new AssemblyReadException( "Problem with descriptor with ID '" + ref + "'", e );
221         }
222         finally
223         {
224             IOUtils.closeQuietly( reader );
225         }
226     }
227 
228     private Assembly addAssemblyFromDescriptorFile( final File descriptor,
229                                                     final AssemblerConfigurationSource configSource,
230                                                     final List<Assembly> assemblies )
231         throws AssemblyReadException, InvalidAssemblerConfigurationException
232     {
233         if ( !descriptor.exists() )
234         {
235             if ( configSource.isIgnoreMissingDescriptor() )
236             {
237                 getLogger().debug( "Ignoring missing assembly descriptor: '" + descriptor + "' per configuration." );
238                 return null;
239             }
240             else
241             {
242                 throw new AssemblyReadException( "Descriptor: '" + descriptor + "' not found" );
243             }
244         }
245 
246         Reader r = null;
247         try
248         {
249             r = ReaderFactory.newXmlReader( descriptor );
250             final Assembly assembly =
251                 readAssembly( r, descriptor.getAbsolutePath(), descriptor.getParentFile(), configSource );
252 
253             assemblies.add( assembly );
254 
255             return assembly;
256         }
257         catch ( final IOException e )
258         {
259             throw new AssemblyReadException( "Error reading assembly descriptor: " + descriptor, e );
260         }
261         finally
262         {
263             IOUtil.close( r );
264         }
265     }
266 
267     private Assembly addAssemblyFromDescriptor( final String spec, final Locator locator,
268                                                 final AssemblerConfigurationSource configSource,
269                                                 final List<Assembly> assemblies )
270         throws AssemblyReadException, InvalidAssemblerConfigurationException
271     {
272         final Location location = locator.resolve( spec );
273 
274         if ( location == null )
275         {
276             if ( configSource.isIgnoreMissingDescriptor() )
277             {
278                 getLogger().debug( "Ignoring missing assembly descriptor with ID '" + spec
279                                        + "' per configuration.\nLocator output was:\n\n"
280                                        + locator.getMessageHolder().render() );
281                 return null;
282             }
283             else
284             {
285                 throw new AssemblyReadException(
286                     "Error locating assembly descriptor: " + spec + "\n\n" + locator.getMessageHolder().render() );
287             }
288         }
289 
290         Reader r = null;
291         try
292         {
293             r = ReaderFactory.newXmlReader( location.getInputStream() );
294 
295             File dir = null;
296             if ( location.getFile() != null )
297             {
298                 dir = location.getFile().getParentFile();
299             }
300 
301             final Assembly assembly = readAssembly( r, spec, dir, configSource );
302 
303             assemblies.add( assembly );
304 
305             return assembly;
306         }
307         catch ( final IOException e )
308         {
309             throw new AssemblyReadException( "Error reading assembly descriptor: " + spec, e );
310         }
311         finally
312         {
313             IOUtil.close( r );
314         }
315 
316     }
317 
318     protected Assembly readAssembly( final Reader reader, final String locationDescription, final File assemblyDir,
319                                      final AssemblerConfigurationSource configSource )
320         throws AssemblyReadException, InvalidAssemblerConfigurationException
321     {
322         Assembly assembly;
323 
324         final MavenProject project = configSource.getProject();
325         try
326         {
327             final AssemblyXpp3Reader r = new AssemblyXpp3Reader();
328             assembly = r.read( reader );
329 
330             mergeComponentsWithMainAssembly( assembly, assemblyDir, configSource );
331 
332             debugPrintAssembly( "Before assembly is interpolated:", assembly );
333 
334             assembly = new AssemblyInterpolator().interpolate( assembly, project, configSource,
335                                                                createProjectInterpolator( project ) );
336 
337             debugPrintAssembly( "After assembly is interpolated:", assembly );
338         }
339         catch ( final IOException e )
340         {
341             throw new AssemblyReadException( "Error reading descriptor: " + locationDescription + ": " + e.getMessage(),
342                                              e );
343         }
344         catch ( final XmlPullParserException e )
345         {
346             throw new AssemblyReadException( "Error reading descriptor: " + locationDescription + ": " + e.getMessage(),
347                                              e );
348         }
349         catch ( final AssemblyInterpolationException e )
350         {
351             throw new AssemblyReadException( "Error reading descriptor: " + locationDescription + ": " + e.getMessage(),
352                                              e );
353         }
354         finally
355         {
356             IOUtil.close( reader );
357         }
358 
359         if ( configSource.isSiteIncluded() || assembly.isIncludeSiteDirectory() )
360         {
361             includeSiteInAssembly( assembly, configSource );
362         }
363 
364         return assembly;
365     }
366 
367     public static FixedStringSearchInterpolator createProjectInterpolator( MavenProject project )
368     {
369         return FixedStringSearchInterpolator.create(
370             new PrefixedPropertiesValueSource( InterpolationConstants.PROJECT_PROPERTIES_PREFIXES,
371                                                project.getProperties(), true ),
372             new PrefixedObjectValueSource( InterpolationConstants.PROJECT_PREFIXES, project, true ) );
373     }
374 
375     private void debugPrintAssembly( final String message, final Assembly assembly )
376     {
377         final StringWriter sWriter = new StringWriter();
378         try
379         {
380             new AssemblyXpp3Writer().write( sWriter, assembly );
381         }
382         catch ( final IOException e )
383         {
384             getLogger().debug(
385                 "Failed to print debug message with assembly descriptor listing, and message: " + message, e );
386         }
387 
388         getLogger().debug( message + "\n\n" + sWriter.toString() + "\n\n" );
389     }
390 
391     /**
392      * Add the contents of all included components to main assembly
393      *
394      * @param assembly    The assembly
395      * @param assemblyDir The assembly directory
396      * @throws AssemblyReadException .
397      */
398     protected void mergeComponentsWithMainAssembly( final Assembly assembly, final File assemblyDir,
399                                                     final AssemblerConfigurationSource configSource )
400         throws AssemblyReadException
401     {
402         final Locator locator = new Locator();
403 
404         if ( assemblyDir != null && assemblyDir.exists() && assemblyDir.isDirectory() )
405         {
406             locator.addStrategy( new RelativeFileLocatorStrategy( assemblyDir ) );
407         }
408 
409         // allow absolute paths in componentDescriptor... MASSEMBLY-486
410         locator.addStrategy( new RelativeFileLocatorStrategy( configSource.getBasedir() ) );
411         locator.addStrategy( new FileLocatorStrategy() );
412         locator.addStrategy( new ClasspathResourceLocatorStrategy() );
413 
414         final AssemblyExpressionEvaluator aee = new AssemblyExpressionEvaluator( configSource );
415 
416         final List<String> componentLocations = assembly.getComponentDescriptors();
417 
418         for ( String location : componentLocations )
419         {
420             // allow expressions in path to component descriptor... MASSEMBLY-486
421             try
422             {
423                 location = aee.evaluate( location ).toString();
424             }
425             catch ( final Exception eee )
426             {
427                 getLogger().error( "Error interpolating componentDescriptor: " + location, eee );
428             }
429 
430             final Location resolvedLocation = locator.resolve( location );
431 
432             if ( resolvedLocation == null )
433             {
434                 throw new AssemblyReadException( "Failed to locate component descriptor: " + location );
435             }
436 
437             Component component = null;
438             Reader reader = null;
439             try
440             {
441                 reader = new InputStreamReader( resolvedLocation.getInputStream() );
442                 component = new ComponentXpp3Reader().read( reader );
443             }
444             catch ( final IOException e )
445             {
446                 throw new AssemblyReadException( "Error reading component descriptor: " + location + " (resolved to: "
447                                                      + resolvedLocation.getSpecification() + ")", e );
448             }
449             catch ( final XmlPullParserException e )
450             {
451                 throw new AssemblyReadException( "Error reading component descriptor: " + location + " (resolved to: "
452                                                      + resolvedLocation.getSpecification() + ")", e );
453             }
454             finally
455             {
456                 IOUtil.close( reader );
457             }
458 
459             mergeComponentWithAssembly( component, assembly );
460         }
461     }
462 
463     /**
464      * Add the content of a single Component to main assembly
465      *
466      * @param component The component
467      * @param assembly  The assembly
468      */
469     protected void mergeComponentWithAssembly( final Component component, final Assembly assembly )
470     {
471         final List<ContainerDescriptorHandlerConfig> containerHandlerDescriptors =
472             component.getContainerDescriptorHandlers();
473 
474         for ( final ContainerDescriptorHandlerConfig cfg : containerHandlerDescriptors )
475         {
476             assembly.addContainerDescriptorHandler( cfg );
477         }
478 
479         final List<DependencySet> dependencySetList = component.getDependencySets();
480 
481         for ( final DependencySet dependencySet : dependencySetList )
482         {
483             assembly.addDependencySet( dependencySet );
484         }
485 
486         final List<FileSet> fileSetList = component.getFileSets();
487 
488         for ( final FileSet fileSet : fileSetList )
489         {
490             assembly.addFileSet( fileSet );
491         }
492 
493         final List<FileItem> fileList = component.getFiles();
494 
495         for ( final FileItem fileItem : fileList )
496         {
497             assembly.addFile( fileItem );
498         }
499 
500         final List<Repository> repositoriesList = component.getRepositories();
501 
502         for ( final Repository repository : repositoriesList )
503         {
504             assembly.addRepository( repository );
505         }
506 
507         final List<ModuleSet> moduleSets = component.getModuleSets();
508         for ( final ModuleSet moduleSet : moduleSets )
509         {
510             assembly.addModuleSet( moduleSet );
511         }
512     }
513 
514     public void includeSiteInAssembly( final Assembly assembly, final AssemblerConfigurationSource configSource )
515         throws InvalidAssemblerConfigurationException
516     {
517         final File siteDirectory = configSource.getSiteDirectory();
518 
519         if ( !siteDirectory.exists() )
520         {
521             throw new InvalidAssemblerConfigurationException(
522                 "site did not exist in the target directory - please run site:site before creating the assembly" );
523         }
524 
525         getLogger().info( "Adding site directory to assembly : " + siteDirectory );
526 
527         final FileSet siteFileSet = new FileSet();
528 
529         siteFileSet.setDirectory( siteDirectory.getPath() );
530 
531         siteFileSet.setOutputDirectory( "/site" );
532 
533         assembly.addFileSet( siteFileSet );
534     }
535 
536     @Override
537     protected Logger getLogger()
538     {
539         Logger logger = super.getLogger();
540 
541         if ( logger == null )
542         {
543             logger = new ConsoleLogger( Logger.LEVEL_INFO, "assemblyReader-internal" );
544             enableLogging( logger );
545         }
546 
547         return logger;
548     }
549 
550 }