SLING-11610 - Sling XSS API 2.3.0 does not work on Java 17

Use the reflection API in a way that is compatible with Java 17. This still requires
invokers to use "-add-opens", see the updates surefire config.
diff --git a/pom.xml b/pom.xml
index 3e37d9e..d9c9d19 100644
--- a/pom.xml
+++ b/pom.xml
@@ -377,5 +377,26 @@
             <scope>test</scope>
         </dependency>
     </dependencies>
+    
+    <profiles>
+        <profile>
+            <id>java12-or-higher</id>
+            <activation>
+                <!-- starting with Java 12 some reflective operations require add-opens instructions -->
+                <jdk>[12,)</jdk>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <argLine>--add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED</argLine>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
 
 </project>
diff --git a/src/main/java/org/apache/sling/xss/impl/AntiSamyPolicyAdapter.java b/src/main/java/org/apache/sling/xss/impl/AntiSamyPolicyAdapter.java
index cb20842..20a8568 100644
--- a/src/main/java/org/apache/sling/xss/impl/AntiSamyPolicyAdapter.java
+++ b/src/main/java/org/apache/sling/xss/impl/AntiSamyPolicyAdapter.java
@@ -19,6 +19,7 @@
 package org.apache.sling.xss.impl;
 
 import java.lang.reflect.Field;
+import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -27,8 +28,8 @@
 import java.util.regex.Pattern;
 
 import org.apache.sling.xss.impl.style.CssValidator;
-import org.apache.sling.xss.impl.xml.Attribute;
 import org.apache.sling.xss.impl.xml.AntiSamyPolicy;
+import org.apache.sling.xss.impl.xml.Attribute;
 import org.apache.sling.xss.impl.xml.Tag;
 import org.jetbrains.annotations.Nullable;
 import org.owasp.html.AttributePolicy;
@@ -222,6 +223,7 @@
 
     private static Predicate<String> matchesPatternsOrLiterals(List<Pattern> patternList, boolean ignoreCase, List<String> literalList) {
         return new Predicate<String>() {
+            @Override
             public boolean apply(String s) {
                 // check if the string matches to the pattern or one of the literal
                 s = ignoreCase ? s.toLowerCase() : s;
@@ -261,13 +263,28 @@
         }
     }
 
-    private void letMeIn(Field field) throws ReflectiveOperationException {
-        if (!field.isAccessible())
-            field.setAccessible(true);
-        if ((field.getModifiers() & Modifier.FINAL) != 0) {
-            Field modifiersField = Field.class.getDeclaredField("modifiers");
+    private void letMeIn(Field inaccessible) throws ReflectiveOperationException {
+        if (!inaccessible.isAccessible())
+            inaccessible.setAccessible(true);
+        if ((inaccessible.getModifiers() & Modifier.FINAL) != 0) {
+            Field modifiersField = null;
+            try { 
+                modifiersField = Field.class.getDeclaredField("modifiers");
+            } catch ( NoSuchFieldException e ) {
+                // fallback for Java 12+
+                Method getDeclaredFields = Class.class.getDeclaredMethod("getDeclaredFields0", boolean.class);
+                getDeclaredFields.setAccessible(true);
+                Field[] fields = (Field[]) getDeclaredFields.invoke(Field.class, false);
+                for ( Field field : fields ) {
+                    if ( "modifiers".equals(field.getName()) ) {
+                        modifiersField = field;
+                    }
+                }
+            }
+            if ( modifiersField == null )
+                throw new IllegalAccessException("Unable to locate modifiers field " + Field.class.getName() + ", aborting setup");
             modifiersField.setAccessible(true);
-            modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
+            modifiersField.setInt(inaccessible, inaccessible.getModifiers() & ~Modifier.FINAL);
         }
     }
 }
\ No newline at end of file