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