mirror of
https://github.com/apache/struts.git
synced 2026-08-05 06:36:58 +00:00
WW-5674 perf(ognl): walk package names in place instead of building prefixes
Replaces the split/IntStream/String.join prefix construction with an index walk, extracted into a pure package-private helper so it can be tested against package name shapes no real Class can produce. Per call this drops a String[], a list wrapper, a stream pipeline, N sublist views and N joined strings, leaving one substring per package level. Equivalence with the replaced implementation is asserted over a matrix of package name shapes and candidate sets.
This commit is contained in:
@@ -32,11 +32,9 @@ import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static java.text.MessageFormat.format;
|
||||
import static java.util.Collections.emptySet;
|
||||
@@ -393,10 +391,33 @@ public class SecurityMemberAccess implements MemberAccess {
|
||||
}
|
||||
|
||||
public static boolean isClassBelongsToPackages(Class<?> clazz, Set<String> matchingPackages) {
|
||||
List<String> packageParts = List.of(toPackageName(clazz).split("\\."));
|
||||
return IntStream.range(0, packageParts.size())
|
||||
.mapToObj(i -> String.join(".", packageParts.subList(0, i + 1)))
|
||||
.anyMatch(matchingPackages::contains);
|
||||
return isPackageBelongsToPackages(toPackageName(clazz), matchingPackages, emptySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether the given package name, or any of its parent packages, is present in either
|
||||
* set. Walks the name in place rather than building the full prefix list, since this runs on
|
||||
* the OGNL member-access path. Shortest prefix first, so broad entries such as {@code java.io}
|
||||
* short-circuit earliest.
|
||||
*
|
||||
* @param packageName the package name to test, empty for the default package
|
||||
* @param first the first set of package names to match against
|
||||
* @param second the second set of package names to match against
|
||||
* @return {@code true} if the package or any parent package is in either set
|
||||
*/
|
||||
static boolean isPackageBelongsToPackages(String packageName, Set<String> first, Set<String> second) {
|
||||
if (first.isEmpty() && second.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
int idx = packageName.indexOf('.');
|
||||
while (idx != -1) {
|
||||
String prefix = packageName.substring(0, idx);
|
||||
if (first.contains(prefix) || second.contains(prefix)) {
|
||||
return true;
|
||||
}
|
||||
idx = packageName.indexOf('.', idx + 1);
|
||||
}
|
||||
return first.contains(packageName) || second.contains(packageName);
|
||||
}
|
||||
|
||||
protected boolean isClassExcluded(Class<?> clazz) {
|
||||
|
||||
+20
@@ -175,4 +175,24 @@ public class SecurityMemberAccessPackageMatchingTest {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void indexWalkMatchesLegacyAcrossPackageNameShapes() {
|
||||
for (String packageName : PACKAGE_NAMES) {
|
||||
for (Set<String> candidates : CANDIDATE_SETS) {
|
||||
assertThat(SecurityMemberAccess.isPackageBelongsToPackages(packageName, candidates, emptySet()))
|
||||
.as("packageName=[%s] candidates=%s", packageName, candidates)
|
||||
.isEqualTo(legacyPrefixMatch(packageName, candidates));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bothSetsEmptyShortCircuitsToFalse() {
|
||||
for (String packageName : PACKAGE_NAMES) {
|
||||
assertThat(SecurityMemberAccess.isPackageBelongsToPackages(packageName, emptySet(), emptySet()))
|
||||
.as("packageName=[%s] with no configured packages", packageName)
|
||||
.isFalse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user