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      @Override
53      public boolean canTransformResource(String resource) {
54          return PLUGIN_XML_PATH.equals(resource);
55      }
56  
57      @Override
58      public void processResource(String resource, InputStream is, List<Relocator> relocators, long time)
59              throws IOException {
60          Xpp3Dom newDom;
61  
62          try {
63              BufferedInputStream bis = new BufferedInputStream(is) {
64                  @Override
65                  public void close() throws IOException {
66                      // leave ZIP open
67                  }
68              };
69  
70              Reader reader = ReaderFactory.newXmlReader(bis);
71  
72              newDom = Xpp3DomBuilder.build(reader);
73          } catch (Exception e) {
74              throw new IOException("Error parsing plugin.xml in " + is, e);
75          }
76  
77          // Only try to merge in mojos if there are some elements in the plugin
78          if (newDom.getChild("mojos") == null) {
79              return;
80          }
81  
82          if (time > this.time) {
83              this.time = time;
84          }
85  
86          for (Xpp3Dom mojo : newDom.getChild("mojos").getChildren("mojo")) {
87  
88              String impl = getValue(mojo, "implementation");
89              impl = getRelocatedClass(impl, relocators);
90              setValue(mojo, "implementation", impl);
91  
92              Xpp3Dom parameters = mojo.getChild("parameters");
93              if (parameters != null) {
94                  for (Xpp3Dom parameter : parameters.getChildren()) {
95                      String type = getValue(parameter, "type");
96                      type = getRelocatedClass(type, relocators);
97                      setValue(parameter, "type", type);
98                  }
99              }
100 
101             Xpp3Dom configuration = mojo.getChild("configuration");
102             if (configuration != null) {
103                 for (Xpp3Dom configurationEntry : configuration.getChildren()) {
104                     String implementation = getAttribute(configurationEntry, "implementation");
105                     implementation = getRelocatedClass(implementation, relocators);
106                     setAttribute(configurationEntry, "implementation", implementation);
107                 }
108             }
109 
110             Xpp3Dom requirements = mojo.getChild("requirements");
111             if (requirements != null && requirements.getChildCount() > 0) {
112                 for (Xpp3Dom requirement : requirements.getChildren()) {
113                     String requiredRole = getValue(requirement, "role");
114                     requiredRole = getRelocatedClass(requiredRole, relocators);
115                     setValue(requirement, "role", requiredRole);
116                 }
117             }
118             mojos.add(mojo);
119         }
120     }
121 
122     @Override
123     public void modifyOutputStream(JarOutputStream jos) throws IOException {
124         byte[] data = getTransformedResource();
125 
126         JarEntry jarEntry = new JarEntry(PLUGIN_XML_PATH);
127         jarEntry.setTime(time);
128         jos.putNextEntry(jarEntry);
129 
130         jos.write(data);
131 
132         mojos.clear();
133     }
134 
135     @Override
136     public boolean hasTransformedResource() {
137         return !mojos.isEmpty();
138     }
139 
140     byte[] getTransformedResource() throws IOException {
141         ByteArrayOutputStream baos = new ByteArrayOutputStream(1024 * 4);
142 
143         try (Writer writer = WriterFactory.newXmlWriter(baos)) {
144             Xpp3Dom dom = new Xpp3Dom("plugin");
145 
146             Xpp3Dom componentDom = new Xpp3Dom("mojos");
147 
148             dom.addChild(componentDom);
149 
150             for (Xpp3Dom mojo : mojos) {
151                 componentDom.addChild(mojo);
152             }
153 
154             Xpp3DomWriter.write(writer, dom);
155         }
156 
157         return baos.toByteArray();
158     }
159 
160     private String getRelocatedClass(String className, List<Relocator> relocators) {
161         if (className != null && className.length() > 0 && relocators != null) {
162             for (Relocator relocator : relocators) {
163                 if (relocator.canRelocateClass(className)) {
164                     return relocator.relocateClass(className);
165                 }
166             }
167         }
168 
169         return className;
170     }
171 
172     private static String getValue(Xpp3Dom dom, String element) {
173         Xpp3Dom child = dom.getChild(element);
174 
175         return (child != null && child.getValue() != null) ? child.getValue() : "";
176     }
177 
178     private static void setValue(Xpp3Dom dom, String element, String value) {
179         Xpp3Dom child = dom.getChild(element);
180 
181         if (child == null || value == null || value.length() <= 0) {
182             return;
183         }
184 
185         child.setValue(value);
186     }
187 
188     private static String getAttribute(Xpp3Dom dom, String attribute) {
189         return (dom.getAttribute(attribute) != null) ? dom.getAttribute(attribute) : "";
190     }
191 
192     private static void setAttribute(Xpp3Dom dom, String attribute, String value) {
193         String attr = dom.getAttribute(attribute);
194 
195         if (attr == null || value == null || value.length() <= 0) {
196             return;
197         }
198 
199         dom.setAttribute(attribute, value);
200     }
201 }