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

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


The following commit(s) were added to refs/heads/main by this push:
     new a47b2b1f7f [main] Add `@Required` and validation to 
`StringMatchFilter` `text` attribute (#3158 port) (#4153)
a47b2b1f7f is described below

commit a47b2b1f7fcf0468a92693cdc081c92b97d81873
Author: Vasily Pelikh <[email protected]>
AuthorDate: Fri Aug 21 18:21:37 2026 +0300

    [main] Add `@Required` and validation to `StringMatchFilter` `text` 
attribute (#3158 port) (#4153)
    
    * Update StringMatchFilter builder API (#3509)
    
    * Guard against NPEs
    * added private constructor to builder
    * added getText() accessor
    * add JVerify nullability annotations
    * added detailed javadoc with implementation details
    * optimized AbstractLifeCycle and AbstractFilter "equalsImpl" 
implementations
    
    
    Signed-off-by: Vasily Pelikh <[email protected]>
    Co-authored-by: Jeff Thomas <[email protected]>
    Co-authored-by: Ramanathan <[email protected]>
---
 .../log4j/core/filter/StringMatchFilterTest.java   | 126 +++++++++++++++++++++
 .../log4j2-stringmatchfilter-3153-nok.xml          |  23 ++++
 .../resources/log4j2-stringmatchfilter-3153-ok.xml |  23 ++++
 .../log4j/core/filter/StringMatchFilter.java       |  53 +++++++--
 4 files changed, 214 insertions(+), 11 deletions(-)

diff --git 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/StringMatchFilterTest.java
 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/StringMatchFilterTest.java
new file mode 100644
index 0000000000..09864f285b
--- /dev/null
+++ 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/StringMatchFilterTest.java
@@ -0,0 +1,126 @@
+/*
+ * 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.
+ */
+package org.apache.logging.log4j.core.filter;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.test.junit.LoggerContextSource;
+import org.apache.logging.log4j.test.ListStatusListener;
+import org.apache.logging.log4j.test.junit.UsingStatusListener;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Unit tests for {@link StringMatchFilter}.
+ */
+@UsingStatusListener
+class StringMatchFilterTest {
+
+    /**
+     * Test the normal valid programmatic instantiation of a {@link 
StringMatchFilter} via its builder.
+     */
+    @Test
+    void testFilterBuilderOK() {
+        StringMatchFilter.Builder stringMatchFilterBuilder = 
StringMatchFilter.newBuilder();
+        stringMatchFilterBuilder.setText("foo");
+        StringMatchFilter stringMatchFilter = stringMatchFilterBuilder.build();
+        assertNotNull(stringMatchFilter, "The filter should not be null.");
+        assertEquals("foo", stringMatchFilter.getText());
+    }
+
+    /**
+     * Test that if no match-string is set on the builder, the '{@link 
StringMatchFilter.Builder#build()}' returns
+     * {@code null}.
+     */
+    @Test
+    void testFilterBuilderFailsWithNullText() {
+        StringMatchFilter.Builder stringMatchFilterBuilder = 
StringMatchFilter.newBuilder();
+        Assertions.assertNull(stringMatchFilterBuilder.build());
+    }
+
+    /**
+     * Test that if a {@code null} string is set as a match-pattern, a {@link 
NullPointerException} is thrown.
+     */
+    @Test
+    @SuppressWarnings({"DataFlowIssue" // invalid null parameter explicitly 
being tested
+    })
+    void testFilterBuilderFailsWithExceptionOnNullText() {
+        StringMatchFilter.Builder stringMatchFilterBuilder = 
StringMatchFilter.newBuilder();
+        Assertions.assertThrows(NullPointerException.class, () -> 
stringMatchFilterBuilder.setText(null));
+    }
+
+    /**
+     * Test that if an empty ({@code ""}) string is set as a match-pattern, an 
{@code IllegalArgumentException} is thrown.
+     */
+    @Test
+    void testFilterBuilderFailsWithExceptionOnEmptyText() {
+        StringMatchFilter.Builder stringMatchFilterBuilder = 
StringMatchFilter.newBuilder();
+        Assertions.assertThrows(IllegalArgumentException.class, () -> 
stringMatchFilterBuilder.setText(""));
+    }
+
+    /**
+     * Test the deprecated {@link 
StringMatchFilter.Builder#setMatchString(String)} alias still works as expected.
+     */
+    @Test
+    void testFilterBuilderWithDeprecatedSetMatchString() {
+        StringMatchFilter.Builder stringMatchFilterBuilder = 
StringMatchFilter.newBuilder();
+        stringMatchFilterBuilder.setMatchString("foo");
+        StringMatchFilter stringMatchFilter = stringMatchFilterBuilder.build();
+        assertNotNull(stringMatchFilter, "The filter should not be null.");
+        assertEquals("foo", stringMatchFilter.getText());
+    }
+
+    /**
+     * Test that if a {@link StringMatchFilter} is specified with a 'text' 
attribute it is correctly instantiated.
+     *
+     * @param configuration the configuration
+     */
+    @Test
+    @LoggerContextSource("log4j2-stringmatchfilter-3153-ok.xml")
+    void testConfigurationWithTextPOS(final Configuration configuration) {
+        final Filter filter = configuration.getFilter();
+        assertNotNull(filter, "The filter should not be null.");
+        assertInstanceOf(
+                StringMatchFilter.class, filter, "Expected a 
StringMatchFilter, but got: " + filter.getClass());
+        assertEquals("FooBar", ((StringMatchFilter) filter).getText());
+    }
+
+    /**
+     * Test that if a {@link StringMatchFilter} is specified without a 'text' 
attribute it is not instantiated and the
+     * {@code @Required} constraint validation reports an error.
+     *
+     * @param configuration the configuration
+     * @param listener the status listener capturing the validation errors
+     */
+    @Test
+    @LoggerContextSource("log4j2-stringmatchfilter-3153-nok.xml")
+    void testConfigurationWithTextNEG(final Configuration configuration, final 
ListStatusListener listener) {
+        final Filter filter = configuration.getFilter();
+        assertNull(filter, "The filter should be null.");
+        assertThat(listener.findStatusData(Level.ERROR)).anyMatch(statusData 
-> statusData
+                .getMessage()
+                .getFormattedMessage()
+                .contains("No text provided for StringMatchFilter"));
+    }
+}
diff --git 
a/log4j-core-test/src/test/resources/log4j2-stringmatchfilter-3153-nok.xml 
b/log4j-core-test/src/test/resources/log4j2-stringmatchfilter-3153-nok.xml
new file mode 100644
index 0000000000..ff7a83ce8c
--- /dev/null
+++ b/log4j-core-test/src/test/resources/log4j2-stringmatchfilter-3153-nok.xml
@@ -0,0 +1,23 @@
+<?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">
+   <StringMatchFilter/> <!-- no 'text' attribute: an unconfigurable filter 
must not be created -->
+   <Loggers>
+      <Root/> <!-- one logger to avoid test warning about no defined loggers 
-->
+   </Loggers>
+</Configuration>
diff --git 
a/log4j-core-test/src/test/resources/log4j2-stringmatchfilter-3153-ok.xml 
b/log4j-core-test/src/test/resources/log4j2-stringmatchfilter-3153-ok.xml
new file mode 100644
index 0000000000..598dfaa302
--- /dev/null
+++ b/log4j-core-test/src/test/resources/log4j2-stringmatchfilter-3153-ok.xml
@@ -0,0 +1,23 @@
+<?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">
+   <StringMatchFilter text="FooBar"/>
+   <Loggers>
+      <Root/> <!-- one logger to avoid test warning about no defined loggers 
-->
+   </Loggers>
+</Configuration>
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/StringMatchFilter.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/StringMatchFilter.java
index 51ee3b2a20..d30a2a64eb 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/StringMatchFilter.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/StringMatchFilter.java
@@ -16,6 +16,7 @@
  */
 package org.apache.logging.log4j.core.filter;
 
+import java.util.Objects;
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.Marker;
 import org.apache.logging.log4j.core.Filter;
@@ -26,7 +27,10 @@ import org.apache.logging.log4j.plugins.Configurable;
 import org.apache.logging.log4j.plugins.Plugin;
 import org.apache.logging.log4j.plugins.PluginBuilderAttribute;
 import org.apache.logging.log4j.plugins.PluginFactory;
+import org.apache.logging.log4j.plugins.util.Assert;
+import org.apache.logging.log4j.plugins.validation.constraints.Required;
 import org.apache.logging.log4j.util.PerformanceSensitive;
+import org.jspecify.annotations.NonNull;
 
 /**
  * This filter returns the onMatch result if the message in the event matches 
the specified text
@@ -38,11 +42,22 @@ import org.apache.logging.log4j.util.PerformanceSensitive;
 public final class StringMatchFilter extends AbstractFilter {
 
     public static final String ATTR_MATCH = "match";
+
     private final String text;
 
-    private StringMatchFilter(final String text, final Result onMatch, final 
Result onMismatch) {
-        super(onMatch, onMismatch);
-        this.text = text;
+    private StringMatchFilter(final Builder builder) {
+        super(builder.getOnMatch(), builder.getOnMismatch());
+        this.text = Assert.requireNonEmpty(builder.text, "The 'text' argument 
must not be null or empty.");
+    }
+
+    /**
+     * Returns the text this filter searches for in event messages.
+     *
+     * @return the text this filter searches for in event messages
+     * @since 3.0.0
+     */
+    public String getText() {
+        return text;
     }
 
     @Override
@@ -230,28 +245,44 @@ public final class StringMatchFilter extends 
AbstractFilter {
     }
 
     @PluginFactory
-    public static StringMatchFilter.Builder newBuilder() {
-        return new StringMatchFilter.Builder();
+    public static Builder newBuilder() {
+        return new Builder();
     }
 
-    public static class Builder extends 
AbstractFilterBuilder<StringMatchFilter.Builder>
-            implements 
org.apache.logging.log4j.core.util.Builder<StringMatchFilter> {
+    public static class Builder extends AbstractFilterBuilder<Builder>
+            implements 
org.apache.logging.log4j.plugins.util.Builder<StringMatchFilter> {
+
         @PluginBuilderAttribute
-        private String text = "";
+        @Required(message = "No text provided for StringMatchFilter")
+        private String text;
 
         /**
          * Sets the text to search in event messages.
          * @param text the text to search in event messages.
          * @return this instance.
+         * @since 3.0.0
          */
-        public StringMatchFilter.Builder setMatchString(final String text) {
-            this.text = text;
+        public Builder setText(@NonNull final String text) {
+            Objects.requireNonNull(text, "The 'text' argument must not be 
null.");
+            this.text = Assert.requireNonEmpty(text, "The 'text' argument must 
not be empty.");
             return this;
         }
 
+        /**
+         * @deprecated since 3.0.0, use {@link #setText(String)} instead.
+         */
+        @Deprecated(since = "3.0.0")
+        public Builder setMatchString(final String text) {
+            return setText(text);
+        }
+
         @Override
         public StringMatchFilter build() {
-            return new StringMatchFilter(this.text, this.getOnMatch(), 
this.getOnMismatch());
+            if (this.text == null) {
+                LOGGER.error("Unable to create StringMatchFilter: The 'text' 
attribute must be configured.");
+                return null;
+            }
+            return new StringMatchFilter(this);
         }
     }
 }

Reply via email to