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