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