| package org.apache.maven.plugin.assembly.io; |
| |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.io.Reader; |
| import java.io.StringWriter; |
| import java.io.UnsupportedEncodingException; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Enumeration; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Properties; |
| import java.util.Set; |
| |
| import org.apache.maven.execution.MavenSession; |
| import org.apache.maven.plugin.assembly.AssemblerConfigurationSource; |
| import org.apache.maven.plugin.assembly.InvalidAssemblerConfigurationException; |
| import org.apache.maven.plugin.assembly.interpolation.AssemblyExpressionEvaluator; |
| import org.apache.maven.plugin.assembly.interpolation.AssemblyInterpolationException; |
| import org.apache.maven.plugin.assembly.interpolation.AssemblyInterpolator; |
| import org.apache.maven.plugin.assembly.model.Assembly; |
| import org.apache.maven.plugin.assembly.model.Component; |
| import org.apache.maven.plugin.assembly.model.ContainerDescriptorHandlerConfig; |
| import org.apache.maven.plugin.assembly.model.DependencySet; |
| import org.apache.maven.plugin.assembly.model.FileItem; |
| import org.apache.maven.plugin.assembly.model.FileSet; |
| import org.apache.maven.plugin.assembly.model.ModuleSet; |
| import org.apache.maven.plugin.assembly.model.Repository; |
| import org.apache.maven.plugin.assembly.model.io.xpp3.AssemblyXpp3Reader; |
| import org.apache.maven.plugin.assembly.model.io.xpp3.AssemblyXpp3Writer; |
| import org.apache.maven.plugin.assembly.model.io.xpp3.ComponentXpp3Reader; |
| import org.apache.maven.project.MavenProject; |
| import org.apache.maven.shared.io.location.ClasspathResourceLocatorStrategy; |
| import org.apache.maven.shared.io.location.FileLocatorStrategy; |
| import org.apache.maven.shared.io.location.Location; |
| import org.apache.maven.shared.io.location.Locator; |
| import org.apache.maven.shared.io.location.LocatorStrategy; |
| import org.codehaus.plexus.logging.AbstractLogEnabled; |
| import org.codehaus.plexus.logging.Logger; |
| import org.codehaus.plexus.logging.console.ConsoleLogger; |
| import org.codehaus.plexus.util.DirectoryScanner; |
| import org.codehaus.plexus.util.IOUtil; |
| import org.codehaus.plexus.util.xml.pull.XmlPullParserException; |
| |
| /** |
| * @version $Id$ |
| */ |
| @org.codehaus.plexus.component.annotations.Component( role = AssemblyReader.class ) |
| public class DefaultAssemblyReader |
| extends AbstractLogEnabled |
| implements AssemblyReader |
| { |
| |
| public List<Assembly> readAssemblies( final AssemblerConfigurationSource configSource ) |
| throws AssemblyReadException, InvalidAssemblerConfigurationException |
| { |
| final Locator locator = new Locator(); |
| |
| final List<LocatorStrategy> strategies = new ArrayList<LocatorStrategy>(); |
| strategies.add( new RelativeFileLocatorStrategy( configSource.getBasedir() ) ); |
| strategies.add( new FileLocatorStrategy() ); |
| |
| final List<LocatorStrategy> refStrategies = new ArrayList<LocatorStrategy>(); |
| refStrategies.add( new PrefixedClasspathLocatorStrategy( "/assemblies/" ) ); |
| |
| final List<Assembly> assemblies = new ArrayList<Assembly>(); |
| |
| final String descriptor = configSource.getDescriptor(); |
| final String descriptorId = configSource.getDescriptorId(); |
| final String[] descriptors = configSource.getDescriptors(); |
| final String[] descriptorRefs = configSource.getDescriptorReferences(); |
| final File descriptorSourceDirectory = configSource.getDescriptorSourceDirectory(); |
| |
| if ( descriptor != null ) |
| { |
| locator.setStrategies( strategies ); |
| addAssemblyFromDescriptor( descriptor, locator, configSource, assemblies ); |
| } |
| |
| if ( descriptorId != null ) |
| { |
| locator.setStrategies( refStrategies ); |
| addAssemblyForDescriptorReference( descriptorId, configSource, assemblies ); |
| } |
| |
| if ( ( descriptors != null ) && ( descriptors.length > 0 ) ) |
| { |
| locator.setStrategies( strategies ); |
| for ( String descriptor1 : descriptors ) |
| { |
| getLogger().info( "Reading assembly descriptor: " + descriptor1 ); |
| addAssemblyFromDescriptor( descriptor1, locator, configSource, assemblies ); |
| } |
| } |
| |
| if ( ( descriptorRefs != null ) && ( descriptorRefs.length > 0 ) ) |
| { |
| locator.setStrategies( refStrategies ); |
| for ( String descriptorRef : descriptorRefs ) |
| { |
| addAssemblyForDescriptorReference( descriptorRef, configSource, assemblies ); |
| } |
| } |
| |
| if ( ( descriptorSourceDirectory != null ) && descriptorSourceDirectory.isDirectory() ) |
| { |
| locator.setStrategies( Collections.singletonList( new RelativeFileLocatorStrategy( |
| descriptorSourceDirectory ) ) ); |
| |
| final DirectoryScanner scanner = new DirectoryScanner(); |
| scanner.setBasedir( descriptorSourceDirectory ); |
| scanner.setIncludes( new String[] { "**/*.xml" } ); |
| scanner.addDefaultExcludes(); |
| |
| try |
| { |
| scanner.scan(); |
| } |
| // FIXME: plexus-utils >= 1.3-SNAPSHOT should fix this. |
| catch ( final NullPointerException e ) |
| { |
| final StackTraceElement frameZero = e.getStackTrace()[0]; |
| |
| if ( "org.codehaus.plexus.util.DirectoryScanner".equals( frameZero.getClassName() ) |
| && "scandir".equals( frameZero.getMethodName() ) ) |
| { |
| if ( getLogger().isDebugEnabled() ) |
| { |
| getLogger().debug( "Caught filesystem error while scanning directories..." |
| + "using zero-length list as the result.", e ); |
| } |
| } |
| else |
| { |
| throw e; |
| } |
| } |
| |
| final String[] paths = scanner.getIncludedFiles(); |
| |
| if ( paths != null ) |
| { |
| for ( String path : paths ) |
| { |
| addAssemblyFromDescriptor( path, locator, configSource, assemblies ); |
| } |
| } |
| } |
| |
| if ( assemblies.isEmpty() ) |
| { |
| if ( configSource.isIgnoreMissingDescriptor() ) |
| { |
| getLogger().debug( "Ignoring missing assembly descriptors per configuration. See messages above for specifics." ); |
| } |
| else |
| { |
| throw new AssemblyReadException( "No assembly descriptors found." ); |
| } |
| } |
| |
| // check unique IDs |
| final Set<String> ids = new HashSet<String>(); |
| for ( final Assembly assembly : assemblies ) |
| { |
| if ( !ids.add( assembly.getId() ) ) |
| { |
| getLogger().warn( "The assembly id " + assembly.getId() + " is used more than once." ); |
| } |
| |
| } |
| return assemblies; |
| } |
| |
| public Assembly getAssemblyForDescriptorReference( final String ref, final AssemblerConfigurationSource configSource ) |
| throws AssemblyReadException, InvalidAssemblerConfigurationException |
| { |
| return addAssemblyForDescriptorReference( ref, configSource, new ArrayList<Assembly>( 1 ) ); |
| } |
| |
| public Assembly getAssemblyFromDescriptorFile( final File file, final AssemblerConfigurationSource configSource ) |
| throws AssemblyReadException, InvalidAssemblerConfigurationException |
| { |
| return addAssemblyFromDescriptorFile( file, configSource, new ArrayList<Assembly>( 1 ) ); |
| } |
| |
| private Assembly addAssemblyForDescriptorReference( final String ref, |
| final AssemblerConfigurationSource configSource, |
| final List<Assembly> assemblies ) |
| throws AssemblyReadException, InvalidAssemblerConfigurationException |
| { |
| final InputStream resourceAsStream = |
| Thread.currentThread().getContextClassLoader().getResourceAsStream( "assemblies/" + ref + ".xml" ); |
| |
| if ( resourceAsStream == null ) |
| { |
| if ( configSource.isIgnoreMissingDescriptor() ) |
| { |
| getLogger().debug( "Ignoring missing assembly descriptor with ID '" + ref + "' per configuration." ); |
| return null; |
| } |
| else |
| { |
| throw new AssemblyReadException( "Descriptor with ID '" + ref + "' not found" ); |
| } |
| } |
| |
| try |
| { |
| // TODO use ReaderFactory.newXmlReader() when plexus-utils is upgraded to 1.4.5+ |
| final Assembly assembly = |
| readAssembly( new InputStreamReader( resourceAsStream, "UTF-8" ), ref, null, configSource ); |
| |
| assemblies.add( assembly ); |
| return assembly; |
| } |
| catch ( final UnsupportedEncodingException e ) |
| { |
| // should not occur since UTF-8 support is mandatory |
| throw new AssemblyReadException( "Encoding not supported for descriptor with ID '" + ref + "'" ); |
| } |
| } |
| |
| private Assembly addAssemblyFromDescriptorFile( final File descriptor, |
| final AssemblerConfigurationSource configSource, |
| final List<Assembly> assemblies ) |
| throws AssemblyReadException, InvalidAssemblerConfigurationException |
| { |
| if ( !descriptor.exists() ) |
| { |
| if ( configSource.isIgnoreMissingDescriptor() ) |
| { |
| getLogger().debug( "Ignoring missing assembly descriptor: '" + descriptor + "' per configuration." ); |
| return null; |
| } |
| else |
| { |
| throw new AssemblyReadException( "Descriptor: '" + descriptor + "' not found" ); |
| } |
| } |
| |
| Reader r = null; |
| try |
| { |
| // TODO use ReaderFactory.newXmlReader() when plexus-utils is upgraded to 1.4.5+ |
| r = new InputStreamReader( new FileInputStream( descriptor ), "UTF-8" ); |
| final Assembly assembly = |
| readAssembly( r, descriptor.getAbsolutePath(), descriptor.getParentFile(), configSource ); |
| |
| assemblies.add( assembly ); |
| |
| return assembly; |
| } |
| catch ( final IOException e ) |
| { |
| throw new AssemblyReadException( "Error reading assembly descriptor: " + descriptor, e ); |
| } |
| finally |
| { |
| IOUtil.close( r ); |
| } |
| } |
| |
| private Assembly addAssemblyFromDescriptor( final String spec, final Locator locator, |
| final AssemblerConfigurationSource configSource, |
| final List<Assembly> assemblies ) |
| throws AssemblyReadException, InvalidAssemblerConfigurationException |
| { |
| final Location location = locator.resolve( spec ); |
| |
| if ( location == null ) |
| { |
| if ( configSource.isIgnoreMissingDescriptor() ) |
| { |
| getLogger().debug( "Ignoring missing assembly descriptor with ID '" + spec |
| + "' per configuration.\nLocator output was:\n\n" |
| + locator.getMessageHolder().render() ); |
| return null; |
| } |
| else |
| { |
| throw new AssemblyReadException( "Error locating assembly descriptor: " + spec + "\n\n" |
| + locator.getMessageHolder().render() ); |
| } |
| } |
| |
| Reader r = null; |
| try |
| { |
| // TODO use ReaderFactory.newXmlReader() when plexus-utils is upgraded to 1.4.5+ |
| r = new InputStreamReader( location.getInputStream(), "UTF-8" ); |
| |
| File dir = null; |
| if ( location.getFile() != null ) |
| { |
| dir = location.getFile().getParentFile(); |
| } |
| |
| final Assembly assembly = readAssembly( r, spec, dir, configSource ); |
| |
| assemblies.add( assembly ); |
| |
| return assembly; |
| } |
| catch ( final IOException e ) |
| { |
| throw new AssemblyReadException( "Error reading assembly descriptor: " + spec, e ); |
| } |
| finally |
| { |
| IOUtil.close( r ); |
| } |
| |
| } |
| |
| protected Assembly readAssembly( final Reader reader, final String locationDescription, final File assemblyDir, |
| final AssemblerConfigurationSource configSource ) |
| throws AssemblyReadException, InvalidAssemblerConfigurationException |
| { |
| Assembly assembly; |
| |
| final File basedir = configSource.getBasedir(); |
| final MavenProject project = configSource.getProject(); |
| |
| try |
| { |
| final Map<String, String> context = new HashMap<String, String>(); |
| final MavenSession session = configSource.getMavenSession(); |
| |
| Properties commandLineProperties = mergeExecutionPropertiesWithSystemPropertiew( session ); |
| |
| for ( final Enumeration<Object> e = commandLineProperties.keys(); e.hasMoreElements(); ) |
| { |
| final String key = (String) e.nextElement(); |
| if ( key == null || key.trim().length() < 1 ) |
| { |
| continue; |
| } |
| |
| context.put( key, commandLineProperties.getProperty( key ) ); |
| } |
| |
| context.put( "basedir", basedir.getAbsolutePath() ); |
| |
| final AssemblyXpp3Reader r = new AssemblyXpp3Reader(); |
| assembly = r.read( reader ); |
| |
| mergeComponentsWithMainAssembly( assembly, assemblyDir, configSource ); |
| |
| debugPrintAssembly( "Before assembly is interpolated:", assembly ); |
| |
| assembly = new AssemblyInterpolator().interpolate( assembly, project, configSource ); |
| |
| debugPrintAssembly( "After assembly is interpolated:", assembly ); |
| } |
| catch ( final IOException e ) |
| { |
| throw new AssemblyReadException( |
| "Error reading descriptor: " + locationDescription + ": " + e.getMessage(), |
| e ); |
| } |
| catch ( final XmlPullParserException e ) |
| { |
| throw new AssemblyReadException( |
| "Error reading descriptor: " + locationDescription + ": " + e.getMessage(), |
| e ); |
| } |
| catch ( final AssemblyInterpolationException e ) |
| { |
| throw new AssemblyReadException( |
| "Error reading descriptor: " + locationDescription + ": " + e.getMessage(), |
| e ); |
| } |
| finally |
| { |
| IOUtil.close( reader ); |
| } |
| |
| if ( configSource.isSiteIncluded() || assembly.isIncludeSiteDirectory() ) |
| { |
| includeSiteInAssembly( assembly, configSource ); |
| } |
| |
| return assembly; |
| } |
| |
| public static Properties mergeExecutionPropertiesWithSystemPropertiew( MavenSession session ) |
| { |
| Properties commandLineProperties = System.getProperties(); |
| if ( session != null ) |
| { |
| commandLineProperties = new Properties(); |
| if ( session.getExecutionProperties() != null ) |
| { |
| commandLineProperties.putAll( session.getExecutionProperties() ); |
| } |
| |
| if ( session.getUserProperties() != null ) |
| { |
| commandLineProperties.putAll( session.getUserProperties() ); |
| } |
| } |
| return commandLineProperties; |
| } |
| |
| private void debugPrintAssembly( final String message, final Assembly assembly ) |
| { |
| final StringWriter sWriter = new StringWriter(); |
| try |
| { |
| new AssemblyXpp3Writer().write( sWriter, assembly ); |
| } |
| catch ( final IOException e ) |
| { |
| getLogger().debug( "Failed to print debug message with assembly descriptor listing, and message: " |
| + message, e ); |
| } |
| |
| getLogger().debug( message + "\n\n" + sWriter.toString() + "\n\n" ); |
| } |
| |
| /** |
| * Add the contents of all included components to main assembly |
| * |
| * @param assembly |
| * @param assemblyDir |
| * @throws AssemblyReadException |
| */ |
| protected void mergeComponentsWithMainAssembly( final Assembly assembly, final File assemblyDir, |
| final AssemblerConfigurationSource configSource ) |
| throws AssemblyReadException |
| { |
| final Locator locator = new Locator(); |
| |
| if ( assemblyDir != null && assemblyDir.exists() && assemblyDir.isDirectory() ) |
| { |
| locator.addStrategy( new RelativeFileLocatorStrategy( assemblyDir ) ); |
| } |
| |
| // allow absolute paths in componentDescriptor... MASSEMBLY-486 |
| locator.addStrategy( new RelativeFileLocatorStrategy( configSource.getBasedir() ) ); |
| locator.addStrategy( new FileLocatorStrategy() ); |
| locator.addStrategy( new ClasspathResourceLocatorStrategy() ); |
| |
| final AssemblyExpressionEvaluator aee = new AssemblyExpressionEvaluator( configSource ); |
| |
| final List<String> componentLocations = assembly.getComponentDescriptors(); |
| |
| for ( String location : componentLocations ) |
| { |
| // allow expressions in path to component descriptor... MASSEMBLY-486 |
| try |
| { |
| location = aee.evaluate( location ).toString(); |
| } |
| catch ( final Exception eee ) |
| { |
| getLogger().error( "Error interpolating componentDescriptor: " + location, eee ); |
| } |
| |
| final Location resolvedLocation = locator.resolve( location ); |
| |
| if ( resolvedLocation == null ) |
| { |
| throw new AssemblyReadException( "Failed to locate component descriptor: " + location ); |
| } |
| |
| Component component = null; |
| Reader reader = null; |
| try |
| { |
| reader = new InputStreamReader( resolvedLocation.getInputStream() ); |
| component = new ComponentXpp3Reader().read( reader ); |
| } |
| catch ( final IOException e ) |
| { |
| throw new AssemblyReadException( "Error reading component descriptor: " + location + " (resolved to: " |
| + resolvedLocation.getSpecification() + ")", e ); |
| } |
| catch ( final XmlPullParserException e ) |
| { |
| throw new AssemblyReadException( "Error reading component descriptor: " + location + " (resolved to: " |
| + resolvedLocation.getSpecification() + ")", e ); |
| } |
| finally |
| { |
| IOUtil.close( reader ); |
| } |
| |
| mergeComponentWithAssembly( component, assembly ); |
| } |
| } |
| |
| /** |
| * Add the content of a single Component to main assembly |
| * |
| * @param component |
| * @param assembly |
| */ |
| protected void mergeComponentWithAssembly( final Component component, final Assembly assembly ) |
| { |
| final List<ContainerDescriptorHandlerConfig> containerHandlerDescriptors = |
| component.getContainerDescriptorHandlers(); |
| |
| for ( final ContainerDescriptorHandlerConfig cfg : containerHandlerDescriptors ) |
| { |
| assembly.addContainerDescriptorHandler( cfg ); |
| } |
| |
| final List<DependencySet> dependencySetList = component.getDependencySets(); |
| |
| for ( final DependencySet dependencySet : dependencySetList ) |
| { |
| assembly.addDependencySet( dependencySet ); |
| } |
| |
| final List<FileSet> fileSetList = component.getFileSets(); |
| |
| for ( final FileSet fileSet : fileSetList ) |
| { |
| assembly.addFileSet( fileSet ); |
| } |
| |
| final List<FileItem> fileList = component.getFiles(); |
| |
| for ( final FileItem fileItem : fileList ) |
| { |
| assembly.addFile( fileItem ); |
| } |
| |
| final List<Repository> repositoriesList = component.getRepositories(); |
| |
| for ( final Repository repository : repositoriesList ) |
| { |
| assembly.addRepository( repository ); |
| } |
| |
| final List<ModuleSet> moduleSets = component.getModuleSets(); |
| for ( final ModuleSet moduleSet : moduleSets ) |
| { |
| assembly.addModuleSet( moduleSet ); |
| } |
| } |
| |
| public void includeSiteInAssembly( final Assembly assembly, final AssemblerConfigurationSource configSource ) |
| throws InvalidAssemblerConfigurationException |
| { |
| final File siteDirectory = configSource.getSiteDirectory(); |
| |
| if ( !siteDirectory.exists() ) |
| { |
| throw new InvalidAssemblerConfigurationException( |
| "site did not exist in the target directory - please run site:site before creating the assembly" ); |
| } |
| |
| getLogger().info( "Adding site directory to assembly : " + siteDirectory ); |
| |
| final FileSet siteFileSet = new FileSet(); |
| |
| siteFileSet.setDirectory( siteDirectory.getPath() ); |
| |
| siteFileSet.setOutputDirectory( "/site" ); |
| |
| assembly.addFileSet( siteFileSet ); |
| } |
| |
| @Override |
| protected Logger getLogger() |
| { |
| Logger logger = super.getLogger(); |
| |
| if ( logger == null ) |
| { |
| logger = new ConsoleLogger( Logger.LEVEL_INFO, "assemblyReader-internal" ); |
| enableLogging( logger ); |
| } |
| |
| return logger; |
| } |
| |
| } |