1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugins.assembly.io;
20
21 import javax.inject.Named;
22 import javax.inject.Singleton;
23
24 import java.io.File;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.InputStreamReader;
28 import java.io.Reader;
29 import java.io.StringWriter;
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.HashSet;
33 import java.util.List;
34 import java.util.Set;
35
36 import org.apache.commons.io.input.XmlStreamReader;
37 import org.apache.maven.plugins.assembly.AssemblerConfigurationSource;
38 import org.apache.maven.plugins.assembly.InvalidAssemblerConfigurationException;
39 import org.apache.maven.plugins.assembly.interpolation.AssemblyExpressionEvaluator;
40 import org.apache.maven.plugins.assembly.interpolation.AssemblyInterpolator;
41 import org.apache.maven.plugins.assembly.model.Assembly;
42 import org.apache.maven.plugins.assembly.model.Component;
43 import org.apache.maven.plugins.assembly.model.ContainerDescriptorHandlerConfig;
44 import org.apache.maven.plugins.assembly.model.DependencySet;
45 import org.apache.maven.plugins.assembly.model.FileItem;
46 import org.apache.maven.plugins.assembly.model.FileSet;
47 import org.apache.maven.plugins.assembly.model.ModuleSet;
48 import org.apache.maven.plugins.assembly.model.io.xpp3.AssemblyXpp3Reader;
49 import org.apache.maven.plugins.assembly.model.io.xpp3.AssemblyXpp3Writer;
50 import org.apache.maven.plugins.assembly.model.io.xpp3.ComponentXpp3Reader;
51 import org.apache.maven.plugins.assembly.resolved.AssemblyId;
52 import org.apache.maven.plugins.assembly.utils.InterpolationConstants;
53 import org.apache.maven.project.MavenProject;
54 import org.codehaus.plexus.interpolation.PrefixAwareRecursionInterceptor;
55 import org.codehaus.plexus.interpolation.RecursionInterceptor;
56 import org.codehaus.plexus.interpolation.fixed.FixedStringSearchInterpolator;
57 import org.codehaus.plexus.interpolation.fixed.InterpolationState;
58 import org.codehaus.plexus.interpolation.fixed.PrefixedObjectValueSource;
59 import org.codehaus.plexus.interpolation.fixed.PrefixedPropertiesValueSource;
60 import org.codehaus.plexus.util.DirectoryScanner;
61 import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
62 import org.slf4j.Logger;
63 import org.slf4j.LoggerFactory;
64
65
66
67
68 @Singleton
69 @Named
70 public class DefaultAssemblyReader implements AssemblyReader {
71 private static final Logger LOGGER = LoggerFactory.getLogger(DefaultAssemblyReader.class);
72
73 public static FixedStringSearchInterpolator createProjectInterpolator(MavenProject project) {
74
75 return FixedStringSearchInterpolator.create(
76 new PrefixedPropertiesValueSource(
77 InterpolationConstants.PROJECT_PROPERTIES_PREFIXES, project.getProperties(), true),
78 new PrefixedObjectValueSource(InterpolationConstants.PROJECT_PREFIXES, project, true));
79
80 }
81
82 @Override
83 public List<Assembly> readAssemblies(final AssemblerConfigurationSource configSource)
84 throws AssemblyReadException, InvalidAssemblerConfigurationException {
85 final Locator locator = new Locator();
86
87 final List<LocatorStrategy> strategies = new ArrayList<>();
88 strategies.add(new RelativeFileLocatorStrategy(configSource.getBasedir()));
89 strategies.add(new FileLocatorStrategy());
90
91 final List<LocatorStrategy> refStrategies = new ArrayList<>();
92 refStrategies.add(new PrefixedClasspathLocatorStrategy("/assemblies/"));
93
94 final List<Assembly> assemblies = new ArrayList<>();
95
96 final String[] descriptors = configSource.getDescriptors();
97 final String[] descriptorRefs = configSource.getDescriptorReferences();
98 final File descriptorSourceDirectory = configSource.getDescriptorSourceDirectory();
99 final List<Assembly> inlineDescriptors = configSource.getInlineDescriptors();
100
101 if ((descriptors != null) && (descriptors.length > 0)) {
102 locator.setStrategies(strategies);
103 for (String descriptor1 : descriptors) {
104 LOGGER.info("Reading assembly descriptor: " + descriptor1);
105 addAssemblyFromDescriptor(descriptor1, locator, configSource, assemblies);
106 }
107 }
108
109 if ((descriptorRefs != null) && (descriptorRefs.length > 0)) {
110 locator.setStrategies(refStrategies);
111 for (String descriptorRef : descriptorRefs) {
112 addAssemblyForDescriptorReference(descriptorRef, configSource, assemblies);
113 }
114 }
115
116 if ((descriptorSourceDirectory != null) && descriptorSourceDirectory.isDirectory()) {
117 locator.setStrategies(
118 Collections.singletonList(new RelativeFileLocatorStrategy(descriptorSourceDirectory)));
119
120 final DirectoryScanner scanner = new DirectoryScanner();
121 scanner.setBasedir(descriptorSourceDirectory);
122 scanner.setIncludes(new String[] {"**/*.xml"});
123 scanner.addDefaultExcludes();
124
125 scanner.scan();
126
127 final String[] paths = scanner.getIncludedFiles();
128
129 for (String path : paths) {
130 addAssemblyFromDescriptor(path, locator, configSource, assemblies);
131 }
132 }
133
134 if (inlineDescriptors != null) {
135 assemblies.addAll(inlineDescriptors);
136 }
137
138 if (assemblies.isEmpty()) {
139 if (configSource.isIgnoreMissingDescriptor()) {
140 LOGGER.debug("Ignoring missing assembly descriptors per configuration. "
141 + "See messages above for specifics.");
142 } else {
143 throw new AssemblyReadException("No assembly descriptors found.");
144 }
145 }
146
147
148 final Set<String> ids = new HashSet<>();
149 for (final Assembly assembly : assemblies) {
150 if (!ids.add(assembly.getId())) {
151 LOGGER.warn("The assembly id " + assembly.getId() + " is used more than once.");
152 }
153 }
154 return assemblies;
155 }
156
157 @Override
158 public Assembly getAssemblyForDescriptorReference(final String ref, final AssemblerConfigurationSource configSource)
159 throws AssemblyReadException, InvalidAssemblerConfigurationException {
160 return addAssemblyForDescriptorReference(ref, configSource, new ArrayList<>(1));
161 }
162
163 @Override
164 public Assembly getAssemblyFromDescriptorFile(final File file, final AssemblerConfigurationSource configSource)
165 throws AssemblyReadException, InvalidAssemblerConfigurationException {
166 return addAssemblyFromDescriptorFile(file, configSource, new ArrayList<>(1));
167 }
168
169 private Assembly addAssemblyForDescriptorReference(
170 final String ref, final AssemblerConfigurationSource configSource, final List<Assembly> assemblies)
171 throws AssemblyReadException, InvalidAssemblerConfigurationException {
172 final InputStream resourceAsStream = getClass().getResourceAsStream("/assemblies/" + ref + ".xml");
173
174 if (resourceAsStream == null) {
175 if (configSource.isIgnoreMissingDescriptor()) {
176 LOGGER.debug("Ignoring missing assembly descriptor with ID '" + ref + "' per configuration.");
177 return null;
178 } else {
179 throw new AssemblyReadException("Descriptor with ID '" + ref + "' not found");
180 }
181 }
182
183 try (Reader reader =
184 XmlStreamReader.builder().setInputStream(resourceAsStream).get()) {
185 final Assembly assembly = readAssembly(reader, ref, null, configSource);
186 assemblies.add(assembly);
187 return assembly;
188 } catch (final IOException e) {
189 throw new AssemblyReadException("Problem with descriptor with ID '" + ref + "'", e);
190 }
191 }
192
193 private Assembly addAssemblyFromDescriptorFile(
194 final File descriptor, final AssemblerConfigurationSource configSource, final List<Assembly> assemblies)
195 throws AssemblyReadException, InvalidAssemblerConfigurationException {
196 if (!descriptor.exists()) {
197 if (configSource.isIgnoreMissingDescriptor()) {
198 LOGGER.debug("Ignoring missing assembly descriptor: '" + descriptor + "' per configuration.");
199 return null;
200 } else {
201 throw new AssemblyReadException("Descriptor: '" + descriptor + "' not found");
202 }
203 }
204
205 try (Reader r = XmlStreamReader.builder().setFile(descriptor).get()) {
206 final Assembly assembly =
207 readAssembly(r, descriptor.getAbsolutePath(), descriptor.getParentFile(), configSource);
208
209 assemblies.add(assembly);
210
211 return assembly;
212 } catch (final IOException e) {
213 throw new AssemblyReadException("Error reading assembly descriptor: " + descriptor, e);
214 }
215 }
216
217 private Assembly addAssemblyFromDescriptor(
218 final String spec,
219 final Locator locator,
220 final AssemblerConfigurationSource configSource,
221 final List<Assembly> assemblies)
222 throws AssemblyReadException, InvalidAssemblerConfigurationException {
223 final Location location = locator.resolve(spec);
224
225 if (location == null) {
226 if (configSource.isIgnoreMissingDescriptor()) {
227 LOGGER.debug("Ignoring missing assembly descriptor with ID '" + spec
228 + "' per configuration.\nLocator output was:\n\n"
229 + locator.getMessageHolder().render());
230 return null;
231 } else {
232 throw new AssemblyReadException("Error locating assembly descriptor: " + spec + "\n\n"
233 + locator.getMessageHolder().render());
234 }
235 }
236
237 try (Reader r = XmlStreamReader.builder()
238 .setInputStream(location.getInputStream())
239 .get()) {
240 File dir = null;
241 if (location.getFile() != null) {
242 dir = location.getFile().getParentFile();
243 }
244
245 final Assembly assembly = readAssembly(r, spec, dir, configSource);
246
247 assemblies.add(assembly);
248
249 return assembly;
250 } catch (final IOException e) {
251 throw new AssemblyReadException("Error reading assembly descriptor: " + spec, e);
252 }
253 }
254
255 public Assembly readAssembly(
256 Reader reader,
257 final String locationDescription,
258 final File assemblyDir,
259 final AssemblerConfigurationSource configSource)
260 throws AssemblyReadException, InvalidAssemblerConfigurationException {
261 Assembly assembly;
262
263 final MavenProject project = configSource.getProject();
264 try {
265
266 InterpolationState is = new InterpolationState();
267 final RecursionInterceptor interceptor =
268 new PrefixAwareRecursionInterceptor(InterpolationConstants.PROJECT_PREFIXES, true);
269 is.setRecursionInterceptor(interceptor);
270
271 FixedStringSearchInterpolator interpolator =
272 AssemblyInterpolator.fullInterpolator(project, createProjectInterpolator(project), configSource);
273 AssemblyXpp3Reader.ContentTransformer transformer =
274 AssemblyInterpolator.assemblyInterpolator(interpolator, is, LOGGER);
275
276 final AssemblyXpp3Reader r = new AssemblyXpp3Reader(transformer);
277 assembly = r.read(reader);
278
279 ComponentXpp3Reader.ContentTransformer ctrans =
280 AssemblyInterpolator.componentInterpolator(interpolator, is, LOGGER);
281 mergeComponentsWithMainAssembly(assembly, assemblyDir, configSource, ctrans);
282 debugPrintAssembly("After assembly is interpolated:", assembly);
283
284 AssemblyInterpolator.checkErrors(AssemblyId.createAssemblyId(assembly), is, LOGGER);
285
286 } catch (final IOException | XmlPullParserException e) {
287 throw new AssemblyReadException(
288 "Error reading descriptor: " + locationDescription + ": " + e.getMessage(), e);
289 }
290
291 if (assembly.isIncludeSiteDirectory()) {
292 includeSiteInAssembly(assembly, configSource);
293 }
294
295 return assembly;
296 }
297
298 private void debugPrintAssembly(final String message, final Assembly assembly) {
299 final StringWriter sWriter = new StringWriter();
300 try {
301 new AssemblyXpp3Writer().write(sWriter, assembly);
302 } catch (final IOException e) {
303 LOGGER.debug("Failed to print debug message with assembly descriptor listing, and message: " + message, e);
304 }
305
306 LOGGER.debug(message + "\n\n" + sWriter + "\n\n");
307 }
308
309
310
311
312
313
314
315
316
317 protected void mergeComponentsWithMainAssembly(
318 final Assembly assembly,
319 final File assemblyDir,
320 final AssemblerConfigurationSource configSource,
321 ComponentXpp3Reader.ContentTransformer transformer)
322 throws AssemblyReadException {
323 final Locator locator = new Locator();
324
325 if (assemblyDir != null && assemblyDir.exists() && assemblyDir.isDirectory()) {
326 locator.addStrategy(new RelativeFileLocatorStrategy(assemblyDir));
327 }
328
329
330 locator.addStrategy(new RelativeFileLocatorStrategy(configSource.getBasedir()));
331 locator.addStrategy(new FileLocatorStrategy());
332 locator.addStrategy(new ClasspathResourceLocatorStrategy());
333
334 final AssemblyExpressionEvaluator aee = new AssemblyExpressionEvaluator(configSource);
335
336 final List<String> componentLocations = assembly.getComponentDescriptors();
337
338 for (String location : componentLocations) {
339
340 try {
341 location = aee.evaluate(location).toString();
342 } catch (final Exception eee) {
343 LOGGER.error("Error interpolating componentDescriptor: " + location, eee);
344 }
345
346 final Location resolvedLocation = locator.resolve(location);
347
348 if (resolvedLocation == null) {
349 throw new AssemblyReadException("Failed to locate component descriptor: " + location);
350 }
351
352 Component component = null;
353 try (Reader reader = new InputStreamReader(resolvedLocation.getInputStream())) {
354 component = new ComponentXpp3Reader(transformer).read(reader);
355 } catch (final IOException | XmlPullParserException e) {
356 throw new AssemblyReadException(
357 "Error reading component descriptor: " + location + " (resolved to: "
358 + resolvedLocation.getSpecification() + ")",
359 e);
360 }
361
362 mergeComponentWithAssembly(component, assembly);
363 }
364 }
365
366
367
368
369
370
371
372 protected void mergeComponentWithAssembly(final Component component, final Assembly assembly) {
373 final List<ContainerDescriptorHandlerConfig> containerHandlerDescriptors =
374 component.getContainerDescriptorHandlers();
375
376 for (final ContainerDescriptorHandlerConfig cfg : containerHandlerDescriptors) {
377 assembly.addContainerDescriptorHandler(cfg);
378 }
379
380 final List<DependencySet> dependencySetList = component.getDependencySets();
381
382 for (final DependencySet dependencySet : dependencySetList) {
383 assembly.addDependencySet(dependencySet);
384 }
385
386 final List<FileSet> fileSetList = component.getFileSets();
387
388 for (final FileSet fileSet : fileSetList) {
389 assembly.addFileSet(fileSet);
390 }
391
392 final List<FileItem> fileList = component.getFiles();
393
394 for (final FileItem fileItem : fileList) {
395 assembly.addFile(fileItem);
396 }
397
398 final List<ModuleSet> moduleSets = component.getModuleSets();
399 for (final ModuleSet moduleSet : moduleSets) {
400 assembly.addModuleSet(moduleSet);
401 }
402 }
403
404 @Override
405 public void includeSiteInAssembly(final Assembly assembly, final AssemblerConfigurationSource configSource)
406 throws InvalidAssemblerConfigurationException {
407 final File siteDirectory = configSource.getSiteDirectory();
408
409 if (!siteDirectory.exists()) {
410 throw new InvalidAssemblerConfigurationException("site did not exist in the target directory - "
411 + "please run site:site before creating the assembly");
412 }
413
414 LOGGER.info("Adding site directory to assembly : " + siteDirectory);
415
416 final FileSet siteFileSet = new FileSet();
417
418 siteFileSet.setDirectory(siteDirectory.getPath());
419
420 siteFileSet.setOutputDirectory("/site");
421
422 assembly.addFileSet(siteFileSet);
423 }
424 }