1 package org.apache.maven.plugin.assembly.io;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
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
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
405
406
407
408
409
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
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
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
479
480
481
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 }