1 package org.apache.maven.plugins.shade.filter;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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 import java.util.zip.ZipException;
40
41
42
43
44
45
46 public class MinijarFilter
47 implements Filter
48 {
49
50 private Log log;
51
52 private Set<Clazz> removable;
53
54 private int classesKept;
55
56 private int classesRemoved;
57
58
59
60
61 MinijarFilter( int classesKept, int classesRemoved, Log log )
62 {
63 this.classesKept = classesKept;
64 this.classesRemoved = classesRemoved;
65 this.log = log;
66 }
67
68
69
70
71
72
73 public MinijarFilter( MavenProject project, Log log )
74 throws IOException
75 {
76 this( project, log, Collections.<SimpleFilter>emptyList() );
77 }
78
79
80
81
82
83
84
85
86 public MinijarFilter( MavenProject project, Log log, List<SimpleFilter> simpleFilters )
87 throws IOException
88 {
89 this.log = log;
90
91 File artifactFile = project.getArtifact().getFile();
92
93 if ( artifactFile != null )
94 {
95 Clazzpath cp = new Clazzpath();
96
97 ClazzpathUnit artifactUnit = cp.addClazzpathUnit( new FileInputStream( artifactFile ), project.toString() );
98
99 for ( Artifact dependency : project.getArtifacts() )
100 {
101 addDependencyToClasspath( cp, dependency );
102 }
103
104 removable = cp.getClazzes();
105 if ( removable.remove( new Clazz( "module-info" ) ) )
106 {
107 log.warn( "Removing module-info from " + artifactFile.getName() );
108 }
109 removePackages( artifactUnit );
110 removable.removeAll( artifactUnit.getClazzes() );
111 removable.removeAll( artifactUnit.getTransitiveDependencies() );
112 removeSpecificallyIncludedClasses( project,
113 simpleFilters == null ? Collections.<SimpleFilter>emptyList() : simpleFilters );
114 }
115 }
116
117 private ClazzpathUnit addDependencyToClasspath( Clazzpath cp, Artifact dependency )
118 throws IOException
119 {
120 InputStream is = null;
121 ClazzpathUnit clazzpathUnit = null;
122 try
123 {
124 is = new FileInputStream( dependency.getFile() );
125 clazzpathUnit = cp.addClazzpathUnit( is, dependency.toString() );
126 is.close();
127 is = null;
128 }
129 catch ( ZipException e )
130 {
131 log.warn( dependency.getFile()
132 + " could not be unpacked/read for minimization; dependency is probably malformed." );
133 IOException ioe = new IOException( "Dependency " + dependency.toString() + " in file "
134 + dependency.getFile() + " could not be unpacked. File is probably corrupt" );
135 ioe.initCause( e );
136 throw ioe;
137 }
138 catch ( ArrayIndexOutOfBoundsException | IllegalArgumentException e )
139 {
140
141 log.warn( dependency.toString()
142 + " could not be analyzed for minimization; dependency is probably malformed." );
143 }
144 finally
145 {
146 IOUtil.close( is );
147 }
148
149 return clazzpathUnit;
150 }
151
152 private void removePackages( ClazzpathUnit artifactUnit )
153 {
154 Set<String> packageNames = new HashSet<String>();
155 removePackages( artifactUnit.getClazzes(), packageNames );
156 removePackages( artifactUnit.getTransitiveDependencies(), packageNames );
157 }
158
159 @SuppressWarnings( "rawtypes" )
160 private void removePackages( Set clazzes, Set<String> packageNames )
161 {
162 for ( Object clazze : clazzes )
163 {
164 Clazz clazz = (Clazz) clazze;
165 String name = clazz.getName();
166 while ( name.contains( "." ) )
167 {
168 name = name.substring( 0, name.lastIndexOf( '.' ) );
169 if ( packageNames.add( name ) )
170 {
171 removable.remove( new Clazz( name + ".package-info" ) );
172 }
173 }
174 }
175 }
176
177 private void removeSpecificallyIncludedClasses( MavenProject project, List<SimpleFilter> simpleFilters )
178 throws IOException
179 {
180
181 Clazzpath checkCp = new Clazzpath();
182 for ( Artifact dependency : project.getArtifacts() )
183 {
184 File jar = dependency.getFile();
185
186 for ( SimpleFilter simpleFilter : simpleFilters )
187 {
188 if ( simpleFilter.canFilter( jar ) )
189 {
190 ClazzpathUnit depClazzpathUnit = addDependencyToClasspath( checkCp, dependency );
191 if ( depClazzpathUnit != null )
192 {
193 Set<Clazz> clazzes = depClazzpathUnit.getClazzes();
194 Iterator<Clazz> j = removable.iterator();
195 while ( j.hasNext() )
196 {
197 Clazz clazz = j.next();
198
199 if ( clazzes.contains( clazz )
200 && simpleFilter.isSpecificallyIncluded( clazz.getName().replace( '.', '/' ) ) )
201 {
202 log.info( clazz.getName() + " not removed because it was specifically included" );
203 j.remove();
204 }
205 }
206 }
207 }
208 }
209 }
210 }
211
212
213 public boolean canFilter( File jar )
214 {
215 return true;
216 }
217
218
219 public boolean isFiltered( String classFile )
220 {
221 String className = classFile.replace( '/', '.' ).replaceFirst( "\\.class$", "" );
222 Clazz clazz = new Clazz( className );
223
224 if ( removable != null && removable.contains( clazz ) )
225 {
226 log.debug( "Removing " + className );
227 classesRemoved += 1;
228 return true;
229 }
230
231 classesKept += 1;
232 return false;
233 }
234
235
236 public void finished()
237 {
238 int classesTotal = classesRemoved + classesKept;
239 if ( classesTotal != 0 )
240 {
241 log.info( "Minimized " + classesTotal + " -> " + classesKept + " (" + 100 * classesKept / classesTotal
242 + "%)" );
243 }
244 else
245 {
246 log.info( "Minimized " + classesTotal + " -> " + classesKept );
247 }
248 }
249 }