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