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 = new XmlStreamWriter(Files.newOutputStream(f.toPath()))) {
96                  final Xpp3Dom dom = new Xpp3Dom("component-set");
97                  final Xpp3Dom componentDom = new Xpp3Dom("components");
98                  dom.addChild(componentDom);
99  
100                 for (final Xpp3Dom component : components.values()) {
101                     componentDom.addChild(component);
102                 }
103 
104                 Xpp3DomWriter.write(fileWriter, dom);
105             }
106 
107             excludeOverride = true;
108 
109             archiver.addFile(f, COMPONENTS_XML_PATH);
110 
111             excludeOverride = false;
112         }
113     }
114 
115     @Override
116     public void finalizeArchiveCreation(final Archiver archiver) {
117         // this will prompt the isSelected() call, below, for all resources added to the archive.
118         // FIXME: This needs to be corrected in the AbstractArchiver, where
119         // runArchiveFinalizers() is called before regular resources are added...
120         // which is done because the manifest needs to be added first, and the
121         // manifest-creation component is a finalizer in the assembly plugin...
122         for (final ResourceIterator it = archiver.getResources(); it.hasNext(); ) {
123             it.next();
124         }
125 
126         try {
127             addToArchive(archiver);
128         } catch (final IOException e) {
129             throw new ArchiverException("Error finalizing component-set for archive. Reason: " + e.getMessage(), e);
130         }
131     }
132 
133     @Override
134     public List<String> getVirtualFiles() {
135         if ((components != null) && !components.isEmpty()) {
136             return Collections.singletonList(COMPONENTS_XML_PATH);
137         }
138 
139         return null;
140     }
141 
142     @Override
143     public boolean isSelected(final FileInfo fileInfo) throws IOException {
144         if (fileInfo.isFile()) {
145             if (excludeOverride) {
146                 return true;
147             }
148 
149             String entry = fileInfo.getName().replace('\\', '/');
150 
151             if (entry.startsWith("/")) {
152                 entry = entry.substring(1);
153             }
154 
155             if (ComponentsXmlArchiverFileFilter.COMPONENTS_XML_PATH.equals(entry)) {
156                 try (Reader reader = new BufferedReader(new XmlStreamReader(fileInfo.getContents()))) {
157                     addComponentsXml(reader);
158                 } catch (final XmlPullParserException e) {
159                     final IOException error =
160                             new IOException("Error finalizing component-set for archive. Reason: " + e.getMessage(), e);
161 
162                     throw error;
163                 }
164                 return false;
165             } else {
166                 return true;
167             }
168         } else {
169             return true;
170         }
171     }
172 
173     @Override
174     public void finalizeArchiveExtraction(final UnArchiver unarchiver) {}
175 }