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

vladimirsitnikov pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/jmeter.git


The following commit(s) were added to refs/heads/master by this push:
     new 032f71f7db chore: remove uses of commons-collect4
032f71f7db is described below

commit 032f71f7dbcac0e96962b9cf50ce1b018d168704
Author: Vladimir Sitnikov <[email protected]>
AuthorDate: Fri Nov 7 20:44:45 2025 +0300

    chore: remove uses of commons-collect4
---
 src/components/build.gradle.kts                    |   1 -
 .../visualizers/ViewResultsFullVisualizer.java     |  14 +-
 src/core/build.gradle.kts                          |   1 -
 .../java/org/apache/jmeter/config/Arguments.java   |   2 +-
 .../java/org/apache/jmeter/gui/LoggerPanel.java    |  11 +-
 .../org/apache/jmeter/save/CSVSaveService.java     |  15 ++-
 src/dist/build.gradle.kts                          |   3 +
 src/jorphan/build.gradle.kts                       |   1 -
 .../apache/jorphan/collections/FilterIterator.kt   |  71 ++++++++++
 .../jorphan/collections/FilterIteratorTest.kt      | 150 +++++++++++++++++++++
 src/protocol/http/build.gradle.kts                 |   3 -
 11 files changed, 246 insertions(+), 26 deletions(-)

diff --git a/src/components/build.gradle.kts b/src/components/build.gradle.kts
index f485e2bc69..e33df038a1 100644
--- a/src/components/build.gradle.kts
+++ b/src/components/build.gradle.kts
@@ -67,7 +67,6 @@ dependencies {
     implementation("org.apache.httpcomponents:httpcore-nio")
     implementation("org.jsoup:jsoup")
     implementation("net.sf.jtidy:jtidy")
-    implementation("org.apache.commons:commons-collections4")
     implementation("org.apache.commons:commons-math3")
     implementation("commons-io:commons-io") {
         because("IOUtils")
diff --git 
a/src/components/src/main/java/org/apache/jmeter/visualizers/ViewResultsFullVisualizer.java
 
b/src/components/src/main/java/org/apache/jmeter/visualizers/ViewResultsFullVisualizer.java
index 5f74df6b34..dd65bc648c 100644
--- 
a/src/components/src/main/java/org/apache/jmeter/visualizers/ViewResultsFullVisualizer.java
+++ 
b/src/components/src/main/java/org/apache/jmeter/visualizers/ViewResultsFullVisualizer.java
@@ -61,7 +61,6 @@ import javax.swing.tree.TreeNode;
 import javax.swing.tree.TreePath;
 import javax.swing.tree.TreeSelectionModel;
 
-import org.apache.commons.collections4.queue.CircularFifoQueue;
 import org.apache.jmeter.JMeter;
 import org.apache.jmeter.assertions.AssertionResult;
 import org.apache.jmeter.gui.GUIMenuSortOrder;
@@ -139,7 +138,8 @@ implements ActionListener, TreeSelectionListener, 
Clearable, ItemListener {
     private Object resultsObject = null;
     private TreeSelectionEvent lastSelectionEvent;
     private JCheckBox autoScrollCB;
-    private final Queue<SampleResult> buffer;
+    private final Queue<SampleResult> buffer = new ArrayDeque<>();
+    private final int maxResults;
     private boolean dataChanged;
 
     /**
@@ -147,12 +147,7 @@ implements ActionListener, TreeSelectionListener, 
Clearable, ItemListener {
      */
     public ViewResultsFullVisualizer() {
         super();
-        final int maxResults = 
JMeterUtils.getPropDefault("view.results.tree.max_results", 500);
-        if (maxResults > 0) {
-            buffer = new CircularFifoQueue<>(maxResults);
-        } else {
-            buffer = new ArrayDeque<>();
-        }
+        this.maxResults = 
JMeterUtils.getPropDefault("view.results.tree.max_results", 500);
         init();
         new Timer(REFRESH_PERIOD, e -> updateGui()).start();
     }
@@ -161,6 +156,9 @@ implements ActionListener, TreeSelectionListener, 
Clearable, ItemListener {
     @Override
     public void add(final SampleResult sample) {
         synchronized (buffer) {
+            if (maxResults > 0 && buffer.size() >= maxResults) {
+                buffer.remove();
+            }
             buffer.add(sample);
             dataChanged = true;
         }
diff --git a/src/core/build.gradle.kts b/src/core/build.gradle.kts
index ab84960a6a..bc51c5d724 100644
--- a/src/core/build.gradle.kts
+++ b/src/core/build.gradle.kts
@@ -99,7 +99,6 @@ dependencies {
     }
     implementation("org.jetbrains.lets-plot:lets-plot-batik")
     implementation("org.jetbrains.lets-plot:lets-plot-kotlin-jvm")
-    implementation("org.apache.commons:commons-collections4")
     implementation("org.apache.commons:commons-math3") {
         because("Mean, DescriptiveStatistics")
     }
diff --git a/src/core/src/main/java/org/apache/jmeter/config/Arguments.java 
b/src/core/src/main/java/org/apache/jmeter/config/Arguments.java
index 0301eb5862..7804b991e6 100644
--- a/src/core/src/main/java/org/apache/jmeter/config/Arguments.java
+++ b/src/core/src/main/java/org/apache/jmeter/config/Arguments.java
@@ -23,12 +23,12 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 
-import org.apache.commons.collections4.iterators.FilterIterator;
 import org.apache.jmeter.testelement.property.CollectionProperty;
 import org.apache.jmeter.testelement.property.JMeterProperty;
 import org.apache.jmeter.testelement.property.PropertyIterator;
 import org.apache.jmeter.testelement.property.TestElementProperty;
 import org.apache.jmeter.testelement.schema.PropertiesAccessor;
+import org.apache.jorphan.collections.FilterIterator;
 import org.apiguardian.api.API;
 
 /**
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/LoggerPanel.java 
b/src/core/src/main/java/org/apache/jmeter/gui/LoggerPanel.java
index d33b6c81d1..fc88e57b42 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/LoggerPanel.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/LoggerPanel.java
@@ -28,7 +28,6 @@ import javax.swing.JTextArea;
 import javax.swing.ScrollPaneConstants;
 import javax.swing.Timer;
 
-import org.apache.commons.collections4.queue.CircularFifoQueue;
 import org.apache.jmeter.gui.logging.GuiLogEventListener;
 import org.apache.jmeter.gui.logging.LogEventObject;
 import org.apache.jmeter.gui.util.JSyntaxTextArea;
@@ -57,7 +56,7 @@ public class LoggerPanel extends JPanel implements 
GuiLogEventListener {
     private static final int LOGGER_PANEL_REFRESH_PERIOD =
             JMeterUtils.getPropDefault("jmeter.gui.refresh_period", 500); // 
$NON-NLS-1$
 
-    private final Queue<String> events;
+    private final Queue<String> events = new ArrayDeque<>();
 
     private volatile boolean logChanged = false;
 
@@ -65,11 +64,6 @@ public class LoggerPanel extends JPanel implements 
GuiLogEventListener {
      * Pane for display JMeter log file
      */
     public LoggerPanel() {
-        if (LOGGER_PANEL_MAX_LINES > 0) {
-            events = new CircularFifoQueue<>(LOGGER_PANEL_MAX_LINES);
-        } else {
-            events = new ArrayDeque<>();
-        }
         textArea = init();
     }
 
@@ -113,6 +107,9 @@ public class LoggerPanel extends JPanel implements 
GuiLogEventListener {
 
         String logMessage = logEventObject.toString();
         synchronized (events) {
+            if (LOGGER_PANEL_MAX_LINES > 0 && events.size() >= 
LOGGER_PANEL_MAX_LINES) {
+                events.remove();
+            }
             events.add(logMessage);
         }
 
diff --git a/src/core/src/main/java/org/apache/jmeter/save/CSVSaveService.java 
b/src/core/src/main/java/org/apache/jmeter/save/CSVSaveService.java
index 5843ac98a7..4d3eaa1ad6 100644
--- a/src/core/src/main/java/org/apache/jmeter/save/CSVSaveService.java
+++ b/src/core/src/main/java/org/apache/jmeter/save/CSVSaveService.java
@@ -36,12 +36,14 @@ import java.time.ZoneId;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Date;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.regex.Matcher;
 
 import javax.swing.table.DefaultTableModel;
 
-import org.apache.commons.collections4.map.LinkedMap;
 import org.apache.jmeter.reporters.ResultCollector;
 import org.apache.jmeter.samplers.SampleEvent;
 import org.apache.jmeter.samplers.SampleResult;
@@ -471,7 +473,8 @@ public final class CSVSaveService {
     }
 
     // Map header names to set() methods
-    private static final LinkedMap<String, Functor> headerLabelMethods = new 
LinkedMap<>();
+    private static final Map<String, Functor> headerLabelMethods = new 
LinkedHashMap<>();
+    private static final Map<String, Integer> headerLabelPositions = new 
HashMap<>();
 
     // These entries must be in the same order as columns are saved/restored.
 
@@ -505,6 +508,10 @@ public final class CSVSaveService {
         headerLabelMethods.put(CSV_HOSTNAME, new Functor("setHostname"));
         headerLabelMethods.put(CSV_IDLETIME, new Functor("setIdleTime"));
         headerLabelMethods.put(CSV_CONNECT_TIME, new 
Functor("setConnectTime"));
+        int pos = 0;
+        for (String key : headerLabelMethods.keySet()) {
+            headerLabelPositions.put(key, pos++);
+        }
     }
 
     /**
@@ -605,8 +612,8 @@ public final class CSVSaveService {
                 previous = Integer.MAX_VALUE; // they are always last
                 continue;
             }
-            int current = headerLabelMethods.indexOf(label);
-            if (current == -1) {
+            Integer current = headerLabelPositions.get(label);
+            if (current == null) {
                 log.warn("Unknown column name {}", label);
                 return null; // unknown column name
             }
diff --git a/src/dist/build.gradle.kts b/src/dist/build.gradle.kts
index 8987916144..e2e52109fe 100644
--- a/src/dist/build.gradle.kts
+++ b/src/dist/build.gradle.kts
@@ -92,6 +92,9 @@ dependencies {
     runtimeOnly("commons-codec:commons-codec") {
         because("commons-codec was a dependency in previous JMeter versions, 
so we keep it for compatibility")
     }
+    runtimeOnly("org.apache.commons:commons-collections4") {
+        because("commons-collections4 was a dependency in previous JMeter 
versions, so we keep it for compatibility")
+    }
 
     binLicense(project(":src:licenses", "binLicense"))
     srcLicense(project(":src:licenses", "srcLicense"))
diff --git a/src/jorphan/build.gradle.kts b/src/jorphan/build.gradle.kts
index 7f87d06193..29b94f1308 100644
--- a/src/jorphan/build.gradle.kts
+++ b/src/jorphan/build.gradle.kts
@@ -25,7 +25,6 @@ dependencies {
     api("org.slf4j:slf4j-api")
 
     implementation("commons-io:commons-io")
-    implementation("org.apache.commons:commons-collections4")
     implementation("org.apache.commons:commons-math3")
     implementation("org.apache.commons:commons-text")
 
diff --git 
a/src/jorphan/src/main/kotlin/org/apache/jorphan/collections/FilterIterator.kt 
b/src/jorphan/src/main/kotlin/org/apache/jorphan/collections/FilterIterator.kt
new file mode 100644
index 0000000000..3241f8e5ee
--- /dev/null
+++ 
b/src/jorphan/src/main/kotlin/org/apache/jorphan/collections/FilterIterator.kt
@@ -0,0 +1,71 @@
+/*
+ * 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.jorphan.collections
+
+/**
+ * Iterator that filters elements from another iterator based on a predicate.
+ *
+ * This iterator lazily evaluates the predicate as elements are requested,
+ * only advancing through the source iterator when needed.
+ *
+ * @param Element the type of elements returned by this iterator
+ * @property iterator the source iterator to filter
+ * @property predicate the predicate that determines which elements to include
+ */
+public class FilterIterator<Element>(
+    private val iterator: Iterator<Element>,
+    private val predicate: (Element) -> Boolean
+) : Iterator<Element> {
+
+    private var nextElement: Element? = null
+    private var hasNextElement = false
+
+    override fun hasNext(): Boolean {
+        return hasNextElement || prepareNext()
+    }
+
+    /**
+     * Prepares the next element by advancing through the iterator
+     * until an element matching the predicate is found.
+     * This method is idempotent - calling it multiple times without
+     * consuming the element has no effect.
+     */
+    private fun prepareNext(): Boolean {
+        while (iterator.hasNext()) {
+            val element = iterator.next()
+            if (predicate(element)) {
+                nextElement = element
+                hasNextElement = true
+                return true
+            }
+        }
+        return false
+    }
+
+    override fun next(): Element {
+        if (!hasNextElement && !prepareNext()) {
+            throw NoSuchElementException("No more elements matching the 
predicate")
+        }
+
+        hasNextElement = false
+        @Suppress("UNCHECKED_CAST")
+        val result = nextElement as Element
+        nextElement = null
+        return result
+    }
+}
diff --git 
a/src/jorphan/src/test/kotlin/org/apache/jorphan/collections/FilterIteratorTest.kt
 
b/src/jorphan/src/test/kotlin/org/apache/jorphan/collections/FilterIteratorTest.kt
new file mode 100644
index 0000000000..a7538b141c
--- /dev/null
+++ 
b/src/jorphan/src/test/kotlin/org/apache/jorphan/collections/FilterIteratorTest.kt
@@ -0,0 +1,150 @@
+/*
+ * 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.jorphan.collections
+
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Assertions.assertFalse
+import org.junit.jupiter.api.Assertions.assertThrows
+import org.junit.jupiter.api.Assertions.assertTrue
+import org.junit.jupiter.api.Test
+
+class FilterIteratorTest {
+
+    private fun <T> assertFilteredResults(collection: Collection<T>, 
predicate: (T) -> Boolean) {
+        assertEquals(
+            collection.filter(predicate).toList().toString(),
+            FilterIterator(collection.iterator(), 
predicate).asSequence().toList().toString(),
+        ) {
+            "input: ${collection.toList()}"
+        }
+    }
+
+    @Test
+    fun `filter even numbers from list`() {
+        val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
+
+        assertFilteredResults(numbers) { it % 2 == 0 }
+    }
+
+    @Test
+    fun `filter odd numbers from list`() {
+        val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
+
+        assertFilteredResults(numbers) { it % 2 != 0 }
+    }
+
+    @Test
+    fun `filter strings by length`() {
+        val words = listOf("a", "ab", "abc", "abcd", "abcde")
+        assertFilteredResults(words) { it.length > 2 }
+    }
+
+    @Test
+    fun `filter with no matches returns empty iterator`() {
+        val numbers = listOf(1, 3, 5, 7, 9)
+        assertFilteredResults(numbers) { it % 2 == 0 }
+    }
+
+    @Test
+    fun `filter with all matches returns all elements`() {
+        val numbers = listOf(2, 4, 6, 8, 10)
+        assertFilteredResults(numbers) { it % 2 == 0 }
+    }
+
+    @Test
+    fun `filter empty list returns empty iterator`() {
+        val empty = emptyList<Int>()
+        assertFilteredResults(empty) { it > 0 }
+    }
+
+    @Test
+    fun `filter with null values`() {
+        val items = listOf("a", null, "b", null, "c")
+        assertFilteredResults(items) { it != null }
+    }
+
+    @Test
+    fun `filter with always true predicate`() {
+        val numbers = listOf(1, 2, 3, 4, 5)
+        assertFilteredResults(numbers) { true }
+    }
+
+    @Test
+    fun `filter with always false predicate`() {
+        val numbers = listOf(1, 2, 3, 4, 5)
+        assertFilteredResults(numbers) { false }
+    }
+
+    @Test
+    fun `calling next without hasNext throws exception`() {
+        val numbers = listOf(1, 3, 5)
+        val filtered = FilterIterator(numbers.iterator()) { it % 2 == 0 }
+
+        assertThrows(NoSuchElementException::class.java) {
+            filtered.next()
+        }
+    }
+
+    @Test
+    fun `calling next after exhausted throws exception`() {
+        val numbers = listOf(2)
+        val filtered = FilterIterator(numbers.iterator()) { it % 2 == 0 }
+
+        assertTrue(filtered.hasNext())
+        assertEquals(2, filtered.next())
+        assertFalse(filtered.hasNext())
+
+        assertThrows(NoSuchElementException::class.java) {
+            filtered.next()
+        }
+    }
+
+    @Test
+    fun `multiple calls to hasNext are idempotent`() {
+        val numbers = listOf(1, 2, 3, 4, 5)
+        val filtered = FilterIterator(numbers.iterator()) { it % 2 == 0 }
+
+        assertTrue(filtered.hasNext())
+        assertTrue(filtered.hasNext())
+        assertTrue(filtered.hasNext())
+        assertEquals(2, filtered.next())
+    }
+
+    @Test
+    fun `filter chain with multiple predicates`() {
+        val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
+
+        // Filter even numbers, then filter those > 5
+        val filtered1 = FilterIterator(numbers.iterator()) { it % 2 == 0 }
+        val filtered2 = FilterIterator(filtered1) { it > 5 }
+
+        val result = filtered2.asSequence().toList()
+
+        assertEquals(listOf(6, 8, 10), result)
+    }
+
+    @Test
+    fun `filter with single element matching`() {
+        val numbers = listOf(1, 2, 3)
+        val filtered = FilterIterator(numbers.iterator()) { it == 2 }
+
+        assertTrue(filtered.hasNext())
+        assertEquals(2, filtered.next())
+        assertFalse(filtered.hasNext())
+    }
+}
diff --git a/src/protocol/http/build.gradle.kts 
b/src/protocol/http/build.gradle.kts
index 36551cd999..2bc10d4ed6 100644
--- a/src/protocol/http/build.gradle.kts
+++ b/src/protocol/http/build.gradle.kts
@@ -55,9 +55,6 @@ dependencies {
     }
     implementation("org.jsoup:jsoup")
     implementation("oro:oro")
-    runtimeOnly("org.apache.commons:commons-collections4") {
-        because("commons-collections4 was a dependency in previous JMeter 
versions, so we keep it for compatibility")
-    }
     implementation("commons-net:commons-net")
     implementation("com.helger.commons:ph-commons") {
         // We don't really need to use/distribute jsr305

Reply via email to