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