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