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.maven.plugins.assembly.AssemblerConfigurationSource;
23  import org.apache.maven.plugins.assembly.InvalidAssemblerConfigurationException;
24  import org.apache.maven.plugins.assembly.interpolation.AssemblyExpressionEvaluator;
25  import org.apache.maven.plugins.assembly.interpolation.AssemblyInterpolator;
26  import org.apache.maven.plugins.assembly.model.Assembly;
27  import org.apache.maven.plugins.assembly.model.Component;
28  import org.apache.maven.plugins.assembly.model.ContainerDescriptorHandlerConfig;
29  import org.apache.maven.plugins.assembly.model.DependencySet;
30  import org.apache.maven.plugins.assembly.model.FileItem;
31  import org.apache.maven.plugins.assembly.model.FileSet;
32  import org.apache.maven.plugins.assembly.model.ModuleSet;
33  import org.apache.maven.plugins.assembly.model.Repository;
34  import org.apache.maven.plugins.assembly.model.io.xpp3.AssemblyXpp3Reader;
35  import org.apache.maven.plugins.assembly.model.io.xpp3.AssemblyXpp3Writer;
36  import org.apache.maven.plugins.assembly.model.io.xpp3.ComponentXpp3Reader;
37  import org.apache.maven.plugins.assembly.resolved.AssemblyId;
38  import org.apache.maven.plugins.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  /**
72   *
73   */
74  @org.codehaus.plexus.component.annotations.Component( role = AssemblyReader.class )
75  public class DefaultAssemblyReader
76      extends AbstractLogEnabled
77      implements AssemblyReader
78  {
79  
80      public static FixedStringSearchInterpolator createProjectInterpolator( MavenProject project )
81      {
82          // CHECKSTYLE_OFF: LineLength
83          return FixedStringSearchInterpolator.create( new PrefixedPropertiesValueSource( InterpolationConstants.PROJECT_PROPERTIES_PREFIXES,
84                                                                                          project.getProperties(), true ),
85                                                       new PrefixedObjectValueSource( InterpolationConstants.PROJECT_PREFIXES,
86                                                                                      project, true ) );
87          // CHECKSTYLE_ON: LineLength
88      }
89  
90      @Override
91      public List<Assembly> readAssemblies( final AssemblerConfigurationSource configSource )
92          throws AssemblyReadException, InvalidAssemblerConfigurationException
93      {
94          final Locator locator = new Locator();
95  
96          final List<LocatorStrategy> strategies = new ArrayList<>();
97          strategies.add( new RelativeFileLocatorStrategy( configSource.getBasedir() ) );
98          strategies.add( new FileLocatorStrategy() );
99  
100         final List<LocatorStrategy> refStrategies = new ArrayList<>();
101         refStrategies.add( new PrefixedClasspathLocatorStrategy( "/assemblies/" ) );
102 
103         final List<Assembly> assemblies = new ArrayList<>();
104 
105         final String[] descriptors = configSource.getDescriptors();
106         final String[] descriptorRefs = configSource.getDescriptorReferences();
107         final File descriptorSourceDirectory = configSource.getDescriptorSourceDirectory();
108 
109         if ( ( descriptors != null ) && ( descriptors.length > 0 ) )
110         {
111             locator.setStrategies( strategies );
112             for ( String descriptor1 : descriptors )
113             {
114                 getLogger().info( "Reading assembly descriptor: " + descriptor1 );
115                 addAssemblyFromDescriptor( descriptor1, locator, configSource, assemblies );
116             }
117         }
118 
119         if ( ( descriptorRefs != null ) && ( descriptorRefs.length > 0 ) )
120         {
121             locator.setStrategies( refStrategies );
122             for ( String descriptorRef : descriptorRefs )
123             {
124                 addAssemblyForDescriptorReference( descriptorRef, configSource, assemblies );
125             }
126         }
127 
128         if ( ( descriptorSourceDirectory != null ) && descriptorSourceDirectory.isDirectory() )
129         {
130             // CHECKSTYLE_OFF: LineLength
131             locator.setStrategies( Collections.<LocatorStrategy>singletonList( new RelativeFileLocatorStrategy( descriptorSourceDirectory ) ) );
132             // CHECKSTYLE_ON: LineLength
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( "Ignoring missing assembly descriptors per configuration. "
154                     + "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<>();
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     @Override
176     public Assembly getAssemblyForDescriptorReference( final String ref,
177                                                        final AssemblerConfigurationSource configSource )
178                                                            throws AssemblyReadException,
179                                                            InvalidAssemblerConfigurationException
180     {
181         return addAssemblyForDescriptorReference( ref, configSource, new ArrayList<Assembly>( 1 ) );
182     }
183 
184     @Override
185     public Assembly getAssemblyFromDescriptorFile( final File file, final AssemblerConfigurationSource configSource )
186         throws AssemblyReadException, InvalidAssemblerConfigurationException
187     {
188         return addAssemblyFromDescriptorFile( file, configSource, new ArrayList<Assembly>( 1 ) );
189     }
190 
191     private Assembly addAssemblyForDescriptorReference( final String ref,
192                                                         final AssemblerConfigurationSource configSource,
193                                                         final List<Assembly> assemblies )
194                                                             throws AssemblyReadException,
195                                                             InvalidAssemblerConfigurationException
196     {
197         final InputStream resourceAsStream = getClass().getResourceAsStream( "/assemblies/" + ref + ".xml" );
198 
199         if ( resourceAsStream == null )
200         {
201             if ( configSource.isIgnoreMissingDescriptor() )
202             {
203                 getLogger().debug( "Ignoring missing assembly descriptor with ID '" + ref + "' per configuration." );
204                 return null;
205             }
206             else
207             {
208                 throw new AssemblyReadException( "Descriptor with ID '" + ref + "' not found" );
209             }
210         }
211 
212         try ( Reader reader = ReaderFactory.newXmlReader( resourceAsStream ) )
213         {
214             final Assembly assembly = readAssembly( reader, ref, null, configSource );
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     }
223 
224     private Assembly addAssemblyFromDescriptorFile( final File descriptor,
225                                                     final AssemblerConfigurationSource configSource,
226                                                     final List<Assembly> assemblies )
227                                                         throws AssemblyReadException,
228                                                         InvalidAssemblerConfigurationException
229     {
230         if ( !descriptor.exists() )
231         {
232             if ( configSource.isIgnoreMissingDescriptor() )
233             {
234                 getLogger().debug( "Ignoring missing assembly descriptor: '" + descriptor + "' per configuration." );
235                 return null;
236             }
237             else
238             {
239                 throw new AssemblyReadException( "Descriptor: '" + descriptor + "' not found" );
240             }
241         }
242 
243         try ( Reader r = ReaderFactory.newXmlReader( descriptor ) )
244         {
245             final Assembly assembly =
246                 readAssembly( r, descriptor.getAbsolutePath(), descriptor.getParentFile(), configSource );
247 
248             assemblies.add( assembly );
249 
250             return assembly;
251         }
252         catch ( final IOException e )
253         {
254             throw new AssemblyReadException( "Error reading assembly descriptor: " + descriptor, e );
255         }
256     }
257 
258     private Assembly addAssemblyFromDescriptor( final String spec, final Locator locator,
259                                                 final AssemblerConfigurationSource configSource,
260                                                 final List<Assembly> assemblies )
261                                                     throws AssemblyReadException, InvalidAssemblerConfigurationException
262     {
263         final Location location = locator.resolve( spec );
264 
265         if ( location == null )
266         {
267             if ( configSource.isIgnoreMissingDescriptor() )
268             {
269                 getLogger().debug( "Ignoring missing assembly descriptor with ID '" + spec
270                     + "' per configuration.\nLocator output was:\n\n" + locator.getMessageHolder().render() );
271                 return null;
272             }
273             else
274             {
275                 throw new AssemblyReadException( "Error locating assembly descriptor: " + spec + "\n\n"
276                     + locator.getMessageHolder().render() );
277             }
278         }
279 
280         
281         try ( Reader r = ReaderFactory.newXmlReader( location.getInputStream() ) )
282         {
283             File dir = null;
284             if ( location.getFile() != null )
285             {
286                 dir = location.getFile().getParentFile();
287             }
288 
289             final Assembly assembly = readAssembly( r, spec, dir, configSource );
290 
291             assemblies.add( assembly );
292 
293             return assembly;
294         }
295         catch ( final IOException e )
296         {
297             throw new AssemblyReadException( "Error reading assembly descriptor: " + spec, e );
298         }
299     }
300 
301     public Assembly readAssembly( Reader reader, final String locationDescription, final File assemblyDir,
302                                   final AssemblerConfigurationSource configSource )
303         throws AssemblyReadException, InvalidAssemblerConfigurationException
304     {
305         Assembly assembly;
306 
307         final MavenProject project = configSource.getProject();
308         try
309         {
310 
311             InterpolationState is = new InterpolationState();
312             final RecursionInterceptor interceptor =
313                 new PrefixAwareRecursionInterceptor( InterpolationConstants.PROJECT_PREFIXES, true );
314             is.setRecursionInterceptor( interceptor );
315 
316             FixedStringSearchInterpolator interpolator =
317                 AssemblyInterpolator.fullInterpolator( project, createProjectInterpolator( project ), configSource );
318             AssemblyXpp3Reader.ContentTransformer transformer =
319                 AssemblyInterpolator.assemblyInterpolator( interpolator, is, getLogger() );
320 
321             final AssemblyXpp3Reader r = new AssemblyXpp3Reader( transformer );
322             assembly = r.read( reader );
323 
324             ComponentXpp3Reader.ContentTransformer ctrans =
325                 AssemblyInterpolator.componentInterpolator( interpolator, is, getLogger() );
326             mergeComponentsWithMainAssembly( assembly, assemblyDir, configSource, ctrans );
327             debugPrintAssembly( "After assembly is interpolated:", assembly );
328 
329             AssemblyInterpolator.checkErrors( AssemblyId.createAssemblyId( assembly ), is, getLogger() );
330 
331             reader.close();
332             reader = null;
333         }
334         catch ( final IOException | XmlPullParserException e )
335         {
336             throw new AssemblyReadException( "Error reading descriptor: " + locationDescription + ": " + e.getMessage(),
337                                              e );
338         }
339         finally
340         {
341             IOUtil.close( reader );
342         }
343 
344         if ( assembly.isIncludeSiteDirectory() )
345         {
346             includeSiteInAssembly( assembly, configSource );
347         }
348 
349         return assembly;
350     }
351 
352     private void debugPrintAssembly( final String message, final Assembly assembly )
353     {
354         final StringWriter sWriter = new StringWriter();
355         try
356         {
357             new AssemblyXpp3Writer().write( sWriter, assembly );
358         }
359         catch ( final IOException e )
360         {
361             getLogger().debug( "Failed to print debug message with assembly descriptor listing, and message: "
362                 + message, e );
363         }
364 
365         getLogger().debug( message + "\n\n" + sWriter.toString() + "\n\n" );
366     }
367 
368     /**
369      * Add the contents of all included components to main assembly
370      *
371      * @param assembly The assembly
372      * @param assemblyDir The assembly directory
373      * @param transformer The component interpolator
374      * @throws AssemblyReadException .
375      */
376     protected void mergeComponentsWithMainAssembly( final Assembly assembly, final File assemblyDir,
377                                                     final AssemblerConfigurationSource configSource,
378                                                     ComponentXpp3Reader.ContentTransformer transformer )
379                                                         throws AssemblyReadException
380     {
381         final Locator locator = new Locator();
382 
383         if ( assemblyDir != null && assemblyDir.exists() && assemblyDir.isDirectory() )
384         {
385             locator.addStrategy( new RelativeFileLocatorStrategy( assemblyDir ) );
386         }
387 
388         // allow absolute paths in componentDescriptor... MASSEMBLY-486
389         locator.addStrategy( new RelativeFileLocatorStrategy( configSource.getBasedir() ) );
390         locator.addStrategy( new FileLocatorStrategy() );
391         locator.addStrategy( new ClasspathResourceLocatorStrategy() );
392 
393         final AssemblyExpressionEvaluatorolation/AssemblyExpressionEvaluator.html#AssemblyExpressionEvaluator">AssemblyExpressionEvaluator aee = new AssemblyExpressionEvaluator( configSource );
394 
395         final List<String> componentLocations = assembly.getComponentDescriptors();
396 
397         for ( String location : componentLocations )
398         {
399             // allow expressions in path to component descriptor... MASSEMBLY-486
400             try
401             {
402                 location = aee.evaluate( location ).toString();
403             }
404             catch ( final Exception eee )
405             {
406                 getLogger().error( "Error interpolating componentDescriptor: " + location, eee );
407             }
408 
409             final Location resolvedLocation = locator.resolve( location );
410 
411             if ( resolvedLocation == null )
412             {
413                 throw new AssemblyReadException( "Failed to locate component descriptor: " + location );
414             }
415 
416             Component component = null;
417             try ( Reader reader = new InputStreamReader( resolvedLocation.getInputStream() ) )
418             {
419                 component = new ComponentXpp3Reader( transformer ).read( reader );
420             }
421             catch ( final IOException | XmlPullParserException e )
422             {
423                 throw new AssemblyReadException( "Error reading component descriptor: " + location + " (resolved to: "
424                     + resolvedLocation.getSpecification() + ")", e );
425             }
426 
427             mergeComponentWithAssembly( component, assembly );
428         }
429     }
430 
431     /**
432      * Add the content of a single Component to main assembly
433      *
434      * @param component The component
435      * @param assembly The assembly
436      */
437     protected void mergeComponentWithAssembly( final Component component, final Assembly assembly )
438     {
439         final List<ContainerDescriptorHandlerConfig> containerHandlerDescriptors =
440             component.getContainerDescriptorHandlers();
441 
442         for ( final ContainerDescriptorHandlerConfig cfg : containerHandlerDescriptors )
443         {
444             assembly.addContainerDescriptorHandler( cfg );
445         }
446 
447         final List<DependencySet> dependencySetList = component.getDependencySets();
448 
449         for ( final DependencySet dependencySet : dependencySetList )
450         {
451             assembly.addDependencySet( dependencySet );
452         }
453 
454         final List<FileSet> fileSetList = component.getFileSets();
455 
456         for ( final FileSet fileSet : fileSetList )
457         {
458             assembly.addFileSet( fileSet );
459         }
460 
461         final List<FileItem> fileList = component.getFiles();
462 
463         for ( final FileItem fileItem : fileList )
464         {
465             assembly.addFile( fileItem );
466         }
467 
468         final List<Repository> repositoriesList = component.getRepositories();
469 
470         for ( final Repository repository : repositoriesList )
471         {
472             assembly.addRepository( repository );
473         }
474 
475         final List<ModuleSet> moduleSets = component.getModuleSets();
476         for ( final ModuleSet moduleSet : moduleSets )
477         {
478             assembly.addModuleSet( moduleSet );
479         }
480     }
481 
482     @Override
483     public void includeSiteInAssembly( final Assembly assembly, final AssemblerConfigurationSource configSource )
484         throws InvalidAssemblerConfigurationException
485     {
486         final File siteDirectory = configSource.getSiteDirectory();
487 
488         if ( !siteDirectory.exists() )
489         {
490             throw new InvalidAssemblerConfigurationException( "site did not exist in the target directory - "
491                 + "please run site:site before creating the assembly" );
492         }
493 
494         getLogger().info( "Adding site directory to assembly : " + siteDirectory );
495 
496         final FileSet siteFileSet = new FileSet();
497 
498         siteFileSet.setDirectory( siteDirectory.getPath() );
499 
500         siteFileSet.setOutputDirectory( "/site" );
501 
502         assembly.addFileSet( siteFileSet );
503     }
504 
505     @Override
506     protected Logger getLogger()
507     {
508         Logger logger = super.getLogger();
509 
510         if ( logger == null )
511         {
512             logger = new ConsoleLogger( Logger.LEVEL_INFO, "assemblyReader-internal" );
513             enableLogging( logger );
514         }
515 
516         return logger;
517     }
518 
519 }