36
36
import java .util .Queue ;
37
37
import java .util .Set ;
38
38
import java .util .logging .Logger ;
39
+ import java .util .stream .Collectors ;
39
40
import javax .annotation .Nullable ;
40
41
import org .apache .bcel .classfile .Field ;
41
42
import org .apache .bcel .classfile .FieldOrMethod ;
@@ -54,6 +55,7 @@ public class LinkageChecker {
54
55
private final ImmutableList <ClassPathEntry > classPath ;
55
56
private final SymbolReferences symbolReferences ;
56
57
private final ClassReferenceGraph classReferenceGraph ;
58
+ private final List <Artifact > sourceFilterList ;
57
59
private final ExcludedErrors excludedErrors ;
58
60
59
61
@ VisibleForTesting
@@ -66,7 +68,7 @@ public ClassReferenceGraph getClassReferenceGraph() {
66
68
}
67
69
68
70
public static LinkageChecker create (List <ClassPathEntry > classPath ) throws IOException {
69
- return create (classPath , ImmutableSet .copyOf (classPath ), null );
71
+ return create (classPath , ImmutableSet .copyOf (classPath ), ImmutableList . of (), null );
70
72
}
71
73
72
74
/**
@@ -79,6 +81,7 @@ public static LinkageChecker create(List<ClassPathEntry> classPath) throws IOExc
79
81
public static LinkageChecker create (
80
82
List <ClassPathEntry > classPath ,
81
83
Iterable <ClassPathEntry > entryPoints ,
84
+ List <Artifact > sourceFilterList ,
82
85
@ Nullable Path exclusionFile )
83
86
throws IOException {
84
87
Preconditions .checkArgument (!classPath .isEmpty (), "The linkage classpath is empty." );
@@ -93,6 +96,7 @@ public static LinkageChecker create(
93
96
classPath ,
94
97
symbolReferenceMaps ,
95
98
classReferenceGraph ,
99
+ sourceFilterList ,
96
100
ExcludedErrors .create (exclusionFile ));
97
101
}
98
102
@@ -129,28 +133,42 @@ public static LinkageChecker create(Bom bom, Path exclusionFile)
129
133
List <ClassPathEntry > artifactsInBom = classpath .subList (0 , managedDependencies .size ());
130
134
ImmutableSet <ClassPathEntry > entryPoints = ImmutableSet .copyOf (artifactsInBom );
131
135
132
- return LinkageChecker .create (classpath , entryPoints , exclusionFile );
136
+ return LinkageChecker .create (classpath , entryPoints , ImmutableList . of (), exclusionFile );
133
137
}
134
138
135
139
@ VisibleForTesting
136
140
LinkageChecker cloneWith (SymbolReferences newSymbolMaps ) {
137
141
return new LinkageChecker (
138
- classDumper , classPath , newSymbolMaps , classReferenceGraph , excludedErrors );
142
+ classDumper , classPath , newSymbolMaps , classReferenceGraph , ImmutableList . of (), excludedErrors );
139
143
}
140
144
141
145
private LinkageChecker (
142
146
ClassDumper classDumper ,
143
147
List <ClassPathEntry > classPath ,
144
148
SymbolReferences symbolReferenceMaps ,
145
149
ClassReferenceGraph classReferenceGraph ,
150
+ List <Artifact > sourceFilterList ,
146
151
ExcludedErrors excludedErrors ) {
147
152
this .classDumper = Preconditions .checkNotNull (classDumper );
148
153
this .classPath = ImmutableList .copyOf (classPath );
149
154
this .classReferenceGraph = Preconditions .checkNotNull (classReferenceGraph );
150
155
this .symbolReferences = Preconditions .checkNotNull (symbolReferenceMaps );
156
+ this .sourceFilterList = sourceFilterList ;
151
157
this .excludedErrors = Preconditions .checkNotNull (excludedErrors );
152
158
}
153
159
160
+ /**
161
+ * Two artifacts are considered equal if only the Maven Coordinates (GAV) are equal. This is
162
+ * included instead of using Artifact.equals() because the `equals()` implementation
163
+ * of DefaultArtifact checks more fields than just the GAV Coordinates (also checks the classifier,
164
+ * file path, properties, etc are all equal).
165
+ */
166
+ private boolean areArtifactsEquals (Artifact artifact1 , Artifact artifact2 ) {
167
+ return artifact1 .getGroupId ().equals (artifact2 .getGroupId ())
168
+ && artifact1 .getArtifactId ().equals (artifact2 .getArtifactId ())
169
+ && artifact1 .getVersion ().equals (artifact2 .getVersion ());
170
+ }
171
+
154
172
/**
155
173
* Searches the classpath for linkage errors.
156
174
*
@@ -161,7 +179,21 @@ public ImmutableSet<LinkageProblem> findLinkageProblems() throws IOException {
161
179
ImmutableSet .Builder <LinkageProblem > problemToClass = ImmutableSet .builder ();
162
180
163
181
// This sourceClassFile is a source of references to other symbols.
164
- for (ClassFile classFile : symbolReferences .getClassFiles ()) {
182
+ Set <ClassFile > classFiles = symbolReferences .getClassFiles ();
183
+
184
+ // Filtering the classFiles from the JARs (instead of using the problem filter) has additional a few
185
+ // additional benefits. 1. Reduces the total amount of linkage references to match and 2. Doesn't require
186
+ // an exclusion file to know all the possible flaky or false positive problems
187
+ if (!sourceFilterList .isEmpty ()) {
188
+ // Filter the list to only contain class files that come from the classes we are interested in.
189
+ // Run through each class file and check that the class file's corresponding artifact matches
190
+ // any artifact specified in the sourceFilterList
191
+ classFiles = classFiles .stream ()
192
+ .filter (x -> sourceFilterList .stream ()
193
+ .anyMatch (y -> areArtifactsEquals (x .getClassPathEntry ().getArtifact (), y )))
194
+ .collect (Collectors .toSet ());
195
+ }
196
+ for (ClassFile classFile : classFiles ) {
165
197
ImmutableSet <ClassSymbol > classSymbols = symbolReferences .getClassSymbols (classFile );
166
198
for (ClassSymbol classSymbol : classSymbols ) {
167
199
if (classSymbol instanceof SuperClassSymbol ) {
@@ -202,7 +234,7 @@ public ImmutableSet<LinkageProblem> findLinkageProblems() throws IOException {
202
234
}
203
235
}
204
236
205
- for (ClassFile classFile : symbolReferences . getClassFiles () ) {
237
+ for (ClassFile classFile : classFiles ) {
206
238
ImmutableSet <MethodSymbol > methodSymbols = symbolReferences .getMethodSymbols (classFile );
207
239
ImmutableSet <String > classFileNames = classFile .getClassPathEntry ().getFileNames ();
208
240
for (MethodSymbol methodSymbol : methodSymbols ) {
@@ -215,7 +247,7 @@ public ImmutableSet<LinkageProblem> findLinkageProblems() throws IOException {
215
247
}
216
248
}
217
249
218
- for (ClassFile classFile : symbolReferences . getClassFiles () ) {
250
+ for (ClassFile classFile : classFiles ) {
219
251
ImmutableSet <FieldSymbol > fieldSymbols = symbolReferences .getFieldSymbols (classFile );
220
252
ImmutableSet <String > classFileNames = classFile .getClassPathEntry ().getFileNames ();
221
253
for (FieldSymbol fieldSymbol : fieldSymbols ) {
0 commit comments