View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.plugins.assembly.filter;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.io.Reader;
24  import java.io.StringReader;
25  import java.io.StringWriter;
26  import java.nio.file.Files;
27  import java.util.ArrayList;
28  import java.util.Collections;
29  import java.util.LinkedHashMap;
30  import java.util.List;
31  import java.util.Map;
32  import java.util.NoSuchElementException;
33  import java.util.zip.ZipEntry;
34  import java.util.zip.ZipFile;
35  
36  import org.codehaus.plexus.archiver.ArchiveEntry;
37  import org.codehaus.plexus.archiver.ArchivedFileSet;
38  import org.codehaus.plexus.archiver.Archiver;
39  import org.codehaus.plexus.archiver.ArchiverException;
40  import org.codehaus.plexus.archiver.FileSet;
41  import org.codehaus.plexus.archiver.ResourceIterator;
42  import org.codehaus.plexus.archiver.diags.NoOpArchiver;
43  import org.codehaus.plexus.archiver.zip.ZipArchiver;
44  import org.codehaus.plexus.components.io.resources.PlexusIoResource;
45  import org.codehaus.plexus.components.io.resources.PlexusIoResourceCollection;
46  import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
47  import org.codehaus.plexus.util.xml.Xpp3Dom;
48  import org.jdom2.Document;
49  import org.jdom2.Text;
50  import org.jdom2.filter.Filters;
51  import org.jdom2.input.SAXBuilder;
52  import org.jdom2.input.sax.XMLReaders;
53  import org.jdom2.xpath.XPathExpression;
54  import org.jdom2.xpath.XPathFactory;
55  import org.junit.jupiter.api.BeforeEach;
56  import org.junit.jupiter.api.Test;
57  import org.junit.jupiter.api.io.TempDir;
58  
59  import static org.junit.jupiter.api.Assertions.assertEquals;
60  import static org.junit.jupiter.api.Assertions.assertFalse;
61  import static org.junit.jupiter.api.Assertions.assertNotNull;
62  import static org.junit.jupiter.api.Assertions.assertNull;
63  
64  public class ComponentsXmlArchiverFileFilterTest {
65  
66      @TempDir
67      private File temporaryFolder;
68  
69      private ComponentsXmlArchiverFileFilter filter;
70  
71      @BeforeEach
72      public void setUp() {
73          filter = new ComponentsXmlArchiverFileFilter();
74      }
75  
76      @Test
77      public void testAddComponentsXmlShouldAddComponentWithoutRoleHint() throws Exception {
78          final Reader reader =
79                  writeComponentsXml(Collections.singletonList(new ComponentDef("role", null, "org.apache.maven.Impl")));
80  
81          filter.addComponentsXml(reader);
82  
83          assertFalse(filter.components.isEmpty());
84  
85          final Xpp3Dom componentDom = filter.components.get("role");
86  
87          assertEquals("role", componentDom.getChild("role").getValue());
88          assertNull(componentDom.getChild("role-hint"));
89          assertEquals(
90                  "org.apache.maven.Impl", componentDom.getChild("implementation").getValue());
91      }
92  
93      @Test
94      public void testAddComponentsXmlShouldAddComponentWithRoleHint() throws Exception {
95          final Reader reader = writeComponentsXml(
96                  Collections.singletonList(new ComponentDef("role", "hint", "org.apache.maven.Impl")));
97  
98          filter.addComponentsXml(reader);
99  
100         assertFalse(filter.components.isEmpty());
101 
102         final Xpp3Dom componentDom = filter.components.get("rolehint");
103 
104         assertEquals("role", componentDom.getChild("role").getValue());
105         assertEquals("hint", componentDom.getChild("role-hint").getValue());
106         assertEquals(
107                 "org.apache.maven.Impl", componentDom.getChild("implementation").getValue());
108     }
109 
110     @Test
111     public void testAddComponentsXmlShouldAddTwoComponentsWithRoleHints() throws Exception {
112         final List<ComponentDef> defs = new ArrayList<>();
113 
114         defs.add(new ComponentDef("role", "hint", "org.apache.maven.Impl"));
115         defs.add(new ComponentDef("role", "hint2", "org.apache.maven.Impl2"));
116 
117         final Reader reader = writeComponentsXml(defs);
118 
119         filter.addComponentsXml(reader);
120 
121         assertFalse(filter.components.isEmpty());
122 
123         Xpp3Dom componentDom = filter.components.get("rolehint");
124 
125         assertEquals("role", componentDom.getChild("role").getValue());
126         assertEquals("hint", componentDom.getChild("role-hint").getValue());
127         assertEquals(
128                 "org.apache.maven.Impl", componentDom.getChild("implementation").getValue());
129 
130         componentDom = filter.components.get("rolehint2");
131 
132         assertEquals("role", componentDom.getChild("role").getValue());
133         assertEquals("hint2", componentDom.getChild("role-hint").getValue());
134         assertEquals(
135                 "org.apache.maven.Impl2",
136                 componentDom.getChild("implementation").getValue());
137     }
138 
139     @Test
140     public void testAddToArchiveShouldWriteComponentWithoutHintToFile() throws Exception {
141         final Xpp3Dom dom = createComponentDom(new ComponentDef("role", null, "impl"));
142 
143         filter.components = new LinkedHashMap<>();
144         filter.components.put("role", dom);
145 
146         final FileCatchingArchiver fca = new FileCatchingArchiver();
147 
148         filter.finalizeArchiveCreation(fca);
149 
150         assertEquals(ComponentsXmlArchiverFileFilter.COMPONENTS_XML_PATH, fca.getDestFileName());
151 
152         final SAXBuilder builder = new SAXBuilder(XMLReaders.NONVALIDATING);
153 
154         final Document doc = builder.build(fca.getFile());
155         XPathFactory xPathFactory = XPathFactory.instance();
156 
157         XPathExpression<Text> role = xPathFactory.compile("//component[position()=1]/role/text()", Filters.text());
158         XPathExpression<Text> hint = xPathFactory.compile("//component[position()=1]/role-hint/text()", Filters.text());
159         XPathExpression<Text> implementation =
160                 xPathFactory.compile("//component[position()=1]/implementation/text()", Filters.text());
161 
162         assertEquals("role", role.evaluateFirst(doc).getText());
163         assertNull(hint.evaluateFirst(doc));
164         assertEquals("impl", implementation.evaluateFirst(doc).getText());
165     }
166 
167     @Test
168     public void testAddToArchiveShouldWriteComponentWithHintToFile() throws Exception {
169         final Xpp3Dom dom = createComponentDom(new ComponentDef("role", "hint", "impl"));
170 
171         filter.components = new LinkedHashMap<>();
172         filter.components.put("rolehint", dom);
173 
174         final FileCatchingArchiver fca = new FileCatchingArchiver();
175 
176         filter.finalizeArchiveCreation(fca);
177 
178         assertEquals(ComponentsXmlArchiverFileFilter.COMPONENTS_XML_PATH, fca.getDestFileName());
179 
180         final SAXBuilder builder = new SAXBuilder(XMLReaders.NONVALIDATING);
181 
182         final Document doc = builder.build(fca.getFile());
183         XPathFactory xPathFactory = XPathFactory.instance();
184 
185         XPathExpression<Text> role = xPathFactory.compile("//component[position()=1]/role/text()", Filters.text());
186         XPathExpression<Text> hint = xPathFactory.compile("//component[position()=1]/role-hint/text()", Filters.text());
187         XPathExpression<Text> implementation =
188                 xPathFactory.compile("//component[position()=1]/implementation/text()", Filters.text());
189 
190         assertEquals("role", role.evaluateFirst(doc).getText());
191         assertEquals("hint", hint.evaluateFirst(doc).getText());
192         assertEquals("impl", implementation.evaluateFirst(doc).getText());
193     }
194 
195     @Test
196     public void testAddToArchiveShouldWriteTwoComponentToFile() throws Exception {
197         filter.components = new LinkedHashMap<>();
198 
199         final Xpp3Dom dom = createComponentDom(new ComponentDef("role", "hint", "impl"));
200 
201         filter.components.put("rolehint", dom);
202 
203         final Xpp3Dom dom2 = createComponentDom(new ComponentDef("role", "hint2", "impl"));
204 
205         filter.components.put("rolehint2", dom2);
206 
207         final FileCatchingArchiver fca = new FileCatchingArchiver();
208 
209         filter.finalizeArchiveCreation(fca);
210 
211         assertEquals(ComponentsXmlArchiverFileFilter.COMPONENTS_XML_PATH, fca.getDestFileName());
212 
213         final SAXBuilder builder = new SAXBuilder(XMLReaders.NONVALIDATING);
214 
215         final Document doc = builder.build(fca.getFile());
216         XPathFactory xPathFactory = XPathFactory.instance();
217 
218         XPathExpression<Text> role = xPathFactory.compile("//component[position()=1]/role/text()", Filters.text());
219         XPathExpression<Text> hint = xPathFactory.compile("//component[position()=1]/role-hint/text()", Filters.text());
220         XPathExpression<Text> implementation =
221                 xPathFactory.compile("//component[position()=1]/implementation/text()", Filters.text());
222 
223         assertEquals("role", role.evaluateFirst(doc).getText());
224         assertEquals("hint", hint.evaluateFirst(doc).getText());
225         assertEquals("impl", implementation.evaluateFirst(doc).getText());
226 
227         XPathExpression<Text> role2 = xPathFactory.compile("//component[position()=2]/role/text()", Filters.text());
228         XPathExpression<Text> hint2 =
229                 xPathFactory.compile("//component[position()=2]/role-hint/text()", Filters.text());
230         XPathExpression<Text> implementation2 =
231                 xPathFactory.compile("//component[position()=2]/implementation/text()", Filters.text());
232 
233         assertEquals("role", role2.evaluateFirst(doc).getText());
234         assertEquals("hint2", hint2.evaluateFirst(doc).getText());
235         assertEquals("impl", implementation2.evaluateFirst(doc).getText());
236     }
237 
238     @Test
239     public void testAddToArchiveShouldWriteTwoComponentToArchivedFile() throws Exception {
240         filter.components = new LinkedHashMap<>();
241 
242         final Xpp3Dom dom = createComponentDom(new ComponentDef("role", "hint", "impl"));
243 
244         filter.components.put("rolehint", dom);
245 
246         final Xpp3Dom dom2 = createComponentDom(new ComponentDef("role", "hint2", "impl"));
247 
248         filter.components.put("rolehint2", dom2);
249 
250         final ZipArchiver archiver = new ZipArchiver();
251 
252         final File archiveFile = newFile(temporaryFolder, "archive");
253 
254         archiver.setDestFile(archiveFile);
255 
256         final File descriptorFile = new File(temporaryFolder, "descriptor.xml");
257 
258         archiver.setArchiveFinalizers(Collections.singletonList(filter));
259 
260         archiver.createArchive();
261 
262         try (ZipFile zf = new ZipFile(archiveFile)) {
263             final ZipEntry ze = zf.getEntry(ComponentsXmlArchiverFileFilter.COMPONENTS_XML_PATH);
264 
265             assertNotNull(ze);
266 
267             Files.copy(zf.getInputStream(ze), descriptorFile.toPath());
268         }
269 
270         final SAXBuilder builder = new SAXBuilder(XMLReaders.NONVALIDATING);
271 
272         final Document doc = builder.build(descriptorFile);
273 
274         XPathFactory xPathFactory = XPathFactory.instance();
275         XPathExpression<Text> role = xPathFactory.compile("//component[position()=1]/role/text()", Filters.text());
276         XPathExpression<Text> hint = xPathFactory.compile("//component[position()=1]/role-hint/text()", Filters.text());
277         XPathExpression<Text> implementation =
278                 xPathFactory.compile("//component[position()=1]/implementation/text()", Filters.text());
279 
280         assertEquals("role", role.evaluateFirst(doc).getText());
281         assertEquals("hint", hint.evaluateFirst(doc).getText());
282         assertEquals("impl", implementation.evaluateFirst(doc).getText());
283 
284         XPathExpression<Text> role2 = xPathFactory.compile("//component[position()=2]/role/text()", Filters.text());
285         XPathExpression<Text> hint2 =
286                 xPathFactory.compile("//component[position()=2]/role-hint/text()", Filters.text());
287         XPathExpression<Text> implementation2 =
288                 xPathFactory.compile("//component[position()=2]/implementation/text()", Filters.text());
289 
290         assertEquals("role", role2.evaluateFirst(doc).getText());
291         assertEquals("hint2", hint2.evaluateFirst(doc).getText());
292         assertEquals("impl", implementation2.evaluateFirst(doc).getText());
293     }
294 
295     private Xpp3Dom createComponentDom(final ComponentDef def) {
296         final Xpp3Dom dom = new Xpp3Dom("component");
297 
298         final Xpp3Dom role = new Xpp3Dom("role");
299         role.setValue(def.role);
300         dom.addChild(role);
301 
302         final String hint = def.roleHint;
303         if (hint != null) {
304             final Xpp3Dom roleHint = new Xpp3Dom("role-hint");
305             roleHint.setValue(hint);
306             dom.addChild(roleHint);
307         }
308 
309         final Xpp3Dom impl = new Xpp3Dom("implementation");
310         impl.setValue(def.implementation);
311         dom.addChild(impl);
312 
313         return dom;
314     }
315 
316     private Reader writeComponentsXml(final List<ComponentDef> componentDefs) throws IOException {
317         final StringWriter writer = new StringWriter();
318 
319         final PrettyPrintXMLWriter xmlWriter = new PrettyPrintXMLWriter(writer);
320 
321         xmlWriter.startElement("component-set");
322         xmlWriter.startElement("components");
323 
324         for (final ComponentDef def : componentDefs) {
325             xmlWriter.startElement("component");
326 
327             xmlWriter.startElement("role");
328             xmlWriter.writeText(def.role);
329             xmlWriter.endElement();
330 
331             final String roleHint = def.roleHint;
332             if (roleHint != null) {
333                 xmlWriter.startElement("role-hint");
334                 xmlWriter.writeText(roleHint);
335                 xmlWriter.endElement();
336             }
337 
338             xmlWriter.startElement("implementation");
339             xmlWriter.writeText(def.implementation);
340             xmlWriter.endElement();
341 
342             xmlWriter.endElement();
343         }
344 
345         xmlWriter.endElement();
346         xmlWriter.endElement();
347 
348         return new StringReader(writer.toString());
349     }
350 
351     private static final class ComponentDef {
352         final String role;
353 
354         final String roleHint;
355 
356         final String implementation;
357 
358         ComponentDef(final String role, final String roleHint, final String implementation) {
359             this.role = role;
360             this.roleHint = roleHint;
361             this.implementation = implementation;
362         }
363     }
364 
365     private static final class FileCatchingArchiver extends NoOpArchiver {
366 
367         private File inputFile;
368 
369         private String destFileName;
370 
371         public void addDirectory(final File directory) throws ArchiverException {
372             throw new UnsupportedOperationException("not supported");
373         }
374 
375         public void addDirectory(final File directory, final String prefix) throws ArchiverException {
376             throw new UnsupportedOperationException("not supported");
377         }
378 
379         public void addDirectory(final File directory, final String[] includes, final String[] excludes)
380                 throws ArchiverException {
381             throw new UnsupportedOperationException("not supported");
382         }
383 
384         public void addDirectory(
385                 final File directory, final String prefix, final String[] includes, final String[] excludes)
386                 throws ArchiverException {
387             throw new UnsupportedOperationException("not supported");
388         }
389 
390         public void addFile(final File inputFile, final String destFileName) throws ArchiverException {
391             this.inputFile = inputFile;
392             this.destFileName = destFileName;
393         }
394 
395         File getFile() {
396             return inputFile;
397         }
398 
399         String getDestFileName() {
400             return destFileName;
401         }
402 
403         public void addFile(final File inputFile, final String destFileName, final int permissions)
404                 throws ArchiverException {
405             throw new UnsupportedOperationException("not supported");
406         }
407 
408         public void createArchive() throws ArchiverException, IOException {
409             throw new UnsupportedOperationException("not supported");
410         }
411 
412         public int getDefaultDirectoryMode() {
413             throw new UnsupportedOperationException("not supported");
414         }
415 
416         public void setDefaultDirectoryMode(final int mode) {
417             throw new UnsupportedOperationException("not supported");
418         }
419 
420         public int getDefaultFileMode() {
421             throw new UnsupportedOperationException("not supported");
422         }
423 
424         public void setDefaultFileMode(final int mode) {
425             throw new UnsupportedOperationException("not supported");
426         }
427 
428         public File getDestFile() {
429             throw new UnsupportedOperationException("not supported");
430         }
431 
432         public void setDestFile(final File destFile) {
433             throw new UnsupportedOperationException("not supported");
434         }
435 
436         public void addSymlink(String s, String s2) throws ArchiverException {
437             throw new UnsupportedOperationException("not supported");
438         }
439 
440         public void addSymlink(String s, int i, String s2) throws ArchiverException {
441             throw new UnsupportedOperationException("not supported");
442         }
443 
444         public Map<String, ArchiveEntry> getFiles() {
445             throw new UnsupportedOperationException("not supported");
446         }
447 
448         public boolean getIncludeEmptyDirs() {
449             throw new UnsupportedOperationException("not supported");
450         }
451 
452         public void setIncludeEmptyDirs(final boolean includeEmptyDirs) {
453             throw new UnsupportedOperationException("not supported");
454         }
455 
456         public void addArchivedFileSet(final File archiveFile) throws ArchiverException {
457             throw new UnsupportedOperationException("not supported");
458         }
459 
460         public void addArchivedFileSet(final File archiveFile, final String prefix) throws ArchiverException {
461             throw new UnsupportedOperationException("not supported");
462         }
463 
464         public void addArchivedFileSet(final File archiveFile, final String[] includes, final String[] excludes)
465                 throws ArchiverException {
466             throw new UnsupportedOperationException("not supported");
467         }
468 
469         public void addArchivedFileSet(
470                 final File archiveFile, final String prefix, final String[] includes, final String[] excludes)
471                 throws ArchiverException {
472             throw new UnsupportedOperationException("not supported");
473         }
474 
475         public boolean isForced() {
476             throw new UnsupportedOperationException("not supported");
477         }
478 
479         public void setForced(final boolean forced) {
480             throw new UnsupportedOperationException("not supported");
481         }
482 
483         public boolean isSupportingForced() {
484             throw new UnsupportedOperationException("not supported");
485         }
486 
487         public void setDotFileDirectory(final File dotFileDirectory) {}
488 
489         public void addArchivedFileSet(final ArchivedFileSet fileSet) throws ArchiverException {
490             throw new UnsupportedOperationException("not supported");
491         }
492 
493         public void addFileSet(final FileSet fileSet) throws ArchiverException {
494             throw new UnsupportedOperationException("not supported");
495         }
496 
497         public void addResource(final PlexusIoResource resource, final String destFileName, final int permissions)
498                 throws ArchiverException {
499             throw new UnsupportedOperationException("not supported");
500         }
501 
502         public void addResources(final PlexusIoResourceCollection resources) throws ArchiverException {
503             throw new UnsupportedOperationException("not supported");
504         }
505 
506         public ResourceIterator getResources() throws ArchiverException {
507             return new ResourceIterator() {
508 
509                 public boolean hasNext() throws ArchiverException {
510                     return false;
511                 }
512 
513                 public ArchiveEntry next() throws ArchiverException {
514                     throw new NoSuchElementException();
515                 }
516 
517                 public void remove() {
518                     throw new NoSuchElementException();
519                 }
520             };
521         }
522 
523         public String getDuplicateBehavior() {
524             return Archiver.DUPLICATES_ADD;
525         }
526 
527         public void setDuplicateBehavior(final String duplicate) {}
528 
529         public int getDirectoryMode() {
530             throw new UnsupportedOperationException("not supported");
531         }
532 
533         public void setDirectoryMode(final int mode) {
534             throw new UnsupportedOperationException("not supported");
535         }
536 
537         public int getFileMode() {
538             throw new UnsupportedOperationException("not supported");
539         }
540 
541         public void setFileMode(final int mode) {
542             throw new UnsupportedOperationException("not supported");
543         }
544 
545         public int getOverrideDirectoryMode() {
546             throw new UnsupportedOperationException("not supported");
547         }
548 
549         public int getOverrideFileMode() {
550             throw new UnsupportedOperationException("not supported");
551         }
552     }
553 
554     private static File newFile(File parent, String child) throws IOException {
555         File result = new File(parent, child);
556         result.createNewFile();
557         return result;
558     }
559 }