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