This is an automated email from the ASF dual-hosted git repository.

pkarwasz pushed a commit to branch 2.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git


The following commit(s) were added to refs/heads/2.x by this push:
     new de63a4ff1e Correct script resolution order dependency (#3718)
de63a4ff1e is described below

commit de63a4ff1ee93f2a0527a4bb8dce3bbd0635b0ec
Author: jhl221123 <[email protected]>
AuthorDate: Fri Jun 20 18:09:30 2025 +0900

    Correct script resolution order dependency (#3718)
    
    Alters AbstractConfiguration to process the Scripts element before other 
components. This allows ScriptRef elements to be resolved correctly, regardless 
of their order in the configuration file.
    
    Co-authored-by: Volkan Yazıcı <[email protected]>
---
 .../core/config/AbstractConfigurationTest.java     | 25 ++++++++++++
 .../test/resources/log4j2-script-order-test.xml    | 31 +++++++++++++++
 .../log4j/core/config/AbstractConfiguration.java   | 45 +++++++++++++---------
 .../.2.x.x/3336_script_resolution_order_fix.xml    | 12 ++++++
 4 files changed, 95 insertions(+), 18 deletions(-)

diff --git 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/AbstractConfigurationTest.java
 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/AbstractConfigurationTest.java
index 91d4405edf..64ce9ac3fc 100644
--- 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/AbstractConfigurationTest.java
+++ 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/AbstractConfigurationTest.java
@@ -24,14 +24,19 @@ import java.util.Collections;
 import java.util.Map;
 import java.util.Map.Entry;
 import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.filter.CompositeFilter;
+import org.apache.logging.log4j.core.filter.ScriptFilter;
 import org.apache.logging.log4j.core.lookup.Interpolator;
 import org.apache.logging.log4j.core.lookup.InterpolatorTest;
 import org.apache.logging.log4j.core.lookup.StrSubstitutor;
+import org.apache.logging.log4j.core.test.junit.LoggerContextSource;
+import org.apache.logging.log4j.test.junit.SetTestProperty;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.ValueSource;
 import org.junitpioneer.jupiter.Issue;
 
+@SetTestProperty(key = "log4j2.scriptEnableLanguages", value = "groovy")
 class AbstractConfigurationTest {
 
     @Test
@@ -60,6 +65,26 @@ class AbstractConfigurationTest {
         }
     }
 
+    @Test
+    @LoggerContextSource("log4j2-script-order-test.xml")
+    void scriptRefShouldBeResolvedWhenScriptsElementIsLast(final Configuration 
config) {
+        assertThat(config.getFilter())
+                .as("Top-level filter should be a CompositeFilter")
+                .isInstanceOf(CompositeFilter.class);
+        final CompositeFilter compositeFilter = (CompositeFilter) 
config.getFilter();
+
+        assertThat(compositeFilter.getFilters())
+                .as("CompositeFilter should contain one filter")
+                .hasSize(1);
+        final ScriptFilter scriptFilter =
+                (ScriptFilter) compositeFilter.getFilters().get(0);
+
+        assertThat(scriptFilter).isNotNull();
+        assertThat(scriptFilter.toString())
+                .as("Script name should match the one in the config")
+                .isEqualTo("GLOBAL_FILTER");
+    }
+
     private static class TestConfiguration extends AbstractConfiguration {
 
         private final Map<String, String> map;
diff --git a/log4j-core-test/src/test/resources/log4j2-script-order-test.xml 
b/log4j-core-test/src/test/resources/log4j2-script-order-test.xml
new file mode 100644
index 0000000000..810b160973
--- /dev/null
+++ b/log4j-core-test/src/test/resources/log4j2-script-order-test.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to you under the Apache License, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License.  You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<Configuration status="WARN">
+    <Filters>
+        <ScriptFilter onMatch="ACCEPT" onMisMatch="DENY">
+            <ScriptRef ref="GLOBAL_FILTER"/>
+        </ScriptFilter>
+    </Filters>
+
+    <Scripts>
+        <Script name="GLOBAL_FILTER" language="groovy"><![CDATA[
+        // simple script
+        return true
+    ]]></Script>
+    </Scripts>
+</Configuration>
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
index 4c3dde9179..402dfe6d7f 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
@@ -682,13 +682,14 @@ public abstract class AbstractConfiguration extends 
AbstractFilterable implement
         preConfigure(rootNode);
         configurationScheduler.start();
         // Find the "Properties" node first
+        final List<Node> children = rootNode.getChildren();
         boolean hasProperties = false;
-        for (final Node node : rootNode.getChildren()) {
-            if ("Properties".equalsIgnoreCase(node.getName())) {
+        for (final Node child : children) {
+            if ("Properties".equalsIgnoreCase(child.getName())) {
                 hasProperties = true;
-                createConfiguration(node, null);
-                if (node.getObject() != null) {
-                    final StrLookup lookup = node.getObject();
+                createConfiguration(child, null);
+                if (child.getObject() != null) {
+                    final StrLookup lookup = child.getObject();
                     runtimeStrSubstitutor.setVariableResolver(lookup);
                     configurationStrSubstitutor.setVariableResolver(lookup);
                 }
@@ -705,10 +706,28 @@ public abstract class AbstractConfiguration extends 
AbstractFilterable implement
             configurationStrSubstitutor.setVariableResolver(interpolator);
         }
 
+        for (final Node child : children) {
+            if ("Scripts".equalsIgnoreCase(child.getName())) {
+                createConfiguration(child, null);
+                if (child.getObject() != null) {
+                    for (final AbstractScript script : 
child.getObject(AbstractScript[].class)) {
+                        if (script instanceof ScriptRef) {
+                            LOGGER.error(
+                                    "Script reference to {} not added. Scripts 
definition cannot contain script references",
+                                    script.getName());
+                        } else if (scriptManager != null) {
+                            scriptManager.addScript(script);
+                        }
+                    }
+                }
+                break;
+            }
+        }
+
         boolean setLoggers = false;
         boolean setRoot = false;
-        for (final Node child : rootNode.getChildren()) {
-            if ("Properties".equalsIgnoreCase(child.getName())) {
+        for (final Node child : children) {
+            if ("Properties".equalsIgnoreCase(child.getName()) || 
"Scripts".equalsIgnoreCase(child.getName())) {
                 // We already used this node
                 continue;
             }
@@ -716,17 +735,7 @@ public abstract class AbstractConfiguration extends 
AbstractFilterable implement
             if (child.getObject() == null) {
                 continue;
             }
-            if ("Scripts".equalsIgnoreCase(child.getName())) {
-                for (final AbstractScript script : 
child.getObject(AbstractScript[].class)) {
-                    if (script instanceof ScriptRef) {
-                        LOGGER.error(
-                                "Script reference to {} not added. Scripts 
definition cannot contain script references",
-                                script.getName());
-                    } else if (scriptManager != null) {
-                        scriptManager.addScript(script);
-                    }
-                }
-            } else if ("Appenders".equalsIgnoreCase(child.getName())) {
+            if ("Appenders".equalsIgnoreCase(child.getName())) {
                 appenders = child.getObject();
             } else if (child.isInstanceOf(Filter.class)) {
                 addFilter(child.getObject(Filter.class));
diff --git a/src/changelog/.2.x.x/3336_script_resolution_order_fix.xml 
b/src/changelog/.2.x.x/3336_script_resolution_order_fix.xml
new file mode 100644
index 0000000000..f9adb48e5b
--- /dev/null
+++ b/src/changelog/.2.x.x/3336_script_resolution_order_fix.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<entry xmlns="https://logging.apache.org/xml/ns";
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+       xsi:schemaLocation="
+           https://logging.apache.org/xml/ns
+           https://logging.apache.org/xml/ns/log4j-changelog-0.xsd";
+       type="fixed">
+    <issue id="3336" 
link="https://github.com/apache/logging-log4j2/issues/3336"/>
+    <description format="asciidoc">
+        Fix script resolution failure when the `Scripts` element is placed 
after a `ScriptRef` in the configuration.
+    </description>
+</entry>

Reply via email to