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.shade.resource;
20  
21  import java.io.BufferedInputStream;
22  import java.io.ByteArrayOutputStream;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.io.Reader;
26  import java.io.Writer;
27  import java.util.ArrayList;
28  import java.util.List;
29  import java.util.jar.JarEntry;
30  import java.util.jar.JarOutputStream;
31  
32  import org.apache.maven.plugins.shade.relocation.Relocator;
33  import org.codehaus.plexus.util.ReaderFactory;
34  import org.codehaus.plexus.util.WriterFactory;
35  import org.codehaus.plexus.util.xml.Xpp3Dom;
36  import org.codehaus.plexus.util.xml.Xpp3DomBuilder;
37  import org.codehaus.plexus.util.xml.Xpp3DomWriter;
38  
39  /**
40   * A resource processor that aggregates Maven <code>plugin.xml</code> files.
41   *
42   * @author Robert Scholte
43   * @since 3.0
44   */
45  public class PluginXmlResourceTransformer extends AbstractCompatibilityTransformer {
46      private List<Xpp3Dom> mojos = new ArrayList<>();
47  
48      private long time = Long.MIN_VALUE;
49  
50      public static final String PLUGIN_XML_PATH = "META-INF/maven/plugin.xml";
51  
52      public boolean canTransformResource(String resource) {
53          return PLUGIN_XML_PATH.equals(resource);
54      }
55  
56      public void processResource(String resource, InputStream is, List<Relocator> relocators, long time)
57              throws IOException {
58          Xpp3Dom newDom;
59  
60          try {
61              BufferedInputStream bis = new BufferedInputStream(is) {
62                  public void close() throws IOException {
63                      // leave ZIP open
64                  }
65              };
66  
67              Reader reader = ReaderFactory.newXmlReader(bis);
68  
69              newDom = Xpp3DomBuilder.build(reader);
70          } catch (Exception e) {
71              throw new IOException("Error parsing plugin.xml in " + is, e);
72          }
73  
74          // Only try to merge in mojos if there are some elements in the plugin
75          if (newDom.getChild("mojos") == null) {
76              return;
77          }
78  
79          if (time > this.time) {
80              this.time = time;
81          }
82  
83          for (Xpp3Dom mojo : newDom.getChild("mojos").getChildren("mojo")) {
84  
85              String impl = getValue(mojo, "implementation");
86              impl = getRelocatedClass(impl, relocators);
87              setValue(mojo, "implementation", impl);
88  
89              Xpp3Dom parameters = mojo.getChild("parameters");
90              if (parameters != null) {
91                  for (Xpp3Dom parameter : parameters.getChildren()) {
92                      String type = getValue(parameter, "type");
93                      type = getRelocatedClass(type, relocators);
94                      setValue(parameter, "type", type);
95                  }
96              }
97  
98              Xpp3Dom configuration = mojo.getChild("configuration");
99              if (configuration != null) {
100                 for (Xpp3Dom configurationEntry : configuration.getChildren()) {
101                     String implementation = getAttribute(configurationEntry, "implementation");
102                     implementation = getRelocatedClass(implementation, relocators);
103                     setAttribute(configurationEntry, "implementation", implementation);
104                 }
105             }
106 
107             Xpp3Dom requirements = mojo.getChild("requirements");
108             if (requirements != null && requirements.getChildCount() > 0) {
109                 for (Xpp3Dom requirement : requirements.getChildren()) {
110                     String requiredRole = getValue(requirement, "role");
111                     requiredRole = getRelocatedClass(requiredRole, relocators);
112                     setValue(requirement, "role", requiredRole);
113                 }
114             }
115             mojos.add(mojo);
116         }
117     }
118 
119     public void modifyOutputStream(JarOutputStream jos) throws IOException {
120         byte[] data = getTransformedResource();
121 
122         JarEntry jarEntry = new JarEntry(PLUGIN_XML_PATH);
123         jarEntry.setTime(time);
124         jos.putNextEntry(jarEntry);
125 
126         jos.write(data);
127 
128         mojos.clear();
129     }
130 
131     public boolean hasTransformedResource() {
132         return !mojos.isEmpty();
133     }
134 
135     byte[] getTransformedResource() throws IOException {
136         ByteArrayOutputStream baos = new ByteArrayOutputStream(1024 * 4);
137 
138         try (Writer writer = WriterFactory.newXmlWriter(baos)) {
139             Xpp3Dom dom = new Xpp3Dom("plugin");
140 
141             Xpp3Dom componentDom = new Xpp3Dom("mojos");
142 
143             dom.addChild(componentDom);
144 
145             for (Xpp3Dom mojo : mojos) {
146                 componentDom.addChild(mojo);
147             }
148 
149             Xpp3DomWriter.write(writer, dom);
150         }
151 
152         return baos.toByteArray();
153     }
154 
155     private String getRelocatedClass(String className, List<Relocator> relocators) {
156         if (className != null && className.length() > 0 && relocators != null) {
157             for (Relocator relocator : relocators) {
158                 if (relocator.canRelocateClass(className)) {
159                     return relocator.relocateClass(className);
160                 }
161             }
162         }
163 
164         return className;
165     }
166 
167     private static String getValue(Xpp3Dom dom, String element) {
168         Xpp3Dom child = dom.getChild(element);
169 
170         return (child != null && child.getValue() != null) ? child.getValue() : "";
171     }
172 
173     private static void setValue(Xpp3Dom dom, String element, String value) {
174         Xpp3Dom child = dom.getChild(element);
175 
176         if (child == null || value == null || value.length() <= 0) {
177             return;
178         }
179 
180         child.setValue(value);
181     }
182 
183     private static String getAttribute(Xpp3Dom dom, String attribute) {
184         return (dom.getAttribute(attribute) != null) ? dom.getAttribute(attribute) : "";
185     }
186 
187     private static void setAttribute(Xpp3Dom dom, String attribute, String value) {
188         String attr = dom.getAttribute(attribute);
189 
190         if (attr == null || value == null || value.length() <= 0) {
191             return;
192         }
193 
194         dom.setAttribute(attribute, value);
195     }
196 }