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