View Javadoc
1   package org.apache.maven.plugins.shade.resource;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.BufferedInputStream;
23  import java.io.ByteArrayOutputStream;
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.io.Reader;
27  import java.io.Writer;
28  import java.util.ArrayList;
29  import java.util.List;
30  import java.util.jar.JarEntry;
31  import java.util.jar.JarOutputStream;
32  
33  import org.apache.maven.plugins.shade.relocation.Relocator;
34  import org.codehaus.plexus.util.ReaderFactory;
35  import org.codehaus.plexus.util.WriterFactory;
36  import org.codehaus.plexus.util.xml.Xpp3Dom;
37  import org.codehaus.plexus.util.xml.Xpp3DomBuilder;
38  import org.codehaus.plexus.util.xml.Xpp3DomWriter;
39  
40  /**
41   * A resource processor that aggregates Maven <code>plugin.xml</code> files.
42   * 
43   * @author Robert Scholte
44   * @since 3.0
45   */
46  public class PluginXmlResourceTransformer
47      extends AbstractCompatibilityTransformer
48  {
49      private List<Xpp3Dom> mojos = new ArrayList<>();
50  
51      private long time = Long.MIN_VALUE;
52  
53      public static final String PLUGIN_XML_PATH = "META-INF/maven/plugin.xml";
54  
55      public boolean canTransformResource( String resource )
56      {
57          return PLUGIN_XML_PATH.equals( resource );
58      }
59  
60      public void processResource( String resource, InputStream is, List<Relocator> relocators, long time )
61          throws IOException
62      {
63          Xpp3Dom newDom;
64  
65          try
66          {
67              BufferedInputStream bis = new BufferedInputStream( is )
68              {
69                  public void close()
70                      throws IOException
71                  {
72                      // leave ZIP open
73                  }
74              };
75  
76              Reader reader = ReaderFactory.newXmlReader( bis );
77  
78              newDom = Xpp3DomBuilder.build( reader );
79          }
80          catch ( Exception e )
81          {
82              throw new IOException( "Error parsing plugin.xml in " + is, e );
83          }
84  
85          // Only try to merge in mojos if there are some elements in the plugin
86          if ( newDom.getChild( "mojos" ) == null )
87          {
88              return;
89          }
90  
91          if ( time > this.time )
92          {
93              this.time = time;        
94          }
95  
96          for ( Xpp3Dom mojo : newDom.getChild( "mojos" ).getChildren( "mojo" ) )
97          {
98  
99              String impl = getValue( mojo, "implementation" );
100             impl = getRelocatedClass( impl, relocators );
101             setValue( mojo, "implementation", impl );
102 
103             Xpp3Dom parameters = mojo.getChild( "parameters" );
104             if ( parameters != null )
105             {
106                 for ( Xpp3Dom parameter : parameters.getChildren() )
107                 {
108                     String type = getValue( parameter, "type" );
109                     type = getRelocatedClass( type, relocators );
110                     setValue( parameter, "type", type );
111                 }
112             }
113 
114             Xpp3Dom configuration = mojo.getChild( "configuration" );
115             if ( configuration != null )
116             {
117                 for ( Xpp3Dom configurationEntry : configuration.getChildren() )
118                 {
119                     String implementation = getAttribute( configurationEntry, "implementation" );
120                     implementation = getRelocatedClass( implementation, relocators );
121                     setAttribute( configurationEntry, "implementation", implementation );
122                 }
123             }
124 
125             Xpp3Dom requirements = mojo.getChild( "requirements" );
126             if ( requirements != null && requirements.getChildCount() > 0 )
127             {
128                 for ( Xpp3Dom requirement : requirements.getChildren() )
129                 {
130                     String requiredRole = getValue( requirement, "role" );
131                     requiredRole = getRelocatedClass( requiredRole, relocators );
132                     setValue( requirement, "role", requiredRole );
133                 }
134             }
135             mojos.add( mojo );
136         }
137     }
138 
139     public void modifyOutputStream( JarOutputStream jos )
140         throws IOException
141     {
142         byte[] data = getTransformedResource();
143 
144         JarEntry jarEntry = new JarEntry( PLUGIN_XML_PATH );
145         jarEntry.setTime( time );
146         jos.putNextEntry( jarEntry );
147 
148         jos.write( data );
149 
150         mojos.clear();
151     }
152 
153     public boolean hasTransformedResource()
154     {
155         return !mojos.isEmpty();
156     }
157 
158     byte[] getTransformedResource()
159         throws IOException
160     {
161         ByteArrayOutputStream baos = new ByteArrayOutputStream( 1024 * 4 );
162 
163         try ( Writer writer = WriterFactory.newXmlWriter( baos ) )
164         {
165             Xpp3Dom dom = new Xpp3Dom( "plugin" );
166 
167             Xpp3Dom componentDom = new Xpp3Dom( "mojos" );
168 
169             dom.addChild( componentDom );
170 
171             for ( Xpp3Dom mojo : mojos )
172             {
173                 componentDom.addChild( mojo );
174             }
175 
176             Xpp3DomWriter.write( writer, dom );
177         }
178 
179         return baos.toByteArray();
180     }
181 
182     private String getRelocatedClass( String className, List<Relocator> relocators )
183     {
184         if ( className != null && className.length() > 0 && relocators != null )
185         {
186             for ( Relocator relocator : relocators )
187             {
188                 if ( relocator.canRelocateClass( className ) )
189                 {
190                     return relocator.relocateClass( className );
191                 }
192             }
193         }
194 
195         return className;
196     }
197 
198     private static String getValue( Xpp3Dom dom, String element )
199     {
200         Xpp3Dom child = dom.getChild( element );
201 
202         return ( child != null && child.getValue() != null ) ? child.getValue() : "";
203     }
204 
205     private static void setValue( Xpp3Dom dom, String element, String value )
206     {
207         Xpp3Dom child = dom.getChild( element );
208 
209         if ( child == null || value == null || value.length() <= 0 )
210         {
211             return;
212         }
213 
214         child.setValue( value );
215     }
216 
217     private static String getAttribute( Xpp3Dom dom, String attribute )
218     {
219         return ( dom.getAttribute( attribute ) != null ) ? dom.getAttribute( attribute ) : "";
220     }
221 
222     private static void setAttribute( Xpp3Dom dom, String attribute, String value )
223     {
224         String attr = dom.getAttribute( attribute );
225 
226         if ( attr == null || value == null || value.length() <= 0 )
227         {
228             return;
229         }
230 
231         dom.setAttribute( attribute, value );
232     }
233 
234 }