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