Skip to content

Commit 5969449

Browse files
fix: remove change listener after removing child configuration object (#8215) (#8226)
Co-authored-by: Sascha Ißbrücker <sissbruecker@vaadin.com>
1 parent 34ada5f commit 5969449

File tree

3 files changed

+51
-3
lines changed

3 files changed

+51
-3
lines changed

vaadin-map-flow-parent/vaadin-map-flow/src/main/java/com/vaadin/flow/component/map/configuration/AbstractConfigurationObject.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ public abstract class AbstractConfigurationObject implements Serializable {
9393

9494
protected final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(
9595
this);
96+
private final SerializablePropertyChangeListener childChangeListener = this::notifyChange;
9697

9798
public AbstractConfigurationObject() {
9899
this.id = UUID.randomUUID().toString();
@@ -165,7 +166,7 @@ protected void addChild(AbstractConfigurationObject configurationObject) {
165166
Objects.requireNonNull(configurationObject,
166167
"Child configuration object must not be null");
167168
children.add(configurationObject);
168-
configurationObject.addPropertyChangeListener(this::notifyChange);
169+
configurationObject.addPropertyChangeListener(childChangeListener);
169170
markAsDirty();
170171
// When adding a sub-hierarchy, we need to make sure that the client
171172
// receives the whole hierarchy. Otherwise objects that have been synced
@@ -198,7 +199,7 @@ protected void removeChild(
198199
if (configurationObject == null)
199200
return;
200201
children.remove(configurationObject);
201-
configurationObject.removePropertyChangeListener(this::notifyChange);
202+
configurationObject.removePropertyChangeListener(childChangeListener);
202203
markAsDirty();
203204
}
204205

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/**
2+
* Copyright 2000-2025 Vaadin Ltd.
3+
*
4+
* This program is available under Vaadin Commercial License and Service Terms.
5+
*
6+
* See {@literal <https://vaadin.com/commercial-license-and-service-terms>} for the full
7+
* license.
8+
*/
9+
package com.vaadin.flow.component.map.configuration;
10+
11+
import java.beans.PropertyChangeListener;
12+
import java.io.Serializable;
13+
14+
/**
15+
* A {@link PropertyChangeListener} that is also {@link Serializable}.
16+
*/
17+
public interface SerializablePropertyChangeListener
18+
extends PropertyChangeListener, Serializable {
19+
}

vaadin-map-flow-parent/vaadin-map-flow/src/test/java/com/vaadin/flow/component/map/configuration/AbstractConfigurationObjectTest.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,30 @@ public void setNestedObject_marksNestedHierarchyAsDirty() {
123123
Mockito.verify(changeCollectorMock).accept(testConfiguration);
124124
}
125125

126+
@Test
127+
public void setNestedObject_addsAndRemovesChangeListener() {
128+
TestConfiguration nestedObject = new TestConfiguration();
129+
Assert.assertFalse(nestedObject.hasListeners());
130+
131+
testConfiguration.setNestedConfiguration(nestedObject);
132+
Assert.assertTrue(nestedObject.hasListeners());
133+
134+
testConfiguration.addPropertyChangeListener(changeListenerMock);
135+
136+
nestedObject.setFoo("test");
137+
Mockito.verify(changeListenerMock, Mockito.times(1))
138+
.propertyChange(Mockito.any());
139+
140+
testConfiguration.setNestedConfiguration(null);
141+
Assert.assertFalse(nestedObject.hasListeners());
142+
Mockito.verify(changeListenerMock, Mockito.times(2))
143+
.propertyChange(Mockito.any());
144+
145+
nestedObject.setFoo("anotherTest");
146+
Mockito.verify(changeListenerMock, Mockito.times(2))
147+
.propertyChange(Mockito.any());
148+
}
149+
126150
@Test
127151
public void removeChangeListener_doesNotNotifyChanges() {
128152
testConfiguration.addPropertyChangeListener(changeListenerMock);
@@ -289,13 +313,17 @@ public void setNestedConfiguration(
289313
TestConfiguration nestedConfiguration) {
290314
removeChild(this.nestedConfiguration);
291315
this.nestedConfiguration = nestedConfiguration;
292-
addChild(nestedConfiguration);
316+
addNullableChild(nestedConfiguration);
293317
}
294318

295319
// Expose method for testing
296320
@Override
297321
protected void deepMarkAsDirty() {
298322
super.deepMarkAsDirty();
299323
}
324+
325+
protected boolean hasListeners() {
326+
return propertyChangeSupport.hasListeners("property");
327+
}
300328
}
301329
}

0 commit comments

Comments
 (0)