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 org.apache.maven.plugins.shade.relocation.Relocator;
23  import org.codehaus.plexus.util.ReaderFactory;
24  import org.codehaus.plexus.util.WriterFactory;
25  import org.codehaus.plexus.util.xml.Xpp3Dom;
26  import org.codehaus.plexus.util.xml.Xpp3DomBuilder;
27  import org.codehaus.plexus.util.xml.Xpp3DomWriter;
28  
29  import java.io.BufferedInputStream;
30  import java.io.ByteArrayOutputStream;
31  import java.io.IOException;
32  import java.io.InputStream;
33  import java.io.Reader;
34  import java.io.Writer;
35  import java.util.LinkedHashMap;
36  import java.util.List;
37  import java.util.Map;
38  import java.util.jar.JarEntry;
39  import java.util.jar.JarOutputStream;
40  
41  /**
42   * A resource processor that aggregates plexus <code>components.xml</code> files.
43   */
44  public class ComponentsXmlResourceTransformer
45      implements ResourceTransformer
46  {
47      private Map<String, Xpp3Dom> components = new LinkedHashMap<>();
48  
49      public static final String COMPONENTS_XML_PATH = "META-INF/plexus/components.xml";
50  
51      public boolean canTransformResource( String resource )
52      {
53          return COMPONENTS_XML_PATH.equals( resource );
54      }
55  
56      public void processResource( String resource, InputStream is, List<Relocator> relocators )
57          throws IOException
58      {
59          Xpp3Dom newDom;
60  
61          try
62          {
63              BufferedInputStream bis = new BufferedInputStream( is )
64              {
65                  public void close()
66                      throws IOException
67                  {
68                      // leave ZIP open
69                  }
70              };
71  
72              Reader reader = ReaderFactory.newXmlReader( bis );
73  
74              newDom = Xpp3DomBuilder.build( reader );
75          }
76          catch ( Exception e )
77          {
78              throw new IOException( "Error parsing components.xml in " + is, e );
79          }
80  
81          // Only try to merge in components if there are some elements in the component-set
82          if ( newDom.getChild( "components" ) == null )
83          {
84              return;
85          }
86  
87          Xpp3Dom[] children = newDom.getChild( "components" ).getChildren( "component" );
88  
89          for ( Xpp3Dom component : children )
90          {
91              String role = getValue( component, "role" );
92              role = getRelocatedClass( role, relocators );
93              setValue( component, "role", role );
94  
95              String roleHint = getValue( component, "role-hint" );
96  
97              String impl = getValue( component, "implementation" );
98              impl = getRelocatedClass( impl, relocators );
99              setValue( component, "implementation", impl );
100 
101             String key = role + ':' + roleHint;
102             if ( components.containsKey( key ) )
103             {
104                 // TODO: use the tools in Plexus to merge these properly. For now, I just need an all-or-nothing
105                 // configuration carry over
106 
107                 Xpp3Dom dom = components.get( key );
108                 if ( dom.getChild( "configuration" ) != null )
109                 {
110                     component.addChild( dom.getChild( "configuration" ) );
111                 }
112             }
113 
114             Xpp3Dom requirements = component.getChild( "requirements" );
115             if ( requirements != null && requirements.getChildCount() > 0 )
116             {
117                 for ( int r = requirements.getChildCount() - 1; r >= 0; r-- )
118                 {
119                     Xpp3Dom requirement = requirements.getChild( r );
120 
121                     String requiredRole = getValue( requirement, "role" );
122                     requiredRole = getRelocatedClass( requiredRole, relocators );
123                     setValue( requirement, "role", requiredRole );
124                 }
125             }
126 
127             components.put( key, component );
128         }
129     }
130 
131     public void modifyOutputStream( JarOutputStream jos )
132         throws IOException
133     {
134         byte[] data = getTransformedResource();
135 
136         jos.putNextEntry( new JarEntry( COMPONENTS_XML_PATH ) );
137 
138         jos.write( data );
139 
140         components.clear();
141     }
142 
143     public boolean hasTransformedResource()
144     {
145         return !components.isEmpty();
146     }
147 
148     byte[] getTransformedResource()
149         throws IOException
150     {
151         ByteArrayOutputStream baos = new ByteArrayOutputStream( 1024 * 4 );
152 
153         try ( Writer writer = WriterFactory.newXmlWriter( baos ) )
154         {
155             Xpp3Dom dom = new Xpp3Dom( "component-set" );
156 
157             Xpp3Dom componentDom = new Xpp3Dom( "components" );
158 
159             dom.addChild( componentDom );
160 
161             for ( Xpp3Dom component : components.values() )
162             {
163                 componentDom.addChild( component );
164             }
165 
166             Xpp3DomWriter.write( writer, dom );
167         }
168 
169         return baos.toByteArray();
170     }
171 
172     private String getRelocatedClass( String className, List<Relocator> relocators )
173     {
174         if ( className != null && className.length() > 0 && relocators != null )
175         {
176             for ( Relocator relocator : relocators )
177             {
178                 if ( relocator.canRelocateClass( className ) )
179                 {
180                     return relocator.relocateClass( className );
181                 }
182             }
183         }
184 
185         return className;
186     }
187 
188     private static String getValue( Xpp3Dom dom, String element )
189     {
190         Xpp3Dom child = dom.getChild( element );
191 
192         return ( child != null && child.getValue() != null ) ? child.getValue() : "";
193     }
194 
195     private static void setValue( Xpp3Dom dom, String element, String value )
196     {
197         Xpp3Dom child = dom.getChild( element );
198 
199         if ( child == null || value == null || value.length() <= 0 )
200         {
201             return;
202         }
203 
204         child.setValue( value );
205     }
206 
207 }