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