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