View Javadoc

1   package org.apache.maven.plugins.shade.filter;
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.artifact.Artifact;
23  import org.apache.maven.plugin.logging.Log;
24  import org.apache.maven.project.MavenProject;
25  import org.codehaus.plexus.util.IOUtil;
26  import org.vafer.jdependency.Clazz;
27  import org.vafer.jdependency.Clazzpath;
28  import org.vafer.jdependency.ClazzpathUnit;
29  
30  import java.io.File;
31  import java.io.FileInputStream;
32  import java.io.IOException;
33  import java.io.InputStream;
34  import java.util.Collections;
35  import java.util.HashSet;
36  import java.util.Iterator;
37  import java.util.List;
38  import java.util.Set;
39  
40  /**
41   * A filter that prevents the inclusion of classes not required in the final jar.
42   *
43   * @author Torsten Curdt
44   */
45  public class MinijarFilter
46      implements Filter
47  {
48  
49      private Log log;
50  
51      private Set removable;
52  
53      private int classesKept;
54  
55      private int classesRemoved;
56  
57      public MinijarFilter( MavenProject project, Log log )
58          throws IOException
59      {
60          this( project, log, Collections.<SimpleFilter>emptyList() );
61      }
62  
63      /**
64       *
65       * @since 1.6
66       */
67      public MinijarFilter( MavenProject project, Log log, List<SimpleFilter> simpleFilters )
68          throws IOException
69      {
70  
71          this.log = log;
72  
73          Clazzpath cp = new Clazzpath();
74  
75          ClazzpathUnit artifactUnit =
76              cp.addClazzpathUnit( new FileInputStream( project.getArtifact().getFile() ), project.toString() );
77  
78          for ( Iterator it = project.getArtifacts().iterator(); it.hasNext(); )
79          {
80              Artifact dependency = (Artifact) it.next();
81  
82              InputStream is = null;
83              try
84              {
85                  is = new FileInputStream( dependency.getFile() );
86                  cp.addClazzpathUnit( is, dependency.toString() );
87              }
88              finally
89              {
90                  IOUtil.close( is );
91              }
92          }
93  
94          removable = cp.getClazzes();
95          removePackages( artifactUnit );
96          removable.removeAll( artifactUnit.getClazzes() );
97          removable.removeAll( artifactUnit.getTransitiveDependencies() );
98          removeSpecificallyIncludedClasses( project, simpleFilters == null
99              ? Collections.<SimpleFilter>emptyList()
100             : simpleFilters );
101     }
102 
103     private void removePackages( ClazzpathUnit artifactUnit )
104     {
105         Set packageNames = new HashSet();
106         removePackages( artifactUnit.getClazzes(), packageNames );
107         removePackages( artifactUnit.getTransitiveDependencies(), packageNames );
108     }
109 
110     private void removePackages( Set clazzes, Set packageNames )
111     {
112         Iterator it = clazzes.iterator();
113         while ( it.hasNext() )
114         {
115             Clazz clazz = (Clazz) it.next();
116             String name = clazz.getName();
117             while ( name.contains( "." ) )
118             {
119                 name = name.substring( 0, name.lastIndexOf( '.' ) );
120                 if ( packageNames.add( name ) )
121                 {
122                     removable.remove( new Clazz( name + ".package-info" ) );
123                 }
124             }
125         }
126     }
127 
128     private void removeSpecificallyIncludedClasses( MavenProject project, List<SimpleFilter> simpleFilters )
129         throws IOException
130     {
131         //remove classes specifically included in filters
132         Clazzpath checkCp = new Clazzpath();
133         for ( Iterator it = project.getArtifacts().iterator(); it.hasNext(); )
134         {
135             Artifact dependency = (Artifact) it.next();
136             File jar = dependency.getFile();
137 
138             for ( Iterator<SimpleFilter> i = simpleFilters.iterator(); i.hasNext(); )
139             {
140                 SimpleFilter simpleFilter = i.next();
141                 if ( simpleFilter.canFilter( jar ) )
142                 {
143                     InputStream is = null;
144                     ClazzpathUnit depClazzpathUnit = null;
145                     try
146                     {
147                         is = new FileInputStream( dependency.getFile() );
148                         depClazzpathUnit = checkCp.addClazzpathUnit( is, dependency.toString() );
149                     }
150                     finally
151                     {
152                         IOUtil.close( is );
153                     }
154 
155                     if ( depClazzpathUnit != null )
156                     {
157                         Iterator<Clazz> j = removable.iterator();
158                         while ( j.hasNext() )
159                         {
160                             Clazz clazz = j.next();
161 
162                             if ( depClazzpathUnit.getClazzes().contains( clazz ) && simpleFilter.isSpecificallyIncluded(
163                                 clazz.getName().replace( '.', '/' ) ) )
164                             {
165                                 log.info( clazz.getName() + " not removed because it was specifically included" );
166                                 j.remove();
167                             }
168                         }
169                     }
170                 }
171             }
172         }
173     }
174 
175     public boolean canFilter( File jar )
176     {
177         return true;
178     }
179 
180     public boolean isFiltered( String classFile )
181     {
182         String className = classFile.replace( '/', '.' ).replaceFirst( "\\.class$", "" );
183         Clazz clazz = new Clazz( className );
184 
185         if ( removable.contains( clazz ) )
186         {
187             log.debug( "Removing " + className );
188             classesRemoved += 1;
189             return true;
190         }
191 
192         classesKept += 1;
193         return false;
194     }
195 
196     public void finished()
197     {
198         int classes_total = classesRemoved + classesKept;
199         log.info(
200             "Minimized " + classes_total + " -> " + classesKept + " (" + (int) ( 100 * classesKept / classes_total )
201                 + "%)" );
202     }
203 }