Skip to content

Commit 28ebcb6

Browse files
committed
Avoid wrong results when Object.hashCode()happen to collide
Use IdentityHashMap instead of HashMap when key is TestElement
1 parent bed24df commit 28ebcb6

File tree

6 files changed

+31
-15
lines changed

6 files changed

+31
-15
lines changed

src/core/src/main/java/org/apache/jmeter/control/GenericController.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.ArrayDeque;
2222
import java.util.ArrayList;
2323
import java.util.Deque;
24+
import java.util.IdentityHashMap;
2425
import java.util.Iterator;
2526
import java.util.List;
2627
import java.util.concurrent.ConcurrentHashMap;
@@ -58,7 +59,7 @@ public class GenericController extends AbstractTestElement implements Controller
5859
private transient Deque<LoopIterationListener> iterationListeners = new ArrayDeque<>();
5960

6061
// Only create the map if it is required
61-
private transient ConcurrentMap<TestElement, Object> children = new ConcurrentHashMap<>();
62+
private transient IdentityHashMap<TestElement, Object> children = new IdentityHashMap<>();
6263

6364
private static final Object DUMMY = new Object();
6465

@@ -354,9 +355,11 @@ public void addTestElement(TestElement child) {
354355
*/
355356
@Override
356357
public final boolean addTestElementOnce(TestElement child){
357-
if (children.putIfAbsent(child, DUMMY) == null) {
358-
addTestElement(child);
359-
return true;
358+
synchronized (children) {
359+
if (children.putIfAbsent(child, DUMMY) == null) {
360+
addTestElement(child);
361+
return true;
362+
}
360363
}
361364
return false;
362365
}
@@ -414,7 +417,7 @@ protected void resetIterCount() {
414417

415418
protected Object readResolve(){
416419
iterationListeners = new ArrayDeque<>();
417-
children = new ConcurrentHashMap<>();
420+
children = new IdentityHashMap<>();
418421
subControllersAndSamplers = new ArrayList<>();
419422

420423
return this;

src/core/src/main/java/org/apache/jmeter/gui/GuiPackage.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.util.ArrayList;
2525
import java.util.Collections;
2626
import java.util.HashMap;
27+
import java.util.IdentityHashMap;
2728
import java.util.List;
2829
import java.util.Map;
2930
import java.util.concurrent.atomic.AtomicInteger;
@@ -102,7 +103,7 @@ public final class GuiPackage implements LocaleChangeListener, HistoryListener {
102103
* to their corresponding GUI components.
103104
* <p>This enables to associate {@link UnsharedComponent} UIs with their {@link TestElement}.</p>
104105
*/
105-
private Map<TestElement, JMeterGUIComponent> nodesToGui = new HashMap<>();
106+
private IdentityHashMap<TestElement, JMeterGUIComponent> nodesToGui = new IdentityHashMap<>();
106107

107108
/**
108109
* Map from Class to JMeterGUIComponent, mapping the Class of a GUI

src/core/src/main/java/org/apache/jmeter/testbeans/gui/TestBeanGUI.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@
3737

3838
import javax.swing.JPopupMenu;
3939

40+
import com.github.benmanes.caffeine.cache.Cache;
41+
import com.github.benmanes.caffeine.cache.Caffeine;
42+
4043
import org.apache.commons.collections4.map.LRUMap;
4144
import org.apache.jmeter.assertions.Assertion;
4245
import org.apache.jmeter.assertions.gui.AbstractAssertionGui;
@@ -109,7 +112,11 @@ public class TestBeanGUI extends AbstractJMeterGuiComponent implements JMeterGUI
109112
* needs to be limited, though, to avoid memory issues when editing very
110113
* large test plans.
111114
*/
112-
private final Map<TestElement, Customizer> customizers = new LRUMap<>(20);
115+
private final Cache<TestElement, Customizer> customizers =
116+
Caffeine.newBuilder()
117+
.weakKeys() // So test elements are compared using identity == rather than .equals
118+
.maximumSize(20)
119+
.build();
113120

114121
/** Index of the customizer in the JPanel's child component list: */
115122
private int customizerIndexInPanel;
@@ -325,7 +332,7 @@ private void setValues(TestElement element) {
325332
if (initialized){
326333
remove(customizerIndexInPanel);
327334
}
328-
Customizer c = customizers.computeIfAbsent(element, e -> {
335+
Customizer c = customizers.get(element, e -> {
329336
Customizer result = createCustomizer();
330337
result.setObject(propertyMap);
331338
return result;

src/core/src/main/java/org/apache/jmeter/threads/AbstractThreadGroup.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import java.io.Serializable;
2121
import java.time.Duration;
22+
import java.util.IdentityHashMap;
2223
import java.util.concurrent.ConcurrentHashMap;
2324
import java.util.concurrent.ConcurrentMap;
2425
import java.util.concurrent.atomic.AtomicInteger;
@@ -50,7 +51,7 @@ public abstract class AbstractThreadGroup extends AbstractTestElement
5051
private static final long serialVersionUID = 240L;
5152

5253
// Only create the map if it is required
53-
private final transient ConcurrentMap<TestElement, Object> children = new ConcurrentHashMap<>();
54+
private final transient IdentityHashMap<TestElement, Object> children = new IdentityHashMap<>();
5455

5556
private static final Object DUMMY = new Object();
5657

@@ -135,9 +136,11 @@ public void addTestElement(TestElement child) {
135136
*/
136137
@Override
137138
public final boolean addTestElementOnce(TestElement child){
138-
if (children.putIfAbsent(child, DUMMY) == null) {
139-
addTestElement(child);
140-
return true;
139+
synchronized (children) {
140+
if (children.putIfAbsent(child, DUMMY) == null) {
141+
addTestElement(child);
142+
return true;
143+
}
141144
}
142145
return false;
143146
}

src/core/src/main/java/org/apache/jmeter/threads/TestCompiler.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.ArrayList;
2121
import java.util.HashMap;
2222
import java.util.HashSet;
23+
import java.util.IdentityHashMap;
2324
import java.util.LinkedList;
2425
import java.util.List;
2526
import java.util.ListIterator;
@@ -69,10 +70,10 @@ public class TestCompiler implements HashTreeTraverser {
6970
// TODO: replace with ArrayDequeue
7071
private final LinkedList<TestElement> stack = new LinkedList<>();
7172

72-
private final Map<Sampler, SamplePackage> samplerConfigMap = new HashMap<>();
73+
private final IdentityHashMap<Sampler, SamplePackage> samplerConfigMap = new IdentityHashMap<>();
7374

74-
private final Map<TransactionController, SamplePackage> transactionControllerConfigMap =
75-
new HashMap<>();
75+
private final IdentityHashMap<TransactionController, SamplePackage> transactionControllerConfigMap =
76+
new IdentityHashMap<>();
7677

7778
private final HashTree testTree;
7879

xdocs/changes.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ Summary
153153
<h3>General</h3>
154154
<ul>
155155
<li><bug>66157</bug><pr>719</pr>Correct theme for darklaf on rsyntaxtextarea</li>
156+
<li>Avoid wrong results when <code>Object.hashCode()</code> happen to collide. Use <code>IdentityHashMap</code> instead of <code>HashMap</code> when key is <code>TestElement</code></li>
156157
</ul>
157158

158159
<!-- =================== Thanks =================== -->

0 commit comments

Comments
 (0)