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 javax.inject.Named;
22  
23  import java.io.BufferedReader;
24  import java.io.File;
25  import java.io.IOException;
26  import java.io.Reader;
27  import java.io.Writer;
28  import java.nio.file.Files;
29  import java.util.Collections;
30  import java.util.LinkedHashMap;
31  import java.util.List;
32  import java.util.Map;
33  
34  import org.apache.commons.io.input.XmlStreamReader;
35  import org.apache.commons.io.output.XmlStreamWriter;
36  import org.codehaus.plexus.archiver.Archiver;
37  import org.codehaus.plexus.archiver.ArchiverException;
38  import org.codehaus.plexus.archiver.ResourceIterator;
39  import org.codehaus.plexus.archiver.UnArchiver;
40  import org.codehaus.plexus.components.io.fileselectors.FileInfo;
41  import org.codehaus.plexus.util.xml.Xpp3Dom;
42  import org.codehaus.plexus.util.xml.Xpp3DomBuilder;
43  import org.codehaus.plexus.util.xml.Xpp3DomWriter;
44  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
45  
46  /**
47   * <code>plexus</code>: Plexus Components XML file filter <code>META-INF/plexus/components.xml</code>.
48   *
49   * @author <a href="mailto:brett@apache.org">Brett Porter</a>
50   *
51   */
52  @Named("plexus")
53  public class ComponentsXmlArchiverFileFilter implements ContainerDescriptorHandler {
54      public static final String COMPONENTS_XML_PATH = "META-INF/plexus/components.xml";
55  
56      // [jdcasey] Switched visibility to protected to allow testing. Also, because this class isn't final, it should
57      // allow
58      // some minimal access to the components accumulated for extending classes.
59      Map<String, Xpp3Dom> components;
60  
61      private boolean excludeOverride = false;
62  
63      void addComponentsXml(final Reader componentsReader) throws XmlPullParserException, IOException {
64          Xpp3Dom newDom = Xpp3DomBuilder.build(componentsReader);
65  
66          if (newDom != null) {
67              newDom = newDom.getChild("components");
68          }
69  
70          if (newDom != null) {
71              final Xpp3Dom[] children = newDom.getChildren();
72  
73              for (final Xpp3Dom component : children) {
74                  if (components == null) {
75                      components = new LinkedHashMap<>();
76                  }
77  
78                  final String role = component.getChild("role").getValue();
79                  final Xpp3Dom child = component.getChild("role-hint");
80                  final String roleHint = child != null ? child.getValue() : "";
81  
82                  final String key = role + roleHint;
83                  if (!components.containsKey(key)) {
84                      components.put(key, component);
85                  }
86              }
87          }
88      }
89  
90      private void addToArchive(final Archiver archiver) throws IOException {
91          if (components != null) {
92              final File f = Files.createTempFile("maven-assembly-plugin", "tmp").toFile();
93              f.deleteOnExit();
94  
95              try (Writer fileWriter = XmlStreamWriter.builder()
96                      .setOutputStream(Files.newOutputStream(f.toPath()))
97                      .get()) {
98                  final Xpp3Dom dom = new Xpp3Dom("component-set");
99                  final Xpp3Dom componentDom = new Xpp3Dom("components");
100                 dom.addChild(componentDom);
101 
102                 for (final Xpp3Dom component : components.values()) {
103                     componentDom.addChild(component);
104                 }
105 
106                 Xpp3DomWriter.write(fileWriter, dom);
107             }
108 
109             excludeOverride = true;
110 
111             archiver.addFile(f, COMPONENTS_XML_PATH);
112 
113             excludeOverride = false;
114         }
115     }
116 
117     @Override
118     public void finalizeArchiveCreation(final Archiver archiver) {
119         // this will prompt the isSelected() call, below, for all resources added to the archive.
120         // FIXME: This needs to be corrected in the AbstractArchiver, where
121         // runArchiveFinalizers() is called before regular resources are added...
122         // which is done because the manifest needs to be added first, and the
123         // manifest-creation component is a finalizer in the assembly plugin...
124         for (final ResourceIterator it = archiver.getResources(); it.hasNext(); ) {
125             it.next();
126         }
127 
128         try {
129             addToArchive(archiver);
130         } catch (final IOException e) {
131             throw new ArchiverException("Error finalizing component-set for archive. Reason: " + e.getMessage(), e);
132         }
133     }
134 
135     @Override
136     public List<String> getVirtualFiles() {
137         if ((components != null) && !components.isEmpty()) {
138             return Collections.singletonList(COMPONENTS_XML_PATH);
139         }
140 
141         return null;
142     }
143 
144     @Override
145     public boolean isSelected(final FileInfo fileInfo) throws IOException {
146         if (fileInfo.isFile()) {
147             if (excludeOverride) {
148                 return true;
149             }
150 
151             String entry = fileInfo.getName().replace('\\', '/');
152 
153             if (entry.startsWith("/")) {
154                 entry = entry.substring(1);
155             }
156 
157             if (ComponentsXmlArchiverFileFilter.COMPONENTS_XML_PATH.equals(entry)) {
158                 try (Reader reader = new BufferedReader(XmlStreamReader.builder()
159                         .setInputStream(fileInfo.getContents())
160                         .get())) {
161                     addComponentsXml(reader);
162                 } catch (final XmlPullParserException e) {
163                     throw new IOException("Error finalizing component-set for archive. Reason: " + e.getMessage(), e);
164                 }
165                 return false;
166             } else {
167                 return true;
168             }
169         } else {
170             return true;
171         }
172     }
173 
174     @Override
175     public void finalizeArchiveExtraction(final UnArchiver unarchiver) {}
176 }