View Javadoc
1   package org.apache.maven.plugins.shade;
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.File;
23  import java.io.IOException;
24  import java.net.URL;
25  import java.net.URLClassLoader;
26  import java.util.ArrayList;
27  import java.util.Arrays;
28  import java.util.Collections;
29  import java.util.LinkedHashSet;
30  import java.util.List;
31  import java.util.Set;
32  
33  import junit.framework.TestCase;
34  
35  import org.apache.maven.plugin.MojoExecutionException;
36  import org.apache.maven.plugins.shade.filter.Filter;
37  import org.apache.maven.plugins.shade.relocation.Relocator;
38  import org.apache.maven.plugins.shade.relocation.SimpleRelocator;
39  import org.apache.maven.plugins.shade.resource.ComponentsXmlResourceTransformer;
40  import org.apache.maven.plugins.shade.resource.ResourceTransformer;
41  import org.codehaus.plexus.logging.AbstractLogger;
42  import org.codehaus.plexus.logging.Logger;
43  import org.codehaus.plexus.logging.console.ConsoleLogger;
44  import org.objectweb.asm.ClassReader;
45  import org.objectweb.asm.ClassVisitor;
46  import org.objectweb.asm.Opcodes;
47  
48  /**
49   * @author Jason van Zyl
50   * @author Mauro Talevi
51   */
52  public class DefaultShaderTest
53      extends TestCase
54  {
55      private static final String[] EXCLUDES = new String[] { "org/codehaus/plexus/util/xml/Xpp3Dom",
56          "org/codehaus/plexus/util/xml/pull.*" };
57  
58      public void testOverlappingResourcesAreLogged() throws IOException, MojoExecutionException {
59          final DefaultShader shader = new DefaultShader();
60          final List<String> debugMessages = new ArrayList<>();
61          final List<String> warnMessages = new ArrayList<>();
62          shader.enableLogging( new AbstractLogger(
63                  Logger.LEVEL_INFO, "TEST_DefaultShaderTest_testOverlappingResourcesAreLogged" )
64          {
65              @Override
66              public void debug( final String s, final Throwable throwable )
67              {
68                  debugMessages.add( s.replace( '\\', '/' ).trim() );
69              }
70  
71              @Override
72              public void info( final String s, final Throwable throwable )
73              {
74                  // no-op
75              }
76  
77              @Override
78              public void warn( final String s, final Throwable throwable )
79              {
80                  warnMessages.add( s.replace( '\\', '/' ).trim() );
81              }
82  
83              @Override
84              public void error( final String s, final Throwable throwable )
85              {
86                  // no-op
87              }
88  
89              @Override
90              public void fatalError( final String s, final Throwable throwable )
91              {
92                  // no-op
93              }
94  
95              @Override
96              public Logger getChildLogger( final String s )
97              {
98                  return this;
99              }
100         });
101 
102         // we will shade two jars and expect to see META-INF/MANIFEST.MF overlaps, this will always be true
103         // but this can lead to a broken deployment if intended for OSGi or so, so even this should be logged
104         final Set<File> set = new LinkedHashSet<>();
105         set.add( new File( "src/test/jars/test-project-1.0-SNAPSHOT.jar" ) );
106         set.add( new File( "src/test/jars/plexus-utils-1.4.1.jar" ) );
107 
108         final ShadeRequest shadeRequest = new ShadeRequest();
109         shadeRequest.setJars( set );
110         shadeRequest.setRelocators( Collections.<Relocator>emptyList() );
111         shadeRequest.setResourceTransformers( Collections.<ResourceTransformer>emptyList() );
112         shadeRequest.setFilters( Collections.<Filter>emptyList() );
113         shadeRequest.setUberJar( new File( "target/foo-custom_testOverlappingResourcesAreLogged.jar" ) );
114         shader.shade( shadeRequest );
115 
116         final String failureWarnMessage = warnMessages.toString();
117         assertTrue(failureWarnMessage, warnMessages.contains(
118                 "plexus-utils-1.4.1.jar, test-project-1.0-SNAPSHOT.jar define 1 overlapping resources:"));
119         assertTrue(failureWarnMessage, warnMessages.contains("- META-INF/MANIFEST.MF"));
120 
121         final String failureDebugMessage = debugMessages.toString();
122         assertTrue(failureDebugMessage, debugMessages.contains(
123                 "We have a duplicate META-INF/MANIFEST.MF in src/test/jars/plexus-utils-1.4.1.jar" ));
124     }
125 
126     public void testShaderWithDefaultShadedPattern()
127         throws Exception
128     {
129         shaderWithPattern( null, new File( "target/foo-default.jar" ), EXCLUDES );
130     }
131 
132     public void testShaderWithStaticInitializedClass()
133         throws Exception
134     {
135         Shader s = newShader();
136 
137         Set<File> set = new LinkedHashSet<>();
138 
139         set.add( new File( "src/test/jars/test-artifact-1.0-SNAPSHOT.jar" ) );
140 
141         List<Relocator> relocators = new ArrayList<>();
142 
143         relocators.add( new SimpleRelocator( "org.apache.maven.plugins.shade", null, null, null ) );
144 
145         List<ResourceTransformer> resourceTransformers = new ArrayList<>();
146 
147         List<Filter> filters = new ArrayList<>();
148 
149         File file = new File( "target/testShaderWithStaticInitializedClass.jar" );
150 
151         ShadeRequest shadeRequest = new ShadeRequest();
152         shadeRequest.setJars( set );
153         shadeRequest.setUberJar( file );
154         shadeRequest.setFilters( filters );
155         shadeRequest.setRelocators( relocators );
156         shadeRequest.setResourceTransformers( resourceTransformers );
157 
158         s.shade( shadeRequest );
159 
160         URLClassLoader cl = new URLClassLoader( new URL[] { file.toURI().toURL() } );
161         Class<?> c = cl.loadClass( "hidden.org.apache.maven.plugins.shade.Lib" );
162         Object o = c.newInstance();
163         assertEquals( "foo.bar/baz", c.getDeclaredField( "CONSTANT" ).get( o ) );
164     }
165 
166     public void testShaderWithCustomShadedPattern()
167         throws Exception
168     {
169         shaderWithPattern( "org/shaded/plexus/util", new File( "target/foo-custom.jar" ), EXCLUDES );
170     }
171 
172     public void testShaderWithoutExcludesShouldRemoveReferencesOfOriginalPattern()
173         throws Exception
174     {
175         // FIXME: shaded jar should not include references to org/codehaus/* (empty dirs) or org.codehaus.* META-INF
176         // files.
177         shaderWithPattern( "org/shaded/plexus/util", new File( "target/foo-custom-without-excludes.jar" ),
178                            new String[] {} );
179     }
180 
181     public void testShaderWithRelocatedClassname()
182         throws Exception
183     {
184         DefaultShader s = newShader();
185 
186         Set<File> set = new LinkedHashSet<>();
187 
188         set.add( new File( "src/test/jars/test-project-1.0-SNAPSHOT.jar" ) );
189 
190         set.add( new File( "src/test/jars/plexus-utils-1.4.1.jar" ) );
191 
192         List<Relocator> relocators = new ArrayList<>();
193 
194         relocators.add( new SimpleRelocator( "org/codehaus/plexus/util/", "_plexus/util/__", null,
195                                              Arrays.<String> asList() ) );
196 
197         List<ResourceTransformer> resourceTransformers = new ArrayList<>();
198 
199         resourceTransformers.add( new ComponentsXmlResourceTransformer() );
200 
201         List<Filter> filters = new ArrayList<>();
202 
203         File file = new File( "target/foo-relocate-class.jar" );
204 
205         ShadeRequest shadeRequest = new ShadeRequest();
206         shadeRequest.setJars( set );
207         shadeRequest.setUberJar( file );
208         shadeRequest.setFilters( filters );
209         shadeRequest.setRelocators( relocators );
210         shadeRequest.setResourceTransformers( resourceTransformers );
211 
212         s.shade( shadeRequest );
213 
214         URLClassLoader cl = new URLClassLoader( new URL[] { file.toURI().toURL() } );
215         Class<?> c = cl.loadClass( "_plexus.util.__StringUtils" );
216         // first, ensure it works:
217         Object o = c.newInstance();
218         assertEquals( "", c.getMethod( "clean", String.class ).invoke( o, (String) null ) );
219 
220         // now, check that its source file was rewritten:
221         final String[] source = { null };
222         final ClassReader classReader = new ClassReader( cl.getResourceAsStream( "_plexus/util/__StringUtils.class" ) );
223         classReader.accept( new ClassVisitor( Opcodes.ASM4 )
224         {
225             @Override
226             public void visitSource( String arg0, String arg1 )
227             {
228                 super.visitSource( arg0, arg1 );
229                 source[0] = arg0;
230             }
231         }, ClassReader.SKIP_CODE );
232         assertEquals( "__StringUtils.java", source[0] );
233     }
234 
235     private void shaderWithPattern( String shadedPattern, File jar, String[] excludes )
236         throws Exception
237     {
238         DefaultShader s = newShader();
239 
240         Set<File> set = new LinkedHashSet<>();
241 
242         set.add( new File( "src/test/jars/test-project-1.0-SNAPSHOT.jar" ) );
243 
244         set.add( new File( "src/test/jars/plexus-utils-1.4.1.jar" ) );
245 
246         List<Relocator> relocators = new ArrayList<>();
247 
248         relocators.add( new SimpleRelocator( "org/codehaus/plexus/util", shadedPattern, null, Arrays.asList( excludes ) ) );
249 
250         List<ResourceTransformer> resourceTransformers = new ArrayList<>();
251 
252         resourceTransformers.add( new ComponentsXmlResourceTransformer() );
253 
254         List<Filter> filters = new ArrayList<>();
255 
256         ShadeRequest shadeRequest = new ShadeRequest();
257         shadeRequest.setJars( set );
258         shadeRequest.setUberJar( jar );
259         shadeRequest.setFilters( filters );
260         shadeRequest.setRelocators( relocators );
261         shadeRequest.setResourceTransformers( resourceTransformers );
262 
263         s.shade( shadeRequest );
264     }
265 
266     private static DefaultShader newShader()
267     {
268         DefaultShader s = new DefaultShader();
269 
270         s.enableLogging( new ConsoleLogger( Logger.LEVEL_INFO, "TEST" ) );
271 
272         return s;
273     }
274 
275 }