Coverage Report - org.apache.maven.plugins.shade.DefaultShader
 
Classes in this File Line Coverage Branch Coverage Complexity
DefaultShader
83%
72/87
74%
31/42
4,455
DefaultShader$RelocatorRemapper
58%
25/43
42%
15/36
4,455
 
 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.FileOutputStream;
 24  
 import java.io.IOException;
 25  
 import java.io.InputStream;
 26  
 import java.util.Enumeration;
 27  
 import java.util.HashSet;
 28  
 import java.util.Iterator;
 29  
 import java.util.List;
 30  
 import java.util.Set;
 31  
 import java.util.ArrayList;
 32  
 import java.util.jar.JarEntry;
 33  
 import java.util.jar.JarFile;
 34  
 import java.util.jar.JarOutputStream;
 35  
 import java.util.zip.ZipException;
 36  
 
 37  
 import org.apache.maven.plugins.shade.relocation.Relocator;
 38  
 import org.apache.maven.plugins.shade.resource.ResourceTransformer;
 39  
 import org.apache.maven.plugins.shade.filter.Filter;
 40  
 import org.codehaus.plexus.logging.AbstractLogEnabled;
 41  
 import org.codehaus.plexus.util.IOUtil;
 42  
 import org.objectweb.asm.ClassReader;
 43  
 import org.objectweb.asm.ClassVisitor;
 44  
 import org.objectweb.asm.ClassWriter;
 45  
 import org.objectweb.asm.commons.Remapper;
 46  
 import org.objectweb.asm.commons.RemappingClassAdapter;
 47  
 
 48  
 /**
 49  
  * @author Jason van Zyl
 50  
  * @plexus.component
 51  
  */
 52  7
 public class DefaultShader
 53  
     extends AbstractLogEnabled
 54  
     implements Shader
 55  
 {
 56  
     public void shade( Set jars, File uberJar, List filters, List relocators, List resourceTransformers )
 57  
     throws IOException
 58  
     {
 59  7
         Set resources = new HashSet();
 60  
 
 61  7
         RelocatorRemapper remapper = new RelocatorRemapper( relocators );
 62  
 
 63  7
         uberJar.getParentFile().mkdirs();
 64  7
         JarOutputStream jos = new JarOutputStream( new FileOutputStream( uberJar ) );
 65  
 
 66  7
         for ( Iterator i = jars.iterator(); i.hasNext(); )
 67  
         {
 68  12
             File jar = (File) i.next();
 69  
 
 70  12
             List jarFilters = getFilters( jar, filters );
 71  
 
 72  12
             JarFile jarFile = new JarFile( jar );
 73  
 
 74  12
             for ( Enumeration j = jarFile.entries(); j.hasMoreElements(); )
 75  
             {
 76  631
                 JarEntry entry = (JarEntry) j.nextElement();
 77  
 
 78  631
                 String name = entry.getName();
 79  631
                 if ( "META-INF/INDEX.LIST".equals( name ) ) 
 80  
                 {
 81  
                     //we cannot allow the jar indexes to be copied over or the 
 82  
                     //jar is useless.   Ideally, we could create a new one
 83  
                     //later
 84  0
                     continue;
 85  
                 }
 86  
 
 87  631
                 String mappedName = remapper.map( name );
 88  
 
 89  631
                 InputStream is = jarFile.getInputStream( entry );
 90  631
                 if ( !entry.isDirectory() && !isFiltered( jarFilters, name ) )
 91  
                 {
 92  483
                     int idx = mappedName.lastIndexOf( '/' );
 93  483
                     if ( idx != -1 )
 94  
                     {
 95  
                         //make sure dirs are created
 96  483
                         String dir = mappedName.substring( 0, idx );
 97  483
                         if ( !resources.contains( dir ) )
 98  
                         {
 99  81
                             addDirectory( resources, jos, dir );
 100  
                         }
 101  
                     }
 102  
 
 103  483
                     if ( name.endsWith( ".class" ) )
 104  
                     {
 105  422
                         addRemappedClass( remapper, jos, jar, name, is );
 106  
                     }
 107  
                     else
 108  
                     {
 109  61
                         if ( !resourceTransformed( resourceTransformers, mappedName, is ) )
 110  
                         {
 111  
                             // Avoid duplicates that aren't accounted for by the resource transformers
 112  56
                             if ( resources.contains( mappedName ) )
 113  
                             {
 114  5
                                 continue;
 115  
                             }
 116  
 
 117  51
                             addResource( resources, jos, mappedName, is );
 118  
                         }
 119  
                     }
 120  
                 }
 121  
 
 122  626
                 IOUtil.close( is );
 123  
             }
 124  
 
 125  12
             jarFile.close();
 126  
         }
 127  
 
 128  7
         for ( Iterator i = resourceTransformers.iterator(); i.hasNext(); )
 129  
         {
 130  5
             ResourceTransformer transformer = (ResourceTransformer) i.next();
 131  
 
 132  5
             if ( transformer.hasTransformedResource() )
 133  
             {
 134  5
                 transformer.modifyOutputStream( jos );
 135  
             }
 136  
         }
 137  
 
 138  7
         IOUtil.close( jos );
 139  7
     }
 140  
 
 141  
     private List getFilters( File jar, List filters )
 142  
     {
 143  12
         List list = new ArrayList();
 144  
 
 145  12
         for ( int i = 0; i < filters.size(); i++ )
 146  
         {
 147  0
             Filter filter = (Filter) filters.get( i );
 148  
 
 149  0
             if ( filter.canFilter( jar ) )
 150  
             {
 151  0
                 list.add( filter );
 152  
             }
 153  
 
 154  
         }
 155  
 
 156  12
         return list;
 157  
     }
 158  
 
 159  
     private void addDirectory( Set resources, JarOutputStream jos, String name )
 160  
         throws IOException
 161  
     {
 162  150
         if ( name.lastIndexOf( '/' ) > 0 )
 163  
         {
 164  129
             String parent = name.substring( 0, name.lastIndexOf( '/' ) );
 165  129
             if ( !resources.contains( parent ) )
 166  
             {
 167  69
                 addDirectory( resources, jos, parent );
 168  
             }
 169  
         }
 170  
 
 171  
         //directory entries must end in "/"
 172  150
         JarEntry entry = new JarEntry( name + "/" );
 173  150
         jos.putNextEntry( entry );
 174  
 
 175  150
         resources.add( name );
 176  150
     }
 177  
 
 178  
     private void addRemappedClass( RelocatorRemapper remapper, JarOutputStream jos, File jar, String name,
 179  
                                    InputStream is )
 180  
         throws IOException
 181  
     {
 182  422
         if ( !remapper.hasRelocators() )
 183  
         {
 184  
             try
 185  
             {
 186  0
                 jos.putNextEntry( new JarEntry( name ) );
 187  0
                 IOUtil.copy( is, jos );
 188  
             }
 189  0
             catch ( ZipException e )
 190  
             {
 191  0
                 getLogger().warn( "We have a duplicate " + name + " in " + jar );
 192  0
             }
 193  
 
 194  0
             return;
 195  
         }
 196  
 
 197  422
         ClassReader cr = new ClassReader( is );
 198  
 
 199  422
         ClassWriter cw = new ClassWriter( cr, 0 );
 200  
 
 201  422
         ClassVisitor cv = new RemappingClassAdapter( cw, remapper );
 202  
 
 203  422
         cr.accept( cv, ClassReader.EXPAND_FRAMES );
 204  
 
 205  422
         byte[] renamedClass = cw.toByteArray();
 206  
 
 207  
         // Need to take the .class off for remapping evaluation
 208  422
         String mappedName = remapper.map( name.substring( 0, name.indexOf( '.' ) ) );
 209  
 
 210  
         try
 211  
         {
 212  
             // Now we put it back on so the class file is written out with the right extension.
 213  422
             jos.putNextEntry( new JarEntry( mappedName + ".class" ) );
 214  
 
 215  422
             IOUtil.copy( renamedClass, jos );
 216  
         }
 217  0
         catch ( ZipException e )
 218  
         {
 219  0
             getLogger().warn( "We have a duplicate " + mappedName + " in " + jar );
 220  422
         }
 221  422
     }
 222  
 
 223  
     private boolean isFiltered( List filters, String name )
 224  
     {
 225  483
         for ( int i = 0; i < filters.size(); i++ )
 226  
         {
 227  0
             Filter filter = (Filter) filters.get( i );
 228  
 
 229  0
             if ( filter.isFiltered( name ) )
 230  
             {
 231  0
                 return true;
 232  
             }
 233  
         }
 234  
 
 235  483
         return false;
 236  
     }
 237  
 
 238  
     private boolean resourceTransformed( List resourceTransformers, String name, InputStream is )
 239  
         throws IOException
 240  
     {
 241  61
         boolean resourceTransformed = false;
 242  
 
 243  61
         for ( Iterator k = resourceTransformers.iterator(); k.hasNext(); )
 244  
         {
 245  55
             ResourceTransformer transformer = (ResourceTransformer) k.next();
 246  
 
 247  55
             if ( transformer.canTransformResource( name ) )
 248  
             {
 249  5
                 transformer.processResource( is );
 250  
 
 251  5
                 resourceTransformed = true;
 252  
 
 253  5
                 break;
 254  
             }
 255  
         }
 256  61
         return resourceTransformed;
 257  
     }
 258  
 
 259  
     private void addResource( Set resources, JarOutputStream jos, String name, InputStream is )
 260  
         throws IOException
 261  
     {
 262  51
         jos.putNextEntry( new JarEntry( name ) );
 263  
 
 264  51
         IOUtil.copy( is, jos );
 265  
 
 266  51
         resources.add( name );
 267  51
     }
 268  
 
 269  7
     class RelocatorRemapper
 270  
         extends Remapper
 271  
     {
 272  
         List relocators;
 273  
 
 274  
         public RelocatorRemapper( List relocators )
 275  7
         {
 276  7
             this.relocators = relocators;
 277  7
         }
 278  
 
 279  
         public boolean hasRelocators()
 280  
         {
 281  422
             return !relocators.isEmpty();
 282  
         }
 283  
 
 284  
         public Object mapValue( Object object )
 285  
         {
 286  8212
             if ( object instanceof String )
 287  
             {
 288  4603
                 String name = (String) object;
 289  4603
                 String value = name;
 290  4603
                 for ( Iterator i = relocators.iterator(); i.hasNext(); )
 291  
                 {
 292  4603
                     Relocator r = (Relocator) i.next();
 293  
 
 294  4603
                     if ( r.canRelocatePath( name ) )
 295  
                     {
 296  0
                         value = r.relocatePath( name );
 297  0
                         break;
 298  
                     }
 299  
 
 300  4603
                     if ( r.canRelocateClass( name ) )
 301  
                     {
 302  1
                         value = r.relocateClass( name );
 303  1
                         break;
 304  
                     }
 305  4602
                     if ( name.length() > 0 && name.charAt( 0 ) == '[' ) 
 306  
                     {
 307  0
                         int count = 0;
 308  0
                         while ( name.length() > 0 && name.charAt(0) == '[' ) 
 309  
                         {
 310  0
                             name = name.substring( 1 );
 311  0
                             ++count;
 312  
                         }
 313  
                         
 314  0
                         if ( name.length() > 0 
 315  
                              && name.charAt( 0 ) == 'L'
 316  
                              && name.charAt( name.length() - 1 ) == ';' ) 
 317  
                         {
 318  0
                             name = name.substring( 1, name.length() - 1 );
 319  
                                                         
 320  0
                             if ( r.canRelocatePath( name ) )
 321  
                             {
 322  0
                                 value = 'L' + r.relocatePath( name ) + ';';
 323  0
                                 while ( count > 0 ) 
 324  
                                 {
 325  0
                                     value = '[' + value;
 326  0
                                     --count;
 327  
                                 }
 328  
                                 break;
 329  
                             }
 330  
 
 331  0
                             if ( r.canRelocateClass( name ) )
 332  
                             {
 333  0
                                 value = 'L' + r.relocateClass( name ) + ';';
 334  0
                                 while (count > 0) 
 335  
                                 {
 336  0
                                     value = '[' + value;
 337  0
                                     --count;
 338  
                                 }
 339  4602
                                 break;
 340  
                             }
 341  
                             
 342  
                         }
 343  
                     }
 344  
                 }
 345  
 
 346  4603
                 return value;
 347  
             } 
 348  
             else 
 349  
             {
 350  3609
                 object = super.mapValue( object );
 351  
             }
 352  3609
             return object;
 353  
         }
 354  
 
 355  
         public String map( String name )
 356  
         {
 357  102041
             String value = name;
 358  102041
             for ( Iterator i = relocators.iterator(); i.hasNext(); )
 359  
             {
 360  102041
                 Relocator r = (Relocator) i.next();
 361  
 
 362  102041
                 if ( r.canRelocatePath( name ) )
 363  
                 {
 364  20977
                     value = r.relocatePath( name );
 365  20977
                     break;
 366  
                 }
 367  
             }
 368  
 
 369  102041
             return value;
 370  
         }
 371  
     }
 372  
 }