[ 
https://issues.apache.org/jira/browse/WW-5337?focusedWorklogId=877305&page=com.atlassian.jira.plugin.system.issuetabpanels:worklog-tabpanel#worklog-877305
 ]

ASF GitHub Bot logged work on WW-5337:
--------------------------------------

                Author: ASF GitHub Bot
            Created on: 21/Aug/23 14:39
            Start Date: 21/Aug/23 14:39
    Worklog Time Spent: 10m 
      Work Description: kusalk commented on code in PR #736:
URL: https://github.com/apache/struts/pull/736#discussion_r1300194447


##########
core/src/main/java/com/opensymphony/xwork2/ognl/OgnlUtil.java:
##########
@@ -261,7 +262,8 @@ public void setDevModeExcludedPackageExemptClasses(String 
commaDelimitedClasses)
     }
 
     private Set<String> parseExcludedPackageNames(String 
commaDelimitedPackageNames) {
-        Set<String> parsedSet = 
TextParseUtil.commaDelimitedStringToSet(commaDelimitedPackageNames);
+        Set<String> parsedSet = 
commaDelimitedStringToSet(commaDelimitedPackageNames)
+                .stream().map(s -> strip(s, ".")).collect(toSet());

Review Comment:
   I think it's unintuitive to require a period at the of each excluded 
package. Currently if the period is missed, the exclusion will act like the 
regex `com\.package.*` which likely isn't what users configuring their 
application intend.
   
   By stripping the period here, we retain backwards compatibility for excluded 
package lists that still have trailing periods.
   
   However, we do drop support for wildcard package exclusions where 
applications genuinely desired the previous behaviour when a trailing period 
wasn't included. They will need to move such exclusions to the regex exclusion 
list instead.



##########
core/src/main/java/com/opensymphony/xwork2/ognl/OgnlUtil.java:
##########
@@ -218,14 +220,13 @@ protected void 
setDevModeExcludedPackageNamePatterns(String commaDelimitedPackag
     }
 
     private Set<Pattern> parseExcludedPackageNamePatterns(String 
commaDelimitedPackagePatterns) {
-        Set<String> packagePatterns = 
TextParseUtil.commaDelimitedStringToSet(commaDelimitedPackagePatterns);
-        Set<Pattern> packageNamePatterns = new HashSet<>();
-
-        for (String pattern : packagePatterns) {
-            packageNamePatterns.add(Pattern.compile(pattern));
+        try {
+            return commaDelimitedStringToSet(commaDelimitedPackagePatterns)
+                    .stream().map(Pattern::compile).collect(toSet());
+        } catch (PatternSyntaxException e) {
+            throw new ConfigurationException(

Review Comment:
   Ensure we throw ConfigurationException when RegEx patterns provided are 
invalid



##########
core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java:
##########
@@ -237,33 +242,27 @@ protected String toPackageName(Class<?> clazz) {
 
     protected boolean isExcludedPackageNamePatterns(Class<?> clazz) {
         String packageName = toPackageName(clazz);
-        for (Pattern pattern : excludedPackageNamePatterns) {
-            if (pattern.matcher(packageName).matches()) {
-                return true;
-            }
-        }
-        return false;
+        return excludedPackageNamePatterns.stream().anyMatch(pattern -> 
pattern.matcher(packageName).matches());
     }
 
     protected boolean isExcludedPackageNames(Class<?> clazz) {
-        String suffixedPackageName = toPackageName(clazz) + ".";
-        for (String excludedPackageName : excludedPackageNames) {
-            if (suffixedPackageName.startsWith(excludedPackageName)) {
+        String packageName = toPackageName(clazz);
+        List<String> packageParts = Arrays.asList(packageName.split("\\."));
+        for (int i = 0; i < packageParts.size(); i++) {
+            String parentPackage = String.join(".", packageParts.subList(0, i 
+ 1));
+            if (excludedPackageNames.contains(parentPackage)) {
                 return true;
             }
         }
         return false;
     }
 
     protected boolean isClassExcluded(Class<?> clazz) {
-        if (clazz == Object.class || (clazz == Class.class && 
!allowStaticFieldAccess)) {
-            return true;
-        }
-        return excludedClasses.stream().anyMatch(clazz::isAssignableFrom);
+        return excludedClasses.contains(clazz);
     }
 
     protected boolean isExcludedPackageExempt(Class<?> clazz) {
-        return excludedPackageExemptClasses.stream().anyMatch(clazz::equals);
+        return excludedPackageExemptClasses.contains(clazz);

Review Comment:
   Constant time operation



##########
core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java:
##########
@@ -237,33 +242,27 @@ protected String toPackageName(Class<?> clazz) {
 
     protected boolean isExcludedPackageNamePatterns(Class<?> clazz) {
         String packageName = toPackageName(clazz);
-        for (Pattern pattern : excludedPackageNamePatterns) {
-            if (pattern.matcher(packageName).matches()) {
-                return true;
-            }
-        }
-        return false;
+        return excludedPackageNamePatterns.stream().anyMatch(pattern -> 
pattern.matcher(packageName).matches());
     }
 
     protected boolean isExcludedPackageNames(Class<?> clazz) {
-        String suffixedPackageName = toPackageName(clazz) + ".";
-        for (String excludedPackageName : excludedPackageNames) {
-            if (suffixedPackageName.startsWith(excludedPackageName)) {
+        String packageName = toPackageName(clazz);
+        List<String> packageParts = Arrays.asList(packageName.split("\\."));
+        for (int i = 0; i < packageParts.size(); i++) {

Review Comment:
   Operations proportional to number of package parts of class being checked. 
Previously it was proportional to number of excluded packages in configuration.
   
   Note that we still retain the behaviour of sub-packages also being excluded.



##########
core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java:
##########
@@ -237,33 +242,27 @@ protected String toPackageName(Class<?> clazz) {
 
     protected boolean isExcludedPackageNamePatterns(Class<?> clazz) {
         String packageName = toPackageName(clazz);
-        for (Pattern pattern : excludedPackageNamePatterns) {
-            if (pattern.matcher(packageName).matches()) {
-                return true;
-            }
-        }
-        return false;
+        return excludedPackageNamePatterns.stream().anyMatch(pattern -> 
pattern.matcher(packageName).matches());
     }
 
     protected boolean isExcludedPackageNames(Class<?> clazz) {
-        String suffixedPackageName = toPackageName(clazz) + ".";
-        for (String excludedPackageName : excludedPackageNames) {
-            if (suffixedPackageName.startsWith(excludedPackageName)) {
+        String packageName = toPackageName(clazz);
+        List<String> packageParts = Arrays.asList(packageName.split("\\."));
+        for (int i = 0; i < packageParts.size(); i++) {
+            String parentPackage = String.join(".", packageParts.subList(0, i 
+ 1));
+            if (excludedPackageNames.contains(parentPackage)) {
                 return true;
             }
         }
         return false;
     }
 
     protected boolean isClassExcluded(Class<?> clazz) {
-        if (clazz == Object.class || (clazz == Class.class && 
!allowStaticFieldAccess)) {

Review Comment:
   Moved this logic to #useExcludedClasses to make this method more performant



##########
core/src/test/java/com/opensymphony/xwork2/ognl/SecurityMemberAccessTest.java:
##########
@@ -178,23 +178,6 @@ public void testMiddleOfInheritanceExclusion3() throws 
Exception {
         assertTrue("barLogic() from BarInterface isn't accessible!!!", 
accessible);
     }
 
-    @Test
-    public void testMiddleOfInheritanceExclusion4() throws Exception {

Review Comment:
   Superinterfaces and superclasses of excluded classes are no longer also 
excluded. I'm not too sure of a real world example where this behaviour might 
actually be useful/sensible. Or where you wouldn't just add to the exclusion 
list the desired superinterfaces/superclasses instead?
   
   If this behaviour is genuinely useful, it would be better if we moved such 
interfaces/classes to a separate exclusion list to keep the regular excluded 
class list performant.



##########
core/src/main/resources/struts-excluded-classes.xml:
##########
@@ -55,52 +55,52 @@
     <!

Issue Time Tracking
-------------------

    Worklog Id:     (was: 877305)
    Time Spent: 40m  (was: 0.5h)

> Improve performance of excluded classes and packages
> ----------------------------------------------------
>
>                 Key: WW-5337
>                 URL: https://issues.apache.org/jira/browse/WW-5337
>             Project: Struts 2
>          Issue Type: Improvement
>          Components: Core
>            Reporter: Kusal Kithul-Godage
>            Priority: Minor
>             Fix For: 6.3.0
>
>          Time Spent: 40m
>  Remaining Estimate: 0h
>
> By using a HashMap we can significantly improve performance for applications 
> with extensive exclusion lists.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to