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 java.io.File;
23 import java.io.FileInputStream;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.util.Collections;
27 import java.util.HashSet;
28 import java.util.Iterator;
29 import java.util.List;
30 import java.util.Set;
31 import java.util.zip.ZipException;
32
33 import org.apache.maven.artifact.Artifact;
34 import org.apache.maven.plugin.logging.Log;
35 import org.apache.maven.project.MavenProject;
36 import org.codehaus.plexus.util.IOUtil;
37 import org.vafer.jdependency.Clazz;
38 import org.vafer.jdependency.Clazzpath;
39 import org.vafer.jdependency.ClazzpathUnit;
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 public MinijarFilter( MavenProject project, Log log )
59 throws IOException
60 {
61 this( project, log, Collections.<SimpleFilter>emptyList() );
62 }
63
64
65
66
67
68 @SuppressWarnings( { "unchecked" } )
69 public MinijarFilter( MavenProject project, Log log, List<SimpleFilter> simpleFilters )
70 throws IOException
71 {
72
73 this.log = log;
74
75 Clazzpath cp = new Clazzpath();
76
77 ClazzpathUnit artifactUnit =
78 cp.addClazzpathUnit( new FileInputStream( project.getArtifact().getFile() ), project.toString() );
79
80 for ( Artifact dependency : project.getArtifacts() )
81 {
82 addDependencyToClasspath( cp, dependency );
83 }
84
85 removable = cp.getClazzes();
86 removePackages( artifactUnit );
87 removable.removeAll( artifactUnit.getClazzes() );
88 removable.removeAll( artifactUnit.getTransitiveDependencies() );
89 removeSpecificallyIncludedClasses( project, simpleFilters == null
90 ? Collections.<SimpleFilter>emptyList()
91 : simpleFilters );
92 }
93
94 private ClazzpathUnit addDependencyToClasspath( Clazzpath cp, Artifact dependency )
95 throws IOException
96 {
97 InputStream is = null;
98 ClazzpathUnit clazzpathUnit = null;
99 try
100 {
101 is = new FileInputStream( dependency.getFile() );
102 clazzpathUnit = cp.addClazzpathUnit( is, dependency.toString() );
103 }
104 catch ( ZipException e )
105 {
106 log.warn( dependency.getFile()
107 + " could not be unpacked/read for minimization; dependency is probably malformed." );
108 IOException ioe =
109 new IOException( "Dependency " + dependency.toString() + " in file " + dependency.getFile()
110 + " could not be unpacked. File is probably corrupt" );
111 ioe.initCause( e );
112 throw ioe;
113 }
114 catch ( ArrayIndexOutOfBoundsException e )
115 {
116
117 log.warn( dependency.toString()
118 + " could not be analyzed for minimization; dependency is probably malformed." );
119 }
120 finally
121 {
122 IOUtil.close( is );
123 }
124
125 return clazzpathUnit;
126 }
127
128 private void removePackages( ClazzpathUnit artifactUnit )
129 {
130 Set<String> packageNames = new HashSet<String>();
131 removePackages( artifactUnit.getClazzes(), packageNames );
132 removePackages( artifactUnit.getTransitiveDependencies(), packageNames );
133 }
134
135 @SuppressWarnings( "rawtypes" )
136 private void removePackages( Set clazzes, Set<String> packageNames )
137 {
138 Iterator it = clazzes.iterator();
139 while ( it.hasNext() )
140 {
141 Clazz clazz = (Clazz) it.next();
142 String name = clazz.getName();
143 while ( name.contains( "." ) )
144 {
145 name = name.substring( 0, name.lastIndexOf( '.' ) );
146 if ( packageNames.add( name ) )
147 {
148 removable.remove( new Clazz( name + ".package-info" ) );
149 }
150 }
151 }
152 }
153
154 private void removeSpecificallyIncludedClasses( MavenProject project, List<SimpleFilter> simpleFilters )
155 throws IOException
156 {
157
158 Clazzpath checkCp = new Clazzpath();
159 for ( Artifact dependency : project.getArtifacts() )
160 {
161 File jar = dependency.getFile();
162
163 for ( SimpleFilter simpleFilter : simpleFilters )
164 {
165 if ( simpleFilter.canFilter( jar ) )
166 {
167 ClazzpathUnit depClazzpathUnit = addDependencyToClasspath( checkCp, dependency );
168 if ( depClazzpathUnit != null )
169 {
170 Iterator<Clazz> j = removable.iterator();
171 while ( j.hasNext() )
172 {
173 Clazz clazz = j.next();
174
175 if ( depClazzpathUnit.getClazzes().contains( clazz )
176 && simpleFilter.isSpecificallyIncluded( clazz.getName().replace( '.', '/' ) ) )
177 {
178 log.info( clazz.getName() + " not removed because it was specifically included" );
179 j.remove();
180 }
181 }
182 }
183 }
184 }
185 }
186 }
187
188 public boolean canFilter( File jar )
189 {
190 return true;
191 }
192
193 public boolean isFiltered( String classFile )
194 {
195 String className = classFile.replace( '/', '.' ).replaceFirst( "\\.class$", "" );
196 Clazz clazz = new Clazz( className );
197
198 if ( removable.contains( clazz ) )
199 {
200 log.debug( "Removing " + className );
201 classesRemoved += 1;
202 return true;
203 }
204
205 classesKept += 1;
206 return false;
207 }
208
209 public void finished()
210 {
211 int classesTotal = classesRemoved + classesKept;
212 log.info(
213 "Minimized " + classesTotal + " -> " + classesKept + " (" + (int) ( 100 * classesKept / classesTotal )
214 + "%)" );
215 }
216 }