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
90 this.log = log;
91
92 Clazzpath cp = new Clazzpath();
93
94 ClazzpathUnit artifactUnit =
95 cp.addClazzpathUnit( new FileInputStream( project.getArtifact().getFile() ), project.toString() );
96
97 for ( Artifact dependency : project.getArtifacts() )
98 {
99 addDependencyToClasspath( cp, dependency );
100 }
101
102 removable = cp.getClazzes();
103 removePackages( artifactUnit );
104 removable.removeAll( artifactUnit.getClazzes() );
105 removable.removeAll( artifactUnit.getTransitiveDependencies() );
106 removeSpecificallyIncludedClasses( project, simpleFilters == null ? Collections.<SimpleFilter>emptyList()
107 : simpleFilters );
108 }
109
110 private ClazzpathUnit addDependencyToClasspath( Clazzpath cp, Artifact dependency )
111 throws IOException
112 {
113 InputStream is = null;
114 ClazzpathUnit clazzpathUnit = null;
115 try
116 {
117 is = new FileInputStream( dependency.getFile() );
118 clazzpathUnit = cp.addClazzpathUnit( is, dependency.toString() );
119 is.close();
120 is = null;
121 }
122 catch ( ZipException e )
123 {
124 log.warn( dependency.getFile()
125 + " could not be unpacked/read for minimization; dependency is probably malformed." );
126 IOException ioe = new IOException( "Dependency " + dependency.toString() + " in file "
127 + dependency.getFile() + " could not be unpacked. File is probably corrupt" );
128 ioe.initCause( e );
129 throw ioe;
130 }
131 catch ( ArrayIndexOutOfBoundsException e )
132 {
133
134 log.warn( dependency.toString()
135 + " could not be analyzed for minimization; dependency is probably malformed." );
136 }
137 finally
138 {
139 IOUtil.close( is );
140 }
141
142 return clazzpathUnit;
143 }
144
145 private void removePackages( ClazzpathUnit artifactUnit )
146 {
147 Set<String> packageNames = new HashSet<String>();
148 removePackages( artifactUnit.getClazzes(), packageNames );
149 removePackages( artifactUnit.getTransitiveDependencies(), packageNames );
150 }
151
152 @SuppressWarnings( "rawtypes" )
153 private void removePackages( Set clazzes, Set<String> packageNames )
154 {
155 for ( Object clazze : clazzes )
156 {
157 Clazz clazz = (Clazz) clazze;
158 String name = clazz.getName();
159 while ( name.contains( "." ) )
160 {
161 name = name.substring( 0, name.lastIndexOf( '.' ) );
162 if ( packageNames.add( name ) )
163 {
164 removable.remove( new Clazz( name + ".package-info" ) );
165 }
166 }
167 }
168 }
169
170 private void removeSpecificallyIncludedClasses( MavenProject project, List<SimpleFilter> simpleFilters )
171 throws IOException
172 {
173
174 Clazzpath checkCp = new Clazzpath();
175 for ( Artifact dependency : project.getArtifacts() )
176 {
177 File jar = dependency.getFile();
178
179 for ( SimpleFilter simpleFilter : simpleFilters )
180 {
181 if ( simpleFilter.canFilter( jar ) )
182 {
183 ClazzpathUnit depClazzpathUnit = addDependencyToClasspath( checkCp, dependency );
184 if ( depClazzpathUnit != null )
185 {
186 Set<Clazz> clazzes = depClazzpathUnit.getClazzes();
187 Iterator<Clazz> j = removable.iterator();
188 while ( j.hasNext() )
189 {
190 Clazz clazz = j.next();
191
192 if ( clazzes.contains( clazz )
193 && simpleFilter.isSpecificallyIncluded( clazz.getName().replace( '.', '/' ) ) )
194 {
195 log.info( clazz.getName() + " not removed because it was specifically included" );
196 j.remove();
197 }
198 }
199 }
200 }
201 }
202 }
203 }
204
205
206 public boolean canFilter( File jar )
207 {
208 return true;
209 }
210
211
212 public boolean isFiltered( String classFile )
213 {
214 String className = classFile.replace( '/', '.' ).replaceFirst( "\\.class$", "" );
215 Clazz clazz = new Clazz( className );
216
217 if ( removable.contains( clazz ) )
218 {
219 log.debug( "Removing " + className );
220 classesRemoved += 1;
221 return true;
222 }
223
224 classesKept += 1;
225 return false;
226 }
227
228
229 public void finished()
230 {
231 int classesTotal = classesRemoved + classesKept;
232 if ( classesTotal != 0 )
233 {
234 log.info( "Minimized " + classesTotal + " -> " + classesKept + " (" + 100 * classesKept / classesTotal
235 + "%)" );
236 }
237 else
238 {
239 log.info( "Minimized " + classesTotal + " -> " + classesKept );
240 }
241 }
242 }