| /* |
| * 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. |
| */ |
| package org.apache.maven.plugins.assembly.io; |
| |
| import javax.inject.Named; |
| import javax.inject.Singleton; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.io.Reader; |
| import java.io.StringWriter; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.apache.commons.io.input.XmlStreamReader; |
| import org.apache.maven.plugins.assembly.AssemblerConfigurationSource; |
| import org.apache.maven.plugins.assembly.InvalidAssemblerConfigurationException; |
| import org.apache.maven.plugins.assembly.interpolation.AssemblyExpressionEvaluator; |
| import org.apache.maven.plugins.assembly.interpolation.AssemblyInterpolator; |
| import org.apache.maven.plugins.assembly.model.Assembly; |
| import org.apache.maven.plugins.assembly.model.Component; |
| import org.apache.maven.plugins.assembly.model.ContainerDescriptorHandlerConfig; |
| import org.apache.maven.plugins.assembly.model.DependencySet; |
| import org.apache.maven.plugins.assembly.model.FileItem; |
| import org.apache.maven.plugins.assembly.model.FileSet; |
| import org.apache.maven.plugins.assembly.model.ModuleSet; |
| import org.apache.maven.plugins.assembly.model.io.xpp3.AssemblyXpp3Reader; |
| import org.apache.maven.plugins.assembly.model.io.xpp3.AssemblyXpp3Writer; |
| import org.apache.maven.plugins.assembly.model.io.xpp3.ComponentXpp3Reader; |
| import org.apache.maven.plugins.assembly.resolved.AssemblyId; |
| import org.apache.maven.plugins.assembly.utils.InterpolationConstants; |
| import org.apache.maven.project.MavenProject; |
| import org.codehaus.plexus.interpolation.PrefixAwareRecursionInterceptor; |
| import org.codehaus.plexus.interpolation.RecursionInterceptor; |
| import org.codehaus.plexus.interpolation.fixed.FixedStringSearchInterpolator; |
| import org.codehaus.plexus.interpolation.fixed.InterpolationState; |
| import org.codehaus.plexus.interpolation.fixed.PrefixedObjectValueSource; |
| import org.codehaus.plexus.interpolation.fixed.PrefixedPropertiesValueSource; |
| import org.codehaus.plexus.util.DirectoryScanner; |
| import org.codehaus.plexus.util.xml.pull.XmlPullParserException; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * |
| */ |
| @Singleton |
| @Named |
| public class DefaultAssemblyReader implements AssemblyReader { |
| private static final Logger LOGGER = LoggerFactory.getLogger(DefaultAssemblyReader.class); |
| |
| public static FixedStringSearchInterpolator createProjectInterpolator(MavenProject project) { |
| // CHECKSTYLE_OFF: LineLength |
| return FixedStringSearchInterpolator.create( |
| new PrefixedPropertiesValueSource( |
| InterpolationConstants.PROJECT_PROPERTIES_PREFIXES, project.getProperties(), true), |
| new PrefixedObjectValueSource(InterpolationConstants.PROJECT_PREFIXES, project, true)); |
| // CHECKSTYLE_ON: LineLength |
| } |
| |
| @Override |
| public List<Assembly> readAssemblies(final AssemblerConfigurationSource configSource) |
| throws AssemblyReadException, InvalidAssemblerConfigurationException { |
| final Locator locator = new Locator(); |
| |
| final List<LocatorStrategy> strategies = new ArrayList<>(); |
| strategies.add(new RelativeFileLocatorStrategy(configSource.getBasedir())); |
| strategies.add(new FileLocatorStrategy()); |
| |
| final List<LocatorStrategy> refStrategies = new ArrayList<>(); |
| refStrategies.add(new PrefixedClasspathLocatorStrategy("/assemblies/")); |
| |
| final List<Assembly> assemblies = new ArrayList<>(); |
| |
| final String[] descriptors = configSource.getDescriptors(); |
| final String[] descriptorRefs = configSource.getDescriptorReferences(); |
| final File descriptorSourceDirectory = configSource.getDescriptorSourceDirectory(); |
| final List<Assembly> inlineDescriptors = configSource.getInlineDescriptors(); |
| |
| if ((descriptors != null) && (descriptors.length > 0)) { |
| locator.setStrategies(strategies); |
| for (String descriptor1 : descriptors) { |
| LOGGER.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(); |
| |
| scanner.scan(); |
| |
| final String[] paths = scanner.getIncludedFiles(); |
| |
| for (String path : paths) { |
| addAssemblyFromDescriptor(path, locator, configSource, assemblies); |
| } |
| } |
| |
| if (inlineDescriptors != null) { |
| assemblies.addAll(inlineDescriptors); |
| } |
| |
| if (assemblies.isEmpty()) { |
| if (configSource.isIgnoreMissingDescriptor()) { |
| LOGGER.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<>(); |
| for (final Assembly assembly : assemblies) { |
| if (!ids.add(assembly.getId())) { |
| LOGGER.warn("The assembly id " + assembly.getId() + " is used more than once."); |
| } |
| } |
| return assemblies; |
| } |
| |
| @Override |
| public Assembly getAssemblyForDescriptorReference(final String ref, final AssemblerConfigurationSource configSource) |
| throws AssemblyReadException, InvalidAssemblerConfigurationException { |
| return addAssemblyForDescriptorReference(ref, configSource, new ArrayList<>(1)); |
| } |
| |
| @Override |
| public Assembly getAssemblyFromDescriptorFile(final File file, final AssemblerConfigurationSource configSource) |
| throws AssemblyReadException, InvalidAssemblerConfigurationException { |
| return addAssemblyFromDescriptorFile(file, configSource, new ArrayList<>(1)); |
| } |
| |
| private Assembly addAssemblyForDescriptorReference( |
| final String ref, final AssemblerConfigurationSource configSource, final List<Assembly> assemblies) |
| throws AssemblyReadException, InvalidAssemblerConfigurationException { |
| final InputStream resourceAsStream = getClass().getResourceAsStream("/assemblies/" + ref + ".xml"); |
| |
| if (resourceAsStream == null) { |
| if (configSource.isIgnoreMissingDescriptor()) { |
| LOGGER.debug("Ignoring missing assembly descriptor with ID '" + ref + "' per configuration."); |
| return null; |
| } else { |
| throw new AssemblyReadException("Descriptor with ID '" + ref + "' not found"); |
| } |
| } |
| |
| try (Reader reader = |
| XmlStreamReader.builder().setInputStream(resourceAsStream).get()) { |
| final Assembly assembly = readAssembly(reader, ref, null, configSource); |
| assemblies.add(assembly); |
| return assembly; |
| } catch (final IOException e) { |
| throw new AssemblyReadException("Problem with descriptor with ID '" + ref + "'", e); |
| } |
| } |
| |
| private Assembly addAssemblyFromDescriptorFile( |
| final File descriptor, final AssemblerConfigurationSource configSource, final List<Assembly> assemblies) |
| throws AssemblyReadException, InvalidAssemblerConfigurationException { |
| if (!descriptor.exists()) { |
| if (configSource.isIgnoreMissingDescriptor()) { |
| LOGGER.debug("Ignoring missing assembly descriptor: '" + descriptor + "' per configuration."); |
| return null; |
| } else { |
| throw new AssemblyReadException("Descriptor: '" + descriptor + "' not found"); |
| } |
| } |
| |
| try (Reader r = XmlStreamReader.builder().setFile(descriptor).get()) { |
| 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); |
| } |
| } |
| |
| 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()) { |
| LOGGER.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()); |
| } |
| } |
| |
| try (Reader r = XmlStreamReader.builder() |
| .setInputStream(location.getInputStream()) |
| .get()) { |
| 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); |
| } |
| } |
| |
| public Assembly readAssembly( |
| Reader reader, |
| final String locationDescription, |
| final File assemblyDir, |
| final AssemblerConfigurationSource configSource) |
| throws AssemblyReadException, InvalidAssemblerConfigurationException { |
| Assembly assembly; |
| |
| final MavenProject project = configSource.getProject(); |
| try { |
| |
| InterpolationState is = new InterpolationState(); |
| final RecursionInterceptor interceptor = |
| new PrefixAwareRecursionInterceptor(InterpolationConstants.PROJECT_PREFIXES, true); |
| is.setRecursionInterceptor(interceptor); |
| |
| FixedStringSearchInterpolator interpolator = |
| AssemblyInterpolator.fullInterpolator(project, createProjectInterpolator(project), configSource); |
| AssemblyXpp3Reader.ContentTransformer transformer = |
| AssemblyInterpolator.assemblyInterpolator(interpolator, is, LOGGER); |
| |
| final AssemblyXpp3Reader r = new AssemblyXpp3Reader(transformer); |
| assembly = r.read(reader); |
| |
| ComponentXpp3Reader.ContentTransformer ctrans = |
| AssemblyInterpolator.componentInterpolator(interpolator, is, LOGGER); |
| mergeComponentsWithMainAssembly(assembly, assemblyDir, configSource, ctrans); |
| debugPrintAssembly("After assembly is interpolated:", assembly); |
| |
| AssemblyInterpolator.checkErrors(AssemblyId.createAssemblyId(assembly), is, LOGGER); |
| |
| } catch (final IOException | XmlPullParserException e) { |
| throw new AssemblyReadException( |
| "Error reading descriptor: " + locationDescription + ": " + e.getMessage(), e); |
| } |
| |
| if (assembly.isIncludeSiteDirectory()) { |
| includeSiteInAssembly(assembly, configSource); |
| } |
| |
| return assembly; |
| } |
| |
| private void debugPrintAssembly(final String message, final Assembly assembly) { |
| final StringWriter sWriter = new StringWriter(); |
| try { |
| new AssemblyXpp3Writer().write(sWriter, assembly); |
| } catch (final IOException e) { |
| LOGGER.debug("Failed to print debug message with assembly descriptor listing, and message: " + message, e); |
| } |
| |
| LOGGER.debug(message + "\n\n" + sWriter + "\n\n"); |
| } |
| |
| /** |
| * Add the contents of all included components to main assembly |
| * |
| * @param assembly the assembly |
| * @param assemblyDir the assembly directory |
| * @param transformer the component interpolator |
| * @throws AssemblyReadException |
| */ |
| protected void mergeComponentsWithMainAssembly( |
| final Assembly assembly, |
| final File assemblyDir, |
| final AssemblerConfigurationSource configSource, |
| ComponentXpp3Reader.ContentTransformer transformer) |
| 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) { |
| LOGGER.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; |
| try (Reader reader = new InputStreamReader(resolvedLocation.getInputStream())) { |
| component = new ComponentXpp3Reader(transformer).read(reader); |
| } catch (final IOException | XmlPullParserException e) { |
| throw new AssemblyReadException( |
| "Error reading component descriptor: " + location + " (resolved to: " |
| + resolvedLocation.getSpecification() + ")", |
| e); |
| } |
| |
| mergeComponentWithAssembly(component, assembly); |
| } |
| } |
| |
| /** |
| * Add the content of a single Component to main assembly |
| * |
| * @param component The component |
| * @param assembly The 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<ModuleSet> moduleSets = component.getModuleSets(); |
| for (final ModuleSet moduleSet : moduleSets) { |
| assembly.addModuleSet(moduleSet); |
| } |
| } |
| |
| @Override |
| 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"); |
| } |
| |
| LOGGER.info("Adding site directory to assembly : " + siteDirectory); |
| |
| final FileSet siteFileSet = new FileSet(); |
| |
| siteFileSet.setDirectory(siteDirectory.getPath()); |
| |
| siteFileSet.setOutputDirectory("/site"); |
| |
| assembly.addFileSet(siteFileSet); |
| } |
| } |